diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-08-24 00:50:53 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-08-24 00:50:53 +0700 |
commit | d3037c2b2f4f76c05e12b4d8e2c3326601440d6f (patch) | |
tree | 138533d108a8033758fbad1a81e942904f4d0557 /crypto/src | |
parent | Cleanup (diff) | |
download | BouncyCastle.NET-ed25519-d3037c2b2f4f76c05e12b4d8e2c3326601440d6f.tar.xz |
Span-based variant for IBlockCipher.ProcessBlock
Diffstat (limited to 'crypto/src')
42 files changed, 3272 insertions, 1391 deletions
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/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/CfbBlockCipherMac.cs b/crypto/src/crypto/macs/CfbBlockCipherMac.cs index 364cf8499..e10bb438d 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; diff --git a/crypto/src/crypto/macs/DSTU7564Mac.cs b/crypto/src/crypto/macs/DSTU7564Mac.cs index 36e86418a..fc905cc99 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"); @@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Crypto.Macs public int DoFinal(byte[] output, int outOff) { - Check.OutputLength(output, outOff, macSize, "Output buffer too short"); + Check.OutputLength(output, outOff, macSize, "output buffer too short"); if (paddedKey == null) throw new InvalidOperationException(AlgorithmName + " not initialised"); 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..8f0acce52 100644 --- a/crypto/src/crypto/modes/CcmBlockCipher.cs +++ b/crypto/src/crypto/modes/CcmBlockCipher.cs @@ -134,7 +134,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/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..2ea8eef1d 100644 --- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs +++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs @@ -472,8 +472,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."); } } 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/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); |