From 531a42d958fba3e509ccedc4df53ce40ec9a4d52 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 28 Sep 2022 19:01:45 +0700 Subject: Port Blake3 from bc-java --- crypto/src/crypto/digests/Blake3Digest.cs | 1047 ++++++++++++++++++++++ crypto/src/crypto/parameters/Blake3Parameters.cs | 58 ++ crypto/src/security/DigestUtilities.cs | 4 + 3 files changed, 1109 insertions(+) create mode 100644 crypto/src/crypto/digests/Blake3Digest.cs create mode 100644 crypto/src/crypto/parameters/Blake3Parameters.cs (limited to 'crypto/src') diff --git a/crypto/src/crypto/digests/Blake3Digest.cs b/crypto/src/crypto/digests/Blake3Digest.cs new file mode 100644 index 000000000..3b85c8c24 --- /dev/null +++ b/crypto/src/crypto/digests/Blake3Digest.cs @@ -0,0 +1,1047 @@ +using System; +using System.Collections.Generic; +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER +using System.Runtime.CompilerServices; +#endif + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + public sealed class Blake3Digest + : IDigest, IMemoable, IXof + { + /** + * Already outputting error. + */ + private const string ERR_OUTPUTTING = "Already outputting"; + + /** + * Number of Words. + */ + private const int NUMWORDS = 8; + + /** + * Number of Rounds. + */ + private const int ROUNDS = 7; + + /** + * Buffer length. + */ + private const int BLOCKLEN = NUMWORDS * Integers.NumBytes * 2; + + /** + * Chunk length. + */ + private const int CHUNKLEN = 1024; + + /** + * ChunkStart Flag. + */ + private const int CHUNKSTART = 1; + + /** + * ChunkEnd Flag. + */ + private const int CHUNKEND = 2; + + /** + * Parent Flag. + */ + private const int PARENT = 4; + + /** + * Root Flag. + */ + private const int ROOT = 8; + + /** + * KeyedHash Flag. + */ + private const int KEYEDHASH = 16; + + /** + * DeriveContext Flag. + */ + private const int DERIVECONTEXT = 32; + + /** + * DeriveKey Flag. + */ + private const int DERIVEKEY = 64; + + /** + * Chaining0 State Locations. + */ + private const int CHAINING0 = 0; + + /** + * Chaining1 State Location. + */ + private const int CHAINING1 = 1; + + /** + * Chaining2 State Location. + */ + private const int CHAINING2 = 2; + + /** + * Chaining3 State Location. + */ + private const int CHAINING3 = 3; + + /** + * Chaining4 State Location. + */ + private const int CHAINING4 = 4; + + /** + * Chaining5 State Location. + */ + private const int CHAINING5 = 5; + + /** + * Chaining6 State Location. + */ + private const int CHAINING6 = 6; + + /** + * Chaining7 State Location. + */ + private const int CHAINING7 = 7; + + /** + * IV0 State Locations. + */ + private const int IV0 = 8; + + /** + * IV1 State Location. + */ + private const int IV1 = 9; + + /** + * IV2 State Location. + */ + private const int IV2 = 10; + + /** + * IV3 State Location. + */ + private const int IV3 = 11; + + /** + * Count0 State Location. + */ + private const int COUNT0 = 12; + + /** + * Count1 State Location. + */ + private const int COUNT1 = 13; + + /** + * DataLen State Location. + */ + private const int DATALEN = 14; + + /** + * Flags State Location. + */ + private const int FLAGS = 15; + + /** + * Message word permutations. + */ + private static readonly byte[] SIGMA = { 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8 }; + + /** + * Blake3 Initialization Vector. + */ + private static readonly uint[] IV = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + }; + + /** + * The byte input/output buffer. + */ + private readonly byte[] m_theBuffer = new byte[BLOCKLEN]; + + /** + * The key. + */ + private readonly uint[] m_theK = new uint[NUMWORDS]; + + /** + * The chaining value. + */ + private readonly uint[] m_theChaining = new uint[NUMWORDS]; + + /** + * The state. + */ + private readonly uint[] m_theV = new uint[NUMWORDS << 1]; + + /** + * The message Buffer. + */ + private readonly uint[] m_theM = new uint[NUMWORDS << 1]; + + /** + * The indices. + */ + private readonly byte[] m_theIndices = new byte[NUMWORDS << 1]; + + /** + * The chainingStack. + */ + private readonly List m_theStack = new List(); + + /** + * The default digestLength. + */ + private readonly int m_theDigestLen; + + /** + * Are we outputting? + */ + private bool m_outputting; + + /** + * How many more bytes can we output? + */ + private long m_outputAvailable; + + /** + * The current mode. + */ + private int m_theMode; + + /** + * The output mode. + */ + private int m_theOutputMode; + + /** + * The output dataLen. + */ + private int m_theOutputDataLen; + + /** + * The block counter. + */ + private long m_theCounter; + + /** + * The # of bytes in the current block. + */ + private int m_theCurrBytes; + + /** + * The position of the next byte in the buffer. + */ + private int m_thePos; + + public Blake3Digest() + : this((BLOCKLEN >> 1) * 8) + { + } + + /// the default digest size (in bits) + public Blake3Digest(int pDigestSize) + { + m_theDigestLen = pDigestSize / 8; + + Init(null); + } + + /** + * Constructor. + * + * @param pSource the source digest. + */ + public Blake3Digest(Blake3Digest pSource) + { + /* Copy default digest length */ + m_theDigestLen = pSource.m_theDigestLen; + + /* Initialise from source */ + Reset(pSource); + } + + public int GetByteLength() => BLOCKLEN; + + public string AlgorithmName => "BLAKE3"; + + public int GetDigestSize() => m_theDigestLen; + + /** + * Initialise. + * + * @param pParams the parameters. + */ + public void Init(Blake3Parameters pParams) + { + /* Access key/context */ + byte[] myKey = pParams?.GetKey(); + byte[] myContext = pParams?.GetContext(); + + /* Reset the digest */ + Reset(); + + /* If we have a key */ + if (myKey != null) + { + /* Initialise with the key */ + InitKey(myKey); + Arrays.Fill(myKey, 0); + + /* else if we have a context */ + } + else if (myContext != null) + { + /* Initialise for deriving context */ + InitNullKey(); + m_theMode = DERIVECONTEXT; + + /* Derive key from context */ + BlockUpdate(myContext, 0, myContext.Length); + DoFinal(m_theBuffer, 0); + InitKeyFromContext(); + Reset(); + + /* Else init null key and reset mode */ + } + else + { + InitNullKey(); + m_theMode = 0; + } + } + + public void Update(byte b) + { + /* Check that we are not outputting */ + if (m_outputting) + throw new InvalidOperationException(ERR_OUTPUTTING); + + /* If the buffer is full */ + int blockLen = m_theBuffer.Length; + int remainingLength = blockLen - m_thePos; + if (remainingLength == 0) + { + /* Process the buffer */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + CompressBlock(m_theBuffer); +#else + CompressBlock(m_theBuffer, 0); +#endif + + /* Reset the buffer */ + Arrays.Fill(m_theBuffer, 0); + m_thePos = 0; + } + + /* Store the byte */ + m_theBuffer[m_thePos] = b; + m_thePos++; + } + + public void BlockUpdate(byte[] pMessage, int pOffset, int pLen) + { + /* Ignore null operation */ + if (pMessage == null) + return; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(pMessage.AsSpan(pOffset, pLen)); +#else + if (pLen == 0) + return; + + /* Check that we are not outputting */ + if (m_outputting) + throw new InvalidOperationException(ERR_OUTPUTTING); + + /* Process any bytes currently in the buffer */ + int remainingLen = 0; // left bytes of buffer + if (m_thePos != 0) + { + /* Calculate space remaining in the buffer */ + remainingLen = BLOCKLEN - m_thePos; + + /* If there is sufficient space in the buffer */ + if (remainingLen >= pLen) + { + /* Copy data into buffer and return */ + Array.Copy(pMessage, pOffset, m_theBuffer, m_thePos, pLen); + m_thePos += pLen; + return; + } + + /* Fill the buffer */ + Array.Copy(pMessage, pOffset, m_theBuffer, m_thePos, remainingLen); + + /* Process the buffer */ + CompressBlock(m_theBuffer, 0); + + /* Reset the buffer */ + m_thePos = 0; + Arrays.Fill(m_theBuffer, 0); + } + + /* process all blocks except the last one */ + int messagePos; + int blockWiseLastPos = pOffset + pLen - BLOCKLEN; + for (messagePos = pOffset + remainingLen; messagePos < blockWiseLastPos; messagePos += BLOCKLEN) + { + /* Process the buffer */ + CompressBlock(pMessage, messagePos); + } + + /* Fill the buffer with the remaining bytes of the message */ + int len = pLen - messagePos; + Array.Copy(pMessage, messagePos, m_theBuffer, 0, pOffset + len); + m_thePos += pOffset + len; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan input) + { + if (input.IsEmpty) + return; + + /* Check that we are not outputting */ + if (m_outputting) + throw new InvalidOperationException(ERR_OUTPUTTING); + + int pLen = input.Length; + + /* Process any bytes currently in the buffer */ + int remainingLen = 0; // left bytes of buffer + if (m_thePos != 0) + { + /* Calculate space remaining in the buffer */ + remainingLen = BLOCKLEN - m_thePos; + + /* If there is sufficient space in the buffer */ + if (remainingLen >= pLen) + { + /* Copy data into buffer and return */ + input.CopyTo(m_theBuffer.AsSpan(m_thePos)); + m_thePos += pLen; + return; + } + + /* Fill the buffer */ + input[..remainingLen].CopyTo(m_theBuffer.AsSpan(m_thePos)); + + /* Process the buffer */ + CompressBlock(m_theBuffer); + + /* Reset the buffer */ + m_thePos = 0; + Arrays.Fill(m_theBuffer, 0); + } + + /* process all blocks except the last one */ + int messagePos; + int blockWiseLastPos = pLen - BLOCKLEN; + for (messagePos = remainingLen; messagePos < blockWiseLastPos; messagePos += BLOCKLEN) + { + /* Process the buffer */ + CompressBlock(input[messagePos..]); + } + + /* Fill the buffer with the remaining bytes of the message */ + input[messagePos..].CopyTo(m_theBuffer); + m_thePos += pLen - messagePos; + } +#endif + + public int DoFinal(byte[] pOutput, int pOutOffset) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return OutputFinal(pOutput.AsSpan(pOutOffset, GetDigestSize())); +#else + return DoFinal(pOutput, pOutOffset, GetDigestSize()); +#endif + } + + public int DoFinal(byte[] pOut, int pOutOffset, int pOutLen) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return OutputFinal(pOut.AsSpan(pOutOffset, pOutLen)); +#else + /* Reject if we are already outputting */ + if (m_outputting) + throw new InvalidOperationException(ERR_OUTPUTTING); + + /* Build the required output */ + int length = DoOutput(pOut, pOutOffset, pOutLen); + + /* reset the underlying digest and return the length */ + Reset(); + return length; +#endif + } + + public int DoOutput(byte[] pOut, int pOutOffset, int pOutLen) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Output(pOut.AsSpan(pOutOffset, pOutLen)); +#else + /* If we have not started outputting yet */ + if (!m_outputting) + { + /* Process the buffer */ + CompressFinalBlock(m_thePos); + } + + /* Reject if there is insufficient Xof remaining */ + if (pOutLen < 0 || (m_outputAvailable >= 0 && pOutLen > m_outputAvailable)) + throw new ArgumentException("Insufficient bytes remaining"); + + /* If we have some remaining data in the current buffer */ + int dataLeft = pOutLen; + int outPos = pOutOffset; + if (m_thePos < BLOCKLEN) + { + /* Copy data from current hash */ + int dataToCopy = System.Math.Min(dataLeft, BLOCKLEN - m_thePos); + Array.Copy(m_theBuffer, m_thePos, pOut, outPos, dataToCopy); + + /* Adjust counters */ + m_thePos += dataToCopy; + outPos += dataToCopy; + dataLeft -= dataToCopy; + } + + /* Loop until we have completed the request */ + while (dataLeft > 0) + { + /* Calculate the next block */ + NextOutputBlock(); + + /* Copy data from current hash */ + int dataToCopy = System.Math.Min(dataLeft, BLOCKLEN); + Array.Copy(m_theBuffer, 0, pOut, outPos, dataToCopy); + + /* Adjust counters */ + m_thePos += dataToCopy; + outPos += dataToCopy; + dataLeft -= dataToCopy; + } + + /* Adjust outputAvailable */ + m_outputAvailable -= pOutLen; + + /* Return the number of bytes transferred */ + return pOutLen; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public int OutputFinal(Span output) + { + /* Reject if we are already outputting */ + if (m_outputting) + throw new InvalidOperationException(ERR_OUTPUTTING); + + /* Build the required output */ + int length = Output(output); + + /* reset the underlying digest and return the length */ + Reset(); + return length; + } + + public int Output(Span output) + { + /* If we have not started outputting yet */ + if (!m_outputting) + { + /* Process the buffer */ + CompressFinalBlock(m_thePos); + } + + int pOutOffset = 0, pOutLen = output.Length; + /* Reject if there is insufficient Xof remaining */ + if (pOutLen < 0 || (m_outputAvailable >= 0 && pOutLen > m_outputAvailable)) + throw new ArgumentException("Insufficient bytes remaining"); + + /* If we have some remaining data in the current buffer */ + int dataLeft = pOutLen; + int outPos = pOutOffset; + if (m_thePos < BLOCKLEN) + { + /* Copy data from current hash */ + int dataToCopy = System.Math.Min(dataLeft, BLOCKLEN - m_thePos); + m_theBuffer.AsSpan(m_thePos, dataToCopy).CopyTo(output[outPos..]); + + /* Adjust counters */ + m_thePos += dataToCopy; + outPos += dataToCopy; + dataLeft -= dataToCopy; + } + + /* Loop until we have completed the request */ + while (dataLeft > 0) + { + /* Calculate the next block */ + NextOutputBlock(); + + /* Copy data from current hash */ + int dataToCopy = System.Math.Min(dataLeft, BLOCKLEN); + m_theBuffer.AsSpan(0, dataToCopy).CopyTo(output[outPos..]); + + /* Adjust counters */ + m_thePos += dataToCopy; + outPos += dataToCopy; + dataLeft -= dataToCopy; + } + + /* Adjust outputAvailable */ + m_outputAvailable -= pOutLen; + + /* Return the number of bytes transferred */ + return pOutLen; + } +#endif + + public void Reset() + { + ResetBlockCount(); + m_thePos = 0; + m_outputting = false; + Arrays.Fill(m_theBuffer, 0); + } + + public void Reset(IMemoable pSource) + { + /* Access source */ + Blake3Digest mySource = (Blake3Digest)pSource; + + /* Reset counter */ + m_theCounter = mySource.m_theCounter; + m_theCurrBytes = mySource.m_theCurrBytes; + m_theMode = mySource.m_theMode; + + /* Reset output state */ + m_outputting = mySource.m_outputting; + m_outputAvailable = mySource.m_outputAvailable; + m_theOutputMode = mySource.m_theOutputMode; + m_theOutputDataLen = mySource.m_theOutputDataLen; + + /* Copy state */ + Array.Copy(mySource.m_theChaining, 0, m_theChaining, 0, m_theChaining.Length); + Array.Copy(mySource.m_theK, 0, m_theK, 0, m_theK.Length); + Array.Copy(mySource.m_theM, 0, m_theM, 0, m_theM.Length); + + /* Copy stack */ + m_theStack.Clear(); + foreach (var element in mySource.m_theStack) + { + m_theStack.Add(Arrays.Clone(element)); + } + + /* Copy buffer */ + Array.Copy(mySource.m_theBuffer, 0, m_theBuffer, 0, m_theBuffer.Length); + m_thePos = mySource.m_thePos; + } + + public IMemoable Copy() + { + return new Blake3Digest(this); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void CompressBlock(ReadOnlySpan block) + { + /* Initialise state and compress message */ + InitChunkBlock(BLOCKLEN, false); + InitM(block); + Compress(); + + /* Adjust stack if we have completed a block */ + if (m_theCurrBytes == 0) + { + AdjustStack(); + } + } + + private void InitM(ReadOnlySpan block) + { + /* Copy message bytes into word array */ + Pack.LE_To_UInt32(block, m_theM); + } +#else + /** + * Compress next block of the message. + * + * @param pMessage the message buffer + * @param pMsgPos the position within the message buffer + */ + private void CompressBlock(byte[] pMessage, int pMsgPos) + { + /* Initialise state and compress message */ + InitChunkBlock(BLOCKLEN, false); + InitM(pMessage, pMsgPos); + Compress(); + + /* Adjust stack if we have completed a block */ + if (m_theCurrBytes == 0) + { + AdjustStack(); + } + } + + /** + * Initialise M from message. + * + * @param pMessage the source message + * @param pMsgPos the message position + */ + private void InitM(byte[] pMessage, int pMsgPos) + { + /* Copy message bytes into word array */ + Pack.LE_To_UInt32(pMessage, pMsgPos, m_theM); + } +#endif + + /** + * Adjust the stack. + */ + private void AdjustStack() + { + /* Loop to combine blocks */ + long myCount = m_theCounter; + while (myCount > 0) + { + /* Break loop if we are not combining */ + if ((myCount & 1) == 1) + break; + + /* Build the message to be hashed */ + uint[] myLeft = m_theStack[m_theStack.Count - 1]; + m_theStack.RemoveAt(m_theStack.Count - 1); + + Array.Copy(myLeft, 0, m_theM, 0, NUMWORDS); + Array.Copy(m_theChaining, 0, m_theM, NUMWORDS, NUMWORDS); + + /* Create parent block */ + InitParentBlock(); + Compress(); + + /* Next block */ + myCount >>= 1; + } + + /* Add back to the stack */ + m_theStack.Add(Arrays.CopyOf(m_theChaining, NUMWORDS)); + } + + /** + * Compress final block. + * + * @param pDataLen the data length + */ + private void CompressFinalBlock(int pDataLen) + { + /* Initialise state and compress message */ + InitChunkBlock(pDataLen, true); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + InitM(m_theBuffer); +#else + InitM(m_theBuffer, 0); +#endif + Compress(); + + /* Finalise stack */ + ProcessStack(); + } + + /** + * Process the stack. + */ + private void ProcessStack() + { + /* Finalise stack */ + while (m_theStack.Count > 0) + { + /* Build the message to be hashed */ + uint[] myLeft = m_theStack[m_theStack.Count - 1]; + m_theStack.RemoveAt(m_theStack.Count - 1); + + Array.Copy(myLeft, 0, m_theM, 0, NUMWORDS); + Array.Copy(m_theChaining, 0, m_theM, NUMWORDS, NUMWORDS); + + /* Create parent block */ + InitParentBlock(); + if (m_theStack.Count < 1) + { + SetRoot(); + } + Compress(); + } + } + + /** + * Perform compression. + */ + private void Compress() + { + /* Initialise the buffers */ + InitIndices(); + + /* Loop through the rounds */ + for (int round = 0; round < ROUNDS - 1; round++) + { + /* Perform the round and permuteM */ + PerformRound(); + PermuteIndices(); + } + PerformRound(); + AdjustChaining(); + } + + /** + * Perform a round. + */ +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + private void PerformRound() + { + /* Apply to columns of V */ + MixG(0, CHAINING0, CHAINING4, IV0, COUNT0); + MixG(1, CHAINING1, CHAINING5, IV1, COUNT1); + MixG(2, CHAINING2, CHAINING6, IV2, DATALEN); + MixG(3, CHAINING3, CHAINING7, IV3, FLAGS); + + /* Apply to diagonals of V */ + MixG(4, CHAINING0, CHAINING5, IV2, FLAGS); + MixG(5, CHAINING1, CHAINING6, IV3, COUNT0); + MixG(6, CHAINING2, CHAINING7, IV0, COUNT1); + MixG(7, CHAINING3, CHAINING4, IV1, DATALEN); + } + + /** + * Adjust Chaining after compression. + */ + private void AdjustChaining() + { + /* If we are outputting */ + if (m_outputting) + { + /* Adjust full state */ + for (int i = 0; i < NUMWORDS; i++) + { + m_theV[i] ^= m_theV[i + NUMWORDS]; + m_theV[i + NUMWORDS] ^= m_theChaining[i]; + } + + /* Output state to buffer */ + Pack.UInt32_To_LE(m_theV, m_theBuffer, 0); + m_thePos = 0; + + /* Else just build chain value */ + } + else + { + /* Combine V into Chaining */ + for (int i = 0; i < NUMWORDS; i++) + { + m_theChaining[i] = m_theV[i] ^ m_theV[i + NUMWORDS]; + } + } + } + + /** + * Mix function G. + * + * @param msgIdx the message index + * @param posA position A in V + * @param posB position B in V + * @param posC position C in V + * @param posD poistion D in V + */ +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + private void MixG(int msgIdx, int posA, int posB, int posC, int posD) + { + /* Determine indices */ + int msg = msgIdx << 1; + + /* Perform the Round */ + m_theV[posA] += m_theV[posB] + m_theM[m_theIndices[msg++]]; + m_theV[posD] = Integers.RotateRight(m_theV[posD] ^ m_theV[posA], 16); + m_theV[posC] += m_theV[posD]; + m_theV[posB] = Integers.RotateRight(m_theV[posB] ^ m_theV[posC], 12); + m_theV[posA] += m_theV[posB] + m_theM[m_theIndices[msg]]; + m_theV[posD] = Integers.RotateRight(m_theV[posD] ^ m_theV[posA], 8); + m_theV[posC] += m_theV[posD]; + m_theV[posB] = Integers.RotateRight(m_theV[posB] ^ m_theV[posC], 7); + } + + /** + * initialise the indices. + */ + private void InitIndices() + { + for (byte i = 0; i < m_theIndices.Length; i++) + { + m_theIndices[i] = i; + } + } + + /** + * PermuteIndices. + */ + private void PermuteIndices() + { + for (byte i = 0; i < m_theIndices.Length; i++) + { + m_theIndices[i] = SIGMA[m_theIndices[i]]; + } + } + + /** + * Initialise null key. + */ + private void InitNullKey() + { + Array.Copy(IV, 0, m_theK, 0, NUMWORDS); + } + + /** + * Initialise key. + * + * @param pKey the keyBytes + */ + private void InitKey(byte[] pKey) + { + /* Copy message bytes into word array */ + Pack.LE_To_UInt32(pKey, 0, m_theK); + m_theMode = KEYEDHASH; + } + + /** + * Initialise key from context. + */ + private void InitKeyFromContext() + { + Array.Copy(m_theV, 0, m_theK, 0, NUMWORDS); + m_theMode = DERIVEKEY; + } + + /** + * Initialise chunk block. + * + * @param pDataLen the dataLength + * @param pFinal is this the final chunk? + */ + private void InitChunkBlock(int pDataLen, bool pFinal) + { + /* Initialise the block */ + Array.Copy(m_theCurrBytes == 0 ? m_theK : m_theChaining, 0, m_theV, 0, NUMWORDS); + Array.Copy(IV, 0, m_theV, NUMWORDS, NUMWORDS >> 1); + m_theV[COUNT0] = (uint)m_theCounter; + m_theV[COUNT1] = (uint)(m_theCounter >> Integers.NumBits); + m_theV[DATALEN] = (uint)pDataLen; + m_theV[FLAGS] = (uint)(m_theMode + + (m_theCurrBytes == 0 ? CHUNKSTART : 0) + + (pFinal ? CHUNKEND : 0)); + + /* * Adjust block count */ + m_theCurrBytes += pDataLen; + if (m_theCurrBytes >= CHUNKLEN) + { + IncrementBlockCount(); + m_theV[FLAGS] |= CHUNKEND; + } + + /* If we are single chunk */ + if (pFinal && m_theStack.Count < 1) + { + SetRoot(); + } + } + + /** + * Initialise parent block. + */ + private void InitParentBlock() + { + /* Initialise the block */ + Array.Copy(m_theK, 0, m_theV, 0, NUMWORDS); + Array.Copy(IV, 0, m_theV, NUMWORDS, NUMWORDS >> 1); + m_theV[COUNT0] = 0; + m_theV[COUNT1] = 0; + m_theV[DATALEN] = BLOCKLEN; + m_theV[FLAGS] = (uint)(m_theMode | PARENT); + } + + /** + * Initialise output block. + */ + private void NextOutputBlock() + { + /* Increment the counter */ + m_theCounter++; + + /* Initialise the block */ + Array.Copy(m_theChaining, 0, m_theV, 0, NUMWORDS); + Array.Copy(IV, 0, m_theV, NUMWORDS, NUMWORDS >> 1); + m_theV[COUNT0] = (uint)m_theCounter; + m_theV[COUNT1] = (uint)(m_theCounter >> Integers.NumBits); + m_theV[DATALEN] = (uint)m_theOutputDataLen; + m_theV[FLAGS] = (uint)m_theOutputMode; + + /* Generate output */ + Compress(); + } + + /** + * IncrementBlockCount. + */ + private void IncrementBlockCount() + { + m_theCounter++; + m_theCurrBytes = 0; + } + + /** + * ResetBlockCount. + */ + private void ResetBlockCount() + { + m_theCounter = 0; + m_theCurrBytes = 0; + } + + /** + * Set root indication. + */ + private void SetRoot() + { + m_theV[FLAGS] |= ROOT; + m_theOutputMode = (int)m_theV[FLAGS]; + m_theOutputDataLen = (int)m_theV[DATALEN]; + m_theCounter = 0; + m_outputting = true; + m_outputAvailable = -1; + Array.Copy(m_theV, 0, m_theChaining, 0, NUMWORDS); + } + } +} diff --git a/crypto/src/crypto/parameters/Blake3Parameters.cs b/crypto/src/crypto/parameters/Blake3Parameters.cs new file mode 100644 index 000000000..03aed1ede --- /dev/null +++ b/crypto/src/crypto/parameters/Blake3Parameters.cs @@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /// Blake3 Parameters. + public sealed class Blake3Parameters + : ICipherParameters + { + private const int KeyLen = 32; + + private byte[] m_theKey; + private byte[] m_theContext; + + /// Create a key parameter. + /// the context + /// the parameter + public static Blake3Parameters Context(byte[] pContext) + { + if (pContext == null) + throw new ArgumentNullException(nameof(pContext)); + + Blake3Parameters myParams = new Blake3Parameters(); + myParams.m_theContext = Arrays.Clone(pContext); + return myParams; + } + + /// Create a key parameter. + /// the key + /// the parameter + public static Blake3Parameters Key(byte[] pKey) + { + if (pKey == null) + throw new ArgumentNullException(nameof(pKey)); + if (pKey.Length != KeyLen) + throw new ArgumentException("Invalid key length", nameof(pKey)); + + Blake3Parameters myParams = new Blake3Parameters(); + myParams.m_theKey = Arrays.Clone(pKey); + return myParams; + } + + /// Obtain the key. + /// the key + public byte[] GetKey() => Arrays.Clone(m_theKey); + + /// Clear the key bytes. + public void ClearKey() + { + Arrays.Fill(m_theKey, 0); + } + + /// Obtain the salt. + /// the salt + public byte[] GetContext() => Arrays.Clone(m_theContext); + } +} diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs index 035280fd6..3f3036c8c 100644 --- a/crypto/src/security/DigestUtilities.cs +++ b/crypto/src/security/DigestUtilities.cs @@ -26,6 +26,7 @@ namespace Org.BouncyCastle.Security private enum DigestAlgorithm { BLAKE2B_160, BLAKE2B_256, BLAKE2B_384, BLAKE2B_512, BLAKE2S_128, BLAKE2S_160, BLAKE2S_224, BLAKE2S_256, + BLAKE3_256, DSTU7564_256, DSTU7564_384, DSTU7564_512, GOST3411, GOST3411_2012_256, GOST3411_2012_512, @@ -122,6 +123,7 @@ namespace Org.BouncyCastle.Security Aliases[MiscObjectIdentifiers.id_blake2s160.Id] = "BLAKE2S-160"; Aliases[MiscObjectIdentifiers.id_blake2s224.Id] = "BLAKE2S-224"; Aliases[MiscObjectIdentifiers.id_blake2s256.Id] = "BLAKE2S-256"; + Aliases[MiscObjectIdentifiers.blake3_256.Id] = "BLAKE3-256"; Aliases[RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id] = "GOST3411-2012-256"; Aliases[RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id] = "GOST3411-2012-512"; @@ -159,6 +161,7 @@ namespace Org.BouncyCastle.Security Oids["BLAKE2S-160"] = MiscObjectIdentifiers.id_blake2s160; Oids["BLAKE2S-224"] = MiscObjectIdentifiers.id_blake2s224; Oids["BLAKE2S-256"] = MiscObjectIdentifiers.id_blake2s256; + Oids["BLAKE3-256"] = MiscObjectIdentifiers.blake3_256; Oids["GOST3411-2012-256"] = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256; Oids["GOST3411-2012-512"] = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512; Oids["DSTU7564-256"] = UAObjectIdentifiers.dstu7564digest_256; @@ -209,6 +212,7 @@ namespace Org.BouncyCastle.Security case DigestAlgorithm.BLAKE2S_160: return new Blake2sDigest(160); case DigestAlgorithm.BLAKE2S_224: return new Blake2sDigest(224); case DigestAlgorithm.BLAKE2S_256: return new Blake2sDigest(256); + case DigestAlgorithm.BLAKE3_256: return new Blake3Digest(256); case DigestAlgorithm.DSTU7564_256: return new Dstu7564Digest(256); case DigestAlgorithm.DSTU7564_384: return new Dstu7564Digest(384); case DigestAlgorithm.DSTU7564_512: return new Dstu7564Digest(512); -- cgit 1.4.1