From f01393e0ce47b124d711e7c3512bd2fab50e98bf Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 31 Aug 2022 01:48:36 +0700 Subject: Span-based variants for IAeadCipher.ProcessByte(s) --- crypto/src/crypto/engines/ChaCha7539Engine.cs | 65 +++- crypto/src/crypto/modes/CcmBlockCipher.cs | 30 +- crypto/src/crypto/modes/ChaCha20Poly1305.cs | 195 +++++++++- crypto/src/crypto/modes/EAXBlockCipher.cs | 104 +++++- crypto/src/crypto/modes/GCMBlockCipher.cs | 505 ++++++++++++++++++++++---- crypto/src/crypto/modes/GcmSivBlockCipher.cs | 74 +++- crypto/src/crypto/modes/IAeadCipher.cs | 8 + crypto/src/crypto/modes/KCcmBlockCipher.cs | 21 +- crypto/src/crypto/modes/OCBBlockCipher.cs | 69 ++++ crypto/src/util/Spans.cs | 18 + 10 files changed, 950 insertions(+), 139 deletions(-) create mode 100644 crypto/src/util/Spans.cs (limited to 'crypto') diff --git a/crypto/src/crypto/engines/ChaCha7539Engine.cs b/crypto/src/crypto/engines/ChaCha7539Engine.cs index d1dd9755b..a438c0bfb 100644 --- a/crypto/src/crypto/engines/ChaCha7539Engine.cs +++ b/crypto/src/crypto/engines/ChaCha7539Engine.cs @@ -81,16 +81,24 @@ namespace Org.BouncyCastle.Crypto.Engines while (inLen >= 128) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlocks2(inBuf.AsSpan(inOff), outBuf.AsSpan(outOff)); +#else ProcessBlocks2(inBuf, inOff, outBuf, outOff); - inOff += 128; +#endif + inOff += 128; inLen -= 128; outOff += 128; } if (inLen >= 64) { - ImplProcessBlock(inBuf, inOff, outBuf, outOff); - inOff += 64; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ImplProcessBlock(inBuf.AsSpan(inOff), outBuf.AsSpan(outOff)); +#else + ImplProcessBlock(inBuf, inOff, outBuf, outOff); +#endif + inOff += 64; inLen -= 64; outOff += 64; } @@ -111,7 +119,8 @@ namespace Org.BouncyCastle.Crypto.Engines // TODO Prevent re-use if encrypting } - internal void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void ProcessBlock(ReadOnlySpan input, Span output) { if (!initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); @@ -120,10 +129,10 @@ namespace Org.BouncyCastle.Crypto.Engines Debug.Assert(index == 0); - ImplProcessBlock(inBytes, inOff, outBytes, outOff); + ImplProcessBlock(input, output); } - internal void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + internal void ProcessBlocks2(ReadOnlySpan input, Span output) { if (!initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); @@ -135,17 +144,56 @@ namespace Org.BouncyCastle.Crypto.Engines #if NETCOREAPP3_0_OR_GREATER if (Avx2.IsSupported) { - ImplProcessBlocks2_X86_Avx2(rounds, engineState, inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)); + ImplProcessBlocks2_X86_Avx2(rounds, engineState, input, output); return; } if (Sse2.IsSupported) { - ImplProcessBlocks2_X86_Sse2(rounds, engineState, inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)); + ImplProcessBlocks2_X86_Sse2(rounds, engineState, input, output); return; } #endif + { + ImplProcessBlock(input, output); + ImplProcessBlock(input[64..], output[64..]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ImplProcessBlock(ReadOnlySpan input, Span output) + { + ChaChaEngine.ChachaCore(rounds, engineState, keyStream); + AdvanceCounter(); + + for (int i = 0; i < 64; ++i) + { + output[i] = (byte)(keyStream[i] ^ input[i]); + } + } +#else + internal void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if (LimitExceeded(64U)) + throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV"); + + Debug.Assert(index == 0); + + ImplProcessBlock(inBytes, inOff, outBytes, outOff); + } + + internal void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if (LimitExceeded(128U)) + throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV"); + + Debug.Assert(index == 0); + { ImplProcessBlock(inBytes, inOff, outBytes, outOff); ImplProcessBlock(inBytes, inOff + 64, outBytes, outOff + 64); @@ -165,6 +213,7 @@ namespace Org.BouncyCastle.Crypto.Engines outBuf[outOff + i] = (byte)(keyStream[i] ^ inBuf[inOff + i]); } } +#endif #if NETCOREAPP3_0_OR_GREATER [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs index 46e7b9c55..0a2adb57d 100644 --- a/crypto/src/crypto/modes/CcmBlockCipher.cs +++ b/crypto/src/crypto/modes/CcmBlockCipher.cs @@ -125,22 +125,23 @@ namespace Org.BouncyCastle.Crypto.Modes } #endif - public virtual int ProcessByte( - byte input, - byte[] outBytes, - int outOff) + public virtual int ProcessByte(byte input, byte[] outBytes, int outOff) { data.WriteByte(input); return 0; } - public virtual int ProcessBytes( - byte[] inBytes, - int inOff, - int inLen, - byte[] outBytes, - int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) + { + data.WriteByte(input); + + return 0; + } +#endif + + public virtual int ProcessBytes(byte[] inBytes, int inOff, int inLen, byte[] outBytes, int outOff) { Check.DataLength(inBytes, inOff, inLen, "input buffer too short"); @@ -149,6 +150,15 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + data.Write(input); + + return 0; + } +#endif + public virtual int DoFinal(byte[] outBytes, int outOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs index 9e30dc510..299387cdf 100644 --- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs +++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs @@ -234,7 +234,11 @@ namespace Org.BouncyCastle.Crypto.Modes if (++mBufPos == mBuf.Length) { mPoly1305.BlockUpdate(mBuf, 0, BufSize); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(mBuf, outBytes.AsSpan(outOff)); +#else ProcessBlock(mBuf, 0, outBytes, outOff); +#endif Array.Copy(mBuf, BufSize, mBuf, 0, MacSize); this.mBufPos = MacSize; return BufSize; @@ -247,7 +251,11 @@ namespace Org.BouncyCastle.Crypto.Modes mBuf[mBufPos] = input; if (++mBufPos == BufSize) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(mBuf, outBytes.AsSpan(outOff)); +#else ProcessBlock(mBuf, 0, outBytes, outOff); +#endif mPoly1305.BlockUpdate(outBytes, outOff, BufSize); this.mBufPos = 0; return BufSize; @@ -260,6 +268,46 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) + { + CheckData(); + + switch (mState) + { + case State.DecData: + { + mBuf[mBufPos] = input; + if (++mBufPos == mBuf.Length) + { + mPoly1305.BlockUpdate(mBuf.AsSpan(0, BufSize)); + ProcessBlock(mBuf, output); + Array.Copy(mBuf, BufSize, mBuf, 0, MacSize); + this.mBufPos = MacSize; + return BufSize; + } + + return 0; + } + case State.EncData: + { + mBuf[mBufPos] = input; + if (++mBufPos == BufSize) + { + ProcessBlock(mBuf, output); + mPoly1305.BlockUpdate(output[..BufSize]); + this.mBufPos = 0; + return BufSize; + } + + return 0; + } + default: + throw new InvalidOperationException(); + } + } +#endif + public virtual int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { if (null == inBytes) @@ -280,6 +328,9 @@ namespace Org.BouncyCastle.Crypto.Modes if (outOff < 0) throw new ArgumentException("cannot be negative", "outOff"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff)); +#else CheckData(); int resultLen = 0; @@ -388,8 +439,120 @@ namespace Org.BouncyCastle.Crypto.Modes } return resultLen; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + CheckData(); + + int resultLen = 0; + + switch (mState) + { + case State.DecData: + { + int available = mBuf.Length - mBufPos; + if (input.Length < available) + { + input.CopyTo(mBuf.AsSpan(mBufPos)); + mBufPos += input.Length; + break; + } + + if (mBufPos >= BufSize) + { + mPoly1305.BlockUpdate(mBuf.AsSpan(0, BufSize)); + ProcessBlock(mBuf, output); + Array.Copy(mBuf, BufSize, mBuf, 0, mBufPos -= BufSize); + resultLen = BufSize; + + available += BufSize; + if (input.Length < available) + { + input.CopyTo(mBuf.AsSpan(mBufPos)); + mBufPos += input.Length; + break; + } + } + + int inLimit1 = mBuf.Length; + int inLimit2 = inLimit1 + BufSize; + + available = BufSize - mBufPos; + input[..available].CopyTo(mBuf.AsSpan(mBufPos)); + mPoly1305.BlockUpdate(mBuf.AsSpan(0, BufSize)); + ProcessBlock(mBuf, output[resultLen..]); + input = input[available..]; + resultLen += BufSize; + + while (input.Length >= inLimit2) + { + mPoly1305.BlockUpdate(input[..(BufSize * 2)]); + ProcessBlocks2(input, output[resultLen..]); + input = input[(BufSize * 2)..]; + resultLen += BufSize * 2; + } + + if (input.Length >= inLimit1) + { + mPoly1305.BlockUpdate(input[..BufSize]); + ProcessBlock(input, output[resultLen..]); + input = input[BufSize..]; + resultLen += BufSize; + } + + mBufPos = input.Length; + input.CopyTo(mBuf); + break; + } + case State.EncData: + { + int available = BufSize - mBufPos; + if (input.Length < available) + { + input.CopyTo(mBuf.AsSpan(mBufPos)); + mBufPos += input.Length; + break; + } + + if (mBufPos > 0) + { + input[..available].CopyTo(mBuf.AsSpan(mBufPos)); + ProcessBlock(mBuf, output); + input = input[available..]; + resultLen = BufSize; + } + + while (input.Length >= BufSize * 2) + { + ProcessBlocks2(input, output[resultLen..]); + input = input[(BufSize * 2)..]; + resultLen += BufSize * 2; + } + + if (input.Length >= BufSize) + { + ProcessBlock(input, output[resultLen..]); + input = input[BufSize..]; + resultLen += BufSize; + } + + mPoly1305.BlockUpdate(output[..resultLen]); + + mBufPos = input.Length; + input.CopyTo(mBuf); + break; + } + default: + throw new InvalidOperationException(); + } + + return resultLen; + } +#endif + public virtual int DoFinal(byte[] outBytes, int outOff) { if (null == outBytes) @@ -621,25 +784,25 @@ namespace Org.BouncyCastle.Crypto.Modes } } - private void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void ProcessBlock(ReadOnlySpan input, Span output) { - Check.OutputLength(outBytes, outOff, 64, "output buffer too short"); + Check.OutputLength(output, 64, "output buffer too short"); - mChacha20.ProcessBlock(inBytes, inOff, outBytes, outOff); + mChacha20.ProcessBlock(input, output); this.mDataCount = IncrementCount(mDataCount, 64U, DataLimit); } - private void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + private void ProcessBlocks2(ReadOnlySpan input, Span output) { - Check.OutputLength(outBytes, outOff, 128, "output buffer too short"); + Check.OutputLength(output, 128, "output buffer too short"); - mChacha20.ProcessBlocks2(inBytes, inOff, outBytes, outOff); + mChacha20.ProcessBlocks2(input, output); this.mDataCount = IncrementCount(mDataCount, 128U, DataLimit); } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private void ProcessData(ReadOnlySpan input, Span output) { Check.OutputLength(output, input.Length, "output buffer too short"); @@ -649,6 +812,24 @@ namespace Org.BouncyCastle.Crypto.Modes this.mDataCount = IncrementCount(mDataCount, (uint)input.Length, DataLimit); } #else + private void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + Check.OutputLength(outBytes, outOff, 64, "output buffer too short"); + + mChacha20.ProcessBlock(inBytes, inOff, outBytes, outOff); + + this.mDataCount = IncrementCount(mDataCount, 64U, DataLimit); + } + + private void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + Check.OutputLength(outBytes, outOff, 128, "output buffer too short"); + + mChacha20.ProcessBlocks2(inBytes, inOff, outBytes, outOff); + + this.mDataCount = IncrementCount(mDataCount, 128U, DataLimit); + } + private void ProcessData(byte[] inBytes, int inOff, int inLen, byte[] outBytes, int outOff) { Check.OutputLength(outBytes, outOff, inLen, "output buffer too short"); diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs index 440b5f439..e63826159 100644 --- a/crypto/src/crypto/modes/EAXBlockCipher.cs +++ b/crypto/src/crypto/modes/EAXBlockCipher.cs @@ -209,25 +209,33 @@ namespace Org.BouncyCastle.Crypto.Modes } #endif - public virtual int ProcessByte( - byte input, - byte[] outBytes, - int outOff) + public virtual int ProcessByte(byte input, byte[] outBytes, int outOff) { InitCipher(); - return Process(input, outBytes, outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Process(input, Spans.FromNullable(outBytes, outOff)); +#else + return Process(input, outBytes, outOff); +#endif } - public virtual int ProcessBytes( - byte[] inBytes, - int inOff, - int len, - byte[] outBytes, - int outOff) - { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) + { InitCipher(); + return Process(input, output); + } +#endif + + public virtual int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) + { + InitCipher(); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff)); +#else int resultLen = 0; for (int i = 0; i != len; i++) @@ -236,9 +244,27 @@ namespace Org.BouncyCastle.Crypto.Modes } return resultLen; +#endif } - public virtual int DoFinal(byte[] outBytes, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + InitCipher(); + + int len = input.Length; + int resultLen = 0; + + for (int i = 0; i != len; i++) + { + resultLen += Process(input[i], output[resultLen..]); + } + + return resultLen; + } +#endif + + public virtual int DoFinal(byte[] outBytes, int outOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER return DoFinal(outBytes.AsSpan(outOff)); @@ -389,12 +415,49 @@ namespace Org.BouncyCastle.Crypto.Modes return totalData < macSize ? 0 : totalData - macSize; } - private int Process( - byte b, - byte[] outBytes, - int outOff) - { - bufBlock[bufOff++] = b; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int Process(byte b, Span output) + { + bufBlock[bufOff++] = b; + + if (bufOff == bufBlock.Length) + { + Check.OutputLength(output, blockSize, "output buffer too short"); + + // TODO Could move the ProcessByte(s) calls to here + //InitCipher(); + + int size; + + if (forEncryption) + { + size = cipher.ProcessBlock(bufBlock, output); + + mac.BlockUpdate(output[..blockSize]); + } + else + { + mac.BlockUpdate(bufBlock.AsSpan(0, blockSize)); + + size = cipher.ProcessBlock(bufBlock, output); + } + + bufOff = 0; + if (!forEncryption) + { + Array.Copy(bufBlock, blockSize, bufBlock, 0, macSize); + bufOff = macSize; + } + + return size; + } + + return 0; + } +#else + private int Process(byte b, byte[] outBytes, int outOff) + { + bufBlock[bufOff++] = b; if (bufOff == bufBlock.Length) { @@ -430,8 +493,9 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } +#endif - private bool VerifyMac(byte[] mac, int off) + private bool VerifyMac(byte[] mac, int off) { int nonEqual = 0; diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs index c2b2cf86d..2cc7ff62d 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs @@ -379,12 +379,20 @@ namespace Org.BouncyCastle.Crypto.Modes { if (forEncryption) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + EncryptBlock(bufBlock, output.AsSpan(outOff)); +#else EncryptBlock(bufBlock, 0, output, outOff); +#endif bufOff = 0; } else { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + DecryptBlock(bufBlock, output.AsSpan(outOff)); +#else DecryptBlock(bufBlock, 0, output, outOff); +#endif Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize); bufOff = macSize; } @@ -393,12 +401,40 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) + { + CheckStatus(); + + bufBlock[bufOff] = input; + if (++bufOff == bufBlock.Length) + { + if (forEncryption) + { + EncryptBlock(bufBlock, output); + bufOff = 0; + } + else + { + DecryptBlock(bufBlock, output); + Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize); + bufOff = macSize; + } + return BlockSize; + } + return 0; + } +#endif + public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { CheckStatus(); Check.DataLength(input, inOff, len, "input buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(input.AsSpan(inOff, len), Spans.FromNullable(output, outOff)); +#else int resultLen = 0; if (forEncryption) @@ -494,8 +530,109 @@ namespace Org.BouncyCastle.Crypto.Modes Array.Copy(input, inOff, bufBlock, 0, bufOff); } + return resultLen; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + CheckStatus(); + + int resultLen = 0; + + if (forEncryption) + { + if (bufOff > 0) + { + int available = BlockSize - bufOff; + if (input.Length < available) + { + input.CopyTo(bufBlock.AsSpan(bufOff)); + bufOff += input.Length; + return 0; + } + + input[..available].CopyTo(bufBlock.AsSpan(bufOff)); + EncryptBlock(bufBlock, output); + input = input[available..]; + resultLen = BlockSize; + //bufOff = 0; + } + + while (input.Length >= BlockSize * 2) + { + EncryptBlocks2(input, output[resultLen..]); + input = input[(BlockSize * 2)..]; + resultLen += BlockSize * 2; + } + + if (input.Length >= BlockSize) + { + EncryptBlock(input, output[resultLen..]); + input = input[BlockSize..]; + resultLen += BlockSize; + } + + bufOff = input.Length; + input.CopyTo(bufBlock); + } + else + { + int available = bufBlock.Length - bufOff; + if (input.Length < available) + { + input.CopyTo(bufBlock.AsSpan(bufOff)); + bufOff += input.Length; + return 0; + } + + if (bufOff >= BlockSize) + { + DecryptBlock(bufBlock, output); + Array.Copy(bufBlock, BlockSize, bufBlock, 0, bufOff -= BlockSize); + resultLen = BlockSize; + + available += BlockSize; + if (input.Length < available) + { + input.CopyTo(bufBlock.AsSpan(bufOff)); + bufOff += input.Length; + return resultLen; + } + } + + int inLimit1 = bufBlock.Length; + int inLimit2 = inLimit1 + BlockSize; + + available = BlockSize - bufOff; + input[..available].CopyTo(bufBlock.AsSpan(bufOff)); + DecryptBlock(bufBlock, output[resultLen..]); + input = input[available..]; + resultLen += BlockSize; + //bufOff = 0; + + while (input.Length >= inLimit2) + { + DecryptBlocks2(input, output[resultLen..]); + input = input[(BlockSize * 2)..]; + resultLen += BlockSize * 2; + } + + if (input.Length >= inLimit1) + { + DecryptBlock(input, output[resultLen..]); + input = input[BlockSize..]; + resultLen += BlockSize; + } + + bufOff = input.Length; + input.CopyTo(bufBlock); + } + return resultLen; } +#endif public int DoFinal(byte[] output, int outOff) { @@ -670,29 +807,30 @@ namespace Org.BouncyCastle.Crypto.Modes } } - private void DecryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void DecryptBlock(ReadOnlySpan input, Span output) { - Check.OutputLength(outBuf, outOff, BlockSize, "Output buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); if (totalLength == 0) { InitCipher(); } - byte[] ctrBlock = new byte[BlockSize]; + Span ctrBlock = stackalloc byte[BlockSize]; GetNextCtrBlock(ctrBlock); #if NETCOREAPP3_0_OR_GREATER if (Sse2.IsSupported && Unsafe.SizeOf>() == BlockSize) { - var t0 = Unsafe.ReadUnaligned>(ref inBuf[inOff]); + var t0 = Unsafe.ReadUnaligned>(ref Unsafe.AsRef(input[0])); var t1 = Unsafe.ReadUnaligned>(ref ctrBlock[0]); var t2 = Unsafe.ReadUnaligned>(ref S[0]); t1 = Sse2.Xor(t1, t0); t2 = Sse2.Xor(t2, t0); - Unsafe.WriteUnaligned(ref outBuf[outOff], t1); + Unsafe.WriteUnaligned(ref output[0], t1); Unsafe.WriteUnaligned(ref S[0], t2); } else @@ -700,20 +838,20 @@ namespace Org.BouncyCastle.Crypto.Modes { for (int i = 0; i < BlockSize; i += 4) { - byte c0 = inBuf[inOff + i + 0]; - byte c1 = inBuf[inOff + i + 1]; - byte c2 = inBuf[inOff + i + 2]; - byte c3 = inBuf[inOff + i + 3]; + byte c0 = input[i + 0]; + byte c1 = input[i + 1]; + byte c2 = input[i + 2]; + byte c3 = input[i + 3]; S[i + 0] ^= c0; S[i + 1] ^= c1; S[i + 2] ^= c2; S[i + 3] ^= c3; - outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); - outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); - outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); - outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); + output[i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); + output[i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); + output[i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); + output[i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); } } multiplier.MultiplyH(S); @@ -721,29 +859,29 @@ namespace Org.BouncyCastle.Crypto.Modes totalLength += BlockSize; } - private void DecryptBlocks2(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + private void DecryptBlocks2(ReadOnlySpan input, Span output) { - Check.OutputLength(outBuf, outOff, BlockSize * 2, "Output buffer too short"); + Check.OutputLength(output, BlockSize * 2, "output buffer too short"); if (totalLength == 0) { InitCipher(); } - byte[] ctrBlock = new byte[BlockSize]; + Span ctrBlock = stackalloc byte[BlockSize]; GetNextCtrBlock(ctrBlock); #if NETCOREAPP3_0_OR_GREATER if (Sse2.IsSupported && Unsafe.SizeOf>() == BlockSize) { - var t0 = Unsafe.ReadUnaligned>(ref inBuf[inOff]); + var t0 = Unsafe.ReadUnaligned>(ref Unsafe.AsRef(input[0])); var t1 = Unsafe.ReadUnaligned>(ref ctrBlock[0]); var t2 = Unsafe.ReadUnaligned>(ref S[0]); t1 = Sse2.Xor(t1, t0); t2 = Sse2.Xor(t2, t0); - Unsafe.WriteUnaligned(ref outBuf[outOff], t1); + Unsafe.WriteUnaligned(ref output[0], t1); Unsafe.WriteUnaligned(ref S[0], t2); } else @@ -751,39 +889,39 @@ namespace Org.BouncyCastle.Crypto.Modes { for (int i = 0; i < BlockSize; i += 4) { - byte c0 = inBuf[inOff + i + 0]; - byte c1 = inBuf[inOff + i + 1]; - byte c2 = inBuf[inOff + i + 2]; - byte c3 = inBuf[inOff + i + 3]; + byte c0 = input[i + 0]; + byte c1 = input[i + 1]; + byte c2 = input[i + 2]; + byte c3 = input[i + 3]; S[i + 0] ^= c0; S[i + 1] ^= c1; S[i + 2] ^= c2; S[i + 3] ^= c3; - outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); - outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); - outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); - outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); + output[i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); + output[i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); + output[i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); + output[i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); } } multiplier.MultiplyH(S); - inOff += BlockSize; - outOff += BlockSize; + input = input[BlockSize..]; + output = output[BlockSize..]; GetNextCtrBlock(ctrBlock); #if NETCOREAPP3_0_OR_GREATER if (Sse2.IsSupported && Unsafe.SizeOf>() == BlockSize) { - var t0 = Unsafe.ReadUnaligned>(ref inBuf[inOff]); + var t0 = Unsafe.ReadUnaligned>(ref Unsafe.AsRef(input[0])); var t1 = Unsafe.ReadUnaligned>(ref ctrBlock[0]); var t2 = Unsafe.ReadUnaligned>(ref S[0]); t1 = Sse2.Xor(t1, t0); t2 = Sse2.Xor(t2, t0); - Unsafe.WriteUnaligned(ref outBuf[outOff], t1); + Unsafe.WriteUnaligned(ref output[0], t1); Unsafe.WriteUnaligned(ref S[0], t2); } else @@ -791,20 +929,20 @@ namespace Org.BouncyCastle.Crypto.Modes { for (int i = 0; i < BlockSize; i += 4) { - byte c0 = inBuf[inOff + i + 0]; - byte c1 = inBuf[inOff + i + 1]; - byte c2 = inBuf[inOff + i + 2]; - byte c3 = inBuf[inOff + i + 3]; + byte c0 = input[i + 0]; + byte c1 = input[i + 1]; + byte c2 = input[i + 2]; + byte c3 = input[i + 3]; S[i + 0] ^= c0; S[i + 1] ^= c1; S[i + 2] ^= c2; S[i + 3] ^= c3; - outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); - outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); - outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); - outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); + output[i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); + output[i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); + output[i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); + output[i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); } } multiplier.MultiplyH(S); @@ -812,29 +950,29 @@ namespace Org.BouncyCastle.Crypto.Modes totalLength += BlockSize * 2; } - private void EncryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + private void EncryptBlock(ReadOnlySpan input, Span output) { - Check.OutputLength(outBuf, outOff, BlockSize, "Output buffer too short"); + Check.OutputLength(output, BlockSize, "output buffer too short"); if (totalLength == 0) { InitCipher(); } - byte[] ctrBlock = new byte[BlockSize]; + Span ctrBlock = stackalloc byte[BlockSize]; GetNextCtrBlock(ctrBlock); #if NETCOREAPP3_0_OR_GREATER if (Sse2.IsSupported && Unsafe.SizeOf>() == BlockSize) { - var t0 = Unsafe.ReadUnaligned>(ref inBuf[inOff]); + var t0 = Unsafe.ReadUnaligned>(ref Unsafe.AsRef(input[0])); var t1 = Unsafe.ReadUnaligned>(ref ctrBlock[0]); var t2 = Unsafe.ReadUnaligned>(ref S[0]); t1 = Sse2.Xor(t1, t0); t2 = Sse2.Xor(t2, t1); - Unsafe.WriteUnaligned(ref outBuf[outOff], t1); + Unsafe.WriteUnaligned(ref output[0], t1); Unsafe.WriteUnaligned(ref S[0], t2); } else @@ -842,20 +980,20 @@ namespace Org.BouncyCastle.Crypto.Modes { for (int i = 0; i < BlockSize; i += 4) { - byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]); - byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]); - byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]); - byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]); + byte c0 = (byte)(ctrBlock[i + 0] ^ input[i + 0]); + byte c1 = (byte)(ctrBlock[i + 1] ^ input[i + 1]); + byte c2 = (byte)(ctrBlock[i + 2] ^ input[i + 2]); + byte c3 = (byte)(ctrBlock[i + 3] ^ input[i + 3]); S[i + 0] ^= c0; S[i + 1] ^= c1; S[i + 2] ^= c2; S[i + 3] ^= c3; - outBuf[outOff + i + 0] = c0; - outBuf[outOff + i + 1] = c1; - outBuf[outOff + i + 2] = c2; - outBuf[outOff + i + 3] = c3; + output[i + 0] = c0; + output[i + 1] = c1; + output[i + 2] = c2; + output[i + 3] = c3; } } multiplier.MultiplyH(S); @@ -863,29 +1001,29 @@ namespace Org.BouncyCastle.Crypto.Modes totalLength += BlockSize; } - private void EncryptBlocks2(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + private void EncryptBlocks2(ReadOnlySpan input, Span output) { - Check.OutputLength(outBuf, outOff, BlockSize * 2, "Output buffer too short"); + Check.OutputLength(output, BlockSize * 2, "Output buffer too short"); if (totalLength == 0) { InitCipher(); } - byte[] ctrBlock = new byte[BlockSize]; + Span ctrBlock = stackalloc byte[BlockSize]; GetNextCtrBlock(ctrBlock); #if NETCOREAPP3_0_OR_GREATER if (Sse2.IsSupported && Unsafe.SizeOf>() == BlockSize) { - var t0 = Unsafe.ReadUnaligned>(ref inBuf[inOff]); + var t0 = Unsafe.ReadUnaligned>(ref Unsafe.AsRef(input[0])); var t1 = Unsafe.ReadUnaligned>(ref ctrBlock[0]); var t2 = Unsafe.ReadUnaligned>(ref S[0]); t1 = Sse2.Xor(t1, t0); t2 = Sse2.Xor(t2, t1); - Unsafe.WriteUnaligned(ref outBuf[outOff], t1); + Unsafe.WriteUnaligned(ref output[0], t1); Unsafe.WriteUnaligned(ref S[0], t2); } else @@ -893,43 +1031,249 @@ namespace Org.BouncyCastle.Crypto.Modes { for (int i = 0; i < BlockSize; i += 4) { - byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]); - byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]); - byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]); - byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]); + byte c0 = (byte)(ctrBlock[i + 0] ^ input[i + 0]); + byte c1 = (byte)(ctrBlock[i + 1] ^ input[i + 1]); + byte c2 = (byte)(ctrBlock[i + 2] ^ input[i + 2]); + byte c3 = (byte)(ctrBlock[i + 3] ^ input[i + 3]); S[i + 0] ^= c0; S[i + 1] ^= c1; S[i + 2] ^= c2; S[i + 3] ^= c3; - outBuf[outOff + i + 0] = c0; - outBuf[outOff + i + 1] = c1; - outBuf[outOff + i + 2] = c2; - outBuf[outOff + i + 3] = c3; + output[i + 0] = c0; + output[i + 1] = c1; + output[i + 2] = c2; + output[i + 3] = c3; } } multiplier.MultiplyH(S); - inOff += BlockSize; - outOff += BlockSize; + input = input[BlockSize..]; + output = output[BlockSize..]; GetNextCtrBlock(ctrBlock); #if NETCOREAPP3_0_OR_GREATER if (Sse2.IsSupported && Unsafe.SizeOf>() == BlockSize) { - var t0 = Unsafe.ReadUnaligned>(ref inBuf[inOff]); + var t0 = Unsafe.ReadUnaligned>(ref Unsafe.AsRef(input[0])); var t1 = Unsafe.ReadUnaligned>(ref ctrBlock[0]); var t2 = Unsafe.ReadUnaligned>(ref S[0]); t1 = Sse2.Xor(t1, t0); t2 = Sse2.Xor(t2, t1); - Unsafe.WriteUnaligned(ref outBuf[outOff], t1); + Unsafe.WriteUnaligned(ref output[0], t1); Unsafe.WriteUnaligned(ref S[0], t2); } else #endif + { + for (int i = 0; i < BlockSize; i += 4) + { + byte c0 = (byte)(ctrBlock[i + 0] ^ input[i + 0]); + byte c1 = (byte)(ctrBlock[i + 1] ^ input[i + 1]); + byte c2 = (byte)(ctrBlock[i + 2] ^ input[i + 2]); + byte c3 = (byte)(ctrBlock[i + 3] ^ input[i + 3]); + + S[i + 0] ^= c0; + S[i + 1] ^= c1; + S[i + 2] ^= c2; + S[i + 3] ^= c3; + + output[i + 0] = c0; + output[i + 1] = c1; + output[i + 2] = c2; + output[i + 3] = c3; + } + } + multiplier.MultiplyH(S); + + totalLength += BlockSize * 2; + } + + private void GetNextCtrBlock(Span block) + { + if (blocksRemaining == 0) + throw new InvalidOperationException("Attempt to process too many blocks"); + + blocksRemaining--; + + Pack.UInt32_To_BE(++counter32, counter, 12); + + cipher.ProcessBlock(counter, block); + } +#else + private void DecryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + { + Check.OutputLength(outBuf, outOff, BlockSize, "Output buffer too short"); + + if (totalLength == 0) + { + InitCipher(); + } + + byte[] ctrBlock = new byte[BlockSize]; + + GetNextCtrBlock(ctrBlock); + { + for (int i = 0; i < BlockSize; i += 4) + { + byte c0 = inBuf[inOff + i + 0]; + byte c1 = inBuf[inOff + i + 1]; + byte c2 = inBuf[inOff + i + 2]; + byte c3 = inBuf[inOff + i + 3]; + + S[i + 0] ^= c0; + S[i + 1] ^= c1; + S[i + 2] ^= c2; + S[i + 3] ^= c3; + + outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); + outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); + outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); + outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); + } + } + multiplier.MultiplyH(S); + + totalLength += BlockSize; + } + + private void DecryptBlocks2(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + { + Check.OutputLength(outBuf, outOff, BlockSize * 2, "Output buffer too short"); + + if (totalLength == 0) + { + InitCipher(); + } + + byte[] ctrBlock = new byte[BlockSize]; + + GetNextCtrBlock(ctrBlock); + { + for (int i = 0; i < BlockSize; i += 4) + { + byte c0 = inBuf[inOff + i + 0]; + byte c1 = inBuf[inOff + i + 1]; + byte c2 = inBuf[inOff + i + 2]; + byte c3 = inBuf[inOff + i + 3]; + + S[i + 0] ^= c0; + S[i + 1] ^= c1; + S[i + 2] ^= c2; + S[i + 3] ^= c3; + + outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); + outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); + outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); + outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); + } + } + multiplier.MultiplyH(S); + + inOff += BlockSize; + outOff += BlockSize; + + GetNextCtrBlock(ctrBlock); + { + for (int i = 0; i < BlockSize; i += 4) + { + byte c0 = inBuf[inOff + i + 0]; + byte c1 = inBuf[inOff + i + 1]; + byte c2 = inBuf[inOff + i + 2]; + byte c3 = inBuf[inOff + i + 3]; + + S[i + 0] ^= c0; + S[i + 1] ^= c1; + S[i + 2] ^= c2; + S[i + 3] ^= c3; + + outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]); + outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]); + outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]); + outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]); + } + } + multiplier.MultiplyH(S); + + totalLength += BlockSize * 2; + } + + private void EncryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + { + Check.OutputLength(outBuf, outOff, BlockSize, "Output buffer too short"); + + if (totalLength == 0) + { + InitCipher(); + } + + byte[] ctrBlock = new byte[BlockSize]; + + GetNextCtrBlock(ctrBlock); + { + for (int i = 0; i < BlockSize; i += 4) + { + byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]); + byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]); + byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]); + byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]); + + S[i + 0] ^= c0; + S[i + 1] ^= c1; + S[i + 2] ^= c2; + S[i + 3] ^= c3; + + outBuf[outOff + i + 0] = c0; + outBuf[outOff + i + 1] = c1; + outBuf[outOff + i + 2] = c2; + outBuf[outOff + i + 3] = c3; + } + } + multiplier.MultiplyH(S); + + totalLength += BlockSize; + } + + private void EncryptBlocks2(byte[] inBuf, int inOff, byte[] outBuf, int outOff) + { + Check.OutputLength(outBuf, outOff, BlockSize * 2, "Output buffer too short"); + + if (totalLength == 0) + { + InitCipher(); + } + + byte[] ctrBlock = new byte[BlockSize]; + + GetNextCtrBlock(ctrBlock); + { + for (int i = 0; i < BlockSize; i += 4) + { + byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]); + byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]); + byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]); + byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]); + + S[i + 0] ^= c0; + S[i + 1] ^= c1; + S[i + 2] ^= c2; + S[i + 3] ^= c3; + + outBuf[outOff + i + 0] = c0; + outBuf[outOff + i + 1] = c1; + outBuf[outOff + i + 2] = c2; + outBuf[outOff + i + 3] = c3; + } + } + multiplier.MultiplyH(S); + + inOff += BlockSize; + outOff += BlockSize; + + GetNextCtrBlock(ctrBlock); { for (int i = 0; i < BlockSize; i += 4) { @@ -954,6 +1298,19 @@ namespace Org.BouncyCastle.Crypto.Modes totalLength += BlockSize * 2; } + private void GetNextCtrBlock(byte[] block) + { + if (blocksRemaining == 0) + throw new InvalidOperationException("Attempt to process too many blocks"); + + blocksRemaining--; + + Pack.UInt32_To_BE(++counter32, counter, 12); + + cipher.ProcessBlock(counter, 0, block, 0); + } +#endif + private void ProcessPartial(byte[] buf, int off, int len, byte[] output, int outOff) { byte[] ctrBlock = new byte[BlockSize]; @@ -1009,18 +1366,6 @@ namespace Org.BouncyCastle.Crypto.Modes multiplier.MultiplyH(Y); } - private void GetNextCtrBlock(byte[] block) - { - if (blocksRemaining == 0) - throw new InvalidOperationException("Attempt to process too many blocks"); - - blocksRemaining--; - - Pack.UInt32_To_BE(++counter32, counter, 12); - - cipher.ProcessBlock(counter, 0, block, 0); - } - private void CheckStatus() { if (!initialised) diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs index d2f17809d..2abe5eece 100644 --- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs +++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs @@ -290,7 +290,7 @@ namespace Org.BouncyCastle.Crypto.Modes CheckAeadStatus(1); /* Process the aead */ - theAEADHasher.updateHash(pByte); + theAEADHasher.UpdateHash(pByte); } public virtual void ProcessAadBytes(byte[] pData, int pOffset, int pLen) @@ -304,7 +304,7 @@ namespace Org.BouncyCastle.Crypto.Modes CheckAeadStatus(pLen); /* Process the aead */ - theAEADHasher.updateHash(pData, pOffset, pLen); + theAEADHasher.UpdateHash(pData, pOffset, pLen); #endif } @@ -315,7 +315,7 @@ namespace Org.BouncyCastle.Crypto.Modes CheckAeadStatus(input.Length); /* Process the aead */ - theAEADHasher.updateHash(input); + theAEADHasher.UpdateHash(input); } #endif @@ -328,7 +328,7 @@ namespace Org.BouncyCastle.Crypto.Modes if (forEncryption) { thePlain.WriteByte(pByte); - theDataHasher.updateHash(pByte); + theDataHasher.UpdateHash(pByte); } else { @@ -339,18 +339,43 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } - public virtual int ProcessBytes(byte[] pData, int pOffset, int pLen, byte[] pOutput, int pOutOffset) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) { /* Check that we have initialised */ - CheckStatus(pLen); + CheckStatus(1); + /* Store the data */ + if (forEncryption) + { + thePlain.WriteByte(input); + theDataHasher.UpdateHash(input); + } + else + { + theEncData.WriteByte(input); + } + + /* No data returned */ + return 0; + } +#endif + + public virtual int ProcessBytes(byte[] pData, int pOffset, int pLen, byte[] pOutput, int pOutOffset) + { Check.DataLength(pData, pOffset, pLen, "input buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(pData.AsSpan(pOffset, pLen), Spans.FromNullable(pOutput, pOutOffset)); +#else + /* Check that we have initialised */ + CheckStatus(pLen); + /* Store the data */ if (forEncryption) { thePlain.Write(pData, pOffset, pLen); - theDataHasher.updateHash(pData, pOffset, pLen); + theDataHasher.UpdateHash(pData, pOffset, pLen); } else { @@ -359,8 +384,31 @@ namespace Org.BouncyCastle.Crypto.Modes /* No data returned */ return 0; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + /* Check that we have initialised */ + CheckStatus(input.Length); + + /* Store the data */ + if (forEncryption) + { + thePlain.Write(input); + theDataHasher.UpdateHash(input); + } + else + { + theEncData.Write(input); + } + + /* No data returned */ + return 0; + } +#endif + public virtual int DoFinal(byte[] pOutput, int pOffset) { Check.OutputLength(pOutput, pOffset, GetOutputSize(0), "output buffer too short"); @@ -500,7 +548,7 @@ namespace Org.BouncyCastle.Crypto.Modes Arrays.Fill(theGHash, (byte)0); if (theInitialAEAD != null) { - theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.Length); + theAEADHasher.UpdateHash(theInitialAEAD, 0, theInitialAEAD.Length); } } @@ -619,7 +667,7 @@ namespace Org.BouncyCastle.Crypto.Modes /* Write data to plain dataStream */ thePlain.Write(myMask, 0, myLen); - theDataHasher.updateHash(myMask, 0, myLen); + theDataHasher.UpdateHash(myMask, 0, myLen); /* Adjust counters */ myRemaining -= myLen; @@ -911,10 +959,10 @@ namespace Org.BouncyCastle.Crypto.Modes * update hash. * @param pByte the byte */ - internal void updateHash(byte pByte) + internal void UpdateHash(byte pByte) { theByte[0] = pByte; - updateHash(theByte, 0, 1); + UpdateHash(theByte, 0, 1); } /** @@ -923,7 +971,7 @@ namespace Org.BouncyCastle.Crypto.Modes * @param pOffset the offset within the buffer * @param pLen the length of data */ - internal void updateHash(byte[] pBuffer, int pOffset, int pLen) + internal void UpdateHash(byte[] pBuffer, int pOffset, int pLen) { /* If we should process the cache */ int mySpace = BUFLEN - numActive; @@ -967,7 +1015,7 @@ namespace Org.BouncyCastle.Crypto.Modes } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - internal void updateHash(ReadOnlySpan buffer) + internal void UpdateHash(ReadOnlySpan buffer) { int pLen = buffer.Length; diff --git a/crypto/src/crypto/modes/IAeadCipher.cs b/crypto/src/crypto/modes/IAeadCipher.cs index f80f3a247..5e92c78f3 100644 --- a/crypto/src/crypto/modes/IAeadCipher.cs +++ b/crypto/src/crypto/modes/IAeadCipher.cs @@ -59,6 +59,10 @@ namespace Org.BouncyCastle.Crypto.Modes */ int ProcessByte(byte input, byte[] outBytes, int outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int ProcessByte(byte input, Span output); +#endif + /** * Process a block of bytes from in putting the result into out. * @@ -72,6 +76,10 @@ namespace Org.BouncyCastle.Crypto.Modes */ int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int ProcessBytes(ReadOnlySpan input, Span output); +#endif + /** * Finish the operation either appending or verifying the MAC at the end of the data. * diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs index db86cf890..493bf56e1 100644 --- a/crypto/src/crypto/modes/KCcmBlockCipher.cs +++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs @@ -7,7 +7,8 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes { - public class KCcmBlockCipher: IAeadBlockCipher + public class KCcmBlockCipher + : IAeadBlockCipher { private static readonly int BYTES_IN_INT = 4; private static readonly int BITS_IN_BYTE = 8; @@ -234,6 +235,15 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) + { + data.WriteByte(input); + + return 0; + } +#endif + public virtual int ProcessBytes(byte[] input, int inOff, int inLen, byte[] output, int outOff) { Check.DataLength(input, inOff, inLen, "input buffer too short"); @@ -243,6 +253,15 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + data.Write(input); + + return 0; + } +#endif + public int ProcessPacket(byte[] input, int inOff, int len, byte[] output, int outOff) { Check.DataLength(input, inOff, len, "input buffer too short"); diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs index 8281c96c1..a4eaa08bd 100644 --- a/crypto/src/crypto/modes/OCBBlockCipher.cs +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs @@ -312,8 +312,24 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessByte(byte input, Span output) + { + mainBlock[mainBlockPos] = input; + if (++mainBlockPos == mainBlock.Length) + { + ProcessMainBlock(output); + return BLOCK_SIZE; + } + return 0; + } +#endif + public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(input.AsSpan(inOff, len), Spans.FromNullable(output, outOff)); +#else int resultLen = 0; for (int i = 0; i < len; ++i) @@ -326,8 +342,29 @@ namespace Org.BouncyCastle.Crypto.Modes } } + return resultLen; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBytes(ReadOnlySpan input, Span output) + { + int len = input.Length; + int resultLen = 0; + + for (int i = 0; i < len; ++i) + { + mainBlock[mainBlockPos] = input[i]; + if (++mainBlockPos == mainBlock.Length) + { + ProcessMainBlock(output[resultLen..]); + resultLen += BLOCK_SIZE; + } + } + return resultLen; } +#endif public virtual int DoFinal(byte[] output, int outOff) { @@ -572,6 +609,38 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected virtual void ProcessMainBlock(Span output) + { + Check.DataLength(output, BLOCK_SIZE, "output buffer too short"); + + /* + * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks + */ + + if (forEncryption) + { + Xor(Checksum, mainBlock); + mainBlockPos = 0; + } + + Xor(OffsetMAIN, GetLSub(OCB_ntz(++mainBlockCount))); + + Xor(mainBlock, OffsetMAIN); + mainCipher.ProcessBlock(mainBlock, 0, mainBlock, 0); + Xor(mainBlock, OffsetMAIN); + + mainBlock.AsSpan(0, BLOCK_SIZE).CopyTo(output); + + if (!forEncryption) + { + Xor(Checksum, mainBlock); + Array.Copy(mainBlock, BLOCK_SIZE, mainBlock, 0, macSize); + mainBlockPos = macSize; + } + } +#endif + protected virtual void Reset(bool clearMac) { hashCipher.Reset(); diff --git a/crypto/src/util/Spans.cs b/crypto/src/util/Spans.cs new file mode 100644 index 000000000..5e1b3737c --- /dev/null +++ b/crypto/src/util/Spans.cs @@ -0,0 +1,18 @@ +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +using System; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace Org.BouncyCastle.Utilities +{ + internal static class Spans + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Span FromNullable(T[]? array, int start) + { + return array == null ? Span.Empty : array.AsSpan(start); + } + } +} +#endif -- cgit 1.4.1