summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-08-23 14:08:56 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-08-23 14:08:56 +0700
commit5bae538777cfabc024f318c9d192a062d68d98b3 (patch)
treeda3da3f8c2ed19179f814cdd7716e214464ee141 /crypto/src
parentFix namespaces, refactoring (diff)
downloadBouncyCastle.NET-ed25519-5bae538777cfabc024f318c9d192a062d68d98b3.tar.xz
Span-based variants for IDigest, IXof
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/IDigest.cs84
-rw-r--r--crypto/src/crypto/IXof.cs27
-rw-r--r--crypto/src/crypto/digests/Blake2bDigest.cs132
-rw-r--r--crypto/src/crypto/digests/Blake2sDigest.cs137
-rw-r--r--crypto/src/crypto/digests/CSHAKEDigest.cs19
-rw-r--r--crypto/src/crypto/digests/DSTU7564Digest.cs131
-rw-r--r--crypto/src/crypto/digests/GOST3411Digest.cs54
-rw-r--r--crypto/src/crypto/digests/GOST3411_2012Digest.cs92
-rw-r--r--crypto/src/crypto/digests/GOST3411_2012_256Digest.cs12
-rw-r--r--crypto/src/crypto/digests/GeneralDigest.cs50
-rw-r--r--crypto/src/crypto/digests/Haraka256Digest.cs90
-rw-r--r--crypto/src/crypto/digests/Haraka512Digest.cs122
-rw-r--r--crypto/src/crypto/digests/HarakaBase.cs12
-rw-r--r--crypto/src/crypto/digests/KeccakDigest.cs97
-rw-r--r--crypto/src/crypto/digests/LongDigest.cs289
-rw-r--r--crypto/src/crypto/digests/MD2Digest.cs59
-rw-r--r--crypto/src/crypto/digests/MD4Digest.cs53
-rw-r--r--crypto/src/crypto/digests/MD5Digest.cs32
-rw-r--r--crypto/src/crypto/digests/NonMemoableDigest.cs14
-rw-r--r--crypto/src/crypto/digests/NullDigest.cs35
-rw-r--r--crypto/src/crypto/digests/ParallelHash.cs116
-rw-r--r--crypto/src/crypto/digests/RipeMD128Digest.cs28
-rw-r--r--crypto/src/crypto/digests/RipeMD160Digest.cs29
-rw-r--r--crypto/src/crypto/digests/RipeMD256Digest.cs32
-rw-r--r--crypto/src/crypto/digests/RipeMD320Digest.cs34
-rw-r--r--crypto/src/crypto/digests/SHA3Digest.cs9
-rw-r--r--crypto/src/crypto/digests/SM3Digest.cs34
-rw-r--r--crypto/src/crypto/digests/Sha1Digest.cs34
-rw-r--r--crypto/src/crypto/digests/Sha224Digest.cs44
-rw-r--r--crypto/src/crypto/digests/Sha256Digest.cs59
-rw-r--r--crypto/src/crypto/digests/Sha384Digest.cs25
-rw-r--r--crypto/src/crypto/digests/Sha512Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha512tDigest.cs47
-rw-r--r--crypto/src/crypto/digests/ShakeDigest.cs28
-rw-r--r--crypto/src/crypto/digests/ShortenedDigest.cs25
-rw-r--r--crypto/src/crypto/digests/SkeinDigest.cs16
-rw-r--r--crypto/src/crypto/digests/SkeinEngine.cs101
-rw-r--r--crypto/src/crypto/digests/TigerDigest.cs70
-rw-r--r--crypto/src/crypto/digests/TupleHash.cs53
-rw-r--r--crypto/src/crypto/digests/WhirlpoolDigest.cs24
-rw-r--r--crypto/src/crypto/digests/XofUtils.cs50
-rw-r--r--crypto/src/crypto/macs/KMac.cs71
-rw-r--r--crypto/src/pqc/crypto/lms/LMSContext.cs14
-rw-r--r--crypto/src/security/DigestUtilities.cs31
44 files changed, 2184 insertions, 352 deletions
diff --git a/crypto/src/crypto/IDigest.cs b/crypto/src/crypto/IDigest.cs

index 6769dcc42..2ba1705c7 100644 --- a/crypto/src/crypto/IDigest.cs +++ b/crypto/src/crypto/IDigest.cs
@@ -2,60 +2,52 @@ using System; namespace Org.BouncyCastle.Crypto { - /** - * interface that a message digest conforms to. - */ + /// <remarks>Base interface for a message digest.</remarks> public interface IDigest { - /** - * return the algorithm name - * - * @return the algorithm name - */ + /// <summary>the algorithm name</summary> string AlgorithmName { get; } - /** - * 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. - */ - int GetDigestSize(); - - /** - * return the size, in bytes, of the internal buffer used by this digest. - * - * @return the size, in bytes, of the internal buffer used by this digest. - */ - int GetByteLength(); - - /** - * update the message digest with a single byte. - * - * @param inByte the input byte to be entered. - */ + /// <summary>Return the size, in bytes, of the digest produced by this message digest.</summary> + /// <returns>the size, in bytes, of the digest produced by this message digest.</returns> + int GetDigestSize(); + + /// <summary>Return the size, in bytes, of the internal buffer used by this digest.</summary> + /// <returns>the size, in bytes, of the internal buffer used by this digest.</returns> + int GetByteLength(); + + /// <summary>Update the message digest with a single byte.</summary> + /// <param name="input">the input byte to be entered.</param> void Update(byte input); - /** - * update the message digest with a block of bytes. - * - * @param input 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. - */ - void BlockUpdate(byte[] input, int inOff, int length); - - /** - * Close the digest, producing the final digest value. The doFinal - * call leaves the digest reset. - * - * @param output the array the digest is to be copied into. - * @param outOff the offset into the out array the digest is to start at. - */ + /// <summary>Update the message digest with a block of bytes.</summary> + /// <param name="input">the byte array containing the data.</param> + /// <param name="inOff">the offset into the byte array where the data starts.</param> + /// <param name="inLen">the length of the data.</param> + void BlockUpdate(byte[] input, int inOff, int inLen); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Update the message digest with a span of bytes.</summary> + /// <param name="input">the span containing the data.</param> + void BlockUpdate(ReadOnlySpan<byte> input); +#endif + + /// <summary>Close the digest, producing the final digest value.</summary> + /// <remarks>This call leaves the digest reset.</remarks> + /// <param name="output">the byte array the digest is to be copied into.</param> + /// <param name="outOff">the offset into the byte array the digest is to start at.</param> + /// <returns>the number of bytes written</returns> int DoFinal(byte[] output, int outOff); - /** - * reset the digest back to it's initial state. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Close the digest, producing the final digest value.</summary> + /// <remarks>This call leaves the digest reset.</remarks> + /// <param name="output">the span the digest is to be copied into.</param> + /// <returns>the number of bytes written</returns> + int DoFinal(Span<byte> output); +#endif + + /// <summary>Reset the digest back to its initial state.</summary> void Reset(); } } diff --git a/crypto/src/crypto/IXof.cs b/crypto/src/crypto/IXof.cs
index f76304d48..8cddb2870 100644 --- a/crypto/src/crypto/IXof.cs +++ b/crypto/src/crypto/IXof.cs
@@ -4,13 +4,13 @@ namespace Org.BouncyCastle.Crypto { /// <remarks> /// With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes. - /// This interface provides the extra method required to support variable output on a digest implementation. + /// This interface provides the extra methods required to support variable output on a digest implementation. /// </remarks> public interface IXof : IDigest { /// <summary> - /// Output the results of the final calculation for this digest to outLen number of bytes. + /// Output the results of the final calculation for this XOF to outLen number of bytes. /// </summary> /// <param name="output">output array to write the output bytes to.</param> /// <param name="outOff">offset to start writing the bytes at.</param> @@ -18,14 +18,33 @@ namespace Org.BouncyCastle.Crypto /// <returns>the number of bytes written</returns> int DoFinal(byte[] output, int outOff, int outLen); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// <summary> - /// 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. + /// Output the results of the final calculation for this XOF to fill the output span. + /// </summary> + /// <param name="output">span to fill with the output bytes.</param> + /// <returns>the number of bytes written</returns> + int OutputFinal(Span<byte> output); +#endif + + /// <summary> + /// Start outputting the results of the final calculation for this XOF. Unlike DoFinal, this method + /// will continue producing output until the XOF is explicitly reset, or signals otherwise. /// </summary> /// <param name="output">output array to write the output bytes to.</param> /// <param name="outOff">offset to start writing the bytes at.</param> /// <param name="outLen">the number of output bytes requested.</param> /// <returns>the number of bytes written</returns> int DoOutput(byte[] output, int outOff, int outLen); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary> + /// Start outputting the results of the final calculation for this XOF. Unlike OutputFinal, this method + /// will continue producing output until the XOF is explicitly reset, or signals otherwise. + /// </summary> + /// <param name="output">span to fill with the output bytes.</param> + /// <returns>the number of bytes written</returns> + int Output(Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs
index 1ac9cfa35..ec80a3355 100644 --- a/crypto/src/crypto/digests/Blake2bDigest.cs +++ b/crypto/src/crypto/digests/Blake2bDigest.cs
@@ -357,11 +357,63 @@ 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); + Array.Copy(message, messagePos, buffer, 0, offset + len - messagePos); bufferPos += offset + len - messagePos; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + if (input.IsEmpty) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < input.Length) + { // full buffer + at least 1 byte + input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos)); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^64 + t1++; + } + Compress(buffer, 0); + bufferPos = 0; + Array.Clear(buffer, 0, buffer.Length);// clear buffer + } + else + { + input.CopyTo(buffer.AsSpan(bufferPos)); + bufferPos += input.Length; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES; + for (messagePos = remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES) + { // block wise 128 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + Compress(input[messagePos..]); + } + + // fill the buffer with left bytes, this might be a full block + input[messagePos..].CopyTo(buffer.AsSpan()); + bufferPos += input.Length - messagePos; + } +#endif + /** * close the digest, producing the final digest value. The doFinal * call leaves the digest reset. @@ -382,19 +434,42 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null Array.Clear(internalState, 0, internalState.Length); - byte[] bytes = new byte[8]; - for (int i = 0; i < chainValue.Length && (i * 8 < digestLength); i++) + int full = digestLength >> 3, partial = digestLength & 7; + Pack.UInt64_To_LE(chainValue, 0, full, output, outOffset); + if (partial > 0) + { + byte[] bytes = new byte[8]; + Pack.UInt64_To_LE(chainValue[full], bytes, 0); + Array.Copy(bytes, 0, output, outOffset + digestLength - partial, partial); + } + + Array.Clear(chainValue, 0, chainValue.Length); + + Reset(); + + return digestLength; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + f0 = 0xFFFFFFFFFFFFFFFFUL; + t0 += (ulong)bufferPos; + if (bufferPos > 0 && t0 == 0) { - Pack.UInt64_To_LE(chainValue[i], bytes, 0); + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null + Array.Clear(internalState, 0, internalState.Length); - if (i * 8 < digestLength - 8) - { - Array.Copy(bytes, 0, output, outOffset + i * 8, 8); - } - else - { - Array.Copy(bytes, 0, output, outOffset + i * 8, digestLength - (i * 8)); - } + int full = digestLength >> 3, partial = digestLength & 7; + Pack.UInt64_To_LE(chainValue.AsSpan(0, full), output); + if (partial > 0) + { + Span<byte> bytes = stackalloc byte[8]; + Pack.UInt64_To_LE(chainValue[full], bytes); + bytes[..partial].CopyTo(output[(digestLength - partial)..]); } Array.Clear(chainValue, 0, chainValue.Length); @@ -403,6 +478,7 @@ namespace Org.BouncyCastle.Crypto.Digests return digestLength; } +#endif /** * Reset the digest back to it's initial state. @@ -453,6 +529,36 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Compress(ReadOnlySpan<byte> message) + { + InitializeInternalState(); + + 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 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); + } + + // update chain values: + for (int offset = 0; offset < chainValue.Length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8]; + } + } +#endif + private void G(ulong m1, ulong m2, int posA, int posB, int posC, int posD) { internalState[posA] = internalState[posA] + internalState[posB] + m1; diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs
index f50419126..187808aa0 100644 --- a/crypto/src/crypto/digests/Blake2sDigest.cs +++ b/crypto/src/crypto/digests/Blake2sDigest.cs
@@ -381,6 +381,61 @@ namespace Org.BouncyCastle.Crypto.Digests bufferPos += offset + len - messagePos; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + if (input.IsEmpty) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < input.Length) + { // full buffer + at least 1 byte + input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos)); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^32 + t1++; + } + Compress(buffer, 0); + bufferPos = 0; + Array.Clear(buffer, 0, buffer.Length);// clear buffer + } + else + { + input.CopyTo(buffer.AsSpan(bufferPos)); + bufferPos += input.Length; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES; + for (messagePos = remainingLength; + messagePos < blockWiseLastPos; + messagePos += BLOCK_LENGTH_BYTES) + { // block wise 64 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + Compress(input[messagePos..]); + } + + // fill the buffer with left bytes, this might be a full block + input[messagePos..].CopyTo(buffer.AsSpan()); + bufferPos += input.Length - messagePos; + } +#endif + /** * Close the digest, producing the final digest value. The doFinal() call * leaves the digest reset. Key, salt and personal string remain. @@ -402,19 +457,44 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null Array.Clear(internalState, 0, internalState.Length); - byte[] bytes = new byte[4]; - for (int i = 0; i < chainValue.Length && (i * 4 < digestLength); i++) + int full = digestLength >> 2, partial = digestLength & 3; + Pack.UInt32_To_LE(chainValue, 0, full, output, outOffset); + if (partial > 0) + { + byte[] bytes = new byte[4]; + Pack.UInt32_To_LE(chainValue[full], bytes, 0); + Array.Copy(bytes, 0, output, outOffset + digestLength - partial, partial); + } + + Array.Clear(chainValue, 0, chainValue.Length); + + Reset(); + + return digestLength; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + f0 = 0xFFFFFFFFU; + t0 += (uint)bufferPos; + // bufferPos may be < 64, so (t0 == 0) does not work + // for 2^32 < message length > 2^32 - 63 + if ((t0 < 0) && (bufferPos > -t0)) { - Pack.UInt32_To_LE(chainValue[i], bytes, 0); + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null + Array.Clear(internalState, 0, internalState.Length); - if (i * 4 < digestLength - 4) - { - Array.Copy(bytes, 0, output, outOffset + i * 4, 4); - } - else - { - Array.Copy(bytes, 0, output, outOffset + i * 4, digestLength - (i * 4)); - } + int full = digestLength >> 2, partial = digestLength & 3; + Pack.UInt32_To_LE(chainValue.AsSpan(0, full), output); + if (partial > 0) + { + Span<byte> bytes = stackalloc byte[4]; + Pack.UInt32_To_LE(chainValue[full], bytes); + bytes[..partial].CopyTo(output[(digestLength - partial)..]); } Array.Clear(chainValue, 0, chainValue.Length); @@ -423,6 +503,7 @@ namespace Org.BouncyCastle.Crypto.Digests return digestLength; } +#endif /** * Reset the digest back to its initial state. The key, the salt and the @@ -453,9 +534,7 @@ namespace Org.BouncyCastle.Crypto.Digests for (int round = 0; round < ROUNDS; round++) { - - // G apply to columns of internalState:m[blake2s_sigma[round][2 * - // blockPos]] /+1 + // 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); @@ -474,6 +553,36 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Compress(ReadOnlySpan<byte> message) + { + InitializeInternalState(); + + 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 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); + } + + // update chain values: + for (int offset = 0; offset < chainValue.Length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8]; + } + } +#endif + private void G(uint m1, uint m2, int posA, int posB, int posC, int posD) { internalState[posA] = internalState[posA] + internalState[posB] + m1; diff --git a/crypto/src/crypto/digests/CSHAKEDigest.cs b/crypto/src/crypto/digests/CSHAKEDigest.cs
index c3b0b7068..fc37b865c 100644 --- a/crypto/src/crypto/digests/CSHAKEDigest.cs +++ b/crypto/src/crypto/digests/CSHAKEDigest.cs
@@ -95,6 +95,25 @@ namespace Org.BouncyCastle.Crypto.Digests return outLen; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int Output(Span<byte> output) + { + if (diff == null) + { + return base.Output(output); + } + + if (!squeezing) + { + AbsorbBits(0x00, 2); + } + + Squeeze(output); + + return output.Length; + } +#endif + public override void Reset() { base.Reset(); diff --git a/crypto/src/crypto/digests/DSTU7564Digest.cs b/crypto/src/crypto/digests/DSTU7564Digest.cs
index b2d90799d..901e549be 100644 --- a/crypto/src/crypto/digests/DSTU7564Digest.cs +++ b/crypto/src/crypto/digests/DSTU7564Digest.cs
@@ -1,11 +1,7 @@ using System; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; - using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Crypto.Digests { @@ -132,25 +128,103 @@ namespace Org.BouncyCastle.Crypto.Digests --length; } - if (length > 0) + while (length >= blockSize) + { + ProcessBlock(input, inOff); + inOff += blockSize; + length -= blockSize; + ++inputBlocks; + } + + while (length > 0) + { + Update(input[inOff++]); + --length; + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + while (bufOff != 0 && input.Length > 0) { - while (length >= blockSize) + Update(input[0]); + input = input[1..]; + } + + while (input.Length >= blockSize) + { + ProcessBlock(input); + input = input[blockSize..]; + ++inputBlocks; + } + + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif + + public virtual int DoFinal(byte[] output, int outOff) + { + // Apply padding: terminator byte and 96-bit length field + { + int inputBytes = bufOff; + buf[bufOff++] = (byte)0x80; + + int lenPos = blockSize - 12; + if (bufOff > lenPos) { - ProcessBlock(input, inOff); - inOff += blockSize; - length -= blockSize; - ++inputBlocks; + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + bufOff = 0; + ProcessBlock(buf, 0); } - while (length > 0) + while (bufOff < lenPos) { - Update(input[inOff++]); - --length; + buf[bufOff++] = 0; + } + + ulong c = ((inputBlocks & 0xFFFFFFFFUL) * (ulong)blockSize + (uint)inputBytes) << 3; + Pack.UInt32_To_LE((uint)c, buf, bufOff); + bufOff += 4; + c >>= 32; + c += ((inputBlocks >> 32) * (ulong)blockSize) << 3; + Pack.UInt64_To_LE(c, buf, bufOff); + //bufOff += 8; + ProcessBlock(buf, 0); + } + + { + Array.Copy(state, 0, tempState1, 0, columns); + + P(tempState1); + + for (int col = 0; col < columns; ++col) + { + state[col] ^= tempState1[col]; } } + + int neededColumns = hashSize / 8; + for (int col = columns - neededColumns; col < columns; ++col) + { + Pack.UInt64_To_LE(state[col], output, outOff); + outOff += 8; + } + + Reset(); + + return hashSize; } - public virtual int DoFinal(byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) { // Apply padding: terminator byte and 96-bit length field { @@ -179,7 +253,7 @@ namespace Org.BouncyCastle.Crypto.Digests c >>= 32; c += ((inputBlocks >> 32) * (ulong)blockSize) << 3; Pack.UInt64_To_LE(c, buf, bufOff); - // bufOff += 8; + //bufOff += 8; ProcessBlock(buf, 0); } @@ -197,14 +271,15 @@ namespace Org.BouncyCastle.Crypto.Digests int neededColumns = hashSize / 8; for (int col = columns - neededColumns; col < columns; ++col) { - Pack.UInt64_To_LE(state[col], output, outOff); - outOff += 8; + Pack.UInt64_To_LE(state[col], output); + output = output[8..]; } Reset(); return hashSize; } +#endif public virtual void Reset() { @@ -236,6 +311,28 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected virtual void ProcessBlock(ReadOnlySpan<byte> input) + { + for (int col = 0; col < columns; ++col) + { + ulong word = Pack.LE_To_UInt64(input); + input = input[8..]; + + tempState1[col] = state[col] ^ word; + tempState2[col] = word; + } + + P(tempState1); + Q(tempState2); + + for (int col = 0; col < columns; ++col) + { + state[col] ^= tempState1[col] ^ tempState2[col]; + } + } +#endif + private void P(ulong[] s) { for (int round = 0; round < rounds; ++round) diff --git a/crypto/src/crypto/digests/GOST3411Digest.cs b/crypto/src/crypto/digests/GOST3411Digest.cs
index 123751baa..dc1d376c3 100644 --- a/crypto/src/crypto/digests/GOST3411Digest.cs +++ b/crypto/src/crypto/digests/GOST3411Digest.cs
@@ -91,10 +91,7 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount++; } - public void BlockUpdate( - byte[] input, - int inOff, - int length) + public void BlockUpdate(byte[] input, int inOff, int length) { while ((xBufOff != 0) && (length > 0)) { @@ -123,6 +120,34 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + while ((xBufOff != 0) && (input.Length > 0)) + { + Update(input[0]); + input = input[1..]; + } + + while (input.Length >= xBuf.Length) + { + input[..xBuf.Length].CopyTo(xBuf.AsSpan()); + + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + input = input[xBuf.Length..]; + byteCount += (uint)xBuf.Length; + } + + // load in the remainder. + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif + // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8 private byte[] K = new byte[32]; @@ -234,7 +259,7 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Copy(S, 0, H, 0, H.Length); } - private void finish() + private void Finish() { ulong bitCount = byteCount * 8; Pack.UInt64_To_LE(bitCount, L); @@ -248,11 +273,9 @@ namespace Org.BouncyCastle.Crypto.Digests processBlock(Sum, 0); } - public int DoFinal( - byte[] output, - int outOff) + public int DoFinal(byte[] output, int outOff) { - finish(); + Finish(); H.CopyTo(output, outOff); @@ -261,6 +284,19 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_LENGTH; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Finish(); + + H.CopyTo(output); + + Reset(); + + return DIGEST_LENGTH; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/GOST3411_2012Digest.cs b/crypto/src/crypto/digests/GOST3411_2012Digest.cs
index 68cb6c035..259f4bcae 100644 --- a/crypto/src/crypto/digests/GOST3411_2012Digest.cs +++ b/crypto/src/crypto/digests/GOST3411_2012Digest.cs
@@ -1,10 +1,11 @@ using System; -using Org.BouncyCastle.Crypto; + using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { - public abstract class Gost3411_2012Digest:IDigest,IMemoable + public abstract class Gost3411_2012Digest + : IDigest, IMemoable { private readonly byte[] IV = new byte[64]; private readonly byte[] N = new byte[64]; @@ -21,8 +22,8 @@ namespace Org.BouncyCastle.Crypto.Digests protected Gost3411_2012Digest(byte[] IV) { - System.Array.Copy(IV,this.IV,64); - System.Array.Copy(IV, h, 64); + Array.Copy(IV,this.IV,64); + Array.Copy(IV, h, 64); } public abstract string AlgorithmName { get; } @@ -43,7 +44,7 @@ namespace Org.BouncyCastle.Crypto.Digests if (bOff != 64) { - System.Array.Copy(block, bOff, m, 64 - lenM, lenM); + Array.Copy(block, bOff, m, 64 - lenM, lenM); } g_N(h, N, m); @@ -60,6 +61,39 @@ namespace Org.BouncyCastle.Crypto.Digests return 64; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + int lenM = 64 - bOff; + + // At this point it is certain that lenM is smaller than 64 + for (int i = 0; i != 64 - lenM; i++) + { + m[i] = 0; + } + + m[63 - lenM] = 1; + + if (bOff != 64) + { + Array.Copy(block, bOff, m, 64 - lenM, lenM); + } + + g_N(h, N, m); + addMod512(N, lenM * 8); + addMod512(Sigma, m); + g_N(h, Zero, N); + g_N(h, Zero, Sigma); + + reverse(h, tmp); + + tmp.CopyTo(output); + + Reset(); + return 64; + } +#endif + public int GetByteLength() { return 64; @@ -73,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Digests bOff = 64; Arrays.Fill(N, (byte)0); Arrays.Fill(Sigma, (byte)0); - System.Array.Copy(IV, 0, h, 0, 64); + Array.Copy(IV, 0, h, 0, 64); Arrays.Fill(block, (byte)0); } @@ -81,14 +115,14 @@ namespace Org.BouncyCastle.Crypto.Digests { Gost3411_2012Digest o = (Gost3411_2012Digest)other; - System.Array.Copy(o.IV, 0, this.IV, 0, 64); - System.Array.Copy(o.N, 0, this.N, 0, 64); - System.Array.Copy(o.Sigma, 0, this.Sigma, 0, 64); - System.Array.Copy(o.Ki, 0, this.Ki, 0, 64); - System.Array.Copy(o.m, 0, this.m, 0, 64); - System.Array.Copy(o.h, 0, this.h, 0, 64); + Array.Copy(o.IV, 0, this.IV, 0, 64); + Array.Copy(o.N, 0, this.N, 0, 64); + Array.Copy(o.Sigma, 0, this.Sigma, 0, 64); + Array.Copy(o.Ki, 0, this.Ki, 0, 64); + Array.Copy(o.m, 0, this.m, 0, 64); + Array.Copy(o.h, 0, this.h, 0, 64); - System.Array.Copy(o.block, 0, this.block, 0, 64); + Array.Copy(o.block, 0, this.block, 0, 64); this.bOff = o.bOff; } @@ -104,7 +138,6 @@ namespace Org.BouncyCastle.Crypto.Digests } } - public void BlockUpdate(byte[] input, int inOff, int len) { while (bOff != 64 && len > 0) @@ -114,7 +147,7 @@ namespace Org.BouncyCastle.Crypto.Digests } while (len >= 64) { - System.Array.Copy(input, inOff, tmp, 0, 64); + Array.Copy(input, inOff, tmp, 0, 64); reverse(tmp, block); g_N(h, N, block); addMod512(N, 512); @@ -130,8 +163,31 @@ namespace Org.BouncyCastle.Crypto.Digests } } - +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + while (bOff != 64 && input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + while (input.Length >= 64) + { + input[..64].CopyTo(tmp.AsSpan()); + reverse(tmp, block); + g_N(h, N, block); + addMod512(N, 512); + addMod512(Sigma, block); + input = input[64..]; + } + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif private void F(byte[] V) { @@ -317,7 +373,7 @@ namespace Org.BouncyCastle.Crypto.Digests private void E(byte[] K, byte[] m) { - System.Array.Copy(K, 0, Ki, 0, 64); + Array.Copy(K, 0, Ki, 0, 64); xor512(K, m); F(K); for (int i = 0; i < 11; ++i) @@ -334,7 +390,7 @@ namespace Org.BouncyCastle.Crypto.Digests private void g_N(byte[] h, byte[] N, byte[] m) { - System.Array.Copy(h, 0, tmp, 0, 64); + Array.Copy(h, 0, tmp, 0, 64); xor512(h, N); F(h); diff --git a/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs b/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs
index 77cf6c50f..47877b2b5 100644 --- a/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs +++ b/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs
@@ -46,6 +46,18 @@ namespace Org.BouncyCastle.Crypto.Digests return 32; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Span<byte> result = stackalloc byte[64]; + base.DoFinal(result); + + result[32..].CopyTo(output); + + return 32; + } +#endif + public override IMemoable Copy() { return new Gost3411_2012_256Digest(this); diff --git a/crypto/src/crypto/digests/GeneralDigest.cs b/crypto/src/crypto/digests/GeneralDigest.cs
index c38b35fd3..02bad2cfb 100644 --- a/crypto/src/crypto/digests/GeneralDigest.cs +++ b/crypto/src/crypto/digests/GeneralDigest.cs
@@ -95,6 +95,50 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount += length; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int length = input.Length; + + // + // fill the current word + // + int i = 0; + if (xBufOff != 0) + { + while (i < length) + { + xBuf[xBufOff++] = input[i++]; + if (xBufOff == 4) + { + ProcessWord(xBuf, 0); + xBufOff = 0; + break; + } + } + } + + // + // process whole words. + // + int limit = length - 3; + for (; i < limit; i += 4) + { + ProcessWord(input.Slice(i, 4)); + } + + // + // load in the remainder. + // + while (i < length) + { + xBuf[xBufOff++] = input[i++]; + } + + byteCount += length; + } +#endif + public void Finish() { long bitLength = (byteCount << 3); @@ -122,6 +166,9 @@ namespace Org.BouncyCastle.Crypto.Digests } internal abstract void ProcessWord(byte[] input, int inOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal abstract void ProcessWord(ReadOnlySpan<byte> word); +#endif internal abstract void ProcessLength(long bitLength); internal abstract void ProcessBlock(); public abstract string AlgorithmName { get; } @@ -129,5 +176,8 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract int DoFinal(byte[] output, int outOff); public abstract IMemoable Copy(); public abstract void Reset(IMemoable t); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract int DoFinal(Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/digests/Haraka256Digest.cs b/crypto/src/crypto/digests/Haraka256Digest.cs
index f7af4bf69..27aea9b29 100644 --- a/crypto/src/crypto/digests/Haraka256Digest.cs +++ b/crypto/src/crypto/digests/Haraka256Digest.cs
@@ -93,7 +93,58 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_SIZE; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int haraka256256(byte[] msg, Span<byte> output) + { + byte[][] s1 = new byte[2][]; + s1[0] = new byte[16]; + s1[1] = new byte[16]; + byte[][] s2 = new byte[2][]; + s2[0] = new byte[16]; + s2[1] = new byte[16]; + + Array.Copy(msg, 0, s1[0], 0, 16); + Array.Copy(msg, 16, s1[1], 0, 16); + + s1[0] = aesEnc(s1[0], RC[0]); + s1[1] = aesEnc(s1[1], RC[1]); + s1[0] = aesEnc(s1[0], RC[2]); + s1[1] = aesEnc(s1[1], RC[3]); + mix256(s1, s2); + + s1[0] = aesEnc(s2[0], RC[4]); + s1[1] = aesEnc(s2[1], RC[5]); + s1[0] = aesEnc(s1[0], RC[6]); + s1[1] = aesEnc(s1[1], RC[7]); + mix256(s1, s2); + + s1[0] = aesEnc(s2[0], RC[8]); + s1[1] = aesEnc(s2[1], RC[9]); + s1[0] = aesEnc(s1[0], RC[10]); + s1[1] = aesEnc(s1[1], RC[11]); + mix256(s1, s2); + s1[0] = aesEnc(s2[0], RC[12]); + s1[1] = aesEnc(s2[1], RC[13]); + s1[0] = aesEnc(s1[0], RC[14]); + s1[1] = aesEnc(s1[1], RC[15]); + mix256(s1, s2); + + s1[0] = aesEnc(s2[0], RC[16]); + s1[1] = aesEnc(s2[1], RC[17]); + s1[0] = aesEnc(s1[0], RC[18]); + s1[1] = aesEnc(s1[1], RC[19]); + mix256(s1, s2); + + s1[0] = Xor(s2[0], msg, 0); + s1[1] = Xor(s2[1], msg, 16); + + s1[0].AsSpan(0, 16).CopyTo(output); + s1[1].AsSpan(0, 16).CopyTo(output[16..]); + + return DIGEST_SIZE; + } +#endif public Haraka256Digest() { @@ -106,10 +157,7 @@ namespace Org.BouncyCastle.Crypto.Digests this.off = digest.off; } - public string getAlgorithmName() - { - return "Haraka-256"; - } + public override string AlgorithmName => "Haraka-256"; public override void Update(byte input) { @@ -132,6 +180,19 @@ namespace Org.BouncyCastle.Crypto.Digests off += len; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void BlockUpdate(ReadOnlySpan<byte> input) + { + if (off + input.Length > 32) + { + throw new ArgumentException("total input cannot be more than 32 bytes"); + } + + input.CopyTo(buffer.AsSpan(off)); + off += input.Length; + } +#endif + public override int DoFinal(byte[] output, int outOff) { if (off != 32) @@ -151,6 +212,27 @@ namespace Org.BouncyCastle.Crypto.Digests return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + if (off != 32) + { + throw new ArgumentException("input must be exactly 32 bytes"); + } + + if (output.Length < 32) + { + throw new ArgumentException("output too short to receive digest"); + } + + int rv = haraka256256(buffer, output); + + Reset(); + + return rv; + } +#endif + public override void Reset() { off = 0; diff --git a/crypto/src/crypto/digests/Haraka512Digest.cs b/crypto/src/crypto/digests/Haraka512Digest.cs
index 36bdcbabe..0faeae710 100644 --- a/crypto/src/crypto/digests/Haraka512Digest.cs +++ b/crypto/src/crypto/digests/Haraka512Digest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { public class Haraka512Digest : HarakaBase @@ -167,10 +169,92 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_SIZE; } - public string GetAlgorithmName() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int Haraka512256(byte[] msg, Span<byte> output) { - return "Haraka-512"; + byte[][] s1 = new byte[4][]; + s1[0] = new byte[16]; + s1[1] = new byte[16]; + s1[2] = new byte[16]; + s1[3] = new byte[16]; + byte[][] s2 = new byte[4][]; + s2[0] = new byte[16]; + s2[1] = new byte[16]; + s2[2] = new byte[16]; + s2[3] = new byte[16]; + + //-- Unrolled version of above. + + Array.Copy(msg, 0, s1[0], 0, 16); + Array.Copy(msg, 16, s1[1], 0, 16); + Array.Copy(msg, 32, s1[2], 0, 16); + Array.Copy(msg, 48, s1[3], 0, 16); + + s1[0] = aesEnc(s1[0], RC[0]); + s1[1] = aesEnc(s1[1], RC[1]); + s1[2] = aesEnc(s1[2], RC[2]); + s1[3] = aesEnc(s1[3], RC[3]); + s1[0] = aesEnc(s1[0], RC[4]); + s1[1] = aesEnc(s1[1], RC[5]); + s1[2] = aesEnc(s1[2], RC[6]); + s1[3] = aesEnc(s1[3], RC[7]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[8]); + s1[1] = aesEnc(s2[1], RC[9]); + s1[2] = aesEnc(s2[2], RC[10]); + s1[3] = aesEnc(s2[3], RC[11]); + s1[0] = aesEnc(s1[0], RC[12]); + s1[1] = aesEnc(s1[1], RC[13]); + s1[2] = aesEnc(s1[2], RC[14]); + s1[3] = aesEnc(s1[3], RC[15]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[16]); + s1[1] = aesEnc(s2[1], RC[17]); + s1[2] = aesEnc(s2[2], RC[18]); + s1[3] = aesEnc(s2[3], RC[19]); + s1[0] = aesEnc(s1[0], RC[20]); + s1[1] = aesEnc(s1[1], RC[21]); + s1[2] = aesEnc(s1[2], RC[22]); + s1[3] = aesEnc(s1[3], RC[23]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[24]); + s1[1] = aesEnc(s2[1], RC[25]); + s1[2] = aesEnc(s2[2], RC[26]); + s1[3] = aesEnc(s2[3], RC[27]); + s1[0] = aesEnc(s1[0], RC[28]); + s1[1] = aesEnc(s1[1], RC[29]); + s1[2] = aesEnc(s1[2], RC[30]); + s1[3] = aesEnc(s1[3], RC[31]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[32]); + s1[1] = aesEnc(s2[1], RC[33]); + s1[2] = aesEnc(s2[2], RC[34]); + s1[3] = aesEnc(s2[3], RC[35]); + s1[0] = aesEnc(s1[0], RC[36]); + s1[1] = aesEnc(s1[1], RC[37]); + s1[2] = aesEnc(s1[2], RC[38]); + s1[3] = aesEnc(s1[3], RC[39]); + Mix512(s1, s2); + + s1[0] = Xor(s2[0], msg, 0); + s1[1] = Xor(s2[1], msg, 16); + s1[2] = Xor(s2[2], msg, 32); + s1[3] = Xor(s2[3], msg, 48); + + s1[0].AsSpan(8, 8).CopyTo(output); + s1[1].AsSpan(8, 8).CopyTo(output[8..]); + s1[2].AsSpan(0, 8).CopyTo(output[16..]); + s1[3].AsSpan(0, 8).CopyTo(output[24..]); + + return DIGEST_SIZE; } +#endif + + public override string AlgorithmName => "Haraka-512"; public override void Update(byte input) { @@ -193,6 +277,19 @@ namespace Org.BouncyCastle.Crypto.Digests off += len; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void BlockUpdate(ReadOnlySpan<byte> input) + { + if (off + input.Length > 64) + { + throw new ArgumentException("total input cannot be more than 64 bytes"); + } + + input.CopyTo(buffer.AsSpan(off)); + off += input.Length; + } +#endif + public override int DoFinal(byte[] output, int outOff) { if (off != 64) @@ -212,6 +309,27 @@ namespace Org.BouncyCastle.Crypto.Digests return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + if (off != 64) + { + throw new ArgumentException("input must be exactly 64 bytes"); + } + + if (output.Length < 32) + { + throw new ArgumentException("output too short to receive digest"); + } + + int rv = Haraka512256(buffer, output); + + Reset(); + + return rv; + } +#endif + public override void Reset() { off = 0; diff --git a/crypto/src/crypto/digests/HarakaBase.cs b/crypto/src/crypto/digests/HarakaBase.cs
index 6157198f1..1270de35c 100644 --- a/crypto/src/crypto/digests/HarakaBase.cs +++ b/crypto/src/crypto/digests/HarakaBase.cs
@@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Org.BouncyCastle.Crypto.Digests { @@ -26,10 +24,7 @@ namespace Org.BouncyCastle.Crypto.Digests {(byte)0xe1, (byte)0xf8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xd9, (byte)0x8e, (byte)0x94, (byte)0x9b, (byte)0x1e, (byte)0x87, (byte)0xe9, (byte)0xce, (byte)0x55, (byte)0x28, (byte)0xdf}, {(byte)0x8c, (byte)0xa1, (byte)0x89, (byte)0x0d, (byte)0xbf, (byte)0xe6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2d, (byte)0x0f, (byte)0xb0, (byte)0x54, (byte)0xbb, (byte)0x16}}; - public string AlgorithmName - { - get { return "Haraka Base"; } - } + public abstract string AlgorithmName { get; } static byte sBox(byte x) { @@ -144,5 +139,10 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract void Reset(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract void BlockUpdate(ReadOnlySpan<byte> input); + + public abstract int DoFinal(Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs
index 2da2e099e..b8305cc13 100644 --- a/crypto/src/crypto/digests/KeccakDigest.cs +++ b/crypto/src/crypto/digests/KeccakDigest.cs
@@ -76,6 +76,13 @@ namespace Org.BouncyCastle.Crypto.Digests Absorb(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + Absorb(input); + } +#endif + public virtual int DoFinal(byte[] output, int outOff) { Squeeze(output, outOff, fixedOutputLength); @@ -85,6 +92,18 @@ namespace Org.BouncyCastle.Crypto.Digests return GetDigestSize(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + int digestSize = GetDigestSize(); + Squeeze(output[..digestSize]); + + Reset(); + + return digestSize; + } +#endif + /* * TODO Possible API change to support partial-byte suffixes. */ @@ -199,6 +218,46 @@ namespace Org.BouncyCastle.Crypto.Digests this.bitsInQueue = remaining << 3; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected void Absorb(ReadOnlySpan<byte> data) + { + if ((bitsInQueue & 7) != 0) + throw new InvalidOperationException("attempt to absorb with odd length queue"); + if (squeezing) + throw new InvalidOperationException("attempt to absorb while squeezing"); + + int bytesInQueue = bitsInQueue >> 3; + int rateBytes = rate >> 3; + + int len = data.Length; + int available = rateBytes - bytesInQueue; + if (len < available) + { + data.CopyTo(dataQueue.AsSpan(bytesInQueue)); + this.bitsInQueue += len << 3; + return; + } + + int count = 0; + if (bytesInQueue > 0) + { + data[..available].CopyTo(dataQueue.AsSpan(bytesInQueue)); + count += available; + KeccakAbsorb(dataQueue, 0); + } + + int remaining; + while ((remaining = len - count) >= rateBytes) + { + KeccakAbsorb(data[count..]); + count += rateBytes; + } + + data[count..].CopyTo(dataQueue.AsSpan()); + this.bitsInQueue = remaining << 3; + } +#endif + protected void AbsorbBits(int data, int bits) { if (bits < 1 || bits > 7) @@ -270,6 +329,30 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected void Squeeze(Span<byte> output) + { + if (!squeezing) + { + PadAndSwitchToSqueezingPhase(); + } + long outputLength = (long)output.Length << 3; + + long i = 0; + while (i < outputLength) + { + if (bitsInQueue == 0) + { + KeccakExtract(); + } + int partialBlock = (int)System.Math.Min(bitsInQueue, outputLength - i); + dataQueue.AsSpan((rate - bitsInQueue) >> 3, partialBlock >> 3).CopyTo(output[(int)(i >> 3)..]); + bitsInQueue -= partialBlock; + i += partialBlock; + } + } +#endif + private void KeccakAbsorb(byte[] data, int off) { int count = rate >> 6; @@ -282,6 +365,20 @@ namespace Org.BouncyCastle.Crypto.Digests KeccakPermutation(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void KeccakAbsorb(ReadOnlySpan<byte> data) + { + int count = rate >> 6, off = 0; + for (int i = 0; i < count; ++i) + { + state[i] ^= Pack.LE_To_UInt64(data[off..]); + off += 8; + } + + KeccakPermutation(); + } +#endif + private void KeccakExtract() { KeccakPermutation(); diff --git a/crypto/src/crypto/digests/LongDigest.cs b/crypto/src/crypto/digests/LongDigest.cs
index aaa0b43ce..6a2f94ece 100644 --- a/crypto/src/crypto/digests/LongDigest.cs +++ b/crypto/src/crypto/digests/LongDigest.cs
@@ -10,46 +10,46 @@ namespace Org.BouncyCastle.Crypto.Digests * Base class for SHA-384 and SHA-512. */ public abstract class LongDigest - : IDigest, IMemoable + : IDigest, IMemoable { - private int MyByteLength = 128; + private int MyByteLength = 128; - private byte[] xBuf; - private int xBufOff; + private byte[] xBuf; + private int xBufOff; - private long byteCount1; - private long byteCount2; + private long byteCount1; + private long byteCount2; internal ulong H1, H2, H3, H4, H5, H6, H7, H8; private ulong[] W = new ulong[80]; private int wOff; - /** + /** * Constructor for variable length word */ internal LongDigest() { xBuf = new byte[8]; - Reset(); + Reset(); } - /** + /** * Copy constructor. We are using copy constructors in place * of the object.Clone() interface as this interface is not * supported by J2ME. */ internal LongDigest( - LongDigest t) - { - xBuf = new byte[t.xBuf.Length]; + LongDigest t) + { + xBuf = new byte[t.xBuf.Length]; - CopyIn(t); - } + CopyIn(t); + } - protected void CopyIn(LongDigest t) - { + protected void CopyIn(LongDigest t) + { Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); xBufOff = t.xBufOff; @@ -84,9 +84,9 @@ namespace Org.BouncyCastle.Crypto.Digests } public void BlockUpdate( - byte[] input, - int inOff, - int length) + byte[] input, + int inOff, + int length) { // // fill the current word @@ -123,12 +123,54 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int inOff = 0; + int length = input.Length; + + // + // fill the current word + // + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length >= xBuf.Length) + { + ProcessWord(input.Slice(inOff, xBuf.Length)); + + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount1 += xBuf.Length; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } +#endif + public void Finish() { AdjustByteCounts(); - long lowBitLength = byteCount1 << 3; - long hiBitLength = byteCount2; + long lowBitLength = byteCount1 << 3; + long hiBitLength = byteCount2; // // add the pad bytes. @@ -151,20 +193,20 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount2 = 0; xBufOff = 0; - for ( int i = 0; i < xBuf.Length; i++ ) + for (int i = 0; i < xBuf.Length; i++) { xBuf[i] = 0; } wOff = 0; - Array.Clear(W, 0, W.Length); + Array.Clear(W, 0, W.Length); } internal void ProcessWord( - byte[] input, - int inOff) + byte[] input, + int inOff) { - W[wOff] = Pack.BE_To_UInt64(input, inOff); + W[wOff] = Pack.BE_To_UInt64(input, inOff); if (++wOff == 16) { @@ -172,6 +214,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void ProcessWord(ReadOnlySpan<byte> word) + { + W[wOff] = Pack.BE_To_UInt64(word); + + if (++wOff == 16) + { + ProcessBlock(); + } + } +#endif + /** * adjust the byte counts so that byteCount2 represents the * upper long (less 3 bits) word of the byte count. @@ -180,14 +234,14 @@ namespace Org.BouncyCastle.Crypto.Digests { if (byteCount1 > 0x1fffffffffffffffL) { - byteCount2 += (long) ((ulong) byteCount1 >> 61); + byteCount2 += (long)((ulong)byteCount1 >> 61); byteCount1 &= 0x1fffffffffffffffL; } } internal void ProcessLength( - long lowW, - long hiW) + long lowW, + long hiW) { if (wOff > 14) { @@ -222,51 +276,51 @@ namespace Org.BouncyCastle.Crypto.Digests ulong g = H7; ulong h = H8; - int t = 0; - for(int i = 0; i < 10; i ++) - { - // t = 8 * i - h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++]; - d += h; - h += Sum0(a) + Maj(a, b, c); - - // t = 8 * i + 1 - g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++]; - c += g; - g += Sum0(h) + Maj(h, a, b); - - // t = 8 * i + 2 - f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++]; - b += f; - f += Sum0(g) + Maj(g, h, a); - - // t = 8 * i + 3 - e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++]; - a += e; - e += Sum0(f) + Maj(f, g, h); - - // t = 8 * i + 4 - d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++]; - h += d; - d += Sum0(e) + Maj(e, f, g); - - // t = 8 * i + 5 - c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++]; - g += c; - c += Sum0(d) + Maj(d, e, f); - - // t = 8 * i + 6 - b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++]; - f += b; - b += Sum0(c) + Maj(c, d, e); - - // t = 8 * i + 7 - a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++]; - e += a; - a += Sum0(b) + Maj(b, c, d); - } - - H1 += a; + int t = 0; + for (int i = 0; i < 10; i++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; H2 += b; H3 += c; H4 += d; @@ -275,14 +329,14 @@ namespace Org.BouncyCastle.Crypto.Digests H7 += g; H8 += h; - // + // // reset the offset and clean out the word buffer. // wOff = 0; - Array.Clear(W, 0, 16); - } + Array.Clear(W, 0, 16); + } - /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ + /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ private static ulong Ch(ulong x, ulong y, ulong z) { return (x & y) ^ (~x & z); @@ -295,61 +349,64 @@ namespace Org.BouncyCastle.Crypto.Digests private static ulong Sum0(ulong x) { - return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); + return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); } - private static ulong Sum1(ulong x) + private static ulong Sum1(ulong x) { - return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); + return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); } private static ulong Sigma0(ulong x) { - return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); + return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); } private static ulong Sigma1(ulong x) { - return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); + return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); } /* SHA-384 and SHA-512 Constants * (represent the first 64 bits of the fractional parts of the * cube roots of the first sixty-four prime numbers) */ - internal static readonly ulong[] K = - { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 - }; - - public int GetByteLength() - { - return MyByteLength; - } - - public abstract string AlgorithmName { get; } - public abstract int GetDigestSize(); + internal static readonly ulong[] K = + { + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 + }; + + public int GetByteLength() + { + return MyByteLength; + } + + public abstract string AlgorithmName { get; } + public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); - public abstract IMemoable Copy(); - public abstract void Reset(IMemoable t); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract int DoFinal(Span<byte> output); +#endif + public abstract IMemoable Copy(); + public abstract void Reset(IMemoable t); } } diff --git a/crypto/src/crypto/digests/MD2Digest.cs b/crypto/src/crypto/digests/MD2Digest.cs
index f72d08768..cea89a311 100644 --- a/crypto/src/crypto/digests/MD2Digest.cs +++ b/crypto/src/crypto/digests/MD2Digest.cs
@@ -1,6 +1,5 @@ using System; -using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests @@ -99,6 +98,30 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + // add padding + byte paddingByte = (byte)(M.Length - mOff); + for (int i = mOff; i < M.Length; i++) + { + M[i] = paddingByte; + } + //do final check sum + ProcessChecksum(M); + // do final block process + ProcessBlock(M); + + ProcessBlock(C); + + X.AsSpan(xOff, 16).CopyTo(output); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the digest back to it's initial state. */ @@ -179,6 +202,40 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + // + // fill the current word + // + while ((mOff != 0) && (input.Length > 0)) + { + Update(input[0]); + input = input[1..]; + } + + // + // process whole words. + // + while (input.Length >= 16) + { + input[..16].CopyTo(M); + ProcessChecksum(M); + ProcessBlock(M); + input = input[16..]; + } + + // + // load in the remainder. + // + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif + internal void ProcessChecksum(byte[] m) { int L = C[15]; diff --git a/crypto/src/crypto/digests/MD4Digest.cs b/crypto/src/crypto/digests/MD4Digest.cs
index 8743f7dad..2eb2c8400 100644 --- a/crypto/src/crypto/digests/MD4Digest.cs +++ b/crypto/src/crypto/digests/MD4Digest.cs
@@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests @@ -60,12 +61,9 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { - X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) - | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + X[xOff++] = (int)Pack.LE_To_UInt32(input, inOff); if (xOff == 16) { @@ -73,6 +71,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -85,32 +95,35 @@ namespace Org.BouncyCastle.Crypto.Digests X[15] = (int)((ulong) bitLength >> 32); } - private void UnpackWord( - int word, - byte[] outBytes, - int outOff) + public override int DoFinal(byte[] output, int outOff) { - outBytes[outOff] = (byte)word; - outBytes[outOff + 1] = (byte)((uint) word >> 8); - outBytes[outOff + 2] = (byte)((uint) word >> 16); - outBytes[outOff + 3] = (byte)((uint) word >> 24); + Finish(); + + Pack.UInt32_To_LE((uint)H1, output, outOff); + Pack.UInt32_To_LE((uint)H2, output, outOff + 4); + Pack.UInt32_To_LE((uint)H3, output, outOff + 8); + Pack.UInt32_To_LE((uint)H4, output, outOff + 12); + + Reset(); + + return DigestLength; } - public override int DoFinal( - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) { Finish(); - UnpackWord(H1, output, outOff); - UnpackWord(H2, output, outOff + 4); - UnpackWord(H3, output, outOff + 8); - UnpackWord(H4, output, outOff + 12); + Pack.UInt32_To_LE((uint)H1, output); + Pack.UInt32_To_LE((uint)H2, output[4..]); + Pack.UInt32_To_LE((uint)H3, output[8..]); + Pack.UInt32_To_LE((uint)H4, output[12..]); Reset(); return DigestLength; } +#endif /** * reset the chaining variables to the IV values. diff --git a/crypto/src/crypto/digests/MD5Digest.cs b/crypto/src/crypto/digests/MD5Digest.cs
index c60ac92a3..062d7bb46 100644 --- a/crypto/src/crypto/digests/MD5Digest.cs +++ b/crypto/src/crypto/digests/MD5Digest.cs
@@ -55,9 +55,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.LE_To_UInt32(input, inOff); @@ -67,6 +65,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.LE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -103,6 +113,22 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE(H1, output); + Pack.UInt32_To_LE(H2, output[4..]); + Pack.UInt32_To_LE(H3, output[8..]); + Pack.UInt32_To_LE(H4, output[12..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/NonMemoableDigest.cs b/crypto/src/crypto/digests/NonMemoableDigest.cs
index 02c49b887..bad38911b 100644 --- a/crypto/src/crypto/digests/NonMemoableDigest.cs +++ b/crypto/src/crypto/digests/NonMemoableDigest.cs
@@ -44,11 +44,25 @@ namespace Org.BouncyCastle.Crypto.Digests mBaseDigest.BlockUpdate(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + mBaseDigest.BlockUpdate(input); + } +#endif + public virtual int DoFinal(byte[] output, int outOff) { return mBaseDigest.DoFinal(output, outOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + return mBaseDigest.DoFinal(output); + } +#endif + public virtual void Reset() { mBaseDigest.Reset(); diff --git a/crypto/src/crypto/digests/NullDigest.cs b/crypto/src/crypto/digests/NullDigest.cs
index d14dd5c9f..28554cf3e 100644 --- a/crypto/src/crypto/digests/NullDigest.cs +++ b/crypto/src/crypto/digests/NullDigest.cs
@@ -35,17 +35,48 @@ namespace Org.BouncyCastle.Crypto.Digests bOut.Write(inBytes, inOff, len); } - public int DoFinal(byte[] outBytes, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { + bOut.Write(input); + } +#endif + + public int DoFinal(byte[] outBytes, int outOff) + { + try + { + byte[] data = bOut.GetBuffer(); + int length = Convert.ToInt32(bOut.Length); + + Array.Copy(data, 0, outBytes, outOff, length); + + return length; + } + finally + { + Reset(); + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { try { - return Streams.WriteBufTo(bOut, outBytes, outOff); + byte[] data = bOut.GetBuffer(); + int length = Convert.ToInt32(bOut.Length); + + data.AsSpan(0, length).CopyTo(output); + + return length; } finally { Reset(); } } +#endif public void Reset() { diff --git a/crypto/src/crypto/digests/ParallelHash.cs b/crypto/src/crypto/digests/ParallelHash.cs
index f28795f5a..8054b2005 100644 --- a/crypto/src/crypto/digests/ParallelHash.cs +++ b/crypto/src/crypto/digests/ParallelHash.cs
@@ -85,7 +85,7 @@ namespace Org.BouncyCastle.Crypto.Digests buffer[bufOff++] = b; if (bufOff == buffer.Length) { - compress(); + Compress(); } } @@ -106,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Digests if (bufOff == buffer.Length) { - compress(); + Compress(); } } @@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Digests { while (len - i >= B) { - compress(inBuf, inOff + i, B); + Compress(inBuf, inOff + i, B); i += B; } } @@ -125,13 +125,49 @@ namespace Org.BouncyCastle.Crypto.Digests } } - private void compress() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) { - compress(buffer, 0, bufOff); + // + // fill the current word + // + int i = 0; + if (bufOff != 0) + { + while (i < input.Length && bufOff != buffer.Length) + { + buffer[bufOff++] = input[i++]; + } + + if (bufOff == buffer.Length) + { + Compress(); + } + } + + if (i < input.Length) + { + while (input.Length - i >= B) + { + Compress(input, i, B); + i += B; + } + } + + while (i < input.Length) + { + Update(input[i++]); + } + } +#endif + + private void Compress() + { + Compress(buffer, 0, bufOff); bufOff = 0; } - private void compress(byte[] buf, int offSet, int len) + private void Compress(byte[] buf, int offSet, int len) { compressor.BlockUpdate(buf, offSet, len); compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length); @@ -141,11 +177,23 @@ namespace Org.BouncyCastle.Crypto.Digests nCount++; } - private void wrapUp(int outputSize) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Compress(ReadOnlySpan<byte> input, int pos, int len) + { + compressor.BlockUpdate(input.Slice(pos, len)); + compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length); + + cshake.BlockUpdate(compressorBuffer, 0, compressorBuffer.Length); + + nCount++; + } +#endif + + private void WrapUp(int outputSize) { if (bufOff != 0) { - compress(); + Compress(); } byte[] nOut = XofUtilities.RightEncode(nCount); byte[] encOut = XofUtilities.RightEncode(outputSize * 8); @@ -160,21 +208,37 @@ namespace Org.BouncyCastle.Crypto.Digests { if (firstOutput) { - wrapUp(outputLength); + WrapUp(outputLength); + } + + int rv = cshake.DoFinal(outBuf, outOff); + + Reset(); + + return rv; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + if (firstOutput) + { + WrapUp(outputLength); } - int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize()); + int rv = cshake.DoFinal(output); Reset(); return rv; } +#endif public virtual int DoFinal(byte[] outBuf, int outOff, int outLen) { if (firstOutput) { - wrapUp(outputLength); + WrapUp(outputLength); } int rv = cshake.DoFinal(outBuf, outOff, outLen); @@ -184,16 +248,44 @@ namespace Org.BouncyCastle.Crypto.Digests return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int OutputFinal(Span<byte> output) + { + if (firstOutput) + { + WrapUp(outputLength); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } +#endif + public virtual int DoOutput(byte[] outBuf, int outOff, int outLen) { if (firstOutput) { - wrapUp(0); + WrapUp(0); } return cshake.DoOutput(outBuf, outOff, outLen); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int Output(Span<byte> output) + { + if (firstOutput) + { + WrapUp(0); + } + + return cshake.Output(output); + } +#endif + public virtual void Reset() { cshake.Reset(); diff --git a/crypto/src/crypto/digests/RipeMD128Digest.cs b/crypto/src/crypto/digests/RipeMD128Digest.cs
index cba2c65d3..b66452682 100644 --- a/crypto/src/crypto/digests/RipeMD128Digest.cs +++ b/crypto/src/crypto/digests/RipeMD128Digest.cs
@@ -68,6 +68,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -94,6 +106,22 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/RipeMD160Digest.cs b/crypto/src/crypto/digests/RipeMD160Digest.cs
index 0fc2a4a1c..a95bff48a 100644 --- a/crypto/src/crypto/digests/RipeMD160Digest.cs +++ b/crypto/src/crypto/digests/RipeMD160Digest.cs
@@ -70,6 +70,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -97,6 +109,23 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + Pack.UInt32_To_LE((uint)H4, output[16..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/RipeMD256Digest.cs b/crypto/src/crypto/digests/RipeMD256Digest.cs
index 621162a6f..40508e9f7 100644 --- a/crypto/src/crypto/digests/RipeMD256Digest.cs +++ b/crypto/src/crypto/digests/RipeMD256Digest.cs
@@ -70,6 +70,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -100,6 +112,26 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + Pack.UInt32_To_LE((uint)H4, output[16..]); + Pack.UInt32_To_LE((uint)H5, output[20..]); + Pack.UInt32_To_LE((uint)H6, output[24..]); + Pack.UInt32_To_LE((uint)H7, output[28..]); + + Reset(); + + return DigestLength; + } +#endif + /// <summary> reset the chaining variables to the IV values.</summary> public override void Reset() { diff --git a/crypto/src/crypto/digests/RipeMD320Digest.cs b/crypto/src/crypto/digests/RipeMD320Digest.cs
index c46bc4fea..ddaf858ff 100644 --- a/crypto/src/crypto/digests/RipeMD320Digest.cs +++ b/crypto/src/crypto/digests/RipeMD320Digest.cs
@@ -73,6 +73,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -105,6 +117,28 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + Pack.UInt32_To_LE((uint)H4, output[16..]); + Pack.UInt32_To_LE((uint)H5, output[20..]); + Pack.UInt32_To_LE((uint)H6, output[24..]); + Pack.UInt32_To_LE((uint)H7, output[28..]); + Pack.UInt32_To_LE((uint)H8, output[32..]); + Pack.UInt32_To_LE((uint)H9, output[36..]); + + Reset(); + + return DigestLength; + } +#endif + /// <summary> reset the chaining variables to the IV values.</summary> public override void Reset() { diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs
index 3dc63a6ed..778c453d8 100644 --- a/crypto/src/crypto/digests/SHA3Digest.cs +++ b/crypto/src/crypto/digests/SHA3Digest.cs
@@ -55,6 +55,15 @@ namespace Org.BouncyCastle.Crypto.Digests return base.DoFinal(output, outOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + AbsorbBits(0x02, 2); + + return base.DoFinal(output); + } +#endif + /* * TODO Possible API change to support partial-byte suffixes. */ diff --git a/crypto/src/crypto/digests/SM3Digest.cs b/crypto/src/crypto/digests/SM3Digest.cs
index 449d7c161..81d4a68a9 100644 --- a/crypto/src/crypto/digests/SM3Digest.cs +++ b/crypto/src/crypto/digests/SM3Digest.cs
@@ -118,7 +118,6 @@ namespace Org.BouncyCastle.Crypto.Digests this.xOff = 0; } - public override int DoFinal(byte[] output, int outOff) { Finish(); @@ -130,13 +129,22 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_LENGTH; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(V, output); + + Reset(); - internal override void ProcessWord(byte[] input, - int inOff) + return DIGEST_LENGTH; + } +#endif + + internal override void ProcessWord(byte[] input, int inOff) { - uint n = Pack.BE_To_UInt32(input, inOff); - this.inwords[this.xOff] = n; - ++this.xOff; + inwords[xOff++] = Pack.BE_To_UInt32(input, inOff); if (this.xOff >= 16) { @@ -144,7 +152,19 @@ namespace Org.BouncyCastle.Crypto.Digests } } - internal override void ProcessLength(long bitLength) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + inwords[xOff++] = Pack.BE_To_UInt32(word); + + if (this.xOff >= 16) + { + ProcessBlock(); + } + } +#endif + + internal override void ProcessLength(long bitLength) { if (this.xOff > (BLOCK_SIZE - 2)) { diff --git a/crypto/src/crypto/digests/Sha1Digest.cs b/crypto/src/crypto/digests/Sha1Digest.cs
index 60ec651d5..9b384b8cb 100644 --- a/crypto/src/crypto/digests/Sha1Digest.cs +++ b/crypto/src/crypto/digests/Sha1Digest.cs
@@ -61,9 +61,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.BE_To_UInt32(input, inOff); @@ -73,6 +71,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.BE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength(long bitLength) { if (xOff > 14) @@ -101,6 +111,23 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(H1, output); + Pack.UInt32_To_BE(H2, output[4..]); + Pack.UInt32_To_BE(H3, output[8..]); + Pack.UInt32_To_BE(H4, output[12..]); + Pack.UInt32_To_BE(H5, output[16..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -279,6 +306,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha224Digest.cs b/crypto/src/crypto/digests/Sha224Digest.cs
index b4e853745..28d09adec 100644 --- a/crypto/src/crypto/digests/Sha224Digest.cs +++ b/crypto/src/crypto/digests/Sha224Digest.cs
@@ -72,9 +72,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.BE_To_UInt32(input, inOff); @@ -84,7 +82,19 @@ namespace Org.BouncyCastle.Crypto.Digests } } - internal override void ProcessLength( +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.BE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + + internal override void ProcessLength( long bitLength) { if (xOff > 14) @@ -96,9 +106,7 @@ namespace Org.BouncyCastle.Crypto.Digests X[15] = (uint)((ulong)bitLength); } - public override int DoFinal( - byte[] output, - int outOff) + public override int DoFinal(byte[] output, int outOff) { Finish(); @@ -115,7 +123,26 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(H1, output); + Pack.UInt32_To_BE(H2, output[4..]); + Pack.UInt32_To_BE(H3, output[8..]); + Pack.UInt32_To_BE(H4, output[12..]); + Pack.UInt32_To_BE(H5, output[16..]); + Pack.UInt32_To_BE(H6, output[20..]); + Pack.UInt32_To_BE(H7, output[24..]); + + Reset(); + + return DigestLength; + } +#endif + + /** * reset the chaining variables */ public override void Reset() @@ -284,6 +311,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha256Digest.cs b/crypto/src/crypto/digests/Sha256Digest.cs
index 63d5b8bee..51859697e 100644 --- a/crypto/src/crypto/digests/Sha256Digest.cs +++ b/crypto/src/crypto/digests/Sha256Digest.cs
@@ -67,9 +67,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.BE_To_UInt32(input, inOff); @@ -79,7 +77,19 @@ namespace Org.BouncyCastle.Crypto.Digests } } - internal override void ProcessLength( +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.BE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + + internal override void ProcessLength( long bitLength) { if (xOff > 14) @@ -91,26 +101,44 @@ namespace Org.BouncyCastle.Crypto.Digests X[15] = (uint)((ulong)bitLength); } - public override int DoFinal( - byte[] output, - int outOff) + public override int DoFinal(byte[] output, int outOff) { Finish(); - Pack.UInt32_To_BE((uint)H1, output, outOff); - Pack.UInt32_To_BE((uint)H2, output, outOff + 4); - Pack.UInt32_To_BE((uint)H3, output, outOff + 8); - Pack.UInt32_To_BE((uint)H4, output, outOff + 12); - Pack.UInt32_To_BE((uint)H5, output, outOff + 16); - Pack.UInt32_To_BE((uint)H6, output, outOff + 20); - Pack.UInt32_To_BE((uint)H7, output, outOff + 24); - Pack.UInt32_To_BE((uint)H8, output, outOff + 28); + Pack.UInt32_To_BE(H1, output, outOff); + Pack.UInt32_To_BE(H2, output, outOff + 4); + Pack.UInt32_To_BE(H3, output, outOff + 8); + Pack.UInt32_To_BE(H4, output, outOff + 12); + Pack.UInt32_To_BE(H5, output, outOff + 16); + Pack.UInt32_To_BE(H6, output, outOff + 20); + Pack.UInt32_To_BE(H7, output, outOff + 24); + Pack.UInt32_To_BE(H8, output, outOff + 28); Reset(); return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(H1, output); + Pack.UInt32_To_BE(H2, output[4..]); + Pack.UInt32_To_BE(H3, output[8..]); + Pack.UInt32_To_BE(H4, output[12..]); + Pack.UInt32_To_BE(H5, output[16..]); + Pack.UInt32_To_BE(H6, output[20..]); + Pack.UInt32_To_BE(H7, output[24..]); + Pack.UInt32_To_BE(H8, output[28..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -313,6 +341,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha384Digest.cs b/crypto/src/crypto/digests/Sha384Digest.cs
index e6c9a9aa9..e4e65ed85 100644 --- a/crypto/src/crypto/digests/Sha384Digest.cs +++ b/crypto/src/crypto/digests/Sha384Digest.cs
@@ -64,6 +64,24 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt64_To_BE(H1, output); + Pack.UInt64_To_BE(H2, output[8..]); + Pack.UInt64_To_BE(H3, output[16..]); + Pack.UInt64_To_BE(H4, output[24..]); + Pack.UInt64_To_BE(H5, output[32..]); + Pack.UInt64_To_BE(H6, output[40..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -72,9 +90,9 @@ namespace Org.BouncyCastle.Crypto.Digests base.Reset(); /* SHA-384 initial hash value - * The first 64 bits of the fractional parts of the square roots - * of the 9th through 16th prime numbers - */ + * The first 64 bits of the fractional parts of the square roots + * of the 9th through 16th prime numbers + */ H1 = 0xcbbb9d5dc1059ed8; H2 = 0x629a292a367cd507; H3 = 0x9159015a3070dd17; @@ -96,6 +114,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha512Digest.cs b/crypto/src/crypto/digests/Sha512Digest.cs
index 2a0964fd3..9156c24bf 100644 --- a/crypto/src/crypto/digests/Sha512Digest.cs +++ b/crypto/src/crypto/digests/Sha512Digest.cs
@@ -67,6 +67,26 @@ namespace Org.BouncyCastle.Crypto.Digests } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt64_To_BE(H1, output); + Pack.UInt64_To_BE(H2, output[8..]); + Pack.UInt64_To_BE(H3, output[16..]); + Pack.UInt64_To_BE(H4, output[24..]); + Pack.UInt64_To_BE(H5, output[32..]); + Pack.UInt64_To_BE(H6, output[40..]); + Pack.UInt64_To_BE(H7, output[48..]); + Pack.UInt64_To_BE(H8, output[56..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -99,6 +119,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha512tDigest.cs b/crypto/src/crypto/digests/Sha512tDigest.cs
index 2caefa763..939dbda4f 100644 --- a/crypto/src/crypto/digests/Sha512tDigest.cs +++ b/crypto/src/crypto/digests/Sha512tDigest.cs
@@ -76,6 +76,26 @@ namespace Org.BouncyCastle.Crypto.Digests return digestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + UInt64_To_BE(H1, output, 0, digestLength); + UInt64_To_BE(H2, output, 8, digestLength - 8); + UInt64_To_BE(H3, output, 16, digestLength - 16); + UInt64_To_BE(H4, output, 24, digestLength - 24); + UInt64_To_BE(H5, output, 32, digestLength - 32); + UInt64_To_BE(H6, output, 40, digestLength - 40); + UInt64_To_BE(H7, output, 48, digestLength - 48); + UInt64_To_BE(H8, output, 56, digestLength - 56); + + Reset(); + + return digestLength; + } +#endif + /** * reset the chaining variables */ @@ -170,7 +190,32 @@ namespace Org.BouncyCastle.Crypto.Digests } } - public override IMemoable Copy() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void UInt64_To_BE(ulong n, Span<byte> bs, int off, int max) + { + if (max > 0) + { + UInt32_To_BE((uint)(n >> 32), bs, off, max); + + if (max > 4) + { + UInt32_To_BE((uint)n, bs, off + 4, max - 4); + } + } + } + + private static void UInt32_To_BE(uint n, Span<byte> bs, int off, int max) + { + int num = System.Math.Min(4, max); + while (--num >= 0) + { + int shift = 8 * (3 - num); + bs[off + num] = (byte)(n >> shift); + } + } +#endif + + public override IMemoable Copy() { return new Sha512tDigest(this); } diff --git a/crypto/src/crypto/digests/ShakeDigest.cs b/crypto/src/crypto/digests/ShakeDigest.cs
index 8d7a7d6e3..17d262261 100644 --- a/crypto/src/crypto/digests/ShakeDigest.cs +++ b/crypto/src/crypto/digests/ShakeDigest.cs
@@ -77,6 +77,34 @@ namespace Org.BouncyCastle.Crypto.Digests return outLen; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public virtual int OutputFinal(Span<byte> output) + { + int length = Output(output); + + Reset(); + + return length; + } + + public virtual int Output(Span<byte> output) + { + if (!squeezing) + { + AbsorbBits(0x0F, 4); + } + + Squeeze(output); + + return output.Length; + } +#endif + /* * TODO Possible API change to support partial-byte suffixes. */ diff --git a/crypto/src/crypto/digests/ShortenedDigest.cs b/crypto/src/crypto/digests/ShortenedDigest.cs
index 9e4d99e7b..9e9998560 100644 --- a/crypto/src/crypto/digests/ShortenedDigest.cs +++ b/crypto/src/crypto/digests/ShortenedDigest.cs
@@ -1,5 +1,4 @@ using System; -using Org.BouncyCastle.Crypto; namespace Org.BouncyCastle.Crypto.Digests { @@ -58,7 +57,14 @@ namespace Org.BouncyCastle.Crypto.Digests baseDigest.BlockUpdate(input, inOff, length); } - public int DoFinal(byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + baseDigest.BlockUpdate(input); + } +#endif + + public int DoFinal(byte[] output, int outOff) { byte[] tmp = new byte[baseDigest.GetDigestSize()]; @@ -69,7 +75,20 @@ namespace Org.BouncyCastle.Crypto.Digests return length; } - public void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Span<byte> tmp = stackalloc byte[baseDigest.GetDigestSize()]; + + baseDigest.DoFinal(tmp); + + tmp[..length].CopyTo(output); + + return length; + } +#endif + + public void Reset() { baseDigest.Reset(); } diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs
index 394f0acd5..3dba9ec75 100644 --- a/crypto/src/crypto/digests/SkeinDigest.cs +++ b/crypto/src/crypto/digests/SkeinDigest.cs
@@ -5,7 +5,6 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { - /// <summary> /// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, /// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher. @@ -111,5 +110,16 @@ namespace Org.BouncyCastle.Crypto.Digests return engine.DoFinal(outBytes, outOff); } - } -} \ No newline at end of file +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + engine.Update(input); + } + + public int DoFinal(Span<byte> output) + { + return engine.DoFinal(output); + } +#endif + } +} diff --git a/crypto/src/crypto/digests/SkeinEngine.cs b/crypto/src/crypto/digests/SkeinEngine.cs
index a36ac8fe7..86aa3c938 100644 --- a/crypto/src/crypto/digests/SkeinEngine.cs +++ b/crypto/src/crypto/digests/SkeinEngine.cs
@@ -431,7 +431,7 @@ namespace Org.BouncyCastle.Crypto.Digests currentOffset = 0; } - int toCopy = System.Math.Min((len - copied), currentBlock.Length - currentOffset); + int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset); Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy); copied += toCopy; currentOffset += toCopy; @@ -439,6 +439,32 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Update(ReadOnlySpan<byte> input, ulong[] output) + { + /* + * Buffer complete blocks for the underlying Threefish cipher, only flushing when there + * are subsequent bytes (last block must be processed in doFinal() with final=true set). + */ + int copied = 0, len = input.Length; + while (len > copied) + { + if (currentOffset == currentBlock.Length) + { + ProcessBlock(output); + tweak.First = false; + currentOffset = 0; + } + + int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset); + input.Slice(copied, toCopy).CopyTo(currentBlock.AsSpan(currentOffset)); + copied += toCopy; + currentOffset += toCopy; + tweak.AdvancePosition(toCopy); + } + } +#endif + private void ProcessBlock(ulong[] output) { engine.threefish.Init(true, engine.chain, tweak.GetWords()); @@ -735,6 +761,14 @@ namespace Org.BouncyCastle.Crypto.Digests ubi.Update(inBytes, inOff, len, chain); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Update(ReadOnlySpan<byte> input) + { + CheckInitialised(); + ubi.Update(input, chain); + } +#endif + public int DoFinal(byte[] outBytes, int outOff) { CheckInitialised(); @@ -770,6 +804,42 @@ namespace Org.BouncyCastle.Crypto.Digests return outputSizeBytes; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + CheckInitialised(); + if (output.Length < outputSizeBytes) + throw new DataLengthException("Output span is too short to hold output"); + + // Finalise message block + UbiFinal(); + + // Process additional post-message parameters + if (postMessageParameters != null) + { + for (int i = 0; i < postMessageParameters.Length; i++) + { + Parameter param = postMessageParameters[i]; + UbiComplete(param.Type, param.Value); + } + } + + // Perform the output transform + int blockSize = BlockSize; + int blocksRequired = (outputSizeBytes + blockSize - 1) / blockSize; + for (int i = 0; i < blocksRequired; i++) + { + int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize)); + //Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite); + Output((ulong)i, output[(i * blockSize)..], toWrite); + } + + Reset(); + + return outputSizeBytes; + } +#endif + private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes) { byte[] currentBytes = new byte[8]; @@ -796,5 +866,34 @@ namespace Org.BouncyCastle.Crypto.Digests } } } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Output(ulong outputSequence, Span<byte> output, int outputBytes) + { + Span<byte> currentBytes = stackalloc byte[8]; + Pack.UInt64_To_LE(outputSequence, currentBytes); + + // Output is a sequence of UBI invocations all of which use and preserve the pre-output state + ulong[] outputWords = new ulong[chain.Length]; + UbiInit(PARAM_TYPE_OUTPUT); + this.ubi.Update(currentBytes, outputWords); + ubi.DoFinal(outputWords); + + int wordsRequired = (outputBytes + 8 - 1) / 8; + for (int i = 0; i < wordsRequired; i++) + { + int toWrite = System.Math.Min(8, outputBytes - (i * 8)); + if (toWrite == 8) + { + Pack.UInt64_To_LE(outputWords[i], output[(i * 8)..]); + } + else + { + Pack.UInt64_To_LE(outputWords[i], currentBytes); + currentBytes[..toWrite].CopyTo(output[(i * 8)..]); + } + } + } +#endif } } diff --git a/crypto/src/crypto/digests/TigerDigest.cs b/crypto/src/crypto/digests/TigerDigest.cs
index a452d3f0b..d83e905db 100644 --- a/crypto/src/crypto/digests/TigerDigest.cs +++ b/crypto/src/crypto/digests/TigerDigest.cs
@@ -603,6 +603,20 @@ namespace Org.BouncyCastle.Crypto.Digests bOff = 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void ProcessWord(ReadOnlySpan<byte> b) + { + x[xOff++] = (long)Pack.LE_To_UInt64(b); + + if (xOff == x.Length) + { + ProcessBlock(); + } + + bOff = 0; + } +#endif + public void Update( byte input) { @@ -656,6 +670,47 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int inOff = 0, length = input.Length; + + // + // fill the current word + // + while ((bOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length >= 8) + { + ProcessWord(input[inOff..]); + + inOff += 8; + length -= 8; + byteCount += 8; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } +#endif + private void RoundABC( long x, long mul) @@ -809,6 +864,21 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt64_To_LE((ulong)a, output); + Pack.UInt64_To_LE((ulong)b, output[8..]); + Pack.UInt64_To_LE((ulong)c, output[16..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ diff --git a/crypto/src/crypto/digests/TupleHash.cs b/crypto/src/crypto/digests/TupleHash.cs
index 98c2d2acf..43030d5d5 100644 --- a/crypto/src/crypto/digests/TupleHash.cs +++ b/crypto/src/crypto/digests/TupleHash.cs
@@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Crypto.Digests cshake.BlockUpdate(bytes, 0, bytes.Length); } - private void wrapUp(int outputSize) + private void WrapUp(int outputSize) { byte[] encOut = XofUtilities.RightEncode(outputSize * 8); @@ -89,23 +89,14 @@ namespace Org.BouncyCastle.Crypto.Digests public virtual int DoFinal(byte[] outBuf, int outOff) { - if (firstOutput) - { - wrapUp(GetDigestSize()); - } - - int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize()); - - Reset(); - - return rv; + return DoFinal(outBuf, outOff, GetDigestSize()); } public virtual int DoFinal(byte[] outBuf, int outOff, int outLen) { if (firstOutput) { - wrapUp(GetDigestSize()); + WrapUp(GetDigestSize()); } int rv = cshake.DoFinal(outBuf, outOff, outLen); @@ -119,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Digests { if (firstOutput) { - wrapUp(0); + WrapUp(0); } return cshake.DoOutput(outBuf, outOff, outLen); @@ -130,5 +121,41 @@ namespace Org.BouncyCastle.Crypto.Digests cshake.Reset(); firstOutput = true; } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + XofUtilities.EncodeTo(cshake, input); + } + + public virtual int DoFinal(Span<byte> output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public virtual int OutputFinal(Span<byte> output) + { + if (firstOutput) + { + WrapUp(GetDigestSize()); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } + + public virtual int Output(Span<byte> output) + { + if (firstOutput) + { + WrapUp(0); + } + + return cshake.Output(output); + } +#endif } } diff --git a/crypto/src/crypto/digests/WhirlpoolDigest.cs b/crypto/src/crypto/digests/WhirlpoolDigest.cs
index b28e259f3..73d389a3c 100644 --- a/crypto/src/crypto/digests/WhirlpoolDigest.cs +++ b/crypto/src/crypto/digests/WhirlpoolDigest.cs
@@ -162,6 +162,20 @@ namespace Org.BouncyCastle.Crypto.Digests return GetDigestSize(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + // sets output[0..DIGEST_LENGTH_BYTES] + Finish(); + + Pack.UInt64_To_BE(_hash, output); + + Reset(); + + return GetDigestSize(); + } +#endif + /** * Reset the chaining variables */ @@ -276,6 +290,16 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + for (int i = 0; i < input.Length; ++i) + { + Update(input[i]); + } + } +#endif + private void Finish() { /* diff --git a/crypto/src/crypto/digests/XofUtils.cs b/crypto/src/crypto/digests/XofUtils.cs
index a4d6622b3..a1242f987 100644 --- a/crypto/src/crypto/digests/XofUtils.cs +++ b/crypto/src/crypto/digests/XofUtils.cs
@@ -28,6 +28,26 @@ namespace Org.BouncyCastle.Crypto.Digests return b; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static int LeftEncode(long length, Span<byte> lengthEncoding) + { + byte n = 1; + + long v = length; + while ((v >>= 8) != 0) + { + n++; + } + + lengthEncoding[0] = n; + for (int i = 1; i <= n; i++) + { + lengthEncoding[i] = (byte)(length >> (8 * (n - i))); + } + return 1 + n; + } +#endif + internal static byte[] RightEncode(long strLen) { byte n = 1; @@ -50,6 +70,26 @@ namespace Org.BouncyCastle.Crypto.Digests return b; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static int RightEncode(long length, Span<byte> lengthEncoding) + { + byte n = 1; + + long v = length; + while ((v >>= 8) != 0) + { + n++; + } + + lengthEncoding[n] = n; + for (int i = 0; i < n; i++) + { + lengthEncoding[i] = (byte)(length >> (8 * (n - i - 1))); + } + return n + 1; + } +#endif + internal static byte[] Encode(byte X) { return Arrays.Concatenate(LeftEncode(8), new byte[] { X }); @@ -63,5 +103,15 @@ namespace Org.BouncyCastle.Crypto.Digests } return Arrays.Concatenate(LeftEncode(len * 8), Arrays.CopyOfRange(inBuf, inOff, inOff + len)); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void EncodeTo(IDigest digest, ReadOnlySpan<byte> buf) + { + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = LeftEncode(buf.Length * 8, lengthEncoding); + digest.BlockUpdate(lengthEncoding[..count]); + digest.BlockUpdate(buf); + } +#endif } } diff --git a/crypto/src/crypto/macs/KMac.cs b/crypto/src/crypto/macs/KMac.cs
index 05031ac2f..ce6c9f701 100644 --- a/crypto/src/crypto/macs/KMac.cs +++ b/crypto/src/crypto/macs/KMac.cs
@@ -39,6 +39,16 @@ namespace Org.BouncyCastle.Crypto.Macs cshake.BlockUpdate(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + cshake.BlockUpdate(input); + } +#endif + public int DoFinal(byte[] output, int outOff) { if (firstOutput) @@ -58,6 +68,27 @@ namespace Org.BouncyCastle.Crypto.Macs return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = XofUtilities.RightEncode(GetMacSize() * 8, lengthEncoding); + cshake.BlockUpdate(lengthEncoding[..count]); + } + + int rv = cshake.OutputFinal(output[..GetMacSize()]); + + Reset(); + + return rv; + } +#endif + public int DoFinal(byte[] output, int outOff, int outLen) { if (firstOutput) @@ -77,6 +108,27 @@ namespace Org.BouncyCastle.Crypto.Macs return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int OutputFinal(Span<byte> output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = XofUtilities.RightEncode(output.Length * 8, lengthEncoding); + cshake.BlockUpdate(lengthEncoding[..count]); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } +#endif + public int DoOutput(byte[] output, int outOff, int outLen) { if (firstOutput) @@ -94,6 +146,25 @@ namespace Org.BouncyCastle.Crypto.Macs return cshake.DoOutput(output, outOff, outLen); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Output(Span<byte> output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = XofUtilities.RightEncode(0, lengthEncoding); + cshake.BlockUpdate(lengthEncoding[..count]); + + firstOutput = false; + } + + return cshake.Output(output); + } +#endif + public int GetByteLength() { return cshake.GetByteLength(); diff --git a/crypto/src/pqc/crypto/lms/LMSContext.cs b/crypto/src/pqc/crypto/lms/LMSContext.cs
index 35c33093b..2113184fe 100644 --- a/crypto/src/pqc/crypto/lms/LMSContext.cs +++ b/crypto/src/pqc/crypto/lms/LMSContext.cs
@@ -124,5 +124,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms { digest.Reset(); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } + + public int DoFinal(Span<byte> output) + { + return digest.DoFinal(output); + } +#endif } -} \ No newline at end of file +} diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs
index 2c9e89277..035280fd6 100644 --- a/crypto/src/security/DigestUtilities.cs +++ b/crypto/src/security/DigestUtilities.cs
@@ -266,9 +266,22 @@ namespace Org.BouncyCastle.Security public static byte[] CalculateDigest(string algorithm, byte[] input) { IDigest digest = GetDigest(algorithm); - digest.BlockUpdate(input, 0, input.Length); - return DoFinal(digest); + return DoFinal(digest, input); + } + + public static byte[] CalculateDigest(string algorithm, byte[] buf, int off, int len) + { + IDigest digest = GetDigest(algorithm); + return DoFinal(digest, buf, off, len); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static byte[] CalculateDigest(string algorithm, ReadOnlySpan<byte> buffer) + { + IDigest digest = GetDigest(algorithm); + return DoFinal(digest, buffer); } +#endif public static byte[] DoFinal(IDigest digest) { @@ -282,5 +295,19 @@ namespace Org.BouncyCastle.Security digest.BlockUpdate(input, 0, input.Length); return DoFinal(digest); } + + public static byte[] DoFinal(IDigest digest, byte[] buf, int off, int len) + { + digest.BlockUpdate(buf, off, len); + return DoFinal(digest); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static byte[] DoFinal(IDigest digest, ReadOnlySpan<byte> buffer) + { + digest.BlockUpdate(buffer); + return DoFinal(digest); + } +#endif } }