From 758b05345c9d2f0b412f75435cb31231a7f70311 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 20 Apr 2023 18:40:56 +0700 Subject: Refactoring: reduced allocations --- crypto/src/asn1/x509/SubjectPublicKeyInfo.cs | 10 ++++- crypto/src/crypto/fpe/FpeFf3_1Engine.cs | 17 +-------- crypto/src/crypto/fpe/SP80038G.cs | 3 +- .../parameters/Ed25519PrivateKeyParameters.cs | 6 +++ .../crypto/parameters/Ed448PrivateKeyParameters.cs | 6 +++ crypto/src/crypto/parameters/KeyParameter.cs | 7 ++++ .../parameters/X25519PrivateKeyParameters.cs | 6 +++ .../crypto/parameters/X25519PublicKeyParameters.cs | 6 +++ .../crypto/parameters/X448PrivateKeyParameters.cs | 6 +++ .../crypto/parameters/X448PublicKeyParameters.cs | 6 +++ crypto/src/openpgp/PgpEncryptedDataGenerator.cs | 44 +++++++++++++++++----- crypto/src/openpgp/PgpPublicKey.cs | 20 ++++++++-- crypto/src/openpgp/PgpSecretKey.cs | 16 +++++++- crypto/src/pkcs/PrivateKeyInfoFactory.cs | 22 ++++++++--- crypto/src/util/Arrays.cs | 9 +++++ crypto/src/x509/SubjectPublicKeyInfoFactory.cs | 20 +++++++++- 16 files changed, 162 insertions(+), 42 deletions(-) diff --git a/crypto/src/asn1/x509/SubjectPublicKeyInfo.cs b/crypto/src/asn1/x509/SubjectPublicKeyInfo.cs index 4875152eb..234990fc7 100644 --- a/crypto/src/asn1/x509/SubjectPublicKeyInfo.cs +++ b/crypto/src/asn1/x509/SubjectPublicKeyInfo.cs @@ -49,7 +49,15 @@ namespace Org.BouncyCastle.Asn1.X509 this.algID = algID; } - private SubjectPublicKeyInfo( +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public SubjectPublicKeyInfo(AlgorithmIdentifier algID, ReadOnlySpan publicKey) + { + this.keyData = new DerBitString(publicKey); + this.algID = algID; + } +#endif + + private SubjectPublicKeyInfo( Asn1Sequence seq) { if (seq.Count != 2) diff --git a/crypto/src/crypto/fpe/FpeFf3_1Engine.cs b/crypto/src/crypto/fpe/FpeFf3_1Engine.cs index bc8d22fae..5e8148bb8 100644 --- a/crypto/src/crypto/fpe/FpeFf3_1Engine.cs +++ b/crypto/src/crypto/fpe/FpeFf3_1Engine.cs @@ -19,9 +19,7 @@ namespace Org.BouncyCastle.Crypto.Fpe : base(baseCipher) { if (IsOverrideSet(SP80038G.FPE_DISABLED)) - { throw new InvalidOperationException("FPE disabled"); - } } public override void Init(bool forEncryption, ICipherParameters parameters) @@ -29,7 +27,7 @@ namespace Org.BouncyCastle.Crypto.Fpe this.forEncryption = forEncryption; this.fpeParameters = (FpeParameters)parameters; - baseCipher.Init(!fpeParameters.UseInverseFunction, ReverseKey(fpeParameters.Key)); + baseCipher.Init(!fpeParameters.UseInverseFunction, fpeParameters.Key.Reverse()); if (fpeParameters.GetTweak().Length != 7) throw new ArgumentException("tweak should be 56 bits"); @@ -82,18 +80,5 @@ namespace Org.BouncyCastle.Crypto.Fpe return length; } - - private static KeyParameter ReverseKey(KeyParameter key) - { -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - return KeyParameter.Create(key.KeyLength, key, (bytes, key) => - { - key.Key.CopyTo(bytes); - bytes.Reverse(); - }); -#else - return new KeyParameter(Arrays.Reverse(key.GetKey())); -#endif - } } } diff --git a/crypto/src/crypto/fpe/SP80038G.cs b/crypto/src/crypto/fpe/SP80038G.cs index c57a34762..5488f20fd 100644 --- a/crypto/src/crypto/fpe/SP80038G.cs +++ b/crypto/src/crypto/fpe/SP80038G.cs @@ -358,11 +358,10 @@ namespace Org.BouncyCastle.Crypto.Fpe // iii. Array.Reverse(P); cipher.ProcessBlock(P, 0, P, 0); - Array.Reverse(P); byte[] S = P; // iv. - return new BigInteger(1, S); + return new BigInteger(1, S, bigEndian: false); } private static void CheckArgs(IBlockCipher cipher, bool isFF1, int radix, ushort[] buf, int off, int len) diff --git a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs index 6d2e44937..3a760afc1 100644 --- a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs @@ -70,6 +70,12 @@ namespace Org.BouncyCastle.Crypto.Parameters return Arrays.Clone(data); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal ReadOnlySpan DataSpan => data; + + internal ReadOnlyMemory DataMemory => data; +#endif + public Ed25519PublicKeyParameters GeneratePublicKey() { lock (data) diff --git a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs index a9d1d0e93..544dbf32d 100644 --- a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs @@ -70,6 +70,12 @@ namespace Org.BouncyCastle.Crypto.Parameters return Arrays.Clone(data); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal ReadOnlySpan DataSpan => data; + + internal ReadOnlyMemory DataMemory => data; +#endif + public Ed448PublicKeyParameters GeneratePublicKey() { lock (data) diff --git a/crypto/src/crypto/parameters/KeyParameter.cs b/crypto/src/crypto/parameters/KeyParameter.cs index bd2482f22..c29dfe61b 100644 --- a/crypto/src/crypto/parameters/KeyParameter.cs +++ b/crypto/src/crypto/parameters/KeyParameter.cs @@ -85,5 +85,12 @@ namespace Org.BouncyCastle.Crypto.Parameters #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER internal ReadOnlySpan Key => m_key; #endif + + internal KeyParameter Reverse() + { + var reversed = new KeyParameter(m_key.Length); + Arrays.Reverse(m_key, reversed.m_key); + return reversed; + } } } diff --git a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs index 8b263c861..ca989a8d3 100644 --- a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs @@ -68,6 +68,12 @@ namespace Org.BouncyCastle.Crypto.Parameters return Arrays.Clone(data); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal ReadOnlySpan DataSpan => data; + + internal ReadOnlyMemory DataMemory => data; +#endif + public X25519PublicKeyParameters GeneratePublicKey() { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER diff --git a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs index 5d94ac10a..c68be3060 100644 --- a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs @@ -60,6 +60,12 @@ namespace Org.BouncyCastle.Crypto.Parameters return Arrays.Clone(data); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal ReadOnlySpan DataSpan => data; + + internal ReadOnlyMemory DataMemory => data; +#endif + private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) diff --git a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs index 555773b10..712885875 100644 --- a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs @@ -68,6 +68,12 @@ namespace Org.BouncyCastle.Crypto.Parameters return Arrays.Clone(data); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal ReadOnlySpan DataSpan => data; + + internal ReadOnlyMemory DataMemory => data; +#endif + public X448PublicKeyParameters GeneratePublicKey() { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER diff --git a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs index 94db22147..e66056f9c 100644 --- a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs @@ -60,6 +60,12 @@ namespace Org.BouncyCastle.Crypto.Parameters return Arrays.Clone(data); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal ReadOnlySpan DataSpan => data; + + internal ReadOnlyMemory DataMemory => data; +#endif + private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs index a6482db6c..ac847ddb6 100644 --- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs +++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs @@ -149,9 +149,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp byte[] secret = new byte[agreement.AgreementSize]; agreement.CalculateAgreement(cryptoPublicKey, secret, 0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span ephPubEncoding = stackalloc byte[1 + X25519PublicKeyParameters.KeySize]; + ((X25519PublicKeyParameters)ephKp.Public).Encode(ephPubEncoding[1..]); +#else byte[] ephPubEncoding = new byte[1 + X25519PublicKeyParameters.KeySize]; - ephPubEncoding[0] = 0x40; ((X25519PublicKeyParameters)ephKp.Public).Encode(ephPubEncoding, 1); +#endif + ephPubEncoding[0] = 0x40; return EncryptSessionInfo(ecPubKey, sessionInfo, secret, ephPubEncoding, random); } @@ -168,9 +173,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp byte[] secret = new byte[agreement.AgreementSize]; agreement.CalculateAgreement(cryptoPublicKey, secret, 0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span ephPubEncoding = stackalloc byte[1 + X448PublicKeyParameters.KeySize]; + ((X448PublicKeyParameters)ephKp.Public).Encode(ephPubEncoding[1..]); +#else byte[] ephPubEncoding = new byte[1 + X448PublicKeyParameters.KeySize]; - ephPubEncoding[0] = 0x40; ((X448PublicKeyParameters)ephKp.Public).Encode(ephPubEncoding, 1); +#endif + ephPubEncoding[0] = 0x40; return EncryptSessionInfo(ecPubKey, sessionInfo, secret, ephPubEncoding, random); } @@ -188,13 +198,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp BigInteger S = agreement.CalculateAgreement(cryptoPublicKey); byte[] secret = BigIntegers.AsUnsignedByteArray(agreement.GetFieldSize(), S); - byte[] ephPubEncoding = ((ECPublicKeyParameters)ephKp.Public).Q.GetEncoded(false); + var q = ((ECPublicKeyParameters)ephKp.Public).Q; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int encodedLength = q.GetEncodedLength(false); + Span ephPubEncoding = encodedLength <= 512 + ? stackalloc byte[encodedLength] + : new byte[encodedLength]; + q.EncodeTo(false, ephPubEncoding); +#else + byte[] ephPubEncoding = q.GetEncoded(false); +#endif + return EncryptSessionInfo(ecPubKey, sessionInfo, secret, ephPubEncoding, random); } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private byte[] EncryptSessionInfo(ECDHPublicBcpgKey ecPubKey, byte[] sessionInfo, byte[] secret, + ReadOnlySpan ephPubEncoding, SecureRandom random) +#else private byte[] EncryptSessionInfo(ECDHPublicBcpgKey ecPubKey, byte[] sessionInfo, byte[] secret, byte[] ephPubEncoding, SecureRandom random) +#endif { var key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, secret)); @@ -402,14 +428,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp sessionInfo[sessionInfo.Length - 1] = (byte)(check); } - private byte[] CreateSessionInfo( - SymmetricKeyAlgorithmTag algorithm, - KeyParameter key) + private byte[] CreateSessionInfo(SymmetricKeyAlgorithmTag algorithm, KeyParameter key) { - byte[] keyBytes = key.GetKey(); - byte[] sessionInfo = new byte[keyBytes.Length + 3]; - sessionInfo[0] = (byte) algorithm; - keyBytes.CopyTo(sessionInfo, 1); + int keyLength = key.KeyLength; + byte[] sessionInfo = new byte[keyLength + 3]; + sessionInfo[0] = (byte)algorithm; + key.CopyTo(sessionInfo, 1, keyLength); AddCheckSum(sessionInfo); return sessionInfo; } diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs index 8b3575909..fa924ff37 100644 --- a/crypto/src/openpgp/PgpPublicKey.cs +++ b/crypto/src/openpgp/PgpPublicKey.cs @@ -575,8 +575,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), - // TODO Span variant +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + pEnc.AsSpan(1))); +#else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); +#endif } else if (EdECObjectIdentifiers.id_X448.Equals(curveOid)) { @@ -586,8 +589,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), - // TODO Span variant +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + pEnc.AsSpan(1))); +#else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); +#endif } else { @@ -608,8 +614,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), - // TODO Span variant +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + pEnc.AsSpan(1))); +#else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); +#endif } else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid)) { @@ -619,8 +628,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), - // TODO Span variant +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + pEnc.AsSpan(1))); +#else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); +#endif } else { diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs index 627b6788a..184621b5c 100644 --- a/crypto/src/openpgp/PgpSecretKey.cs +++ b/crypto/src/openpgp/PgpSecretKey.cs @@ -67,9 +67,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } else { - // 'reverse' because the native format for X25519 private keys is little-endian + // The native format for X25519 private keys is little-endian X25519PrivateKeyParameters xK = (X25519PrivateKeyParameters)privKey.Key; - secKey = new ECSecretBcpgKey(new BigInteger(1, Arrays.ReverseInPlace(xK.GetEncoded()))); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + secKey = new ECSecretBcpgKey(new BigInteger(1, xK.DataSpan, bigEndian: false)); +#else + secKey = new ECSecretBcpgKey(new BigInteger(1, xK.GetEncoded(), bigEndian: false)); +#endif } break; } @@ -81,11 +85,19 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { if (privKey.Key is Ed25519PrivateKeyParameters ed25519K) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + secKey = new EdSecretBcpgKey(new BigInteger(1, ed25519K.DataSpan)); +#else secKey = new EdSecretBcpgKey(new BigInteger(1, ed25519K.GetEncoded())); +#endif } else if (privKey.Key is Ed448PrivateKeyParameters ed448K) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + secKey = new EdSecretBcpgKey(new BigInteger(1, ed448K.DataSpan)); +#else secKey = new EdSecretBcpgKey(new BigInteger(1, ed448K.GetEncoded())); +#endif } else { diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs index d56831f35..1bfeedb79 100644 --- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs +++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs @@ -112,18 +112,28 @@ namespace Org.BouncyCastle.Pkcs return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object(), attributes); } - if (privateKey is ECPrivateKeyParameters) + if (privateKey is ECPrivateKeyParameters priv) { - ECPrivateKeyParameters priv = (ECPrivateKeyParameters) privateKey; - DerBitString publicKey = new DerBitString(ECKeyPairGenerator.GetCorrespondingPublicKey(priv).Q.GetEncoded(false)); + var pub = ECKeyPairGenerator.GetCorrespondingPublicKey(priv); + var q = pub.Q; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int encodedLength = q.GetEncodedLength(false); + Span pubEncoding = encodedLength <= 512 + ? stackalloc byte[encodedLength] + : new byte[encodedLength]; + q.EncodeTo(false, pubEncoding); +#else + byte[] pubEncoding = q.GetEncoded(false); +#endif + + DerBitString publicKey = new DerBitString(pubEncoding); ECDomainParameters dp = priv.Parameters; // ECGOST3410 - if (dp is ECGost3410Parameters) + if (dp is ECGost3410Parameters domainParameters) { - ECGost3410Parameters domainParameters = (ECGost3410Parameters) dp; - Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( (domainParameters).PublicKeyParamSet, (domainParameters).DigestParamSet, diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index 83fafb388..f99065512 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -958,6 +958,15 @@ namespace Org.BouncyCastle.Utilities return result; } + internal static void Reverse(T[] input, T[] output) + { + int last = input.Length - 1; + for (int i = 0; i <= last; ++i) + { + output[i] = input[last - i]; + } + } + public static T[] ReverseInPlace(T[] array) { if (null == array) diff --git a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs index fc0492fe4..36facc3aa 100644 --- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs @@ -174,7 +174,17 @@ namespace Org.BouncyCastle.X509 x962 = new X962Parameters(_key.PublicKeyParamSet); } - byte[] pubKey = _key.Q.GetEncoded(false); + var q = _key.Q; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int encodedLength = q.GetEncodedLength(false); + Span pubKey = encodedLength <= 512 + ? stackalloc byte[encodedLength] + : new byte[encodedLength]; + q.EncodeTo(false, pubKey); +#else + byte[] pubKey = q.GetEncoded(false); +#endif AlgorithmIdentifier algID = new AlgorithmIdentifier( X9ObjectIdentifiers.IdECPublicKey, x962.ToAsn1Object()); @@ -212,14 +222,22 @@ namespace Org.BouncyCastle.X509 { X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.DataSpan); +#else return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.GetEncoded()); +#endif } if (publicKey is X25519PublicKeyParameters) { X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.DataSpan); +#else return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.GetEncoded()); +#endif } if (publicKey is Ed448PublicKeyParameters) -- cgit 1.4.1