From 515f53364796324fc2b024e1ccab9b5adc4bcd7b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 12 Jan 2023 18:15:17 +0700 Subject: Improvements to EdDSA use cases - see https://github.com/bcgit/bc-csharp/issues/406 --- crypto/src/cms/CMSSignedDataStreamGenerator.cs | 20 ++-- crypto/src/crypto/operators/Asn1Signature.cs | 4 + crypto/src/ocsp/OCSPReqGenerator.cs | 54 ++++------- crypto/src/openpgp/PgpSignatureGenerator.cs | 24 +++-- crypto/src/openpgp/PgpV3SignatureGenerator.cs | 23 +++-- crypto/src/security/SignerUtilities.cs | 121 ++++++++++++++++++------- 6 files changed, 144 insertions(+), 102 deletions(-) diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs index 33b661761..48abfbfa2 100644 --- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs +++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs @@ -102,18 +102,18 @@ namespace Org.BouncyCastle.Cms if (_sAttr != null) { - _sig = Helper.GetSignatureInstance(signatureName); - } - else + _sig = SignerUtilities.InitSigner(signatureName, true, key, outer.m_random); + } + else { // Note: Need to use raw signatures here since we have already calculated the digest if (_encName.Equals("RSA")) { - _sig = Helper.GetSignatureInstance("RSA"); - } - else if (_encName.Equals("DSA")) + _sig = SignerUtilities.InitSigner("RSA", true, key, outer.m_random); + } + else if (_encName.Equals("DSA")) { - _sig = Helper.GetSignatureInstance("NONEwithDSA"); + _sig = SignerUtilities.InitSigner("NONEwithDSA", true, key, outer.m_random); } // TODO Add support for raw PSS // else if (_encName.equals("RSAandMGF1")) @@ -135,10 +135,8 @@ namespace Org.BouncyCastle.Cms { throw new SignatureException("algorithm: " + _encName + " not supported in base signatures."); } - } - - _sig.Init(true, new ParametersWithRandom(key, outer.m_random)); - } + } + } public SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm, byte[] calculatedDigest) diff --git a/crypto/src/crypto/operators/Asn1Signature.cs b/crypto/src/crypto/operators/Asn1Signature.cs index ea8d28771..acbeb12e8 100644 --- a/crypto/src/crypto/operators/Asn1Signature.cs +++ b/crypto/src/crypto/operators/Asn1Signature.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; @@ -97,6 +98,9 @@ namespace Org.BouncyCastle.Crypto.Operators m_algorithms.Add("GOST3411-2012-256WITHECGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256); m_algorithms.Add("GOST3411-2012-512WITHECGOST3410", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512); m_algorithms.Add("GOST3411-2012-512WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512); + m_algorithms.Add("Ed25519", EdECObjectIdentifiers.id_Ed25519); + m_algorithms.Add("Ed448", EdECObjectIdentifiers.id_Ed448); + // TODO Ed25519ctx, Ed25519ph, Ed448ph // // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. diff --git a/crypto/src/ocsp/OCSPReqGenerator.cs b/crypto/src/ocsp/OCSPReqGenerator.cs index dda1625e5..9a5d72ae8 100644 --- a/crypto/src/ocsp/OCSPReqGenerator.cs +++ b/crypto/src/ocsp/OCSPReqGenerator.cs @@ -6,6 +6,7 @@ using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Ocsp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Security.Certificates; @@ -93,13 +94,10 @@ namespace Org.BouncyCastle.Ocsp this.requestExtensions = requestExtensions; } - private OcspReq GenerateRequest( - DerObjectIdentifier signingAlgorithm, - AsymmetricKeyParameter privateKey, - X509Certificate[] chain, - SecureRandom random) + private OcspReq GenerateRequest(DerObjectIdentifier signingAlgorithm, AsymmetricKeyParameter privateKey, + X509Certificate[] chain, SecureRandom random) { - Asn1EncodableVector requests = new Asn1EncodableVector(); + Asn1EncodableVector requests = new Asn1EncodableVector(list.Count); foreach (RequestObject reqObj in list) { @@ -114,42 +112,29 @@ namespace Org.BouncyCastle.Ocsp } TbsRequest tbsReq = new TbsRequest(requestorName, new DerSequence(requests), requestExtensions); - - ISigner sig = null; Signature signature = null; if (signingAlgorithm != null) { if (requestorName == null) - { throw new OcspException("requestorName must be specified if request is signed."); - } - try - { - sig = SignerUtilities.GetSigner(signingAlgorithm.Id); - if (random != null) - { - sig.Init(true, new ParametersWithRandom(privateKey, random)); - } - else - { - sig.Init(true, privateKey); - } + ISigner signer; + try + { + signer = SignerUtilities.InitSigner(signingAlgorithm, true, privateKey, random); } catch (Exception e) { throw new OcspException("exception creating signature: " + e, e); } - DerBitString bitSig = null; - + DerBitString bitSig; try { - byte[] encoded = tbsReq.GetEncoded(); - sig.BlockUpdate(encoded, 0, encoded.Length); + tbsReq.EncodeTo(new SignerSink(signer), Asn1Encodable.Der); - bitSig = new DerBitString(sig.GenerateSignature()); + bitSig = new DerBitString(signer.GenerateSignature()); } catch (Exception e) { @@ -158,9 +143,10 @@ namespace Org.BouncyCastle.Ocsp AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(signingAlgorithm, DerNull.Instance); - if (chain != null && chain.Length > 0) + Asn1Sequence certs = null; + if (!Arrays.IsNullOrEmpty(chain)) { - Asn1EncodableVector v = new Asn1EncodableVector(); + Asn1EncodableVector v = new Asn1EncodableVector(chain.Length); try { for (int i = 0; i != chain.Length; i++) @@ -177,15 +163,13 @@ namespace Org.BouncyCastle.Ocsp throw new OcspException("error encoding certs", e); } - signature = new Signature(sigAlgId, bitSig, new DerSequence(v)); + certs = new DerSequence(v); } - else - { - signature = new Signature(sigAlgId, bitSig); - } - } - return new OcspReq(new OcspRequest(tbsReq, signature)); + signature = new Signature(sigAlgId, bitSig, certs); + } + + return new OcspReq(new OcspRequest(tbsReq, signature)); } /** diff --git a/crypto/src/openpgp/PgpSignatureGenerator.cs b/crypto/src/openpgp/PgpSignatureGenerator.cs index 12edf9f89..64d256653 100644 --- a/crypto/src/openpgp/PgpSignatureGenerator.cs +++ b/crypto/src/openpgp/PgpSignatureGenerator.cs @@ -40,36 +40,34 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } /// Initialise the generator for signing. - public void InitSign( - int sigType, - PgpPrivateKey privKey) + public void InitSign(int sigType, PgpPrivateKey privKey) { InitSign(sigType, privKey, null); } /// Initialise the generator for signing. - public void InitSign( - int sigType, - PgpPrivateKey privKey, - SecureRandom random) + public void InitSign(int sigType, PgpPrivateKey privKey, SecureRandom random) { this.privKey = privKey; this.signatureType = sigType; AsymmetricKeyParameter key = privKey.Key; - if (sig == null) - { - this.sig = PgpUtilities.CreateSigner(keyAlgorithm, hashAlgorithm, key); - } + this.sig = PgpUtilities.CreateSigner(keyAlgorithm, hashAlgorithm, key); try { ICipherParameters cp = key; - if (random != null) + + // TODO Ask SignerUtilities whether random is permitted? + if (keyAlgorithm == PublicKeyAlgorithmTag.EdDsa) { - cp = new ParametersWithRandom(cp, random); + // EdDSA signers don't expect a SecureRandom } + else + { + cp = ParameterUtilities.WithRandom(cp, random); + } sig.Init(true, cp); } diff --git a/crypto/src/openpgp/PgpV3SignatureGenerator.cs b/crypto/src/openpgp/PgpV3SignatureGenerator.cs index 324dbd768..03dd8795d 100644 --- a/crypto/src/openpgp/PgpV3SignatureGenerator.cs +++ b/crypto/src/openpgp/PgpV3SignatureGenerator.cs @@ -47,20 +47,23 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp AsymmetricKeyParameter key = privKey.Key; - if (sig == null) - { - this.sig = PgpUtilities.CreateSigner(keyAlgorithm, hashAlgorithm, key); - } + this.sig = PgpUtilities.CreateSigner(keyAlgorithm, hashAlgorithm, key); try { - ICipherParameters cp = key; - if (random != null) - { - cp = new ParametersWithRandom(cp, random); - } + ICipherParameters cp = key; + + // TODO Ask SignerUtilities whether random is permitted? + if (keyAlgorithm == PublicKeyAlgorithmTag.EdDsa) + { + // EdDSA signers don't expect a SecureRandom + } + else + { + cp = ParameterUtilities.WithRandom(cp, random); + } - sig.Init(true, cp); + sig.Init(true, cp); } catch (InvalidKeyException e) { diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs index 6500cdf13..917759a8e 100644 --- a/crypto/src/security/SignerUtilities.cs +++ b/crypto/src/security/SignerUtilities.cs @@ -10,16 +10,16 @@ using Org.BouncyCastle.Asn1.GM; using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.TeleTrust; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; -using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; -using Org.BouncyCastle.Asn1.Rosstandart; namespace Org.BouncyCastle.Security { @@ -28,9 +28,10 @@ namespace Org.BouncyCastle.Security /// public static class SignerUtilities { - internal static readonly IDictionary AlgorithmMap = + private static readonly IDictionary AlgorithmMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - internal static readonly IDictionary Oids = + private static readonly HashSet NoRandom = new HashSet(StringComparer.OrdinalIgnoreCase); + private static readonly IDictionary Oids = new Dictionary(StringComparer.OrdinalIgnoreCase); static SignerUtilities() @@ -408,6 +409,14 @@ namespace Org.BouncyCastle.Security AlgorithmMap["SM3WITHSM2"] = "SM3withSM2"; AlgorithmMap[GMObjectIdentifiers.sm2sign_with_sm3.Id] = "SM3withSM2"; + NoRandom.Add("Ed25519"); + NoRandom.Add(EdECObjectIdentifiers.id_Ed25519.Id); + NoRandom.Add("Ed25519ctx"); + NoRandom.Add("Ed25519ph"); + NoRandom.Add("Ed448"); + NoRandom.Add(EdECObjectIdentifiers.id_Ed448.Id); + NoRandom.Add("Ed448ph"); + Oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption; Oids["MD4withRSA"] = PkcsObjectIdentifiers.MD4WithRsaEncryption; Oids["MD5withRSA"] = PkcsObjectIdentifiers.MD5WithRsaEncryption; @@ -519,6 +528,11 @@ namespace Org.BouncyCastle.Security return DerNull.Instance; } + private static string GetMechanism(string algorithm) + { + return AlgorithmMap.TryGetValue(algorithm, out var v) ? v : algorithm.ToUpperInvariant(); + } + private static Asn1Encodable GetPssX509Parameters( string digestName) { @@ -536,6 +550,9 @@ namespace Org.BouncyCastle.Security public static ISigner GetSigner(DerObjectIdentifier id) { + if (id == null) + throw new ArgumentNullException(nameof(id)); + return GetSigner(id.Id); } @@ -544,8 +561,17 @@ namespace Org.BouncyCastle.Security if (algorithm == null) throw new ArgumentNullException(nameof(algorithm)); - string mechanism = CollectionUtilities.GetValueOrKey(AlgorithmMap, algorithm.ToUpperInvariant()); + string mechanism = GetMechanism(algorithm); + + var signer = GetSignerForMechanism(mechanism); + if (signer == null) + throw new SecurityUtilityException("Signer " + algorithm + " not recognised."); + + return signer; + } + private static ISigner GetSignerForMechanism(string mechanism) + { if (Platform.StartsWith(mechanism, "Ed")) { if (mechanism.Equals("Ed25519")) @@ -638,30 +664,37 @@ namespace Org.BouncyCastle.Security { return new Gost3410DigestSigner(new Gost3410Signer(), new Gost3411Digest()); } - if (mechanism.Equals("ECGOST3410")) - { - return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411Digest()); - } - if (mechanism.Equals("ECGOST3410-2012-256")) - { - return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411_2012_256Digest()); - } - if (mechanism.Equals("ECGOST3410-2012-512")) - { - return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411_2012_512Digest()); - } - if (mechanism.Equals("SHA1WITHRSA/ISO9796-2")) - { - return new Iso9796d2Signer(new RsaBlindedEngine(), new Sha1Digest(), true); - } - if (mechanism.Equals("MD5WITHRSA/ISO9796-2")) + if (Platform.StartsWith(mechanism, "ECGOST3410")) { - return new Iso9796d2Signer(new RsaBlindedEngine(), new MD5Digest(), true); + if (mechanism.Equals("ECGOST3410")) + { + return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411Digest()); + } + if (mechanism.Equals("ECGOST3410-2012-256")) + { + return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411_2012_256Digest()); + } + if (mechanism.Equals("ECGOST3410-2012-512")) + { + return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411_2012_512Digest()); + } } - if (mechanism.Equals("RIPEMD160WITHRSA/ISO9796-2")) + + if (Platform.EndsWith(mechanism, "/ISO9796-2")) { - return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true); + if (mechanism.Equals("SHA1WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new Sha1Digest(), true); + } + if (mechanism.Equals("MD5WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new MD5Digest(), true); + } + if (mechanism.Equals("RIPEMD160WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true); + } } if (Platform.EndsWith(mechanism, "/X9.31")) @@ -672,19 +705,20 @@ namespace Org.BouncyCastle.Security { int endPos = withPos + "WITH".Length; - string digestName = x931.Substring(0, withPos); - IDigest digest = DigestUtilities.GetDigest(digestName); - string cipherName = x931.Substring(endPos, x931.Length - endPos); if (cipherName.Equals("RSA")) { IAsymmetricBlockCipher cipher = new RsaBlindedEngine(); + + string digestName = x931.Substring(0, withPos); + IDigest digest = DigestUtilities.GetDigest(digestName); + return new X931Signer(cipher, digest); } } } - throw new SecurityUtilityException("Signer " + algorithm + " not recognised."); + return null; } public static string GetEncodingName(DerObjectIdentifier oid) @@ -692,15 +726,36 @@ namespace Org.BouncyCastle.Security return CollectionUtilities.GetValueOrNull(AlgorithmMap, oid.Id); } - public static ISigner InitSigner(DerObjectIdentifier algorithmOid, bool forSigning, AsymmetricKeyParameter privateKey, SecureRandom random) + // TODO Rename 'privateKey' to 'key' + public static ISigner InitSigner(DerObjectIdentifier algorithmOid, bool forSigning, + AsymmetricKeyParameter privateKey, SecureRandom random) { + if (algorithmOid == null) + throw new ArgumentNullException(nameof(algorithmOid)); + return InitSigner(algorithmOid.Id, forSigning, privateKey, random); } - public static ISigner InitSigner(string algorithm, bool forSigning, AsymmetricKeyParameter privateKey, SecureRandom random) + // TODO Rename 'privateKey' to 'key' + public static ISigner InitSigner(string algorithm, bool forSigning, AsymmetricKeyParameter privateKey, + SecureRandom random) { - ISigner signer = GetSigner(algorithm); - signer.Init(forSigning, ParameterUtilities.WithRandom(privateKey, random)); + if (algorithm == null) + throw new ArgumentNullException(nameof(algorithm)); + + string mechanism = GetMechanism(algorithm); + + var signer = GetSignerForMechanism(mechanism); + if (signer == null) + throw new SecurityUtilityException("Signer " + algorithm + " not recognised."); + + ICipherParameters cipherParameters = privateKey; + if (forSigning && !NoRandom.Contains(mechanism)) + { + cipherParameters = ParameterUtilities.WithRandom(cipherParameters, random); + } + + signer.Init(forSigning, cipherParameters); return signer; } } -- cgit 1.4.1