diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs
index ec80a3355..953ac0062 100644
--- a/crypto/src/crypto/digests/Blake2bDigest.cs
+++ b/crypto/src/crypto/digests/Blake2bDigest.cs
@@ -1,4 +1,7 @@
using System;
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
@@ -39,7 +42,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* BLAKE2b is optimized for 64-bit platforms and produces digests of any size
* between 1 and 64 bytes.
*/
- public class Blake2bDigest
+ public sealed class Blake2bDigest
: IDigest
{
// Blake2b Initialization Vector:
@@ -276,12 +279,10 @@ namespace Org.BouncyCastle.Crypto.Digests
*
* @param b the input byte to be entered.
*/
- public virtual void Update(byte b)
+ public void Update(byte b)
{
- int remainingLength = 0; // left bytes of buffer
-
// process the buffer if full else add to buffer:
- remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ int remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
if (remainingLength == 0)
{ // full buffer
t0 += BLOCK_LENGTH_BYTES;
@@ -289,7 +290,11 @@ namespace Org.BouncyCastle.Crypto.Digests
{ // if message > 2^64
t1++;
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Compress(buffer);
+#else
Compress(buffer, 0);
+#endif
Array.Clear(buffer, 0, buffer.Length);// clear buffer
buffer[0] = b;
bufferPos = 1;
@@ -298,7 +303,6 @@ namespace Org.BouncyCastle.Crypto.Digests
{
buffer[bufferPos] = b;
bufferPos++;
- return;
}
}
@@ -309,11 +313,14 @@ namespace Org.BouncyCastle.Crypto.Digests
* @param offset the offset into the byte array where the data starts.
* @param len the length of the data.
*/
- public virtual void BlockUpdate(byte[] message, int offset, int len)
+ public void BlockUpdate(byte[] message, int offset, int len)
{
if (message == null || len == 0)
return;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ BlockUpdate(message.AsSpan(offset, len));
+#else
int remainingLength = 0; // left bytes of buffer
if (bufferPos != 0)
@@ -323,8 +330,7 @@ namespace Org.BouncyCastle.Crypto.Digests
remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
if (remainingLength < len)
{ // full buffer + at least 1 byte
- Array.Copy(message, offset, buffer, bufferPos,
- remainingLength);
+ Array.Copy(message, offset, buffer, bufferPos, remainingLength);
t0 += BLOCK_LENGTH_BYTES;
if (t0 == 0)
{ // if message > 2^64
@@ -359,10 +365,11 @@ namespace Org.BouncyCastle.Crypto.Digests
// fill the buffer with left bytes, this might be a full block
Array.Copy(message, messagePos, buffer, 0, offset + len - messagePos);
bufferPos += offset + len - messagePos;
+#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+ public void BlockUpdate(ReadOnlySpan<byte> input)
{
if (input.IsEmpty)
return;
@@ -382,7 +389,7 @@ namespace Org.BouncyCastle.Crypto.Digests
{ // if message > 2^64
t1++;
}
- Compress(buffer, 0);
+ Compress(buffer);
bufferPos = 0;
Array.Clear(buffer, 0, buffer.Length);// clear buffer
}
@@ -422,8 +429,11 @@ namespace Org.BouncyCastle.Crypto.Digests
* @param out the array the digest is to be copied into.
* @param outOffset the offset into the out array the digest is to start at.
*/
- public virtual int DoFinal(byte[] output, int outOffset)
+ public int DoFinal(byte[] output, int outOffset)
{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return DoFinal(output.AsSpan(outOffset));
+#else
f0 = 0xFFFFFFFFFFFFFFFFUL;
t0 += (ulong)bufferPos;
if (bufferPos > 0 && t0 == 0)
@@ -448,10 +458,11 @@ namespace Org.BouncyCastle.Crypto.Digests
Reset();
return digestLength;
+#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public virtual int DoFinal(Span<byte> output)
+ public int DoFinal(Span<byte> output)
{
f0 = 0xFFFFFFFFFFFFFFFFUL;
t0 += (ulong)bufferPos;
@@ -459,7 +470,7 @@ namespace Org.BouncyCastle.Crypto.Digests
{
t1++;
}
- Compress(buffer, 0);
+ Compress(buffer);
Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null
Array.Clear(internalState, 0, internalState.Length);
@@ -485,7 +496,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* The key, the salt and the personal string will
* remain for further computations.
*/
- public virtual void Reset()
+ public void Reset()
{
bufferPos = 0;
f0 = 0L;
@@ -501,25 +512,26 @@ namespace Org.BouncyCastle.Crypto.Digests
Init();
}
- private void Compress(byte[] message, int messagePos)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void Compress(ReadOnlySpan<byte> message)
{
InitializeInternalState();
- ulong[] m = new ulong[16];
- Pack.LE_To_UInt64(message, messagePos, m);
+ Span<ulong> m = stackalloc ulong[16];
+ Pack.LE_To_UInt64(message, m);
for (int round = 0; round < ROUNDS; round++)
{
// G apply to columns of internalState:m[blake2b_sigma[round][2 * blockPos]] /+1
- G(m[blake2b_sigma[round,0]], m[blake2b_sigma[round,1]], 0, 4, 8, 12);
- G(m[blake2b_sigma[round,2]], m[blake2b_sigma[round,3]], 1, 5, 9, 13);
- G(m[blake2b_sigma[round,4]], m[blake2b_sigma[round,5]], 2, 6, 10, 14);
- G(m[blake2b_sigma[round,6]], m[blake2b_sigma[round,7]], 3, 7, 11, 15);
+ G(m[blake2b_sigma[round, 0]], m[blake2b_sigma[round, 1]], 0, 4, 8, 12);
+ G(m[blake2b_sigma[round, 2]], m[blake2b_sigma[round, 3]], 1, 5, 9, 13);
+ G(m[blake2b_sigma[round, 4]], m[blake2b_sigma[round, 5]], 2, 6, 10, 14);
+ G(m[blake2b_sigma[round, 6]], m[blake2b_sigma[round, 7]], 3, 7, 11, 15);
// G apply to diagonals of internalState:
- G(m[blake2b_sigma[round,8]], m[blake2b_sigma[round,9]], 0, 5, 10, 15);
- G(m[blake2b_sigma[round,10]], m[blake2b_sigma[round,11]], 1, 6, 11, 12);
- G(m[blake2b_sigma[round,12]], m[blake2b_sigma[round,13]], 2, 7, 8, 13);
- G(m[blake2b_sigma[round,14]], m[blake2b_sigma[round,15]], 3, 4, 9, 14);
+ G(m[blake2b_sigma[round, 8]], m[blake2b_sigma[round, 9]], 0, 5, 10, 15);
+ G(m[blake2b_sigma[round, 10]], m[blake2b_sigma[round, 11]], 1, 6, 11, 12);
+ G(m[blake2b_sigma[round, 12]], m[blake2b_sigma[round, 13]], 2, 7, 8, 13);
+ G(m[blake2b_sigma[round, 14]], m[blake2b_sigma[round, 15]], 3, 4, 9, 14);
}
// update chain values:
@@ -528,27 +540,26 @@ namespace Org.BouncyCastle.Crypto.Digests
chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8];
}
}
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- private void Compress(ReadOnlySpan<byte> message)
+#else
+ private void Compress(byte[] message, int messagePos)
{
InitializeInternalState();
- Span<ulong> m = stackalloc ulong[16];
- Pack.LE_To_UInt64(message, m);
+ ulong[] m = new ulong[16];
+ Pack.LE_To_UInt64(message, messagePos, m);
for (int round = 0; round < ROUNDS; round++)
{
// G apply to columns of internalState:m[blake2b_sigma[round][2 * blockPos]] /+1
- G(m[blake2b_sigma[round, 0]], m[blake2b_sigma[round, 1]], 0, 4, 8, 12);
- G(m[blake2b_sigma[round, 2]], m[blake2b_sigma[round, 3]], 1, 5, 9, 13);
- G(m[blake2b_sigma[round, 4]], m[blake2b_sigma[round, 5]], 2, 6, 10, 14);
- G(m[blake2b_sigma[round, 6]], m[blake2b_sigma[round, 7]], 3, 7, 11, 15);
+ G(m[blake2b_sigma[round,0]], m[blake2b_sigma[round,1]], 0, 4, 8, 12);
+ G(m[blake2b_sigma[round,2]], m[blake2b_sigma[round,3]], 1, 5, 9, 13);
+ G(m[blake2b_sigma[round,4]], m[blake2b_sigma[round,5]], 2, 6, 10, 14);
+ G(m[blake2b_sigma[round,6]], m[blake2b_sigma[round,7]], 3, 7, 11, 15);
// G apply to diagonals of internalState:
- G(m[blake2b_sigma[round, 8]], m[blake2b_sigma[round, 9]], 0, 5, 10, 15);
- G(m[blake2b_sigma[round, 10]], m[blake2b_sigma[round, 11]], 1, 6, 11, 12);
- G(m[blake2b_sigma[round, 12]], m[blake2b_sigma[round, 13]], 2, 7, 8, 13);
- G(m[blake2b_sigma[round, 14]], m[blake2b_sigma[round, 15]], 3, 4, 9, 14);
+ G(m[blake2b_sigma[round,8]], m[blake2b_sigma[round,9]], 0, 5, 10, 15);
+ G(m[blake2b_sigma[round,10]], m[blake2b_sigma[round,11]], 1, 6, 11, 12);
+ G(m[blake2b_sigma[round,12]], m[blake2b_sigma[round,13]], 2, 7, 8, 13);
+ G(m[blake2b_sigma[round,14]], m[blake2b_sigma[round,15]], 3, 4, 9, 14);
}
// update chain values:
@@ -559,6 +570,9 @@ namespace Org.BouncyCastle.Crypto.Digests
}
#endif
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
private void G(ulong m1, ulong m2, int posA, int posB, int posC, int posD)
{
internalState[posA] = internalState[posA] + internalState[posB] + m1;
@@ -581,17 +595,14 @@ namespace Org.BouncyCastle.Crypto.Digests
*
* @return the algorithm name
*/
- public virtual string AlgorithmName
- {
- get { return "BLAKE2b"; }
- }
+ public string AlgorithmName => "BLAKE2b";
/**
* return the size, in bytes, of the digest produced by this message digest.
*
* @return the size, in bytes, of the digest produced by this message digest.
*/
- public virtual int GetDigestSize()
+ public int GetDigestSize()
{
return digestLength;
}
@@ -602,7 +613,7 @@ namespace Org.BouncyCastle.Crypto.Digests
*
* @return byte length of the digests internal buffer.
*/
- public virtual int GetByteLength()
+ public int GetByteLength()
{
return BLOCK_LENGTH_BYTES;
}
@@ -611,7 +622,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* Overwrite the key
* if it is no longer used (zeroization)
*/
- public virtual void ClearKey()
+ public void ClearKey()
{
if (key != null)
{
@@ -624,7 +635,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* Overwrite the salt (pepper) if it
* is secret and no longer used (zeroization)
*/
- public virtual void ClearSalt()
+ public void ClearSalt()
{
if (salt != null)
{
diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs
index 187808aa0..a6ee75af5 100644
--- a/crypto/src/crypto/digests/Blake2sDigest.cs
+++ b/crypto/src/crypto/digests/Blake2sDigest.cs
@@ -1,4 +1,7 @@
using System;
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
@@ -40,7 +43,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* BLAKE2s is optimized for 32-bit platforms and produces digests of any size
* between 1 and 32 bytes.
*/
- public class Blake2sDigest
+ public sealed class Blake2sDigest
: IDigest
{
/**
@@ -83,16 +86,13 @@ namespace Org.BouncyCastle.Crypto.Digests
private byte[] key = null;
// Tree hashing parameters:
- // Because this class does not implement the Tree Hashing Mode,
- // these parameters can be treated as constants (see Init() function)
- /*
- * private int fanout = 1; // 0-255
- * private int depth = 1; // 1 - 255
- * private int leafLength= 0;
- * private long nodeOffset = 0L;
- * private int nodeDepth = 0;
- * private int innerHashLength = 0;
- */
+ // The Tree Hashing Mode is not supported but these are used for the XOF implementation
+ private int fanout = 1; // 0-255
+ private int depth = 1; // 0-255
+ private int leafLength = 0;
+ private long nodeOffset = 0L;
+ private int nodeDepth = 0;
+ private int innerHashLength = 0;
/**
* Whenever this buffer overflows, it will be processed in the Compress()
@@ -145,8 +145,19 @@ namespace Org.BouncyCastle.Crypto.Digests
this.keyLength = digest.keyLength;
this.key = Arrays.Clone(digest.key);
this.digestLength = digest.digestLength;
+ this.internalState = Arrays.Clone(digest.internalState);
this.chainValue = Arrays.Clone(digest.chainValue);
+ this.t0 = digest.t0;
+ this.t1 = digest.t1;
+ this.f0 = digest.f0;
+ this.salt = Arrays.Clone(digest.salt);
this.personalization = Arrays.Clone(digest.personalization);
+ this.fanout = digest.fanout;
+ this.depth = digest.depth;
+ this.leafLength = digest.leafLength;
+ this.nodeOffset = digest.nodeOffset;
+ this.nodeDepth = digest.nodeDepth;
+ this.innerHashLength = digest.innerHashLength;
}
/**
@@ -159,10 +170,9 @@ namespace Org.BouncyCastle.Crypto.Digests
if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0)
throw new ArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256");
- buffer = new byte[BLOCK_LENGTH_BYTES];
- keyLength = 0;
digestLength = digestBits / 8;
- Init();
+
+ Init(null, null, null);
}
/**
@@ -176,21 +186,7 @@ namespace Org.BouncyCastle.Crypto.Digests
*/
public Blake2sDigest(byte[] key)
{
- buffer = new byte[BLOCK_LENGTH_BYTES];
- if (key != null)
- {
- if (key.Length > 32)
- throw new ArgumentException("Keys > 32 are not supported");
-
- this.key = new byte[key.Length];
- Array.Copy(key, 0, this.key, 0, key.Length);
-
- keyLength = key.Length;
- Array.Copy(key, 0, buffer, 0, key.Length);
- bufferPos = BLOCK_LENGTH_BYTES; // zero padding
- }
- digestLength = 32;
- Init();
+ Init(null, null, key);
}
/**
@@ -206,65 +202,79 @@ namespace Org.BouncyCastle.Crypto.Digests
* @param salt 8 bytes or null
* @param personalization 8 bytes or null
*/
- public Blake2sDigest(byte[] key, int digestBytes, byte[] salt,
- byte[] personalization)
+ public Blake2sDigest(byte[] key, int digestBytes, byte[] salt, byte[] personalization)
{
if (digestBytes < 1 || digestBytes > 32)
throw new ArgumentException("Invalid digest length (required: 1 - 32)");
this.digestLength = digestBytes;
- this.buffer = new byte[BLOCK_LENGTH_BYTES];
- if (salt != null)
- {
- if (salt.Length != 8)
- throw new ArgumentException("Salt length must be exactly 8 bytes");
+ Init(salt, personalization, key);
+ }
- this.salt = new byte[8];
- Array.Copy(salt, 0, this.salt, 0, salt.Length);
- }
- if (personalization != null)
- {
- if (personalization.Length != 8)
- throw new ArgumentException("Personalization length must be exactly 8 bytes");
+ // XOF root hash parameters
+ internal Blake2sDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization, long offset)
+ {
+ digestLength = digestBytes;
+ nodeOffset = offset;
- this.personalization = new byte[8];
- Array.Copy(personalization, 0, this.personalization, 0, personalization.Length);
- }
- if (key != null)
- {
- if (key.Length > 32)
- throw new ArgumentException("Keys > 32 bytes are not supported");
+ Init(salt, personalization, key);
+ }
- this.key = new byte[key.Length];
- Array.Copy(key, 0, this.key, 0, key.Length);
+ // XOF internal hash parameters
+ internal Blake2sDigest(int digestBytes, int hashLength, long offset)
+ {
+ digestLength = digestBytes;
+ nodeOffset = offset;
+ fanout = 0;
+ depth = 0;
+ leafLength = hashLength;
+ innerHashLength = hashLength;
+ nodeDepth = 0;
+
+ Init(null, null, null);
+ }
+ // initialize the digest's parameters
+ private void Init(byte[] salt, byte[] personalization, byte[] key)
+ {
+ buffer = new byte[BLOCK_LENGTH_BYTES];
+
+ if (key != null && key.Length > 0)
+ {
keyLength = key.Length;
- Array.Copy(key, 0, buffer, 0, key.Length);
+ if (keyLength > 32)
+ throw new ArgumentException("Keys > 32 bytes are not supported");
+
+ this.key = new byte[keyLength];
+ Array.Copy(key, 0, this.key, 0, keyLength);
+ Array.Copy(key, 0, buffer, 0, keyLength);
bufferPos = BLOCK_LENGTH_BYTES; // zero padding
}
- Init();
- }
- // initialize chainValue
- private void Init()
- {
if (chainValue == null)
{
chainValue = new uint[8];
- chainValue[0] = blake2s_IV[0] ^ (uint)(digestLength | (keyLength << 8) | 0x1010000);
- // 0x1010000 = ((fanout << 16) | (depth << 24));
- // with fanout = 1; depth = 0;
- chainValue[1] = blake2s_IV[1];// ^ leafLength; with leafLength = 0;
- chainValue[2] = blake2s_IV[2];// ^ nodeOffset; with nodeOffset = 0;
- chainValue[3] = blake2s_IV[3];// ^ ( (nodeOffset << 32) | (nodeDepth << 16) | (innerHashLength << 24) );
- // with nodeDepth = 0; innerHashLength = 0;
+ chainValue[0] = blake2s_IV[0]
+ ^ (uint)(digestLength | (keyLength << 8) | ((fanout << 16) | (depth << 24)));
+ chainValue[1] = blake2s_IV[1] ^ (uint)leafLength;
+
+ int nofHi = (int)(nodeOffset >> 32);
+ int nofLo = (int)nodeOffset;
+ chainValue[2] = blake2s_IV[2] ^ (uint)nofLo;
+ chainValue[3] = blake2s_IV[3] ^ (uint)(nofHi | (nodeDepth << 16) | (innerHashLength << 24));
chainValue[4] = blake2s_IV[4];
chainValue[5] = blake2s_IV[5];
if (salt != null)
{
+ if (salt.Length != 8)
+ throw new ArgumentException("Salt length must be exactly 8 bytes");
+
+ this.salt = new byte[8];
+ Array.Copy(salt, 0, this.salt, 0, salt.Length);
+
chainValue[4] ^= Pack.LE_To_UInt32(salt, 0);
chainValue[5] ^= Pack.LE_To_UInt32(salt, 4);
}
@@ -273,6 +283,12 @@ namespace Org.BouncyCastle.Crypto.Digests
chainValue[7] = blake2s_IV[7];
if (personalization != null)
{
+ if (personalization.Length != 8)
+ throw new ArgumentException("Personalization length must be exactly 8 bytes");
+
+ this.personalization = new byte[8];
+ Array.Copy(personalization, 0, this.personalization, 0, personalization.Length);
+
chainValue[6] ^= Pack.LE_To_UInt32(personalization, 0);
chainValue[7] ^= Pack.LE_To_UInt32(personalization, 4);
}
@@ -295,12 +311,10 @@ namespace Org.BouncyCastle.Crypto.Digests
*
* @param b the input byte to be entered.
*/
- public virtual void Update(byte b)
+ public void Update(byte b)
{
- int remainingLength; // left bytes of buffer
-
// process the buffer if full else add to buffer:
- remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+ int remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
if (remainingLength == 0)
{ // full buffer
t0 += BLOCK_LENGTH_BYTES;
@@ -308,7 +322,11 @@ namespace Org.BouncyCastle.Crypto.Digests
{ // if message > 2^32
t1++;
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Compress(buffer);
+#else
Compress(buffer, 0);
+#endif
Array.Clear(buffer, 0, buffer.Length);// clear buffer
buffer[0] = b;
bufferPos = 1;
@@ -327,11 +345,14 @@ namespace Org.BouncyCastle.Crypto.Digests
* @param offset the offset into the byte array where the data starts.
* @param len the length of the data.
*/
- public virtual void BlockUpdate(byte[] message, int offset, int len)
+ public void BlockUpdate(byte[] message, int offset, int len)
{
if (message == null || len == 0)
return;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ BlockUpdate(message.AsSpan(offset, len));
+#else
int remainingLength = 0; // left bytes of buffer
if (bufferPos != 0)
@@ -379,10 +400,11 @@ namespace Org.BouncyCastle.Crypto.Digests
Array.Copy(message, messagePos, buffer, 0, offset + len
- messagePos);
bufferPos += offset + len - messagePos;
+#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+ public void BlockUpdate(ReadOnlySpan<byte> input)
{
if (input.IsEmpty)
return;
@@ -402,7 +424,7 @@ namespace Org.BouncyCastle.Crypto.Digests
{ // if message > 2^32
t1++;
}
- Compress(buffer, 0);
+ Compress(buffer);
bufferPos = 0;
Array.Clear(buffer, 0, buffer.Length);// clear buffer
}
@@ -443,8 +465,11 @@ namespace Org.BouncyCastle.Crypto.Digests
* @param out the array the digest is to be copied into.
* @param outOffset the offset into the out array the digest is to start at.
*/
- public virtual int DoFinal(byte[] output, int outOffset)
+ public int DoFinal(byte[] output, int outOffset)
{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return DoFinal(output.AsSpan(outOffset));
+#else
f0 = 0xFFFFFFFFU;
t0 += (uint)bufferPos;
// bufferPos may be < 64, so (t0 == 0) does not work
@@ -471,10 +496,11 @@ namespace Org.BouncyCastle.Crypto.Digests
Reset();
return digestLength;
+#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public virtual int DoFinal(Span<byte> output)
+ public int DoFinal(Span<byte> output)
{
f0 = 0xFFFFFFFFU;
t0 += (uint)bufferPos;
@@ -484,7 +510,7 @@ namespace Org.BouncyCastle.Crypto.Digests
{
t1++;
}
- Compress(buffer, 0);
+ Compress(buffer);
Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null
Array.Clear(internalState, 0, internalState.Length);
@@ -509,7 +535,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* Reset the digest back to its initial state. The key, the salt and the
* personal string will remain for further computations.
*/
- public virtual void Reset()
+ public void Reset()
{
bufferPos = 0;
f0 = 0;
@@ -522,28 +548,30 @@ namespace Org.BouncyCastle.Crypto.Digests
Array.Copy(key, 0, buffer, 0, key.Length);
bufferPos = BLOCK_LENGTH_BYTES; // zero padding
}
- Init();
+
+ Init(this.salt, this.personalization, this.key);
}
- private void Compress(byte[] message, int messagePos)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void Compress(ReadOnlySpan<byte> message)
{
InitializeInternalState();
- uint[] m = new uint[16];
- Pack.LE_To_UInt32(message, messagePos, m);
+ Span<uint> m = stackalloc uint[16];
+ Pack.LE_To_UInt32(message, m);
for (int round = 0; round < ROUNDS; round++)
{
// G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1
- G(m[blake2s_sigma[round,0]], m[blake2s_sigma[round,1]], 0, 4, 8, 12);
- G(m[blake2s_sigma[round,2]], m[blake2s_sigma[round,3]], 1, 5, 9, 13);
- G(m[blake2s_sigma[round,4]], m[blake2s_sigma[round,5]], 2, 6, 10, 14);
- G(m[blake2s_sigma[round,6]], m[blake2s_sigma[round,7]], 3, 7, 11, 15);
+ G(m[blake2s_sigma[round, 0]], m[blake2s_sigma[round, 1]], 0, 4, 8, 12);
+ G(m[blake2s_sigma[round, 2]], m[blake2s_sigma[round, 3]], 1, 5, 9, 13);
+ G(m[blake2s_sigma[round, 4]], m[blake2s_sigma[round, 5]], 2, 6, 10, 14);
+ G(m[blake2s_sigma[round, 6]], m[blake2s_sigma[round, 7]], 3, 7, 11, 15);
// G apply to diagonals of internalState:
- G(m[blake2s_sigma[round,8]], m[blake2s_sigma[round,9]], 0, 5, 10, 15);
- G(m[blake2s_sigma[round,10]], m[blake2s_sigma[round,11]], 1, 6, 11, 12);
- G(m[blake2s_sigma[round,12]], m[blake2s_sigma[round,13]], 2, 7, 8, 13);
- G(m[blake2s_sigma[round,14]], m[blake2s_sigma[round,15]], 3, 4, 9, 14);
+ G(m[blake2s_sigma[round, 8]], m[blake2s_sigma[round, 9]], 0, 5, 10, 15);
+ G(m[blake2s_sigma[round, 10]], m[blake2s_sigma[round, 11]], 1, 6, 11, 12);
+ G(m[blake2s_sigma[round, 12]], m[blake2s_sigma[round, 13]], 2, 7, 8, 13);
+ G(m[blake2s_sigma[round, 14]], m[blake2s_sigma[round, 15]], 3, 4, 9, 14);
}
// update chain values:
@@ -552,27 +580,26 @@ namespace Org.BouncyCastle.Crypto.Digests
chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8];
}
}
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- private void Compress(ReadOnlySpan<byte> message)
+#else
+ private void Compress(byte[] message, int messagePos)
{
InitializeInternalState();
- Span<uint> m = stackalloc uint[16];
- Pack.LE_To_UInt32(message, m);
+ uint[] m = new uint[16];
+ Pack.LE_To_UInt32(message, messagePos, m);
for (int round = 0; round < ROUNDS; round++)
{
// G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1
- G(m[blake2s_sigma[round, 0]], m[blake2s_sigma[round, 1]], 0, 4, 8, 12);
- G(m[blake2s_sigma[round, 2]], m[blake2s_sigma[round, 3]], 1, 5, 9, 13);
- G(m[blake2s_sigma[round, 4]], m[blake2s_sigma[round, 5]], 2, 6, 10, 14);
- G(m[blake2s_sigma[round, 6]], m[blake2s_sigma[round, 7]], 3, 7, 11, 15);
+ G(m[blake2s_sigma[round,0]], m[blake2s_sigma[round,1]], 0, 4, 8, 12);
+ G(m[blake2s_sigma[round,2]], m[blake2s_sigma[round,3]], 1, 5, 9, 13);
+ G(m[blake2s_sigma[round,4]], m[blake2s_sigma[round,5]], 2, 6, 10, 14);
+ G(m[blake2s_sigma[round,6]], m[blake2s_sigma[round,7]], 3, 7, 11, 15);
// G apply to diagonals of internalState:
- G(m[blake2s_sigma[round, 8]], m[blake2s_sigma[round, 9]], 0, 5, 10, 15);
- G(m[blake2s_sigma[round, 10]], m[blake2s_sigma[round, 11]], 1, 6, 11, 12);
- G(m[blake2s_sigma[round, 12]], m[blake2s_sigma[round, 13]], 2, 7, 8, 13);
- G(m[blake2s_sigma[round, 14]], m[blake2s_sigma[round, 15]], 3, 4, 9, 14);
+ G(m[blake2s_sigma[round,8]], m[blake2s_sigma[round,9]], 0, 5, 10, 15);
+ G(m[blake2s_sigma[round,10]], m[blake2s_sigma[round,11]], 1, 6, 11, 12);
+ G(m[blake2s_sigma[round,12]], m[blake2s_sigma[round,13]], 2, 7, 8, 13);
+ G(m[blake2s_sigma[round,14]], m[blake2s_sigma[round,15]], 3, 4, 9, 14);
}
// update chain values:
@@ -583,21 +610,19 @@ namespace Org.BouncyCastle.Crypto.Digests
}
#endif
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
private void G(uint m1, uint m2, int posA, int posB, int posC, int posD)
{
internalState[posA] = internalState[posA] + internalState[posB] + m1;
- internalState[posD] = rotr32(internalState[posD] ^ internalState[posA], 16);
+ internalState[posD] = Integers.RotateRight(internalState[posD] ^ internalState[posA], 16);
internalState[posC] = internalState[posC] + internalState[posD];
- internalState[posB] = rotr32(internalState[posB] ^ internalState[posC], 12);
+ internalState[posB] = Integers.RotateRight(internalState[posB] ^ internalState[posC], 12);
internalState[posA] = internalState[posA] + internalState[posB] + m2;
- internalState[posD] = rotr32(internalState[posD] ^ internalState[posA], 8);
+ internalState[posD] = Integers.RotateRight(internalState[posD] ^ internalState[posA], 8);
internalState[posC] = internalState[posC] + internalState[posD];
- internalState[posB] = rotr32(internalState[posB] ^ internalState[posC], 7);
- }
-
- private uint rotr32(uint x, int rot)
- {
- return x >> rot | x << -rot;
+ internalState[posB] = Integers.RotateRight(internalState[posB] ^ internalState[posC], 7);
}
/**
@@ -605,17 +630,14 @@ namespace Org.BouncyCastle.Crypto.Digests
*
* @return the algorithm name
*/
- public virtual string AlgorithmName
- {
- get { return "BLAKE2s"; }
- }
+ public string AlgorithmName => "BLAKE2s";
/**
* Return the size in bytes of the digest produced by this message digest.
*
* @return the size in bytes of the digest produced by this message digest.
*/
- public virtual int GetDigestSize()
+ public int GetDigestSize()
{
return digestLength;
}
@@ -626,7 +648,7 @@ namespace Org.BouncyCastle.Crypto.Digests
*
* @return byte length of the digest's internal buffer.
*/
- public virtual int GetByteLength()
+ public int GetByteLength()
{
return BLOCK_LENGTH_BYTES;
}
@@ -634,7 +656,7 @@ namespace Org.BouncyCastle.Crypto.Digests
/**
* Overwrite the key if it is no longer used (zeroization).
*/
- public virtual void ClearKey()
+ public void ClearKey()
{
if (key != null)
{
@@ -647,7 +669,7 @@ namespace Org.BouncyCastle.Crypto.Digests
* Overwrite the salt (pepper) if it is secret and no longer used
* (zeroization).
*/
- public virtual void ClearSalt()
+ public void ClearSalt()
{
if (salt != null)
{
diff --git a/crypto/src/crypto/digests/Blake2xsDigest.cs b/crypto/src/crypto/digests/Blake2xsDigest.cs
new file mode 100644
index 000000000..43bfbac18
--- /dev/null
+++ b/crypto/src/crypto/digests/Blake2xsDigest.cs
@@ -0,0 +1,368 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+ /*
+ The BLAKE2 cryptographic hash function was designed by Jean-
+ Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
+ Winnerlein.
+
+ Reference Implementation and Description can be found at: https://blake2.net/blake2x.pdf
+ */
+
+ /**
+ * Implementation of the eXtendable Output Function (XOF) BLAKE2xs.
+ * <p/>
+ * BLAKE2xs offers a built-in keying mechanism to be used directly
+ * for authentication ("Prefix-MAC") rather than a HMAC construction.
+ * <p/>
+ * BLAKE2xs offers a built-in support for a salt for randomized hashing
+ * and a personal string for defining a unique hash function for each application.
+ * <p/>
+ * BLAKE2xs is optimized for 32-bit platforms and produces digests of any size
+ * between 1 and 2^16-2 bytes. The length can also be unknown and then the maximum
+ * length will be 2^32 blocks of 32 bytes.
+ */
+ public sealed class Blake2xsDigest
+ : IXof
+ {
+ /**
+ * Magic number to indicate an unknown length of digest
+ */
+ public const int UnknownDigestLength = 65535;
+
+ private const int DigestLength = 32;
+ private const long MaxNumberBlocks = 1L << 32;
+
+ /**
+ * Expected digest length for the xof. It can be unknown.
+ */
+ private int digestLength;
+
+ /**
+ * Root hash that will take the updates
+ */
+ private Blake2sDigest hash;
+
+ /**
+ * Digest of the root hash
+ */
+ private byte[] h0 = null;
+
+ /**
+ * Digest of each round of the XOF
+ */
+ private byte[] buf = new byte[32];
+
+ /**
+ * Current position for a round
+ */
+ private int bufPos = 32;
+
+ /**
+ * Overall position of the digest. It is useful when the length is known
+ * in advance to get last block length.
+ */
+ private int digestPos = 0;
+
+ /**
+ * Keep track of the round number to detect the end of the digest after
+ * 2^32 blocks of 32 bytes.
+ */
+ private long blockPos = 0;
+
+ /**
+ * Current node offset incremented by 1 every round.
+ */
+ private long nodeOffset;
+
+ /**
+ * BLAKE2xs for hashing with unknown digest length
+ */
+ public Blake2xsDigest()
+ : this(UnknownDigestLength)
+ {
+ }
+
+ /**
+ * BLAKE2xs for hashing
+ *
+ * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
+ */
+ public Blake2xsDigest(int digestBytes)
+ : this(digestBytes, null, null, null)
+ {
+ }
+
+ /**
+ * BLAKE2xs with key
+ *
+ * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
+ * @param key A key up to 32 bytes or null
+ */
+ public Blake2xsDigest(int digestBytes, byte[] key)
+ : this(digestBytes, key, null, null)
+ {
+ }
+
+ /**
+ * BLAKE2xs with key, salt and personalization
+ *
+ * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
+ * @param key A key up to 32 bytes or null
+ * @param salt 8 bytes or null
+ * @param personalization 8 bytes or null
+ */
+ public Blake2xsDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization)
+ {
+ if (digestBytes < 1 || digestBytes > UnknownDigestLength)
+ throw new ArgumentException("BLAKE2xs digest length must be between 1 and 2^16-1");
+
+ digestLength = digestBytes;
+ nodeOffset = ComputeNodeOffset();
+ hash = new Blake2sDigest(DigestLength, key, salt, personalization, nodeOffset);
+ }
+
+ public Blake2xsDigest(Blake2xsDigest digest)
+ {
+ digestLength = digest.digestLength;
+ hash = new Blake2sDigest(digest.hash);
+ h0 = Arrays.Clone(digest.h0);
+ buf = Arrays.Clone(digest.buf);
+ bufPos = digest.bufPos;
+ digestPos = digest.digestPos;
+ blockPos = digest.blockPos;
+ nodeOffset = digest.nodeOffset;
+ }
+
+ /**
+ * Return the algorithm name.
+ *
+ * @return the algorithm name
+ */
+ public string AlgorithmName => "BLAKE2xs";
+
+ /**
+ * Return the size in bytes of the digest produced by this message digest.
+ *
+ * @return the size in bytes of the digest produced by this message digest.
+ */
+ public int GetDigestSize() => digestLength;
+
+ /**
+ * Return the size in bytes of the internal buffer the digest applies its
+ * compression function to.
+ *
+ * @return byte length of the digest's internal buffer.
+ */
+ public int GetByteLength() => hash.GetByteLength();
+
+ /**
+ * Return the maximum size in bytes the digest can produce when the length
+ * is unknown
+ *
+ * @return byte length of the largest digest with unknown length
+ */
+ public long GetUnknownMaxLength()
+ {
+ return MaxNumberBlocks * DigestLength;
+ }
+
+ /**
+ * Update the message digest with a single byte.
+ *
+ * @param in the input byte to be entered.
+ */
+ public void Update(byte b)
+ {
+ hash.Update(b);
+ }
+
+ /**
+ * Update the message digest with a block of bytes.
+ *
+ * @param in the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void BlockUpdate(byte[] input, int inOff, int inLen)
+ {
+ hash.BlockUpdate(input, inOff, inLen);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public void BlockUpdate(ReadOnlySpan<byte> input)
+ {
+ hash.BlockUpdate(input);
+ }
+#endif
+
+ /**
+ * Reset the digest back to its initial state. The key, the salt and the
+ * personal string will remain for further computations.
+ */
+ public void Reset()
+ {
+ hash.Reset();
+
+ h0 = null;
+ bufPos = DigestLength;
+ digestPos = 0;
+ blockPos = 0;
+ nodeOffset = ComputeNodeOffset();
+ }
+
+ /**
+ * Close the digest, producing the final digest value. The doFinal() call
+ * leaves the digest reset. Key, salt and personal string remain.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOffset the offset into the out array the digest is to start at.
+ */
+ public int DoFinal(byte[] output, int outOff)
+ {
+ return DoFinal(output, outOff, digestLength);
+ }
+
+ /**
+ * Close the digest, producing the final digest value. The doFinal() call
+ * leaves the digest reset. Key, salt, personal string remain.
+ *
+ * @param out output array to write the output bytes to.
+ * @param outOff offset to start writing the bytes at.
+ * @param outLen the number of output bytes requested.
+ */
+ public int DoFinal(byte[] output, int outOff, int outLen)
+ {
+ int ret = DoOutput(output, outOff, outLen);
+
+ Reset();
+
+ return ret;
+ }
+
+ /**
+ * Start outputting the results of the final calculation for this digest. Unlike doFinal, this method
+ * will continue producing output until the Xof is explicitly reset, or signals otherwise.
+ *
+ * @param out output array to write the output bytes to.
+ * @param outOff offset to start writing the bytes at.
+ * @param outLen the number of output bytes requested.
+ * @return the number of bytes written
+ */
+ public int DoOutput(byte[] output, int outOff, int outLen)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return Output(output.AsSpan(outOff, outLen));
+#else
+ if (h0 == null)
+ {
+ h0 = new byte[hash.GetDigestSize()];
+ hash.DoFinal(h0, 0);
+ }
+
+ if (digestLength != UnknownDigestLength)
+ {
+ if (digestPos + outLen > digestLength)
+ throw new ArgumentException("Output length is above the digest length");
+ }
+ else if (blockPos << 5 >= GetUnknownMaxLength())
+ {
+ throw new ArgumentException("Maximum length is 2^32 blocks of 32 bytes");
+ }
+
+ for (int i = 0; i < outLen; i++)
+ {
+ if (bufPos >= DigestLength)
+ {
+ Blake2sDigest h = new Blake2sDigest(ComputeStepLength(), DigestLength, nodeOffset);
+ h.BlockUpdate(h0, 0, h0.Length);
+
+ Arrays.Fill(buf, 0);
+ h.DoFinal(buf, 0);
+ bufPos = 0;
+ nodeOffset++;
+ blockPos++;
+ }
+ output[outOff + i] = buf[bufPos];
+ bufPos++;
+ digestPos++;
+ }
+
+ return outLen;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public int DoFinal(Span<byte> output)
+ {
+ return OutputFinal(output[..digestLength]);
+ }
+
+ public int OutputFinal(Span<byte> output)
+ {
+ int ret = Output(output);
+
+ Reset();
+
+ return ret;
+ }
+
+ public int Output(Span<byte> output)
+ {
+ int outLen = output.Length;
+ if (h0 == null)
+ {
+ h0 = new byte[hash.GetDigestSize()];
+ hash.DoFinal(h0);
+ }
+
+ if (digestLength != UnknownDigestLength)
+ {
+ if (digestPos + outLen > digestLength)
+ throw new ArgumentException("Output length is above the digest length");
+ }
+ else if (blockPos << 5 >= GetUnknownMaxLength())
+ {
+ throw new ArgumentException("Maximum length is 2^32 blocks of 32 bytes");
+ }
+
+ for (int i = 0; i < outLen; i++)
+ {
+ if (bufPos >= DigestLength)
+ {
+ Blake2sDigest h = new Blake2sDigest(ComputeStepLength(), DigestLength, nodeOffset);
+ h.BlockUpdate(h0);
+
+ Arrays.Fill(buf, 0);
+ h.DoFinal(buf);
+ bufPos = 0;
+ nodeOffset++;
+ blockPos++;
+ }
+ output[i] = buf[bufPos];
+ bufPos++;
+ digestPos++;
+ }
+
+ return outLen;
+ }
+#endif
+
+ // get the next round length. If the length is unknown, the digest length is always the maximum.
+ private int ComputeStepLength()
+ {
+ if (digestLength == UnknownDigestLength)
+ return DigestLength;
+
+ return System.Math.Min(DigestLength, digestLength - digestPos);
+ }
+
+ private long ComputeNodeOffset()
+ {
+ return digestLength * 0x100000000L;
+ }
+ }
+}
|