From a0b75007cc33d1ead75b2aed6439a7b272469bed Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 13 Apr 2023 16:48:27 +0700 Subject: Refactoring around AeadParameters and ParametersWIthIV --- crypto/src/cms/CMSEnvelopedHelper.cs | 8 ++++ crypto/src/cms/PasswordRecipientInfoGenerator.cs | 20 +++++++-- crypto/src/cms/PasswordRecipientInformation.cs | 13 ++++-- crypto/src/crypto/engines/AsconEngine.cs | 25 +++++------ crypto/src/crypto/modes/CcmBlockCipher.cs | 2 +- crypto/src/crypto/modes/ChaCha20Poly1305.cs | 22 +++++++++- crypto/src/crypto/modes/EAXBlockCipher.cs | 24 +++++++++-- crypto/src/crypto/modes/GCMBlockCipher.cs | 22 +++++++++- crypto/src/crypto/modes/GcmSivBlockCipher.cs | 35 +++++++++------ crypto/src/crypto/modes/KCcmBlockCipher.cs | 43 ++++++++---------- crypto/src/crypto/modes/OCBBlockCipher.cs | 55 +++++++++++++++++++++--- crypto/src/crypto/parameters/ParametersWithIV.cs | 28 ++++++++++++ 12 files changed, 223 insertions(+), 74 deletions(-) diff --git a/crypto/src/cms/CMSEnvelopedHelper.cs b/crypto/src/cms/CMSEnvelopedHelper.cs index 40c5e6868..c623ec677 100644 --- a/crypto/src/cms/CMSEnvelopedHelper.cs +++ b/crypto/src/cms/CMSEnvelopedHelper.cs @@ -1,4 +1,7 @@ using System; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +using System.Buffers; +#endif using System.Collections.Generic; using System.IO; @@ -240,7 +243,12 @@ namespace Org.BouncyCastle.Cms || alg.Equals(CmsEnvelopedGenerator.IdeaCbc) || alg.Equals(CmsEnvelopedGenerator.Cast5Cbc)) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + cipherParameters = ParametersWithIV.Create(cipherParameters, 8, 0, + (bytes, state) => bytes.Fill(state)); +#else cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]); +#endif } } diff --git a/crypto/src/cms/PasswordRecipientInfoGenerator.cs b/crypto/src/cms/PasswordRecipientInfoGenerator.cs index 5bbf8f727..1243bea9f 100644 --- a/crypto/src/cms/PasswordRecipientInfoGenerator.cs +++ b/crypto/src/cms/PasswordRecipientInfoGenerator.cs @@ -48,17 +48,29 @@ namespace Org.BouncyCastle.Cms // Note: In Java build, the IV is automatically generated in JCE layer int ivLength = Platform.StartsWithIgnoreCase(rfc3211WrapperName, "DES") ? 8 : 16; - byte[] iv = new byte[ivLength]; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + var parametersWithIV = ParametersWithIV.Create(keyEncryptionKey, ivLength, random, + (bytes, random) => random.NextBytes(bytes)); +#else + byte[] iv = new byte[ivLength]; random.NextBytes(iv); - ICipherParameters parameters = new ParametersWithIV(keyEncryptionKey, iv); - keyWrapper.Init(true, new ParametersWithRandom(parameters, random)); + var parametersWithIV = new ParametersWithIV(keyEncryptionKey, iv); +#endif + + keyWrapper.Init(true, new ParametersWithRandom(parametersWithIV, random)); Asn1OctetString encryptedKey = new DerOctetString( keyWrapper.Wrap(keyBytes, 0, keyBytes.Length)); DerSequence seq = new DerSequence( new DerObjectIdentifier(keyEncryptionKeyOID), - new DerOctetString(iv)); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + new DerOctetString(parametersWithIV.IV) +#else + new DerOctetString(iv) +#endif + ); AlgorithmIdentifier keyEncryptionAlgorithm = new AlgorithmIdentifier( PkcsObjectIdentifiers.IdAlgPwriKek, seq); diff --git a/crypto/src/cms/PasswordRecipientInformation.cs b/crypto/src/cms/PasswordRecipientInformation.cs index f629caba6..ff4a20d68 100644 --- a/crypto/src/cms/PasswordRecipientInformation.cs +++ b/crypto/src/cms/PasswordRecipientInformation.cs @@ -54,12 +54,17 @@ namespace Org.BouncyCastle.Cms string cName = CmsEnvelopedHelper.Instance.GetRfc3211WrapperName(kekAlgName); IWrapper keyWrapper = WrapperUtilities.GetWrapper(cName); - byte[] iv = Asn1OctetString.GetInstance(kekAlgParams[1]).GetOctets(); + var iv = Asn1OctetString.GetInstance(kekAlgParams[1]); - ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName); - parameters = new ParametersWithIV(parameters, iv); + ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName); - keyWrapper.Init(false, parameters); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + parameters = new ParametersWithIV(parameters, iv.GetOctetsSpan()); +#else + parameters = new ParametersWithIV(parameters, iv.GetOctets()); +#endif + + keyWrapper.Init(false, parameters); KeyParameter sKey = ParameterUtilities.CreateKeyParameter( GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length)); diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs index 3ca5ede60..4bda8a02a 100644 --- a/crypto/src/crypto/engines/AsconEngine.cs +++ b/crypto/src/crypto/engines/AsconEngine.cs @@ -114,12 +114,20 @@ namespace Org.BouncyCastle.Crypto.Engines public void Init(bool forEncryption, ICipherParameters parameters) { KeyParameter key; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan npub; +#else byte[] npub; +#endif if (parameters is AeadParameters aeadParameters) { key = aeadParameters.Key; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + npub = aeadParameters.Nonce; +#else npub = aeadParameters.GetNonce(); +#endif initialAssociatedText = aeadParameters.GetAssociatedText(); int macSizeBits = aeadParameters.MacSize; @@ -129,7 +137,11 @@ namespace Org.BouncyCastle.Crypto.Engines else if (parameters is ParametersWithIV withIV) { key = withIV.Parameters as KeyParameter; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + npub = withIV.IV; +#else npub = withIV.GetIV(); +#endif initialAssociatedText = null; } else @@ -139,7 +151,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (key == null) throw new ArgumentException("Ascon Init parameters must include a key"); - if (npub == null || npub.Length != CRYPTO_ABYTES) + if (npub.Length != CRYPTO_ABYTES) throw new ArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV"); #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER @@ -156,25 +168,14 @@ namespace Org.BouncyCastle.Crypto.Engines if (CRYPTO_KEYBYTES == 16) { -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - K1 = Pack.BE_To_UInt64(k); - K2 = Pack.BE_To_UInt64(k[8..]); -#else K1 = Pack.BE_To_UInt64(k, 0); K2 = Pack.BE_To_UInt64(k, 8); -#endif } else if (CRYPTO_KEYBYTES == 20) { -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - K0 = Pack.BE_To_UInt32(k); - K1 = Pack.BE_To_UInt64(k[4..]); - K2 = Pack.BE_To_UInt64(k[12..]); -#else K0 = Pack.BE_To_UInt32(k, 0); K1 = Pack.BE_To_UInt64(k, 4); K2 = Pack.BE_To_UInt64(k, 12); -#endif } else { diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs index 7784e7cca..b7d90298a 100644 --- a/crypto/src/crypto/modes/CcmBlockCipher.cs +++ b/crypto/src/crypto/modes/CcmBlockCipher.cs @@ -81,7 +81,7 @@ namespace Org.BouncyCastle.Crypto.Modes keyParam = cipherParameters; } - if (nonce == null || nonce.Length < 7 || nonce.Length > 13) + if (nonce.Length < 7 || nonce.Length > 13) throw new ArgumentException("nonce must have length from 7 to 13 octets"); Reset(); diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs index 01bf6ccb8..56bef5e93 100644 --- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs +++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs @@ -72,7 +72,11 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual void Init(bool forEncryption, ICipherParameters parameters) { KeyParameter initKeyParam; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan initNonce; +#else byte[] initNonce; +#endif ICipherParameters chacha20Params; if (parameters is AeadParameters aeadParams) @@ -82,7 +86,11 @@ namespace Org.BouncyCastle.Crypto.Modes throw new ArgumentException("Invalid value for MAC size: " + macSizeBits); initKeyParam = aeadParams.Key; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + initNonce = aeadParams.Nonce; +#else initNonce = aeadParams.GetNonce(); +#endif chacha20Params = new ParametersWithIV(initKeyParam, initNonce); this.mInitialAad = aeadParams.GetAssociatedText(); @@ -90,7 +98,11 @@ namespace Org.BouncyCastle.Crypto.Modes else if (parameters is ParametersWithIV ivParams) { initKeyParam = (KeyParameter)ivParams.Parameters; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + initNonce = ivParams.IV; +#else initNonce = ivParams.GetIV(); +#endif chacha20Params = ivParams; this.mInitialAad = null; @@ -113,11 +125,15 @@ namespace Org.BouncyCastle.Crypto.Modes } // Validate nonce - if (null == initNonce || NonceSize != initNonce.Length) + if (NonceSize != initNonce.Length) throw new ArgumentException("Nonce must be 96 bits"); // Check for encryption with reused nonce +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (State.Uninitialized != mState && forEncryption && initNonce.SequenceEqual(mNonce)) +#else if (State.Uninitialized != mState && forEncryption && Arrays.AreEqual(mNonce, initNonce)) +#endif { if (null == initKeyParam || initKeyParam.FixedTimeEquals(mKey)) throw new ArgumentException("cannot reuse nonce for ChaCha20Poly1305 encryption"); @@ -128,7 +144,11 @@ namespace Org.BouncyCastle.Crypto.Modes initKeyParam.CopyTo(mKey, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + initNonce.CopyTo(mNonce); +#else Array.Copy(initNonce, 0, mNonce, 0, NonceSize); +#endif mChacha20.Init(true, chacha20Params); diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs index 3b95cd7f2..858786d10 100644 --- a/crypto/src/crypto/modes/EAXBlockCipher.cs +++ b/crypto/src/crypto/modes/EAXBlockCipher.cs @@ -72,19 +72,31 @@ namespace Org.BouncyCastle.Crypto.Modes { this.forEncryption = forEncryption; - byte[] nonce; - ICipherParameters keyParam; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan nonce; +#else + byte[] nonce; +#endif + ICipherParameters keyParam; if (parameters is AeadParameters aeadParameters) { - nonce = aeadParameters.GetNonce(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + nonce = aeadParameters.Nonce; +#else + nonce = aeadParameters.GetNonce(); +#endif initialAssociatedText = aeadParameters.GetAssociatedText(); macSize = aeadParameters.MacSize / 8; keyParam = aeadParameters.Key; } else if (parameters is ParametersWithIV parametersWithIV) { - nonce = parametersWithIV.GetIV(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + nonce = parametersWithIV.IV; +#else + nonce = parametersWithIV.GetIV(); +#endif initialAssociatedText = null; macSize = mac.GetMacSize() / 2; keyParam = parametersWithIV.Parameters; @@ -103,7 +115,11 @@ namespace Org.BouncyCastle.Crypto.Modes tag[blockSize - 1] = (byte)Tag.N; mac.BlockUpdate(tag, 0, blockSize); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + mac.BlockUpdate(nonce); +#else mac.BlockUpdate(nonce, 0, nonce.Length); +#endif mac.DoFinal(nonceMac, 0); // Same BlockCipher underlies this and the mac, so reuse last key on cipher diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs index fef980091..c592c3af3 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs @@ -117,11 +117,19 @@ namespace Org.BouncyCastle.Crypto.Modes this.initialised = true; KeyParameter keyParam; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan newNonce; +#else byte[] newNonce; +#endif if (parameters is AeadParameters aeadParameters) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + newNonce = aeadParameters.Nonce; +#else newNonce = aeadParameters.GetNonce(); +#endif initialAssociatedText = aeadParameters.GetAssociatedText(); int macSizeBits = aeadParameters.MacSize; @@ -133,7 +141,11 @@ namespace Org.BouncyCastle.Crypto.Modes } else if (parameters is ParametersWithIV withIV) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + newNonce = withIV.IV; +#else newNonce = withIV.GetIV(); +#endif initialAssociatedText = null; macSize = 16; keyParam = (KeyParameter)withIV.Parameters; @@ -146,12 +158,16 @@ namespace Org.BouncyCastle.Crypto.Modes int bufLength = forEncryption ? BlockSize : (BlockSize + macSize); this.bufBlock = new byte[bufLength]; - if (newNonce == null || newNonce.Length < 1) + if (newNonce.Length < 1) throw new ArgumentException("IV must be at least 1 byte"); if (forEncryption) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (nonce != null && newNonce.SequenceEqual(nonce)) +#else if (nonce != null && Arrays.AreEqual(nonce, newNonce)) +#endif { if (keyParam == null) throw new ArgumentException("cannot reuse nonce for GCM encryption"); @@ -161,7 +177,11 @@ namespace Org.BouncyCastle.Crypto.Modes } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + nonce = newNonce.ToArray(); +#else nonce = newNonce; +#endif if (keyParam != null) { lastKey = keyParam.GetKey(); diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs index e11411433..d58fc0e65 100644 --- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs +++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs @@ -176,21 +176,31 @@ namespace Org.BouncyCastle.Crypto.Modes { /* Set defaults */ byte[] myInitialAEAD = null; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan myNonce; +#else byte[] myNonce; +#endif KeyParameter myKey; /* Access parameters */ - if (cipherParameters is AeadParameters) + if (cipherParameters is AeadParameters myAEAD) { - AeadParameters myAEAD = (AeadParameters)cipherParameters; myInitialAEAD = myAEAD.GetAssociatedText(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + myNonce = myAEAD.Nonce; +#else myNonce = myAEAD.GetNonce(); +#endif myKey = myAEAD.Key; } - else if (cipherParameters is ParametersWithIV) + else if (cipherParameters is ParametersWithIV myParms) { - ParametersWithIV myParms = (ParametersWithIV)cipherParameters; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + myNonce = myParms.IV; +#else myNonce = myParms.GetIV(); +#endif myKey = (KeyParameter)myParms.Parameters; } else @@ -199,28 +209,25 @@ namespace Org.BouncyCastle.Crypto.Modes } /* Check nonceSize */ - if (myNonce == null || myNonce.Length != NONCELEN) - { + if (myNonce.Length != NONCELEN) throw new ArgumentException("Invalid nonce"); - } /* Check keysize */ if (myKey == null) - { throw new ArgumentException("Invalid key"); - } - byte[] k = myKey.GetKey(); - - if (k.Length != BUFLEN && k.Length != (BUFLEN << 1)) - { + int keyLength = myKey.KeyLength; + if (keyLength != BUFLEN && keyLength != (BUFLEN << 1)) throw new ArgumentException("Invalid key"); - } /* Reset details */ forEncryption = pEncrypt; theInitialAEAD = myInitialAEAD; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + theNonce = myNonce.ToArray(); +#else theNonce = myNonce; +#endif /* Initialise the keys */ DeriveKeys(myKey); diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs index c13c41543..6afb617b5 100644 --- a/crypto/src/crypto/modes/KCcmBlockCipher.cs +++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs @@ -42,7 +42,7 @@ namespace Org.BouncyCastle.Crypto.Modes */ private int Nb_ = 4; - private void setNb(int Nb) + private void SetNb(int Nb) { if (Nb == 4 || Nb == 6 || Nb == 8) { @@ -85,38 +85,32 @@ namespace Org.BouncyCastle.Crypto.Modes this.buffer = new byte[engine.GetBlockSize()]; this.s = new byte[engine.GetBlockSize()]; this.counter = new byte[engine.GetBlockSize()]; - setNb(Nb); + SetNb(Nb); } public virtual void Init(bool forEncryption, ICipherParameters parameters) { - - ICipherParameters cipherParameters; - if (parameters is AeadParameters) + ICipherParameters cipherParameters; + if (parameters is AeadParameters param) { + if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0) + throw new ArgumentException("Invalid mac size specified"); - AeadParameters param = (AeadParameters)parameters; - - if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0) - { - throw new ArgumentException("Invalid mac size specified"); - } - - nonce = param.GetNonce(); - macSize = param.MacSize / BITS_IN_BYTE; - initialAssociatedText = param.GetAssociatedText(); - cipherParameters = param.Key; + nonce = param.GetNonce(); + macSize = param.MacSize / BITS_IN_BYTE; + initialAssociatedText = param.GetAssociatedText(); + cipherParameters = param.Key; } - else if (parameters is ParametersWithIV) + else if (parameters is ParametersWithIV paramsWithIV) { - nonce = ((ParametersWithIV)parameters).GetIV(); - macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified - initialAssociatedText = null; - cipherParameters = ((ParametersWithIV)parameters).Parameters; + nonce = paramsWithIV.GetIV(); + macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified + initialAssociatedText = null; + cipherParameters = paramsWithIV.Parameters; } else { - throw new ArgumentException("Invalid parameters specified"); + throw new ArgumentException("Invalid parameters specified"); } this.mac = new byte[macSize]; @@ -133,10 +127,7 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual string AlgorithmName => engine.AlgorithmName + "/KCCM"; - public virtual int GetBlockSize() - { - return engine.GetBlockSize(); - } + public virtual int GetBlockSize() => engine.GetBlockSize(); public virtual IBlockCipher UnderlyingCipher => engine; diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs index ce86780c5..c0bca2681 100644 --- a/crypto/src/crypto/modes/OCBBlockCipher.cs +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs @@ -93,10 +93,18 @@ namespace Org.BouncyCastle.Crypto.Modes KeyParameter keyParameter; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan N; +#else byte[] N; +#endif if (parameters is AeadParameters aeadParameters) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + N = aeadParameters.Nonce; +#else N = aeadParameters.GetNonce(); +#endif initialAssociatedText = aeadParameters.GetAssociatedText(); int macSizeBits = aeadParameters.MacSize; @@ -108,7 +116,11 @@ namespace Org.BouncyCastle.Crypto.Modes } else if (parameters is ParametersWithIV parametersWithIV) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + N = parametersWithIV.IV; +#else N = parametersWithIV.GetIV(); +#endif initialAssociatedText = null; macSize = 16; keyParameter = (KeyParameter) parametersWithIV.Parameters; @@ -121,15 +133,8 @@ namespace Org.BouncyCastle.Crypto.Modes this.hashBlock = new byte[16]; this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)]; - if (N == null) - { - N = new byte[0]; - } - if (N.Length > 15) - { throw new ArgumentException("IV must be no more than 15 bytes"); - } /* * KEY-DEPENDENT INITIALISATION @@ -221,6 +226,42 @@ namespace Org.BouncyCastle.Crypto.Modes return bottom; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int ProcessNonce(ReadOnlySpan N) + { + // TODO[api] Redesign to avoid this exceptional case + // Avoid problems if ProcessNonce() was overridden before this method even existed. + if (GetType() != typeof(OcbBlockCipher)) + return ProcessNonce(N.ToArray()); + + Span nonce = stackalloc byte[16]; + N.CopyTo(nonce[^N.Length..]); + nonce[0] = (byte)(macSize << 4); + nonce[15 - N.Length] |= 1; + + int bottom = nonce[15] & 0x3F; + nonce[15] &= 0xC0; + + /* + * When used with incrementing nonces, the cipher is only applied once every 64 inits. + */ + if (KtopInput == null || !nonce.SequenceEqual(KtopInput)) + { + KtopInput = nonce.ToArray(); + + Span Ktop = stackalloc byte[16]; + hashCipher.ProcessBlock(KtopInput, Ktop); + Ktop.CopyTo(Stretch); + for (int i = 0; i < 8; ++i) + { + Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]); + } + } + + return bottom; + } +#endif + public virtual int GetBlockSize() { return BLOCK_SIZE; diff --git a/crypto/src/crypto/parameters/ParametersWithIV.cs b/crypto/src/crypto/parameters/ParametersWithIV.cs index e642e828a..c5f04aab1 100644 --- a/crypto/src/crypto/parameters/ParametersWithIV.cs +++ b/crypto/src/crypto/parameters/ParametersWithIV.cs @@ -1,10 +1,28 @@ using System; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +using System.Buffers; +#endif namespace Org.BouncyCastle.Crypto.Parameters { public class ParametersWithIV : ICipherParameters { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static ParametersWithIV Create(ICipherParameters parameter, int ivLength, TState state, + SpanAction action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + if (ivLength < 0) + throw new ArgumentOutOfRangeException(nameof(ivLength)); + + ParametersWithIV result = new ParametersWithIV(parameter, ivLength); + action(result.m_iv, state); + return result; + } +#endif + internal static ICipherParameters ApplyOptionalIV(ICipherParameters parameters, byte[] iv) { return iv == null ? parameters : new ParametersWithIV(parameters, iv); @@ -44,6 +62,16 @@ namespace Org.BouncyCastle.Crypto.Parameters } #endif + private ParametersWithIV(ICipherParameters parameters, int ivLength) + { + if (ivLength < 0) + throw new ArgumentOutOfRangeException(nameof(ivLength)); + + // NOTE: 'parameters' may be null to imply key re-use + m_parameters = parameters; + m_iv = new byte[ivLength]; + } + public byte[] GetIV() { return (byte[])m_iv.Clone(); -- cgit 1.4.1