From 5bae538777cfabc024f318c9d192a062d68d98b3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 23 Aug 2022 14:08:56 +0700 Subject: Span-based variants for IDigest, IXof --- crypto/src/crypto/IDigest.cs | 84 +++--- crypto/src/crypto/IXof.cs | 27 +- crypto/src/crypto/digests/Blake2bDigest.cs | 132 +++++++++- crypto/src/crypto/digests/Blake2sDigest.cs | 137 +++++++++- crypto/src/crypto/digests/CSHAKEDigest.cs | 19 ++ crypto/src/crypto/digests/DSTU7564Digest.cs | 131 ++++++++-- crypto/src/crypto/digests/GOST3411Digest.cs | 54 +++- crypto/src/crypto/digests/GOST3411_2012Digest.cs | 92 +++++-- .../src/crypto/digests/GOST3411_2012_256Digest.cs | 12 + crypto/src/crypto/digests/GeneralDigest.cs | 50 ++++ crypto/src/crypto/digests/Haraka256Digest.cs | 90 ++++++- crypto/src/crypto/digests/Haraka512Digest.cs | 122 ++++++++- crypto/src/crypto/digests/HarakaBase.cs | 12 +- crypto/src/crypto/digests/KeccakDigest.cs | 97 +++++++ crypto/src/crypto/digests/LongDigest.cs | 289 ++++++++++++--------- crypto/src/crypto/digests/MD2Digest.cs | 59 ++++- crypto/src/crypto/digests/MD4Digest.cs | 53 ++-- crypto/src/crypto/digests/MD5Digest.cs | 32 ++- crypto/src/crypto/digests/NonMemoableDigest.cs | 14 + crypto/src/crypto/digests/NullDigest.cs | 35 ++- crypto/src/crypto/digests/ParallelHash.cs | 116 ++++++++- crypto/src/crypto/digests/RipeMD128Digest.cs | 28 ++ crypto/src/crypto/digests/RipeMD160Digest.cs | 29 +++ crypto/src/crypto/digests/RipeMD256Digest.cs | 32 +++ crypto/src/crypto/digests/RipeMD320Digest.cs | 34 +++ crypto/src/crypto/digests/SHA3Digest.cs | 9 + crypto/src/crypto/digests/SM3Digest.cs | 34 ++- crypto/src/crypto/digests/Sha1Digest.cs | 34 ++- crypto/src/crypto/digests/Sha224Digest.cs | 44 +++- crypto/src/crypto/digests/Sha256Digest.cs | 59 +++-- crypto/src/crypto/digests/Sha384Digest.cs | 25 +- crypto/src/crypto/digests/Sha512Digest.cs | 21 +- crypto/src/crypto/digests/Sha512tDigest.cs | 47 +++- crypto/src/crypto/digests/ShakeDigest.cs | 28 ++ crypto/src/crypto/digests/ShortenedDigest.cs | 25 +- crypto/src/crypto/digests/SkeinDigest.cs | 16 +- crypto/src/crypto/digests/SkeinEngine.cs | 101 ++++++- crypto/src/crypto/digests/TigerDigest.cs | 70 +++++ crypto/src/crypto/digests/TupleHash.cs | 53 +++- crypto/src/crypto/digests/WhirlpoolDigest.cs | 24 ++ crypto/src/crypto/digests/XofUtils.cs | 50 ++++ crypto/src/crypto/macs/KMac.cs | 71 +++++ crypto/src/pqc/crypto/lms/LMSContext.cs | 14 +- crypto/src/security/DigestUtilities.cs | 31 ++- crypto/test/src/crypto/test/Blake2bDigestTest.cs | 2 + crypto/test/src/crypto/test/Blake2sDigestTest.cs | 2 + crypto/test/src/crypto/test/CSHAKETest.cs | 3 + crypto/test/src/crypto/test/DigestTest.cs | 59 ++++- crypto/test/src/crypto/test/Haraka256DigestTest.cs | 22 +- crypto/test/src/crypto/test/Haraka512DigestTest.cs | 21 +- crypto/test/src/crypto/test/KeccakDigestTest.cs | 2 + crypto/test/src/crypto/test/ParallelHashTest.cs | 3 + crypto/test/src/crypto/test/SHA3DigestTest.cs | 2 + crypto/test/src/crypto/test/ShakeDigestTest.cs | 2 + crypto/test/src/crypto/test/ShortenedDigestTest.cs | 2 + crypto/test/src/crypto/test/SkeinDigestTest.cs | 2 + crypto/test/src/crypto/test/TupleHashTest.cs | 62 +++++ 57 files changed, 2364 insertions(+), 356 deletions(-) (limited to 'crypto') 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. - */ + /// Base interface for a message digest. public interface IDigest { - /** - * return the algorithm name - * - * @return the algorithm name - */ + /// the algorithm name 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. - */ + /// Return the size, in bytes, of the digest produced by this message digest. + /// 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. + /// the size, in bytes, of the internal buffer used by this digest. + int GetByteLength(); + + /// Update the message digest with a single byte. + /// the input byte to be entered. 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. - */ + /// Update the message digest with a block of bytes. + /// the byte array containing the data. + /// the offset into the byte array where the data starts. + /// the length of the data. + void BlockUpdate(byte[] input, int inOff, int inLen); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// Update the message digest with a span of bytes. + /// the span containing the data. + void BlockUpdate(ReadOnlySpan input); +#endif + + /// Close the digest, producing the final digest value. + /// This call leaves the digest reset. + /// the byte array the digest is to be copied into. + /// the offset into the byte array the digest is to start at. + /// the number of bytes written int DoFinal(byte[] output, int outOff); - /** - * reset the digest back to it's initial state. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// Close the digest, producing the final digest value. + /// This call leaves the digest reset. + /// the span the digest is to be copied into. + /// the number of bytes written + int DoFinal(Span output); +#endif + + /// Reset the digest back to its initial state. 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 { /// /// 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. /// public interface IXof : IDigest { /// - /// 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. /// /// output array to write the output bytes to. /// offset to start writing the bytes at. @@ -18,14 +18,33 @@ namespace Org.BouncyCastle.Crypto /// the number of bytes written int DoFinal(byte[] output, int outOff, int outLen); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// - /// 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. + /// + /// span to fill with the output bytes. + /// the number of bytes written + int OutputFinal(Span output); +#endif + + /// + /// 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. /// /// output array to write the output bytes to. /// offset to start writing the bytes at. /// the number of output bytes requested. /// the number of bytes written int DoOutput(byte[] output, int outOff, int outLen); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// + /// 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. + /// + /// span to fill with the output bytes. + /// the number of bytes written + int Output(Span 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 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 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 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 message) + { + InitializeInternalState(); + + Span 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 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 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 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 message) + { + InitializeInternalState(); + + Span 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 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 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 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 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 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 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 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 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 output) + { + Span 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 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 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 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 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 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 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 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 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 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 input); + + public abstract int DoFinal(Span 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 + /// reset the chaining variables to the IV values. 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 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 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 + /// reset the chaining variables to the IV values. 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public virtual int OutputFinal(Span output) + { + int length = Output(output); + + Reset(); + + return length; + } + + public virtual int Output(Span 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 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 output) + { + Span 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 { - /// /// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, /// based on the Threefish 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 input) + { + engine.Update(input); + } + + public int DoFinal(Span 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 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 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 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 output, int outputBytes) + { + Span 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 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 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 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 input) + { + XofUtilities.EncodeTo(cshake, input); + } + + public virtual int DoFinal(Span output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public virtual int OutputFinal(Span output) + { + if (firstOutput) + { + WrapUp(GetDigestSize()); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } + + public virtual int Output(Span 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 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 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 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 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 buf) + { + Span 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 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 output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span 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 output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span 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 output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span 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 input) + { + digest.BlockUpdate(input); + } + + public int DoFinal(Span 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 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 buffer) + { + digest.BlockUpdate(buffer); + return DoFinal(digest); + } +#endif } } diff --git a/crypto/test/src/crypto/test/Blake2bDigestTest.cs b/crypto/test/src/crypto/test/Blake2bDigestTest.cs index e7835e2a7..518331b2e 100644 --- a/crypto/test/src/crypto/test/Blake2bDigestTest.cs +++ b/crypto/test/src/crypto/test/Blake2bDigestTest.cs @@ -146,6 +146,8 @@ namespace Org.BouncyCastle.Crypto.Tests ResetTest(); DoTestNullKeyVsUnkeyed(); DoTestLengthConstruction(); + + DigestTest.SpanConsistencyTests(this, new Blake2bDigest(512)); } private void CloneTest() diff --git a/crypto/test/src/crypto/test/Blake2sDigestTest.cs b/crypto/test/src/crypto/test/Blake2sDigestTest.cs index 2080e2871..10c35579f 100644 --- a/crypto/test/src/crypto/test/Blake2sDigestTest.cs +++ b/crypto/test/src/crypto/test/Blake2sDigestTest.cs @@ -293,6 +293,8 @@ namespace Org.BouncyCastle.Crypto.Tests RunSelfTest(); DoTestNullKeyVsUnkeyed(); DoTestLengthConstruction(); + + DigestTest.SpanConsistencyTests(this, new Blake2sDigest(256)); } [Test] diff --git a/crypto/test/src/crypto/test/CSHAKETest.cs b/crypto/test/src/crypto/test/CSHAKETest.cs index 6a3c99a0f..581832aaf 100644 --- a/crypto/test/src/crypto/test/CSHAKETest.cs +++ b/crypto/test/src/crypto/test/CSHAKETest.cs @@ -103,6 +103,9 @@ namespace Org.BouncyCastle.Crypto.Tests checkSHAKE(128, new CShakeDigest(128, null, new byte[0]), Hex.Decode("eeaabeef")); checkSHAKE(128, new CShakeDigest(128, null, null), Hex.Decode("eeaabeef")); checkSHAKE(256, new CShakeDigest(256, null, null), Hex.Decode("eeaabeef")); + + DigestTest.SpanConsistencyTests(this, new CShakeDigest(128, null, null)); + DigestTest.SpanConsistencyTests(this, new CShakeDigest(256, null, null)); } private void checkZeroPadZ() diff --git a/crypto/test/src/crypto/test/DigestTest.cs b/crypto/test/src/crypto/test/DigestTest.cs index 930979643..e2d2a47ef 100644 --- a/crypto/test/src/crypto/test/DigestTest.cs +++ b/crypto/test/src/crypto/test/DigestTest.cs @@ -1,7 +1,6 @@ using System; -using Org.BouncyCastle.Crypto; - +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.Test; @@ -11,6 +10,8 @@ namespace Org.BouncyCastle.Crypto.Tests public abstract class DigestTest : SimpleTest { + internal static readonly SecureRandom Random = new SecureRandom(); + private IDigest digest; private string[] input; private string[] results; @@ -108,6 +109,8 @@ namespace Org.BouncyCastle.Crypto.Tests { Fail("failing memo copy vector test", results[results.Length - 1], Hex.ToHexString(resBuf)); } + + SpanConsistencyTests(this, digest); } private byte[] toByteArray( @@ -179,5 +182,57 @@ namespace Org.BouncyCastle.Crypto.Tests Fail("64k test failed"); } } + + internal static void SpanConsistencyTests(SimpleTest test, IDigest digest) + { + // NOTE: .NET Core 2.1 has Span, but is tested against our .NET Standard 2.0 assembly. +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + + // Span-based API consistency checks + byte[] data = new byte[16 + 256]; + Random.NextBytes(data); + + for (int len = 0; len <= 256; ++len) + { + int off = Random.Next(0, 17); + + SpanConsistencyTest(test, digest, data, off, len); + } +#endif + } + + internal static void SpanConsistencyTest(SimpleTest test, IDigest digest, byte[] buf, int off, int len) + { + // NOTE: .NET Core 2.1 has Span, but is tested against our .NET Standard 2.0 assembly. + //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + digest.Reset(); + + byte[] arrayResult = DigestUtilities.DoFinal(digest, buf, off, len); + byte[] spanResult1 = DigestUtilities.DoFinal(digest, buf.AsSpan(off, len)); + + if (!test.AreEqual(arrayResult, spanResult1)) + { + test.Fail("failing span consistency test 1", Hex.ToHexString(arrayResult), Hex.ToHexString(spanResult1)); + } + + int pos = 0; + while (pos < len) + { + int next = 1 + Random.Next(len - pos); + digest.BlockUpdate(buf.AsSpan(off + pos, next)); + pos += next; + } + + byte[] spanResult2 = new byte[digest.GetDigestSize()]; + digest.DoFinal(spanResult2.AsSpan()); + + if (!test.AreEqual(arrayResult, spanResult2)) + { + test.Fail("failing span consistency test 2", Hex.ToHexString(arrayResult), Hex.ToHexString(spanResult2)); + } +#endif + } } } diff --git a/crypto/test/src/crypto/test/Haraka256DigestTest.cs b/crypto/test/src/crypto/test/Haraka256DigestTest.cs index 3f7706211..e8a181169 100644 --- a/crypto/test/src/crypto/test/Haraka256DigestTest.cs +++ b/crypto/test/src/crypto/test/Haraka256DigestTest.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.Test; @@ -185,6 +186,8 @@ namespace Org.BouncyCastle.Crypto.Tests TestInputTooShort(); TestOutput(); TestMonty(); + + SpanConsistencyTests(); } [Test] @@ -194,5 +197,22 @@ namespace Org.BouncyCastle.Crypto.Tests Assert.AreEqual(Name + ": Okay", resultText); } + + private void SpanConsistencyTests() + { + // NOTE: .NET Core 2.1 has Span, but is tested against our .NET Standard 2.0 assembly. + //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + // Span-based API consistency checks + byte[] data = new byte[16 + 32]; + DigestTest.Random.NextBytes(data); + + var digest = new Haraka256Digest(); + for (int off = 0; off <= 16; ++off) + { + DigestTest.SpanConsistencyTest(this, digest, data, off, 32); + } +#endif + } } -} \ No newline at end of file +} diff --git a/crypto/test/src/crypto/test/Haraka512DigestTest.cs b/crypto/test/src/crypto/test/Haraka512DigestTest.cs index 11aa746af..88208100e 100644 --- a/crypto/test/src/crypto/test/Haraka512DigestTest.cs +++ b/crypto/test/src/crypto/test/Haraka512DigestTest.cs @@ -188,6 +188,8 @@ namespace Org.BouncyCastle.Crypto.Tests TestInputTooShort(); TestOutput(); TestMonty(); + + SpanConsistencyTests(); } [Test] @@ -197,5 +199,22 @@ namespace Org.BouncyCastle.Crypto.Tests Assert.AreEqual(Name + ": Okay", resultText); } + + private void SpanConsistencyTests() + { + // NOTE: .NET Core 2.1 has Span, but is tested against our .NET Standard 2.0 assembly. + //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + // Span-based API consistency checks + byte[] data = new byte[16 + 64]; + DigestTest.Random.NextBytes(data); + + var digest = new Haraka512Digest(); + for (int off = 0; off <= 16; ++off) + { + DigestTest.SpanConsistencyTest(this, digest, data, off, 64); + } +#endif + } } -} \ No newline at end of file +} diff --git a/crypto/test/src/crypto/test/KeccakDigestTest.cs b/crypto/test/src/crypto/test/KeccakDigestTest.cs index ddabddab4..12b310fc0 100644 --- a/crypto/test/src/crypto/test/KeccakDigestTest.cs +++ b/crypto/test/src/crypto/test/KeccakDigestTest.cs @@ -273,6 +273,8 @@ namespace Org.BouncyCastle.Crypto.Tests // Fail("Keccak mismatch on " + digest.AlgorithmName + " extreme data test"); //} //Console.WriteLine("Done"); + + DigestTest.SpanConsistencyTests(this, digest); } private void TestDigestDoFinal(IDigest digest) diff --git a/crypto/test/src/crypto/test/ParallelHashTest.cs b/crypto/test/src/crypto/test/ParallelHashTest.cs index 536567313..e37516366 100644 --- a/crypto/test/src/crypto/test/ParallelHashTest.cs +++ b/crypto/test/src/crypto/test/ParallelHashTest.cs @@ -119,6 +119,9 @@ namespace Org.BouncyCastle.Crypto.Tests IsTrue("oops!", Arrays.AreEqual(Hex.Decode("6b3e790b330c889a204c2fbc728d809f19367328d852f4002dc829f73afd6bcefb7fe5b607b13a801c0be5c1170bdb794e339458fdb0e62a6af3d42558970249"), res)); testEmpty(); + + DigestTest.SpanConsistencyTests(this, new ParallelHash(128, new byte[0], 8)); + DigestTest.SpanConsistencyTests(this, new ParallelHash(256, new byte[0], 8)); } private void testEmpty() diff --git a/crypto/test/src/crypto/test/SHA3DigestTest.cs b/crypto/test/src/crypto/test/SHA3DigestTest.cs index 7b9ab26cf..2984f1c83 100644 --- a/crypto/test/src/crypto/test/SHA3DigestTest.cs +++ b/crypto/test/src/crypto/test/SHA3DigestTest.cs @@ -40,6 +40,8 @@ namespace Org.BouncyCastle.Crypto.Tests public override void PerformTest() { TestVectors(); + + DigestTest.SpanConsistencyTests(this, new Sha3Digest()); } public void TestVectors() diff --git a/crypto/test/src/crypto/test/ShakeDigestTest.cs b/crypto/test/src/crypto/test/ShakeDigestTest.cs index 4b4d0fbd6..0aeedb256 100644 --- a/crypto/test/src/crypto/test/ShakeDigestTest.cs +++ b/crypto/test/src/crypto/test/ShakeDigestTest.cs @@ -40,6 +40,8 @@ namespace Org.BouncyCastle.Crypto.Tests public override void PerformTest() { TestVectors(); + + DigestTest.SpanConsistencyTests(this, new ShakeDigest()); } public void TestVectors() diff --git a/crypto/test/src/crypto/test/ShortenedDigestTest.cs b/crypto/test/src/crypto/test/ShortenedDigestTest.cs index 927ffee3a..01c408219 100644 --- a/crypto/test/src/crypto/test/ShortenedDigestTest.cs +++ b/crypto/test/src/crypto/test/ShortenedDigestTest.cs @@ -74,6 +74,8 @@ namespace Org.BouncyCastle.Crypto.Tests { // expected } + + DigestTest.SpanConsistencyTests(this, new ShortenedDigest(new Sha1Digest(), 10)); } public override string Name diff --git a/crypto/test/src/crypto/test/SkeinDigestTest.cs b/crypto/test/src/crypto/test/SkeinDigestTest.cs index 50a2d9565..7935eaafb 100644 --- a/crypto/test/src/crypto/test/SkeinDigestTest.cs +++ b/crypto/test/src/crypto/test/SkeinDigestTest.cs @@ -205,6 +205,8 @@ namespace Org.BouncyCastle.Crypto.Tests Case test = TEST_CASES[i]; runTest(test); } + + DigestTest.SpanConsistencyTests(this, new SkeinDigest(256, 256)); } private void runTest(Case dc) diff --git a/crypto/test/src/crypto/test/TupleHashTest.cs b/crypto/test/src/crypto/test/TupleHashTest.cs index fd1ee001f..ba9010e99 100644 --- a/crypto/test/src/crypto/test/TupleHashTest.cs +++ b/crypto/test/src/crypto/test/TupleHashTest.cs @@ -3,6 +3,7 @@ using System; using NUnit.Framework; using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.Test; @@ -103,6 +104,8 @@ namespace Org.BouncyCastle.Crypto.Tests IsTrue("oops!", !Arrays.AreEqual(Hex.Decode("45 00 0B E6 3F 9B 6B FD 89 F5 47 17 67 0F 69 A9 BC 76 35 91 A4 F0 5C 50 D6 88 91 A7 44 BC C6 E7 D6 D5 B5 E8 2C 01 8D A9 99 ED 35 B0 BB 49 C9 67 8E 52 6A BD 8E 85 C1 3E D2 54 02 1D B9 E7 90 CE"), res)); IsTrue("oops!", Arrays.AreEqual(Hex.Decode("0c59b11464f2336c34663ed51b2b950bec743610856f36c28d1d088d8a2446284dd09830a6a178dc752376199fae935d86cfdee5913d4922dfd369b66a53c897"), res)); + + SpanConsistencyTests(); } [Test] @@ -112,5 +115,64 @@ namespace Org.BouncyCastle.Crypto.Tests Assert.AreEqual(Name + ": Okay", resultText); } + + internal void SpanConsistencyTests() + { + // NOTE: .NET Core 2.1 has Span, but is tested against our .NET Standard 2.0 assembly. + //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + IDigest digest1 = new TupleHash(128, new byte[0]); + IDigest digest2 = new TupleHash(128, new byte[0]); + + // Span-based API consistency checks + byte[] data = new byte[16 + 256]; + DigestTest.Random.NextBytes(data); + + for (int len = 0; len <= 256; ++len) + { + int off = DigestTest.Random.Next(0, 17); + + SpanConsistencyTest(digest1, digest2, data, off, len); + } +#endif + } + + internal void SpanConsistencyTest(IDigest digest1, IDigest digest2, byte[] buf, int off, int len) + { + // NOTE: .NET Core 2.1 has Span, but is tested against our .NET Standard 2.0 assembly. + //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + digest1.Reset(); + digest2.Reset(); + + byte[] arrayResult1 = DigestUtilities.DoFinal(digest1, buf, off, len); + byte[] spanResult1 = DigestUtilities.DoFinal(digest2, buf.AsSpan(off, len)); + + if (!AreEqual(arrayResult1, spanResult1)) + { + Fail("failing span consistency test 1", Hex.ToHexString(arrayResult1), Hex.ToHexString(spanResult1)); + } + + int pos = 0; + while (pos < len) + { + int next = 1 + DigestTest.Random.Next(len - pos); + digest1.BlockUpdate(buf, off + pos, next); + digest2.BlockUpdate(buf.AsSpan(off + pos, next)); + pos += next; + } + + byte[] arrayResult2 = new byte[digest1.GetDigestSize()]; + digest1.DoFinal(arrayResult2, 0); + + byte[] spanResult2 = new byte[digest2.GetDigestSize()]; + digest2.DoFinal(spanResult2.AsSpan()); + + if (!AreEqual(arrayResult2, spanResult2)) + { + Fail("failing span consistency test 2", Hex.ToHexString(arrayResult2), Hex.ToHexString(spanResult2)); + } +#endif + } } } -- cgit 1.4.1