diff options
author | David Hook <dgh@cryptoworkshop.com> | 2022-08-25 12:58:05 +1000 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2022-08-25 12:58:05 +1000 |
commit | fa5fc2339e292e53a8c1c9cc16c2e8242f5066d1 (patch) | |
tree | 5487957c1417f3ae0028dd62de3d2b26368135eb /crypto/src | |
parent | initial CRYSTALS-Kyber implementation (diff) | |
parent | Span-based variant for IMac.DoFinal (diff) | |
download | BouncyCastle.NET-ed25519-fa5fc2339e292e53a8c1c9cc16c2e8242f5066d1.tar.xz |
Merge remote-tracking branch 'refs/remotes/origin/master'
Diffstat (limited to 'crypto/src')
141 files changed, 6994 insertions, 2361 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 } } } |