diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-09-03 01:32:06 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-09-03 01:32:06 +0700 |
commit | 06b3437c22c3afe1d1f5bb60c13c6b0cddf70591 (patch) | |
tree | 0c7e6e1c98558aa0a1b7dbf65e261e5078426a51 | |
parent | Stream modernization (diff) | |
download | BouncyCastle.NET-ed25519-06b3437c22c3afe1d1f5bb60c13c6b0cddf70591.tar.xz |
Improve span-based GCM code
-rw-r--r-- | crypto/src/crypto/macs/GMac.cs | 14 | ||||
-rw-r--r-- | crypto/src/crypto/modes/GCMBlockCipher.cs | 144 | ||||
-rw-r--r-- | crypto/src/crypto/modes/gcm/GcmUtilities.cs | 8 |
3 files changed, 153 insertions, 13 deletions
diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs index aa124bb04..e1555f1e6 100644 --- a/crypto/src/crypto/macs/GMac.cs +++ b/crypto/src/crypto/macs/GMac.cs @@ -109,11 +109,15 @@ namespace Org.BouncyCastle.Crypto.Macs #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public int DoFinal(Span<byte> output) { - // TODO[span] call cipher.DoFinal(Span<byte) when available - byte[] tmp = new byte[GetMacSize()]; - int result = DoFinal(tmp, 0); - tmp.CopyTo(output); - return result; + try + { + return cipher.DoFinal(output); + } + catch (InvalidCipherTextException e) + { + // Impossible in encrypt mode + throw new InvalidOperationException(e.ToString()); + } } #endif diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs index 05db1c2c4..2ab406fc3 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs @@ -638,6 +638,9 @@ namespace Org.BouncyCastle.Crypto.Modes public int DoFinal(byte[] output, int outOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else CheckStatus(); if (totalLength == 0) @@ -744,20 +747,118 @@ namespace Org.BouncyCastle.Crypto.Modes Reset(false); return resultLen; +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public virtual int DoFinal(Span<byte> output) { - // TODO[span] Implement efficiently + CheckStatus(); + + if (totalLength == 0) + { + InitCipher(); + } + + int extra = bufOff; + + if (forEncryption) + { + Check.OutputLength(output, extra + macSize, "output buffer too short"); + } + else + { + if (extra < macSize) + throw new InvalidCipherTextException("data too short"); + + extra -= macSize; + + Check.OutputLength(output, extra, "output buffer too short"); + } + + if (extra > 0) + { + ProcessPartial(bufBlock.AsSpan(0, extra), output); + } + + atLength += (uint)atBlockPos; - int outputLen = GetOutputSize(0); - Check.OutputLength(output, outputLen, "output buffer too short"); + if (atLength > atLengthPre) + { + /* + * Some AAD was sent after the cipher started. We determine the difference b/w the hash value + * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at). + * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or + * partial) cipher-text blocks produced, and adjust the current hash. + */ - byte[] bytes = new byte[outputLen]; - int len = DoFinal(bytes, 0); - bytes[..len].CopyTo(output); - return len; + // Finish hash for partial AAD block + if (atBlockPos > 0) + { + gHASHPartial(S_at, atBlock, 0, atBlockPos); + } + + // Find the difference between the AAD hashes + if (atLengthPre > 0) + { + GcmUtilities.Xor(S_at, S_atPre); + } + + // Number of cipher-text blocks produced + long c = (long)(((totalLength * 8) + 127) >> 7); + + // Calculate the adjustment factor + byte[] H_c = new byte[16]; + if (exp == null) + { + exp = new BasicGcmExponentiator(); + exp.Init(H); + } + exp.ExponentiateX(c, H_c); + + // Carry the difference forward + GcmUtilities.Multiply(S_at, H_c); + + // Adjust the current hash + GcmUtilities.Xor(S, S_at); + } + + // Final gHASH + Span<byte> X = stackalloc byte[BlockSize]; + Pack.UInt64_To_BE(atLength * 8UL, X); + Pack.UInt64_To_BE(totalLength * 8UL, X[8..]); + + gHASHBlock(S, X); + + // T = MSBt(GCTRk(J0,S)) + Span<byte> tag = stackalloc byte[BlockSize]; + cipher.ProcessBlock(J0, tag); + GcmUtilities.Xor(tag, S); + + int resultLen = extra; + + // We place into macBlock our calculated value for T + this.macBlock = new byte[macSize]; + tag[..macSize].CopyTo(macBlock); + + if (forEncryption) + { + // Append T to the message + macBlock.CopyTo(output[bufOff..]); + resultLen += macSize; + } + else + { + // Retrieve the T value from the message and compare to calculated one + Span<byte> msgMac = stackalloc byte[macSize]; + bufBlock.AsSpan(extra, macSize).CopyTo(msgMac); + if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac)) + throw new InvalidCipherTextException("mac check in GCM failed"); + } + + Reset(false); + + return resultLen; } #endif @@ -1110,6 +1211,26 @@ namespace Org.BouncyCastle.Crypto.Modes cipher.ProcessBlock(counter, block); } + + private void ProcessPartial(Span<byte> partialBlock, Span<byte> output) + { + Span<byte> ctrBlock = stackalloc byte[BlockSize]; + GetNextCtrBlock(ctrBlock); + + if (forEncryption) + { + GcmUtilities.Xor(partialBlock, ctrBlock, partialBlock.Length); + gHASHPartial(S, partialBlock); + } + else + { + gHASHPartial(S, partialBlock); + GcmUtilities.Xor(partialBlock, ctrBlock, partialBlock.Length); + } + + partialBlock.CopyTo(output); + totalLength += (uint)partialBlock.Length; + } #else private void DecryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) { @@ -1316,7 +1437,6 @@ namespace Org.BouncyCastle.Crypto.Modes cipher.ProcessBlock(counter, 0, block, 0); } -#endif private void ProcessPartial(byte[] buf, int off, int len, byte[] output, int outOff) { @@ -1337,6 +1457,7 @@ namespace Org.BouncyCastle.Crypto.Modes Array.Copy(buf, off, output, outOff, len); totalLength += (uint)len; } +#endif private void gHASH(byte[] Y, byte[] b, int len) { @@ -1354,6 +1475,13 @@ namespace Org.BouncyCastle.Crypto.Modes GcmUtilities.Xor(Y, b); multiplier.MultiplyH(Y); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void gHASHPartial(byte[] Y, ReadOnlySpan<byte> b) + { + GcmUtilities.Xor(Y, b, b.Length); + multiplier.MultiplyH(Y); + } #else private void gHASHBlock(byte[] Y, byte[] b) { diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs index 78a1f0860..1aa437fcd 100644 --- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs +++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs @@ -288,6 +288,14 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm } while (i < 16); } + + internal static void Xor(Span<byte> x, ReadOnlySpan<byte> y, int len) + { + for (int i = 0; i < len; ++i) + { + x[i] ^= y[i]; + } + } #endif private static ulong ImplMul64(ulong x, ulong y) |