diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs
index 804097b3b..e0a448dda 100644
--- a/crypto/src/crypto/macs/GMac.cs
+++ b/crypto/src/crypto/macs/GMac.cs
@@ -89,16 +89,7 @@ namespace Org.BouncyCastle.Crypto.Macs
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public void BlockUpdate(ReadOnlySpan<byte> input)
{
- // TODO[span] Add span-based variant of ProcessAadBytes
- byte[] tmp = new byte[64];
- while (input.Length > 64)
- {
- input[..64].CopyTo(tmp);
- input = input[64..];
- cipher.ProcessAadBytes(tmp, 0, 64);
- }
- input.CopyTo(tmp);
- cipher.ProcessAadBytes(tmp, 0, input.Length);
+ cipher.ProcessAadBytes(input);
}
#endif
diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index 8f0acce52..256cc1b13 100644
--- a/crypto/src/crypto/modes/CcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -117,6 +117,14 @@ namespace Org.BouncyCastle.Crypto.Modes
associatedText.Write(inBytes, inOff, len);
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ // TODO: Process AAD online
+ associatedText.Write(input);
+ }
+#endif
+
public virtual int ProcessByte(
byte input,
byte[] outBytes,
diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
index 462013200..385977fd5 100644
--- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs
+++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
@@ -209,6 +209,19 @@ namespace Org.BouncyCastle.Crypto.Modes
}
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ CheckAad();
+
+ if (!input.IsEmpty)
+ {
+ this.mAadCount = IncrementCount(mAadCount, (uint)input.Length, AadLimit);
+ mPoly1305.BlockUpdate(input);
+ }
+ }
+#endif
+
public virtual int ProcessByte(byte input, byte[] outBytes, int outOff)
{
CheckData();
diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index 624f385b5..ffe32ec68 100644
--- a/crypto/src/crypto/modes/EAXBlockCipher.cs
+++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -194,13 +194,22 @@ namespace Org.BouncyCastle.Crypto.Modes
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
if (cipherInitialized)
- {
throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
- }
+
mac.BlockUpdate(inBytes, inOff, len);
}
- public virtual int ProcessByte(
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ if (cipherInitialized)
+ throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
+
+ mac.BlockUpdate(input);
+ }
+#endif
+
+ public virtual int ProcessByte(
byte input,
byte[] outBytes,
int outOff)
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index ac54e9762..bf9c14e28 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -278,6 +278,9 @@ namespace Org.BouncyCastle.Crypto.Modes
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ProcessAadBytes(inBytes.AsSpan(inOff, len));
+#else
CheckStatus();
if (atBlockPos > 0)
@@ -309,7 +312,42 @@ namespace Org.BouncyCastle.Crypto.Modes
atBlockPos = BlockSize + inLimit - inOff;
Array.Copy(inBytes, inOff, atBlock, 0, atBlockPos);
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ CheckStatus();
+
+ if (atBlockPos > 0)
+ {
+ int available = BlockSize - atBlockPos;
+ if (input.Length < available)
+ {
+ input.CopyTo(atBlock.AsSpan(atBlockPos));
+ atBlockPos += input.Length;
+ return;
+ }
+
+ input[..available].CopyTo(atBlock.AsSpan(atBlockPos));
+ gHASHBlock(S_at, atBlock);
+ atLength += BlockSize;
+ input = input[available..];
+ //atBlockPos = 0;
+ }
+
+ while (input.Length >= BlockSize)
+ {
+ gHASHBlock(S_at, input);
+ atLength += BlockSize;
+ input = input[BlockSize..];
+ }
+
+ input.CopyTo(atBlock);
+ atBlockPos = input.Length;
}
+#endif
private void InitCipher()
{
@@ -930,6 +968,13 @@ namespace Org.BouncyCastle.Crypto.Modes
}
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void gHASHBlock(byte[] Y, ReadOnlySpan<byte> b)
+ {
+ GcmUtilities.Xor(Y, b);
+ multiplier.MultiplyH(Y);
+ }
+#else
private void gHASHBlock(byte[] Y, byte[] b)
{
GcmUtilities.Xor(Y, b);
@@ -941,6 +986,7 @@ namespace Org.BouncyCastle.Crypto.Modes
GcmUtilities.Xor(Y, b, off);
multiplier.MultiplyH(Y);
}
+#endif
private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
{
diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
index 63808a53a..284a952a6 100644
--- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs
+++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
@@ -1,7 +1,6 @@
using System;
using System.IO;
-using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes.Gcm;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
@@ -296,15 +295,30 @@ namespace Org.BouncyCastle.Crypto.Modes
public virtual void ProcessAadBytes(byte[] pData, int pOffset, int pLen)
{
- /* Check that we can supply AEAD */
- CheckAeadStatus(pLen);
-
/* Check input buffer */
CheckBuffer(pData, pOffset, pLen, false);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ProcessAadBytes(pData.AsSpan(pOffset, pLen));
+#else
+ /* Check that we can supply AEAD */
+ CheckAeadStatus(pLen);
+
/* Process the aead */
theAEADHasher.updateHash(pData, pOffset, pLen);
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ /* Check that we can supply AEAD */
+ CheckAeadStatus(input.Length);
+
+ /* Process the aead */
+ theAEADHasher.updateHash(input);
}
+#endif
public virtual int ProcessByte(byte pByte, byte[] pOutput, int pOutOffset)
{
@@ -647,6 +661,18 @@ namespace Org.BouncyCastle.Crypto.Modes
}
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private static void fillReverse(ReadOnlySpan<byte> input, Span<byte> output)
+ {
+ /* Loop through the buffer */
+ for (int i = 0, j = BUFLEN - 1; i < input.Length; i++, j--)
+ {
+ /* Copy byte */
+ output[j] = input[i];
+ }
+ }
+#endif
+
/**
* xor a full block buffer.
* @param pLeft the left operand and result
@@ -891,6 +917,49 @@ namespace Org.BouncyCastle.Crypto.Modes
numHashed += (ulong)pLen;
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal void updateHash(ReadOnlySpan<byte> buffer)
+ {
+ int pLen = buffer.Length;
+
+ /* If we should process the cache */
+ int mySpace = BUFLEN - numActive;
+ if (numActive > 0 && buffer.Length >= mySpace)
+ {
+ /* Copy data into the cache and hash it */
+ buffer[..mySpace].CopyTo(theBuffer.AsSpan(numActive));
+ fillReverse(theBuffer, parent.theReverse);
+ parent.gHASH(parent.theReverse);
+
+ /* Adjust counters */
+ buffer = buffer[mySpace..];
+ numActive = 0;
+ }
+
+ /* While we have full blocks */
+ while (buffer.Length >= BUFLEN)
+ {
+ /* Access the next data */
+ fillReverse(buffer[..BUFLEN], parent.theReverse);
+ parent.gHASH(parent.theReverse);
+
+ /* Adjust counters */
+ buffer = buffer[BUFLEN..];
+ }
+
+ /* If we have remaining data */
+ if (!buffer.IsEmpty)
+ {
+ /* Copy data into the cache */
+ buffer.CopyTo(theBuffer.AsSpan(numActive));
+ numActive += buffer.Length;
+ }
+
+ /* Adjust the number of bytes processed */
+ numHashed += (ulong)pLen;
+ }
+#endif
+
/**
* complete hash.
*/
diff --git a/crypto/src/crypto/modes/IAeadCipher.cs b/crypto/src/crypto/modes/IAeadCipher.cs
index 437693cb6..c61e13b01 100644
--- a/crypto/src/crypto/modes/IAeadCipher.cs
+++ b/crypto/src/crypto/modes/IAeadCipher.cs
@@ -41,6 +41,13 @@ namespace Org.BouncyCastle.Crypto.Modes
/// <param name="len">The number of bytes to be processed.</param>
void ProcessAadBytes(byte[] inBytes, int inOff, int len);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ /// <summary>Add a span of bytes to the associated data check.</summary>
+ /// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
+ /// <param name="input">the span containing the data.</param>
+ void ProcessAadBytes(ReadOnlySpan<byte> input);
+#endif
+
/**
* Encrypt/decrypt a single byte.
*
diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs
index afa7063a3..afa68a794 100644
--- a/crypto/src/crypto/modes/KCcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs
@@ -158,6 +158,13 @@ namespace Org.BouncyCastle.Crypto.Modes
associatedText.Write(input, inOff, len);
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ associatedText.Write(input);
+ }
+#endif
+
private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
{
if (assocLen - assocOff < engine.GetBlockSize())
diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs
index 28e88a6c9..db6aa39ae 100644
--- a/crypto/src/crypto/modes/OCBBlockCipher.cs
+++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -287,6 +287,20 @@ namespace Org.BouncyCastle.Crypto.Modes
}
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ for (int i = 0; i < input.Length; ++i)
+ {
+ hashBlock[hashBlockPos] = input[i];
+ if (++hashBlockPos == hashBlock.Length)
+ {
+ ProcessHashBlock();
+ }
+ }
+ }
+#endif
+
public virtual int ProcessByte(byte input, byte[] output, int outOff)
{
mainBlock[mainBlockPos] = input;
diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
index 676d74107..78a1f0860 100644
--- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs
+++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -275,6 +275,21 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm
z.n1 = x.n1 ^ y.n1;
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void Xor(Span<byte> x, ReadOnlySpan<byte> y)
+ {
+ int i = 0;
+ do
+ {
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ }
+ while (i < 16);
+ }
+#endif
+
private static ulong ImplMul64(ulong x, ulong y)
{
ulong x0 = x & 0x1111111111111111UL;
|