diff options
157 files changed, 7193 insertions, 2366 deletions
diff --git a/crypto/src/BouncyCastle.Crypto.csproj b/crypto/src/BouncyCastle.Crypto.csproj index bc17aae6a..2ebe4efdb 100644 --- a/crypto/src/BouncyCastle.Crypto.csproj +++ b/crypto/src/BouncyCastle.Crypto.csproj @@ -25,7 +25,7 @@ <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.108"> + <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.109"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/crypto/src/crypto/IBlockCipher.cs b/crypto/src/crypto/IBlockCipher.cs index a3ad6d6e5..b26aaa49f 100644 --- a/crypto/src/crypto/IBlockCipher.cs +++ b/crypto/src/crypto/IBlockCipher.cs @@ -28,9 +28,19 @@ namespace Org.BouncyCastle.Crypto /// <returns>The number of bytes processed and produced.</returns> int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff); - /// <summary> - /// Reset the cipher to the same state as it was after the last init (if there was one). - /// </summary> +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + + /// <summary>Process a block.</summary> + /// <param name="input">The input block as a span.</param> + /// <param name="output">The output span.</param> + /// <exception cref="DataLengthException">If input block is wrong size, or output span too small.</exception> + /// <returns>The number of bytes processed and produced.</returns> + int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output); +#endif + + /// <summary> + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// </summary> void Reset(); } } diff --git a/crypto/src/crypto/IDSA.cs b/crypto/src/crypto/IDSA.cs index 7d1fd5197..459914f1f 100644 --- a/crypto/src/crypto/IDSA.cs +++ b/crypto/src/crypto/IDSA.cs @@ -4,38 +4,29 @@ using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Crypto { - /** - * interface for classes implementing the Digital Signature Algorithm - */ + /// <summary>Interface for classes implementing the Digital Signature Algorithm</summary> public interface IDsa { + /// <summary>The algorithm name.</summary> string AlgorithmName { get; } - /** - * initialise the signer for signature generation or signature - * verification. - * - * @param forSigning true if we are generating a signature, false - * otherwise. - * @param param key parameters for signature generation. - */ + /// <summary>Initialise the signer for signature generation or signature verification.</summary> + /// <param name="forSigning">true if we are generating a signature, false otherwise.</param> + /// <param name="parameters">key parameters for signature generation.</param> void Init(bool forSigning, ICipherParameters parameters); - /** - * sign the passed in message (usually the output of a hash function). - * - * @param message the message to be signed. - * @return two big integers representing the r and s values respectively. - */ + /// <summary>Sign the passed in message (usually the output of a hash function).</summary> + /// <param name="message">the message to be signed.</param> + /// <returns>two big integers representing the r and s values respectively.</returns> BigInteger[] GenerateSignature(byte[] message); - /** - * verify the message message against the signature values r and s. - * - * @param message the message that was supposed to have been signed. - * @param r the r signature value. - * @param s the s signature value. - */ + /// <summary>The order of the group that the r, s values in signatures belong to.</summary> + BigInteger Order { get; } + + /// <summary>Verify the message message against the signature values r and s.</summary> + /// <param name="message">the message that was supposed to have been signed.</param> + /// <param name="r">the r signature value.</param> + /// <param name="s">the s signature value.</param> bool VerifySignature(byte[] message, BigInteger r, BigInteger s); } } diff --git a/crypto/src/crypto/IDigest.cs b/crypto/src/crypto/IDigest.cs index 6769dcc42..36caf3728 100644 --- a/crypto/src/crypto/IDigest.cs +++ b/crypto/src/crypto/IDigest.cs @@ -2,60 +2,52 @@ using System; namespace Org.BouncyCastle.Crypto { - /** - * interface that a message digest conforms to. - */ + /// <remarks>Base interface for a message digest.</remarks> public interface IDigest { - /** - * return the algorithm name - * - * @return the algorithm name - */ + /// <summary>The algorithm name.</summary> string AlgorithmName { get; } - /** - * return the size, in bytes, of the digest produced by this message digest. - * - * @return the size, in bytes, of the digest produced by this message digest. - */ - int GetDigestSize(); - - /** - * return the size, in bytes, of the internal buffer used by this digest. - * - * @return the size, in bytes, of the internal buffer used by this digest. - */ - int GetByteLength(); - - /** - * update the message digest with a single byte. - * - * @param inByte the input byte to be entered. - */ + /// <summary>Return the size, in bytes, of the digest produced by this message digest.</summary> + /// <returns>the size, in bytes, of the digest produced by this message digest.</returns> + int GetDigestSize(); + + /// <summary>Return the size, in bytes, of the internal buffer used by this digest.</summary> + /// <returns>the size, in bytes, of the internal buffer used by this digest.</returns> + int GetByteLength(); + + /// <summary>Update the message digest with a single byte.</summary> + /// <param name="input">the input byte to be entered.</param> void Update(byte input); - /** - * update the message digest with a block of bytes. - * - * @param input the byte array containing the data. - * @param inOff the offset into the byte array where the data starts. - * @param len the length of the data. - */ - void BlockUpdate(byte[] input, int inOff, int length); - - /** - * Close the digest, producing the final digest value. The doFinal - * call leaves the digest reset. - * - * @param output the array the digest is to be copied into. - * @param outOff the offset into the out array the digest is to start at. - */ + /// <summary>Update the message digest with a block of bytes.</summary> + /// <param name="input">the byte array containing the data.</param> + /// <param name="inOff">the offset into the byte array where the data starts.</param> + /// <param name="inLen">the length of the data.</param> + void BlockUpdate(byte[] input, int inOff, int inLen); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Update the message digest with a span of bytes.</summary> + /// <param name="input">the span containing the data.</param> + void BlockUpdate(ReadOnlySpan<byte> input); +#endif + + /// <summary>Close the digest, producing the final digest value.</summary> + /// <remarks>This call leaves the digest reset.</remarks> + /// <param name="output">the byte array the digest is to be copied into.</param> + /// <param name="outOff">the offset into the byte array the digest is to start at.</param> + /// <returns>the number of bytes written</returns> int DoFinal(byte[] output, int outOff); - /** - * reset the digest back to it's initial state. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Close the digest, producing the final digest value.</summary> + /// <remarks>This call leaves the digest reset.</remarks> + /// <param name="output">the span the digest is to be copied into.</param> + /// <returns>the number of bytes written</returns> + int DoFinal(Span<byte> output); +#endif + + /// <summary>Reset the digest back to its initial state.</summary> void Reset(); } } diff --git a/crypto/src/crypto/IDsaExt.cs b/crypto/src/crypto/IDsaExt.cs deleted file mode 100644 index 15b55787a..000000000 --- a/crypto/src/crypto/IDsaExt.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -using Org.BouncyCastle.Math; - -namespace Org.BouncyCastle.Crypto -{ - /// <summary> - /// An "extended" interface for classes implementing DSA-style algorithms, that provides access - /// to the group order. - /// </summary> - public interface IDsaExt - : IDsa - { - /// <summary>The order of the group that the r, s values in signatures belong to.</summary> - BigInteger Order { get; } - } -} diff --git a/crypto/src/crypto/IMac.cs b/crypto/src/crypto/IMac.cs index 03a86e8b6..aa539b968 100644 --- a/crypto/src/crypto/IMac.cs +++ b/crypto/src/crypto/IMac.cs @@ -2,68 +2,52 @@ using System; namespace Org.BouncyCastle.Crypto { - /** - * The base interface for implementations of message authentication codes (MACs). - */ + /// <summary>The base interface for implementations of message authentication codes (MACs).</summary> public interface IMac { - /** - * Initialise the MAC. - * - * @param param the key and other data required by the MAC. - * @exception ArgumentException if the parameters argument is - * inappropriate. - */ + /// <summary>Initialise the MAC.</summary> + /// <param name="parameters">The key or other data required by the MAC.</param> void Init(ICipherParameters parameters); - /** - * Return the name of the algorithm the MAC implements. - * - * @return the name of the algorithm the MAC implements. - */ + /// <summary>The algorithm name.</summary> string AlgorithmName { get; } - /** - * Return the block size for this MAC (in bytes). - * - * @return the block size for this MAC in bytes. - */ - int GetMacSize(); + /// <summary>Return the size, in bytes, of the MAC produced by this implementation.</summary> + /// <returns>the size, in bytes, of the MAC produced by this implementation.</returns> + int GetMacSize(); - /** - * add a single byte to the mac for processing. - * - * @param in the byte to be processed. - * @exception InvalidOperationException if the MAC is not initialised. - */ + /// <summary>Update the MAC with a single byte.</summary> + /// <param name="input">the input byte to be entered.</param> void Update(byte input); - /** - * @param in the array containing the input. - * @param inOff the index in the array the data begins at. - * @param len the length of the input starting at inOff. - * @exception InvalidOperationException if the MAC is not initialised. - * @exception DataLengthException if there isn't enough data in in. - */ - void BlockUpdate(byte[] input, int inOff, int len); + /// <summary>Update the MAC with a block of bytes.</summary> + /// <param name="input">the byte array containing the data.</param> + /// <param name="inOff">the offset into the byte array where the data starts.</param> + /// <param name="inLen">the length of the data.</param> + void BlockUpdate(byte[] input, int inOff, int inLen); - /** - * Compute the final stage of the MAC writing the output to the out - * parameter. - * <p> - * doFinal leaves the MAC in the same state it was after the last init. - * </p> - * @param out the array the MAC is to be output to. - * @param outOff the offset into the out buffer the output is to start at. - * @exception DataLengthException if there isn't enough space in out. - * @exception InvalidOperationException if the MAC is not initialised. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Update the MAC with a span of bytes.</summary> + /// <param name="input">the span containing the data.</param> + void BlockUpdate(ReadOnlySpan<byte> input); +#endif + + /// <summary>Perform final calculations, producing the result MAC.</summary> + /// <remarks>This call leaves the MAC reset.</remarks> + /// <param name="output">the byte array the MAC is to be copied into.</param> + /// <param name="outOff">the offset into the byte array the MAC is to start at.</param> + /// <returns>the number of bytes written</returns> int DoFinal(byte[] output, int outOff); - /** - * Reset the MAC. At the end of resetting the MAC should be in the - * in the same state it was after the last init (if there was one). - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Perform final calculations, producing the result MAC.</summary> + /// <remarks>This call leaves the MAC reset.</remarks> + /// <param name="output">the span the MAC is to be copied into.</param> + /// <returns>the number of bytes written</returns> + int DoFinal(Span<byte> output); +#endif + + /// <summary>Reset the MAC back to its initial state.</summary> void Reset(); } } diff --git a/crypto/src/crypto/ISigner.cs b/crypto/src/crypto/ISigner.cs index e03bbf4d3..668e5e4cd 100644 --- a/crypto/src/crypto/ISigner.cs +++ b/crypto/src/crypto/ISigner.cs @@ -1,50 +1,45 @@ - using System; -using System.Text; namespace Org.BouncyCastle.Crypto { public interface ISigner { - /** - * Return the name of the algorithm the signer implements. - * - * @return the name of the algorithm the signer implements. - */ + /// <summary>The algorithm name.</summary> string AlgorithmName { get; } - /** - * Initialise the signer for signing or verification. - * - * @param forSigning true if for signing, false otherwise - * @param param necessary parameters. - */ - void Init(bool forSigning, ICipherParameters parameters); - - /** - * update the internal digest with the byte b - */ + /// <summary>Initialise the signer for signing or verification.</summary> + /// <param name="forSigning">true if for signing, false otherwise.</param> + /// <param name="parameters">necessary parameters.</param> + void Init(bool forSigning, ICipherParameters parameters); + + /// <summary>Update the signer with a single byte.</summary> + /// <param name="input">the input byte to be entered.</param> void Update(byte input); - /** - * update the internal digest with the byte array in - */ - void BlockUpdate(byte[] input, int inOff, int length); + /// <summary>Update the signer with a block of bytes.</summary> + /// <param name="input">the byte array containing the data.</param> + /// <param name="inOff">the offset into the byte array where the data starts.</param> + /// <param name="inLen">the length of the data.</param> + void BlockUpdate(byte[] input, int inOff, int inLen); - /** - * Generate a signature for the message we've been loaded with using - * the key we were initialised with. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Update the signer with a span of bytes.</summary> + /// <param name="input">the span containing the data.</param> + void BlockUpdate(ReadOnlySpan<byte> input); +#endif + + /// <summary>Generate a signature for the message we've been loaded with using the key we were initialised with. + /// </summary> + /// <returns>A byte array containing the signature for the message.</returns> byte[] GenerateSignature(); - /** - * return true if the internal state represents the signature described - * in the passed in array. - */ + + /// <summary>Return true if the internal state represents the signature described in the passed in array. + /// </summary> + /// <param name="signature">an array containing the candidate signature to verify.</param> + /// <returns>true if the internal state represents the signature described in the passed in array.</returns> bool VerifySignature(byte[] signature); - /** - * reset the internal state - */ + /// <summary>Reset the signer back to its initial state.</summary> void Reset(); } } diff --git a/crypto/src/crypto/IXof.cs b/crypto/src/crypto/IXof.cs index f76304d48..8cddb2870 100644 --- a/crypto/src/crypto/IXof.cs +++ b/crypto/src/crypto/IXof.cs @@ -4,13 +4,13 @@ namespace Org.BouncyCastle.Crypto { /// <remarks> /// With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes. - /// This interface provides the extra method required to support variable output on a digest implementation. + /// This interface provides the extra methods required to support variable output on a digest implementation. /// </remarks> public interface IXof : IDigest { /// <summary> - /// Output the results of the final calculation for this digest to outLen number of bytes. + /// Output the results of the final calculation for this XOF to outLen number of bytes. /// </summary> /// <param name="output">output array to write the output bytes to.</param> /// <param name="outOff">offset to start writing the bytes at.</param> @@ -18,14 +18,33 @@ namespace Org.BouncyCastle.Crypto /// <returns>the number of bytes written</returns> int DoFinal(byte[] output, int outOff, int outLen); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER /// <summary> - /// Start outputting the results of the final calculation for this digest. Unlike DoFinal, this method - /// will continue producing output until the Xof is explicitly reset, or signals otherwise. + /// Output the results of the final calculation for this XOF to fill the output span. + /// </summary> + /// <param name="output">span to fill with the output bytes.</param> + /// <returns>the number of bytes written</returns> + int OutputFinal(Span<byte> output); +#endif + + /// <summary> + /// Start outputting the results of the final calculation for this XOF. Unlike DoFinal, this method + /// will continue producing output until the XOF is explicitly reset, or signals otherwise. /// </summary> /// <param name="output">output array to write the output bytes to.</param> /// <param name="outOff">offset to start writing the bytes at.</param> /// <param name="outLen">the number of output bytes requested.</param> /// <returns>the number of bytes written</returns> int DoOutput(byte[] output, int outOff, int outLen); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary> + /// Start outputting the results of the final calculation for this XOF. Unlike OutputFinal, this method + /// will continue producing output until the XOF is explicitly reset, or signals otherwise. + /// </summary> + /// <param name="output">span to fill with the output bytes.</param> + /// <returns>the number of bytes written</returns> + int Output(Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs index 1ac9cfa35..ec80a3355 100644 --- a/crypto/src/crypto/digests/Blake2bDigest.cs +++ b/crypto/src/crypto/digests/Blake2bDigest.cs @@ -357,11 +357,63 @@ namespace Org.BouncyCastle.Crypto.Digests } // fill the buffer with left bytes, this might be a full block - Array.Copy(message, messagePos, buffer, 0, offset + len - - messagePos); + Array.Copy(message, messagePos, buffer, 0, offset + len - messagePos); bufferPos += offset + len - messagePos; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + if (input.IsEmpty) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < input.Length) + { // full buffer + at least 1 byte + input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos)); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^64 + t1++; + } + Compress(buffer, 0); + bufferPos = 0; + Array.Clear(buffer, 0, buffer.Length);// clear buffer + } + else + { + input.CopyTo(buffer.AsSpan(bufferPos)); + bufferPos += input.Length; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES; + for (messagePos = remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES) + { // block wise 128 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + Compress(input[messagePos..]); + } + + // fill the buffer with left bytes, this might be a full block + input[messagePos..].CopyTo(buffer.AsSpan()); + bufferPos += input.Length - messagePos; + } +#endif + /** * close the digest, producing the final digest value. The doFinal * call leaves the digest reset. @@ -382,19 +434,42 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null Array.Clear(internalState, 0, internalState.Length); - byte[] bytes = new byte[8]; - for (int i = 0; i < chainValue.Length && (i * 8 < digestLength); i++) + int full = digestLength >> 3, partial = digestLength & 7; + Pack.UInt64_To_LE(chainValue, 0, full, output, outOffset); + if (partial > 0) + { + byte[] bytes = new byte[8]; + Pack.UInt64_To_LE(chainValue[full], bytes, 0); + Array.Copy(bytes, 0, output, outOffset + digestLength - partial, partial); + } + + Array.Clear(chainValue, 0, chainValue.Length); + + Reset(); + + return digestLength; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + f0 = 0xFFFFFFFFFFFFFFFFUL; + t0 += (ulong)bufferPos; + if (bufferPos > 0 && t0 == 0) { - Pack.UInt64_To_LE(chainValue[i], bytes, 0); + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null + Array.Clear(internalState, 0, internalState.Length); - if (i * 8 < digestLength - 8) - { - Array.Copy(bytes, 0, output, outOffset + i * 8, 8); - } - else - { - Array.Copy(bytes, 0, output, outOffset + i * 8, digestLength - (i * 8)); - } + int full = digestLength >> 3, partial = digestLength & 7; + Pack.UInt64_To_LE(chainValue.AsSpan(0, full), output); + if (partial > 0) + { + Span<byte> bytes = stackalloc byte[8]; + Pack.UInt64_To_LE(chainValue[full], bytes); + bytes[..partial].CopyTo(output[(digestLength - partial)..]); } Array.Clear(chainValue, 0, chainValue.Length); @@ -403,6 +478,7 @@ namespace Org.BouncyCastle.Crypto.Digests return digestLength; } +#endif /** * Reset the digest back to it's initial state. @@ -453,6 +529,36 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Compress(ReadOnlySpan<byte> message) + { + InitializeInternalState(); + + Span<ulong> m = stackalloc ulong[16]; + Pack.LE_To_UInt64(message, m); + + for (int round = 0; round < ROUNDS; round++) + { + // G apply to columns of internalState:m[blake2b_sigma[round][2 * blockPos]] /+1 + G(m[blake2b_sigma[round, 0]], m[blake2b_sigma[round, 1]], 0, 4, 8, 12); + G(m[blake2b_sigma[round, 2]], m[blake2b_sigma[round, 3]], 1, 5, 9, 13); + G(m[blake2b_sigma[round, 4]], m[blake2b_sigma[round, 5]], 2, 6, 10, 14); + G(m[blake2b_sigma[round, 6]], m[blake2b_sigma[round, 7]], 3, 7, 11, 15); + // G apply to diagonals of internalState: + G(m[blake2b_sigma[round, 8]], m[blake2b_sigma[round, 9]], 0, 5, 10, 15); + G(m[blake2b_sigma[round, 10]], m[blake2b_sigma[round, 11]], 1, 6, 11, 12); + G(m[blake2b_sigma[round, 12]], m[blake2b_sigma[round, 13]], 2, 7, 8, 13); + G(m[blake2b_sigma[round, 14]], m[blake2b_sigma[round, 15]], 3, 4, 9, 14); + } + + // update chain values: + for (int offset = 0; offset < chainValue.Length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8]; + } + } +#endif + private void G(ulong m1, ulong m2, int posA, int posB, int posC, int posD) { internalState[posA] = internalState[posA] + internalState[posB] + m1; diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs index f50419126..187808aa0 100644 --- a/crypto/src/crypto/digests/Blake2sDigest.cs +++ b/crypto/src/crypto/digests/Blake2sDigest.cs @@ -381,6 +381,61 @@ namespace Org.BouncyCastle.Crypto.Digests bufferPos += offset + len - messagePos; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + if (input.IsEmpty) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < input.Length) + { // full buffer + at least 1 byte + input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos)); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^32 + t1++; + } + Compress(buffer, 0); + bufferPos = 0; + Array.Clear(buffer, 0, buffer.Length);// clear buffer + } + else + { + input.CopyTo(buffer.AsSpan(bufferPos)); + bufferPos += input.Length; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES; + for (messagePos = remainingLength; + messagePos < blockWiseLastPos; + messagePos += BLOCK_LENGTH_BYTES) + { // block wise 64 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + Compress(input[messagePos..]); + } + + // fill the buffer with left bytes, this might be a full block + input[messagePos..].CopyTo(buffer.AsSpan()); + bufferPos += input.Length - messagePos; + } +#endif + /** * Close the digest, producing the final digest value. The doFinal() call * leaves the digest reset. Key, salt and personal string remain. @@ -402,19 +457,44 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null Array.Clear(internalState, 0, internalState.Length); - byte[] bytes = new byte[4]; - for (int i = 0; i < chainValue.Length && (i * 4 < digestLength); i++) + int full = digestLength >> 2, partial = digestLength & 3; + Pack.UInt32_To_LE(chainValue, 0, full, output, outOffset); + if (partial > 0) + { + byte[] bytes = new byte[4]; + Pack.UInt32_To_LE(chainValue[full], bytes, 0); + Array.Copy(bytes, 0, output, outOffset + digestLength - partial, partial); + } + + Array.Clear(chainValue, 0, chainValue.Length); + + Reset(); + + return digestLength; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + f0 = 0xFFFFFFFFU; + t0 += (uint)bufferPos; + // bufferPos may be < 64, so (t0 == 0) does not work + // for 2^32 < message length > 2^32 - 63 + if ((t0 < 0) && (bufferPos > -t0)) { - Pack.UInt32_To_LE(chainValue[i], bytes, 0); + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null + Array.Clear(internalState, 0, internalState.Length); - if (i * 4 < digestLength - 4) - { - Array.Copy(bytes, 0, output, outOffset + i * 4, 4); - } - else - { - Array.Copy(bytes, 0, output, outOffset + i * 4, digestLength - (i * 4)); - } + int full = digestLength >> 2, partial = digestLength & 3; + Pack.UInt32_To_LE(chainValue.AsSpan(0, full), output); + if (partial > 0) + { + Span<byte> bytes = stackalloc byte[4]; + Pack.UInt32_To_LE(chainValue[full], bytes); + bytes[..partial].CopyTo(output[(digestLength - partial)..]); } Array.Clear(chainValue, 0, chainValue.Length); @@ -423,6 +503,7 @@ namespace Org.BouncyCastle.Crypto.Digests return digestLength; } +#endif /** * Reset the digest back to its initial state. The key, the salt and the @@ -453,9 +534,7 @@ namespace Org.BouncyCastle.Crypto.Digests for (int round = 0; round < ROUNDS; round++) { - - // G apply to columns of internalState:m[blake2s_sigma[round][2 * - // blockPos]] /+1 + // G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1 G(m[blake2s_sigma[round,0]], m[blake2s_sigma[round,1]], 0, 4, 8, 12); G(m[blake2s_sigma[round,2]], m[blake2s_sigma[round,3]], 1, 5, 9, 13); G(m[blake2s_sigma[round,4]], m[blake2s_sigma[round,5]], 2, 6, 10, 14); @@ -474,6 +553,36 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Compress(ReadOnlySpan<byte> message) + { + InitializeInternalState(); + + Span<uint> m = stackalloc uint[16]; + Pack.LE_To_UInt32(message, m); + + for (int round = 0; round < ROUNDS; round++) + { + // G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1 + G(m[blake2s_sigma[round, 0]], m[blake2s_sigma[round, 1]], 0, 4, 8, 12); + G(m[blake2s_sigma[round, 2]], m[blake2s_sigma[round, 3]], 1, 5, 9, 13); + G(m[blake2s_sigma[round, 4]], m[blake2s_sigma[round, 5]], 2, 6, 10, 14); + G(m[blake2s_sigma[round, 6]], m[blake2s_sigma[round, 7]], 3, 7, 11, 15); + // G apply to diagonals of internalState: + G(m[blake2s_sigma[round, 8]], m[blake2s_sigma[round, 9]], 0, 5, 10, 15); + G(m[blake2s_sigma[round, 10]], m[blake2s_sigma[round, 11]], 1, 6, 11, 12); + G(m[blake2s_sigma[round, 12]], m[blake2s_sigma[round, 13]], 2, 7, 8, 13); + G(m[blake2s_sigma[round, 14]], m[blake2s_sigma[round, 15]], 3, 4, 9, 14); + } + + // update chain values: + for (int offset = 0; offset < chainValue.Length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8]; + } + } +#endif + private void G(uint m1, uint m2, int posA, int posB, int posC, int posD) { internalState[posA] = internalState[posA] + internalState[posB] + m1; diff --git a/crypto/src/crypto/digests/CSHAKEDigest.cs b/crypto/src/crypto/digests/CSHAKEDigest.cs index c3b0b7068..fc37b865c 100644 --- a/crypto/src/crypto/digests/CSHAKEDigest.cs +++ b/crypto/src/crypto/digests/CSHAKEDigest.cs @@ -95,6 +95,25 @@ namespace Org.BouncyCastle.Crypto.Digests return outLen; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int Output(Span<byte> output) + { + if (diff == null) + { + return base.Output(output); + } + + if (!squeezing) + { + AbsorbBits(0x00, 2); + } + + Squeeze(output); + + return output.Length; + } +#endif + public override void Reset() { base.Reset(); diff --git a/crypto/src/crypto/digests/DSTU7564Digest.cs b/crypto/src/crypto/digests/DSTU7564Digest.cs index b2d90799d..901e549be 100644 --- a/crypto/src/crypto/digests/DSTU7564Digest.cs +++ b/crypto/src/crypto/digests/DSTU7564Digest.cs @@ -1,11 +1,7 @@ using System; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; - using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Crypto.Digests { @@ -132,25 +128,103 @@ namespace Org.BouncyCastle.Crypto.Digests --length; } - if (length > 0) + while (length >= blockSize) + { + ProcessBlock(input, inOff); + inOff += blockSize; + length -= blockSize; + ++inputBlocks; + } + + while (length > 0) + { + Update(input[inOff++]); + --length; + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + while (bufOff != 0 && input.Length > 0) { - while (length >= blockSize) + Update(input[0]); + input = input[1..]; + } + + while (input.Length >= blockSize) + { + ProcessBlock(input); + input = input[blockSize..]; + ++inputBlocks; + } + + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif + + public virtual int DoFinal(byte[] output, int outOff) + { + // Apply padding: terminator byte and 96-bit length field + { + int inputBytes = bufOff; + buf[bufOff++] = (byte)0x80; + + int lenPos = blockSize - 12; + if (bufOff > lenPos) { - ProcessBlock(input, inOff); - inOff += blockSize; - length -= blockSize; - ++inputBlocks; + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + bufOff = 0; + ProcessBlock(buf, 0); } - while (length > 0) + while (bufOff < lenPos) { - Update(input[inOff++]); - --length; + buf[bufOff++] = 0; + } + + ulong c = ((inputBlocks & 0xFFFFFFFFUL) * (ulong)blockSize + (uint)inputBytes) << 3; + Pack.UInt32_To_LE((uint)c, buf, bufOff); + bufOff += 4; + c >>= 32; + c += ((inputBlocks >> 32) * (ulong)blockSize) << 3; + Pack.UInt64_To_LE(c, buf, bufOff); + //bufOff += 8; + ProcessBlock(buf, 0); + } + + { + Array.Copy(state, 0, tempState1, 0, columns); + + P(tempState1); + + for (int col = 0; col < columns; ++col) + { + state[col] ^= tempState1[col]; } } + + int neededColumns = hashSize / 8; + for (int col = columns - neededColumns; col < columns; ++col) + { + Pack.UInt64_To_LE(state[col], output, outOff); + outOff += 8; + } + + Reset(); + + return hashSize; } - public virtual int DoFinal(byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) { // Apply padding: terminator byte and 96-bit length field { @@ -179,7 +253,7 @@ namespace Org.BouncyCastle.Crypto.Digests c >>= 32; c += ((inputBlocks >> 32) * (ulong)blockSize) << 3; Pack.UInt64_To_LE(c, buf, bufOff); - // bufOff += 8; + //bufOff += 8; ProcessBlock(buf, 0); } @@ -197,14 +271,15 @@ namespace Org.BouncyCastle.Crypto.Digests int neededColumns = hashSize / 8; for (int col = columns - neededColumns; col < columns; ++col) { - Pack.UInt64_To_LE(state[col], output, outOff); - outOff += 8; + Pack.UInt64_To_LE(state[col], output); + output = output[8..]; } Reset(); return hashSize; } +#endif public virtual void Reset() { @@ -236,6 +311,28 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected virtual void ProcessBlock(ReadOnlySpan<byte> input) + { + for (int col = 0; col < columns; ++col) + { + ulong word = Pack.LE_To_UInt64(input); + input = input[8..]; + + tempState1[col] = state[col] ^ word; + tempState2[col] = word; + } + + P(tempState1); + Q(tempState2); + + for (int col = 0; col < columns; ++col) + { + state[col] ^= tempState1[col] ^ tempState2[col]; + } + } +#endif + private void P(ulong[] s) { for (int round = 0; round < rounds; ++round) diff --git a/crypto/src/crypto/digests/GOST3411Digest.cs b/crypto/src/crypto/digests/GOST3411Digest.cs index 123751baa..dc1d376c3 100644 --- a/crypto/src/crypto/digests/GOST3411Digest.cs +++ b/crypto/src/crypto/digests/GOST3411Digest.cs @@ -91,10 +91,7 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount++; } - public void BlockUpdate( - byte[] input, - int inOff, - int length) + public void BlockUpdate(byte[] input, int inOff, int length) { while ((xBufOff != 0) && (length > 0)) { @@ -123,6 +120,34 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + while ((xBufOff != 0) && (input.Length > 0)) + { + Update(input[0]); + input = input[1..]; + } + + while (input.Length >= xBuf.Length) + { + input[..xBuf.Length].CopyTo(xBuf.AsSpan()); + + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + input = input[xBuf.Length..]; + byteCount += (uint)xBuf.Length; + } + + // load in the remainder. + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif + // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8 private byte[] K = new byte[32]; @@ -234,7 +259,7 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Copy(S, 0, H, 0, H.Length); } - private void finish() + private void Finish() { ulong bitCount = byteCount * 8; Pack.UInt64_To_LE(bitCount, L); @@ -248,11 +273,9 @@ namespace Org.BouncyCastle.Crypto.Digests processBlock(Sum, 0); } - public int DoFinal( - byte[] output, - int outOff) + public int DoFinal(byte[] output, int outOff) { - finish(); + Finish(); H.CopyTo(output, outOff); @@ -261,6 +284,19 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_LENGTH; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Finish(); + + H.CopyTo(output); + + Reset(); + + return DIGEST_LENGTH; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/GOST3411_2012Digest.cs b/crypto/src/crypto/digests/GOST3411_2012Digest.cs index 68cb6c035..259f4bcae 100644 --- a/crypto/src/crypto/digests/GOST3411_2012Digest.cs +++ b/crypto/src/crypto/digests/GOST3411_2012Digest.cs @@ -1,10 +1,11 @@ using System; -using Org.BouncyCastle.Crypto; + using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { - public abstract class Gost3411_2012Digest:IDigest,IMemoable + public abstract class Gost3411_2012Digest + : IDigest, IMemoable { private readonly byte[] IV = new byte[64]; private readonly byte[] N = new byte[64]; @@ -21,8 +22,8 @@ namespace Org.BouncyCastle.Crypto.Digests protected Gost3411_2012Digest(byte[] IV) { - System.Array.Copy(IV,this.IV,64); - System.Array.Copy(IV, h, 64); + Array.Copy(IV,this.IV,64); + Array.Copy(IV, h, 64); } public abstract string AlgorithmName { get; } @@ -43,7 +44,7 @@ namespace Org.BouncyCastle.Crypto.Digests if (bOff != 64) { - System.Array.Copy(block, bOff, m, 64 - lenM, lenM); + Array.Copy(block, bOff, m, 64 - lenM, lenM); } g_N(h, N, m); @@ -60,6 +61,39 @@ namespace Org.BouncyCastle.Crypto.Digests return 64; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + int lenM = 64 - bOff; + + // At this point it is certain that lenM is smaller than 64 + for (int i = 0; i != 64 - lenM; i++) + { + m[i] = 0; + } + + m[63 - lenM] = 1; + + if (bOff != 64) + { + Array.Copy(block, bOff, m, 64 - lenM, lenM); + } + + g_N(h, N, m); + addMod512(N, lenM * 8); + addMod512(Sigma, m); + g_N(h, Zero, N); + g_N(h, Zero, Sigma); + + reverse(h, tmp); + + tmp.CopyTo(output); + + Reset(); + return 64; + } +#endif + public int GetByteLength() { return 64; @@ -73,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Digests bOff = 64; Arrays.Fill(N, (byte)0); Arrays.Fill(Sigma, (byte)0); - System.Array.Copy(IV, 0, h, 0, 64); + Array.Copy(IV, 0, h, 0, 64); Arrays.Fill(block, (byte)0); } @@ -81,14 +115,14 @@ namespace Org.BouncyCastle.Crypto.Digests { Gost3411_2012Digest o = (Gost3411_2012Digest)other; - System.Array.Copy(o.IV, 0, this.IV, 0, 64); - System.Array.Copy(o.N, 0, this.N, 0, 64); - System.Array.Copy(o.Sigma, 0, this.Sigma, 0, 64); - System.Array.Copy(o.Ki, 0, this.Ki, 0, 64); - System.Array.Copy(o.m, 0, this.m, 0, 64); - System.Array.Copy(o.h, 0, this.h, 0, 64); + Array.Copy(o.IV, 0, this.IV, 0, 64); + Array.Copy(o.N, 0, this.N, 0, 64); + Array.Copy(o.Sigma, 0, this.Sigma, 0, 64); + Array.Copy(o.Ki, 0, this.Ki, 0, 64); + Array.Copy(o.m, 0, this.m, 0, 64); + Array.Copy(o.h, 0, this.h, 0, 64); - System.Array.Copy(o.block, 0, this.block, 0, 64); + Array.Copy(o.block, 0, this.block, 0, 64); this.bOff = o.bOff; } @@ -104,7 +138,6 @@ namespace Org.BouncyCastle.Crypto.Digests } } - public void BlockUpdate(byte[] input, int inOff, int len) { while (bOff != 64 && len > 0) @@ -114,7 +147,7 @@ namespace Org.BouncyCastle.Crypto.Digests } while (len >= 64) { - System.Array.Copy(input, inOff, tmp, 0, 64); + Array.Copy(input, inOff, tmp, 0, 64); reverse(tmp, block); g_N(h, N, block); addMod512(N, 512); @@ -130,8 +163,31 @@ namespace Org.BouncyCastle.Crypto.Digests } } - +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + while (bOff != 64 && input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + while (input.Length >= 64) + { + input[..64].CopyTo(tmp.AsSpan()); + reverse(tmp, block); + g_N(h, N, block); + addMod512(N, 512); + addMod512(Sigma, block); + input = input[64..]; + } + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif private void F(byte[] V) { @@ -317,7 +373,7 @@ namespace Org.BouncyCastle.Crypto.Digests private void E(byte[] K, byte[] m) { - System.Array.Copy(K, 0, Ki, 0, 64); + Array.Copy(K, 0, Ki, 0, 64); xor512(K, m); F(K); for (int i = 0; i < 11; ++i) @@ -334,7 +390,7 @@ namespace Org.BouncyCastle.Crypto.Digests private void g_N(byte[] h, byte[] N, byte[] m) { - System.Array.Copy(h, 0, tmp, 0, 64); + Array.Copy(h, 0, tmp, 0, 64); xor512(h, N); F(h); diff --git a/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs b/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs index 77cf6c50f..47877b2b5 100644 --- a/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs +++ b/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs @@ -46,6 +46,18 @@ namespace Org.BouncyCastle.Crypto.Digests return 32; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Span<byte> result = stackalloc byte[64]; + base.DoFinal(result); + + result[32..].CopyTo(output); + + return 32; + } +#endif + public override IMemoable Copy() { return new Gost3411_2012_256Digest(this); diff --git a/crypto/src/crypto/digests/GeneralDigest.cs b/crypto/src/crypto/digests/GeneralDigest.cs index c38b35fd3..02bad2cfb 100644 --- a/crypto/src/crypto/digests/GeneralDigest.cs +++ b/crypto/src/crypto/digests/GeneralDigest.cs @@ -95,6 +95,50 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount += length; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int length = input.Length; + + // + // fill the current word + // + int i = 0; + if (xBufOff != 0) + { + while (i < length) + { + xBuf[xBufOff++] = input[i++]; + if (xBufOff == 4) + { + ProcessWord(xBuf, 0); + xBufOff = 0; + break; + } + } + } + + // + // process whole words. + // + int limit = length - 3; + for (; i < limit; i += 4) + { + ProcessWord(input.Slice(i, 4)); + } + + // + // load in the remainder. + // + while (i < length) + { + xBuf[xBufOff++] = input[i++]; + } + + byteCount += length; + } +#endif + public void Finish() { long bitLength = (byteCount << 3); @@ -122,6 +166,9 @@ namespace Org.BouncyCastle.Crypto.Digests } internal abstract void ProcessWord(byte[] input, int inOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal abstract void ProcessWord(ReadOnlySpan<byte> word); +#endif internal abstract void ProcessLength(long bitLength); internal abstract void ProcessBlock(); public abstract string AlgorithmName { get; } @@ -129,5 +176,8 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract int DoFinal(byte[] output, int outOff); public abstract IMemoable Copy(); public abstract void Reset(IMemoable t); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract int DoFinal(Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/digests/Haraka256Digest.cs b/crypto/src/crypto/digests/Haraka256Digest.cs index f7af4bf69..27aea9b29 100644 --- a/crypto/src/crypto/digests/Haraka256Digest.cs +++ b/crypto/src/crypto/digests/Haraka256Digest.cs @@ -93,7 +93,58 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_SIZE; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int haraka256256(byte[] msg, Span<byte> output) + { + byte[][] s1 = new byte[2][]; + s1[0] = new byte[16]; + s1[1] = new byte[16]; + byte[][] s2 = new byte[2][]; + s2[0] = new byte[16]; + s2[1] = new byte[16]; + + Array.Copy(msg, 0, s1[0], 0, 16); + Array.Copy(msg, 16, s1[1], 0, 16); + + s1[0] = aesEnc(s1[0], RC[0]); + s1[1] = aesEnc(s1[1], RC[1]); + s1[0] = aesEnc(s1[0], RC[2]); + s1[1] = aesEnc(s1[1], RC[3]); + mix256(s1, s2); + + s1[0] = aesEnc(s2[0], RC[4]); + s1[1] = aesEnc(s2[1], RC[5]); + s1[0] = aesEnc(s1[0], RC[6]); + s1[1] = aesEnc(s1[1], RC[7]); + mix256(s1, s2); + + s1[0] = aesEnc(s2[0], RC[8]); + s1[1] = aesEnc(s2[1], RC[9]); + s1[0] = aesEnc(s1[0], RC[10]); + s1[1] = aesEnc(s1[1], RC[11]); + mix256(s1, s2); + s1[0] = aesEnc(s2[0], RC[12]); + s1[1] = aesEnc(s2[1], RC[13]); + s1[0] = aesEnc(s1[0], RC[14]); + s1[1] = aesEnc(s1[1], RC[15]); + mix256(s1, s2); + + s1[0] = aesEnc(s2[0], RC[16]); + s1[1] = aesEnc(s2[1], RC[17]); + s1[0] = aesEnc(s1[0], RC[18]); + s1[1] = aesEnc(s1[1], RC[19]); + mix256(s1, s2); + + s1[0] = Xor(s2[0], msg, 0); + s1[1] = Xor(s2[1], msg, 16); + + s1[0].AsSpan(0, 16).CopyTo(output); + s1[1].AsSpan(0, 16).CopyTo(output[16..]); + + return DIGEST_SIZE; + } +#endif public Haraka256Digest() { @@ -106,10 +157,7 @@ namespace Org.BouncyCastle.Crypto.Digests this.off = digest.off; } - public string getAlgorithmName() - { - return "Haraka-256"; - } + public override string AlgorithmName => "Haraka-256"; public override void Update(byte input) { @@ -132,6 +180,19 @@ namespace Org.BouncyCastle.Crypto.Digests off += len; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void BlockUpdate(ReadOnlySpan<byte> input) + { + if (off + input.Length > 32) + { + throw new ArgumentException("total input cannot be more than 32 bytes"); + } + + input.CopyTo(buffer.AsSpan(off)); + off += input.Length; + } +#endif + public override int DoFinal(byte[] output, int outOff) { if (off != 32) @@ -151,6 +212,27 @@ namespace Org.BouncyCastle.Crypto.Digests return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + if (off != 32) + { + throw new ArgumentException("input must be exactly 32 bytes"); + } + + if (output.Length < 32) + { + throw new ArgumentException("output too short to receive digest"); + } + + int rv = haraka256256(buffer, output); + + Reset(); + + return rv; + } +#endif + public override void Reset() { off = 0; diff --git a/crypto/src/crypto/digests/Haraka512Digest.cs b/crypto/src/crypto/digests/Haraka512Digest.cs index 36bdcbabe..0faeae710 100644 --- a/crypto/src/crypto/digests/Haraka512Digest.cs +++ b/crypto/src/crypto/digests/Haraka512Digest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { public class Haraka512Digest : HarakaBase @@ -167,10 +169,92 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_SIZE; } - public string GetAlgorithmName() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int Haraka512256(byte[] msg, Span<byte> output) { - return "Haraka-512"; + byte[][] s1 = new byte[4][]; + s1[0] = new byte[16]; + s1[1] = new byte[16]; + s1[2] = new byte[16]; + s1[3] = new byte[16]; + byte[][] s2 = new byte[4][]; + s2[0] = new byte[16]; + s2[1] = new byte[16]; + s2[2] = new byte[16]; + s2[3] = new byte[16]; + + //-- Unrolled version of above. + + Array.Copy(msg, 0, s1[0], 0, 16); + Array.Copy(msg, 16, s1[1], 0, 16); + Array.Copy(msg, 32, s1[2], 0, 16); + Array.Copy(msg, 48, s1[3], 0, 16); + + s1[0] = aesEnc(s1[0], RC[0]); + s1[1] = aesEnc(s1[1], RC[1]); + s1[2] = aesEnc(s1[2], RC[2]); + s1[3] = aesEnc(s1[3], RC[3]); + s1[0] = aesEnc(s1[0], RC[4]); + s1[1] = aesEnc(s1[1], RC[5]); + s1[2] = aesEnc(s1[2], RC[6]); + s1[3] = aesEnc(s1[3], RC[7]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[8]); + s1[1] = aesEnc(s2[1], RC[9]); + s1[2] = aesEnc(s2[2], RC[10]); + s1[3] = aesEnc(s2[3], RC[11]); + s1[0] = aesEnc(s1[0], RC[12]); + s1[1] = aesEnc(s1[1], RC[13]); + s1[2] = aesEnc(s1[2], RC[14]); + s1[3] = aesEnc(s1[3], RC[15]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[16]); + s1[1] = aesEnc(s2[1], RC[17]); + s1[2] = aesEnc(s2[2], RC[18]); + s1[3] = aesEnc(s2[3], RC[19]); + s1[0] = aesEnc(s1[0], RC[20]); + s1[1] = aesEnc(s1[1], RC[21]); + s1[2] = aesEnc(s1[2], RC[22]); + s1[3] = aesEnc(s1[3], RC[23]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[24]); + s1[1] = aesEnc(s2[1], RC[25]); + s1[2] = aesEnc(s2[2], RC[26]); + s1[3] = aesEnc(s2[3], RC[27]); + s1[0] = aesEnc(s1[0], RC[28]); + s1[1] = aesEnc(s1[1], RC[29]); + s1[2] = aesEnc(s1[2], RC[30]); + s1[3] = aesEnc(s1[3], RC[31]); + Mix512(s1, s2); + + s1[0] = aesEnc(s2[0], RC[32]); + s1[1] = aesEnc(s2[1], RC[33]); + s1[2] = aesEnc(s2[2], RC[34]); + s1[3] = aesEnc(s2[3], RC[35]); + s1[0] = aesEnc(s1[0], RC[36]); + s1[1] = aesEnc(s1[1], RC[37]); + s1[2] = aesEnc(s1[2], RC[38]); + s1[3] = aesEnc(s1[3], RC[39]); + Mix512(s1, s2); + + s1[0] = Xor(s2[0], msg, 0); + s1[1] = Xor(s2[1], msg, 16); + s1[2] = Xor(s2[2], msg, 32); + s1[3] = Xor(s2[3], msg, 48); + + s1[0].AsSpan(8, 8).CopyTo(output); + s1[1].AsSpan(8, 8).CopyTo(output[8..]); + s1[2].AsSpan(0, 8).CopyTo(output[16..]); + s1[3].AsSpan(0, 8).CopyTo(output[24..]); + + return DIGEST_SIZE; } +#endif + + public override string AlgorithmName => "Haraka-512"; public override void Update(byte input) { @@ -193,6 +277,19 @@ namespace Org.BouncyCastle.Crypto.Digests off += len; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void BlockUpdate(ReadOnlySpan<byte> input) + { + if (off + input.Length > 64) + { + throw new ArgumentException("total input cannot be more than 64 bytes"); + } + + input.CopyTo(buffer.AsSpan(off)); + off += input.Length; + } +#endif + public override int DoFinal(byte[] output, int outOff) { if (off != 64) @@ -212,6 +309,27 @@ namespace Org.BouncyCastle.Crypto.Digests return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + if (off != 64) + { + throw new ArgumentException("input must be exactly 64 bytes"); + } + + if (output.Length < 32) + { + throw new ArgumentException("output too short to receive digest"); + } + + int rv = Haraka512256(buffer, output); + + Reset(); + + return rv; + } +#endif + public override void Reset() { off = 0; diff --git a/crypto/src/crypto/digests/HarakaBase.cs b/crypto/src/crypto/digests/HarakaBase.cs index 6157198f1..1270de35c 100644 --- a/crypto/src/crypto/digests/HarakaBase.cs +++ b/crypto/src/crypto/digests/HarakaBase.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Org.BouncyCastle.Crypto.Digests { @@ -26,10 +24,7 @@ namespace Org.BouncyCastle.Crypto.Digests {(byte)0xe1, (byte)0xf8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xd9, (byte)0x8e, (byte)0x94, (byte)0x9b, (byte)0x1e, (byte)0x87, (byte)0xe9, (byte)0xce, (byte)0x55, (byte)0x28, (byte)0xdf}, {(byte)0x8c, (byte)0xa1, (byte)0x89, (byte)0x0d, (byte)0xbf, (byte)0xe6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2d, (byte)0x0f, (byte)0xb0, (byte)0x54, (byte)0xbb, (byte)0x16}}; - public string AlgorithmName - { - get { return "Haraka Base"; } - } + public abstract string AlgorithmName { get; } static byte sBox(byte x) { @@ -144,5 +139,10 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract void Reset(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract void BlockUpdate(ReadOnlySpan<byte> input); + + public abstract int DoFinal(Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs index 2da2e099e..b8305cc13 100644 --- a/crypto/src/crypto/digests/KeccakDigest.cs +++ b/crypto/src/crypto/digests/KeccakDigest.cs @@ -76,6 +76,13 @@ namespace Org.BouncyCastle.Crypto.Digests Absorb(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + Absorb(input); + } +#endif + public virtual int DoFinal(byte[] output, int outOff) { Squeeze(output, outOff, fixedOutputLength); @@ -85,6 +92,18 @@ namespace Org.BouncyCastle.Crypto.Digests return GetDigestSize(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + int digestSize = GetDigestSize(); + Squeeze(output[..digestSize]); + + Reset(); + + return digestSize; + } +#endif + /* * TODO Possible API change to support partial-byte suffixes. */ @@ -199,6 +218,46 @@ namespace Org.BouncyCastle.Crypto.Digests this.bitsInQueue = remaining << 3; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected void Absorb(ReadOnlySpan<byte> data) + { + if ((bitsInQueue & 7) != 0) + throw new InvalidOperationException("attempt to absorb with odd length queue"); + if (squeezing) + throw new InvalidOperationException("attempt to absorb while squeezing"); + + int bytesInQueue = bitsInQueue >> 3; + int rateBytes = rate >> 3; + + int len = data.Length; + int available = rateBytes - bytesInQueue; + if (len < available) + { + data.CopyTo(dataQueue.AsSpan(bytesInQueue)); + this.bitsInQueue += len << 3; + return; + } + + int count = 0; + if (bytesInQueue > 0) + { + data[..available].CopyTo(dataQueue.AsSpan(bytesInQueue)); + count += available; + KeccakAbsorb(dataQueue, 0); + } + + int remaining; + while ((remaining = len - count) >= rateBytes) + { + KeccakAbsorb(data[count..]); + count += rateBytes; + } + + data[count..].CopyTo(dataQueue.AsSpan()); + this.bitsInQueue = remaining << 3; + } +#endif + protected void AbsorbBits(int data, int bits) { if (bits < 1 || bits > 7) @@ -270,6 +329,30 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected void Squeeze(Span<byte> output) + { + if (!squeezing) + { + PadAndSwitchToSqueezingPhase(); + } + long outputLength = (long)output.Length << 3; + + long i = 0; + while (i < outputLength) + { + if (bitsInQueue == 0) + { + KeccakExtract(); + } + int partialBlock = (int)System.Math.Min(bitsInQueue, outputLength - i); + dataQueue.AsSpan((rate - bitsInQueue) >> 3, partialBlock >> 3).CopyTo(output[(int)(i >> 3)..]); + bitsInQueue -= partialBlock; + i += partialBlock; + } + } +#endif + private void KeccakAbsorb(byte[] data, int off) { int count = rate >> 6; @@ -282,6 +365,20 @@ namespace Org.BouncyCastle.Crypto.Digests KeccakPermutation(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void KeccakAbsorb(ReadOnlySpan<byte> data) + { + int count = rate >> 6, off = 0; + for (int i = 0; i < count; ++i) + { + state[i] ^= Pack.LE_To_UInt64(data[off..]); + off += 8; + } + + KeccakPermutation(); + } +#endif + private void KeccakExtract() { KeccakPermutation(); diff --git a/crypto/src/crypto/digests/LongDigest.cs b/crypto/src/crypto/digests/LongDigest.cs index aaa0b43ce..6a2f94ece 100644 --- a/crypto/src/crypto/digests/LongDigest.cs +++ b/crypto/src/crypto/digests/LongDigest.cs @@ -10,46 +10,46 @@ namespace Org.BouncyCastle.Crypto.Digests * Base class for SHA-384 and SHA-512. */ public abstract class LongDigest - : IDigest, IMemoable + : IDigest, IMemoable { - private int MyByteLength = 128; + private int MyByteLength = 128; - private byte[] xBuf; - private int xBufOff; + private byte[] xBuf; + private int xBufOff; - private long byteCount1; - private long byteCount2; + private long byteCount1; + private long byteCount2; internal ulong H1, H2, H3, H4, H5, H6, H7, H8; private ulong[] W = new ulong[80]; private int wOff; - /** + /** * Constructor for variable length word */ internal LongDigest() { xBuf = new byte[8]; - Reset(); + Reset(); } - /** + /** * Copy constructor. We are using copy constructors in place * of the object.Clone() interface as this interface is not * supported by J2ME. */ internal LongDigest( - LongDigest t) - { - xBuf = new byte[t.xBuf.Length]; + LongDigest t) + { + xBuf = new byte[t.xBuf.Length]; - CopyIn(t); - } + CopyIn(t); + } - protected void CopyIn(LongDigest t) - { + protected void CopyIn(LongDigest t) + { Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); xBufOff = t.xBufOff; @@ -84,9 +84,9 @@ namespace Org.BouncyCastle.Crypto.Digests } public void BlockUpdate( - byte[] input, - int inOff, - int length) + byte[] input, + int inOff, + int length) { // // fill the current word @@ -123,12 +123,54 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int inOff = 0; + int length = input.Length; + + // + // fill the current word + // + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length >= xBuf.Length) + { + ProcessWord(input.Slice(inOff, xBuf.Length)); + + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount1 += xBuf.Length; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } +#endif + public void Finish() { AdjustByteCounts(); - long lowBitLength = byteCount1 << 3; - long hiBitLength = byteCount2; + long lowBitLength = byteCount1 << 3; + long hiBitLength = byteCount2; // // add the pad bytes. @@ -151,20 +193,20 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount2 = 0; xBufOff = 0; - for ( int i = 0; i < xBuf.Length; i++ ) + for (int i = 0; i < xBuf.Length; i++) { xBuf[i] = 0; } wOff = 0; - Array.Clear(W, 0, W.Length); + Array.Clear(W, 0, W.Length); } internal void ProcessWord( - byte[] input, - int inOff) + byte[] input, + int inOff) { - W[wOff] = Pack.BE_To_UInt64(input, inOff); + W[wOff] = Pack.BE_To_UInt64(input, inOff); if (++wOff == 16) { @@ -172,6 +214,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void ProcessWord(ReadOnlySpan<byte> word) + { + W[wOff] = Pack.BE_To_UInt64(word); + + if (++wOff == 16) + { + ProcessBlock(); + } + } +#endif + /** * adjust the byte counts so that byteCount2 represents the * upper long (less 3 bits) word of the byte count. @@ -180,14 +234,14 @@ namespace Org.BouncyCastle.Crypto.Digests { if (byteCount1 > 0x1fffffffffffffffL) { - byteCount2 += (long) ((ulong) byteCount1 >> 61); + byteCount2 += (long)((ulong)byteCount1 >> 61); byteCount1 &= 0x1fffffffffffffffL; } } internal void ProcessLength( - long lowW, - long hiW) + long lowW, + long hiW) { if (wOff > 14) { @@ -222,51 +276,51 @@ namespace Org.BouncyCastle.Crypto.Digests ulong g = H7; ulong h = H8; - int t = 0; - for(int i = 0; i < 10; i ++) - { - // t = 8 * i - h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++]; - d += h; - h += Sum0(a) + Maj(a, b, c); - - // t = 8 * i + 1 - g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++]; - c += g; - g += Sum0(h) + Maj(h, a, b); - - // t = 8 * i + 2 - f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++]; - b += f; - f += Sum0(g) + Maj(g, h, a); - - // t = 8 * i + 3 - e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++]; - a += e; - e += Sum0(f) + Maj(f, g, h); - - // t = 8 * i + 4 - d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++]; - h += d; - d += Sum0(e) + Maj(e, f, g); - - // t = 8 * i + 5 - c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++]; - g += c; - c += Sum0(d) + Maj(d, e, f); - - // t = 8 * i + 6 - b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++]; - f += b; - b += Sum0(c) + Maj(c, d, e); - - // t = 8 * i + 7 - a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++]; - e += a; - a += Sum0(b) + Maj(b, c, d); - } - - H1 += a; + int t = 0; + for (int i = 0; i < 10; i++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; H2 += b; H3 += c; H4 += d; @@ -275,14 +329,14 @@ namespace Org.BouncyCastle.Crypto.Digests H7 += g; H8 += h; - // + // // reset the offset and clean out the word buffer. // wOff = 0; - Array.Clear(W, 0, 16); - } + Array.Clear(W, 0, 16); + } - /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ + /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ private static ulong Ch(ulong x, ulong y, ulong z) { return (x & y) ^ (~x & z); @@ -295,61 +349,64 @@ namespace Org.BouncyCastle.Crypto.Digests private static ulong Sum0(ulong x) { - return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); + return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); } - private static ulong Sum1(ulong x) + private static ulong Sum1(ulong x) { - return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); + return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); } private static ulong Sigma0(ulong x) { - return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); + return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); } private static ulong Sigma1(ulong x) { - return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); + return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); } /* SHA-384 and SHA-512 Constants * (represent the first 64 bits of the fractional parts of the * cube roots of the first sixty-four prime numbers) */ - internal static readonly ulong[] K = - { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 - }; - - public int GetByteLength() - { - return MyByteLength; - } - - public abstract string AlgorithmName { get; } - public abstract int GetDigestSize(); + internal static readonly ulong[] K = + { + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 + }; + + public int GetByteLength() + { + return MyByteLength; + } + + public abstract string AlgorithmName { get; } + public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); - public abstract IMemoable Copy(); - public abstract void Reset(IMemoable t); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract int DoFinal(Span<byte> output); +#endif + public abstract IMemoable Copy(); + public abstract void Reset(IMemoable t); } } diff --git a/crypto/src/crypto/digests/MD2Digest.cs b/crypto/src/crypto/digests/MD2Digest.cs index f72d08768..cea89a311 100644 --- a/crypto/src/crypto/digests/MD2Digest.cs +++ b/crypto/src/crypto/digests/MD2Digest.cs @@ -1,6 +1,5 @@ using System; -using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests @@ -99,6 +98,30 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + // add padding + byte paddingByte = (byte)(M.Length - mOff); + for (int i = mOff; i < M.Length; i++) + { + M[i] = paddingByte; + } + //do final check sum + ProcessChecksum(M); + // do final block process + ProcessBlock(M); + + ProcessBlock(C); + + X.AsSpan(xOff, 16).CopyTo(output); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the digest back to it's initial state. */ @@ -179,6 +202,40 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + // + // fill the current word + // + while ((mOff != 0) && (input.Length > 0)) + { + Update(input[0]); + input = input[1..]; + } + + // + // process whole words. + // + while (input.Length >= 16) + { + input[..16].CopyTo(M); + ProcessChecksum(M); + ProcessBlock(M); + input = input[16..]; + } + + // + // load in the remainder. + // + while (input.Length > 0) + { + Update(input[0]); + input = input[1..]; + } + } +#endif + internal void ProcessChecksum(byte[] m) { int L = C[15]; diff --git a/crypto/src/crypto/digests/MD4Digest.cs b/crypto/src/crypto/digests/MD4Digest.cs index 8743f7dad..2eb2c8400 100644 --- a/crypto/src/crypto/digests/MD4Digest.cs +++ b/crypto/src/crypto/digests/MD4Digest.cs @@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests @@ -60,12 +61,9 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { - X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) - | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + X[xOff++] = (int)Pack.LE_To_UInt32(input, inOff); if (xOff == 16) { @@ -73,6 +71,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -85,32 +95,35 @@ namespace Org.BouncyCastle.Crypto.Digests X[15] = (int)((ulong) bitLength >> 32); } - private void UnpackWord( - int word, - byte[] outBytes, - int outOff) + public override int DoFinal(byte[] output, int outOff) { - outBytes[outOff] = (byte)word; - outBytes[outOff + 1] = (byte)((uint) word >> 8); - outBytes[outOff + 2] = (byte)((uint) word >> 16); - outBytes[outOff + 3] = (byte)((uint) word >> 24); + Finish(); + + Pack.UInt32_To_LE((uint)H1, output, outOff); + Pack.UInt32_To_LE((uint)H2, output, outOff + 4); + Pack.UInt32_To_LE((uint)H3, output, outOff + 8); + Pack.UInt32_To_LE((uint)H4, output, outOff + 12); + + Reset(); + + return DigestLength; } - public override int DoFinal( - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) { Finish(); - UnpackWord(H1, output, outOff); - UnpackWord(H2, output, outOff + 4); - UnpackWord(H3, output, outOff + 8); - UnpackWord(H4, output, outOff + 12); + Pack.UInt32_To_LE((uint)H1, output); + Pack.UInt32_To_LE((uint)H2, output[4..]); + Pack.UInt32_To_LE((uint)H3, output[8..]); + Pack.UInt32_To_LE((uint)H4, output[12..]); Reset(); return DigestLength; } +#endif /** * reset the chaining variables to the IV values. diff --git a/crypto/src/crypto/digests/MD5Digest.cs b/crypto/src/crypto/digests/MD5Digest.cs index c60ac92a3..062d7bb46 100644 --- a/crypto/src/crypto/digests/MD5Digest.cs +++ b/crypto/src/crypto/digests/MD5Digest.cs @@ -55,9 +55,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.LE_To_UInt32(input, inOff); @@ -67,6 +65,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.LE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -103,6 +113,22 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE(H1, output); + Pack.UInt32_To_LE(H2, output[4..]); + Pack.UInt32_To_LE(H3, output[8..]); + Pack.UInt32_To_LE(H4, output[12..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/NonMemoableDigest.cs b/crypto/src/crypto/digests/NonMemoableDigest.cs index 02c49b887..bad38911b 100644 --- a/crypto/src/crypto/digests/NonMemoableDigest.cs +++ b/crypto/src/crypto/digests/NonMemoableDigest.cs @@ -44,11 +44,25 @@ namespace Org.BouncyCastle.Crypto.Digests mBaseDigest.BlockUpdate(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + mBaseDigest.BlockUpdate(input); + } +#endif + public virtual int DoFinal(byte[] output, int outOff) { return mBaseDigest.DoFinal(output, outOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + return mBaseDigest.DoFinal(output); + } +#endif + public virtual void Reset() { mBaseDigest.Reset(); diff --git a/crypto/src/crypto/digests/NullDigest.cs b/crypto/src/crypto/digests/NullDigest.cs index d14dd5c9f..28554cf3e 100644 --- a/crypto/src/crypto/digests/NullDigest.cs +++ b/crypto/src/crypto/digests/NullDigest.cs @@ -35,17 +35,48 @@ namespace Org.BouncyCastle.Crypto.Digests bOut.Write(inBytes, inOff, len); } - public int DoFinal(byte[] outBytes, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { + bOut.Write(input); + } +#endif + + public int DoFinal(byte[] outBytes, int outOff) + { + try + { + byte[] data = bOut.GetBuffer(); + int length = Convert.ToInt32(bOut.Length); + + Array.Copy(data, 0, outBytes, outOff, length); + + return length; + } + finally + { + Reset(); + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { try { - return Streams.WriteBufTo(bOut, outBytes, outOff); + byte[] data = bOut.GetBuffer(); + int length = Convert.ToInt32(bOut.Length); + + data.AsSpan(0, length).CopyTo(output); + + return length; } finally { Reset(); } } +#endif public void Reset() { diff --git a/crypto/src/crypto/digests/ParallelHash.cs b/crypto/src/crypto/digests/ParallelHash.cs index f28795f5a..8054b2005 100644 --- a/crypto/src/crypto/digests/ParallelHash.cs +++ b/crypto/src/crypto/digests/ParallelHash.cs @@ -85,7 +85,7 @@ namespace Org.BouncyCastle.Crypto.Digests buffer[bufOff++] = b; if (bufOff == buffer.Length) { - compress(); + Compress(); } } @@ -106,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Digests if (bufOff == buffer.Length) { - compress(); + Compress(); } } @@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Digests { while (len - i >= B) { - compress(inBuf, inOff + i, B); + Compress(inBuf, inOff + i, B); i += B; } } @@ -125,13 +125,49 @@ namespace Org.BouncyCastle.Crypto.Digests } } - private void compress() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) { - compress(buffer, 0, bufOff); + // + // fill the current word + // + int i = 0; + if (bufOff != 0) + { + while (i < input.Length && bufOff != buffer.Length) + { + buffer[bufOff++] = input[i++]; + } + + if (bufOff == buffer.Length) + { + Compress(); + } + } + + if (i < input.Length) + { + while (input.Length - i >= B) + { + Compress(input, i, B); + i += B; + } + } + + while (i < input.Length) + { + Update(input[i++]); + } + } +#endif + + private void Compress() + { + Compress(buffer, 0, bufOff); bufOff = 0; } - private void compress(byte[] buf, int offSet, int len) + private void Compress(byte[] buf, int offSet, int len) { compressor.BlockUpdate(buf, offSet, len); compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length); @@ -141,11 +177,23 @@ namespace Org.BouncyCastle.Crypto.Digests nCount++; } - private void wrapUp(int outputSize) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Compress(ReadOnlySpan<byte> input, int pos, int len) + { + compressor.BlockUpdate(input.Slice(pos, len)); + compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length); + + cshake.BlockUpdate(compressorBuffer, 0, compressorBuffer.Length); + + nCount++; + } +#endif + + private void WrapUp(int outputSize) { if (bufOff != 0) { - compress(); + Compress(); } byte[] nOut = XofUtilities.RightEncode(nCount); byte[] encOut = XofUtilities.RightEncode(outputSize * 8); @@ -160,21 +208,37 @@ namespace Org.BouncyCastle.Crypto.Digests { if (firstOutput) { - wrapUp(outputLength); + WrapUp(outputLength); + } + + int rv = cshake.DoFinal(outBuf, outOff); + + Reset(); + + return rv; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + if (firstOutput) + { + WrapUp(outputLength); } - int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize()); + int rv = cshake.DoFinal(output); Reset(); return rv; } +#endif public virtual int DoFinal(byte[] outBuf, int outOff, int outLen) { if (firstOutput) { - wrapUp(outputLength); + WrapUp(outputLength); } int rv = cshake.DoFinal(outBuf, outOff, outLen); @@ -184,16 +248,44 @@ namespace Org.BouncyCastle.Crypto.Digests return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int OutputFinal(Span<byte> output) + { + if (firstOutput) + { + WrapUp(outputLength); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } +#endif + public virtual int DoOutput(byte[] outBuf, int outOff, int outLen) { if (firstOutput) { - wrapUp(0); + WrapUp(0); } return cshake.DoOutput(outBuf, outOff, outLen); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int Output(Span<byte> output) + { + if (firstOutput) + { + WrapUp(0); + } + + return cshake.Output(output); + } +#endif + public virtual void Reset() { cshake.Reset(); diff --git a/crypto/src/crypto/digests/RipeMD128Digest.cs b/crypto/src/crypto/digests/RipeMD128Digest.cs index cba2c65d3..b66452682 100644 --- a/crypto/src/crypto/digests/RipeMD128Digest.cs +++ b/crypto/src/crypto/digests/RipeMD128Digest.cs @@ -68,6 +68,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -94,6 +106,22 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/RipeMD160Digest.cs b/crypto/src/crypto/digests/RipeMD160Digest.cs index 0fc2a4a1c..a95bff48a 100644 --- a/crypto/src/crypto/digests/RipeMD160Digest.cs +++ b/crypto/src/crypto/digests/RipeMD160Digest.cs @@ -70,6 +70,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -97,6 +109,23 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + Pack.UInt32_To_LE((uint)H4, output[16..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables to the IV values. */ diff --git a/crypto/src/crypto/digests/RipeMD256Digest.cs b/crypto/src/crypto/digests/RipeMD256Digest.cs index 621162a6f..40508e9f7 100644 --- a/crypto/src/crypto/digests/RipeMD256Digest.cs +++ b/crypto/src/crypto/digests/RipeMD256Digest.cs @@ -70,6 +70,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -100,6 +112,26 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + Pack.UInt32_To_LE((uint)H4, output[16..]); + Pack.UInt32_To_LE((uint)H5, output[20..]); + Pack.UInt32_To_LE((uint)H6, output[24..]); + Pack.UInt32_To_LE((uint)H7, output[28..]); + + Reset(); + + return DigestLength; + } +#endif + /// <summary> reset the chaining variables to the IV values.</summary> public override void Reset() { diff --git a/crypto/src/crypto/digests/RipeMD320Digest.cs b/crypto/src/crypto/digests/RipeMD320Digest.cs index c46bc4fea..ddaf858ff 100644 --- a/crypto/src/crypto/digests/RipeMD320Digest.cs +++ b/crypto/src/crypto/digests/RipeMD320Digest.cs @@ -73,6 +73,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff++] = (int)Pack.LE_To_UInt32(word); + + if (xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength( long bitLength) { @@ -105,6 +117,28 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_LE((uint)H0, output); + Pack.UInt32_To_LE((uint)H1, output[4..]); + Pack.UInt32_To_LE((uint)H2, output[8..]); + Pack.UInt32_To_LE((uint)H3, output[12..]); + Pack.UInt32_To_LE((uint)H4, output[16..]); + Pack.UInt32_To_LE((uint)H5, output[20..]); + Pack.UInt32_To_LE((uint)H6, output[24..]); + Pack.UInt32_To_LE((uint)H7, output[28..]); + Pack.UInt32_To_LE((uint)H8, output[32..]); + Pack.UInt32_To_LE((uint)H9, output[36..]); + + Reset(); + + return DigestLength; + } +#endif + /// <summary> reset the chaining variables to the IV values.</summary> public override void Reset() { diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs index 3dc63a6ed..778c453d8 100644 --- a/crypto/src/crypto/digests/SHA3Digest.cs +++ b/crypto/src/crypto/digests/SHA3Digest.cs @@ -55,6 +55,15 @@ namespace Org.BouncyCastle.Crypto.Digests return base.DoFinal(output, outOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + AbsorbBits(0x02, 2); + + return base.DoFinal(output); + } +#endif + /* * TODO Possible API change to support partial-byte suffixes. */ diff --git a/crypto/src/crypto/digests/SM3Digest.cs b/crypto/src/crypto/digests/SM3Digest.cs index 449d7c161..81d4a68a9 100644 --- a/crypto/src/crypto/digests/SM3Digest.cs +++ b/crypto/src/crypto/digests/SM3Digest.cs @@ -118,7 +118,6 @@ namespace Org.BouncyCastle.Crypto.Digests this.xOff = 0; } - public override int DoFinal(byte[] output, int outOff) { Finish(); @@ -130,13 +129,22 @@ namespace Org.BouncyCastle.Crypto.Digests return DIGEST_LENGTH; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(V, output); + + Reset(); - internal override void ProcessWord(byte[] input, - int inOff) + return DIGEST_LENGTH; + } +#endif + + internal override void ProcessWord(byte[] input, int inOff) { - uint n = Pack.BE_To_UInt32(input, inOff); - this.inwords[this.xOff] = n; - ++this.xOff; + inwords[xOff++] = Pack.BE_To_UInt32(input, inOff); if (this.xOff >= 16) { @@ -144,7 +152,19 @@ namespace Org.BouncyCastle.Crypto.Digests } } - internal override void ProcessLength(long bitLength) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + inwords[xOff++] = Pack.BE_To_UInt32(word); + + if (this.xOff >= 16) + { + ProcessBlock(); + } + } +#endif + + internal override void ProcessLength(long bitLength) { if (this.xOff > (BLOCK_SIZE - 2)) { diff --git a/crypto/src/crypto/digests/Sha1Digest.cs b/crypto/src/crypto/digests/Sha1Digest.cs index 60ec651d5..9b384b8cb 100644 --- a/crypto/src/crypto/digests/Sha1Digest.cs +++ b/crypto/src/crypto/digests/Sha1Digest.cs @@ -61,9 +61,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.BE_To_UInt32(input, inOff); @@ -73,6 +71,18 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.BE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + internal override void ProcessLength(long bitLength) { if (xOff > 14) @@ -101,6 +111,23 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(H1, output); + Pack.UInt32_To_BE(H2, output[4..]); + Pack.UInt32_To_BE(H3, output[8..]); + Pack.UInt32_To_BE(H4, output[12..]); + Pack.UInt32_To_BE(H5, output[16..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -279,6 +306,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha224Digest.cs b/crypto/src/crypto/digests/Sha224Digest.cs index b4e853745..28d09adec 100644 --- a/crypto/src/crypto/digests/Sha224Digest.cs +++ b/crypto/src/crypto/digests/Sha224Digest.cs @@ -72,9 +72,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.BE_To_UInt32(input, inOff); @@ -84,7 +82,19 @@ namespace Org.BouncyCastle.Crypto.Digests } } - internal override void ProcessLength( +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.BE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + + internal override void ProcessLength( long bitLength) { if (xOff > 14) @@ -96,9 +106,7 @@ namespace Org.BouncyCastle.Crypto.Digests X[15] = (uint)((ulong)bitLength); } - public override int DoFinal( - byte[] output, - int outOff) + public override int DoFinal(byte[] output, int outOff) { Finish(); @@ -115,7 +123,26 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(H1, output); + Pack.UInt32_To_BE(H2, output[4..]); + Pack.UInt32_To_BE(H3, output[8..]); + Pack.UInt32_To_BE(H4, output[12..]); + Pack.UInt32_To_BE(H5, output[16..]); + Pack.UInt32_To_BE(H6, output[20..]); + Pack.UInt32_To_BE(H7, output[24..]); + + Reset(); + + return DigestLength; + } +#endif + + /** * reset the chaining variables */ public override void Reset() @@ -284,6 +311,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha256Digest.cs b/crypto/src/crypto/digests/Sha256Digest.cs index 63d5b8bee..51859697e 100644 --- a/crypto/src/crypto/digests/Sha256Digest.cs +++ b/crypto/src/crypto/digests/Sha256Digest.cs @@ -67,9 +67,7 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } - internal override void ProcessWord( - byte[] input, - int inOff) + internal override void ProcessWord(byte[] input, int inOff) { X[xOff] = Pack.BE_To_UInt32(input, inOff); @@ -79,7 +77,19 @@ namespace Org.BouncyCastle.Crypto.Digests } } - internal override void ProcessLength( +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override void ProcessWord(ReadOnlySpan<byte> word) + { + X[xOff] = Pack.BE_To_UInt32(word); + + if (++xOff == 16) + { + ProcessBlock(); + } + } +#endif + + internal override void ProcessLength( long bitLength) { if (xOff > 14) @@ -91,26 +101,44 @@ namespace Org.BouncyCastle.Crypto.Digests X[15] = (uint)((ulong)bitLength); } - public override int DoFinal( - byte[] output, - int outOff) + public override int DoFinal(byte[] output, int outOff) { Finish(); - Pack.UInt32_To_BE((uint)H1, output, outOff); - Pack.UInt32_To_BE((uint)H2, output, outOff + 4); - Pack.UInt32_To_BE((uint)H3, output, outOff + 8); - Pack.UInt32_To_BE((uint)H4, output, outOff + 12); - Pack.UInt32_To_BE((uint)H5, output, outOff + 16); - Pack.UInt32_To_BE((uint)H6, output, outOff + 20); - Pack.UInt32_To_BE((uint)H7, output, outOff + 24); - Pack.UInt32_To_BE((uint)H8, output, outOff + 28); + Pack.UInt32_To_BE(H1, output, outOff); + Pack.UInt32_To_BE(H2, output, outOff + 4); + Pack.UInt32_To_BE(H3, output, outOff + 8); + Pack.UInt32_To_BE(H4, output, outOff + 12); + Pack.UInt32_To_BE(H5, output, outOff + 16); + Pack.UInt32_To_BE(H6, output, outOff + 20); + Pack.UInt32_To_BE(H7, output, outOff + 24); + Pack.UInt32_To_BE(H8, output, outOff + 28); Reset(); return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt32_To_BE(H1, output); + Pack.UInt32_To_BE(H2, output[4..]); + Pack.UInt32_To_BE(H3, output[8..]); + Pack.UInt32_To_BE(H4, output[12..]); + Pack.UInt32_To_BE(H5, output[16..]); + Pack.UInt32_To_BE(H6, output[20..]); + Pack.UInt32_To_BE(H7, output[24..]); + Pack.UInt32_To_BE(H8, output[28..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -313,6 +341,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha384Digest.cs b/crypto/src/crypto/digests/Sha384Digest.cs index e6c9a9aa9..e4e65ed85 100644 --- a/crypto/src/crypto/digests/Sha384Digest.cs +++ b/crypto/src/crypto/digests/Sha384Digest.cs @@ -64,6 +64,24 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt64_To_BE(H1, output); + Pack.UInt64_To_BE(H2, output[8..]); + Pack.UInt64_To_BE(H3, output[16..]); + Pack.UInt64_To_BE(H4, output[24..]); + Pack.UInt64_To_BE(H5, output[32..]); + Pack.UInt64_To_BE(H6, output[40..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -72,9 +90,9 @@ namespace Org.BouncyCastle.Crypto.Digests base.Reset(); /* SHA-384 initial hash value - * The first 64 bits of the fractional parts of the square roots - * of the 9th through 16th prime numbers - */ + * The first 64 bits of the fractional parts of the square roots + * of the 9th through 16th prime numbers + */ H1 = 0xcbbb9d5dc1059ed8; H2 = 0x629a292a367cd507; H3 = 0x9159015a3070dd17; @@ -96,6 +114,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha512Digest.cs b/crypto/src/crypto/digests/Sha512Digest.cs index 2a0964fd3..9156c24bf 100644 --- a/crypto/src/crypto/digests/Sha512Digest.cs +++ b/crypto/src/crypto/digests/Sha512Digest.cs @@ -67,6 +67,26 @@ namespace Org.BouncyCastle.Crypto.Digests } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt64_To_BE(H1, output); + Pack.UInt64_To_BE(H2, output[8..]); + Pack.UInt64_To_BE(H3, output[16..]); + Pack.UInt64_To_BE(H4, output[24..]); + Pack.UInt64_To_BE(H5, output[32..]); + Pack.UInt64_To_BE(H6, output[40..]); + Pack.UInt64_To_BE(H7, output[48..]); + Pack.UInt64_To_BE(H8, output[56..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ @@ -99,6 +119,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - } } diff --git a/crypto/src/crypto/digests/Sha512tDigest.cs b/crypto/src/crypto/digests/Sha512tDigest.cs index 2caefa763..939dbda4f 100644 --- a/crypto/src/crypto/digests/Sha512tDigest.cs +++ b/crypto/src/crypto/digests/Sha512tDigest.cs @@ -76,6 +76,26 @@ namespace Org.BouncyCastle.Crypto.Digests return digestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + Finish(); + + UInt64_To_BE(H1, output, 0, digestLength); + UInt64_To_BE(H2, output, 8, digestLength - 8); + UInt64_To_BE(H3, output, 16, digestLength - 16); + UInt64_To_BE(H4, output, 24, digestLength - 24); + UInt64_To_BE(H5, output, 32, digestLength - 32); + UInt64_To_BE(H6, output, 40, digestLength - 40); + UInt64_To_BE(H7, output, 48, digestLength - 48); + UInt64_To_BE(H8, output, 56, digestLength - 56); + + Reset(); + + return digestLength; + } +#endif + /** * reset the chaining variables */ @@ -170,7 +190,32 @@ namespace Org.BouncyCastle.Crypto.Digests } } - public override IMemoable Copy() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void UInt64_To_BE(ulong n, Span<byte> bs, int off, int max) + { + if (max > 0) + { + UInt32_To_BE((uint)(n >> 32), bs, off, max); + + if (max > 4) + { + UInt32_To_BE((uint)n, bs, off + 4, max - 4); + } + } + } + + private static void UInt32_To_BE(uint n, Span<byte> bs, int off, int max) + { + int num = System.Math.Min(4, max); + while (--num >= 0) + { + int shift = 8 * (3 - num); + bs[off + num] = (byte)(n >> shift); + } + } +#endif + + public override IMemoable Copy() { return new Sha512tDigest(this); } diff --git a/crypto/src/crypto/digests/ShakeDigest.cs b/crypto/src/crypto/digests/ShakeDigest.cs index 8d7a7d6e3..17d262261 100644 --- a/crypto/src/crypto/digests/ShakeDigest.cs +++ b/crypto/src/crypto/digests/ShakeDigest.cs @@ -77,6 +77,34 @@ namespace Org.BouncyCastle.Crypto.Digests return outLen; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int DoFinal(Span<byte> output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public virtual int OutputFinal(Span<byte> output) + { + int length = Output(output); + + Reset(); + + return length; + } + + public virtual int Output(Span<byte> output) + { + if (!squeezing) + { + AbsorbBits(0x0F, 4); + } + + Squeeze(output); + + return output.Length; + } +#endif + /* * TODO Possible API change to support partial-byte suffixes. */ diff --git a/crypto/src/crypto/digests/ShortenedDigest.cs b/crypto/src/crypto/digests/ShortenedDigest.cs index 9e4d99e7b..9e9998560 100644 --- a/crypto/src/crypto/digests/ShortenedDigest.cs +++ b/crypto/src/crypto/digests/ShortenedDigest.cs @@ -1,5 +1,4 @@ using System; -using Org.BouncyCastle.Crypto; namespace Org.BouncyCastle.Crypto.Digests { @@ -58,7 +57,14 @@ namespace Org.BouncyCastle.Crypto.Digests baseDigest.BlockUpdate(input, inOff, length); } - public int DoFinal(byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + baseDigest.BlockUpdate(input); + } +#endif + + public int DoFinal(byte[] output, int outOff) { byte[] tmp = new byte[baseDigest.GetDigestSize()]; @@ -69,7 +75,20 @@ namespace Org.BouncyCastle.Crypto.Digests return length; } - public void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Span<byte> tmp = stackalloc byte[baseDigest.GetDigestSize()]; + + baseDigest.DoFinal(tmp); + + tmp[..length].CopyTo(output); + + return length; + } +#endif + + public void Reset() { baseDigest.Reset(); } diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs index 394f0acd5..d56c0e788 100644 --- a/crypto/src/crypto/digests/SkeinDigest.cs +++ b/crypto/src/crypto/digests/SkeinDigest.cs @@ -5,7 +5,6 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { - /// <summary> /// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, /// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher. @@ -103,7 +102,7 @@ namespace Org.BouncyCastle.Crypto.Digests public void BlockUpdate(byte[] inBytes, int inOff, int len) { - engine.Update(inBytes, inOff, len); + engine.BlockUpdate(inBytes, inOff, len); } public int DoFinal(byte[] outBytes, int outOff) @@ -111,5 +110,16 @@ namespace Org.BouncyCastle.Crypto.Digests return engine.DoFinal(outBytes, outOff); } - } -} \ No newline at end of file +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + engine.BlockUpdate(input); + } + + public int DoFinal(Span<byte> output) + { + return engine.DoFinal(output); + } +#endif + } +} diff --git a/crypto/src/crypto/digests/SkeinEngine.cs b/crypto/src/crypto/digests/SkeinEngine.cs index a36ac8fe7..2535f786a 100644 --- a/crypto/src/crypto/digests/SkeinEngine.cs +++ b/crypto/src/crypto/digests/SkeinEngine.cs @@ -431,7 +431,7 @@ namespace Org.BouncyCastle.Crypto.Digests currentOffset = 0; } - int toCopy = System.Math.Min((len - copied), currentBlock.Length - currentOffset); + int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset); Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy); copied += toCopy; currentOffset += toCopy; @@ -439,6 +439,32 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Update(ReadOnlySpan<byte> input, ulong[] output) + { + /* + * Buffer complete blocks for the underlying Threefish cipher, only flushing when there + * are subsequent bytes (last block must be processed in doFinal() with final=true set). + */ + int copied = 0, len = input.Length; + while (len > copied) + { + if (currentOffset == currentBlock.Length) + { + ProcessBlock(output); + tweak.First = false; + currentOffset = 0; + } + + int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset); + input.Slice(copied, toCopy).CopyTo(currentBlock.AsSpan(currentOffset)); + copied += toCopy; + currentOffset += toCopy; + tweak.AdvancePosition(toCopy); + } + } +#endif + private void ProcessBlock(ulong[] output) { engine.threefish.Init(true, engine.chain, tweak.GetWords()); @@ -726,15 +752,23 @@ namespace Org.BouncyCastle.Crypto.Digests public void Update(byte inByte) { singleByte[0] = inByte; - Update(singleByte, 0, 1); + BlockUpdate(singleByte, 0, 1); } - public void Update(byte[] inBytes, int inOff, int len) + public void BlockUpdate(byte[] inBytes, int inOff, int len) { CheckInitialised(); ubi.Update(inBytes, inOff, len, chain); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + CheckInitialised(); + ubi.Update(input, chain); + } +#endif + public int DoFinal(byte[] outBytes, int outOff) { CheckInitialised(); @@ -770,6 +804,42 @@ namespace Org.BouncyCastle.Crypto.Digests return outputSizeBytes; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + CheckInitialised(); + if (output.Length < outputSizeBytes) + throw new DataLengthException("Output span is too short to hold output"); + + // Finalise message block + UbiFinal(); + + // Process additional post-message parameters + if (postMessageParameters != null) + { + for (int i = 0; i < postMessageParameters.Length; i++) + { + Parameter param = postMessageParameters[i]; + UbiComplete(param.Type, param.Value); + } + } + + // Perform the output transform + int blockSize = BlockSize; + int blocksRequired = (outputSizeBytes + blockSize - 1) / blockSize; + for (int i = 0; i < blocksRequired; i++) + { + int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize)); + //Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite); + Output((ulong)i, output[(i * blockSize)..], toWrite); + } + + Reset(); + + return outputSizeBytes; + } +#endif + private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes) { byte[] currentBytes = new byte[8]; @@ -796,5 +866,34 @@ namespace Org.BouncyCastle.Crypto.Digests } } } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Output(ulong outputSequence, Span<byte> output, int outputBytes) + { + Span<byte> currentBytes = stackalloc byte[8]; + Pack.UInt64_To_LE(outputSequence, currentBytes); + + // Output is a sequence of UBI invocations all of which use and preserve the pre-output state + ulong[] outputWords = new ulong[chain.Length]; + UbiInit(PARAM_TYPE_OUTPUT); + this.ubi.Update(currentBytes, outputWords); + ubi.DoFinal(outputWords); + + int wordsRequired = (outputBytes + 8 - 1) / 8; + for (int i = 0; i < wordsRequired; i++) + { + int toWrite = System.Math.Min(8, outputBytes - (i * 8)); + if (toWrite == 8) + { + Pack.UInt64_To_LE(outputWords[i], output[(i * 8)..]); + } + else + { + Pack.UInt64_To_LE(outputWords[i], currentBytes); + currentBytes[..toWrite].CopyTo(output[(i * 8)..]); + } + } + } +#endif } } diff --git a/crypto/src/crypto/digests/TigerDigest.cs b/crypto/src/crypto/digests/TigerDigest.cs index a452d3f0b..d83e905db 100644 --- a/crypto/src/crypto/digests/TigerDigest.cs +++ b/crypto/src/crypto/digests/TigerDigest.cs @@ -603,6 +603,20 @@ namespace Org.BouncyCastle.Crypto.Digests bOff = 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void ProcessWord(ReadOnlySpan<byte> b) + { + x[xOff++] = (long)Pack.LE_To_UInt64(b); + + if (xOff == x.Length) + { + ProcessBlock(); + } + + bOff = 0; + } +#endif + public void Update( byte input) { @@ -656,6 +670,47 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int inOff = 0, length = input.Length; + + // + // fill the current word + // + while ((bOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length >= 8) + { + ProcessWord(input[inOff..]); + + inOff += 8; + length -= 8; + byteCount += 8; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } +#endif + private void RoundABC( long x, long mul) @@ -809,6 +864,21 @@ namespace Org.BouncyCastle.Crypto.Digests return DigestLength; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Finish(); + + Pack.UInt64_To_LE((ulong)a, output); + Pack.UInt64_To_LE((ulong)b, output[8..]); + Pack.UInt64_To_LE((ulong)c, output[16..]); + + Reset(); + + return DigestLength; + } +#endif + /** * reset the chaining variables */ diff --git a/crypto/src/crypto/digests/TupleHash.cs b/crypto/src/crypto/digests/TupleHash.cs index 98c2d2acf..43030d5d5 100644 --- a/crypto/src/crypto/digests/TupleHash.cs +++ b/crypto/src/crypto/digests/TupleHash.cs @@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Crypto.Digests cshake.BlockUpdate(bytes, 0, bytes.Length); } - private void wrapUp(int outputSize) + private void WrapUp(int outputSize) { byte[] encOut = XofUtilities.RightEncode(outputSize * 8); @@ -89,23 +89,14 @@ namespace Org.BouncyCastle.Crypto.Digests public virtual int DoFinal(byte[] outBuf, int outOff) { - if (firstOutput) - { - wrapUp(GetDigestSize()); - } - - int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize()); - - Reset(); - - return rv; + return DoFinal(outBuf, outOff, GetDigestSize()); } public virtual int DoFinal(byte[] outBuf, int outOff, int outLen) { if (firstOutput) { - wrapUp(GetDigestSize()); + WrapUp(GetDigestSize()); } int rv = cshake.DoFinal(outBuf, outOff, outLen); @@ -119,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Digests { if (firstOutput) { - wrapUp(0); + WrapUp(0); } return cshake.DoOutput(outBuf, outOff, outLen); @@ -130,5 +121,41 @@ namespace Org.BouncyCastle.Crypto.Digests cshake.Reset(); firstOutput = true; } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + XofUtilities.EncodeTo(cshake, input); + } + + public virtual int DoFinal(Span<byte> output) + { + return OutputFinal(output[..GetDigestSize()]); + } + + public virtual int OutputFinal(Span<byte> output) + { + if (firstOutput) + { + WrapUp(GetDigestSize()); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } + + public virtual int Output(Span<byte> output) + { + if (firstOutput) + { + WrapUp(0); + } + + return cshake.Output(output); + } +#endif } } diff --git a/crypto/src/crypto/digests/WhirlpoolDigest.cs b/crypto/src/crypto/digests/WhirlpoolDigest.cs index b28e259f3..73d389a3c 100644 --- a/crypto/src/crypto/digests/WhirlpoolDigest.cs +++ b/crypto/src/crypto/digests/WhirlpoolDigest.cs @@ -162,6 +162,20 @@ namespace Org.BouncyCastle.Crypto.Digests return GetDigestSize(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + // sets output[0..DIGEST_LENGTH_BYTES] + Finish(); + + Pack.UInt64_To_BE(_hash, output); + + Reset(); + + return GetDigestSize(); + } +#endif + /** * Reset the chaining variables */ @@ -276,6 +290,16 @@ namespace Org.BouncyCastle.Crypto.Digests } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + for (int i = 0; i < input.Length; ++i) + { + Update(input[i]); + } + } +#endif + private void Finish() { /* diff --git a/crypto/src/crypto/digests/XofUtils.cs b/crypto/src/crypto/digests/XofUtils.cs index a4d6622b3..a1242f987 100644 --- a/crypto/src/crypto/digests/XofUtils.cs +++ b/crypto/src/crypto/digests/XofUtils.cs @@ -28,6 +28,26 @@ namespace Org.BouncyCastle.Crypto.Digests return b; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static int LeftEncode(long length, Span<byte> lengthEncoding) + { + byte n = 1; + + long v = length; + while ((v >>= 8) != 0) + { + n++; + } + + lengthEncoding[0] = n; + for (int i = 1; i <= n; i++) + { + lengthEncoding[i] = (byte)(length >> (8 * (n - i))); + } + return 1 + n; + } +#endif + internal static byte[] RightEncode(long strLen) { byte n = 1; @@ -50,6 +70,26 @@ namespace Org.BouncyCastle.Crypto.Digests return b; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static int RightEncode(long length, Span<byte> lengthEncoding) + { + byte n = 1; + + long v = length; + while ((v >>= 8) != 0) + { + n++; + } + + lengthEncoding[n] = n; + for (int i = 0; i < n; i++) + { + lengthEncoding[i] = (byte)(length >> (8 * (n - i - 1))); + } + return n + 1; + } +#endif + internal static byte[] Encode(byte X) { return Arrays.Concatenate(LeftEncode(8), new byte[] { X }); @@ -63,5 +103,15 @@ namespace Org.BouncyCastle.Crypto.Digests } return Arrays.Concatenate(LeftEncode(len * 8), Arrays.CopyOfRange(inBuf, inOff, inOff + len)); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void EncodeTo(IDigest digest, ReadOnlySpan<byte> buf) + { + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = LeftEncode(buf.Length * 8, lengthEncoding); + digest.BlockUpdate(lengthEncoding[..count]); + digest.BlockUpdate(buf); + } +#endif } } diff --git a/crypto/src/crypto/engines/AesEngine.cs b/crypto/src/crypto/engines/AesEngine.cs index 10c720968..21daf06d8 100644 --- a/crypto/src/crypto/engines/AesEngine.cs +++ b/crypto/src/crypto/engines/AesEngine.cs @@ -486,6 +486,16 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, 16, "input buffer too short"); Check.OutputLength(output, outOff, 16, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (forEncryption) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey); + } +#else if (forEncryption) { EncryptBlock(input, inOff, output, outOff, WorkingKey); @@ -494,14 +504,134 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff, WorkingKey); } +#endif return BLOCK_SIZE; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (WorkingKey == null) + throw new InvalidOperationException("AES engine not initialised"); + + Check.DataLength(input, 16, "input buffer too short"); + Check.OutputLength(output, 16, "output buffer too short"); + + if (forEncryption) + { + EncryptBlock(input, output, WorkingKey); + } + else + { + DecryptBlock(input, output, WorkingKey); + } + + return BLOCK_SIZE; + } +#endif + public virtual void Reset() { } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW) + { + uint C0 = Pack.LE_To_UInt32(input); + uint C1 = Pack.LE_To_UInt32(input[4..]); + uint C2 = Pack.LE_To_UInt32(input[8..]); + uint C3 = Pack.LE_To_UInt32(input[12..]); + + uint[] kw = KW[0]; + uint t0 = C0 ^ kw[0]; + uint t1 = C1 ^ kw[1]; + uint t2 = C2 ^ kw[2]; + + uint r0, r1, r2, r3 = C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1]; + r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3]; + kw = KW[r++]; + t0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + t1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1]; + t2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3]; + } + + kw = KW[r++]; + r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1]; + r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3]; + + // the final round's table is a simple function of S so we don't use a whole other four tables for it + + kw = KW[r]; + C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[0]; + C1 = (uint)s[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[1]; + C2 = (uint)s[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + C3 = (uint)s[r3 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + + Pack.UInt32_To_LE(C0, output); + Pack.UInt32_To_LE(C1, output[4..]); + Pack.UInt32_To_LE(C2, output[8..]); + Pack.UInt32_To_LE(C3, output[12..]); + } + + private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW) + { + uint C0 = Pack.LE_To_UInt32(input); + uint C1 = Pack.LE_To_UInt32(input[4..]); + uint C2 = Pack.LE_To_UInt32(input[8..]); + uint C3 = Pack.LE_To_UInt32(input[12..]); + + uint[] kw = KW[ROUNDS]; + uint t0 = C0 ^ kw[0]; + uint t1 = C1 ^ kw[1]; + uint t2 = C2 ^ kw[2]; + + uint r0, r1, r2, r3 = C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3]; + kw = KW[r--]; + t0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0]; + t1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1]; + t2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3]; + } + + kw = KW[1]; + r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3]; + + // the final round's table is a simple function of Si so we don't use a whole other four tables for it + + kw = KW[0]; + C0 = (uint)Si[r0 & 255] ^ (((uint)s[(r3 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; + C1 = (uint)s[r1 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r2 >> 24) & 255]) << 24) ^ kw[1]; + C2 = (uint)s[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[2]; + C3 = (uint)Si[r3 & 255] ^ (((uint)s[(r2 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[3]; + + Pack.UInt32_To_LE(C0, output); + Pack.UInt32_To_LE(C1, output[4..]); + Pack.UInt32_To_LE(C2, output[8..]); + Pack.UInt32_To_LE(C3, output[12..]); + } +#else private void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW) { uint C0 = Pack.LE_To_UInt32(input, inOff + 0); @@ -597,5 +727,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_LE(C2, output, outOff + 8); Pack.UInt32_To_LE(C3, output, outOff + 12); } +#endif } } diff --git a/crypto/src/crypto/engines/AesLightEngine.cs b/crypto/src/crypto/engines/AesLightEngine.cs index 8d5a98a9f..f34901fac 100644 --- a/crypto/src/crypto/engines/AesLightEngine.cs +++ b/crypto/src/crypto/engines/AesLightEngine.cs @@ -380,6 +380,16 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, 16, "input buffer too short"); Check.OutputLength(output, outOff, 16, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (forEncryption) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey); + } +#else if (forEncryption) { EncryptBlock(input, inOff, output, outOff, WorkingKey); @@ -388,14 +398,134 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff, WorkingKey); } +#endif return BLOCK_SIZE; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (WorkingKey == null) + throw new InvalidOperationException("AES engine not initialised"); + + Check.DataLength(input, 16, "input buffer too short"); + Check.OutputLength(output, 16, "output buffer too short"); + + if (forEncryption) + { + EncryptBlock(input, output, WorkingKey); + } + else + { + DecryptBlock(input, output, WorkingKey); + } + + return BLOCK_SIZE; + } +#endif + public virtual void Reset() { } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW) + { + uint C0 = Pack.LE_To_UInt32(input); + uint C1 = Pack.LE_To_UInt32(input[4..]); + uint C2 = Pack.LE_To_UInt32(input[8..]); + uint C3 = Pack.LE_To_UInt32(input[12..]); + + uint[] kw = KW[0]; + uint t0 = C0 ^ kw[0]; + uint t1 = C1 ^ kw[1]; + uint t2 = C2 ^ kw[2]; + + uint r0, r1, r2, r3 = C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1]; + r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3]; + kw = KW[r++]; + t0 = Mcol((uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + t1 = Mcol((uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24)) ^ kw[1]; + t2 = Mcol((uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24)) ^ kw[3]; + } + + kw = KW[r++]; + r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1]; + r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3]; + + // the final round is a simple function of S + + kw = KW[r]; + C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; + C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; + C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + + Pack.UInt32_To_LE(C0, output); + Pack.UInt32_To_LE(C1, output[4..]); + Pack.UInt32_To_LE(C2, output[8..]); + Pack.UInt32_To_LE(C3, output[12..]); + } + + private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW) + { + uint C0 = Pack.LE_To_UInt32(input); + uint C1 = Pack.LE_To_UInt32(input[4..]); + uint C2 = Pack.LE_To_UInt32(input[8..]); + uint C3 = Pack.LE_To_UInt32(input[12..]); + + uint[] kw = KW[ROUNDS]; + uint t0 = C0 ^ kw[0]; + uint t1 = C1 ^ kw[1]; + uint t2 = C2 ^ kw[2]; + + uint r0, r1, r2, r3 = C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0]; + r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1]; + r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3]; + kw = KW[r--]; + t0 = Inv_Mcol((uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ ((uint)Si[(r1 >> 24) & 255] << 24)) ^ kw[0]; + t1 = Inv_Mcol((uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(r2 >> 24) & 255] << 24)) ^ kw[1]; + t2 = Inv_Mcol((uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ ((uint)Si[(r0 >> 24) & 255] << 24)) ^ kw[3]; + } + + kw = KW[1]; + r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0]; + r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1]; + r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3]; + + // the final round's table is a simple function of Si + + kw = KW[0]; + C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0]; + C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; + C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; + C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; + + Pack.UInt32_To_LE(C0, output); + Pack.UInt32_To_LE(C1, output[4..]); + Pack.UInt32_To_LE(C2, output[8..]); + Pack.UInt32_To_LE(C3, output[12..]); + } +#else private void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW) { uint C0 = Pack.LE_To_UInt32(input, inOff + 0); @@ -491,5 +621,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_LE(C2, output, outOff + 8); Pack.UInt32_To_LE(C3, output, outOff + 12); } +#endif } } diff --git a/crypto/src/crypto/engines/AriaEngine.cs b/crypto/src/crypto/engines/AriaEngine.cs index 2f94dc048..75af84320 100644 --- a/crypto/src/crypto/engines/AriaEngine.cs +++ b/crypto/src/crypto/engines/AriaEngine.cs @@ -195,6 +195,36 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (m_roundKeys == null) + throw new InvalidOperationException("ARIA engine not initialised"); + + Check.DataLength(input, BlockSize, "input buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); + + byte[] z = new byte[BlockSize]; + input[..BlockSize].CopyTo(z); + + int i = 0, rounds = m_roundKeys.Length - 3; + while (i < rounds) + { + FO(z, m_roundKeys[i++]); + FE(z, m_roundKeys[i++]); + } + + FO(z, m_roundKeys[i++]); + Xor(z, m_roundKeys[i++]); + SL2(z); + Xor(z, m_roundKeys[i]); + + z.CopyTo(output); + + return BlockSize; + } +#endif + public virtual void Reset() { // Empty diff --git a/crypto/src/crypto/engines/BlowfishEngine.cs b/crypto/src/crypto/engines/BlowfishEngine.cs index 1b3dd9743..aa323581a 100644 --- a/crypto/src/crypto/engines/BlowfishEngine.cs +++ b/crypto/src/crypto/engines/BlowfishEngine.cs @@ -347,11 +347,7 @@ namespace Org.BouncyCastle.Crypto.Engines get { return false; } } - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("Blowfish not initialised"); @@ -359,7 +355,17 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); - if (encrypting) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (encrypting) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else + if (encrypting) { EncryptBlock(input, inOff, output, outOff); } @@ -367,11 +373,34 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff); } +#endif return BLOCK_SIZE; } - public void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey == null) + throw new InvalidOperationException("Blowfish not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, output); + } + else + { + DecryptBlock(input, output); + } + + return BLOCK_SIZE; + } +#endif + + public void Reset() { } @@ -499,16 +528,46 @@ namespace Org.BouncyCastle.Crypto.Engines ProcessTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3); } - /** - * Encrypt the given input starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * The input will be an exact multiple of our blocksize. - */ - private void EncryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + uint xl = Pack.BE_To_UInt32(input); + uint xr = Pack.BE_To_UInt32(input[4..]); + + xl ^= P[0]; + + for (int i = 1; i < ROUNDS; i += 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i + 1]; + } + + xr ^= P[ROUNDS + 1]; + + Pack.UInt32_To_BE(xr, output); + Pack.UInt32_To_BE(xl, output[4..]); + } + + private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + uint xl = Pack.BE_To_UInt32(input); + uint xr = Pack.BE_To_UInt32(input[4..]); + + xl ^= P[ROUNDS + 1]; + + for (int i = ROUNDS; i > 0; i -= 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i - 1]; + } + + xr ^= P[0]; + + Pack.UInt32_To_BE(xr, output); + Pack.UInt32_To_BE(xl, output[4..]); + } +#else + private void EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { uint xl = Pack.BE_To_UInt32(src, srcIndex); uint xr = Pack.BE_To_UInt32(src, srcIndex+4); @@ -527,16 +586,7 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_BE(xl, dst, dstIndex + 4); } - /** - * Decrypt the given input starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * The input will be an exact multiple of our blocksize. - */ - private void DecryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) + private void DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { uint xl = Pack.BE_To_UInt32(src, srcIndex); uint xr = Pack.BE_To_UInt32(src, srcIndex + 4); @@ -554,5 +604,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_BE(xr, dst, dstIndex); Pack.UInt32_To_BE(xl, dst, dstIndex + 4); } - } +#endif + } } diff --git a/crypto/src/crypto/engines/CamelliaEngine.cs b/crypto/src/crypto/engines/CamelliaEngine.cs index 2222e4b7c..512448a27 100644 --- a/crypto/src/crypto/engines/CamelliaEngine.cs +++ b/crypto/src/crypto/engines/CamelliaEngine.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Engines { @@ -275,25 +276,6 @@ namespace Org.BouncyCastle.Crypto.Engines ki[3 + ioff] = ko[1 + ooff]; } - private static uint bytes2uint(byte[] src, int offset) - { - uint word = 0; - for (int i = 0; i < 4; i++) - { - word = (word << 8) + (uint)src[i + offset]; - } - return word; - } - - private static void uint2bytes(uint word, byte[] dst, int offset) - { - for (int i = 0; i < 4; i++) - { - dst[(3 - i) + offset] = (byte)word; - word >>= 8; - } - } - private static void camelliaF2(uint[] s, uint[] skey, int keyoff) { uint t1, t2, u, v; @@ -346,38 +328,23 @@ namespace Org.BouncyCastle.Crypto.Engines switch (key.Length) { - case 16: - _keyIs128 = true; - k[0] = bytes2uint(key, 0); - k[1] = bytes2uint(key, 4); - k[2] = bytes2uint(key, 8); - k[3] = bytes2uint(key, 12); - k[4] = k[5] = k[6] = k[7] = 0; - break; - case 24: - k[0] = bytes2uint(key, 0); - k[1] = bytes2uint(key, 4); - k[2] = bytes2uint(key, 8); - k[3] = bytes2uint(key, 12); - k[4] = bytes2uint(key, 16); - k[5] = bytes2uint(key, 20); - k[6] = ~k[4]; - k[7] = ~k[5]; - _keyIs128 = false; - break; - case 32: - k[0] = bytes2uint(key, 0); - k[1] = bytes2uint(key, 4); - k[2] = bytes2uint(key, 8); - k[3] = bytes2uint(key, 12); - k[4] = bytes2uint(key, 16); - k[5] = bytes2uint(key, 20); - k[6] = bytes2uint(key, 24); - k[7] = bytes2uint(key, 28); - _keyIs128 = false; - break; - default: - throw new ArgumentException("key sizes are only 16/24/32 bytes."); + case 16: + _keyIs128 = true; + Pack.BE_To_UInt32(key, 0, k, 0, 4); + k[4] = k[5] = k[6] = k[7] = 0; + break; + case 24: + Pack.BE_To_UInt32(key, 0, k, 0, 6); + k[6] = ~k[4]; + k[7] = ~k[5]; + _keyIs128 = false; + break; + case 32: + Pack.BE_To_UInt32(key, 0, k, 0, 8); + _keyIs128 = false; + break; + default: + throw new ArgumentException("key sizes are only 16/24/32 bytes."); } for (int i = 0; i < 4; i++) @@ -537,13 +504,78 @@ namespace Org.BouncyCastle.Crypto.Engines } } - private int processBlock128(byte[] input, int inOff, byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int ProcessBlock128(ReadOnlySpan<byte> input, Span<byte> output) + { + uint[] state = new uint[4]; + Pack.BE_To_UInt32(input, state); + + state[0] ^= kw[0]; + state[1] ^= kw[1]; + state[2] ^= kw[2]; + state[3] ^= kw[3]; + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + + Pack.UInt32_To_BE(state[2] ^ kw[4], output); + Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]); + Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]); + Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]); + + return BLOCK_SIZE; + } + + private int ProcessBlock192or256(ReadOnlySpan<byte> input, Span<byte> output) + { + uint[] state = new uint[4]; + Pack.BE_To_UInt32(input, state); + + state[0] ^= kw[0]; + state[1] ^= kw[1]; + state[2] ^= kw[2]; + state[3] ^= kw[3]; + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + camelliaFLs(state, ke, 8); + camelliaF2(state, subkey, 36); + camelliaF2(state, subkey, 40); + camelliaF2(state, subkey, 44); + + Pack.UInt32_To_BE(state[2] ^ kw[4], output); + Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]); + Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]); + Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]); + + return BLOCK_SIZE; + } +#else + private int ProcessBlock128(byte[] input, int inOff, byte[] output, int outOff) { uint[] state = new uint[4]; for (int i = 0; i < 4; i++) { - state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i]; + state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i]; } camelliaF2(state, subkey, 0); @@ -558,26 +590,21 @@ namespace Org.BouncyCastle.Crypto.Engines camelliaF2(state, subkey, 28); camelliaF2(state, subkey, 32); - state[2] ^= kw[4]; - state[3] ^= kw[5]; - state[0] ^= kw[6]; - state[1] ^= kw[7]; - - uint2bytes(state[2], output, outOff); - uint2bytes(state[3], output, outOff + 4); - uint2bytes(state[0], output, outOff + 8); - uint2bytes(state[1], output, outOff + 12); + Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff); + Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4); + Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8); + Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12); return BLOCK_SIZE; } - private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff) + private int ProcessBlock192or256(byte[] input, int inOff, byte[] output, int outOff) { uint[] state = new uint[4]; for (int i = 0; i < 4; i++) { - state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i]; + state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i]; } camelliaF2(state, subkey, 0); @@ -596,18 +623,14 @@ namespace Org.BouncyCastle.Crypto.Engines camelliaF2(state, subkey, 40); camelliaF2(state, subkey, 44); - state[2] ^= kw[4]; - state[3] ^= kw[5]; - state[0] ^= kw[6]; - state[1] ^= kw[7]; - - uint2bytes(state[2], output, outOff); - uint2bytes(state[3], output, outOff + 4); - uint2bytes(state[0], output, outOff + 8); - uint2bytes(state[1], output, outOff + 12); + Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff); + Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4); + Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8); + Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12); return BLOCK_SIZE; } +#endif public CamelliaEngine() { @@ -640,11 +663,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (!initialised) throw new InvalidOperationException("Camellia engine not initialised"); @@ -652,17 +671,48 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); - if (_keyIs128) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (_keyIs128) + { + return ProcessBlock128(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + return ProcessBlock192or256(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else + if (_keyIs128) + { + return ProcessBlock128(input, inOff, output, outOff); + } + else + { + return ProcessBlock192or256(input, inOff, output, outOff); + } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!initialised) + throw new InvalidOperationException("Camellia engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + if (_keyIs128) { - return processBlock128(input, inOff, output, outOff); + return ProcessBlock128(input, output); } else { - return processBlock192or256(input, inOff, output, outOff); + return ProcessBlock192or256(input, output); } } +#endif - public virtual void Reset() + public virtual void Reset() { // nothing } diff --git a/crypto/src/crypto/engines/CamelliaLightEngine.cs b/crypto/src/crypto/engines/CamelliaLightEngine.cs index daf0316e2..03611f137 100644 --- a/crypto/src/crypto/engines/CamelliaLightEngine.cs +++ b/crypto/src/crypto/engines/CamelliaLightEngine.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Engines { @@ -158,25 +159,6 @@ namespace Org.BouncyCastle.Crypto.Engines ki[3 + ioff] = ko[1 + ooff]; } - private static uint bytes2uint(byte[] src, int offset) - { - uint word = 0; - for (int i = 0; i < 4; i++) - { - word = (word << 8) + (uint)src[i + offset]; - } - return word; - } - - private static void uint2bytes(uint word, byte[] dst, int offset) - { - for (int i = 0; i < 4; i++) - { - dst[(3 - i) + offset] = (byte)word; - word >>= 8; - } - } - private byte lRot8(byte v, int rot) { return (byte)(((uint)v << rot) | ((uint)v >> (8 - rot))); @@ -258,38 +240,23 @@ namespace Org.BouncyCastle.Crypto.Engines switch (key.Length) { - case 16: - _keyis128 = true; - k[0] = bytes2uint(key, 0); - k[1] = bytes2uint(key, 4); - k[2] = bytes2uint(key, 8); - k[3] = bytes2uint(key, 12); - k[4] = k[5] = k[6] = k[7] = 0; - break; - case 24: - k[0] = bytes2uint(key, 0); - k[1] = bytes2uint(key, 4); - k[2] = bytes2uint(key, 8); - k[3] = bytes2uint(key, 12); - k[4] = bytes2uint(key, 16); - k[5] = bytes2uint(key, 20); - k[6] = ~k[4]; - k[7] = ~k[5]; - _keyis128 = false; - break; - case 32: - k[0] = bytes2uint(key, 0); - k[1] = bytes2uint(key, 4); - k[2] = bytes2uint(key, 8); - k[3] = bytes2uint(key, 12); - k[4] = bytes2uint(key, 16); - k[5] = bytes2uint(key, 20); - k[6] = bytes2uint(key, 24); - k[7] = bytes2uint(key, 28); - _keyis128 = false; - break; - default: - throw new ArgumentException("key sizes are only 16/24/32 bytes."); + case 16: + _keyis128 = true; + Pack.BE_To_UInt32(key, 0, k, 0, 4); + k[4] = k[5] = k[6] = k[7] = 0; + break; + case 24: + Pack.BE_To_UInt32(key, 0, k, 0, 6); + k[6] = ~k[4]; + k[7] = ~k[5]; + _keyis128 = false; + break; + case 32: + Pack.BE_To_UInt32(key, 0, k, 0, 8); + _keyis128 = false; + break; + default: + throw new ArgumentException("key sizes are only 16/24/32 bytes."); } for (int i = 0; i < 4; i++) @@ -449,13 +416,78 @@ namespace Org.BouncyCastle.Crypto.Engines } } - private int processBlock128(byte[] input, int inOff, byte[] output, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int ProcessBlock128(ReadOnlySpan<byte> input, Span<byte> output) + { + uint[] state = new uint[4]; + Pack.BE_To_UInt32(input, state); + + state[0] ^= kw[0]; + state[1] ^= kw[1]; + state[2] ^= kw[2]; + state[3] ^= kw[3]; + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + + Pack.UInt32_To_BE(state[2] ^ kw[4], output); + Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]); + Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]); + Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]); + + return BLOCK_SIZE; + } + + private int ProcessBlock192or256(ReadOnlySpan<byte> input, Span<byte> output) + { + uint[] state = new uint[4]; + Pack.BE_To_UInt32(input, state); + + state[0] ^= kw[0]; + state[1] ^= kw[1]; + state[2] ^= kw[2]; + state[3] ^= kw[3]; + + camelliaF2(state, subkey, 0); + camelliaF2(state, subkey, 4); + camelliaF2(state, subkey, 8); + camelliaFLs(state, ke, 0); + camelliaF2(state, subkey, 12); + camelliaF2(state, subkey, 16); + camelliaF2(state, subkey, 20); + camelliaFLs(state, ke, 4); + camelliaF2(state, subkey, 24); + camelliaF2(state, subkey, 28); + camelliaF2(state, subkey, 32); + camelliaFLs(state, ke, 8); + camelliaF2(state, subkey, 36); + camelliaF2(state, subkey, 40); + camelliaF2(state, subkey, 44); + + Pack.UInt32_To_BE(state[2] ^ kw[4], output); + Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]); + Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]); + Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]); + + return BLOCK_SIZE; + } +#else + private int ProcessBlock128(byte[] input, int inOff, byte[] output, int outOff) { uint[] state = new uint[4]; for (int i = 0; i < 4; i++) { - state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i]; + state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i]; } camelliaF2(state, subkey, 0); @@ -470,26 +502,21 @@ namespace Org.BouncyCastle.Crypto.Engines camelliaF2(state, subkey, 28); camelliaF2(state, subkey, 32); - state[2] ^= kw[4]; - state[3] ^= kw[5]; - state[0] ^= kw[6]; - state[1] ^= kw[7]; - - uint2bytes(state[2], output, outOff); - uint2bytes(state[3], output, outOff + 4); - uint2bytes(state[0], output, outOff + 8); - uint2bytes(state[1], output, outOff + 12); + Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff); + Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4); + Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8); + Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12); return BLOCK_SIZE; } - private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff) + private int ProcessBlock192or256(byte[] input, int inOff, byte[] output, int outOff) { uint[] state = new uint[4]; for (int i = 0; i < 4; i++) { - state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i]; + state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i]; } camelliaF2(state, subkey, 0); @@ -508,18 +535,14 @@ namespace Org.BouncyCastle.Crypto.Engines camelliaF2(state, subkey, 40); camelliaF2(state, subkey, 44); - state[2] ^= kw[4]; - state[3] ^= kw[5]; - state[0] ^= kw[6]; - state[1] ^= kw[7]; - - uint2bytes(state[2], output, outOff); - uint2bytes(state[3], output, outOff + 4); - uint2bytes(state[0], output, outOff + 8); - uint2bytes(state[1], output, outOff + 12); + Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff); + Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4); + Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8); + Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12); return BLOCK_SIZE; } +#endif public CamelliaLightEngine() { @@ -553,11 +576,7 @@ namespace Org.BouncyCastle.Crypto.Engines initialised = true; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (!initialised) throw new InvalidOperationException("Camellia engine not initialised"); @@ -565,17 +584,48 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); - if (_keyis128) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (_keyis128) + { + return ProcessBlock128(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + return ProcessBlock192or256(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else + if (_keyis128) + { + return ProcessBlock128(input, inOff, output, outOff); + } + else + { + return ProcessBlock192or256(input, inOff, output, outOff); + } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!initialised) + throw new InvalidOperationException("Camellia engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + if (_keyis128) { - return processBlock128(input, inOff, output, outOff); + return ProcessBlock128(input, output); } else { - return processBlock192or256(input, inOff, output, outOff); + return ProcessBlock192or256(input, output); } } +#endif - public virtual void Reset() + public virtual void Reset() { } } diff --git a/crypto/src/crypto/engines/Cast5Engine.cs b/crypto/src/crypto/engines/Cast5Engine.cs index 398f6d43a..93288a237 100644 --- a/crypto/src/crypto/engines/Cast5Engine.cs +++ b/crypto/src/crypto/engines/Cast5Engine.cs @@ -352,19 +352,25 @@ namespace Org.BouncyCastle.Crypto.Engines get { return false; } } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - int blockSize = GetBlockSize(); if (_workingKey == null) throw new InvalidOperationException(AlgorithmName + " not initialised"); + int blockSize = GetBlockSize(); Check.DataLength(input, inOff, blockSize, "input buffer too short"); Check.OutputLength(output, outOff, blockSize, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (_encrypting) + { + return EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + return DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else if (_encrypting) { return EncryptBlock(input, inOff, output, outOff); @@ -373,7 +379,29 @@ namespace Org.BouncyCastle.Crypto.Engines { return DecryptBlock(input, inOff, output, outOff); } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (_workingKey == null) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + int blockSize = GetBlockSize(); + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + if (_encrypting) + { + return EncryptBlock(input, output); + } + else + { + return DecryptBlock(input, output); + } } +#endif public virtual void Reset() { @@ -566,20 +594,45 @@ namespace Org.BouncyCastle.Crypto.Engines _Kr[16]=(int)((S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f); } - /** - * Encrypt the given input starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * - * @param src The plaintext buffer - * @param srcIndex An offset into src - * @param dst The ciphertext buffer - * @param dstIndex An offset into dst - */ - internal virtual int EncryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal virtual int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + + uint L0 = Pack.BE_To_UInt32(input); + uint R0 = Pack.BE_To_UInt32(input[4..]); + + uint[] result = new uint[2]; + CAST_Encipher(L0, R0, result); + + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], output); + Pack.UInt32_To_BE(result[1], output[4..]); + + return BLOCK_SIZE; + } + + internal virtual int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + uint L16 = Pack.BE_To_UInt32(input); + uint R16 = Pack.BE_To_UInt32(input[4..]); + + uint[] result = new uint[2]; + CAST_Decipher(L16, R16, result); + + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], output); + Pack.UInt32_To_BE(result[1], output[4..]); + + return BLOCK_SIZE; + } +#else + internal virtual int EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { // process the input block // batch the units up into a 32 bit chunk and go for it @@ -598,20 +651,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - /** - * Decrypt the given input starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * - * @param src The plaintext buffer - * @param srcIndex An offset into src - * @param dst The ciphertext buffer - * @param dstIndex An offset into dst - */ - internal virtual int DecryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) + internal virtual int DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { // process the input block // batch the units up into a 32 bit chunk and go for it @@ -628,6 +668,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } +#endif /** * The first of the three processing functions for the @@ -702,28 +743,28 @@ namespace Org.BouncyCastle.Crypto.Engines Li = Rp; switch (i) { - case 1: - case 4: - case 7: - case 10: - case 13: - case 16: - Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); - break; - case 2: - case 5: - case 8: - case 11: - case 14: - Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); - break; - case 3: - case 6: - case 9: - case 12: - case 15: - Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); - break; + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); + break; } } @@ -752,28 +793,28 @@ namespace Org.BouncyCastle.Crypto.Engines Li = Rp; switch (i) { - case 1: - case 4: - case 7: - case 10: - case 13: - case 16: - Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); - break; - case 2: - case 5: - case 8: - case 11: - case 14: - Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); - break; - case 3: - case 6: - case 9: - case 12: - case 15: - Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); - break; + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); + break; } } diff --git a/crypto/src/crypto/engines/Cast6Engine.cs b/crypto/src/crypto/engines/Cast6Engine.cs index c5c419b78..c3f379fcf 100644 --- a/crypto/src/crypto/engines/Cast6Engine.cs +++ b/crypto/src/crypto/engines/Cast6Engine.cs @@ -134,20 +134,44 @@ namespace Org.BouncyCastle.Crypto.Engines } } - /** - * Encrypt the given input starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * - * @param src The plaintext buffer - * @param srcIndex An offset into src - * @param dst The ciphertext buffer - * @param dstIndex An offset into dst - */ - internal override int EncryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal override int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // process the input block + // batch the units up into 4x32 bit chunks and go for it + uint A = Pack.BE_To_UInt32(input); + uint B = Pack.BE_To_UInt32(input[4..]); + uint C = Pack.BE_To_UInt32(input[8..]); + uint D = Pack.BE_To_UInt32(input[12..]); + uint[] result = new uint[4]; + CAST_Encipher(A, B, C, D, result); + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], output); + Pack.UInt32_To_BE(result[1], output[4..]); + Pack.UInt32_To_BE(result[2], output[8..]); + Pack.UInt32_To_BE(result[3], output[12..]); + return BLOCK_SIZE; + } + + internal override int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // process the input block + // batch the units up into 4x32 bit chunks and go for it + uint A = Pack.BE_To_UInt32(input); + uint B = Pack.BE_To_UInt32(input[4..]); + uint C = Pack.BE_To_UInt32(input[8..]); + uint D = Pack.BE_To_UInt32(input[12..]); + uint[] result = new uint[4]; + CAST_Decipher(A, B, C, D, result); + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], output); + Pack.UInt32_To_BE(result[1], output[4..]); + Pack.UInt32_To_BE(result[2], output[8..]); + Pack.UInt32_To_BE(result[3], output[12..]); + return BLOCK_SIZE; + } +#else + internal override int EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { // process the input block // batch the units up into 4x32 bit chunks and go for it @@ -165,20 +189,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - /** - * Decrypt the given input starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * - * @param src The plaintext buffer - * @param srcIndex An offset into src - * @param dst The ciphertext buffer - * @param dstIndex An offset into dst - */ - internal override int DecryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) + internal override int DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { // process the input block // batch the units up into 4x32 bit chunks and go for it @@ -195,8 +206,9 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_BE(result[3], dst, dstIndex + 12); return BLOCK_SIZE; } +#endif - /** + /** * Does the 12 quad rounds rounds to encrypt the block. * * @param A the 00-31 bits of the plaintext block diff --git a/crypto/src/crypto/engines/DesEdeEngine.cs b/crypto/src/crypto/engines/DesEdeEngine.cs index 2fac24ac0..ffb18d753 100644 --- a/crypto/src/crypto/engines/DesEdeEngine.cs +++ b/crypto/src/crypto/engines/DesEdeEngine.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines @@ -63,11 +64,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public override int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public override int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey1 == null) throw new InvalidOperationException("DESede engine not initialised"); @@ -75,23 +72,59 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); - byte[] temp = new byte[BLOCK_SIZE]; + uint hi32 = Pack.BE_To_UInt32(input, inOff); + uint lo32 = Pack.BE_To_UInt32(input, inOff + 4); if (forEncryption) { - DesFunc(workingKey1, input, inOff, temp, 0); - DesFunc(workingKey2, temp, 0, temp, 0); - DesFunc(workingKey3, temp, 0, output, outOff); + DesFunc(workingKey1, ref hi32, ref lo32); + DesFunc(workingKey2, ref hi32, ref lo32); + DesFunc(workingKey3, ref hi32, ref lo32); } else { - DesFunc(workingKey3, input, inOff, temp, 0); - DesFunc(workingKey2, temp, 0, temp, 0); - DesFunc(workingKey1, temp, 0, output, outOff); + DesFunc(workingKey3, ref hi32, ref lo32); + DesFunc(workingKey2, ref hi32, ref lo32); + DesFunc(workingKey1, ref hi32, ref lo32); } + Pack.UInt32_To_BE(hi32, output, outOff); + Pack.UInt32_To_BE(lo32, output, outOff + 4); + + return BLOCK_SIZE; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey1 == null) + throw new InvalidOperationException("DESede engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + uint hi32 = Pack.BE_To_UInt32(input); + uint lo32 = Pack.BE_To_UInt32(input[4..]); + + if (forEncryption) + { + DesFunc(workingKey1, ref hi32, ref lo32); + DesFunc(workingKey2, ref hi32, ref lo32); + DesFunc(workingKey3, ref hi32, ref lo32); + } + else + { + DesFunc(workingKey3, ref hi32, ref lo32); + DesFunc(workingKey2, ref hi32, ref lo32); + DesFunc(workingKey1, ref hi32, ref lo32); + } + + Pack.UInt32_To_BE(hi32, output); + Pack.UInt32_To_BE(lo32, output[4..]); + return BLOCK_SIZE; } +#endif public override void Reset() { diff --git a/crypto/src/crypto/engines/DesEngine.cs b/crypto/src/crypto/engines/DesEngine.cs index cfd50681e..25f559652 100644 --- a/crypto/src/crypto/engines/DesEngine.cs +++ b/crypto/src/crypto/engines/DesEngine.cs @@ -31,10 +31,10 @@ namespace Org.BouncyCastle.Crypto.Engines bool forEncryption, ICipherParameters parameters) { - if (!(parameters is KeyParameter)) + if (!(parameters is KeyParameter keyParameter)) throw new ArgumentException("invalid parameter passed to DES init - " + Platform.GetTypeName(parameters)); - workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parameters).GetKey()); + workingKey = GenerateWorkingKey(forEncryption, keyParameter.GetKey()); } public virtual string AlgorithmName @@ -52,11 +52,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("DES engine not initialised"); @@ -64,11 +60,38 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); - DesFunc(workingKey, input, inOff, output, outOff); + uint hi32 = Pack.BE_To_UInt32(input, inOff); + uint lo32 = Pack.BE_To_UInt32(input, inOff + 4); + + DesFunc(workingKey, ref hi32, ref lo32); + + Pack.UInt32_To_BE(hi32, output, outOff); + Pack.UInt32_To_BE(lo32, output, outOff + 4); return BLOCK_SIZE; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey == null) + throw new InvalidOperationException("DES engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + uint hi32 = Pack.BE_To_UInt32(input); + uint lo32 = Pack.BE_To_UInt32(input[4..]); + + DesFunc(workingKey, ref hi32, ref lo32); + + Pack.UInt32_To_BE(hi32, output); + Pack.UInt32_To_BE(lo32, output[4..]); + + return BLOCK_SIZE; + } +#endif + public virtual void Reset() { } @@ -388,19 +411,11 @@ namespace Org.BouncyCastle.Crypto.Engines return newKey; } - /** - * the DES engine. - */ - internal static void DesFunc( - int[] wKey, - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + internal static void DesFunc(int[] wKey, ref uint hi32, ref uint lo32) { - uint left = Pack.BE_To_UInt32(input, inOff); - uint right = Pack.BE_To_UInt32(input, inOff + 4); - uint work; + uint left = hi32; + uint right = lo32; + uint work; work = ((left >> 4) ^ right) & 0x0f0f0f0f; right ^= work; @@ -468,8 +483,8 @@ namespace Org.BouncyCastle.Crypto.Engines left ^= work; right ^= (work << 4); - Pack.UInt32_To_BE(right, outBytes, outOff); - Pack.UInt32_To_BE(left, outBytes, outOff + 4); + hi32 = right; + lo32 = left; } } } diff --git a/crypto/src/crypto/engines/Dstu7624Engine.cs b/crypto/src/crypto/engines/Dstu7624Engine.cs index 844a873a8..a0ff8ebd4 100644 --- a/crypto/src/crypto/engines/Dstu7624Engine.cs +++ b/crypto/src/crypto/engines/Dstu7624Engine.cs @@ -268,7 +268,11 @@ namespace Org.BouncyCastle.Crypto.Engines { case 2: { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + EncryptBlock_128(input.AsSpan(inOff), output.AsSpan(outOff)); +#else EncryptBlock_128(input, inOff, output, outOff); +#endif break; } default: @@ -299,7 +303,11 @@ namespace Org.BouncyCastle.Crypto.Engines { case 2: { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + DecryptBlock_128(input.AsSpan(inOff), output.AsSpan(outOff)); +#else DecryptBlock_128(input, inOff, output, outOff); +#endif break; } default: @@ -327,6 +335,82 @@ namespace Org.BouncyCastle.Crypto.Engines return GetBlockSize(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey == null) + throw new InvalidOperationException("Dstu7624Engine not initialised"); + + Check.DataLength(input, GetBlockSize(), "input buffer too short"); + Check.OutputLength(output, GetBlockSize(), "output buffer too short"); + + if (forEncryption) + { + /* Encrypt */ + switch (wordsInBlock) + { + case 2: + { + EncryptBlock_128(input, output); + break; + } + default: + { + Pack.LE_To_UInt64(input, internalState); + AddRoundKey(0); + for (int round = 0;;) + { + EncryptionRound(); + + if (++round == roundsAmount) + { + break; + } + + XorRoundKey(round); + } + AddRoundKey(roundsAmount); + Pack.UInt64_To_LE(internalState, output); + break; + } + } + } + else + { + /* Decrypt */ + switch (wordsInBlock) + { + case 2: + { + DecryptBlock_128(input, output); + break; + } + default: + { + Pack.LE_To_UInt64(input, internalState); + SubRoundKey(roundsAmount); + for (int round = roundsAmount;;) + { + DecryptionRound(); + + if (--round == 0) + { + break; + } + + XorRoundKey(round); + } + SubRoundKey(0); + Pack.UInt64_To_LE(internalState, output); + break; + } + } + } + + return GetBlockSize(); + } +#endif + private void EncryptionRound() { SubBytes(); @@ -341,6 +425,133 @@ namespace Org.BouncyCastle.Crypto.Engines InvSubBytes(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void DecryptBlock_128(ReadOnlySpan<byte> input, Span<byte> output) + { + ulong c0 = Pack.LE_To_UInt64(input); + ulong c1 = Pack.LE_To_UInt64(input[8..]); + + ulong[] roundKey = roundKeys[roundsAmount]; + c0 -= roundKey[0]; + c1 -= roundKey[1]; + + for (int round = roundsAmount; ;) + { + c0 = MixColumnInv(c0); + c1 = MixColumnInv(c1); + + uint lo0 = (uint)c0, hi0 = (uint)(c0 >> 32); + uint lo1 = (uint)c1, hi1 = (uint)(c1 >> 32); + + { + byte t0 = T0[lo0 & 0xFF]; + byte t1 = T1[(lo0 >> 8) & 0xFF]; + byte t2 = T2[(lo0 >> 16) & 0xFF]; + byte t3 = T3[lo0 >> 24]; + lo0 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = T0[hi1 & 0xFF]; + byte t5 = T1[(hi1 >> 8) & 0xFF]; + byte t6 = T2[(hi1 >> 16) & 0xFF]; + byte t7 = T3[hi1 >> 24]; + hi1 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c0 = (ulong)lo0 | ((ulong)hi1 << 32); + } + + { + byte t0 = T0[lo1 & 0xFF]; + byte t1 = T1[(lo1 >> 8) & 0xFF]; + byte t2 = T2[(lo1 >> 16) & 0xFF]; + byte t3 = T3[lo1 >> 24]; + lo1 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = T0[hi0 & 0xFF]; + byte t5 = T1[(hi0 >> 8) & 0xFF]; + byte t6 = T2[(hi0 >> 16) & 0xFF]; + byte t7 = T3[hi0 >> 24]; + hi0 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c1 = (ulong)lo1 | ((ulong)hi0 << 32); + } + + if (--round == 0) + { + break; + } + + roundKey = roundKeys[round]; + c0 ^= roundKey[0]; + c1 ^= roundKey[1]; + } + + roundKey = roundKeys[0]; + c0 -= roundKey[0]; + c1 -= roundKey[1]; + + Pack.UInt64_To_LE(c0, output); + Pack.UInt64_To_LE(c1, output[8..]); + } + + private void EncryptBlock_128(ReadOnlySpan<byte> input, Span<byte> output) + { + ulong c0 = Pack.LE_To_UInt64(input); + ulong c1 = Pack.LE_To_UInt64(input[8..]); + + ulong[] roundKey = roundKeys[0]; + c0 += roundKey[0]; + c1 += roundKey[1]; + + for (int round = 0; ;) + { + uint lo0 = (uint)c0, hi0 = (uint)(c0 >> 32); + uint lo1 = (uint)c1, hi1 = (uint)(c1 >> 32); + + { + byte t0 = S0[lo0 & 0xFF]; + byte t1 = S1[(lo0 >> 8) & 0xFF]; + byte t2 = S2[(lo0 >> 16) & 0xFF]; + byte t3 = S3[lo0 >> 24]; + lo0 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = S0[hi1 & 0xFF]; + byte t5 = S1[(hi1 >> 8) & 0xFF]; + byte t6 = S2[(hi1 >> 16) & 0xFF]; + byte t7 = S3[hi1 >> 24]; + hi1 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c0 = (ulong)lo0 | ((ulong)hi1 << 32); + } + + { + byte t0 = S0[lo1 & 0xFF]; + byte t1 = S1[(lo1 >> 8) & 0xFF]; + byte t2 = S2[(lo1 >> 16) & 0xFF]; + byte t3 = S3[lo1 >> 24]; + lo1 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = S0[hi0 & 0xFF]; + byte t5 = S1[(hi0 >> 8) & 0xFF]; + byte t6 = S2[(hi0 >> 16) & 0xFF]; + byte t7 = S3[hi0 >> 24]; + hi0 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c1 = (ulong)lo1 | ((ulong)hi0 << 32); + } + + c0 = MixColumn(c0); + c1 = MixColumn(c1); + + if (++round == roundsAmount) + { + break; + } + + roundKey = roundKeys[round]; + c0 ^= roundKey[0]; + c1 ^= roundKey[1]; + } + + roundKey = roundKeys[roundsAmount]; + c0 += roundKey[0]; + c1 += roundKey[1]; + + Pack.UInt64_To_LE(c0, output); + Pack.UInt64_To_LE(c1, output[8..]); + } +#else private void DecryptBlock_128(byte[] input, int inOff, byte[] output, int outOff) { ulong c0 = Pack.LE_To_UInt64(input, inOff); @@ -466,6 +677,7 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt64_To_LE(c0, output, outOff); Pack.UInt64_To_LE(c1, output, outOff + 8); } +#endif private void SubBytes() { @@ -900,7 +1112,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - #region TABLES AND S-BOXES +#region TABLES AND S-BOXES private const ulong mdsMatrix = 0x0407060801050101UL; private const ulong mdsInvMatrix = 0xCAD7492FA87695ADUL; @@ -1057,7 +1269,7 @@ namespace Org.BouncyCastle.Crypto.Engines 0xf3, 0x83, 0x28, 0x32, 0x45, 0x1e, 0xa4, 0xd3, 0xa2, 0x46, 0x6e, 0x9c, 0xdd, 0x63, 0xd4, 0x9d }; - #endregion +#endregion public virtual string AlgorithmName { diff --git a/crypto/src/crypto/engines/GOST28147Engine.cs b/crypto/src/crypto/engines/GOST28147Engine.cs index 8ef8aeb13..ee5a1ba53 100644 --- a/crypto/src/crypto/engines/GOST28147Engine.cs +++ b/crypto/src/crypto/engines/GOST28147Engine.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines @@ -151,14 +152,10 @@ namespace Org.BouncyCastle.Crypto.Engines * @param parameters the parameters required to set up the cipher. * @exception ArgumentException if the parameters argument is inappropriate. */ - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (parameters is ParametersWithSBox) + if (parameters is ParametersWithSBox param) { - ParametersWithSBox param = (ParametersWithSBox)parameters; - // // Set the S-Box // @@ -173,14 +170,12 @@ namespace Org.BouncyCastle.Crypto.Engines // if (param.Parameters != null) { - workingKey = generateWorkingKey(forEncryption, - ((KeyParameter)param.Parameters).GetKey()); + workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)param.Parameters).GetKey()); } } - else if (parameters is KeyParameter) + else if (parameters is KeyParameter keyParameter) { - workingKey = generateWorkingKey(forEncryption, - ((KeyParameter)parameters).GetKey()); + workingKey = GenerateWorkingKey(forEncryption, keyParameter.GetKey()); } else if (parameters != null) { @@ -204,11 +199,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("Gost28147 engine not initialised"); @@ -216,30 +207,45 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BlockSize, "input buffer too short"); Check.OutputLength(output, outOff, BlockSize, "output buffer too short"); - Gost28147Func(workingKey, input, inOff, output, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Gost28147Func(workingKey, input.AsSpan(inOff), output.AsSpan(outOff)); +#else + Gost28147Func(workingKey, input, inOff, output, outOff); +#endif return BlockSize; } - public virtual void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { + if (workingKey == null) + throw new InvalidOperationException("Gost28147 engine not initialised"); + + Check.DataLength(input, BlockSize, "input buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); + + Gost28147Func(workingKey, input, output); + + return BlockSize; } +#endif - private int[] generateWorkingKey( - bool forEncryption, - byte[] userKey) + public virtual void Reset() + { + } + + private int[] GenerateWorkingKey(bool forEncryption, byte[] userKey) { this.forEncryption = forEncryption; if (userKey.Length != 32) - { throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!"); - } int[] key = new int[8]; - for(int i=0; i!=8; i++) + for(int i=0; i != 8; i++) { - key[i] = bytesToint(userKey,i*4); + key[i] = (int)Pack.LE_To_UInt32(userKey, i * 4); } return key; @@ -267,16 +273,12 @@ namespace Org.BouncyCastle.Crypto.Engines return omLeft | omRight; } - private void Gost28147Func( - int[] workingKey, - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void Gost28147Func(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output) { - int N1, N2, tmp; //tmp -> for saving N1 - N1 = bytesToint(inBytes, inOff); - N2 = bytesToint(inBytes, inOff + 4); + int N1 = (int)Pack.LE_To_UInt32(input); + int N2 = (int)Pack.LE_To_UInt32(input[4..]); + int tmp; //tmp -> for saving N1 if (this.forEncryption) { @@ -322,30 +324,64 @@ namespace Org.BouncyCastle.Crypto.Engines N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1) - intTobytes(N1, outBytes, outOff); - intTobytes(N2, outBytes, outOff + 4); + Pack.UInt32_To_LE((uint)N1, output); + Pack.UInt32_To_LE((uint)N2, output[4..]); } - - //array of bytes to type int - private static int bytesToint( - byte[] inBytes, - int inOff) +#else + private void Gost28147Func(int[] workingKey, byte[] inBytes, int inOff, byte[] outBytes, int outOff) { - return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) + - ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff); - } + int N1 = (int)Pack.LE_To_UInt32(inBytes, inOff); + int N2 = (int)Pack.LE_To_UInt32(inBytes, inOff + 4); + int tmp; //tmp -> for saving N1 - //int to array of bytes - private static void intTobytes( - int num, - byte[] outBytes, - int outOff) - { - outBytes[outOff + 3] = (byte)(num >> 24); - outBytes[outOff + 2] = (byte)(num >> 16); - outBytes[outOff + 1] = (byte)(num >> 8); - outBytes[outOff] = (byte)num; + if (this.forEncryption) + { + for(int k = 0; k < 3; k++) // 1-24 steps + { + for(int j = 0; j < 8; j++) + { + tmp = N1; + int step = Gost28147_mainStep(N1, workingKey[j]); + N1 = N2 ^ step; // CM2 + N2 = tmp; + } + } + for(int j = 7; j > 0; j--) // 25-31 steps + { + tmp = N1; + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + } + else //decrypt + { + for(int j = 0; j < 8; j++) // 1-8 steps + { + tmp = N1; + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + for(int k = 0; k < 3; k++) //9-31 steps + { + for(int j = 7; j >= 0; j--) + { + if ((k == 2) && (j==0)) + { + break; // break 32 step + } + tmp = N1; + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + } + } + + N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1) + + Pack.UInt32_To_LE((uint)N1, outBytes, outOff); + Pack.UInt32_To_LE((uint)N2, outBytes, outOff + 4); } +#endif /** * Return the S-Box associated with SBoxName diff --git a/crypto/src/crypto/engines/IdeaEngine.cs b/crypto/src/crypto/engines/IdeaEngine.cs index 6c0379174..c5d3eb36f 100644 --- a/crypto/src/crypto/engines/IdeaEngine.cs +++ b/crypto/src/crypto/engines/IdeaEngine.cs @@ -64,11 +64,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("IDEA engine not initialised"); @@ -76,28 +72,59 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + IdeaFunc(workingKey, input.AsSpan(inOff), output.AsSpan(outOff)); +#else IdeaFunc(workingKey, input, inOff, output, outOff); +#endif return BLOCK_SIZE; } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey == null) + throw new InvalidOperationException("IDEA engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + IdeaFunc(workingKey, input, output); + return BLOCK_SIZE; + } +#endif + public virtual void Reset() { } - private static readonly int MASK = 0xffff; - private static readonly int BASE = 0x10001; - private int BytesToWord( - byte[] input, - int inOff) + + private static readonly int MASK = 0xffff; + private static readonly int BASE = 0x10001; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int BytesToWord(ReadOnlySpan<byte> input) + { + return ((input[0] << 8) & 0xff00) + (input[1] & 0xff); + } + + private void WordToBytes(int word, Span<byte> output) + { + output[0] = (byte)((uint)word >> 8); + output[1] = (byte)word; + } +#else + private int BytesToWord(byte[] input, int inOff) { return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff); } - private void WordToBytes( - int word, - byte[] outBytes, - int outOff) + + private void WordToBytes(int word, byte[] outBytes, int outOff) { outBytes[outOff] = (byte)((uint) word >> 8); outBytes[outOff + 1] = (byte)word; } +#endif + /** * return x = x * y where the multiplication is done modulo * 65537 (0x10001) (as defined in the IDEA specification) and @@ -128,19 +155,51 @@ namespace Org.BouncyCastle.Crypto.Engines } return x & MASK; } - private void IdeaFunc( - int[] workingKey, - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void IdeaFunc(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output) + { + int x0 = BytesToWord(input); + int x1 = BytesToWord(input[2..]); + int x2 = BytesToWord(input[4..]); + int x3 = BytesToWord(input[6..]); + int keyOff = 0, t0, t1; + for (int round = 0; round < 8; round++) + { + x0 = Mul(x0, workingKey[keyOff++]); + x1 += workingKey[keyOff++]; + x1 &= MASK; + x2 += workingKey[keyOff++]; + x2 &= MASK; + x3 = Mul(x3, workingKey[keyOff++]); + t0 = x1; + t1 = x2; + x2 ^= x0; + x1 ^= x3; + x2 = Mul(x2, workingKey[keyOff++]); + x1 += x2; + x1 &= MASK; + x1 = Mul(x1, workingKey[keyOff++]); + x2 += x1; + x2 &= MASK; + x0 ^= x1; + x3 ^= x2; + x1 ^= t1; + x2 ^= t0; + } + WordToBytes(Mul(x0, workingKey[keyOff++]), output); + WordToBytes(x2 + workingKey[keyOff++], output[2..]); /* NB: Order */ + WordToBytes(x1 + workingKey[keyOff++], output[4..]); + WordToBytes(Mul(x3, workingKey[keyOff]), output[6..]); + } +#else + private void IdeaFunc(int[] workingKey, byte[] input, int inOff, byte[] outBytes, int outOff) { - int x0, x1, x2, x3, t0, t1; - int keyOff = 0; - x0 = BytesToWord(input, inOff); - x1 = BytesToWord(input, inOff + 2); - x2 = BytesToWord(input, inOff + 4); - x3 = BytesToWord(input, inOff + 6); + int x0 = BytesToWord(input, inOff); + int x1 = BytesToWord(input, inOff + 2); + int x2 = BytesToWord(input, inOff + 4); + int x3 = BytesToWord(input, inOff + 6); + int keyOff = 0, t0, t1; for (int round = 0; round < 8; round++) { x0 = Mul(x0, workingKey[keyOff++]); @@ -169,16 +228,17 @@ namespace Org.BouncyCastle.Crypto.Engines WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4); WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6); } +#endif + /** * The following function is used to expand the user key to the encryption * subkey. The first 16 bytes are the user key, and the rest of the subkey * is calculated by rotating the previous 16 bytes by 25 bits to the left, * and so on until the subkey is completed. */ - private int[] ExpandKey( - byte[] uKey) + private int[] ExpandKey(byte[] uKey) { - int[] key = new int[52]; + int[] key = new int[52]; if (uKey.Length < 16) { byte[] tmp = new byte[16]; @@ -187,7 +247,11 @@ namespace Org.BouncyCastle.Crypto.Engines } for (int i = 0; i < 8; i++) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + key[i] = BytesToWord(uKey[(i * 2)..]); +#else key[i] = BytesToWord(uKey, i * 2); +#endif } for (int i = 8; i < 52; i++) { diff --git a/crypto/src/crypto/engines/NoekeonEngine.cs b/crypto/src/crypto/engines/NoekeonEngine.cs index 838a40339..2866d8d75 100644 --- a/crypto/src/crypto/engines/NoekeonEngine.cs +++ b/crypto/src/crypto/engines/NoekeonEngine.cs @@ -92,11 +92,7 @@ namespace Org.BouncyCastle.Crypto.Engines this._initialised = true; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (!_initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); @@ -104,15 +100,179 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, Size, "input buffer too short"); Check.OutputLength(output, outOff, Size, "output buffer too short"); - return _forEncryption - ? EncryptBlock(input, inOff, output, outOff) - : DecryptBlock(input, inOff, output, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return _forEncryption + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return _forEncryption + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.DataLength(input, Size, "input buffer too short"); + Check.OutputLength(output, Size, "output buffer too short"); + + return _forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); + } +#endif + public virtual void Reset() { } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + uint a0 = Pack.BE_To_UInt32(input); + uint a1 = Pack.BE_To_UInt32(input[4..]); + uint a2 = Pack.BE_To_UInt32(input[8..]); + uint a3 = Pack.BE_To_UInt32(input[12..]); + + uint k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; + + int round = 0; + for (;;) + { + a0 ^= RoundConstants[round]; + + // theta(a, k); + { + uint t02 = a0 ^ a2; + t02 ^= Integers.RotateLeft(t02, 8) ^ Integers.RotateLeft(t02, 24); + + a0 ^= k0; + a1 ^= k1; + a2 ^= k2; + a3 ^= k3; + + uint t13 = a1 ^ a3; + t13 ^= Integers.RotateLeft(t13, 8) ^ Integers.RotateLeft(t13, 24); + + a0 ^= t13; + a1 ^= t02; + a2 ^= t13; + a3 ^= t02; + } + + if (++round > Size) + break; + + // pi1(a); + { + a1 = Integers.RotateLeft(a1, 1); + a2 = Integers.RotateLeft(a2, 5); + a3 = Integers.RotateLeft(a3, 2); + } + + // gamma(a); + { + uint t = a3; + a1 ^= a3 | a2; + a3 = a0 ^ (a2 & ~a1); + + a2 = t ^ ~a1 ^ a2 ^ a3; + + a1 ^= a3 | a2; + a0 = t ^ (a2 & a1); + } + + // pi2(a); + { + a1 = Integers.RotateLeft(a1, 31); + a2 = Integers.RotateLeft(a2, 27); + a3 = Integers.RotateLeft(a3, 30); + } + } + + Pack.UInt32_To_BE(a0, output); + Pack.UInt32_To_BE(a1, output[4..]); + Pack.UInt32_To_BE(a2, output[8..]); + Pack.UInt32_To_BE(a3, output[12..]); + + return Size; + } + + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + uint a0 = Pack.BE_To_UInt32(input); + uint a1 = Pack.BE_To_UInt32(input[4..]); + uint a2 = Pack.BE_To_UInt32(input[8..]); + uint a3 = Pack.BE_To_UInt32(input[12..]); + + uint k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; + + int round = Size; + for (;;) + { + // theta(a, k); + { + uint t02 = a0 ^ a2; + t02 ^= Integers.RotateLeft(t02, 8) ^ Integers.RotateLeft(t02, 24); + + a0 ^= k0; + a1 ^= k1; + a2 ^= k2; + a3 ^= k3; + + uint t13 = a1 ^ a3; + t13 ^= Integers.RotateLeft(t13, 8) ^ Integers.RotateLeft(t13, 24); + + a0 ^= t13; + a1 ^= t02; + a2 ^= t13; + a3 ^= t02; + } + + a0 ^= RoundConstants[round]; + + if (--round < 0) + break; + + // pi1(a); + { + a1 = Integers.RotateLeft(a1, 1); + a2 = Integers.RotateLeft(a2, 5); + a3 = Integers.RotateLeft(a3, 2); + } + + // gamma(a); + { + uint t = a3; + a1 ^= a3 | a2; + a3 = a0 ^ (a2 & ~a1); + + a2 = t ^ ~a1 ^ a2 ^ a3; + + a1 ^= a3 | a2; + a0 = t ^ (a2 & a1); + } + + // pi2(a); + { + a1 = Integers.RotateLeft(a1, 31); + a2 = Integers.RotateLeft(a2, 27); + a3 = Integers.RotateLeft(a3, 30); + } + } + + Pack.UInt32_To_BE(a0, output); + Pack.UInt32_To_BE(a1, output[4..]); + Pack.UInt32_To_BE(a2, output[8..]); + Pack.UInt32_To_BE(a3, output[12..]); + + return Size; + } +#else private int EncryptBlock(byte[] input, int inOff, byte[] output, int outOff) { uint a0 = Pack.BE_To_UInt32(input, inOff); @@ -254,5 +414,6 @@ namespace Org.BouncyCastle.Crypto.Engines return Size; } +#endif } } diff --git a/crypto/src/crypto/engines/NullEngine.cs b/crypto/src/crypto/engines/NullEngine.cs index f883b7c29..b79cfba93 100644 --- a/crypto/src/crypto/engines/NullEngine.cs +++ b/crypto/src/crypto/engines/NullEngine.cs @@ -41,11 +41,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (!initialised) throw new InvalidOperationException("Null engine not initialised"); @@ -61,7 +57,22 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } - public virtual void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!initialised) + throw new InvalidOperationException("Null engine not initialised"); + + Check.DataLength(input, BlockSize, "input buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); + + input[..BlockSize].CopyTo(output); + + return BlockSize; + } +#endif + + public virtual void Reset() { // nothing needs to be done } diff --git a/crypto/src/crypto/engines/RC2Engine.cs b/crypto/src/crypto/engines/RC2Engine.cs index 4aca1894f..972c4128a 100644 --- a/crypto/src/crypto/engines/RC2Engine.cs +++ b/crypto/src/crypto/engines/RC2Engine.cs @@ -159,11 +159,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("RC2 engine not initialised"); @@ -171,6 +167,16 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (encrypting) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else if (encrypting) { EncryptBlock(input, inOff, output, outOff); @@ -179,26 +185,150 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff); } +#endif + + return BLOCK_SIZE; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey == null) + throw new InvalidOperationException("RC2 engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, output); + } + else + { + DecryptBlock(input, output); + } return BLOCK_SIZE; } +#endif /** * return the result rotating the 16 bit number in x left by y */ - private int RotateWordLeft( - int x, - int y) + private int RotateWordLeft(int x, int y) { x &= 0xffff; return (x << y) | (x >> (16 - y)); } - private void EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int x76, x54, x32, x10; + + x76 = ((input[7] & 0xff) << 8) + (input[6] & 0xff); + x54 = ((input[5] & 0xff) << 8) + (input[4] & 0xff); + x32 = ((input[3] & 0xff) << 8) + (input[2] & 0xff); + x10 = ((input[1] & 0xff) << 8) + (input[0] & 0xff); + + for (int i = 0; i <= 16; i += 4) + { + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i + 1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i + 2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i + 3], 5); + } + + x10 += workingKey[x76 & 63]; + x32 += workingKey[x10 & 63]; + x54 += workingKey[x32 & 63]; + x76 += workingKey[x54 & 63]; + + for (int i = 20; i <= 40; i += 4) + { + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i + 1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i + 2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i + 3], 5); + } + + x10 += workingKey[x76 & 63]; + x32 += workingKey[x10 & 63]; + x54 += workingKey[x32 & 63]; + x76 += workingKey[x54 & 63]; + + for (int i = 44; i < 64; i += 4) + { + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i + 1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i + 2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i + 3], 5); + } + + output[0] = (byte)x10; + output[1] = (byte)(x10 >> 8); + output[2] = (byte)x32; + output[3] = (byte)(x32 >> 8); + output[4] = (byte)x54; + output[5] = (byte)(x54 >> 8); + output[6] = (byte)x76; + output[7] = (byte)(x76 >> 8); + } + + private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int x76, x54, x32, x10; + + x76 = ((input[7] & 0xff) << 8) + (input[6] & 0xff); + x54 = ((input[5] & 0xff) << 8) + (input[4] & 0xff); + x32 = ((input[3] & 0xff) << 8) + (input[2] & 0xff); + x10 = ((input[1] & 0xff) << 8) + (input[0] & 0xff); + + for (int i = 60; i >= 44; i -= 4) + { + x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i + 3]); + x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i + 2]); + x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i + 1]); + x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i]); + } + + x76 -= workingKey[x54 & 63]; + x54 -= workingKey[x32 & 63]; + x32 -= workingKey[x10 & 63]; + x10 -= workingKey[x76 & 63]; + + for (int i = 40; i >= 20; i -= 4) + { + x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i + 3]); + x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i + 2]); + x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i + 1]); + x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i]); + } + + x76 -= workingKey[x54 & 63]; + x54 -= workingKey[x32 & 63]; + x32 -= workingKey[x10 & 63]; + x10 -= workingKey[x76 & 63]; + + for (int i = 16; i >= 0; i -= 4) + { + x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i + 3]); + x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i + 2]); + x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i + 1]); + x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i]); + } + + output[0] = (byte)x10; + output[1] = (byte)(x10 >> 8); + output[2] = (byte)x32; + output[3] = (byte)(x32 >> 8); + output[4] = (byte)x54; + output[5] = (byte)(x54 >> 8); + output[6] = (byte)x76; + output[7] = (byte)(x76 >> 8); + } +#else + private void EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { int x76, x54, x32, x10; @@ -209,10 +339,10 @@ namespace Org.BouncyCastle.Crypto.Engines for (int i = 0; i <= 16; i += 4) { - x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); - x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); - x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); - x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); } x10 += workingKey[x76 & 63]; @@ -222,10 +352,10 @@ namespace Org.BouncyCastle.Crypto.Engines for (int i = 20; i <= 40; i += 4) { - x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); - x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); - x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); - x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); } x10 += workingKey[x76 & 63]; @@ -235,10 +365,10 @@ namespace Org.BouncyCastle.Crypto.Engines for (int i = 44; i < 64; i += 4) { - x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); - x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); - x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); - x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); } outBytes[outOff + 0] = (byte)x10; @@ -251,11 +381,7 @@ namespace Org.BouncyCastle.Crypto.Engines outBytes[outOff + 7] = (byte)(x76 >> 8); } - private void DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + private void DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { int x76, x54, x32, x10; @@ -307,5 +433,6 @@ namespace Org.BouncyCastle.Crypto.Engines outBytes[outOff + 6] = (byte)x76; outBytes[outOff + 7] = (byte)(x76 >> 8); } +#endif } } diff --git a/crypto/src/crypto/engines/RC532Engine.cs b/crypto/src/crypto/engines/RC532Engine.cs index d1c29e624..aa3da5870 100644 --- a/crypto/src/crypto/engines/RC532Engine.cs +++ b/crypto/src/crypto/engines/RC532Engine.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines @@ -46,7 +47,6 @@ namespace Org.BouncyCastle.Crypto.Engines public RC532Engine() { _noRounds = 12; // the default -// _S = null; } public virtual string AlgorithmName @@ -72,23 +72,17 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (typeof(RC5Parameters).IsInstanceOfType(parameters)) + if (parameters is RC5Parameters rc5Parameters) { - RC5Parameters p = (RC5Parameters)parameters; + _noRounds = rc5Parameters.Rounds; - _noRounds = p.Rounds; - - SetKey(p.GetKey()); + SetKey(rc5Parameters.GetKey()); } - else if (typeof(KeyParameter).IsInstanceOfType(parameters)) + else if (parameters is KeyParameter keyParameter) { - KeyParameter p = (KeyParameter)parameters; - - SetKey(p.GetKey()); + SetKey(keyParameter.GetKey()); } else { @@ -98,16 +92,27 @@ namespace Org.BouncyCastle.Crypto.Engines this.forEncryption = forEncryption; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return forEncryption + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return forEncryption + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { - return (forEncryption) - ? EncryptBlock(input, inOff, output, outOff) - : DecryptBlock(input, inOff, output, outOff); + return forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); } +#endif public virtual void Reset() { @@ -118,8 +123,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @param key the key to be used */ - private void SetKey( - byte[] key) + private void SetKey(byte[] key) { // // KEY EXPANSION: @@ -133,7 +137,7 @@ namespace Org.BouncyCastle.Crypto.Engines // of K. Any unfilled byte positions in L are zeroed. In the // case that b = c = 0, set c = 1 and L[0] = 0. // - int[] L = new int[(key.Length + (4 - 1)) / 4]; + int[] L = new int[(key.Length + 3) / 4]; for (int i = 0; i != key.Length; i++) { @@ -175,120 +179,81 @@ namespace Org.BouncyCastle.Crypto.Engines for (int k = 0; k < iter; k++) { - A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); - B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + A = _S[ii] = Integers.RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = Integers.RotateLeft(L[jj] + A + B, A + B); ii = (ii+1) % _S.Length; jj = (jj+1) % L.Length; } } - /** - * Encrypt the given block starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * - * @param in in byte buffer containing data to encrypt - * @param inOff offset into src buffer - * @param out out buffer where encrypted data is written - * @param outOff offset into out buffer - */ - private int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { - int A = BytesToWord(input, inOff) + _S[0]; - int B = BytesToWord(input, inOff + 4) + _S[1]; + int A = (int)Pack.LE_To_UInt32(input) + _S[0]; + int B = (int)Pack.LE_To_UInt32(input[4..]) + _S[1]; for (int i = 1; i <= _noRounds; i++) { - A = RotateLeft(A ^ B, B) + _S[2*i]; - B = RotateLeft(B ^ A, A) + _S[2*i+1]; + A = Integers.RotateLeft(A ^ B, B) + _S[2*i]; + B = Integers.RotateLeft(B ^ A, A) + _S[2*i+1]; } - WordToBytes(A, outBytes, outOff); - WordToBytes(B, outBytes, outOff + 4); + Pack.UInt32_To_LE((uint)A, output); + Pack.UInt32_To_LE((uint)B, output[4..]); - return 2 * 4; + return 8; } - private int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { - int A = BytesToWord(input, inOff); - int B = BytesToWord(input, inOff + 4); + int A = (int)Pack.LE_To_UInt32(input); + int B = (int)Pack.LE_To_UInt32(input[4..]); for (int i = _noRounds; i >= 1; i--) { - B = RotateRight(B - _S[2*i+1], A) ^ A; - A = RotateRight(A - _S[2*i], B) ^ B; + B = Integers.RotateRight(B - _S[2*i+1], A) ^ A; + A = Integers.RotateRight(A - _S[2*i], B) ^ B; } - WordToBytes(A - _S[0], outBytes, outOff); - WordToBytes(B - _S[1], outBytes, outOff + 4); + Pack.UInt32_To_LE((uint)(A - _S[0]), output); + Pack.UInt32_To_LE((uint)(B - _S[1]), output[4..]); - return 2 * 4; + return 8; } +#else + private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) + { + int A = (int)Pack.LE_To_UInt32(input, inOff) + _S[0]; + int B = (int)Pack.LE_To_UInt32(input, inOff + 4) + _S[1]; + for (int i = 1; i <= _noRounds; i++) + { + A = Integers.RotateLeft(A ^ B, B) + _S[2*i]; + B = Integers.RotateLeft(B ^ A, A) + _S[2*i+1]; + } - ////////////////////////////////////////////////////////////// - // - // PRIVATE Helper Methods - // - ////////////////////////////////////////////////////////////// + Pack.UInt32_To_LE((uint)A, outBytes, outOff); + Pack.UInt32_To_LE((uint)B, outBytes, outOff + 4); - /** - * Perform a left "spin" of the word. The rotation of the given - * word <em>x</em> is rotated left by <em>y</em> bits. - * Only the <em>lg(32)</em> low-order bits of <em>y</em> - * are used to determine the rotation amount. Here it is - * assumed that the wordsize used is a power of 2. - * - * @param x word to rotate - * @param y number of bits to rotate % 32 - */ - private int RotateLeft(int x, int y) { - return ((int) ( (uint) (x << (y & (32-1))) | - ((uint) x >> (32 - (y & (32-1)))) ) - ); - } - - /** - * Perform a right "spin" of the word. The rotation of the given - * word <em>x</em> is rotated left by <em>y</em> bits. - * Only the <em>lg(32)</em> low-order bits of <em>y</em> - * are used to determine the rotation amount. Here it is - * assumed that the wordsize used is a power of 2. - * - * @param x word to rotate - * @param y number of bits to rotate % 32 - */ - private int RotateRight(int x, int y) { - return ((int) ( ((uint) x >> (y & (32-1))) | - (uint) (x << (32 - (y & (32-1)))) ) - ); + return 8; } - private int BytesToWord( - byte[] src, - int srcOff) + private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8) - | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24); - } + int A = (int)Pack.LE_To_UInt32(input, inOff); + int B = (int)Pack.LE_To_UInt32(input, inOff + 4); - private void WordToBytes( - int word, - byte[] dst, - int dstOff) - { - dst[dstOff] = (byte)word; - dst[dstOff + 1] = (byte)(word >> 8); - dst[dstOff + 2] = (byte)(word >> 16); - dst[dstOff + 3] = (byte)(word >> 24); + for (int i = _noRounds; i >= 1; i--) + { + B = Integers.RotateRight(B - _S[2*i+1], A) ^ A; + A = Integers.RotateRight(A - _S[2*i], B) ^ B; + } + + Pack.UInt32_To_LE((uint)(A - _S[0]), outBytes, outOff); + Pack.UInt32_To_LE((uint)(B - _S[1]), outBytes, outOff + 4); + + return 8; } +#endif } } diff --git a/crypto/src/crypto/engines/RC564Engine.cs b/crypto/src/crypto/engines/RC564Engine.cs index 097fd60ba..8d524f420 100644 --- a/crypto/src/crypto/engines/RC564Engine.cs +++ b/crypto/src/crypto/engines/RC564Engine.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines @@ -15,9 +16,6 @@ namespace Org.BouncyCastle.Crypto.Engines public class RC564Engine : IBlockCipher { - private static readonly int wordSize = 64; - private static readonly int bytesPerWord = wordSize / 8; - /* * the number of rounds to perform */ @@ -64,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Engines public virtual int GetBlockSize() { - return 2 * bytesPerWord; + return 16; } /** @@ -75,33 +73,39 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (!(typeof(RC5Parameters).IsInstanceOfType(parameters))) - { + if (!(parameters is RC5Parameters rc5Parameters)) throw new ArgumentException("invalid parameter passed to RC564 init - " + Platform.GetTypeName(parameters)); - } - - RC5Parameters p = (RC5Parameters)parameters; this.forEncryption = forEncryption; - _noRounds = p.Rounds; + _noRounds = rc5Parameters.Rounds; - SetKey(p.GetKey()); + SetKey(rc5Parameters.GetKey()); + } + + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return forEncryption + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return forEncryption + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { - return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) - : DecryptBlock(input, inOff, output, outOff); + return forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); } +#endif public virtual void Reset() { @@ -112,8 +116,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @param key the key to be used */ - private void SetKey( - byte[] key) + private void SetKey(byte[] key) { // // KEY EXPANSION: @@ -127,11 +130,11 @@ namespace Org.BouncyCastle.Crypto.Engines // of K. Any unfilled byte positions in L are zeroed. In the // case that b = c = 0, set c = 1 and L[0] = 0. // - long[] L = new long[(key.Length + (bytesPerWord - 1)) / bytesPerWord]; + long[] L = new long[(key.Length + 7) / 8]; for (int i = 0; i != key.Length; i++) { - L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord)); + L[i / 8] += (long)(key[i] & 0xff) << (8 * (i % 8)); } // @@ -169,127 +172,81 @@ namespace Org.BouncyCastle.Crypto.Engines for (int k = 0; k < iter; k++) { - A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); - B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + A = _S[ii] = Longs.RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = Longs.RotateLeft(L[jj] + A + B, (int)(A + B)); ii = (ii+1) % _S.Length; jj = (jj+1) % L.Length; } } - /** - * Encrypt the given block starting at the given offset and place - * the result in the provided buffer starting at the given offset. - * - * @param in in byte buffer containing data to encrypt - * @param inOff offset into src buffer - * @param out out buffer where encrypted data is written - * @param outOff offset into out buffer - */ - private int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { - long A = BytesToWord(input, inOff) + _S[0]; - long B = BytesToWord(input, inOff + bytesPerWord) + _S[1]; + long A = (long)Pack.LE_To_UInt64(input) + _S[0]; + long B = (long)Pack.LE_To_UInt64(input[8..]) + _S[1]; for (int i = 1; i <= _noRounds; i++) { - A = RotateLeft(A ^ B, B) + _S[2*i]; - B = RotateLeft(B ^ A, A) + _S[2*i+1]; + A = Longs.RotateLeft(A ^ B, (int)B) + _S[2*i]; + B = Longs.RotateLeft(B ^ A, (int)A) + _S[2*i+1]; } - WordToBytes(A, outBytes, outOff); - WordToBytes(B, outBytes, outOff + bytesPerWord); + Pack.UInt64_To_LE((ulong)A, output); + Pack.UInt64_To_LE((ulong)B, output[8..]); - return 2 * bytesPerWord; + return 16; } - private int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { - long A = BytesToWord(input, inOff); - long B = BytesToWord(input, inOff + bytesPerWord); + long A = (long)Pack.LE_To_UInt64(input); + long B = (long)Pack.LE_To_UInt64(input[8..]); for (int i = _noRounds; i >= 1; i--) { - B = RotateRight(B - _S[2*i+1], A) ^ A; - A = RotateRight(A - _S[2*i], B) ^ B; + B = Longs.RotateRight(B - _S[2*i+1], (int)A) ^ A; + A = Longs.RotateRight(A - _S[2*i], (int)B) ^ B; } - WordToBytes(A - _S[0], outBytes, outOff); - WordToBytes(B - _S[1], outBytes, outOff + bytesPerWord); + Pack.UInt64_To_LE((ulong)(A - _S[0]), output); + Pack.UInt64_To_LE((ulong)(B - _S[1]), output[8..]); - return 2 * bytesPerWord; + return 16; } +#else + private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) + { + long A = (long)Pack.LE_To_UInt64(input, inOff) + _S[0]; + long B = (long)Pack.LE_To_UInt64(input, inOff + 8) + _S[1]; + for (int i = 1; i <= _noRounds; i++) + { + A = Longs.RotateLeft(A ^ B, (int)B) + _S[2*i]; + B = Longs.RotateLeft(B ^ A, (int)A) + _S[2*i+1]; + } - ////////////////////////////////////////////////////////////// - // - // PRIVATE Helper Methods - // - ////////////////////////////////////////////////////////////// - - /** - * Perform a left "spin" of the word. The rotation of the given - * word <em>x</em> is rotated left by <em>y</em> bits. - * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em> - * are used to determine the rotation amount. Here it is - * assumed that the wordsize used is a power of 2. - * - * @param x word to rotate - * @param y number of bits to rotate % wordSize - */ - private long RotateLeft(long x, long y) { - return ((long) ( (ulong) (x << (int) (y & (wordSize-1))) | - ((ulong) x >> (int) (wordSize - (y & (wordSize-1))))) - ); - } + Pack.UInt64_To_LE((ulong)A, outBytes, outOff); + Pack.UInt64_To_LE((ulong)B, outBytes, outOff + 8); - /** - * Perform a right "spin" of the word. The rotation of the given - * word <em>x</em> is rotated left by <em>y</em> bits. - * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em> - * are used to determine the rotation amount. Here it is - * assumed that the wordsize used is a power of 2. - * - * @param x word to rotate - * @param y number of bits to rotate % wordSize - */ - private long RotateRight(long x, long y) { - return ((long) ( ((ulong) x >> (int) (y & (wordSize-1))) | - (ulong) (x << (int) (wordSize - (y & (wordSize-1))))) - ); + return 16; } - private long BytesToWord( - byte[] src, - int srcOff) + private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - long word = 0; + long A = (long)Pack.LE_To_UInt64(input, inOff); + long B = (long)Pack.LE_To_UInt64(input, inOff + 8); - for (int i = bytesPerWord - 1; i >= 0; i--) + for (int i = _noRounds; i >= 1; i--) { - word = (word << 8) + (src[i + srcOff] & 0xff); + B = Longs.RotateRight(B - _S[2*i+1], (int)A) ^ A; + A = Longs.RotateRight(A - _S[2*i], (int)B) ^ B; } - return word; - } + Pack.UInt64_To_LE((ulong)(A - _S[0]), outBytes, outOff); + Pack.UInt64_To_LE((ulong)(B - _S[1]), outBytes, outOff + 8); - private void WordToBytes( - long word, - byte[] dst, - int dstOff) - { - for (int i = 0; i < bytesPerWord; i++) - { - dst[i + dstOff] = (byte)word; - word = (long) ((ulong) word >> 8); - } + return 16; } +#endif } } diff --git a/crypto/src/crypto/engines/RC6Engine.cs b/crypto/src/crypto/engines/RC6Engine.cs index 9aeb1e7cb..316bae65e 100644 --- a/crypto/src/crypto/engines/RC6Engine.cs +++ b/crypto/src/crypto/engines/RC6Engine.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines @@ -11,9 +12,6 @@ namespace Org.BouncyCastle.Crypto.Engines public class RC6Engine : IBlockCipher { - private static readonly int wordSize = 32; - private static readonly int bytesPerWord = wordSize / 8; - /* * the number of rounds to perform */ @@ -46,7 +44,6 @@ namespace Org.BouncyCastle.Crypto.Engines */ public RC6Engine() { -// _S = null; } public virtual string AlgorithmName @@ -61,7 +58,7 @@ namespace Org.BouncyCastle.Crypto.Engines public virtual int GetBlockSize() { - return 4 * bytesPerWord; + return 16; } /** @@ -72,36 +69,51 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the parameters argument is * inappropriate. */ - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (!(parameters is KeyParameter)) + if (!(parameters is KeyParameter keyParameter)) throw new ArgumentException("invalid parameter passed to RC6 init - " + Platform.GetTypeName(parameters)); this.forEncryption = forEncryption; - KeyParameter p = (KeyParameter)parameters; - SetKey(p.GetKey()); + SetKey(keyParameter.GetKey()); } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - int blockSize = GetBlockSize(); if (_S == null) throw new InvalidOperationException("RC6 engine not initialised"); + int blockSize = GetBlockSize(); Check.DataLength(input, inOff, blockSize, "input buffer too short"); Check.OutputLength(output, outOff, blockSize, "output buffer too short"); - return (forEncryption) - ? EncryptBlock(input, inOff, output, outOff) - : DecryptBlock(input, inOff, output, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return forEncryption + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return forEncryption + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (_S == null) + throw new InvalidOperationException("RC6 engine not initialised"); + + int blockSize = GetBlockSize(); + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + return forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); } +#endif public virtual void Reset() { @@ -128,17 +140,17 @@ namespace Org.BouncyCastle.Crypto.Engines // case that b = c = 0, set c = 1 and L[0] = 0. // // compute number of dwords - int c = (key.Length + (bytesPerWord - 1)) / bytesPerWord; + int c = (key.Length + 3) / 4; if (c == 0) { c = 1; } - int[] L = new int[(key.Length + bytesPerWord - 1) / bytesPerWord]; + int[] L = new int[(key.Length + 3) / 4]; // load all key bytes into array of key dwords for (int i = key.Length - 1; i >= 0; i--) { - L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff); + L[i / 4] = (L[i / 4] << 8) + (key[i] & 0xff); } // @@ -178,24 +190,21 @@ namespace Org.BouncyCastle.Crypto.Engines for (int k = 0; k < iter; k++) { - A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); - B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + A = _S[ii] = Integers.RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = Integers.RotateLeft( L[jj] + A + B, A + B); ii = (ii+1) % _S.Length; jj = (jj+1) % L.Length; } } - private int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { // load A,B,C and D registers from in. - int A = BytesToWord(input, inOff); - int B = BytesToWord(input, inOff + bytesPerWord); - int C = BytesToWord(input, inOff + bytesPerWord*2); - int D = BytesToWord(input, inOff + bytesPerWord*3); + int A = (int)Pack.LE_To_UInt32(input); + int B = (int)Pack.LE_To_UInt32(input[4..]); + int C = (int)Pack.LE_To_UInt32(input[8..]); + int D = (int)Pack.LE_To_UInt32(input[12..]); // Do pseudo-round #0: pre-whitening of B and D B += _S[0]; @@ -204,21 +213,21 @@ namespace Org.BouncyCastle.Crypto.Engines // perform round #1,#2 ... #ROUNDS of encryption for (int i = 1; i <= _noRounds; i++) { - int t = 0,u = 0; + int t = 0, u = 0; - t = B*(2*B+1); - t = RotateLeft(t,5); + t = B * (2 * B + 1); + t = Integers.RotateLeft(t, 5); - u = D*(2*D+1); - u = RotateLeft(u,5); + u = D * (2 * D + 1); + u = Integers.RotateLeft(u, 5); A ^= t; - A = RotateLeft(A,u); - A += _S[2*i]; + A = Integers.RotateLeft(A, u); + A += _S[2 * i]; C ^= u; - C = RotateLeft(C,t); - C += _S[2*i+1]; + C = Integers.RotateLeft(C, t); + C += _S[2 * i + 1]; int temp = A; A = B; @@ -226,39 +235,36 @@ namespace Org.BouncyCastle.Crypto.Engines C = D; D = temp; } + // do pseudo-round #(ROUNDS+1) : post-whitening of A and C - A += _S[2*_noRounds+2]; - C += _S[2*_noRounds+3]; + A += _S[2 * _noRounds + 2]; + C += _S[2 * _noRounds + 3]; // store A, B, C and D registers to out - WordToBytes(A, outBytes, outOff); - WordToBytes(B, outBytes, outOff + bytesPerWord); - WordToBytes(C, outBytes, outOff + bytesPerWord*2); - WordToBytes(D, outBytes, outOff + bytesPerWord*3); + Pack.UInt32_To_LE((uint)A, output); + Pack.UInt32_To_LE((uint)B, output[4..]); + Pack.UInt32_To_LE((uint)C, output[8..]); + Pack.UInt32_To_LE((uint)D, output[12..]); - return 4 * bytesPerWord; + return 16; } - private int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { // load A,B,C and D registers from out. - int A = BytesToWord(input, inOff); - int B = BytesToWord(input, inOff + bytesPerWord); - int C = BytesToWord(input, inOff + bytesPerWord*2); - int D = BytesToWord(input, inOff + bytesPerWord*3); + int A = (int)Pack.LE_To_UInt32(input); + int B = (int)Pack.LE_To_UInt32(input[4..]); + int C = (int)Pack.LE_To_UInt32(input[8..]); + int D = (int)Pack.LE_To_UInt32(input[12..]); // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C - C -= _S[2*_noRounds+3]; - A -= _S[2*_noRounds+2]; + C -= _S[2 * _noRounds + 3]; + A -= _S[2 * _noRounds + 2]; // Undo round #ROUNDS, .., #2,#1 of encryption for (int i = _noRounds; i >= 1; i--) { - int t=0,u = 0; + int t = 0, u = 0; int temp = D; D = C; @@ -266,96 +272,133 @@ namespace Org.BouncyCastle.Crypto.Engines B = A; A = temp; - t = B*(2*B+1); - t = RotateLeft(t, LGW); + t = B * (2 * B + 1); + t = Integers.RotateLeft(t, LGW); - u = D*(2*D+1); - u = RotateLeft(u, LGW); + u = D * (2 * D + 1); + u = Integers.RotateLeft(u, LGW); - C -= _S[2*i+1]; - C = RotateRight(C,t); + C -= _S[2 * i + 1]; + C = Integers.RotateRight(C, t); C ^= u; - A -= _S[2*i]; - A = RotateRight(A,u); + A -= _S[2 * i]; + A = Integers.RotateRight(A, u); A ^= t; - } + // Undo pseudo-round #0: pre-whitening of B and D D -= _S[1]; B -= _S[0]; - WordToBytes(A, outBytes, outOff); - WordToBytes(B, outBytes, outOff + bytesPerWord); - WordToBytes(C, outBytes, outOff + bytesPerWord*2); - WordToBytes(D, outBytes, outOff + bytesPerWord*3); + Pack.UInt32_To_LE((uint)A, output); + Pack.UInt32_To_LE((uint)B, output[4..]); + Pack.UInt32_To_LE((uint)C, output[8..]); + Pack.UInt32_To_LE((uint)D, output[12..]); - return 4 * bytesPerWord; + return 16; } +#else + private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) + { + // load A,B,C and D registers from in. + int A = (int)Pack.LE_To_UInt32(input, inOff); + int B = (int)Pack.LE_To_UInt32(input, inOff + 4); + int C = (int)Pack.LE_To_UInt32(input, inOff + 8); + int D = (int)Pack.LE_To_UInt32(input, inOff + 12); + // Do pseudo-round #0: pre-whitening of B and D + B += _S[0]; + D += _S[1]; - ////////////////////////////////////////////////////////////// - // - // PRIVATE Helper Methods - // - ////////////////////////////////////////////////////////////// + // perform round #1,#2 ... #ROUNDS of encryption + for (int i = 1; i <= _noRounds; i++) + { + int t = 0,u = 0; - /** - * Perform a left "spin" of the word. The rotation of the given - * word <em>x</em> is rotated left by <em>y</em> bits. - * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em> - * are used to determine the rotation amount. Here it is - * assumed that the wordsize used is a power of 2. - * - * @param x word to rotate - * @param y number of bits to rotate % wordSize - */ - private int RotateLeft(int x, int y) - { - return ((int)((uint)(x << (y & (wordSize-1))) - | ((uint) x >> (wordSize - (y & (wordSize-1)))))); - } + t = B*(2*B+1); + t = Integers.RotateLeft(t,5); - /** - * Perform a right "spin" of the word. The rotation of the given - * word <em>x</em> is rotated left by <em>y</em> bits. - * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em> - * are used to determine the rotation amount. Here it is - * assumed that the wordsize used is a power of 2. - * - * @param x word to rotate - * @param y number of bits to rotate % wordSize - */ - private int RotateRight(int x, int y) - { - return ((int)(((uint) x >> (y & (wordSize-1))) - | (uint)(x << (wordSize - (y & (wordSize-1)))))); - } + u = D*(2*D+1); + u = Integers.RotateLeft(u,5); - private int BytesToWord( - byte[] src, - int srcOff) - { - int word = 0; + A ^= t; + A = Integers.RotateLeft(A,u); + A += _S[2*i]; - for (int i = bytesPerWord - 1; i >= 0; i--) - { - word = (word << 8) + (src[i + srcOff] & 0xff); + C ^= u; + C = Integers.RotateLeft(C,t); + C += _S[2*i+1]; + + int temp = A; + A = B; + B = C; + C = D; + D = temp; } - return word; + // do pseudo-round #(ROUNDS+1) : post-whitening of A and C + A += _S[2*_noRounds+2]; + C += _S[2*_noRounds+3]; + + // store A, B, C and D registers to out + Pack.UInt32_To_LE((uint)A, outBytes, outOff); + Pack.UInt32_To_LE((uint)B, outBytes, outOff + 4); + Pack.UInt32_To_LE((uint)C, outBytes, outOff + 8); + Pack.UInt32_To_LE((uint)D, outBytes, outOff + 12); + + return 16; } - private void WordToBytes( - int word, - byte[] dst, - int dstOff) + private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - for (int i = 0; i < bytesPerWord; i++) + // load A,B,C and D registers from out. + int A = (int)Pack.LE_To_UInt32(input, inOff); + int B = (int)Pack.LE_To_UInt32(input, inOff + 4); + int C = (int)Pack.LE_To_UInt32(input, inOff + 8); + int D = (int)Pack.LE_To_UInt32(input, inOff + 12); + + // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C + C -= _S[2*_noRounds+3]; + A -= _S[2*_noRounds+2]; + + // Undo round #ROUNDS, .., #2,#1 of encryption + for (int i = _noRounds; i >= 1; i--) { - dst[i + dstOff] = (byte)word; - word = (int) ((uint) word >> 8); + int t=0,u = 0; + + int temp = D; + D = C; + C = B; + B = A; + A = temp; + + t = B*(2*B+1); + t = Integers.RotateLeft(t, LGW); + + u = D*(2*D+1); + u = Integers.RotateLeft(u, LGW); + + C -= _S[2*i+1]; + C = Integers.RotateRight(C,t); + C ^= u; + + A -= _S[2*i]; + A = Integers.RotateRight(A,u); + A ^= t; } + + // Undo pseudo-round #0: pre-whitening of B and D + D -= _S[1]; + B -= _S[0]; + + Pack.UInt32_To_LE((uint)A, outBytes, outOff); + Pack.UInt32_To_LE((uint)B, outBytes, outOff + 4); + Pack.UInt32_To_LE((uint)C, outBytes, outOff + 8); + Pack.UInt32_To_LE((uint)D, outBytes, outOff + 12); + + return 16; } +#endif } } diff --git a/crypto/src/crypto/engines/RijndaelEngine.cs b/crypto/src/crypto/engines/RijndaelEngine.cs index 7025cb5dc..2bd404973 100644 --- a/crypto/src/crypto/engines/RijndaelEngine.cs +++ b/crypto/src/crypto/engines/RijndaelEngine.cs @@ -601,11 +601,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BC / 2; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("Rijndael engine not initialised"); @@ -613,7 +609,11 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, (BC / 2), "input buffer too short"); Check.OutputLength(output, outOff, (BC / 2), "output buffer too short"); - UnPackBlock(input, inOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + UnPackBlock(input.AsSpan(inOff)); +#else + UnPackBlock(input, inOff); +#endif if (forEncryption) { @@ -624,20 +624,80 @@ namespace Org.BouncyCastle.Crypto.Engines DecryptBlock(workingKey); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + PackBlock(output.AsSpan(outOff)); +#else PackBlock(output, outOff); +#endif return BC / 2; } - public virtual void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { + if (workingKey == null) + throw new InvalidOperationException("Rijndael engine not initialised"); + + Check.DataLength(input, (BC / 2), "input buffer too short"); + Check.OutputLength(output, (BC / 2), "output buffer too short"); + + UnPackBlock(input); + + if (forEncryption) + { + EncryptBlock(workingKey); + } + else + { + DecryptBlock(workingKey); + } + + PackBlock(output); + + return BC / 2; } +#endif - private void UnPackBlock( - byte[] bytes, - int off) + public virtual void Reset() + { + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void UnPackBlock(ReadOnlySpan<byte> input) + { + int index = 0; + + A0 = (long)(input[index++] & 0xff); + A1 = (long)(input[index++] & 0xff); + A2 = (long)(input[index++] & 0xff); + A3 = (long)(input[index++] & 0xff); + + for (int j = 8; j != BC; j += 8) + { + A0 |= (long)(input[index++] & 0xff) << j; + A1 |= (long)(input[index++] & 0xff) << j; + A2 |= (long)(input[index++] & 0xff) << j; + A3 |= (long)(input[index++] & 0xff) << j; + } + } + + private void PackBlock(Span<byte> output) + { + int index = 0; + + for (int j = 0; j != BC; j += 8) + { + output[index++] = (byte)(A0 >> j); + output[index++] = (byte)(A1 >> j); + output[index++] = (byte)(A2 >> j); + output[index++] = (byte)(A3 >> j); + } + } +#else + private void UnPackBlock(byte[] bytes, int off) { - int index = off; + int index = off; A0 = (long)(bytes[index++] & 0xff); A1 = (long)(bytes[index++] & 0xff); @@ -653,11 +713,9 @@ namespace Org.BouncyCastle.Crypto.Engines } } - private void PackBlock( - byte[] bytes, - int off) + private void PackBlock(byte[] bytes, int off) { - int index = off; + int index = off; for (int j = 0; j != BC; j += 8) { @@ -667,8 +725,9 @@ namespace Org.BouncyCastle.Crypto.Engines bytes[index++] = (byte)(A3 >> j); } } +#endif - private void EncryptBlock( + private void EncryptBlock( long[][] rk) { int r; diff --git a/crypto/src/crypto/engines/SEEDEngine.cs b/crypto/src/crypto/engines/SEEDEngine.cs index d4142c867..6b511e4cc 100644 --- a/crypto/src/crypto/engines/SEEDEngine.cs +++ b/crypto/src/crypto/engines/SEEDEngine.cs @@ -1,5 +1,7 @@ using System; + using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Engines { @@ -168,12 +170,10 @@ namespace Org.BouncyCastle.Crypto.Engines private int[] wKey; private bool forEncryption; - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { this.forEncryption = forEncryption; - wKey = createWorkingKey(((KeyParameter)parameters).GetKey()); + wKey = CreateWorkingKey(((KeyParameter)parameters).GetKey()); } public virtual string AlgorithmName @@ -191,11 +191,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } - public virtual int ProcessBlock( - byte[] inBuf, - int inOff, - byte[] outBuf, - int outOff) + public virtual int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) { if (wKey == null) throw new InvalidOperationException("SEED engine not initialised"); @@ -203,8 +199,47 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(inBuf, inOff, BlockSize, "input buffer too short"); Check.OutputLength(outBuf, outOff, BlockSize, "output buffer too short"); - long l = bytesToLong(inBuf, inOff + 0); - long r = bytesToLong(inBuf, inOff + 8); + long l = (long)Pack.BE_To_UInt64(inBuf, inOff + 0); + long r = (long)Pack.BE_To_UInt64(inBuf, inOff + 8); + + if (forEncryption) + { + for (int i = 0; i < 16; i++) + { + long nl = r; + + r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r); + l = nl; + } + } + else + { + for (int i = 15; i >= 0; i--) + { + long nl = r; + + r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r); + l = nl; + } + } + + Pack.UInt64_To_BE((ulong)r, outBuf, outOff + 0); + Pack.UInt64_To_BE((ulong)l, outBuf, outOff + 8); + + return BlockSize; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (wKey == null) + throw new InvalidOperationException("SEED engine not initialised"); + + Check.DataLength(input, BlockSize, "input buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); + + long l = (long)Pack.BE_To_UInt64(input); + long r = (long)Pack.BE_To_UInt64(input[8..]); if (forEncryption) { @@ -227,25 +262,25 @@ namespace Org.BouncyCastle.Crypto.Engines } } - longToBytes(outBuf, outOff + 0, r); - longToBytes(outBuf, outOff + 8, l); + Pack.UInt64_To_BE((ulong)r, output); + Pack.UInt64_To_BE((ulong)l, output[8..]); return BlockSize; } +#endif - public virtual void Reset() + public virtual void Reset() { } - private int[] createWorkingKey( - byte[] inKey) + private int[] CreateWorkingKey(byte[] inKey) { if (inKey.Length != 16) throw new ArgumentException("key size must be 128 bits"); int[] key = new int[32]; - long lower = bytesToLong(inKey, 0); - long upper = bytesToLong(inKey, 8); + long lower = (long)Pack.BE_To_UInt64(inKey, 0); + long upper = (long)Pack.BE_To_UInt64(inKey, 8); int key0 = extractW0(lower); int key1 = extractW1(lower); @@ -298,31 +333,6 @@ namespace Org.BouncyCastle.Crypto.Engines return ((long)((ulong) x >> 8)) | (x << 56); } - private long bytesToLong( - byte[] src, - int srcOff) - { - long word = 0; - - for (int i = 0; i <= 7; i++) - { - word = (word << 8) + (src[i + srcOff] & 0xff); - } - - return word; - } - - private void longToBytes( - byte[] dest, - int destOff, - long value) - { - for (int i = 0; i < 8; i++) - { - dest[i + destOff] = (byte)(value >> ((7 - i) * 8)); - } - } - private int G( int x) { diff --git a/crypto/src/crypto/engines/SM4Engine.cs b/crypto/src/crypto/engines/SM4Engine.cs index 7477b070e..6a7206a01 100644 --- a/crypto/src/crypto/engines/SM4Engine.cs +++ b/crypto/src/crypto/engines/SM4Engine.cs @@ -182,6 +182,37 @@ namespace Org.BouncyCastle.Crypto.Engines return BlockSize; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (null == rk) + throw new InvalidOperationException("SM4 not initialised"); + + Check.DataLength(input, BlockSize, "input buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); + + uint X0 = Pack.BE_To_UInt32(input); + uint X1 = Pack.BE_To_UInt32(input[4..]); + uint X2 = Pack.BE_To_UInt32(input[8..]); + uint X3 = Pack.BE_To_UInt32(input[12..]); + + for (int i = 0; i < 32; i += 4) + { + X0 ^= T(X1 ^ X2 ^ X3 ^ rk[i ]); // F0 + X1 ^= T(X2 ^ X3 ^ X0 ^ rk[i + 1]); // F1 + X2 ^= T(X3 ^ X0 ^ X1 ^ rk[i + 2]); // F2 + X3 ^= T(X0 ^ X1 ^ X2 ^ rk[i + 3]); // F3 + } + + Pack.UInt32_To_BE(X3, output); + Pack.UInt32_To_BE(X2, output[4..]); + Pack.UInt32_To_BE(X1, output[8..]); + Pack.UInt32_To_BE(X0, output[12..]); + + return BlockSize; + } +#endif + public virtual void Reset() { } diff --git a/crypto/src/crypto/engines/SerpentEngine.cs b/crypto/src/crypto/engines/SerpentEngine.cs index 76799f045..00473fa0a 100644 --- a/crypto/src/crypto/engines/SerpentEngine.cs +++ b/crypto/src/crypto/engines/SerpentEngine.cs @@ -150,14 +150,130 @@ namespace Org.BouncyCastle.Crypto.Engines return w; } - /** - * Encrypt one block of plaintext. - * - * @param input the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param output the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + X0 = (int)Pack.LE_To_UInt32(input); + X1 = (int)Pack.LE_To_UInt32(input[4..]); + X2 = (int)Pack.LE_To_UInt32(input[8..]); + X3 = (int)Pack.LE_To_UInt32(input[12..]); + + Sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT(); + Sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT(); + Sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT(); + Sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT(); + Sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT(); + Sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT(); + Sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT(); + Sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT(); + Sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT(); + Sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT(); + Sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT(); + Sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT(); + Sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT(); + Sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT(); + Sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT(); + Sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT(); + Sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT(); + Sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT(); + Sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT(); + Sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT(); + Sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT(); + Sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT(); + Sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT(); + Sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT(); + Sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT(); + Sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT(); + Sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT(); + Sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT(); + Sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT(); + Sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT(); + Sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT(); + Sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3); + + Pack.UInt32_To_LE((uint)(wKey[128] ^ X0), output); + Pack.UInt32_To_LE((uint)(wKey[129] ^ X1), output[4..]); + Pack.UInt32_To_LE((uint)(wKey[130] ^ X2), output[8..]); + Pack.UInt32_To_LE((uint)(wKey[131] ^ X3), output[12..]); + } + + protected override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + X0 = wKey[128] ^ (int)Pack.LE_To_UInt32(input); + X1 = wKey[129] ^ (int)Pack.LE_To_UInt32(input[4..]); + X2 = wKey[130] ^ (int)Pack.LE_To_UInt32(input[8..]); + X3 = wKey[131] ^ (int)Pack.LE_To_UInt32(input[12..]); + + Ib7(X0, X1, X2, X3); + X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7]; + InverseLT(); Ib0(X0, X1, X2, X3); + + Pack.UInt32_To_LE((uint)(X0 ^ wKey[0]), output); + Pack.UInt32_To_LE((uint)(X1 ^ wKey[1]), output[4..]); + Pack.UInt32_To_LE((uint)(X2 ^ wKey[2]), output[8..]); + Pack.UInt32_To_LE((uint)(X3 ^ wKey[3]), output[12..]); + } +#else protected override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff) { X0 = (int)Pack.LE_To_UInt32(input, inOff); @@ -204,14 +320,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_LE((uint)(wKey[131] ^ X3), output, outOff + 12); } - /** - * Decrypt one block of ciphertext. - * - * @param input the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param output the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - */ protected override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff) { X0 = wKey[128] ^ (int)Pack.LE_To_UInt32(input, inOff); @@ -288,5 +396,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_LE((uint)(X2 ^ wKey[2]), output, outOff + 8); Pack.UInt32_To_LE((uint)(X3 ^ wKey[3]), output, outOff + 12); } +#endif } } diff --git a/crypto/src/crypto/engines/SerpentEngineBase.cs b/crypto/src/crypto/engines/SerpentEngineBase.cs index 9de552233..8ddbc4b6f 100644 --- a/crypto/src/crypto/engines/SerpentEngineBase.cs +++ b/crypto/src/crypto/engines/SerpentEngineBase.cs @@ -75,6 +75,16 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BlockSize, "input buffer too short"); Check.OutputLength(output, outOff, BlockSize, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (encrypting) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else if (encrypting) { EncryptBlock(input, inOff, output, outOff); @@ -83,9 +93,32 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff); } +#endif + + return BlockSize; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (wKey == null) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.DataLength(input, BlockSize, "input buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, output); + } + else + { + DecryptBlock(input, output); + } return BlockSize; } +#endif public virtual void Reset() { @@ -462,8 +495,12 @@ namespace Org.BouncyCastle.Crypto.Engines protected abstract int[] MakeWorkingKey(byte[] key); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected abstract void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output); + protected abstract void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output); +#else protected abstract void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff); - protected abstract void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff); +#endif } } diff --git a/crypto/src/crypto/engines/SkipjackEngine.cs b/crypto/src/crypto/engines/SkipjackEngine.cs index c90646cc4..e78111abd 100644 --- a/crypto/src/crypto/engines/SkipjackEngine.cs +++ b/crypto/src/crypto/engines/SkipjackEngine.cs @@ -87,11 +87,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (key1 == null) throw new InvalidOperationException("SKIPJACK engine not initialised"); @@ -99,6 +95,16 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (encrypting) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else if (encrypting) { EncryptBlock(input, inOff, output, outOff); @@ -107,10 +113,33 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff); } +#endif return BLOCK_SIZE; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (key1 == null) + throw new InvalidOperationException("SKIPJACK engine not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, output); + } + else + { + DecryptBlock(input, output); + } + + return BLOCK_SIZE; + } +#endif + public virtual void Reset() { } @@ -135,11 +164,97 @@ namespace Org.BouncyCastle.Crypto.Engines return ((g5 << 8) + g6); } - public virtual int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int w1 = (input[0] << 8) + (input[1] & 0xff); + int w2 = (input[2] << 8) + (input[3] & 0xff); + int w3 = (input[4] << 8) + (input[5] & 0xff); + int w4 = (input[6] << 8) + (input[7] & 0xff); + + int k = 0; + + for (int t = 0; t < 2; t++) + { + for (int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w2; + w2 = G(k, w1); + w1 = w2 ^ tmp ^ (k + 1); + k++; + } + + for (int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w1 ^ w2 ^ (k + 1); + w2 = G(k, w1); + w1 = tmp; + k++; + } + } + + output[0] = (byte)((w1 >> 8)); + output[1] = (byte)(w1); + output[2] = (byte)((w2 >> 8)); + output[3] = (byte)(w2); + output[4] = (byte)((w3 >> 8)); + output[5] = (byte)(w3); + output[6] = (byte)((w4 >> 8)); + output[7] = (byte)(w4); + + return BLOCK_SIZE; + } + + public virtual int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int w2 = (input[0] << 8) + (input[1] & 0xff); + int w1 = (input[2] << 8) + (input[3] & 0xff); + int w4 = (input[4] << 8) + (input[5] & 0xff); + int w3 = (input[6] << 8) + (input[7] & 0xff); + + int k = 31; + + for (int t = 0; t < 2; t++) + { + for (int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w2; + w2 = H(k, w1); + w1 = w2 ^ tmp ^ (k + 1); + k--; + } + + for (int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w1 ^ w2 ^ (k + 1); + w2 = H(k, w1); + w1 = tmp; + k--; + } + } + + output[0] = (byte)((w2 >> 8)); + output[1] = (byte)(w2); + output[2] = (byte)((w1 >> 8)); + output[3] = (byte)(w1); + output[4] = (byte)((w4 >> 8)); + output[5] = (byte)(w4); + output[6] = (byte)((w3 >> 8)); + output[7] = (byte)(w3); + + return BLOCK_SIZE; + } + +#else + public virtual int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { int w1 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff); int w2 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff); @@ -183,31 +298,7 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } - /** - * the inverse of the G permutation. - */ - private int H( - int k, - int w) - { - int h1, h2, h3, h4, h5, h6; - - h1 = w & 0xff; - h2 = (w >> 8) & 0xff; - - h3 = ftable[h2 ^ key3[k]] ^ h1; - h4 = ftable[h3 ^ key2[k]] ^ h2; - h5 = ftable[h4 ^ key1[k]] ^ h3; - h6 = ftable[h5 ^ key0[k]] ^ h4; - - return ((h6 << 8) + h5); - } - - public virtual int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + public virtual int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { int w2 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff); int w1 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff); @@ -218,7 +309,7 @@ namespace Org.BouncyCastle.Crypto.Engines for (int t = 0; t < 2; t++) { - for(int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { int tmp = w4; w4 = w3; @@ -228,7 +319,7 @@ namespace Org.BouncyCastle.Crypto.Engines k--; } - for(int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { int tmp = w4; w4 = w3; @@ -250,5 +341,22 @@ namespace Org.BouncyCastle.Crypto.Engines return BLOCK_SIZE; } +#endif + + /** + * the inverse of the G permutation. + */ + private int H(int k, int w) + { + int h1 = w & 0xff; + int h2 = (w >> 8) & 0xff; + + int h3 = ftable[h2 ^ key3[k]] ^ h1; + int h4 = ftable[h3 ^ key2[k]] ^ h2; + int h5 = ftable[h4 ^ key1[k]] ^ h3; + int h6 = ftable[h5 ^ key0[k]] ^ h4; + + return (h6 << 8) + h5; + } } } diff --git a/crypto/src/crypto/engines/TEAEngine.cs b/crypto/src/crypto/engines/TEAEngine.cs index 7b700145e..bb6ae6dcc 100644 --- a/crypto/src/crypto/engines/TEAEngine.cs +++ b/crypto/src/crypto/engines/TEAEngine.cs @@ -60,11 +60,9 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException if the params argument is * inappropriate. */ - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (!(parameters is KeyParameter)) + if (!(parameters is KeyParameter keyParameter)) { throw new ArgumentException("invalid parameter passed to TEA init - " + Platform.GetTypeName(parameters)); @@ -73,16 +71,10 @@ namespace Org.BouncyCastle.Crypto.Engines _forEncryption = forEncryption; _initialised = true; - KeyParameter p = (KeyParameter) parameters; - - setKey(p.GetKey()); + SetKey(keyParameter.GetKey()); } - public virtual int ProcessBlock( - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) + public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { if (!_initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); @@ -90,12 +82,33 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(inBytes, inOff, block_size, "input buffer too short"); Check.OutputLength(outBytes, outOff, block_size, "output buffer too short"); - return _forEncryption - ? encryptBlock(inBytes, inOff, outBytes, outOff) - : decryptBlock(inBytes, inOff, outBytes, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return _forEncryption + ? EncryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)) + : DecryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)); +#else + return _forEncryption + ? EncryptBlock(inBytes, inOff, outBytes, outOff) + : DecryptBlock(inBytes, inOff, outBytes, outOff); +#endif } - public virtual void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.DataLength(input, block_size, "input buffer too short"); + Check.OutputLength(output, block_size, "output buffer too short"); + + return _forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); + } +#endif + + public virtual void Reset() { } @@ -104,8 +117,7 @@ namespace Org.BouncyCastle.Crypto.Engines * * @param key the key to be used */ - private void setKey( - byte[] key) + private void SetKey(byte[] key) { _a = Pack.BE_To_UInt32(key, 0); _b = Pack.BE_To_UInt32(key, 4); @@ -113,18 +125,57 @@ namespace Org.BouncyCastle.Crypto.Engines _d = Pack.BE_To_UInt32(key, 12); } - private int encryptBlock( - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(input); + uint v1 = Pack.BE_To_UInt32(input[4..]); + + uint sum = 0; + + for (int i = 0; i != rounds; i++) + { + sum += delta; + v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >> 5) + _b); + v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >> 5) + _d); + } + + Pack.UInt32_To_BE(v0, output); + Pack.UInt32_To_BE(v1, output[4..]); + + return block_size; + } + + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(input); + uint v1 = Pack.BE_To_UInt32(input[4..]); + + uint sum = d_sum; + + for (int i = 0; i != rounds; i++) + { + v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >> 5) + _d); + v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >> 5) + _b); + sum -= delta; + } + + Pack.UInt32_To_BE(v0, output); + Pack.UInt32_To_BE(v1, output[4..]); + + return block_size; + } +#else + private int EncryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { // Pack bytes into integers uint v0 = Pack.BE_To_UInt32(inBytes, inOff); uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4); - + uint sum = 0; - + for (int i = 0; i != rounds; i++) { sum += delta; @@ -138,11 +189,7 @@ namespace Org.BouncyCastle.Crypto.Engines return block_size; } - private int decryptBlock( - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { // Pack bytes into integers uint v0 = Pack.BE_To_UInt32(inBytes, inOff); @@ -162,5 +209,6 @@ namespace Org.BouncyCastle.Crypto.Engines return block_size; } +#endif } } diff --git a/crypto/src/crypto/engines/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs index c5aee5395..c22691fc2 100644 --- a/crypto/src/crypto/engines/ThreefishEngine.cs +++ b/crypto/src/crypto/engines/ThreefishEngine.cs @@ -285,15 +285,8 @@ namespace Org.BouncyCastle.Crypto.Engines public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { - if ((outOff + blocksizeBytes) > outBytes.Length) - { - throw new DataLengthException("Output buffer too short"); - } - - if ((inOff + blocksizeBytes) > inBytes.Length) - { - throw new DataLengthException("Input buffer too short"); - } + Check.DataLength(inBytes, inOff, blocksizeBytes, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blocksizeBytes, "output buffer too short"); Pack.LE_To_UInt64(inBytes, inOff, currentBlock); ProcessBlock(this.currentBlock, this.currentBlock); @@ -301,6 +294,19 @@ namespace Org.BouncyCastle.Crypto.Engines return blocksizeBytes; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blocksizeBytes, "input buffer too short"); + Check.OutputLength(output, blocksizeBytes, "output buffer too short"); + + Pack.LE_To_UInt64(input, currentBlock); + ProcessBlock(this.currentBlock, this.currentBlock); + Pack.UInt64_To_LE(currentBlock, output); + return blocksizeBytes; + } +#endif + /// <summary> /// Process a block of data represented as 64 bit words. /// </summary> @@ -317,13 +323,9 @@ namespace Org.BouncyCastle.Crypto.Engines } if (inWords.Length != blocksizeWords) - { - throw new DataLengthException("Input buffer too short"); - } + throw new DataLengthException("input buffer too short"); if (outWords.Length != blocksizeWords) - { - throw new DataLengthException("Output buffer too short"); - } + throw new OutputLengthException("output buffer too short"); if (forEncryption) { diff --git a/crypto/src/crypto/engines/TnepresEngine.cs b/crypto/src/crypto/engines/TnepresEngine.cs index ce687d1e5..cb008a182 100644 --- a/crypto/src/crypto/engines/TnepresEngine.cs +++ b/crypto/src/crypto/engines/TnepresEngine.cs @@ -157,14 +157,130 @@ namespace Org.BouncyCastle.Crypto.Engines return w; } - /** - * Encrypt one block of plaintext. - * - * @param input the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param output the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + X3 = (int)Pack.BE_To_UInt32(input); + X2 = (int)Pack.BE_To_UInt32(input[4..]); + X1 = (int)Pack.BE_To_UInt32(input[8..]); + X0 = (int)Pack.BE_To_UInt32(input[12..]); + + Sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT(); + Sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT(); + Sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT(); + Sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT(); + Sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT(); + Sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT(); + Sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT(); + Sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT(); + Sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT(); + Sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT(); + Sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT(); + Sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT(); + Sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT(); + Sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT(); + Sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT(); + Sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT(); + Sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT(); + Sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT(); + Sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT(); + Sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT(); + Sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT(); + Sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT(); + Sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT(); + Sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT(); + Sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT(); + Sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT(); + Sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT(); + Sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT(); + Sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT(); + Sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT(); + Sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT(); + Sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3); + + Pack.UInt32_To_BE((uint)(wKey[131] ^ X3), output); + Pack.UInt32_To_BE((uint)(wKey[130] ^ X2), output[4..]); + Pack.UInt32_To_BE((uint)(wKey[129] ^ X1), output[8..]); + Pack.UInt32_To_BE((uint)(wKey[128] ^ X0), output[12..]); + } + + protected override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + X3 = wKey[131] ^ (int)Pack.BE_To_UInt32(input); + X2 = wKey[130] ^ (int)Pack.BE_To_UInt32(input[4..]); + X1 = wKey[129] ^ (int)Pack.BE_To_UInt32(input[8..]); + X0 = wKey[128] ^ (int)Pack.BE_To_UInt32(input[12..]); + + Ib7(X0, X1, X2, X3); + X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7]; + InverseLT(); Ib0(X0, X1, X2, X3); + + Pack.UInt32_To_BE((uint)(X3 ^ wKey[3]), output); + Pack.UInt32_To_BE((uint)(X2 ^ wKey[2]), output[4..]); + Pack.UInt32_To_BE((uint)(X1 ^ wKey[1]), output[8..]); + Pack.UInt32_To_BE((uint)(X0 ^ wKey[0]), output[12..]); + } +#else protected override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff) { X3 = (int)Pack.BE_To_UInt32(input, inOff); @@ -211,14 +327,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_BE((uint)(wKey[128] ^ X0), output, outOff + 12); } - /** - * Decrypt one block of ciphertext. - * - * @param input the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param output the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - */ protected override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff) { X3 = wKey[131] ^ (int)Pack.BE_To_UInt32(input, inOff); @@ -295,5 +403,6 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_BE((uint)(X1 ^ wKey[1]), output, outOff + 8); Pack.UInt32_To_BE((uint)(X0 ^ wKey[0]), output, outOff + 12); } +#endif } } diff --git a/crypto/src/crypto/engines/TwofishEngine.cs b/crypto/src/crypto/engines/TwofishEngine.cs index 0758451e4..cb3e35b0a 100644 --- a/crypto/src/crypto/engines/TwofishEngine.cs +++ b/crypto/src/crypto/engines/TwofishEngine.cs @@ -299,11 +299,7 @@ namespace Org.BouncyCastle.Crypto.Engines get { return false; } } - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("Twofish not initialised"); @@ -311,6 +307,16 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short"); Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (encrypting) + { + EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } + else + { + DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); + } +#else if (encrypting) { EncryptBlock(input, inOff, output, outOff); @@ -319,9 +325,32 @@ namespace Org.BouncyCastle.Crypto.Engines { DecryptBlock(input, inOff, output, outOff); } +#endif + + return BLOCK_SIZE; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (workingKey == null) + throw new InvalidOperationException("Twofish not initialised"); + + Check.DataLength(input, BLOCK_SIZE, "input buffer too short"); + Check.OutputLength(output, BLOCK_SIZE, "output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, output); + } + else + { + DecryptBlock(input, output); + } return BLOCK_SIZE; } +#endif public void Reset() { @@ -424,6 +453,80 @@ namespace Org.BouncyCastle.Crypto.Engines */ } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + * + * encryptBlock uses the pre-calculated gSBox[] and subKey[] + * arrays. + */ + private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int x0 = (int)Pack.LE_To_UInt32(input) ^ gSubKeys[INPUT_WHITEN]; + int x1 = (int)Pack.LE_To_UInt32(input[4..]) ^ gSubKeys[INPUT_WHITEN + 1]; + int x2 = (int)Pack.LE_To_UInt32(input[8..]) ^ gSubKeys[INPUT_WHITEN + 2]; + int x3 = (int)Pack.LE_To_UInt32(input[12..]) ^ gSubKeys[INPUT_WHITEN + 3]; + + int k = ROUND_SUBKEYS; + int t0, t1; + for (int r = 0; r < ROUNDS; r +=2) + { + t0 = Fe32_0(x0); + t1 = Fe32_3(x1); + x2 ^= t0 + t1 + gSubKeys[k++]; + x2 = Integers.RotateRight(x2, 1); + x3 = Integers.RotateLeft(x3, 1) ^ (t0 + 2*t1 + gSubKeys[k++]); + + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x0 ^= t0 + t1 + gSubKeys[k++]; + x0 = Integers.RotateRight(x0, 1); + x1 = Integers.RotateLeft(x1, 1) ^ (t0 + 2*t1 + gSubKeys[k++]); + } + + Pack.UInt32_To_LE((uint)(x2 ^ gSubKeys[OUTPUT_WHITEN]), output); + Pack.UInt32_To_LE((uint)(x3 ^ gSubKeys[OUTPUT_WHITEN + 1]), output[4..]); + Pack.UInt32_To_LE((uint)(x0 ^ gSubKeys[OUTPUT_WHITEN + 2]), output[8..]); + Pack.UInt32_To_LE((uint)(x1 ^ gSubKeys[OUTPUT_WHITEN + 3]), output[12..]); + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int x2 = (int)Pack.LE_To_UInt32(input) ^ gSubKeys[OUTPUT_WHITEN]; + int x3 = (int)Pack.LE_To_UInt32(input[4..]) ^ gSubKeys[OUTPUT_WHITEN + 1]; + int x0 = (int)Pack.LE_To_UInt32(input[8..]) ^ gSubKeys[OUTPUT_WHITEN + 2]; + int x1 = (int)Pack.LE_To_UInt32(input[12..]) ^ gSubKeys[OUTPUT_WHITEN + 3]; + + int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ; + int t0, t1; + for (int r = 0; r< ROUNDS ; r +=2) + { + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x1 ^= t0 + 2*t1 + gSubKeys[k--]; + x0 = Integers.RotateLeft(x0, 1) ^ (t0 + t1 + gSubKeys[k--]); + x1 = Integers.RotateRight(x1, 1); + + t0 = Fe32_0(x0); + t1 = Fe32_3(x1); + x3 ^= t0 + 2*t1 + gSubKeys[k--]; + x2 = Integers.RotateLeft(x2, 1) ^ (t0 + t1 + gSubKeys[k--]); + x3 = Integers.RotateRight(x3, 1); + } + + Pack.UInt32_To_LE((uint)(x0 ^ gSubKeys[INPUT_WHITEN]), output); + Pack.UInt32_To_LE((uint)(x1 ^ gSubKeys[INPUT_WHITEN + 1]), output[4..]); + Pack.UInt32_To_LE((uint)(x2 ^ gSubKeys[INPUT_WHITEN + 2]), output[8..]); + Pack.UInt32_To_LE((uint)(x3 ^ gSubKeys[INPUT_WHITEN + 3]), output[12..]); + } +#else /** * Encrypt the given input starting at the given offset and place * the result in the provided buffer starting at the given offset. @@ -432,11 +535,7 @@ namespace Org.BouncyCastle.Crypto.Engines * encryptBlock uses the pre-calculated gSBox[] and subKey[] * arrays. */ - private void EncryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) + private void EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { int x0 = (int)Pack.LE_To_UInt32(src, srcIndex) ^ gSubKeys[INPUT_WHITEN]; int x1 = (int)Pack.LE_To_UInt32(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1]; @@ -471,11 +570,7 @@ namespace Org.BouncyCastle.Crypto.Engines * the result in the provided buffer starting at the given offset. * The input will be an exact multiple of our blocksize. */ - private void DecryptBlock( - byte[] src, - int srcIndex, - byte[] dst, - int dstIndex) + private void DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex) { int x2 = (int)Pack.LE_To_UInt32(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN]; int x3 = (int)Pack.LE_To_UInt32(src, srcIndex + 4) ^ gSubKeys[OUTPUT_WHITEN + 1]; @@ -484,7 +579,7 @@ namespace Org.BouncyCastle.Crypto.Engines int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ; int t0, t1; - for (int r = 0; r< ROUNDS ; r +=2) + for (int r = 0; r < ROUNDS ; r += 2) { t0 = Fe32_0(x2); t1 = Fe32_3(x3); @@ -504,6 +599,7 @@ namespace Org.BouncyCastle.Crypto.Engines Pack.UInt32_To_LE((uint)(x2 ^ gSubKeys[INPUT_WHITEN + 2]), dst, dstIndex + 8); Pack.UInt32_To_LE((uint)(x3 ^ gSubKeys[INPUT_WHITEN + 3]), dst, dstIndex + 12); } +#endif /* * TODO: This can be optimised and made cleaner by combining diff --git a/crypto/src/crypto/engines/XTEAEngine.cs b/crypto/src/crypto/engines/XTEAEngine.cs index 5fcfa4a57..e70498a5f 100644 --- a/crypto/src/crypto/engines/XTEAEngine.cs +++ b/crypto/src/crypto/engines/XTEAEngine.cs @@ -76,11 +76,7 @@ namespace Org.BouncyCastle.Crypto.Engines setKey(p.GetKey()); } - public virtual int ProcessBlock( - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) + public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { if (!_initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); @@ -88,12 +84,33 @@ namespace Org.BouncyCastle.Crypto.Engines Check.DataLength(inBytes, inOff, block_size, "input buffer too short"); Check.OutputLength(outBytes, outOff, block_size, "output buffer too short"); - return _forEncryption - ? encryptBlock(inBytes, inOff, outBytes, outOff) - : decryptBlock(inBytes, inOff, outBytes, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return _forEncryption + ? EncryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)) + : DecryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)); +#else + return _forEncryption + ? EncryptBlock(inBytes, inOff, outBytes, outOff) + : DecryptBlock(inBytes, inOff, outBytes, outOff); +#endif } - public virtual void Reset() +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.DataLength(input, block_size, "input buffer too short"); + Check.OutputLength(output, block_size, "output buffer too short"); + + return _forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); + } +#endif + + public virtual void Reset() { } @@ -119,13 +136,45 @@ namespace Org.BouncyCastle.Crypto.Engines } } - private int encryptBlock( - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(input); + uint v1 = Pack.BE_To_UInt32(input[4..]); + + for (int i = 0; i < rounds; i++) + { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i]; + } + + Pack.UInt32_To_BE(v0, output); + Pack.UInt32_To_BE(v1, output[4..]); + + return block_size; + } + + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(input); + uint v1 = Pack.BE_To_UInt32(input[4..]); + + for (int i = rounds - 1; i >= 0; i--) + { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i]; + } + + Pack.UInt32_To_BE(v0, output); + Pack.UInt32_To_BE(v1, output[4..]); + + return block_size; + } +#else + private int EncryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { uint v0 = Pack.BE_To_UInt32(inBytes, inOff); uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4); @@ -141,11 +190,7 @@ namespace Org.BouncyCastle.Crypto.Engines return block_size; } - private int decryptBlock( - byte[] inBytes, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { // Pack bytes into integers uint v0 = Pack.BE_To_UInt32(inBytes, inOff); @@ -162,5 +207,6 @@ namespace Org.BouncyCastle.Crypto.Engines return block_size; } +#endif } } diff --git a/crypto/src/crypto/macs/CMac.cs b/crypto/src/crypto/macs/CMac.cs index 682c12bac..342dbd93d 100644 --- a/crypto/src/crypto/macs/CMac.cs +++ b/crypto/src/crypto/macs/CMac.cs @@ -132,8 +132,7 @@ namespace Org.BouncyCastle.Crypto.Macs return ret; } - public void Init( - ICipherParameters parameters) + public void Init(ICipherParameters parameters) { if (parameters is KeyParameter) { @@ -159,8 +158,7 @@ namespace Org.BouncyCastle.Crypto.Macs return macSize; } - public void Update( - byte input) + public void Update(byte input) { if (bufOff == buf.Length) { @@ -171,14 +169,14 @@ namespace Org.BouncyCastle.Crypto.Macs buf[bufOff++] = input; } - public void BlockUpdate( - byte[] inBytes, - int inOff, - int len) + public void BlockUpdate(byte[] inBytes, int inOff, int len) { if (len < 0) throw new ArgumentException("Can't have a negative input length!"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(inBytes.AsSpan(inOff, len)); +#else int blockSize = cipher.GetBlockSize(); int gapLen = blockSize - bufOff; @@ -204,13 +202,43 @@ namespace Org.BouncyCastle.Crypto.Macs Array.Copy(inBytes, inOff, buf, bufOff, len); bufOff += len; +#endif } - public int DoFinal( - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { int blockSize = cipher.GetBlockSize(); + int gapLen = blockSize - bufOff; + + if (input.Length > gapLen) + { + input[..gapLen].CopyTo(buf.AsSpan(bufOff)); + + cipher.ProcessBlock(buf, mac); + + bufOff = 0; + input = input[gapLen..]; + + while (input.Length > blockSize) + { + cipher.ProcessBlock(input, mac); + input = input[blockSize..]; + } + } + + input.CopyTo(buf.AsSpan(bufOff)); + + bufOff += input.Length; + } +#endif + + public int DoFinal(byte[] outBytes, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(outBytes.AsSpan(outOff)); +#else + int blockSize = cipher.GetBlockSize(); byte[] lu; if (bufOff == blockSize) @@ -235,7 +263,39 @@ namespace Org.BouncyCastle.Crypto.Macs Reset(); return macSize; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + int blockSize = cipher.GetBlockSize(); + + byte[] lu; + if (bufOff == blockSize) + { + lu = Lu; + } + else + { + new ISO7816d4Padding().AddPadding(buf, bufOff); + lu = Lu2; + } + + for (int i = 0; i < mac.Length; i++) + { + buf[i] ^= lu[i]; + } + + cipher.ProcessBlock(buf, mac); + + mac.AsSpan(0, macSize).CopyTo(output); + + Reset(); + + return macSize; } +#endif /** * Reset the mac generator. diff --git a/crypto/src/crypto/macs/CbcBlockCipherMac.cs b/crypto/src/crypto/macs/CbcBlockCipherMac.cs index 146e16aa8..abf06170c 100644 --- a/crypto/src/crypto/macs/CbcBlockCipherMac.cs +++ b/crypto/src/crypto/macs/CbcBlockCipherMac.cs @@ -99,8 +99,7 @@ namespace Org.BouncyCastle.Crypto.Macs get { return cipher.AlgorithmName; } } - public void Init( - ICipherParameters parameters) + public void Init(ICipherParameters parameters) { Reset(); @@ -112,8 +111,7 @@ namespace Org.BouncyCastle.Crypto.Macs return macSize; } - public void Update( - byte input) + public void Update(byte input) { if (bufOff == buf.Length) { @@ -124,15 +122,15 @@ namespace Org.BouncyCastle.Crypto.Macs buf[bufOff++] = input; } - public void BlockUpdate( - byte[] input, - int inOff, - int len) + public void BlockUpdate(byte[] input, int inOff, int len) { if (len < 0) throw new ArgumentException("Can't have a negative input length!"); - int blockSize = cipher.GetBlockSize(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, len)); +#else + int blockSize = cipher.GetBlockSize(); int gapLen = blockSize - bufOff; if (len > gapLen) @@ -157,13 +155,43 @@ namespace Org.BouncyCastle.Crypto.Macs Array.Copy(input, inOff, buf, bufOff, len); bufOff += len; +#endif } - public int DoFinal( - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { int blockSize = cipher.GetBlockSize(); + int gapLen = blockSize - bufOff; + + if (input.Length > gapLen) + { + input[..gapLen].CopyTo(buf.AsSpan(bufOff)); + + cipher.ProcessBlock(buf, buf); + + bufOff = 0; + input = input[gapLen..]; + + while (input.Length > blockSize) + { + cipher.ProcessBlock(input, buf); + input = input[blockSize..]; + } + } + + input.CopyTo(buf.AsSpan(bufOff)); + + bufOff += input.Length; + } +#endif + + public int DoFinal(byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else + int blockSize = cipher.GetBlockSize(); if (padding == null) { @@ -191,9 +219,44 @@ namespace Org.BouncyCastle.Crypto.Macs Reset(); return macSize; +#endif } - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + int blockSize = cipher.GetBlockSize(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + } + else + { + if (bufOff == blockSize) + { + cipher.ProcessBlock(buf, buf); + bufOff = 0; + } + + padding.AddPadding(buf, bufOff); + } + + cipher.ProcessBlock(buf, buf); + + buf.AsSpan(0, macSize).CopyTo(output); + + Reset(); + + return macSize; + } +#endif + + /** * Reset the mac generator. */ public void Reset() diff --git a/crypto/src/crypto/macs/CfbBlockCipherMac.cs b/crypto/src/crypto/macs/CfbBlockCipherMac.cs index 364cf8499..a4d005700 100644 --- a/crypto/src/crypto/macs/CfbBlockCipherMac.cs +++ b/crypto/src/crypto/macs/CfbBlockCipherMac.cs @@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Macs /** * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. */ - class MacCFBBlockCipher + internal class MacCfbBlockCipher : IBlockCipher { private byte[] IV; @@ -26,7 +26,7 @@ namespace Org.BouncyCastle.Crypto.Macs * feedback mode. * @param blockSize the block size in bits (note: a multiple of 8) */ - public MacCFBBlockCipher( + public MacCfbBlockCipher( IBlockCipher cipher, int bitBlockSize) { @@ -47,13 +47,10 @@ namespace Org.BouncyCastle.Crypto.Macs * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( - bool forEncryption, - ICipherParameters parameters) + public void Init(bool forEncryption, ICipherParameters parameters) { - if (parameters is ParametersWithIV) + if (parameters is ParametersWithIV ivParam) { - ParametersWithIV ivParam = (ParametersWithIV)parameters; byte[] iv = ivParam.GetIV(); if (iv.Length < IV.Length) @@ -99,30 +96,10 @@ namespace Org.BouncyCastle.Crypto.Macs return blockSize; } - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - if ((inOff + blockSize) > input.Length) - throw new DataLengthException("input buffer too short"); - - if ((outOff + blockSize) > outBytes.Length) - throw new DataLengthException("output buffer too short"); + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); @@ -143,7 +120,33 @@ namespace Org.BouncyCastle.Crypto.Macs return blockSize; } - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + cipher.ProcessBlock(cfbV, cfbOutV); + + // + // XOR the cfbV with the plaintext producing the cipher text + // + for (int i = 0; i < blockSize; i++) + { + output[i] = (byte)(cfbOutV[i] ^ input[i]); + } + + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + output[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize)); + + return blockSize; + } +#endif + + /** * reset the chaining vector back to the IV and reset the underlying * cipher. */ @@ -167,7 +170,7 @@ namespace Org.BouncyCastle.Crypto.Macs private byte[] mac; private byte[] Buffer; private int bufOff; - private MacCFBBlockCipher cipher; + private MacCfbBlockCipher cipher; private IBlockCipherPadding padding; private int macSize; @@ -247,7 +250,7 @@ namespace Org.BouncyCastle.Crypto.Macs mac = new byte[cipher.GetBlockSize()]; - this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize); + this.cipher = new MacCfbBlockCipher(cipher, cfbBitSize); this.padding = padding; this.macSize = macSizeInBits / 8; @@ -260,8 +263,7 @@ namespace Org.BouncyCastle.Crypto.Macs get { return cipher.AlgorithmName; } } - public void Init( - ICipherParameters parameters) + public void Init(ICipherParameters parameters) { Reset(); @@ -273,8 +275,7 @@ namespace Org.BouncyCastle.Crypto.Macs return macSize; } - public void Update( - byte input) + public void Update(byte input) { if (bufOff == Buffer.Length) { @@ -285,15 +286,15 @@ namespace Org.BouncyCastle.Crypto.Macs Buffer[bufOff++] = input; } - public void BlockUpdate( - byte[] input, - int inOff, - int len) + public void BlockUpdate(byte[] input, int inOff, int len) { if (len < 0) throw new ArgumentException("Can't have a negative input length!"); - int blockSize = cipher.GetBlockSize(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, len)); +#else + int blockSize = cipher.GetBlockSize(); int resultLen = 0; int gapLen = blockSize - bufOff; @@ -319,13 +320,44 @@ namespace Org.BouncyCastle.Crypto.Macs Array.Copy(input, inOff, Buffer, bufOff, len); bufOff += len; +#endif } - public int DoFinal( - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (input.Length > gapLen) + { + input[..gapLen].CopyTo(Buffer.AsSpan(bufOff)); + + resultLen += cipher.ProcessBlock(Buffer, mac); + + bufOff = 0; + input = input[gapLen..]; + + while (input.Length > blockSize) + { + resultLen += cipher.ProcessBlock(input, mac); + input = input[blockSize..]; + } + } + + input.CopyTo(Buffer.AsSpan(bufOff)); + + bufOff += input.Length; + } +#endif + + public int DoFinal(byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else + int blockSize = cipher.GetBlockSize(); // pad with zeroes if (this.padding == null) @@ -349,8 +381,39 @@ namespace Org.BouncyCastle.Crypto.Macs Reset(); return macSize; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + int blockSize = cipher.GetBlockSize(); + + // pad with zeroes + if (this.padding == null) + { + while (bufOff < blockSize) + { + Buffer[bufOff++] = 0; + } + } + else + { + padding.AddPadding(Buffer, bufOff); + } + + cipher.ProcessBlock(Buffer, 0, mac, 0); + + cipher.GetMacBlock(mac); + + mac.AsSpan(0, macSize).CopyTo(output); + + Reset(); + + return macSize; + } +#endif + /** * Reset the mac generator. */ @@ -364,5 +427,4 @@ namespace Org.BouncyCastle.Crypto.Macs cipher.Reset(); } } - } diff --git a/crypto/src/crypto/macs/DSTU7564Mac.cs b/crypto/src/crypto/macs/DSTU7564Mac.cs index 36e86418a..401d85a1e 100644 --- a/crypto/src/crypto/macs/DSTU7564Mac.cs +++ b/crypto/src/crypto/macs/DSTU7564Mac.cs @@ -61,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Macs public void BlockUpdate(byte[] input, int inOff, int len) { - Check.DataLength(input, inOff, len, "Input buffer too short"); + Check.DataLength(input, inOff, len, "input buffer too short"); if (paddedKey == null) throw new InvalidOperationException(AlgorithmName + " not initialised"); @@ -70,6 +70,17 @@ namespace Org.BouncyCastle.Crypto.Macs inputLength += (ulong)len; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + if (paddedKey == null) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + engine.BlockUpdate(input); + inputLength += (ulong)input.Length; + } +#endif + public void Update(byte input) { engine.Update(input); @@ -78,11 +89,11 @@ namespace Org.BouncyCastle.Crypto.Macs public int DoFinal(byte[] output, int outOff) { - Check.OutputLength(output, outOff, macSize, "Output buffer too short"); - if (paddedKey == null) throw new InvalidOperationException(AlgorithmName + " not initialised"); + Check.OutputLength(output, outOff, macSize, "output buffer too short"); + Pad(); engine.BlockUpdate(invertedKey, 0, invertedKey.Length); @@ -92,6 +103,24 @@ namespace Org.BouncyCastle.Crypto.Macs return engine.DoFinal(output, outOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + if (paddedKey == null) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.OutputLength(output, macSize, "output buffer too short"); + + Pad(); + + engine.BlockUpdate(invertedKey); + + inputLength = 0; + + return engine.DoFinal(output); + } +#endif + public void Reset() { inputLength = 0; diff --git a/crypto/src/crypto/macs/DSTU7624Mac.cs b/crypto/src/crypto/macs/DSTU7624Mac.cs index 953d8164f..8fecb1915 100644 --- a/crypto/src/crypto/macs/DSTU7624Mac.cs +++ b/crypto/src/crypto/macs/DSTU7624Mac.cs @@ -1,87 +1,89 @@ using System; -using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; - +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Macs { - /** + /** * implementation of DSTU 7624 MAC */ - public class Dstu7624Mac : IMac - { - private int macSize; - - private Dstu7624Engine engine; - private int blockSize; + public class Dstu7624Mac : IMac + { + private int macSize; + + private Dstu7624Engine engine; + private int blockSize; + + private byte[] c, cTemp, kDelta; + private byte[] buf; + private int bufOff; - private byte[] c, cTemp, kDelta; - private byte[] buf; - private int bufOff; + public Dstu7624Mac(int blockSizeBits, int q) + { + engine = new Dstu7624Engine(blockSizeBits); + + blockSize = blockSizeBits / 8; - public Dstu7624Mac(int blockSizeBits, int q) - { - engine = new Dstu7624Engine(blockSizeBits); + macSize = q / 8; - blockSize = blockSizeBits / 8; + c = new byte[blockSize]; - macSize = q / 8; + cTemp = new byte[blockSize]; - c = new byte[blockSize]; - - cTemp = new byte[blockSize]; + kDelta = new byte[blockSize]; + buf = new byte[blockSize]; + } + + public void Init(ICipherParameters parameters) + { + if (parameters is KeyParameter) + { + engine.Init(true, (KeyParameter)parameters); + + engine.ProcessBlock(kDelta, 0, kDelta, 0); + } + else + { + throw new ArgumentException("invalid parameter passed to Dstu7624Mac init - " + + Platform.GetTypeName(parameters)); + } + } + + public string AlgorithmName + { + get { return "Dstu7624Mac"; } + } - kDelta = new byte[blockSize]; - buf = new byte[blockSize]; + public int GetMacSize() + { + return macSize; } - public void Init(ICipherParameters parameters) - { - if (parameters is KeyParameter) - { - engine.Init(true, (KeyParameter)parameters); - - engine.ProcessBlock(kDelta, 0, kDelta, 0); - } - else - { - throw new ArgumentException("invalid parameter passed to Dstu7624Mac init - " - + Platform.GetTypeName(parameters)); - } - } - - public string AlgorithmName - { - get { return "Dstu7624Mac"; } - } - - public int GetMacSize() - { - return macSize; - } - - public void Update(byte input) - { + public void Update(byte input) + { if (bufOff == buf.Length) { - processBlock(buf, 0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(buf); +#else + ProcessBlock(buf, 0); +#endif bufOff = 0; } buf[bufOff++] = input; } - public void BlockUpdate(byte[] input, int inOff, int len) - { + public void BlockUpdate(byte[] input, int inOff, int len) + { if (len < 0) - { - throw new ArgumentException( - "Can't have a negative input length!"); - } + throw new ArgumentException("Can't have a negative input length!"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, len)); +#else int blockSize = engine.GetBlockSize(); int gapLen = blockSize - bufOff; @@ -89,7 +91,7 @@ namespace Org.BouncyCastle.Crypto.Macs { Array.Copy(input, inOff, buf, bufOff, gapLen); - processBlock(buf, 0); + ProcessBlock(buf, 0); bufOff = 0; len -= gapLen; @@ -97,7 +99,7 @@ namespace Org.BouncyCastle.Crypto.Macs while (len > blockSize) { - processBlock(input, inOff); + ProcessBlock(input, inOff); len -= blockSize; inOff += blockSize; @@ -107,47 +109,107 @@ namespace Org.BouncyCastle.Crypto.Macs Array.Copy(input, inOff, buf, bufOff, len); bufOff += len; +#endif } - private void processBlock(byte[] input, int inOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int blockSize = engine.GetBlockSize(); + int gapLen = blockSize - bufOff; + + if (input.Length > gapLen) + { + input[..gapLen].CopyTo(buf.AsSpan(bufOff)); + + ProcessBlock(buf); + + bufOff = 0; + input = input[gapLen..]; + + while (input.Length > blockSize) + { + ProcessBlock(input); + input = input[blockSize..]; + } + } + + input.CopyTo(buf.AsSpan(bufOff)); + + bufOff += input.Length; + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void ProcessBlock(ReadOnlySpan<byte> input) + { + Xor(c, input, cTemp); + + engine.ProcessBlock(cTemp, c); + } + + private void Xor(ReadOnlySpan<byte> c, ReadOnlySpan<byte> input, Span<byte> xorResult) + { + for (int byteIndex = 0; byteIndex < blockSize; byteIndex++) + { + xorResult[byteIndex] = (byte)(c[byteIndex] ^ input[byteIndex]); + } + } +#else + private void ProcessBlock(byte[] input, int inOff) { Xor(c, 0, input, inOff, cTemp); engine.ProcessBlock(cTemp, 0, c, 0); } +#endif private void Xor(byte[] c, int cOff, byte[] input, int inOff, byte[] xorResult) - { - for (int byteIndex = 0; byteIndex < blockSize; byteIndex++) - { - xorResult[byteIndex] = (byte)(c[byteIndex + cOff] ^ input[byteIndex + inOff]); - } - } - - public int DoFinal(byte[] output, int outOff) - { - if (bufOff % buf.Length != 0) + { + for (int byteIndex = 0; byteIndex < blockSize; byteIndex++) { - throw new DataLengthException("Input must be a multiple of blocksize"); + xorResult[byteIndex] = (byte)(c[byteIndex + cOff] ^ input[byteIndex + inOff]); } + } + + public int DoFinal(byte[] output, int outOff) + { + if (bufOff % buf.Length != 0) + throw new DataLengthException("Input must be a multiple of blocksize"); + + Check.OutputLength(output, outOff, macSize, "output buffer too short"); //Last block Xor(c, 0, buf, 0, cTemp); Xor(cTemp, 0, kDelta, 0, c); engine.ProcessBlock(c, 0, c, 0); - if (macSize + outOff > output.Length) - { - throw new DataLengthException("Output buffer too short"); - } - Array.Copy(c, 0, output, outOff, macSize); return macSize; } - public void Reset() - { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + if (bufOff % buf.Length != 0) + throw new DataLengthException("Input must be a multiple of blocksize"); + + Check.OutputLength(output, macSize, "output buffer too short"); + + //Last block + Xor(c, 0, buf, 0, cTemp); + Xor(cTemp, 0, kDelta, 0, c); + engine.ProcessBlock(c, c); + + c.AsSpan(0, macSize).CopyTo(output); + + return macSize; + } +#endif + + public void Reset() + { Arrays.Fill(c, (byte)0x00); Arrays.Fill(cTemp, (byte)0x00); Arrays.Fill(kDelta, (byte)0x00); @@ -156,5 +218,5 @@ namespace Org.BouncyCastle.Crypto.Macs engine.ProcessBlock(kDelta, 0, kDelta, 0); bufOff = 0; } - } + } } diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs index 0554c44f0..aa124bb04 100644 --- a/crypto/src/crypto/macs/GMac.cs +++ b/crypto/src/crypto/macs/GMac.cs @@ -52,10 +52,8 @@ namespace Org.BouncyCastle.Crypto.Macs /// </summary> public void Init(ICipherParameters parameters) { - if (parameters is ParametersWithIV) + if (parameters is ParametersWithIV param) { - ParametersWithIV param = (ParametersWithIV)parameters; - byte[] iv = param.GetIV(); KeyParameter keyParam = (KeyParameter)param.Parameters; @@ -88,6 +86,13 @@ namespace Org.BouncyCastle.Crypto.Macs cipher.ProcessAadBytes(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + cipher.ProcessAadBytes(input); + } +#endif + public int DoFinal(byte[] output, int outOff) { try @@ -101,6 +106,17 @@ namespace Org.BouncyCastle.Crypto.Macs } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + // TODO[span] call cipher.DoFinal(Span<byte) when available + byte[] tmp = new byte[GetMacSize()]; + int result = DoFinal(tmp, 0); + tmp.CopyTo(output); + return result; + } +#endif + public void Reset() { cipher.Reset(); diff --git a/crypto/src/crypto/macs/GOST28147Mac.cs b/crypto/src/crypto/macs/GOST28147Mac.cs index 33c2d67ee..8c39fc6b0 100644 --- a/crypto/src/crypto/macs/GOST28147Mac.cs +++ b/crypto/src/crypto/macs/GOST28147Mac.cs @@ -1,7 +1,7 @@ using System; -using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Macs @@ -9,10 +9,11 @@ namespace Org.BouncyCastle.Crypto.Macs /** * implementation of GOST 28147-89 MAC */ - public class Gost28147Mac : IMac + public class Gost28147Mac + : IMac { - private const int blockSize = 8; - private const int macSize = 4; + private const int BlockSize = 8; + private const int MacSize = 4; private int bufOff; private byte[] buf; private byte[] mac; @@ -36,8 +37,8 @@ namespace Org.BouncyCastle.Crypto.Macs public Gost28147Mac() { - mac = new byte[blockSize]; - buf = new byte[blockSize]; + mac = new byte[BlockSize]; + buf = new byte[BlockSize]; bufOff = 0; } @@ -50,22 +51,19 @@ namespace Org.BouncyCastle.Crypto.Macs int[] key = new int[8]; for(int i=0; i!=8; i++) { - key[i] = bytesToint(userKey,i*4); + key[i] = (int)Pack.LE_To_UInt32(userKey, i * 4); } return key; } - public void Init( - ICipherParameters parameters) + public void Init(ICipherParameters parameters) { Reset(); - buf = new byte[blockSize]; + buf = new byte[BlockSize]; macIV = null; - if (parameters is ParametersWithSBox) + if (parameters is ParametersWithSBox param) { - ParametersWithSBox param = (ParametersWithSBox)parameters; - // // Set the S-Box // @@ -79,17 +77,15 @@ namespace Org.BouncyCastle.Crypto.Macs workingKey = GenerateWorkingKey(((KeyParameter)param.Parameters).GetKey()); } } - else if (parameters is KeyParameter) + else if (parameters is KeyParameter keyParameter) { - workingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey()); + workingKey = GenerateWorkingKey(keyParameter.GetKey()); } - else if (parameters is ParametersWithIV) + else if (parameters is ParametersWithIV ivParam) { - ParametersWithIV p = (ParametersWithIV)parameters; - - workingKey = GenerateWorkingKey(((KeyParameter)p.Parameters).GetKey()); - Array.Copy(p.GetIV(), 0, mac, 0, mac.Length); - macIV = p.GetIV(); // don't skip the initial CM5Func + workingKey = GenerateWorkingKey(((KeyParameter)ivParam.Parameters).GetKey()); + macIV = ivParam.GetIV(); // don't skip the initial CM5Func + Array.Copy(macIV, 0, mac, 0, mac.Length); } else { @@ -105,10 +101,10 @@ namespace Org.BouncyCastle.Crypto.Macs public int GetMacSize() { - return macSize; + return MacSize; } - private int gost28147_mainStep(int n1, int key) + private int Gost28147_mainStep(int n1, int key) { int cm = (key + n1); // CM1 @@ -130,178 +126,222 @@ namespace Org.BouncyCastle.Crypto.Macs return omLeft | omRight; } - private void gost28147MacFunc( + private void Gost28147MacFunc( int[] workingKey, byte[] input, int inOff, byte[] output, int outOff) { - int N1, N2, tmp; //tmp -> for saving N1 - N1 = bytesToint(input, inOff); - N2 = bytesToint(input, inOff + 4); + int N1 = (int)Pack.LE_To_UInt32(input, inOff); + int N2 = (int)Pack.LE_To_UInt32(input, inOff + 4); + int tmp; //tmp -> for saving N1 for (int k = 0; k < 2; k++) // 1-16 steps { for (int j = 0; j < 8; j++) { tmp = N1; - N1 = N2 ^ gost28147_mainStep(N1, workingKey[j]); // CM2 + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 N2 = tmp; } } - intTobytes(N1, output, outOff); - intTobytes(N2, output, outOff + 4); - } - - //array of bytes to type int - private static int bytesToint( - byte[] input, - int inOff) - { - return (int)((input[inOff + 3] << 24) & 0xff000000) + ((input[inOff + 2] << 16) & 0xff0000) - + ((input[inOff + 1] << 8) & 0xff00) + (input[inOff] & 0xff); - } - - //int to array of bytes - private static void intTobytes( - int num, - byte[] output, - int outOff) - { - output[outOff + 3] = (byte)(num >> 24); - output[outOff + 2] = (byte)(num >> 16); - output[outOff + 1] = (byte)(num >> 8); - output[outOff] = (byte)num; - } - - private static byte[] CM5func( - byte[] buf, - int bufOff, - byte[] mac) - { - byte[] sum = new byte[buf.Length - bufOff]; - - Array.Copy(buf, bufOff, sum, 0, mac.Length); - - for (int i = 0; i != mac.Length; i++) - { - sum[i] = (byte)(sum[i] ^ mac[i]); - } - - return sum; + Pack.UInt32_To_LE((uint)N1, output, outOff); + Pack.UInt32_To_LE((uint)N2, output, outOff + 4); } - public void Update( - byte input) + public void Update(byte input) { if (bufOff == buf.Length) { - byte[] sumbuf = new byte[buf.Length]; - Array.Copy(buf, 0, sumbuf, 0, mac.Length); - + byte[] sum = new byte[buf.Length]; if (firstStep) { firstStep = false; if (macIV != null) { - sumbuf = CM5func(buf, 0, macIV); + Cm5Func(buf, 0, macIV, sum); } - } + else + { + Array.Copy(buf, 0, sum, 0, mac.Length); + } + } else { - sumbuf = CM5func(buf, 0, mac); + Cm5Func(buf, 0, mac, sum); } - gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + Gost28147MacFunc(workingKey, sum, 0, mac, 0); bufOff = 0; } buf[bufOff++] = input; } - public void BlockUpdate( - byte[] input, - int inOff, - int len) + public void BlockUpdate(byte[] input, int inOff, int len) { if (len < 0) throw new ArgumentException("Can't have a negative input length!"); - int gapLen = blockSize - bufOff; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, len)); +#else + int gapLen = BlockSize - bufOff; if (len > gapLen) { Array.Copy(input, inOff, buf, bufOff, gapLen); - byte[] sumbuf = new byte[buf.Length]; - Array.Copy(buf, 0, sumbuf, 0, mac.Length); - + byte[] sum = new byte[buf.Length]; if (firstStep) { firstStep = false; if (macIV != null) { - sumbuf = CM5func(buf, 0, macIV); + Cm5Func(buf, 0, macIV, sum); } - } + else + { + Array.Copy(buf, 0, sum, 0, mac.Length); + } + } else { - sumbuf = CM5func(buf, 0, mac); + Cm5Func(buf, 0, mac, sum); } - gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + Gost28147MacFunc(workingKey, sum, 0, mac, 0); bufOff = 0; len -= gapLen; inOff += gapLen; - while (len > blockSize) + while (len > BlockSize) { - sumbuf = CM5func(input, inOff, mac); - gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + Cm5Func(input, inOff, mac, sum); + Gost28147MacFunc(workingKey, sum, 0, mac, 0); - len -= blockSize; - inOff += blockSize; + len -= BlockSize; + inOff += BlockSize; } } Array.Copy(input, inOff, buf, bufOff, len); bufOff += len; +#endif } - public int DoFinal( - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { + int gapLen = BlockSize - bufOff; + + if (input.Length > gapLen) + { + input[..gapLen].CopyTo(buf.AsSpan(bufOff)); + + byte[] sum = new byte[buf.Length]; + if (firstStep) + { + firstStep = false; + if (macIV != null) + { + Cm5Func(buf, macIV, sum); + } + else + { + Array.Copy(buf, 0, sum, 0, mac.Length); + } + } + else + { + Cm5Func(buf, mac, sum); + } + + Gost28147MacFunc(workingKey, sum, 0, mac, 0); + + bufOff = 0; + input = input[gapLen..]; + + while (input.Length > BlockSize) + { + Cm5Func(input, mac, sum); + Gost28147MacFunc(workingKey, sum, 0, mac, 0); + + input = input[BlockSize..]; + } + } + + input.CopyTo(buf.AsSpan(bufOff)); + + bufOff += input.Length; + } +#endif + + public int DoFinal(byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else //padding with zero - while (bufOff < blockSize) + while (bufOff < BlockSize) { buf[bufOff++] = 0; } - byte[] sumbuf = new byte[buf.Length]; - Array.Copy(buf, 0, sumbuf, 0, mac.Length); + byte[] sum = new byte[buf.Length]; + if (firstStep) + { + firstStep = false; + Array.Copy(buf, 0, sum, 0, mac.Length); + } + else + { + Cm5Func(buf, 0, mac, sum); + } + + Gost28147MacFunc(workingKey, sum, 0, mac, 0); + Array.Copy(mac, (mac.Length/2)-MacSize, output, outOff, MacSize); + + Reset(); + + return MacSize; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + //padding with zero + while (bufOff < BlockSize) + { + buf[bufOff++] = 0; + } + + byte[] sum = new byte[buf.Length]; if (firstStep) { firstStep = false; + Array.Copy(buf, 0, sum, 0, mac.Length); } else { - sumbuf = CM5func(buf, 0, mac); + Cm5Func(buf, 0, mac, sum); } - gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + Gost28147MacFunc(workingKey, sum, 0, mac, 0); - Array.Copy(mac, (mac.Length/2)-macSize, output, outOff, macSize); + mac.AsSpan((mac.Length / 2) - MacSize, MacSize).CopyTo(output); Reset(); - return macSize; + return MacSize; } +#endif public void Reset() { @@ -311,5 +351,23 @@ namespace Org.BouncyCastle.Crypto.Macs firstStep = true; } + + private static void Cm5Func(byte[] buf, int bufOff, byte[] mac, byte[] sum) + { + for (int i = 0; i < BlockSize; ++i) + { + sum[i] = (byte)(buf[bufOff + i] ^ mac[i]); + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Cm5Func(ReadOnlySpan<byte> buffer, ReadOnlySpan<byte> mac, Span<byte> sum) + { + for (int i = 0; i < BlockSize; ++i) + { + sum[i] = (byte)(buffer[i] ^ mac[i]); + } + } +#endif } } diff --git a/crypto/src/crypto/macs/HMac.cs b/crypto/src/crypto/macs/HMac.cs index a717ce4f7..3445a945b 100644 --- a/crypto/src/crypto/macs/HMac.cs +++ b/crypto/src/crypto/macs/HMac.cs @@ -99,14 +99,24 @@ namespace Org.BouncyCastle.Crypto.Macs digest.BlockUpdate(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } +#endif + public virtual int DoFinal(byte[] output, int outOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else digest.DoFinal(outputBuf, blockLength); if (opadState != null) { ((IMemoable)digest).Reset(opadState); - digest.BlockUpdate(outputBuf, blockLength, digest.GetDigestSize()); + digest.BlockUpdate(outputBuf, blockLength, digestSize); } else { @@ -127,7 +137,40 @@ namespace Org.BouncyCastle.Crypto.Macs } return len; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + digest.DoFinal(outputBuf.AsSpan(blockLength)); + + if (opadState != null) + { + ((IMemoable)digest).Reset(opadState); + digest.BlockUpdate(outputBuf.AsSpan(blockLength, digestSize)); + } + else + { + digest.BlockUpdate(outputBuf); + } + + int len = digest.DoFinal(output); + + Array.Clear(outputBuf, blockLength, digestSize); + + if (ipadState != null) + { + ((IMemoable)digest).Reset(ipadState); + } + else + { + digest.BlockUpdate(inputPad); + } + + return len; } +#endif /** * Reset the mac generator. diff --git a/crypto/src/crypto/macs/ISO9797Alg3Mac.cs b/crypto/src/crypto/macs/ISO9797Alg3Mac.cs index 6fee619c1..40a68007e 100644 --- a/crypto/src/crypto/macs/ISO9797Alg3Mac.cs +++ b/crypto/src/crypto/macs/ISO9797Alg3Mac.cs @@ -180,14 +180,14 @@ namespace Org.BouncyCastle.Crypto.Macs buf[bufOff++] = input; } - public void BlockUpdate( - byte[] input, - int inOff, - int len) + public void BlockUpdate(byte[] input, int inOff, int len) { if (len < 0) throw new ArgumentException("Can't have a negative input length!"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, len)); +#else int blockSize = cipher.GetBlockSize(); int resultLen = 0; int gapLen = blockSize - bufOff; @@ -214,13 +214,44 @@ namespace Org.BouncyCastle.Crypto.Macs Array.Copy(input, inOff, buf, bufOff, len); bufOff += len; +#endif } - public int DoFinal( - byte[] output, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) { int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (input.Length > gapLen) + { + input[..gapLen].CopyTo(buf.AsSpan(bufOff)); + + resultLen += cipher.ProcessBlock(buf, mac); + + bufOff = 0; + input = input[gapLen..]; + + while (input.Length > blockSize) + { + resultLen += cipher.ProcessBlock(input, mac); + input = input[blockSize..]; + } + } + + input.CopyTo(buf.AsSpan(bufOff)); + + bufOff += input.Length; + } +#endif + + public int DoFinal(byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else + int blockSize = cipher.GetBlockSize(); if (padding == null) { @@ -258,7 +289,52 @@ namespace Org.BouncyCastle.Crypto.Macs Reset(); return macSize; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + int blockSize = cipher.GetBlockSize(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + } + else + { + if (bufOff == blockSize) + { + cipher.ProcessBlock(buf, mac); + bufOff = 0; + } + + padding.AddPadding(buf, bufOff); + } + + cipher.ProcessBlock(buf, mac); + + // Added to code from base class + DesEngine deseng = new DesEngine(); + + deseng.Init(false, this.lastKey2); + deseng.ProcessBlock(mac, mac); + + deseng.Init(true, this.lastKey3); + deseng.ProcessBlock(mac, mac); + // **** + + mac.AsSpan(0, macSize).CopyTo(output); + + Reset(); + + return macSize; } +#endif /** * Reset the mac generator. diff --git a/crypto/src/crypto/macs/KMac.cs b/crypto/src/crypto/macs/KMac.cs index 05031ac2f..ce6c9f701 100644 --- a/crypto/src/crypto/macs/KMac.cs +++ b/crypto/src/crypto/macs/KMac.cs @@ -39,6 +39,16 @@ namespace Org.BouncyCastle.Crypto.Macs cshake.BlockUpdate(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + cshake.BlockUpdate(input); + } +#endif + public int DoFinal(byte[] output, int outOff) { if (firstOutput) @@ -58,6 +68,27 @@ namespace Org.BouncyCastle.Crypto.Macs return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = XofUtilities.RightEncode(GetMacSize() * 8, lengthEncoding); + cshake.BlockUpdate(lengthEncoding[..count]); + } + + int rv = cshake.OutputFinal(output[..GetMacSize()]); + + Reset(); + + return rv; + } +#endif + public int DoFinal(byte[] output, int outOff, int outLen) { if (firstOutput) @@ -77,6 +108,27 @@ namespace Org.BouncyCastle.Crypto.Macs return rv; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int OutputFinal(Span<byte> output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = XofUtilities.RightEncode(output.Length * 8, lengthEncoding); + cshake.BlockUpdate(lengthEncoding[..count]); + } + + int rv = cshake.OutputFinal(output); + + Reset(); + + return rv; + } +#endif + public int DoOutput(byte[] output, int outOff, int outLen) { if (firstOutput) @@ -94,6 +146,25 @@ namespace Org.BouncyCastle.Crypto.Macs return cshake.DoOutput(output, outOff, outLen); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Output(Span<byte> output) + { + if (firstOutput) + { + if (!initialised) + throw new InvalidOperationException("KMAC not initialized"); + + Span<byte> lengthEncoding = stackalloc byte[9]; + int count = XofUtilities.RightEncode(0, lengthEncoding); + cshake.BlockUpdate(lengthEncoding[..count]); + + firstOutput = false; + } + + return cshake.Output(output); + } +#endif + public int GetByteLength() { return cshake.GetByteLength(); diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs index 595d9b051..eb90e387e 100644 --- a/crypto/src/crypto/macs/Poly1305.cs +++ b/crypto/src/crypto/macs/Poly1305.cs @@ -167,15 +167,22 @@ namespace Org.BouncyCastle.Crypto.Macs currentBlock[currentBlockOffset++] = input; if (currentBlockOffset == BlockSize) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(currentBlock); +#else ProcessBlock(currentBlock, 0); +#endif currentBlockOffset = 0; } } public void BlockUpdate(byte[] input, int inOff, int len) { - // TODO Validity check on arguments + Check.DataLength(input, inOff, len, "input buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, len)); +#else int available = BlockSize - currentBlockOffset; if (len < available) { @@ -189,37 +196,108 @@ namespace Org.BouncyCastle.Crypto.Macs { Array.Copy(input, inOff, currentBlock, currentBlockOffset, available); pos = available; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(currentBlock); +#else ProcessBlock(currentBlock, 0); +#endif } int remaining; while ((remaining = len - pos) >= BlockSize) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(input.AsSpan(inOff + pos)); +#else ProcessBlock(input, inOff + pos); +#endif pos += BlockSize; } Array.Copy(input, inOff + pos, currentBlock, 0, remaining); currentBlockOffset = remaining; +#endif } - private void ProcessBlock(byte[] buf, int off) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + int available = BlockSize - currentBlockOffset; + if (input.Length < available) + { + input.CopyTo(currentBlock.AsSpan(currentBlockOffset)); + currentBlockOffset += input.Length; + return; + } + + int pos = 0; + if (currentBlockOffset > 0) + { + input[..available].CopyTo(currentBlock.AsSpan(currentBlockOffset)); + pos = available; + ProcessBlock(currentBlock); + } + + int remaining; + while ((remaining = input.Length - pos) >= BlockSize) + { + ProcessBlock(input[pos..]); + pos += BlockSize; + } + + input[pos..].CopyTo(currentBlock); + currentBlockOffset = remaining; + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void ProcessBlock(ReadOnlySpan<byte> block) { #if NETCOREAPP3_0_OR_GREATER if (BitConverter.IsLittleEndian) { Span<uint> t = stackalloc uint[4]; - Unsafe.CopyBlockUnaligned(ref Unsafe.As<uint, byte>(ref t[0]), ref buf[off], 16); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<uint, byte>(ref t[0]), ref Unsafe.AsRef(block[0]), 16); h0 += t[0] & 0x3ffffffU; h1 += ((t[1] << 6) | (t[0] >> 26)) & 0x3ffffffU; h2 += ((t[2] << 12) | (t[1] >> 20)) & 0x3ffffffU; h3 += ((t[3] << 18) | (t[2] >> 14)) & 0x3ffffffU; - h4 += (1 << 24) | (t[3] >> 8); + h4 += (1 << 24) | (t[3] >> 8); } else #endif { + uint t0 = Pack.LE_To_UInt32(block); + uint t1 = Pack.LE_To_UInt32(block[4..]); + uint t2 = Pack.LE_To_UInt32(block[8..]); + uint t3 = Pack.LE_To_UInt32(block[12..]); + + h0 += t0 & 0x3ffffffU; + h1 += ((t1 << 6) | (t0 >> 26)) & 0x3ffffffU; + h2 += ((t2 << 12) | (t1 >> 20)) & 0x3ffffffU; + h3 += ((t3 << 18) | (t2 >> 14)) & 0x3ffffffU; + h4 += ( 1 << 24) | (t3 >> 8); + } + + ulong tp0 = (ulong)h0 * r0 + (ulong)h1 * s4 + (ulong)h2 * s3 + (ulong)h3 * s2 + (ulong)h4 * s1; + ulong tp1 = (ulong)h0 * r1 + (ulong)h1 * r0 + (ulong)h2 * s4 + (ulong)h3 * s3 + (ulong)h4 * s2; + ulong tp2 = (ulong)h0 * r2 + (ulong)h1 * r1 + (ulong)h2 * r0 + (ulong)h3 * s4 + (ulong)h4 * s3; + ulong tp3 = (ulong)h0 * r3 + (ulong)h1 * r2 + (ulong)h2 * r1 + (ulong)h3 * r0 + (ulong)h4 * s4; + ulong tp4 = (ulong)h0 * r4 + (ulong)h1 * r3 + (ulong)h2 * r2 + (ulong)h3 * r1 + (ulong)h4 * r0; + + h0 = (uint)tp0 & 0x3ffffff; tp1 += (tp0 >> 26); + h1 = (uint)tp1 & 0x3ffffff; tp2 += (tp1 >> 26); + h2 = (uint)tp2 & 0x3ffffff; tp3 += (tp2 >> 26); + h3 = (uint)tp3 & 0x3ffffff; tp4 += (tp3 >> 26); + h4 = (uint)tp4 & 0x3ffffff; + h0 += (uint)(tp4 >> 26) * 5; + h1 += h0 >> 26; h0 &= 0x3ffffff; + } +#else + private void ProcessBlock(byte[] buf, int off) + { + { uint t0 = Pack.LE_To_UInt32(buf, off + 0); uint t1 = Pack.LE_To_UInt32(buf, off + 4); uint t2 = Pack.LE_To_UInt32(buf, off + 8); @@ -246,10 +324,14 @@ namespace Org.BouncyCastle.Crypto.Macs h0 += (uint)(tp4 >> 26) * 5; h1 += h0 >> 26; h0 &= 0x3ffffff; } +#endif public int DoFinal(byte[] output, int outOff) { - Check.DataLength(output, outOff, BlockSize, "Output buffer is too short."); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else + Check.OutputLength(output, outOff, BlockSize, "output buffer is too short."); if (currentBlockOffset > 0) { @@ -289,7 +371,54 @@ namespace Org.BouncyCastle.Crypto.Macs Reset(); return BlockSize; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + Check.OutputLength(output, BlockSize, "output buffer is too short."); + + if (currentBlockOffset > 0) + { + // Process padded block + if (currentBlockOffset < BlockSize) + { + currentBlock[currentBlockOffset++] = 1; + while (currentBlockOffset < BlockSize) + { + currentBlock[currentBlockOffset++] = 0; + } + + h4 -= (1 << 24); + } + + ProcessBlock(currentBlock); + } + + Debug.Assert(h4 >> 26 == 0); + + //h0 += (h4 >> 26) * 5U + 5U; h4 &= 0x3ffffff; + h0 += 5U; + h1 += h0 >> 26; h0 &= 0x3ffffff; + h2 += h1 >> 26; h1 &= 0x3ffffff; + h3 += h2 >> 26; h2 &= 0x3ffffff; + h4 += h3 >> 26; h3 &= 0x3ffffff; + + long c = ((int)(h4 >> 26) - 1) * 5; + c += (long)k0 + ((h0) | (h1 << 26)); + Pack.UInt32_To_LE((uint)c, output); c >>= 32; + c += (long)k1 + ((h1 >> 6) | (h2 << 20)); + Pack.UInt32_To_LE((uint)c, output[4..]); c >>= 32; + c += (long)k2 + ((h2 >> 12) | (h3 << 14)); + Pack.UInt32_To_LE((uint)c, output[8..]); c >>= 32; + c += (long)k3 + ((h3 >> 18) | (h4 << 8)); + Pack.UInt32_To_LE((uint)c, output[12..]); + + Reset(); + return BlockSize; } +#endif public void Reset() { diff --git a/crypto/src/crypto/macs/SipHash.cs b/crypto/src/crypto/macs/SipHash.cs index e1a19fa5b..2e8a89e3d 100644 --- a/crypto/src/crypto/macs/SipHash.cs +++ b/crypto/src/crypto/macs/SipHash.cs @@ -80,6 +80,9 @@ namespace Org.BouncyCastle.Crypto.Macs public virtual void BlockUpdate(byte[] input, int offset, int length) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(offset, length)); +#else int i = 0, fullWords = length & ~7; if (wordPos == 0) { @@ -115,8 +118,51 @@ namespace Org.BouncyCastle.Crypto.Macs } } } +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + int length = input.Length; + int i = 0, fullWords = length & ~7; + if (wordPos == 0) + { + for (; i < fullWords; i += 8) + { + m = (long)Pack.LE_To_UInt64(input[i..]); + ProcessMessageWord(); + } + for (; i < length; ++i) + { + m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56)); + } + wordPos = length - fullWords; + } + else + { + int bits = wordPos << 3; + for (; i < fullWords; i += 8) + { + ulong n = Pack.LE_To_UInt64(input[i..]); + m = (long)((n << bits) | ((ulong)m >> -bits)); + ProcessMessageWord(); + m = (long)n; + } + for (; i < length; ++i) + { + m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56)); + + if (++wordPos == 8) + { + ProcessMessageWord(); + wordPos = 0; + } + } + } + } +#endif + public virtual long DoFinal() { // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0 @@ -144,6 +190,15 @@ namespace Org.BouncyCastle.Crypto.Macs return 8; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + long result = DoFinal(); + Pack.UInt64_To_LE((ulong)result, output); + return 8; + } +#endif + public virtual void Reset() { v0 = k0 ^ 0x736f6d6570736575L; diff --git a/crypto/src/crypto/macs/SkeinMac.cs b/crypto/src/crypto/macs/SkeinMac.cs index 07eff24f4..aaf5b312d 100644 --- a/crypto/src/crypto/macs/SkeinMac.cs +++ b/crypto/src/crypto/macs/SkeinMac.cs @@ -106,13 +106,26 @@ namespace Org.BouncyCastle.Crypto.Macs public void BlockUpdate(byte[] input, int inOff, int len) { - engine.Update(input, inOff, len); + engine.BlockUpdate(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + engine.BlockUpdate(input); + } +#endif + public int DoFinal(byte[] output, int outOff) { return engine.DoFinal(output, outOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + return engine.DoFinal(output); + } +#endif } } diff --git a/crypto/src/crypto/macs/VMPCMac.cs b/crypto/src/crypto/macs/VMPCMac.cs index 6f2da075c..c2902179f 100644 --- a/crypto/src/crypto/macs/VMPCMac.cs +++ b/crypto/src/crypto/macs/VMPCMac.cs @@ -22,6 +22,9 @@ namespace Org.BouncyCastle.Crypto.Macs public virtual int DoFinal(byte[] output, int outOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else // Execute the Post-Processing Phase for (int r = 1; r < 25; r++) { @@ -68,8 +71,61 @@ namespace Org.BouncyCastle.Crypto.Macs Reset(); return M.Length; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int DoFinal(Span<byte> output) + { + // Execute the Post-Processing Phase + for (int r = 1; r < 25; r++) + { + s = P[(s + P[n & 0xff]) & 0xff]; + + x4 = P[(x4 + x3 + r) & 0xff]; + x3 = P[(x3 + x2 + r) & 0xff]; + x2 = P[(x2 + x1 + r) & 0xff]; + x1 = P[(x1 + s + r) & 0xff]; + T[g & 0x1f] = (byte)(T[g & 0x1f] ^ x1); + T[(g + 1) & 0x1f] = (byte)(T[(g + 1) & 0x1f] ^ x2); + T[(g + 2) & 0x1f] = (byte)(T[(g + 2) & 0x1f] ^ x3); + T[(g + 3) & 0x1f] = (byte)(T[(g + 3) & 0x1f] ^ x4); + g = (byte)((g + 4) & 0x1f); + + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte)((n + 1) & 0xff); + } + + // Input T to the IV-phase of the VMPC KSA + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + // Store 20 new outputs of the VMPC Stream Cipher input table M + byte[] M = new byte[20]; + for (int i = 0; i < 20; i++) + { + s = P[(s + P[i & 0xff]) & 0xff]; + M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + + byte temp = P[i & 0xff]; + P[i & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + M.CopyTo(output); + Reset(); + + return M.Length; + } +#endif + public virtual string AlgorithmName { get { return "VMPC-MAC"; } @@ -159,15 +215,24 @@ namespace Org.BouncyCastle.Crypto.Macs n = (byte) ((n + 1) & 0xff); } - public virtual void BlockUpdate(byte[] input, int inOff, int len) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { - if ((inOff + len) > input.Length) - throw new DataLengthException("input buffer too short"); + Check.DataLength(input, inOff, inLen, "input buffer too short"); - for (int i = 0; i < len; i++) + for (int i = 0; i < inLen; i++) { Update(input[inOff + i]); } } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + for (int i = 0; i < input.Length; i++) + { + Update(input[i]); + } + } +#endif } } diff --git a/crypto/src/crypto/modes/CbcBlockCipher.cs b/crypto/src/crypto/modes/CbcBlockCipher.cs index 9345fd8c2..eb89c81ee 100644 --- a/crypto/src/crypto/modes/CbcBlockCipher.cs +++ b/crypto/src/crypto/modes/CbcBlockCipher.cs @@ -59,15 +59,12 @@ namespace Org.BouncyCastle.Crypto.Modes this.encrypting = forEncryption; - if (parameters is ParametersWithIV) + if (parameters is ParametersWithIV ivParam) { - ParametersWithIV ivParam = (ParametersWithIV)parameters; - byte[] iv = ivParam.GetIV(); + byte[] iv = ivParam.GetIV(); if (iv.Length != blockSize) - { throw new ArgumentException("initialisation vector must be the same length as block size"); - } Array.Copy(iv, 0, IV, 0, iv.Length); @@ -112,29 +109,27 @@ namespace Org.BouncyCastle.Crypto.Modes return cipher.GetBlockSize(); } - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return encrypting + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return encrypting + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { - return (encrypting) - ? EncryptBlock(input, inOff, output, outOff) - : DecryptBlock(input, inOff, output, outOff); + return encrypting + ? EncryptBlock(input, output) + : DecryptBlock(input, output); } +#endif /** * reset the chaining vector back to the IV and reset the underlying @@ -148,33 +143,50 @@ namespace Org.BouncyCastle.Crypto.Modes cipher.Reset(); } - /** - * Do the appropriate chaining step for CBC mode encryption. - * - * @param in the array containing the data to be encrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - private int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + for (int i = 0; i < blockSize; i++) + { + cbcV[i] ^= input[i]; + } + + int length = cipher.ProcessBlock(cbcV, output); + + output[..blockSize].CopyTo(cbcV); + + return length; + } + + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { - if ((inOff + blockSize) > input.Length) + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + input[..blockSize].CopyTo(cbcNextV); + + int length = cipher.ProcessBlock(input, output); + + for (int i = 0; i < blockSize; i++) { - throw new DataLengthException("input buffer too short"); + output[i] ^= cbcV[i]; } - /* - * XOR the cbcV and the input, - * then encrypt the cbcV - */ + byte[] tmp = cbcV; + cbcV = cbcNextV; + cbcNextV = tmp; + + return length; + } +#else + private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) + { + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); + for (int i = 0; i < blockSize; i++) { cbcV[i] ^= input[inOff + i]; @@ -182,60 +194,31 @@ namespace Org.BouncyCastle.Crypto.Modes int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff); - /* - * copy ciphertext to cbcV - */ Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length); return length; } - /** - * Do the appropriate chaining step for CBC mode decryption. - * - * @param in the array containing the data to be decrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the decrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - private int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); Array.Copy(input, inOff, cbcNextV, 0, blockSize); int length = cipher.ProcessBlock(input, inOff, outBytes, outOff); - /* - * XOR the cbcV and the output - */ for (int i = 0; i < blockSize; i++) { outBytes[outOff + i] ^= cbcV[i]; } - /* - * swap the back up buffer into next position - */ - byte[] tmp; - - tmp = cbcV; + byte[] tmp = cbcV; cbcV = cbcNextV; cbcNextV = tmp; return length; } +#endif } - } diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs index abd7dbb8d..256cc1b13 100644 --- a/crypto/src/crypto/modes/CcmBlockCipher.cs +++ b/crypto/src/crypto/modes/CcmBlockCipher.cs @@ -117,6 +117,14 @@ namespace Org.BouncyCastle.Crypto.Modes associatedText.Write(inBytes, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + // TODO: Process AAD online + associatedText.Write(input); + } +#endif + public virtual int ProcessByte( byte input, byte[] outBytes, @@ -134,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Modes byte[] outBytes, int outOff) { - Check.DataLength(inBytes, inOff, inLen, "Input buffer too short"); + Check.DataLength(inBytes, inOff, inLen, "input buffer too short"); data.Write(inBytes, inOff, inLen); diff --git a/crypto/src/crypto/modes/CfbBlockCipher.cs b/crypto/src/crypto/modes/CfbBlockCipher.cs index ed0be407a..bcbffcfb6 100644 --- a/crypto/src/crypto/modes/CfbBlockCipher.cs +++ b/crypto/src/crypto/modes/CfbBlockCipher.cs @@ -108,56 +108,76 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - return (encrypting) - ? EncryptBlock(input, inOff, output, outOff) - : DecryptBlock(input, inOff, output, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return encrypting + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return encrypting + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif } - /** - * Do the appropriate processing for CFB mode encryption. - * - * @param in the array containing the data to be encrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { - if ((inOff + blockSize) > input.Length) + return encrypting + ? EncryptBlock(input, output) + : DecryptBlock(input, output); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + cipher.ProcessBlock(cfbV, cfbOutV); + // + // XOR the cfbV with the plaintext producing the ciphertext + // + for (int i = 0; i < blockSize; i++) { - throw new DataLengthException("input buffer too short"); + output[i] = (byte)(cfbOutV[i] ^ input[i]); } - if ((outOff + blockSize) > outBytes.Length) + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + output[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize)); + return blockSize; + } + + public int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + input[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize)); + // + // XOR the cfbV with the ciphertext producing the plaintext + // + for (int i = 0; i < blockSize; i++) { - throw new DataLengthException("output buffer too short"); + output[i] = (byte)(cfbOutV[i] ^ input[i]); } + return blockSize; + } +#else + public int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) + { + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); // // XOR the cfbV with the plaintext producing the ciphertext @@ -173,32 +193,12 @@ namespace Org.BouncyCastle.Crypto.Modes Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize); return blockSize; } - /** - * Do the appropriate processing for CFB mode decryption. - * - * @param in the array containing the data to be decrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + + public int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - if ((outOff + blockSize) > outBytes.Length) - { - throw new DataLengthException("output buffer too short"); - } + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); // // change over the input block. @@ -214,6 +214,8 @@ namespace Org.BouncyCastle.Crypto.Modes } return blockSize; } +#endif + /** * reset the chaining vector back to the IV and reset the underlying * cipher. diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs index 462013200..385977fd5 100644 --- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs +++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs @@ -209,6 +209,19 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + CheckAad(); + + if (!input.IsEmpty) + { + this.mAadCount = IncrementCount(mAadCount, (uint)input.Length, AadLimit); + mPoly1305.BlockUpdate(input); + } + } +#endif + public virtual int ProcessByte(byte input, byte[] outBytes, int outOff) { CheckData(); diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs index 624f385b5..ffe32ec68 100644 --- a/crypto/src/crypto/modes/EAXBlockCipher.cs +++ b/crypto/src/crypto/modes/EAXBlockCipher.cs @@ -194,13 +194,22 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) { if (cipherInitialized) - { throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); - } + mac.BlockUpdate(inBytes, inOff, len); } - public virtual int ProcessByte( +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + if (cipherInitialized) + throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); + + mac.BlockUpdate(input); + } +#endif + + public virtual int ProcessByte( byte input, byte[] outBytes, int outOff) diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs index ac54e9762..bf9c14e28 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs @@ -278,6 +278,9 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessAadBytes(inBytes.AsSpan(inOff, len)); +#else CheckStatus(); if (atBlockPos > 0) @@ -309,7 +312,42 @@ namespace Org.BouncyCastle.Crypto.Modes atBlockPos = BlockSize + inLimit - inOff; Array.Copy(inBytes, inOff, atBlock, 0, atBlockPos); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + CheckStatus(); + + if (atBlockPos > 0) + { + int available = BlockSize - atBlockPos; + if (input.Length < available) + { + input.CopyTo(atBlock.AsSpan(atBlockPos)); + atBlockPos += input.Length; + return; + } + + input[..available].CopyTo(atBlock.AsSpan(atBlockPos)); + gHASHBlock(S_at, atBlock); + atLength += BlockSize; + input = input[available..]; + //atBlockPos = 0; + } + + while (input.Length >= BlockSize) + { + gHASHBlock(S_at, input); + atLength += BlockSize; + input = input[BlockSize..]; + } + + input.CopyTo(atBlock); + atBlockPos = input.Length; } +#endif private void InitCipher() { @@ -930,6 +968,13 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void gHASHBlock(byte[] Y, ReadOnlySpan<byte> b) + { + GcmUtilities.Xor(Y, b); + multiplier.MultiplyH(Y); + } +#else private void gHASHBlock(byte[] Y, byte[] b) { GcmUtilities.Xor(Y, b); @@ -941,6 +986,7 @@ namespace Org.BouncyCastle.Crypto.Modes GcmUtilities.Xor(Y, b, off); multiplier.MultiplyH(Y); } +#endif private void gHASHPartial(byte[] Y, byte[] b, int off, int len) { diff --git a/crypto/src/crypto/modes/GOFBBlockCipher.cs b/crypto/src/crypto/modes/GOFBBlockCipher.cs index 436b58a1d..4c8576a58 100644 --- a/crypto/src/crypto/modes/GOFBBlockCipher.cs +++ b/crypto/src/crypto/modes/GOFBBlockCipher.cs @@ -1,7 +1,7 @@ using System; -using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Modes { @@ -131,41 +131,17 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - - if ((outOff + blockSize) > output.Length) - { - throw new DataLengthException("output buffer too short"); - } + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(output, outOff, blockSize, "output buffer too short"); if (firstStep) { firstStep = false; cipher.ProcessBlock(ofbV, 0, ofbOutV, 0); - N3 = bytesToint(ofbOutV, 0); - N4 = bytesToint(ofbOutV, 4); + N3 = (int)Pack.LE_To_UInt32(ofbOutV, 0); + N4 = (int)Pack.LE_To_UInt32(ofbOutV, 4); } N3 += C2; N4 += C1; @@ -176,8 +152,8 @@ namespace Org.BouncyCastle.Crypto.Modes N4++; } } - intTobytes(N3, ofbV, 0); - intTobytes(N4, ofbV, 4); + Pack.UInt32_To_LE((uint)N3, ofbV, 0); + Pack.UInt32_To_LE((uint)N4, ofbV, 4); cipher.ProcessBlock(ofbV, 0, ofbOutV, 0); @@ -199,6 +175,52 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + if (firstStep) + { + firstStep = false; + cipher.ProcessBlock(ofbV, ofbOutV); + N3 = (int)Pack.LE_To_UInt32(ofbOutV, 0); + N4 = (int)Pack.LE_To_UInt32(ofbOutV, 4); + } + N3 += C2; + N4 += C1; + if (N4 < C1) // addition is mod (2**32 - 1) + { + if (N4 > 0) + { + N4++; + } + } + Pack.UInt32_To_LE((uint)N3, ofbV, 0); + Pack.UInt32_To_LE((uint)N4, ofbV, 4); + + cipher.ProcessBlock(ofbV, ofbOutV); + + // + // XOR the ofbV with the plaintext producing the cipher text (and + // the next input block). + // + for (int i = 0; i < blockSize; i++) + { + output[i] = (byte)(ofbOutV[i] ^ input[i]); + } + + // + // change over the input block. + // + Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize); + Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize); + + return blockSize; + } +#endif + /** * reset the feedback vector back to the IV and reset the underlying * cipher. @@ -209,26 +231,5 @@ namespace Org.BouncyCastle.Crypto.Modes cipher.Reset(); } - - //array of bytes to type int - private int bytesToint( - byte[] inBytes, - int inOff) - { - return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) + - ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff); - } - - //int to array of bytes - private void intTobytes( - int num, - byte[] outBytes, - int outOff) - { - outBytes[outOff + 3] = (byte)(num >> 24); - outBytes[outOff + 2] = (byte)(num >> 16); - outBytes[outOff + 1] = (byte)(num >> 8); - outBytes[outOff] = (byte)num; - } } } diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs index 5af3429f2..284a952a6 100644 --- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs +++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs @@ -1,7 +1,6 @@ using System; using System.IO; -using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes.Gcm; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; @@ -296,16 +295,31 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void ProcessAadBytes(byte[] pData, int pOffset, int pLen) { - /* Check that we can supply AEAD */ - CheckAeadStatus(pLen); - /* Check input buffer */ CheckBuffer(pData, pOffset, pLen, false); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessAadBytes(pData.AsSpan(pOffset, pLen)); +#else + /* Check that we can supply AEAD */ + CheckAeadStatus(pLen); + /* Process the aead */ theAEADHasher.updateHash(pData, pOffset, pLen); +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + /* Check that we can supply AEAD */ + CheckAeadStatus(input.Length); + + /* Process the aead */ + theAEADHasher.updateHash(input); + } +#endif + public virtual int ProcessByte(byte pByte, byte[] pOutput, int pOutOffset) { /* Check that we have initialised */ @@ -472,8 +486,8 @@ namespace Org.BouncyCastle.Crypto.Modes if (badLen || myLast > myBufLen) { throw pOutput - ? new OutputLengthException("Output buffer too short.") - : new DataLengthException("Input buffer too short."); + ? new OutputLengthException("output buffer too short.") + : new DataLengthException("input buffer too short."); } } @@ -647,6 +661,18 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void fillReverse(ReadOnlySpan<byte> input, Span<byte> output) + { + /* Loop through the buffer */ + for (int i = 0, j = BUFLEN - 1; i < input.Length; i++, j--) + { + /* Copy byte */ + output[j] = input[i]; + } + } +#endif + /** * xor a full block buffer. * @param pLeft the left operand and result @@ -875,8 +901,8 @@ namespace Org.BouncyCastle.Crypto.Modes parent.gHASH(parent.theReverse); /* Adjust counters */ - numProcessed += mySpace; - myRemaining -= mySpace; + numProcessed += BUFLEN; + myRemaining -= BUFLEN; } /* If we have remaining data */ @@ -891,6 +917,49 @@ namespace Org.BouncyCastle.Crypto.Modes numHashed += (ulong)pLen; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void updateHash(ReadOnlySpan<byte> buffer) + { + int pLen = buffer.Length; + + /* If we should process the cache */ + int mySpace = BUFLEN - numActive; + if (numActive > 0 && buffer.Length >= mySpace) + { + /* Copy data into the cache and hash it */ + buffer[..mySpace].CopyTo(theBuffer.AsSpan(numActive)); + fillReverse(theBuffer, parent.theReverse); + parent.gHASH(parent.theReverse); + + /* Adjust counters */ + buffer = buffer[mySpace..]; + numActive = 0; + } + + /* While we have full blocks */ + while (buffer.Length >= BUFLEN) + { + /* Access the next data */ + fillReverse(buffer[..BUFLEN], parent.theReverse); + parent.gHASH(parent.theReverse); + + /* Adjust counters */ + buffer = buffer[BUFLEN..]; + } + + /* If we have remaining data */ + if (!buffer.IsEmpty) + { + /* Copy data into the cache */ + buffer.CopyTo(theBuffer.AsSpan(numActive)); + numActive += buffer.Length; + } + + /* Adjust the number of bytes processed */ + numHashed += (ulong)pLen; + } +#endif + /** * complete hash. */ diff --git a/crypto/src/crypto/modes/IAeadCipher.cs b/crypto/src/crypto/modes/IAeadCipher.cs index 437693cb6..c61e13b01 100644 --- a/crypto/src/crypto/modes/IAeadCipher.cs +++ b/crypto/src/crypto/modes/IAeadCipher.cs @@ -41,6 +41,13 @@ namespace Org.BouncyCastle.Crypto.Modes /// <param name="len">The number of bytes to be processed.</param> void ProcessAadBytes(byte[] inBytes, int inOff, int len); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <summary>Add a span of bytes to the associated data check.</summary> + /// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks> + /// <param name="input">the span containing the data.</param> + void ProcessAadBytes(ReadOnlySpan<byte> input); +#endif + /** * Encrypt/decrypt a single byte. * diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs index afa7063a3..afa68a794 100644 --- a/crypto/src/crypto/modes/KCcmBlockCipher.cs +++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs @@ -158,6 +158,13 @@ namespace Org.BouncyCastle.Crypto.Modes associatedText.Write(input, inOff, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + associatedText.Write(input); + } +#endif + private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen) { if (assocLen - assocOff < engine.GetBlockSize()) diff --git a/crypto/src/crypto/modes/KCtrBlockCipher.cs b/crypto/src/crypto/modes/KCtrBlockCipher.cs index ff0249a6c..79b74f84c 100644 --- a/crypto/src/crypto/modes/KCtrBlockCipher.cs +++ b/crypto/src/crypto/modes/KCtrBlockCipher.cs @@ -118,25 +118,30 @@ namespace Org.BouncyCastle.Crypto.Modes public void ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - if (outOff + len > output.Length) - { - throw new DataLengthException("Output buffer too short"); - } - - if (inOff + len > input.Length) - { - throw new DataLengthException("Input buffer too small"); - } + Check.DataLength(input, inOff, len, "input buffer too small"); + Check.OutputLength(output, outOff, len, "output buffer too short"); int inStart = inOff; int inEnd = inOff + len; int outStart = outOff; - while (inStart<inEnd) + while (inStart < inEnd) + { + output[outStart++] = CalculateByte(input[inStart++]); + } + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.OutputLength(output, input.Length, "output buffer too short"); + + for (int i = 0; i < input.Length; ++i) { - output[outStart++] = CalculateByte(input[inStart++]); + output[i] = CalculateByte(input[i]); } } +#endif protected byte CalculateByte(byte b) { @@ -176,19 +181,27 @@ namespace Org.BouncyCastle.Crypto.Modes */ public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - if (input.Length - inOff< GetBlockSize()) - { - throw new DataLengthException("Input buffer too short"); - } - if (output.Length - outOff< GetBlockSize()) - { - throw new DataLengthException("Output buffer too short"); - } + int blockSize = GetBlockSize(); + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(output, outOff, blockSize, "output buffer too short"); + + ProcessBytes(input, inOff, blockSize, output, outOff); + + return blockSize; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + int blockSize = GetBlockSize(); + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); - ProcessBytes(input, inOff, GetBlockSize(), output, outOff); + ProcessBytes(input[..blockSize], output); - return GetBlockSize(); + return blockSize; } +#endif /** * reset the chaining vector back to the IV and reset the underlying diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs index 28e88a6c9..db6aa39ae 100644 --- a/crypto/src/crypto/modes/OCBBlockCipher.cs +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs @@ -287,6 +287,20 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) + { + for (int i = 0; i < input.Length; ++i) + { + hashBlock[hashBlockPos] = input[i]; + if (++hashBlockPos == hashBlock.Length) + { + ProcessHashBlock(); + } + } + } +#endif + public virtual int ProcessByte(byte input, byte[] output, int outOff) { mainBlock[mainBlockPos] = input; diff --git a/crypto/src/crypto/modes/OfbBlockCipher.cs b/crypto/src/crypto/modes/OfbBlockCipher.cs index a99f8c5d7..ac9b9a06c 100644 --- a/crypto/src/crypto/modes/OfbBlockCipher.cs +++ b/crypto/src/crypto/modes/OfbBlockCipher.cs @@ -61,9 +61,8 @@ namespace Org.BouncyCastle.Crypto.Modes bool forEncryption, //ignored by this OFB mode ICipherParameters parameters) { - if (parameters is ParametersWithIV) + if (parameters is ParametersWithIV ivParam) { - ParametersWithIV ivParam = (ParametersWithIV)parameters; byte[] iv = ivParam.GetIV(); if (iv.Length < IV.Length) @@ -118,36 +117,38 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(output, outOff, blockSize, "output buffer too short"); + + cipher.ProcessBlock(ofbV, 0, ofbOutV, 0); - if ((outOff + blockSize) > output.Length) + // + // XOR the ofbV with the plaintext producing the cipher text (and + // the next input block). + // + for (int i = 0; i < blockSize; i++) { - throw new DataLengthException("output buffer too short"); + output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]); } - cipher.ProcessBlock(ofbV, 0, ofbOutV, 0); + // + // change over the input block. + // + Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize); + Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize); + + return blockSize; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + cipher.ProcessBlock(ofbV, ofbOutV); // // XOR the ofbV with the plaintext producing the cipher text (and @@ -155,7 +156,7 @@ namespace Org.BouncyCastle.Crypto.Modes // for (int i = 0; i < blockSize; i++) { - output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]); + output[i] = (byte)(ofbOutV[i] ^ input[i]); } // @@ -166,6 +167,7 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } +#endif /** * reset the feedback vector back to the IV and reset the underlying diff --git a/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs b/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs index 038ca783d..45998248c 100644 --- a/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs +++ b/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs @@ -79,29 +79,29 @@ namespace Org.BouncyCastle.Crypto.Modes return cipher.GetBlockSize(); } - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { - return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return forEncryption + ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)) + : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff)); +#else + return forEncryption + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); +#endif } - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + return forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); + } +#endif + + /** * reset the chaining vector back to the IV and reset the underlying * cipher. */ @@ -125,15 +125,12 @@ namespace Org.BouncyCastle.Crypto.Modes * @exception ArgumentException if the parameters argument is * inappropriate. */ - public void Init( - bool forEncryption, - ICipherParameters parameters) + public void Init(bool forEncryption, ICipherParameters parameters) { this.forEncryption = forEncryption; - if (parameters is ParametersWithIV) + if (parameters is ParametersWithIV ivParam) { - ParametersWithIV ivParam = (ParametersWithIV)parameters; byte[] iv = ivParam.GetIV(); if (iv.Length < IV.Length) @@ -169,34 +166,132 @@ namespace Org.BouncyCastle.Crypto.Modes return (byte)(FRE[blockOff] ^ data); } - /** - * Do the appropriate processing for CFB IV mode encryption. - * - * @param in the array containing the data to be encrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - private int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output) { - if ((inOff + blockSize) > input.Length) + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + if (count > blockSize) { - throw new DataLengthException("input buffer too short"); + FR[blockSize - 2] = output[0] = EncryptByte(input[0], blockSize - 2); + FR[blockSize - 1] = output[1] = EncryptByte(input[1], blockSize - 1); + + cipher.ProcessBlock(FR, FRE); + + for (int n = 2; n < blockSize; n++) + { + FR[n - 2] = output[n] = EncryptByte(input[n], n - 2); + } } + else if (count == 0) + { + cipher.ProcessBlock(FR, FRE); + + for (int n = 0; n < blockSize; n++) + { + FR[n] = output[n] = EncryptByte(input[n], n); + } - if ((outOff + blockSize) > outBytes.Length) + count += blockSize; + } + else if (count == blockSize) { - throw new DataLengthException("output buffer too short"); + cipher.ProcessBlock(FR, FRE); + + output[0] = EncryptByte(input[0], 0); + output[1] = EncryptByte(input[1], 1); + + // + // do reset + // + Array.Copy(FR, 2, FR, 0, blockSize - 2); + output[..2].CopyTo(FR.AsSpan(blockSize - 2)); + + cipher.ProcessBlock(FR, FRE); + + for (int n = 2; n < blockSize; n++) + { + FR[n - 2] = output[n] = EncryptByte(input[n], n - 2); + } + + count += blockSize; } + return blockSize; + } + + private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.DataLength(input, blockSize, "input buffer too short"); + Check.OutputLength(output, blockSize, "output buffer too short"); + + if (count > blockSize) + { + byte inVal = input[0]; + FR[blockSize - 2] = inVal; + output[0] = EncryptByte(inVal, blockSize - 2); + + inVal = input[1]; + FR[blockSize - 1] = inVal; + output[1] = EncryptByte(inVal, blockSize - 1); + + cipher.ProcessBlock(FR, FRE); + + for (int n = 2; n < blockSize; n++) + { + inVal = input[n]; + FR[n - 2] = inVal; + output[n] = EncryptByte(inVal, n - 2); + } + } + else if (count == 0) + { + cipher.ProcessBlock(FR, FRE); + + for (int n = 0; n < blockSize; n++) + { + FR[n] = input[n]; + output[n] = EncryptByte(input[n], n); + } + + count += blockSize; + } + else if (count == blockSize) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + byte inVal1 = input[0]; + byte inVal2 = input[1]; + output[0] = EncryptByte(inVal1, 0); + output[1] = EncryptByte(inVal2, 1); + + Array.Copy(FR, 2, FR, 0, blockSize - 2); + + FR[blockSize - 2] = inVal1; + FR[blockSize - 1] = inVal2; + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + byte inVal = input[n]; + FR[n - 2] = inVal; + output[n] = EncryptByte(inVal, n - 2); + } + + count += blockSize; + } + + return blockSize; + } +#else + private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) + { + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); + if (count > blockSize) { FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2); @@ -246,33 +341,10 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } - /** - * Do the appropriate processing for CFB IV mode decryption. - * - * @param in the array containing the data to be decrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - private int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff) + private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff) { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - - if ((outOff + blockSize) > outBytes.Length) - { - throw new DataLengthException("output buffer too short"); - } + Check.DataLength(input, inOff, blockSize, "input buffer too short"); + Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short"); if (count > blockSize) { @@ -300,7 +372,7 @@ namespace Org.BouncyCastle.Crypto.Modes for (int n = 0; n < blockSize; n++) { FR[n] = input[inOff + n]; - outBytes[n] = EncryptByte(input[inOff + n], n); + outBytes[outOff + n] = EncryptByte(input[inOff + n], n); } count += blockSize; @@ -333,5 +405,6 @@ namespace Org.BouncyCastle.Crypto.Modes return blockSize; } +#endif } } diff --git a/crypto/src/crypto/modes/SicBlockCipher.cs b/crypto/src/crypto/modes/SicBlockCipher.cs index 0bea4a455..431e2952c 100644 --- a/crypto/src/crypto/modes/SicBlockCipher.cs +++ b/crypto/src/crypto/modes/SicBlockCipher.cs @@ -85,11 +85,7 @@ namespace Org.BouncyCastle.Crypto.Modes return cipher.GetBlockSize(); } - public virtual int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) + public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { cipher.ProcessBlock(counter, 0, counterOut, 0); @@ -110,6 +106,29 @@ namespace Org.BouncyCastle.Crypto.Modes return counter.Length; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + cipher.ProcessBlock(counter, 0, counterOut, 0); + + // + // XOR the counterOut with the plaintext producing the cipher text + // + for (int i = 0; i < counterOut.Length; i++) + { + output[i] = (byte)(counterOut[i] ^ input[i]); + } + + // Increment the counter + int j = counter.Length; + while (--j >= 0 && ++counter[j] == 0) + { + } + + return counter.Length; + } +#endif + public virtual void Reset() { Arrays.Fill(counter, (byte)0); diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs index 676d74107..78a1f0860 100644 --- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs +++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs @@ -275,6 +275,21 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm z.n1 = x.n1 ^ y.n1; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void Xor(Span<byte> x, ReadOnlySpan<byte> y) + { + int i = 0; + do + { + x[i] ^= y[i]; ++i; + x[i] ^= y[i]; ++i; + x[i] ^= y[i]; ++i; + x[i] ^= y[i]; ++i; + } + while (i < 16); + } +#endif + private static ulong ImplMul64(ulong x, ulong y) { ulong x0 = x & 0x1111111111111111UL; diff --git a/crypto/src/crypto/signers/DsaDigestSigner.cs b/crypto/src/crypto/signers/DsaDigestSigner.cs index 15444a0f7..e8c2487ba 100644 --- a/crypto/src/crypto/signers/DsaDigestSigner.cs +++ b/crypto/src/crypto/signers/DsaDigestSigner.cs @@ -14,19 +14,12 @@ namespace Org.BouncyCastle.Crypto.Signers private readonly IDsaEncoding encoding; private bool forSigning; - public DsaDigestSigner( - IDsa dsa, - IDigest digest) + public DsaDigestSigner(IDsa dsa, IDigest digest) + : this(dsa, digest, StandardDsaEncoding.Instance) { - this.dsa = dsa; - this.digest = digest; - this.encoding = StandardDsaEncoding.Instance; } - public DsaDigestSigner( - IDsaExt dsa, - IDigest digest, - IDsaEncoding encoding) + public DsaDigestSigner(IDsa dsa, IDigest digest, IDsaEncoding encoding) { this.dsa = dsa; this.digest = digest; @@ -38,17 +31,14 @@ namespace Org.BouncyCastle.Crypto.Signers get { return digest.AlgorithmName + "with" + dsa.AlgorithmName; } } - public virtual void Init( - bool forSigning, - ICipherParameters parameters) + public virtual void Init(bool forSigning, ICipherParameters parameters) { this.forSigning = forSigning; AsymmetricKeyParameter k; - - if (parameters is ParametersWithRandom) + if (parameters is ParametersWithRandom withRandom) { - k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + k = (AsymmetricKeyParameter)withRandom.Parameters; } else { @@ -66,31 +56,24 @@ namespace Org.BouncyCastle.Crypto.Signers dsa.Init(forSigning, parameters); } - /** - * update the internal digest with the byte b - */ - public virtual void Update( - byte input) + public virtual void Update(byte input) { digest.Update(input); } - /** - * update the internal digest with the byte array in - */ - public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) + { + digest.BlockUpdate(input, inOff, inLen); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) { - digest.BlockUpdate(input, inOff, length); + digest.BlockUpdate(input); } +#endif - /** - * Generate a signature for the message we've been loaded with using - * the key we were initialised with. - */ - public virtual byte[] GenerateSignature() + public virtual byte[] GenerateSignature() { if (!forSigning) throw new InvalidOperationException("DSADigestSigner not initialised for signature generation."); @@ -110,9 +93,7 @@ namespace Org.BouncyCastle.Crypto.Signers } } - /// <returns>true if the internal state represents the signature described in the passed in array.</returns> - public virtual bool VerifySignature( - byte[] signature) + public virtual bool VerifySignature(byte[] signature) { if (forSigning) throw new InvalidOperationException("DSADigestSigner not initialised for verification"); @@ -132,7 +113,6 @@ namespace Org.BouncyCastle.Crypto.Signers } } - /// <summary>Reset the internal state</summary> public virtual void Reset() { digest.Reset(); @@ -140,7 +120,7 @@ namespace Org.BouncyCastle.Crypto.Signers protected virtual BigInteger GetOrder() { - return dsa is IDsaExt ? ((IDsaExt)dsa).Order : null; + return dsa.Order; } } } diff --git a/crypto/src/crypto/signers/DsaSigner.cs b/crypto/src/crypto/signers/DsaSigner.cs index 1f5d9b937..7799edc0e 100644 --- a/crypto/src/crypto/signers/DsaSigner.cs +++ b/crypto/src/crypto/signers/DsaSigner.cs @@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Signers * Cryptography", pages 452 - 453. */ public class DsaSigner - : IDsaExt + : IDsa { protected readonly IDsaKCalculator kCalculator; diff --git a/crypto/src/crypto/signers/ECDsaSigner.cs b/crypto/src/crypto/signers/ECDsaSigner.cs index 0a265d96e..590c3236b 100644 --- a/crypto/src/crypto/signers/ECDsaSigner.cs +++ b/crypto/src/crypto/signers/ECDsaSigner.cs @@ -1,6 +1,5 @@ using System; -using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; @@ -14,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Signers * EC-DSA as described in X9.62 */ public class ECDsaSigner - : IDsaExt + : IDsa { private static readonly BigInteger Eight = BigInteger.ValueOf(8); diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs index 7df43f0a0..7b3833b66 100644 --- a/crypto/src/crypto/signers/ECGOST3410Signer.cs +++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs @@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Signers * GOST R 34.10-2001 Signature Algorithm */ public class ECGost3410Signer - : IDsaExt + : IDsa { private ECKeyParameters key; private SecureRandom random; diff --git a/crypto/src/crypto/signers/ECNRSigner.cs b/crypto/src/crypto/signers/ECNRSigner.cs index bc193e797..b22d7a977 100644 --- a/crypto/src/crypto/signers/ECNRSigner.cs +++ b/crypto/src/crypto/signers/ECNRSigner.cs @@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Signers * EC-NR as described in IEEE 1363-2000 */ public class ECNRSigner - : IDsaExt + : IDsa { private bool forSigning; private ECKeyParameters key; diff --git a/crypto/src/crypto/signers/Ed25519Signer.cs b/crypto/src/crypto/signers/Ed25519Signer.cs index 4646ce1a5..59dc1bec5 100644 --- a/crypto/src/crypto/signers/Ed25519Signer.cs +++ b/crypto/src/crypto/signers/Ed25519Signer.cs @@ -52,6 +52,13 @@ namespace Org.BouncyCastle.Crypto.Signers buffer.Write(buf, off, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + buffer.Write(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning || null == privateKey) diff --git a/crypto/src/crypto/signers/Ed25519ctxSigner.cs b/crypto/src/crypto/signers/Ed25519ctxSigner.cs index 293afedf5..4ccca8f22 100644 --- a/crypto/src/crypto/signers/Ed25519ctxSigner.cs +++ b/crypto/src/crypto/signers/Ed25519ctxSigner.cs @@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers buffer.Write(buf, off, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + buffer.Write(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning || null == privateKey) diff --git a/crypto/src/crypto/signers/Ed25519phSigner.cs b/crypto/src/crypto/signers/Ed25519phSigner.cs index 8f4afab19..800447143 100644 --- a/crypto/src/crypto/signers/Ed25519phSigner.cs +++ b/crypto/src/crypto/signers/Ed25519phSigner.cs @@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers prehash.BlockUpdate(buf, off, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + prehash.BlockUpdate(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning || null == privateKey) diff --git a/crypto/src/crypto/signers/Ed448Signer.cs b/crypto/src/crypto/signers/Ed448Signer.cs index 723ee7e33..3a7def690 100644 --- a/crypto/src/crypto/signers/Ed448Signer.cs +++ b/crypto/src/crypto/signers/Ed448Signer.cs @@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers buffer.Write(buf, off, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + buffer.Write(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning || null == privateKey) diff --git a/crypto/src/crypto/signers/Ed448phSigner.cs b/crypto/src/crypto/signers/Ed448phSigner.cs index 197c2f706..30d4a0aba 100644 --- a/crypto/src/crypto/signers/Ed448phSigner.cs +++ b/crypto/src/crypto/signers/Ed448phSigner.cs @@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers prehash.BlockUpdate(buf, off, len); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + prehash.BlockUpdate(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning || null == privateKey) diff --git a/crypto/src/crypto/signers/GOST3410DigestSigner.cs b/crypto/src/crypto/signers/GOST3410DigestSigner.cs index 81a5ff1cc..9564e43d3 100644 --- a/crypto/src/crypto/signers/GOST3410DigestSigner.cs +++ b/crypto/src/crypto/signers/GOST3410DigestSigner.cs @@ -15,11 +15,7 @@ namespace Org.BouncyCastle.Crypto.Signers private int halfSize; private bool forSigning; - - - public Gost3410DigestSigner( - IDsa signer, - IDigest digest) + public Gost3410DigestSigner(IDsa signer, IDigest digest) { this.dsaSigner = signer; this.digest = digest; @@ -34,9 +30,7 @@ namespace Org.BouncyCastle.Crypto.Signers get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } } - public virtual void Init( - bool forSigning, - ICipherParameters parameters) + public virtual void Init(bool forSigning, ICipherParameters parameters) { this.forSigning = forSigning; @@ -66,30 +60,23 @@ namespace Org.BouncyCastle.Crypto.Signers dsaSigner.Init(forSigning, parameters); } - /** - * update the internal digest with the byte b - */ - public virtual void Update( - byte input) + public virtual void Update(byte input) { digest.Update(input); } - /** - * update the internal digest with the byte array in - */ - public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) + { + digest.BlockUpdate(input, inOff, inLen); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) { - digest.BlockUpdate(input, inOff, length); + digest.BlockUpdate(input); } +#endif - /** - * Generate a signature for the message we've been loaded with using - * the key we were initialised with. - */ public virtual byte[] GenerateSignature() { if (!forSigning) @@ -116,9 +103,7 @@ namespace Org.BouncyCastle.Crypto.Signers } } - /// <returns>true if the internal state represents the signature described in the passed in array.</returns> - public virtual bool VerifySignature( - byte[] signature) + public virtual bool VerifySignature(byte[] signature) { if (forSigning) throw new InvalidOperationException("DSADigestSigner not initialised for verification"); @@ -140,7 +125,6 @@ namespace Org.BouncyCastle.Crypto.Signers return dsaSigner.VerifySignature(hash, R, S); } - /// <summary>Reset the internal state</summary> public virtual void Reset() { digest.Reset(); diff --git a/crypto/src/crypto/signers/GOST3410Signer.cs b/crypto/src/crypto/signers/GOST3410Signer.cs index bcc1125b1..a0d8f8a1f 100644 --- a/crypto/src/crypto/signers/GOST3410Signer.cs +++ b/crypto/src/crypto/signers/GOST3410Signer.cs @@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Signers * Gost R 34.10-94 Signature Algorithm */ public class Gost3410Signer - : IDsaExt + : IDsa { private Gost3410KeyParameters key; private SecureRandom random; diff --git a/crypto/src/crypto/signers/GenericSigner.cs b/crypto/src/crypto/signers/GenericSigner.cs index a5512176f..e0ff685ae 100644 --- a/crypto/src/crypto/signers/GenericSigner.cs +++ b/crypto/src/crypto/signers/GenericSigner.cs @@ -59,26 +59,23 @@ namespace Org.BouncyCastle.Crypto.Signers engine.Init(forSigning, parameters); } - /** - * update the internal digest with the byte b - */ public virtual void Update(byte input) { digest.Update(input); } - /** - * update the internal digest with the byte array in - */ - public virtual void BlockUpdate(byte[] input, int inOff, int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { - digest.BlockUpdate(input, inOff, length); + digest.BlockUpdate(input, inOff, inLen); } - /** - * Generate a signature for the message we've been loaded with using the key - * we were initialised with. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning) @@ -90,10 +87,6 @@ namespace Org.BouncyCastle.Crypto.Signers return engine.ProcessBlock(hash, 0, hash.Length); } - /** - * return true if the internal state represents the signature described in - * the passed in array. - */ public virtual bool VerifySignature(byte[] signature) { if (forSigning) diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs index ad2718280..573765c1a 100644 --- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs +++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs @@ -290,27 +290,46 @@ namespace Org.BouncyCastle.Crypto.Signers } } - /// <summary> update the internal digest with the byte array in</summary> - public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, inLen)); +#else if (preSig == null) { - while (length > 0 && messageLength < mBuf.Length) + while (inLen > 0 && messageLength < mBuf.Length) { this.Update(input[inOff]); inOff++; - length--; + inLen--; + } + } + + if (inLen > 0) + { + digest.BlockUpdate(input, inOff, inLen); + } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + if (preSig == null) + { + while (!input.IsEmpty && messageLength < mBuf.Length) + { + this.Update(input[0]); + input = input[1..]; } } - if (length > 0) + if (!input.IsEmpty) { - digest.BlockUpdate(input, inOff, length); + digest.BlockUpdate(input); } } +#endif /// <summary> reset the internal state</summary> public virtual void Reset() diff --git a/crypto/src/crypto/signers/Iso9796d2Signer.cs b/crypto/src/crypto/signers/Iso9796d2Signer.cs index f28c4ac71..ea1dc3f18 100644 --- a/crypto/src/crypto/signers/Iso9796d2Signer.cs +++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs @@ -218,9 +218,7 @@ namespace Org.BouncyCastle.Crypto.Signers recoveredMessage.CopyTo(mBuf, 0); } - /// <summary> update the internal digest with the byte b</summary> - public virtual void Update( - byte input) + public virtual void Update(byte input) { digest.Update(input); @@ -232,26 +230,42 @@ namespace Org.BouncyCastle.Crypto.Signers messageLength++; } - /// <summary> update the internal digest with the byte array in</summary> - public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { - while (length > 0 && messageLength < mBuf.Length) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, inLen)); +#else + while (inLen > 0 && messageLength < mBuf.Length) { - //for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++) - //{ - // mBuf[messageLength + i] = input[inOff + i]; - //} this.Update(input[inOff]); inOff++; - length--; + inLen--; } - digest.BlockUpdate(input, inOff, length); - messageLength += length; + if (inLen > 0) + { + digest.BlockUpdate(input, inOff, inLen); + messageLength += inLen; + } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + while (!input.IsEmpty && messageLength < mBuf.Length) + { + this.Update(input[0]); + input = input[1..]; + } + + if (!input.IsEmpty) + { + digest.BlockUpdate(input); + messageLength += input.Length; + } } +#endif /// <summary> reset the internal state</summary> public virtual void Reset() diff --git a/crypto/src/crypto/signers/PssSigner.cs b/crypto/src/crypto/signers/PssSigner.cs index 2a941df47..b033bb251 100644 --- a/crypto/src/crypto/signers/PssSigner.cs +++ b/crypto/src/crypto/signers/PssSigner.cs @@ -198,31 +198,28 @@ namespace Org.BouncyCastle.Crypto.Signers Array.Clear(block, 0, block.Length); } - /// <summary> update the internal digest with the byte b</summary> - public virtual void Update( - byte input) + public virtual void Update(byte input) { contentDigest1.Update(input); } - /// <summary> update the internal digest with the byte array in</summary> - public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) + { + contentDigest1.BlockUpdate(input, inOff, inLen); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) { - contentDigest1.BlockUpdate(input, inOff, length); + contentDigest1.BlockUpdate(input); } +#endif - /// <summary> reset the internal state</summary> public virtual void Reset() { contentDigest1.Reset(); } - /// <summary> Generate a signature for the message we've been loaded with using - /// the key we were initialised with. - /// </summary> public virtual byte[] GenerateSignature() { if (contentDigest1.GetDigestSize() != hLen) @@ -268,11 +265,7 @@ namespace Org.BouncyCastle.Crypto.Signers return b; } - /// <summary> return true if the internal state represents the signature described - /// in the passed in array. - /// </summary> - public virtual bool VerifySignature( - byte[] signature) + public virtual bool VerifySignature(byte[] signature) { if (contentDigest1.GetDigestSize() != hLen) throw new InvalidOperationException(); diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs index 25bd4af4e..75b3a24b9 100644 --- a/crypto/src/crypto/signers/RsaDigestSigner.cs +++ b/crypto/src/crypto/signers/RsaDigestSigner.cs @@ -122,30 +122,23 @@ namespace Org.BouncyCastle.Crypto.Signers rsaEngine.Init(forSigning, parameters); } - /** - * update the internal digest with the byte b - */ - public virtual void Update( - byte input) + public virtual void Update(byte input) { digest.Update(input); } - /** - * update the internal digest with the byte array in - */ - public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { - digest.BlockUpdate(input, inOff, length); + digest.BlockUpdate(input, inOff, inLen); } - /** - * Generate a signature for the message we've been loaded with using - * the key we were initialised with. - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } +#endif + public virtual byte[] GenerateSignature() { if (!forSigning) @@ -158,12 +151,7 @@ namespace Org.BouncyCastle.Crypto.Signers return rsaEngine.ProcessBlock(data, 0, data.Length); } - /** - * return true if the internal state represents the signature described - * in the passed in array. - */ - public virtual bool VerifySignature( - byte[] signature) + public virtual bool VerifySignature(byte[] signature) { if (forSigning) throw new InvalidOperationException("RsaDigestSigner not initialised for verification"); diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs index c344a220a..24aedd970 100644 --- a/crypto/src/crypto/signers/SM2Signer.cs +++ b/crypto/src/crypto/signers/SM2Signer.cs @@ -106,11 +106,18 @@ namespace Org.BouncyCastle.Crypto.Signers digest.Update(b); } - public virtual void BlockUpdate(byte[] buf, int off, int len) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { - digest.BlockUpdate(buf, off, len); + digest.BlockUpdate(input, inOff, inLen); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } +#endif + public virtual bool VerifySignature(byte[] signature) { try diff --git a/crypto/src/crypto/signers/X931Signer.cs b/crypto/src/crypto/signers/X931Signer.cs index 0907403a8..6c0aa9427 100644 --- a/crypto/src/crypto/signers/X931Signer.cs +++ b/crypto/src/crypto/signers/X931Signer.cs @@ -88,34 +88,28 @@ namespace Org.BouncyCastle.Crypto.Signers Array.Clear(block, 0, block.Length); } - /** - * update the internal digest with the byte b - */ public virtual void Update(byte b) { digest.Update(b); } - /** - * update the internal digest with the byte array in - */ - public virtual void BlockUpdate(byte[] input, int off, int len) + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { - digest.BlockUpdate(input, off, len); + digest.BlockUpdate(input, inOff, inLen); } - /** - * reset the internal state - */ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } +#endif + public virtual void Reset() { digest.Reset(); } - /** - * generate a signature for the loaded message using the key we were - * initialised with. - */ public virtual byte[] GenerateSignature() { CreateSignatureBlock(); @@ -156,10 +150,6 @@ namespace Org.BouncyCastle.Crypto.Signers block[delta - 1] = (byte)0xba; } - /** - * return true if the signature represents a ISO9796-2 signature - * for the passed in message. - */ public virtual bool VerifySignature(byte[] signature) { try diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs index fd6ee1d9f..7706b5a42 100644 --- a/crypto/src/crypto/util/Pack.cs +++ b/crypto/src/crypto/util/Pack.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Utilities { @@ -441,6 +442,7 @@ namespace Org.BouncyCastle.Crypto.Utilities } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint BE_To_UInt32(ReadOnlySpan<byte> bs) { return (uint)bs[0] << 24 @@ -449,6 +451,7 @@ namespace Org.BouncyCastle.Crypto.Utilities | bs[3]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void BE_To_UInt32(ReadOnlySpan<byte> bs, Span<uint> ns) { for (int i = 0; i < ns.Length; ++i) @@ -458,6 +461,7 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ulong BE_To_UInt64(ReadOnlySpan<byte> bs) { uint hi = BE_To_UInt32(bs); @@ -465,6 +469,7 @@ namespace Org.BouncyCastle.Crypto.Utilities return ((ulong)hi << 32) | lo; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void BE_To_UInt64(ReadOnlySpan<byte> bs, Span<ulong> ns) { for (int i = 0; i < ns.Length; ++i) @@ -474,6 +479,7 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint LE_To_UInt32(ReadOnlySpan<byte> bs) { return bs[0] @@ -482,6 +488,7 @@ namespace Org.BouncyCastle.Crypto.Utilities | (uint)bs[3] << 24; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void LE_To_UInt32(ReadOnlySpan<byte> bs, Span<uint> ns) { for (int i = 0; i < ns.Length; ++i) @@ -491,6 +498,7 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ulong LE_To_UInt64(ReadOnlySpan<byte> bs) { uint lo = LE_To_UInt32(bs); @@ -498,6 +506,7 @@ namespace Org.BouncyCastle.Crypto.Utilities return (ulong)hi << 32 | lo; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void LE_To_UInt64(ReadOnlySpan<byte> bs, Span<ulong> ns) { for (int i = 0; i < ns.Length; ++i) @@ -507,6 +516,7 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt32_To_BE(uint n, Span<byte> bs) { bs[0] = (byte)(n >> 24); @@ -515,6 +525,7 @@ namespace Org.BouncyCastle.Crypto.Utilities bs[3] = (byte) n; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt32_To_BE(ReadOnlySpan<uint> ns, Span<byte> bs) { for (int i = 0; i < ns.Length; ++i) @@ -524,6 +535,7 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt32_To_LE(uint n, Span<byte> bs) { bs[0] = (byte) n; @@ -532,6 +544,7 @@ namespace Org.BouncyCastle.Crypto.Utilities bs[3] = (byte)(n >> 24); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt32_To_LE(ReadOnlySpan<uint> ns, Span<byte> bs) { for (int i = 0; i < ns.Length; ++i) @@ -541,12 +554,14 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt64_To_BE(ulong n, Span<byte> bs) { UInt32_To_BE((uint)(n >> 32), bs); UInt32_To_BE((uint)n, bs[4..]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt64_To_BE(ReadOnlySpan<ulong> ns, Span<byte> bs) { for (int i = 0; i < ns.Length; ++i) @@ -556,12 +571,14 @@ namespace Org.BouncyCastle.Crypto.Utilities } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt64_To_LE(ulong n, Span<byte> bs) { UInt32_To_LE((uint)n, bs); UInt32_To_LE((uint)(n >> 32), bs[4..]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void UInt64_To_LE(ReadOnlySpan<ulong> ns, Span<byte> bs) { for (int i = 0; i < ns.Length; ++i) diff --git a/crypto/src/pqc/crypto/lms/LMSContext.cs b/crypto/src/pqc/crypto/lms/LMSContext.cs index 35c33093b..2113184fe 100644 --- a/crypto/src/pqc/crypto/lms/LMSContext.cs +++ b/crypto/src/pqc/crypto/lms/LMSContext.cs @@ -124,5 +124,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms { digest.Reset(); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan<byte> input) + { + digest.BlockUpdate(input); + } + + public int DoFinal(Span<byte> output) + { + return digest.DoFinal(output); + } +#endif } -} \ No newline at end of file +} diff --git a/crypto/src/pqc/crypto/sphincsplus/Adrs.cs b/crypto/src/pqc/crypto/sphincsplus/Adrs.cs index a6ec753fd..229fe8133 100644 --- a/crypto/src/pqc/crypto/sphincsplus/Adrs.cs +++ b/crypto/src/pqc/crypto/sphincsplus/Adrs.cs @@ -1,11 +1,10 @@ - using System; + using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { - internal class Adrs { public static uint WOTS_HASH = 0; diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs index 1e2ef67cf..65f75b068 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs @@ -1,7 +1,9 @@ using System; -namespace Org.BouncyCastle.pqc.crypto.sphincsplus + +namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { - class HarakaS256Digest : HarakaSBase + internal class HarakaS256Digest + : HarakaSBase { public HarakaS256Digest(HarakaSXof harakaSXof) { diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs index 883d731be..a1c1e3db9 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs @@ -1,5 +1,6 @@ using System; -namespace Org.BouncyCastle.pqc.crypto.sphincsplus + +namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { /** * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf @@ -7,7 +8,8 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus * Haraka512-256 with reference to Python Reference Impl from: https://github.com/sphincs/sphincsplus * </p> */ - class HarakaS512Digest : HarakaSBase + internal class HarakaS512Digest + : HarakaSBase { public HarakaS512Digest(HarakaSBase harakaSBase) { @@ -43,7 +45,6 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus off += len; } - public int DoFinal(byte[] output, int outOff) { byte[] s = new byte[64]; diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs index c10e2e195..8af826dba 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs @@ -1,7 +1,8 @@ using System; + using Org.BouncyCastle.Utilities; -namespace Org.BouncyCastle.pqc.crypto.sphincsplus +namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { /** * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf @@ -9,19 +10,19 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus * Haraka512-256 with reference to Python Reference Impl from: https://github.com/sphincs/sphincsplus * </p> */ - public class HarakaSBase + internal class HarakaSBase { internal ulong[][] haraka512_rc = new ulong[][]{ - new ulong[]{0x24cf0ab9086f628bL, 0xbdd6eeecc83b8382L, 0xd96fb0306cdad0a7L, 0xaace082ac8f95f89L, 0x449d8e8870d7041fL, 0x49bb2f80b2b3e2f8L, 0x0569ae98d93bb258L, 0x23dc9691e7d6a4b1L}, - new ulong[]{0xd8ba10ede0fe5b6eL, 0x7ecf7dbe424c7b8eL, 0x6ea9949c6df62a31L, 0xbf3f3c97ec9c313eL, 0x241d03a196a1861eL, 0xead3a51116e5a2eaL, 0x77d479fcad9574e3L, 0x18657a1af894b7a0L}, - new ulong[]{0x10671e1a7f595522L, 0xd9a00ff675d28c7bL, 0x2f1edf0d2b9ba661L, 0xb8ff58b8e3de45f9L, 0xee29261da9865c02L, 0xd1532aa4b50bdf43L, 0x8bf858159b231bb1L, 0xdf17439d22d4f599L}, - new ulong[]{0xdd4b2f0870b918c0L, 0x757a81f3b39b1bb6L, 0x7a5c556898952e3fL, 0x7dd70a16d915d87aL, 0x3ae61971982b8301L, 0xc3ab319e030412beL, 0x17c0033ac094a8cbL, 0x5a0630fc1a8dc4efL}, - new ulong[]{0x17708988c1632f73L, 0xf92ddae090b44f4fL, 0x11ac0285c43aa314L, 0x509059941936b8baL, 0xd03e152fa2ce9b69L, 0x3fbcbcb63a32998bL, 0x6204696d692254f7L, 0x915542ed93ec59b4L}, - new ulong[]{0xf4ed94aa8879236eL, 0xff6cb41cd38e03c0L, 0x069b38602368aeabL, 0x669495b820f0ddbaL, 0xf42013b1b8bf9e3dL, 0xcf935efe6439734dL, 0xbc1dcf42ca29e3f8L, 0x7e6d3ed29f78ad67L}, - new ulong[]{0xf3b0f6837ffcddaaL, 0x3a76faef934ddf41L, 0xcec7ae583a9c8e35L, 0xe4dd18c68f0260afL, 0x2c0e5df1ad398eaaL, 0x478df5236ae22e8cL, 0xfb944c46fe865f39L, 0xaa48f82f028132baL}, - new ulong[]{0x231b9ae2b76aca77L, 0x292a76a712db0b40L, 0x5850625dc8134491L, 0x73137dd469810fb5L, 0x8a12a6a202a474fdL, 0xd36fd9daa78bdb80L, 0xb34c5e733505706fL, 0xbaf1cdca818d9d96L}, - new ulong[]{0x2e99781335e8c641L, 0xbddfe5cce47d560eL, 0xf74e9bf32e5e040cL, 0x1d7a709d65996be9L, 0x670df36a9cf66cddL, 0xd05ef84a176a2875L, 0x0f888e828cb1c44eL, 0x1a79e9c9727b052cL}, - new ulong[]{0x83497348628d84deL, 0x2e9387d51f22a754L, 0xb000068da2f852d6L, 0x378c9e1190fd6fe5L, 0x870027c316de7293L, 0xe51a9d4462e047bbL, 0x90ecf7f8c6251195L, 0x655953bfbed90a9cL}, + new ulong[]{0x24cf0ab9086f628bL, 0xbdd6eeecc83b8382L, 0xd96fb0306cdad0a7L, 0xaace082ac8f95f89L, 0x449d8e8870d7041fL, 0x49bb2f80b2b3e2f8L, 0x0569ae98d93bb258L, 0x23dc9691e7d6a4b1L}, + new ulong[]{0xd8ba10ede0fe5b6eL, 0x7ecf7dbe424c7b8eL, 0x6ea9949c6df62a31L, 0xbf3f3c97ec9c313eL, 0x241d03a196a1861eL, 0xead3a51116e5a2eaL, 0x77d479fcad9574e3L, 0x18657a1af894b7a0L}, + new ulong[]{0x10671e1a7f595522L, 0xd9a00ff675d28c7bL, 0x2f1edf0d2b9ba661L, 0xb8ff58b8e3de45f9L, 0xee29261da9865c02L, 0xd1532aa4b50bdf43L, 0x8bf858159b231bb1L, 0xdf17439d22d4f599L}, + new ulong[]{0xdd4b2f0870b918c0L, 0x757a81f3b39b1bb6L, 0x7a5c556898952e3fL, 0x7dd70a16d915d87aL, 0x3ae61971982b8301L, 0xc3ab319e030412beL, 0x17c0033ac094a8cbL, 0x5a0630fc1a8dc4efL}, + new ulong[]{0x17708988c1632f73L, 0xf92ddae090b44f4fL, 0x11ac0285c43aa314L, 0x509059941936b8baL, 0xd03e152fa2ce9b69L, 0x3fbcbcb63a32998bL, 0x6204696d692254f7L, 0x915542ed93ec59b4L}, + new ulong[]{0xf4ed94aa8879236eL, 0xff6cb41cd38e03c0L, 0x069b38602368aeabL, 0x669495b820f0ddbaL, 0xf42013b1b8bf9e3dL, 0xcf935efe6439734dL, 0xbc1dcf42ca29e3f8L, 0x7e6d3ed29f78ad67L}, + new ulong[]{0xf3b0f6837ffcddaaL, 0x3a76faef934ddf41L, 0xcec7ae583a9c8e35L, 0xe4dd18c68f0260afL, 0x2c0e5df1ad398eaaL, 0x478df5236ae22e8cL, 0xfb944c46fe865f39L, 0xaa48f82f028132baL}, + new ulong[]{0x231b9ae2b76aca77L, 0x292a76a712db0b40L, 0x5850625dc8134491L, 0x73137dd469810fb5L, 0x8a12a6a202a474fdL, 0xd36fd9daa78bdb80L, 0xb34c5e733505706fL, 0xbaf1cdca818d9d96L}, + new ulong[]{0x2e99781335e8c641L, 0xbddfe5cce47d560eL, 0xf74e9bf32e5e040cL, 0x1d7a709d65996be9L, 0x670df36a9cf66cddL, 0xd05ef84a176a2875L, 0x0f888e828cb1c44eL, 0x1a79e9c9727b052cL}, + new ulong[]{0x83497348628d84deL, 0x2e9387d51f22a754L, 0xb000068da2f852d6L, 0x378c9e1190fd6fe5L, 0x870027c316de7293L, 0xe51a9d4462e047bbL, 0x90ecf7f8c6251195L, 0x655953bfbed90a9cL}, }; internal uint[][] haraka256_rc = new uint[10][]; @@ -791,4 +792,3 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus } } } - diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs index bcd62aa87..97bd3c07c 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs @@ -1,7 +1,9 @@ using System; -namespace Org.BouncyCastle.pqc.crypto.sphincsplus + +namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { - class HarakaSXof : HarakaSBase + internal class HarakaSXof + : HarakaSBase { public String GetAlgorithmName() { @@ -82,6 +84,4 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus return outLen; } } - } - diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs index f93d43183..e7455d253 100644 --- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs +++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs @@ -6,12 +6,11 @@ using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; -using Org.BouncyCastle.pqc.crypto.sphincsplus; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { - abstract class SPHINCSPlusEngine + internal abstract class SPHINCSPlusEngine { bool robust; @@ -91,7 +90,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus this.T = 1U << a; } - public abstract void init(byte[] pkSeed); + public abstract void Init(byte[] pkSeed); public abstract byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1); @@ -146,7 +145,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus this.msgDigestBuf = new byte[msgDigest.GetDigestSize()]; } - public override void init(byte[] pkSeed) + public override void Init(byte[] pkSeed) { byte[] padding = new byte[bl]; @@ -366,7 +365,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus this.maskDigest = new ShakeDigest(256); } - public override void init(byte[] pkSeed) + public override void Init(byte[] pkSeed) { // TODO: add use of memo } @@ -526,7 +525,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus } - internal class HarakaSEngine : SPHINCSPlusEngine + internal class HarakaSEngine + : SPHINCSPlusEngine { public HarakaSXof harakaSXof; public HarakaS256Digest harakaS256Digest; @@ -537,7 +537,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus } - public override void init(byte[] pkSeed) + public override void Init(byte[] pkSeed) { harakaSXof = new HarakaSXof(pkSeed); harakaS256Digest = new HarakaS256Digest(harakaSXof); diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs index dbb93a812..0eaf9557a 100644 --- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs +++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs @@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus sk = new SK(SecRand(engine.N), SecRand(engine.N)); pkSeed = SecRand(engine.N); } - engine.init(pkSeed); + engine.Init(pkSeed); // TODO PK pk = new PK(pkSeed, new HT(engine, sk.seed, pkSeed).HTPubKey); @@ -51,11 +51,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus private byte[] SecRand(int n) { - byte[] rv = new byte[n]; - - random.NextBytes(rv); - - return rv; + return SecureRandom.GetNextBytes(random, n); } } -} \ No newline at end of file +} diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs index 1a1c90766..c6664f889 100644 --- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs +++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs @@ -60,7 +60,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus // init SPHINCSPlusEngine engine = privKey.GetParameters().GetEngine(); - engine.init(privKey.GetPublicSeed()); + engine.Init(privKey.GetPublicSeed()); // generate randomizer byte[] optRand = new byte[engine.N]; if (random != null) @@ -119,7 +119,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus // init SPHINCSPlusEngine engine = pubKey.GetParameters().GetEngine(); - engine.init(pubKey.GetSeed()); + engine.Init(pubKey.GetSeed()); Adrs adrs = new Adrs(); SIG sig = new SIG(engine.N, engine.K, engine.A, engine.D, engine.H_PRIME, engine.WOTS_LEN, signature); diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs index 2c9e89277..035280fd6 100644 --- a/crypto/src/security/DigestUtilities.cs +++ b/crypto/src/security/DigestUtilities.cs @@ -266,9 +266,22 @@ namespace Org.BouncyCastle.Security public static byte[] CalculateDigest(string algorithm, byte[] input) { IDigest digest = GetDigest(algorithm); - digest.BlockUpdate(input, 0, input.Length); - return DoFinal(digest); + return DoFinal(digest, input); + } + + public static byte[] CalculateDigest(string algorithm, byte[] buf, int off, int len) + { + IDigest digest = GetDigest(algorithm); + return DoFinal(digest, buf, off, len); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static byte[] CalculateDigest(string algorithm, ReadOnlySpan<byte> buffer) + { + IDigest digest = GetDigest(algorithm); + return DoFinal(digest, buffer); } +#endif public static byte[] DoFinal(IDigest digest) { @@ -282,5 +295,19 @@ namespace Org.BouncyCastle.Security digest.BlockUpdate(input, 0, input.Length); return DoFinal(digest); } + + public static byte[] DoFinal(IDigest digest, byte[] buf, int off, int len) + { + digest.BlockUpdate(buf, off, len); + return DoFinal(digest); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static byte[] DoFinal(IDigest digest, ReadOnlySpan<byte> buffer) + { + digest.BlockUpdate(buffer); + return DoFinal(digest); + } +#endif } } diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs index ae05a1664..1667e5d6f 100644 --- a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs +++ b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs @@ -10,11 +10,11 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC : TlsAeadCipherImpl { private readonly bool m_isEncrypting; - private readonly IAeadBlockCipher m_cipher; + private readonly IAeadCipher m_cipher; private KeyParameter key; - internal BcTlsAeadCipherImpl(IAeadBlockCipher cipher, bool isEncrypting) + internal BcTlsAeadCipherImpl(IAeadCipher cipher, bool isEncrypting) { this.m_cipher = cipher; this.m_isEncrypting = isEncrypting; diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs index 0375950c3..e84361e49 100644 --- a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs +++ b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs @@ -499,8 +499,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) { - BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Ccm(), true); - BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Ccm(), false); + BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), true); + BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), false); return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_CCM); } @@ -508,8 +508,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) { - BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Gcm(), true); - BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Gcm(), false); + BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), true); + BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), false); return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM); } @@ -517,8 +517,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC protected virtual TlsAeadCipher CreateCipher_Aria_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) { - BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aria_Gcm(), true); - BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aria_Gcm(), false); + BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aria_Gcm(), true); + BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aria_Gcm(), false); return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM); } @@ -526,8 +526,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) { - BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Camellia_Gcm(), true); - BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Camellia_Gcm(), false); + BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Camellia_Gcm(), true); + BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Camellia_Gcm(), false); return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM); } @@ -546,16 +546,16 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC protected virtual TlsAeadCipher CreateCipher_SM4_Ccm(TlsCryptoParameters cryptoParams) { - BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Ccm(), true); - BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Ccm(), false); + BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Ccm(), true); + BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Ccm(), false); return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_CCM); } protected virtual TlsAeadCipher CreateCipher_SM4_Gcm(TlsCryptoParameters cryptoParams) { - BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Gcm(), true); - BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Gcm(), false); + BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Gcm(), true); + BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Gcm(), false); return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_GCM); } @@ -596,43 +596,43 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC return new SM4Engine(); } - protected virtual IAeadBlockCipher CreateCcmMode(IBlockCipher engine) + protected virtual IAeadCipher CreateCcmMode(IBlockCipher engine) { return new CcmBlockCipher(engine); } - protected virtual IAeadBlockCipher CreateGcmMode(IBlockCipher engine) + protected virtual IAeadCipher CreateGcmMode(IBlockCipher engine) { // TODO Consider allowing custom configuration of multiplier return new GcmBlockCipher(engine); } - protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm() + protected virtual IAeadCipher CreateAeadCipher_Aes_Ccm() { return CreateCcmMode(CreateAesEngine()); } - protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm() + protected virtual IAeadCipher CreateAeadCipher_Aes_Gcm() { return CreateGcmMode(CreateAesEngine()); } - protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aria_Gcm() + protected virtual IAeadCipher CreateAeadCipher_Aria_Gcm() { return CreateGcmMode(CreateAriaEngine()); } - protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm() + protected virtual IAeadCipher CreateAeadCipher_Camellia_Gcm() { return CreateGcmMode(CreateCamelliaEngine()); } - protected virtual IAeadBlockCipher CreateAeadBlockCipher_SM4_Ccm() + protected virtual IAeadCipher CreateAeadCipher_SM4_Ccm() { return CreateCcmMode(CreateSM4Engine()); } - protected virtual IAeadBlockCipher CreateAeadBlockCipher_SM4_Gcm() + protected virtual IAeadCipher CreateAeadCipher_SM4_Gcm() { return CreateGcmMode(CreateSM4Engine()); } diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs index ff907ac80..b0c899500 100644 --- a/crypto/src/util/Integers.cs +++ b/crypto/src/util/Integers.cs @@ -1,5 +1,6 @@ using System; #if NETCOREAPP3_0_OR_GREATER +using System.Numerics; using System.Runtime.Intrinsics.X86; #endif @@ -107,24 +108,40 @@ namespace Org.BouncyCastle.Utilities public static int RotateLeft(int i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return (int)BitOperations.RotateLeft((uint)i, distance); +#else return (i << distance) ^ (int)((uint)i >> -distance); +#endif } [CLSCompliant(false)] public static uint RotateLeft(uint i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return BitOperations.RotateLeft(i, distance); +#else return (i << distance) ^ (i >> -distance); +#endif } public static int RotateRight(int i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return (int)BitOperations.RotateRight((uint)i, distance); +#else return (int)((uint)i >> distance) ^ (i << -distance); +#endif } [CLSCompliant(false)] public static uint RotateRight(uint i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return BitOperations.RotateRight(i, distance); +#else return (i >> distance) ^ (i << -distance); +#endif } } } diff --git a/crypto/src/util/Longs.cs b/crypto/src/util/Longs.cs index 45dd91090..0bb35de25 100644 --- a/crypto/src/util/Longs.cs +++ b/crypto/src/util/Longs.cs @@ -1,5 +1,6 @@ using System; #if NETCOREAPP3_0_OR_GREATER +using System.Numerics; using System.Runtime.Intrinsics.X86; #endif @@ -108,24 +109,40 @@ namespace Org.BouncyCastle.Utilities public static long RotateLeft(long i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return (long)BitOperations.RotateLeft((ulong)i, distance); +#else return (i << distance) ^ (long)((ulong)i >> -distance); +#endif } [CLSCompliant(false)] public static ulong RotateLeft(ulong i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return BitOperations.RotateLeft(i, distance); +#else return (i << distance) ^ (i >> -distance); +#endif } public static long RotateRight(long i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return (long)BitOperations.RotateRight((ulong)i, distance); +#else return (long)((ulong)i >> distance) ^ (i << -distance); +#endif } [CLSCompliant(false)] public static ulong RotateRight(ulong i, int distance) { +#if NETCOREAPP3_0_OR_GREATER + return BitOperations.RotateRight(i, distance); +#else return (i >> distance) ^ (i << -distance); +#endif } } } diff --git a/crypto/test/BouncyCastle.Crypto.Tests.csproj b/crypto/test/BouncyCastle.Crypto.Tests.csproj index 43f348fa8..8ae0914d5 100644 --- a/crypto/test/BouncyCastle.Crypto.Tests.csproj +++ b/crypto/test/BouncyCastle.Crypto.Tests.csproj @@ -22,7 +22,7 @@ <EmbeddedResource Include="data\**\*.*" Exclude="**\README.txt" /> </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" /> <PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> </ItemGroup> diff --git a/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs b/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs index 65209abdb..3e90c5752 100644 --- a/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs +++ b/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs @@ -512,6 +512,15 @@ namespace Org.BouncyCastle.Crypto.Prng.Test return cipher.ProcessBlock(input, inOff, output, outOff); } + // NOTE: .NET Core 2.1 has Span<T>, 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 + public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) + { + return cipher.ProcessBlock(input, output); + } +#endif + public void Reset() { cipher.Reset(); 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..1809c07fb 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<T>, 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<T>, 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/GcmSivTest.cs b/crypto/test/src/crypto/test/GcmSivTest.cs index 16e53abdf..47f824db6 100644 --- a/crypto/test/src/crypto/test/GcmSivTest.cs +++ b/crypto/test/src/crypto/test/GcmSivTest.cs @@ -74,6 +74,15 @@ namespace Org.BouncyCastle.Crypto.Tests pCipher.DoFinal(myOutput, 0); IsTrue("Encryption mismatch", Arrays.AreEqual(myExpected, myOutput)); + if (myData.Length >= 2) + { + /* Repeat processing checking processBytes with non-empty internal buffer */ + pCipher.ProcessByte(myData[0], null, 0); + pCipher.ProcessBytes(myData, 1, myData.Length - 1, null, 0); + pCipher.DoFinal(myOutput, 0); + IsTrue("Encryption mismatch", Arrays.AreEqual(myExpected, myOutput)); + } + /* Re-initialise the cipher */ pCipher.Init(false, myParams); pCipher.ProcessBytes(myOutput, 0, myOutput.Length, null, 0); diff --git a/crypto/test/src/crypto/test/Haraka256DigestTest.cs b/crypto/test/src/crypto/test/Haraka256DigestTest.cs index 3f7706211..18ff65ddc 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<T>, 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..1ef178222 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<T>, 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..918fcbea4 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<T>, 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<T>, 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 + } } } |