SHA3.ps1

January 12, 2016 · View on GitHub

#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 { ///

/// Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ /// /// /// Following the naming conventions used in the C source code to enable easy review of the implementation. /// 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 { ///

/// Implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ /// /// /// Following the naming conventions used in the C source code to enable easy review of the implementation. /// 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 { ///

General array utilities. 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="HelloWorld"plain = "Hello World" plainBytes = [System.Text.Encoding]::ASCII.GetBytes(plain)plain) sha3 = New-Object Org.BouncyCastle.Crypto.Digests.Sha3Digest(512) [Byte[]]digest=(1..digest = (1..sha3.GetDigestSize()) |% {0} sha3.BlockUpdate(sha3.BlockUpdate(plainBytes, 0, plainBytes.Length)plainBytes.Length) sha3.DoFinal($digest, 0) | Out-Null

hash=""hash = "" digest |% {hash+=hash += ("{0:X02}" -f _) } Write-Host ("[+] Message: {0} " -f plain)WriteHostplain) Write-Host ("[+] Hash: {0} " -f $hash)