Created
January 12, 2016 15:21
-
-
Save FrankSpierings/3577b0365d02df6f7eeb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Thank you Bouncy Castle. | |
#Slight code modifications compared to the original C# code. | |
$source = @" | |
using System; | |
using System.Diagnostics; | |
using Org.BouncyCastle.Utilities; | |
namespace Org.BouncyCastle.Crypto.Digests | |
{ | |
/// <summary> | |
/// Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ | |
/// </summary> | |
/// <remarks> | |
/// Following the naming conventions used in the C source code to enable easy review of the implementation. | |
/// </remarks> | |
public class KeccakDigest | |
{ | |
private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants(); | |
private static readonly int[] KeccakRhoOffsets = KeccakInitializeRhoOffsets(); | |
private static ulong[] KeccakInitializeRoundConstants() | |
{ | |
ulong[] keccakRoundConstants = new ulong[24]; | |
byte LFSRState = 0x01; | |
for (int i = 0; i < 24; i++) | |
{ | |
keccakRoundConstants[i] = 0; | |
for (int j = 0; j < 7; j++) | |
{ | |
int bitPosition = (1 << j) - 1; | |
// LFSR86540 | |
bool loBit = (LFSRState & 0x01) != 0; | |
if (loBit) | |
{ | |
keccakRoundConstants[i] ^= 1UL << bitPosition; | |
} | |
bool hiBit = (LFSRState & 0x80) != 0; | |
LFSRState <<= 1; | |
if (hiBit) | |
{ | |
LFSRState ^= 0x71; | |
} | |
} | |
} | |
return keccakRoundConstants; | |
} | |
private static int[] KeccakInitializeRhoOffsets() | |
{ | |
int[] keccakRhoOffsets = new int[25]; | |
int x, y, t, newX, newY; | |
int rhoOffset = 0; | |
keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = rhoOffset; | |
x = 1; | |
y = 0; | |
for (t = 1; t < 25; t++) | |
{ | |
//rhoOffset = ((t + 1) * (t + 2) / 2) % 64; | |
rhoOffset = (rhoOffset + t) & 63; | |
keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset; | |
newX = (0 * x + 1 * y) % 5; | |
newY = (2 * x + 3 * y) % 5; | |
x = newX; | |
y = newY; | |
} | |
return keccakRhoOffsets; | |
} | |
protected byte[] state = new byte[(1600 / 8)]; | |
protected byte[] dataQueue = new byte[(1536 / 8)]; | |
protected int rate; | |
protected int bitsInQueue; | |
protected int fixedOutputLength; | |
protected bool squeezing; | |
protected int bitsAvailableForSqueezing; | |
protected byte[] chunk; | |
protected byte[] oneByte; | |
private void ClearDataQueueSection(int off, int len) | |
{ | |
for (int i = off; i != off + len; i++) | |
{ | |
dataQueue[i] = 0; | |
} | |
} | |
public KeccakDigest() | |
: this(288) | |
{ | |
} | |
public KeccakDigest(int bitLength) | |
{ | |
Init(bitLength); | |
} | |
public KeccakDigest(KeccakDigest source) | |
{ | |
CopyIn(source); | |
} | |
private void CopyIn(KeccakDigest source) | |
{ | |
Array.Copy(source.state, 0, this.state, 0, source.state.Length); | |
Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length); | |
this.rate = source.rate; | |
this.bitsInQueue = source.bitsInQueue; | |
this.fixedOutputLength = source.fixedOutputLength; | |
this.squeezing = source.squeezing; | |
this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing; | |
this.chunk = Arrays.Clone(source.chunk); | |
this.oneByte = Arrays.Clone(source.oneByte); | |
} | |
public virtual string AlgorithmName | |
{ | |
get { return "Keccak-" + fixedOutputLength; } | |
} | |
public virtual int GetDigestSize() | |
{ | |
return fixedOutputLength / 8; | |
} | |
public virtual void Update(byte input) | |
{ | |
oneByte[0] = input; | |
Absorb(oneByte, 0, 8L); | |
} | |
public virtual void BlockUpdate(byte[] input, int inOff, int len) | |
{ | |
Absorb(input, inOff, len * 8L); | |
} | |
public virtual int DoFinal(byte[] output, int outOff) | |
{ | |
Squeeze(output, outOff, fixedOutputLength); | |
Reset(); | |
return GetDigestSize(); | |
} | |
/* | |
* TODO Possible API change to support partial-byte suffixes. | |
*/ | |
protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits) | |
{ | |
if (partialBits > 0) | |
{ | |
oneByte[0] = partialByte; | |
Absorb(oneByte, 0, partialBits); | |
} | |
Squeeze(output, outOff, fixedOutputLength); | |
Reset(); | |
return GetDigestSize(); | |
} | |
public virtual void Reset() | |
{ | |
Init(fixedOutputLength); | |
} | |
/** | |
* Return the size of block that the compression function is applied to in bytes. | |
* | |
* @return internal byte length of a block. | |
*/ | |
public virtual int GetByteLength() | |
{ | |
return rate / 8; | |
} | |
private void Init(int bitLength) | |
{ | |
switch (bitLength) | |
{ | |
case 128: | |
InitSponge(1344, 256); | |
break; | |
case 224: | |
InitSponge(1152, 448); | |
break; | |
case 256: | |
InitSponge(1088, 512); | |
break; | |
case 288: | |
InitSponge(1024, 576); | |
break; | |
case 384: | |
InitSponge(832, 768); | |
break; | |
case 512: | |
InitSponge(576, 1024); | |
break; | |
default: | |
throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength"); | |
} | |
} | |
private void InitSponge(int rate, int capacity) | |
{ | |
if (rate + capacity != 1600) | |
{ | |
throw new InvalidOperationException("rate + capacity != 1600"); | |
} | |
if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) | |
{ | |
throw new InvalidOperationException("invalid rate value"); | |
} | |
this.rate = rate; | |
// this is never read, need to check to see why we want to save it | |
// this.capacity = capacity; | |
this.fixedOutputLength = 0; | |
Arrays.Fill(this.state, (byte)0); | |
Arrays.Fill(this.dataQueue, (byte)0); | |
this.bitsInQueue = 0; | |
this.squeezing = false; | |
this.bitsAvailableForSqueezing = 0; | |
this.fixedOutputLength = capacity / 2; | |
this.chunk = new byte[rate / 8]; | |
this.oneByte = new byte[1]; | |
} | |
private void AbsorbQueue() | |
{ | |
KeccakAbsorb(state, dataQueue, rate / 8); | |
bitsInQueue = 0; | |
} | |
protected virtual void Absorb(byte[] data, int off, long databitlen) | |
{ | |
long i, j, wholeBlocks; | |
if ((bitsInQueue % 8) != 0) | |
{ | |
throw new InvalidOperationException("attempt to absorb with odd length queue."); | |
} | |
if (squeezing) | |
{ | |
throw new InvalidOperationException("attempt to absorb while squeezing."); | |
} | |
i = 0; | |
while (i < databitlen) | |
{ | |
if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate))) | |
{ | |
wholeBlocks = (databitlen - i) / rate; | |
for (j = 0; j < wholeBlocks; j++) | |
{ | |
Array.Copy(data, (int)(off + (i / 8) + (j * chunk.Length)), chunk, 0, chunk.Length); | |
KeccakAbsorb(state, chunk, chunk.Length); | |
} | |
i += wholeBlocks * rate; | |
} | |
else | |
{ | |
int partialBlock = (int)(databitlen - i); | |
if (partialBlock + bitsInQueue > rate) | |
{ | |
partialBlock = rate - bitsInQueue; | |
} | |
int partialByte = partialBlock % 8; | |
partialBlock -= partialByte; | |
Array.Copy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8); | |
bitsInQueue += partialBlock; | |
i += partialBlock; | |
if (bitsInQueue == rate) | |
{ | |
AbsorbQueue(); | |
} | |
if (partialByte > 0) | |
{ | |
int mask = (1 << partialByte) - 1; | |
dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask); | |
bitsInQueue += partialByte; | |
i += partialByte; | |
} | |
} | |
} | |
} | |
private void PadAndSwitchToSqueezingPhase() | |
{ | |
if (bitsInQueue + 1 == rate) | |
{ | |
dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); | |
AbsorbQueue(); | |
ClearDataQueueSection(0, rate / 8); | |
} | |
else | |
{ | |
ClearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); | |
dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); | |
} | |
dataQueue[(rate - 1) / 8] |= (byte)(1U << ((rate - 1) % 8)); | |
AbsorbQueue(); | |
if (rate == 1024) | |
{ | |
KeccakExtract1024bits(state, dataQueue); | |
bitsAvailableForSqueezing = 1024; | |
} | |
else | |
{ | |
KeccakExtract(state, dataQueue, rate / 64); | |
bitsAvailableForSqueezing = rate; | |
} | |
squeezing = true; | |
} | |
protected virtual void Squeeze(byte[] output, int offset, long outputLength) | |
{ | |
long i; | |
int partialBlock; | |
if (!squeezing) | |
{ | |
PadAndSwitchToSqueezingPhase(); | |
} | |
if ((outputLength % 8) != 0) | |
{ | |
throw new InvalidOperationException("outputLength not a multiple of 8"); | |
} | |
i = 0; | |
while (i < outputLength) | |
{ | |
if (bitsAvailableForSqueezing == 0) | |
{ | |
KeccakPermutation(state); | |
if (rate == 1024) | |
{ | |
KeccakExtract1024bits(state, dataQueue); | |
bitsAvailableForSqueezing = 1024; | |
} | |
else | |
{ | |
KeccakExtract(state, dataQueue, rate / 64); | |
bitsAvailableForSqueezing = rate; | |
} | |
} | |
partialBlock = bitsAvailableForSqueezing; | |
if ((long)partialBlock > outputLength - i) | |
{ | |
partialBlock = (int)(outputLength - i); | |
} | |
Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8); | |
bitsAvailableForSqueezing -= partialBlock; | |
i += partialBlock; | |
} | |
} | |
private static void FromBytesToWords(ulong[] stateAsWords, byte[] state) | |
{ | |
for (int i = 0; i < (1600 / 64); i++) | |
{ | |
stateAsWords[i] = 0; | |
int index = i * (64 / 8); | |
for (int j = 0; j < (64 / 8); j++) | |
{ | |
stateAsWords[i] |= ((ulong)state[index + j] & 0xff) << ((8 * j)); | |
} | |
} | |
} | |
private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords) | |
{ | |
for (int i = 0; i < (1600 / 64); i++) | |
{ | |
int index = i * (64 / 8); | |
for (int j = 0; j < (64 / 8); j++) | |
{ | |
state[index + j] = (byte)(stateAsWords[i] >> (8 * j)); | |
} | |
} | |
} | |
private void KeccakPermutation(byte[] state) | |
{ | |
ulong[] longState = new ulong[state.Length / 8]; | |
FromBytesToWords(longState, state); | |
KeccakPermutationOnWords(longState); | |
FromWordsToBytes(state, longState); | |
} | |
private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) | |
{ | |
for (int i = 0; i < dataLengthInBytes; i++) | |
{ | |
state[i] ^= data[i]; | |
} | |
KeccakPermutation(state); | |
} | |
private void KeccakPermutationOnWords(ulong[] state) | |
{ | |
int i; | |
for (i = 0; i < 24; i++) | |
{ | |
Theta(state); | |
Rho(state); | |
Pi(state); | |
Chi(state); | |
Iota(state, i); | |
} | |
} | |
ulong[] C = new ulong[5]; | |
private void Theta(ulong[] A) | |
{ | |
for (int x = 0; x < 5; x++) | |
{ | |
C[x] = 0; | |
for (int y = 0; y < 5; y++) | |
{ | |
C[x] ^= A[x + 5 * y]; | |
} | |
} | |
for (int x = 0; x < 5; x++) | |
{ | |
ulong dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >> (64 - 1)))) ^ C[(x + 4) % 5]; | |
for (int y = 0; y < 5; y++) | |
{ | |
A[x + 5 * y] ^= dX; | |
} | |
} | |
} | |
private void Rho(ulong[] A) | |
{ | |
for (int x = 0; x < 5; x++) | |
{ | |
for (int y = 0; y < 5; y++) | |
{ | |
int index = x + 5 * y; | |
A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >> (64 - KeccakRhoOffsets[index]))) : A[index]); | |
} | |
} | |
} | |
ulong[] tempA = new ulong[25]; | |
private void Pi(ulong[] A) | |
{ | |
Array.Copy(A, 0, tempA, 0, tempA.Length); | |
for (int x = 0; x < 5; x++) | |
{ | |
for (int y = 0; y < 5; y++) | |
{ | |
A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; | |
} | |
} | |
} | |
ulong[] chiC = new ulong[5]; | |
private void Chi(ulong[] A) | |
{ | |
for (int y = 0; y < 5; y++) | |
{ | |
for (int x = 0; x < 5; x++) | |
{ | |
chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); | |
} | |
for (int x = 0; x < 5; x++) | |
{ | |
A[x + 5 * y] = chiC[x]; | |
} | |
} | |
} | |
private static void Iota(ulong[] A, int indexRound) | |
{ | |
A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; | |
} | |
private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) | |
{ | |
KeccakPermutationAfterXor(byteState, data, dataInBytes); | |
} | |
private void KeccakExtract1024bits(byte[] byteState, byte[] data) | |
{ | |
Array.Copy(byteState, 0, data, 0, 128); | |
} | |
private void KeccakExtract(byte[] byteState, byte[] data, int laneCount) | |
{ | |
Array.Copy(byteState, 0, data, 0, laneCount * 8); | |
} | |
// public virtual IMemoable Copy() | |
// { | |
// return new KeccakDigest(this); | |
// } | |
// public virtual void Reset(IMemoable other) | |
// { | |
// KeccakDigest d = (KeccakDigest)other; | |
// CopyIn(d); | |
// } | |
} | |
} | |
namespace Org.BouncyCastle.Crypto.Digests | |
{ | |
/// <summary> | |
/// Implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ | |
/// </summary> | |
/// <remarks> | |
/// Following the naming conventions used in the C source code to enable easy review of the implementation. | |
/// </remarks> | |
public class Sha3Digest | |
: KeccakDigest | |
{ | |
private static int CheckBitLength(int bitLength) | |
{ | |
switch (bitLength) | |
{ | |
case 224: | |
case 256: | |
case 384: | |
case 512: | |
return bitLength; | |
default: | |
throw new ArgumentException(bitLength + " not supported for SHA-3", "bitLength"); | |
} | |
} | |
public Sha3Digest() | |
: this(256) | |
{ | |
} | |
public Sha3Digest(int bitLength) | |
: base(CheckBitLength(bitLength)) | |
{ | |
} | |
public Sha3Digest(Sha3Digest source) | |
: base(source) | |
{ | |
} | |
public override string AlgorithmName | |
{ | |
get { return "SHA3-" + fixedOutputLength; } | |
} | |
public override int DoFinal(byte[] output, int outOff) | |
{ | |
Absorb(new byte[]{ 0x02 }, 0, 2); | |
return base.DoFinal(output, outOff); | |
} | |
/* | |
* TODO Possible API change to support partial-byte suffixes. | |
*/ | |
protected override int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits) | |
{ | |
if (partialBits < 0 || partialBits > 7) | |
throw new ArgumentException("must be in the range [0,7]", "partialBits"); | |
int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x02 << partialBits); | |
Debug.Assert(finalInput >= 0); | |
int finalBits = partialBits + 2; | |
if (finalBits >= 8) | |
{ | |
oneByte[0] = (byte)finalInput; | |
Absorb(oneByte, 0, 8); | |
finalBits -= 8; | |
finalInput >>= 8; | |
} | |
return base.DoFinal(output, outOff, (byte)finalInput, finalBits); | |
} | |
} | |
} | |
namespace Org.BouncyCastle.Utilities | |
{ | |
/// <summary> General array utilities.</summary> | |
public abstract class Arrays | |
{ | |
public static byte[] Clone( | |
byte[] data) | |
{ | |
return data == null ? null : (byte[])data.Clone(); | |
} | |
public static void Fill( | |
byte[] buf, | |
byte b) | |
{ | |
int i = buf.Length; | |
while (i > 0) | |
{ | |
buf[--i] = b; | |
} | |
} | |
} | |
} | |
"@ | |
Add-type -TypeDefinition $source -Language CSharp | |
$plain = "Hello World" | |
$plainBytes = [System.Text.Encoding]::ASCII.GetBytes($plain) | |
$sha3 = New-Object Org.BouncyCastle.Crypto.Digests.Sha3Digest(512) | |
[Byte[]]$digest = (1..$sha3.GetDigestSize()) |% {0} | |
$sha3.BlockUpdate($plainBytes, 0, $plainBytes.Length) | |
$sha3.DoFinal($digest, 0) | Out-Null | |
$hash = "" | |
$digest |% {$hash += $("{0:X02}" -f $_) } | |
Write-Host $("[+] Message: {0} " -f $plain) | |
Write-Host $("[+] Hash: {0} " -f $hash) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment