// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 using System; using software.amazon.cryptography.primitives.internaldafny.types; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Security; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Asn1; using Wrappers_Compile; using icharseq = Dafny.ISequence; using ibyteseq = Dafny.ISequence; using byteseq = Dafny.Sequence; using _IError = software.amazon.cryptography.primitives.internaldafny.types._IError; using Error_Opaque = software.amazon.cryptography.primitives.internaldafny.types.Error_Opaque; using Error_AwsCryptographicPrimitivesError = software.amazon.cryptography.primitives.internaldafny.types.Error_AwsCryptographicPrimitivesError; namespace Signature { public partial class ECDSA { public static _IResult ExternKeyGen(_IECDSASignatureAlgorithm x) { try { ECKeyPairGenerator generator = new ECKeyPairGenerator(); SecureRandom rng = new SecureRandom(); X9ECParameters p; if (x.is_ECDSA__P384) { p = ECNamedCurveTable.GetByName("secp384r1"); } else if (x.is_ECDSA__P256) { p = ECNamedCurveTable.GetByName("secp256r1"); } else { return Result .create_Failure(new Error_AwsCryptographicPrimitivesError( Dafny.Sequence.FromString(String.Format("Unsupported ECDSA parameters: {0}", x)))); } generator.Init(new ECKeyGenerationParameters(new ECDomainParameters(p.Curve, p.G, p.N, p.H), rng)); AsymmetricCipherKeyPair kp = generator.GenerateKeyPair(); ECPoint pt = ((ECPublicKeyParameters)kp.Public).Q; // serialize the public and private keys, and then return them var verificationKey = SerializePublicKey((ECPublicKeyParameters)kp.Public); var signingKey = byteseq.FromArray(((ECPrivateKeyParameters)kp.Private).D.ToByteArray()); return Result .create_Success(new SignatureKeyPair(verificationKey, signingKey)); } catch (Exception e) { return Result .create_Failure(new Error_Opaque(e)); } } /// /// Compresses and encodes the elliptic curve point corresponding to the given public key. /// See SEC-1 v2 (http://www.secg.org/sec1-v2.pdf), sections 2.3.3 and 2.3.5. For /// example, note: /// the compressed y-coordinate is placed in the leftmost octet of the octet string /// along with an indication that point compression is on, and the x-coordinate is /// placed in the remainder of the octet string /// Requires: keyParams.Parameters.Curve is a prime curve (if not, a cast exception will be thrown) /// public static ibyteseq SerializePublicKey(ECPublicKeyParameters keyParams) { ECPoint pt = keyParams.Q; // zero-pad x coordinate var xBytes = pt.AffineXCoord.GetEncoded(); var curve = (FpCurve)keyParams.Parameters.Curve; int fieldByteSize = (curve.FieldSize + 7) / 8; if (xBytes.Length < fieldByteSize) { var paddingLength = fieldByteSize - xBytes.Length; var paddedX = new byte[fieldByteSize]; System.Array.Clear(paddedX, 0, paddingLength); xBytes.CopyTo(paddedX, paddingLength); xBytes = paddedX; } // compress y coordinate var y = pt.AffineYCoord.ToBigInteger(); byte compressedY = y.Mod(BigInteger.ValueOf(2)).Equals(BigInteger.ValueOf(0)) ? (byte)2 : (byte)3; var yBytes = new byte[] { compressedY }; // return yBytes + xBytes: return byteseq.Concat(byteseq.FromArray(yBytes), (byteseq.FromArray(xBytes))); } public static _IResult Verify( _IECDSASignatureAlgorithm x, ibyteseq vk, ibyteseq msg, ibyteseq sig ) { try { X9ECParameters parameters; if (x.is_ECDSA__P384) { parameters = ECNamedCurveTable.GetByName("secp384r1"); } else if (x.is_ECDSA__P256) { parameters = ECNamedCurveTable.GetByName("secp256r1"); } else { return Result .create_Failure(new Error_AwsCryptographicPrimitivesError( Dafny.Sequence.FromString(String.Format("Unsupported ECDSA parameters: {0}", x)))); } byte[] digest = InternalDigest(x, msg); ECDomainParameters dp = new ECDomainParameters(parameters.Curve, parameters.G, parameters.N, parameters.H); ECPoint pt = parameters.Curve.DecodePoint((byte[])vk.Elements.Clone()); ECPublicKeyParameters vkp = new ECPublicKeyParameters(pt, dp); ECDsaSigner sign = new ECDsaSigner(); sign.Init(false, vkp); BigInteger r, s; DERDeserialize(sig.Elements, out r, out s); return Result.create_Success(sign.VerifySignature(digest, r, s)); } catch (Exception e) { return Result .create_Failure(new Error_Opaque(e)); } } public static _IResult Sign(_IECDSASignatureAlgorithm x, ibyteseq sk, ibyteseq msg) { try { X9ECParameters parameters; if (x.is_ECDSA__P384) { parameters = ECNamedCurveTable.GetByName("secp384r1"); } else if (x.is_ECDSA__P256) { parameters = ECNamedCurveTable.GetByName("secp256r1"); } else { return Result .create_Failure(new Error_AwsCryptographicPrimitivesError( Dafny.Sequence.FromString(String.Format("Unsupported ECDSA parameters: {0}", x)))); } byte[] digest = InternalDigest(x, msg); ECDomainParameters dp = new ECDomainParameters(parameters.Curve, parameters.G, parameters.N, parameters.H); ECPrivateKeyParameters skp = new ECPrivateKeyParameters(new BigInteger(sk.Elements), dp); ECDsaSigner sign = new ECDsaSigner(); sign.Init(true, skp); byte[] serializedSignature; // This loop can in theory run forever, but the chances of that are negligible. // We may want to consider failing, after some number of loops, if we can do so in a way consistent with other ESDKs. do { // sig is array of two integers: r and s BigInteger[] sig = sign.GenerateSignature(digest); serializedSignature = DERSerialize(sig[0], sig[1]); if (serializedSignature.Length != Signature.__default.SignatureLength(x)) { // Most of the time, a signature of the wrong length can be fixed // by negating s in the signature relative to the group order. serializedSignature = DERSerialize(sig[0], parameters.N.Subtract(sig[1])); } } while (serializedSignature.Length != Signature.__default.SignatureLength(x)); return Result.create_Success(byteseq.FromArray(serializedSignature)); } catch (Exception e) { return Result .create_Failure(new Error_Opaque(e)); } } private static byte[] InternalDigest(_IECDSASignatureAlgorithm x, ibyteseq msg) { System.Security.Cryptography.HashAlgorithm alg; if (x.is_ECDSA__P384) { alg = System.Security.Cryptography.SHA384.Create(); } else if (x.is_ECDSA__P256) { alg = System.Security.Cryptography.SHA256.Create(); } else { throw new System.Exception(String.Format("Unsupported Curve: {0}", x)); } return alg.ComputeHash(msg.Elements); } private static byte[] DERSerialize(BigInteger r, BigInteger s) { DerSequence derSeq = new DerSequence(new DerInteger(r), new DerInteger(s)); return derSeq.GetEncoded(); } private static void DERDeserialize(byte[] bytes, out BigInteger r, out BigInteger s) { Asn1InputStream asn1 = new Asn1InputStream(bytes); var seq = (Asn1Sequence)asn1.ReadObject(); var dr = (DerInteger)seq[0]; var ds = (DerInteger)seq[1]; r = new BigInteger(dr.ToString()); s = new BigInteger(ds.ToString()); } } }