diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs index b5cf5295e47872..47c55fb8699f1d 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs @@ -96,6 +96,119 @@ internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve) return key; } + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcKeyParameters", StringMarshalling = StringMarshalling.Utf8)] + private static partial int EvpPKeyCreateByEcKeyParameters( + out SafeEvpPKeyHandle pkey, + string oid, + byte[]? qx, int qxLength, + byte[]? qy, int qyLength, + byte[]? d, int dLength); + + internal static SafeEvpPKeyHandle? EvpPKeyCreateByEcKeyParameters( + string oid, + byte[]? qx, int qxLength, + byte[]? qy, int qyLength, + byte[]? d, int dLength) + { + SafeEvpPKeyHandle pkey; + int rc = EvpPKeyCreateByEcKeyParameters(out pkey, oid, qx, qxLength, qy, qyLength, d, dLength); + + if (rc == -1) + { + pkey.Dispose(); + ErrClearError(); + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid)); + } + + if (rc != 1 || pkey.IsInvalid) + { + pkey.Dispose(); + ErrClearError(); + return null; + } + + return pkey; + } + + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcExplicitParameters")] + private static partial SafeEvpPKeyHandle CryptoNative_EvpPKeyCreateByEcExplicitParameters( + ECCurve.ECCurveType curveType, + byte[]? qx, int qxLength, + byte[]? qy, int qyLength, + byte[]? d, int dLength, + byte[] p, int pLength, + byte[] a, int aLength, + byte[] b, int bLength, + byte[] gx, int gxLength, + byte[] gy, int gyLength, + byte[] order, int orderLength, + byte[]? cofactor, int cofactorLength, + byte[]? seed, int seedLength); + + internal static SafeEvpPKeyHandle? EvpPKeyCreateByEcExplicitParameters( + ECCurve.ECCurveType curveType, + byte[]? qx, int qxLength, + byte[]? qy, int qyLength, + byte[]? d, int dLength, + byte[] p, int pLength, + byte[] a, int aLength, + byte[] b, int bLength, + byte[] gx, int gxLength, + byte[] gy, int gyLength, + byte[] order, int orderLength, + byte[]? cofactor, int cofactorLength, + byte[]? seed, int seedLength) + { + SafeEvpPKeyHandle pkey = CryptoNative_EvpPKeyCreateByEcExplicitParameters( + curveType, + qx, qxLength, + qy, qyLength, + d, dLength, + p, pLength, + a, aLength, + b, bLength, + gx, gxLength, + gy, gyLength, + order, orderLength, + cofactor, cofactorLength, + seed, seedLength); + + if (pkey.IsInvalid) + { + pkey.Dispose(); + ErrClearError(); + return null; + } + + return pkey; + } + + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcKeyOid", StringMarshalling = StringMarshalling.Utf8)] + private static partial int EvpPKeyGenerateByEcKeyOid( + out SafeEvpPKeyHandle pkey, + string oid); + + internal static SafeEvpPKeyHandle? EvpPKeyGenerateByEcKeyOid(string oid) + { + int rc = EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, oid); + + if (rc == -1) + { + pkey.Dispose(); + ErrClearError(); + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid)); + } + + if (rc != 1 || pkey.IsInvalid) + { + pkey.Dispose(); + ErrClearError(); + return null; + } + + return pkey; + } + [LibraryImport(Libraries.CryptoNative)] private static partial int CryptoNative_EvpPKeyGetEcGroupNid(SafeEvpPKeyHandle pkey, out int nid); @@ -104,11 +217,12 @@ internal static bool EvpPKeyHasCurveName(SafeEvpPKeyHandle pkey) int rc = CryptoNative_EvpPKeyGetEcGroupNid(pkey, out int nidCurveName); if (rc == 1) { - // Key is invalid or doesn't have a curve - return (nidCurveName != Interop.Crypto.NID_undef); + return nidCurveName != Interop.Crypto.NID_undef; } - throw Interop.Crypto.CreateOpenSslCryptographicException(); + // rc == 0 means the group name could not be retrieved + // (e.g., explicit curve or OpenSSL < 3.0). Treat as no curve name. + return false; } /// @@ -122,7 +236,35 @@ internal static bool EvpPKeyHasCurveName(SafeEvpPKeyHandle pkey) return nidCurveName != Interop.Crypto.NID_undef ? CurveNidToOidValue(nidCurveName) : null; } - throw Interop.Crypto.CreateOpenSslCryptographicException(); + // rc == 0 means the group name could not be retrieved + // (e.g., explicit curve or OpenSSL < 3.0). + return null; + } + + [LibraryImport(Libraries.CryptoNative)] + private static partial int CryptoNative_EvpPKeyEcHasExplicitEncoding(SafeEvpPKeyHandle pkey); + + /// + /// Returns true if the key has explicit encoding, false if named (or encoding unavailable), + /// null if the API is unavailable (pre-3.0) and the caller should use an alternative method. + /// + internal static bool? EvpPKeyEcHasExplicitEncoding(SafeEvpPKeyHandle pkey) + { + int result = CryptoNative_EvpPKeyEcHasExplicitEncoding(pkey); + return result switch + { + 1 => true, + 0 => false, + _ => null, + }; + } + + [LibraryImport(Libraries.CryptoNative)] + private static partial int CryptoNative_EvpPKeyGetEcFieldDegree(SafeEvpPKeyHandle pkey); + + internal static int EvpPKeyGetEcFieldDegree(SafeEvpPKeyHandle pkey) + { + return CryptoNative_EvpPKeyGetEcFieldDegree(pkey); } [LibraryImport(Libraries.CryptoNative)] diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs index a064f682ca65c6..ae93353f36402b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs @@ -89,17 +89,19 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa Debug.Assert(_key is not null); // Callers should validate prior. bool thisIsNamed; - - using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(_key.Value)) { - if (ecKey == null || ecKey.IsInvalid) + bool? explicitEncoding = Interop.Crypto.EvpPKeyEcHasExplicitEncoding(_key.Value); + if (explicitEncoding.HasValue) { - // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible. - thisIsNamed = Interop.Crypto.EvpPKeyHasCurveName(_key.Value); + thisIsNamed = !explicitEncoding.Value; } else { - thisIsNamed = Interop.Crypto.EcKeyHasCurveName(ecKey); + // Pre-3.0 fallback: check via EC_KEY. + using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(_key.Value)) + { + thisIsNamed = Interop.Crypto.EcKeyHasCurveName(ecKey); + } } } @@ -118,7 +120,7 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa otherKey = new ECDiffieHellmanOpenSslPublicKey(otherParameters); } - bool otherIsNamed = otherKey.HasCurveName; + bool otherIsNamed = !otherKey.HasExplicitEncoding; // We need to always duplicate handle in case this operation is done by multiple threads and one of them disposes the handle SafeEvpPKeyHandle? ourKey = _key.Value; @@ -143,10 +145,7 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa } else if (otherIsNamed) { - using (ECOpenSsl tmp = new ECOpenSsl(otherKey.ExportExplicitParameters())) - { - theirKey = tmp.CreateEvpPKeyHandle(); - } + theirKey = ECOpenSsl.ImportECKey(otherKey.ExportExplicitParameters(), out _); } else { @@ -155,11 +154,8 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa // This is generally not expected to fail except: // - when key can't be accessed but is available (i.e. TPM) // - private key is actually missing - using (ECOpenSsl tmp = new ECOpenSsl(ExportExplicitParameters(true))) - { - ourKey = tmp.CreateEvpPKeyHandle(); - disposeOurKey = true; - } + ourKey = ECOpenSsl.ImportECKey(ExportExplicitParameters(true), out _); + disposeOurKey = true; } catch (CryptographicException) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs index 8a225c5b4b3951..1afd236cbe8613 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography { internal sealed class ECDiffieHellmanOpenSslPublicKey : ECDiffieHellmanPublicKey { - private ECOpenSsl? _key; + private SafeEvpPKeyHandle? _key; internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle) { @@ -17,27 +17,12 @@ internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle) if (pkeyHandle.IsInvalid) throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(pkeyHandle)); - // If ecKey is valid it has already been up-ref'd, so we can just use this handle as-is. - SafeEcKeyHandle key = Interop.Crypto.EvpPkeyGetEcKey(pkeyHandle); - - if (key == null || key.IsInvalid) - { - key?.Dispose(); - - // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible. - // Since you cannot mix EC_KEY and EVP_PKEY params API we need to export and re-import the public key. - ECParameters ecParams = ECOpenSsl.ExportParameters(pkeyHandle, includePrivateParameters: false); - _key = new ECOpenSsl(ecParams); - } - else - { - _key = new ECOpenSsl(key); - } + _key = pkeyHandle.DuplicateHandle(); } internal ECDiffieHellmanOpenSslPublicKey(ECParameters parameters) { - _key = new ECOpenSsl(parameters); + _key = ECOpenSsl.ImportECKey(parameters, out _); } #pragma warning disable 0672 // Member overrides an obsolete member. @@ -60,14 +45,55 @@ public override ECParameters ExportExplicitParameters() => public override ECParameters ExportParameters() => ECOpenSsl.ExportParameters(GetKey(), includePrivateParameters: false); - internal bool HasCurveName => Interop.Crypto.EcKeyHasCurveName(GetKey()); + internal bool HasCurveName => Interop.Crypto.EvpPKeyHasCurveName(GetKey()); + + internal bool HasExplicitEncoding + { + get + { + ThrowIfDisposed(); + + bool? result = Interop.Crypto.EvpPKeyEcHasExplicitEncoding(GetKey()); + + if (result.HasValue) + { + return result.Value; + } + + // Fallback for EC_KEY-backed handles: check via EC_KEY. + using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(GetKey())) + { + if (ecKey is null || ecKey.IsInvalid) + { + throw new CryptographicException(SR.Cryptography_InvalidHandle); + } + + return !Interop.Crypto.EcKeyHasCurveName(ecKey); + } + } + } internal int KeySize { get { ThrowIfDisposed(); - return _key.KeySize; + + int keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(_key); + + if (keySize != 0) + { + return keySize; + } + + // Fallback for EC_KEY-backed handles: get size via EC_KEY. + using (SafeEcKeyHandle? ecKey = Interop.Crypto.EvpPkeyGetEcKey(_key)) + { + if (ecKey is not null && !ecKey.IsInvalid) + return Interop.Crypto.EcKeyGetSize(ecKey); + } + + throw new CryptographicException(SR.Cryptography_InvalidHandle); } } @@ -84,8 +110,7 @@ protected override void Dispose(bool disposing) internal SafeEvpPKeyHandle DuplicateKeyHandle() { - SafeEcKeyHandle currentKey = GetKey(); - return Interop.Crypto.CreateEvpPkeyFromEcKey(currentKey); + return GetKey().DuplicateHandle(); } [MemberNotNull(nameof(_key))] @@ -94,10 +119,10 @@ private void ThrowIfDisposed() ObjectDisposedException.ThrowIf(_key is null, this); } - private SafeEcKeyHandle GetKey() + private SafeEvpPKeyHandle GetKey() { ThrowIfDisposed(); - return _key.Value; + return _key; } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs index 625b532a023015..5df77b7d874b99 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs @@ -181,14 +181,13 @@ public static ECParameters ExportParameters(SafeEvpPKeyHandle pkey, bool include { CheckInvalidKey(pkey); - using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey)) + if (SafeEvpPKeyHandle.OpenSslVersion >= 0x3_00_00_00_0) { - if (ecKey == null || ecKey.IsInvalid) - { - // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible. - return ExportECParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters); - } + return ExportECParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters); + } + using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey)) + { return ECOpenSsl.ExportParameters(ecKey, includePrivateParameters); } } @@ -197,14 +196,13 @@ public static ECParameters ExportExplicitParameters(SafeEvpPKeyHandle pkey, bool { CheckInvalidKey(pkey); - using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey)) + if (SafeEvpPKeyHandle.OpenSslVersion >= 0x3_00_00_00_0) { - if (ecKey == null || ecKey.IsInvalid) - { - // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible. - return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters); - } + return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters); + } + using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey)) + { return ECOpenSsl.ExportExplicitParameters(ecKey, includePrivateParameters); } } @@ -216,15 +214,21 @@ public static ECParameters ExportExplicitParameters(SafeEvpPKeyHandle pkey, bool /// private static ECParameters ExportECParametersFromEvpPKeyUsingParams(SafeEvpPKeyHandle pkey, bool includePrivateParameters) { - string? curveName = Interop.Crypto.EvpPKeyGetCurveName(pkey); - if (curveName == null) + // Check encoding first — explicit-encoding keys must be exported with + // explicit curve parameters even if OpenSSL can match a named curve. + if (Interop.Crypto.EvpPKeyEcHasExplicitEncoding(pkey) == true) { return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters); } - else + + string? curveName = Interop.Crypto.EvpPKeyGetCurveName(pkey); + + if (curveName is null) { - return ExportNamedCurveParametersFromEvpPKeyUsingParams(pkey, curveName, includePrivateParameters); + return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters); } + + return ExportNamedCurveParametersFromEvpPKeyUsingParams(pkey, curveName, includePrivateParameters); } private static ECParameters ExportNamedCurveParametersFromEvpPKeyUsingParams(SafeEvpPKeyHandle pkey, string curveName, bool includePrivateParameters) diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs index 20fd8788b1053e..043bbd765586e8 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs @@ -117,6 +117,22 @@ private void FreeKey() internal static SafeEvpPKeyHandle GenerateECKey(int keySize) { + string oid = keySize switch + { + 256 => ECOpenSsl.ECDSA_P256_OID_VALUE, + 384 => ECOpenSsl.ECDSA_P384_OID_VALUE, + 521 => ECOpenSsl.ECDSA_P521_OID_VALUE, + _ => throw new InvalidOperationException(SR.Cryptography_InvalidKeySize), + }; + + SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyGenerateByEcKeyOid(oid); + + if (pkey is not null) + { + return pkey; + } + + // Fallback to legacy EC_KEY path SafeEvpPKeyHandle ret = ImportECKeyCore(new ECOpenSsl(keySize), out int createdKeySize); Debug.Assert(keySize == createdKeySize); return ret; @@ -124,11 +140,121 @@ internal static SafeEvpPKeyHandle GenerateECKey(int keySize) internal static SafeEvpPKeyHandle GenerateECKey(ECCurve curve, out int keySize) { - return ImportECKeyCore(new ECOpenSsl(curve), out keySize); + curve.Validate(); + + if (curve.IsNamed) + { + string oid = !string.IsNullOrEmpty(curve.Oid.Value) ? curve.Oid.Value : curve.Oid.FriendlyName!; + + SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyGenerateByEcKeyOid(oid); + + if (pkey is not null) + { + keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey); + + if (keySize != 0) + { + return pkey; + } + + pkey.Dispose(); + } + } + else if (curve.IsPrime || curve.IsCharacteristic2) + { + byte[] pField = curve.IsPrime ? curve.Prime! : curve.Polynomial!; + + // Pass null Q and null D to trigger key generation instead of import. + SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyCreateByEcExplicitParameters( + curve.CurveType, + null, 0, + null, 0, + null, 0, + pField, pField.Length, + curve.A!, curve.A!.Length, + curve.B!, curve.B!.Length, + curve.G.X!, curve.G.X!.Length, + curve.G.Y!, curve.G.Y!.Length, + curve.Order!, curve.Order!.Length, + curve.Cofactor!, curve.Cofactor!.Length, + curve.Seed, curve.Seed is null ? 0 : curve.Seed.Length); + + if (pkey is not null) + { + keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey); + + if (keySize != 0) + { + return pkey; + } + + pkey.Dispose(); + } + } + + // Fallback to legacy EC_KEY path (explicit curves or OpenSSL < 3.0) + return ImportECKeyCore(new ECOpenSsl(curve), out keySize); } internal static SafeEvpPKeyHandle ImportECKey(ECParameters parameters, out int keySize) { + parameters.Validate(); + + if (parameters.Curve.IsNamed) + { + string oid = !string.IsNullOrEmpty(parameters.Curve.Oid.Value) ? + parameters.Curve.Oid.Value : parameters.Curve.Oid.FriendlyName!; + + SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyCreateByEcKeyParameters( + oid, + parameters.Q.X, parameters.Q.X?.Length ?? 0, + parameters.Q.Y, parameters.Q.Y?.Length ?? 0, + parameters.D, parameters.D is null ? 0 : parameters.D.Length); + + if (pkey is not null) + { + keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey); + + if (keySize != 0) + { + return pkey; + } + + pkey.Dispose(); + } + } + else if (parameters.Curve.IsPrime || parameters.Curve.IsCharacteristic2) + { + byte[] pField = parameters.Curve.IsPrime ? parameters.Curve.Prime! : parameters.Curve.Polynomial!; + + SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyCreateByEcExplicitParameters( + parameters.Curve.CurveType, + parameters.Q.X, parameters.Q.X?.Length ?? 0, + parameters.Q.Y, parameters.Q.Y?.Length ?? 0, + parameters.D, parameters.D is null ? 0 : parameters.D.Length, + pField, pField.Length, + parameters.Curve.A!, parameters.Curve.A!.Length, + parameters.Curve.B!, parameters.Curve.B!.Length, + parameters.Curve.G.X!, parameters.Curve.G.X!.Length, + parameters.Curve.G.Y!, parameters.Curve.G.Y!.Length, + parameters.Curve.Order!, parameters.Curve.Order!.Length, + parameters.Curve.Cofactor!, parameters.Curve.Cofactor!.Length, + parameters.Curve.Seed, parameters.Curve.Seed is null ? 0 : parameters.Curve.Seed.Length); + + if (pkey is not null) + { + keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey); + + if (keySize != 0) + { + return pkey; + } + + pkey.Dispose(); + } + } + + // Fallback to legacy EC_KEY path return ImportECKeyCore(new ECOpenSsl(parameters), out keySize); } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs index 27bb25e7d6674e..ff4cb7cc792238 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs @@ -510,6 +510,72 @@ private static void VerifyExplicitCurve(ECParameters parameters, ECDiffieHellman ECParameters paramSecondExport = ec.ExportExplicitParameters(curveDef.IncludePrivate); AssertEqual(parameters, paramSecondExport); } + + [Theory] + [MemberData(nameof(NistEccCdhPrimeCurveVectors))] + public static void EcdhKeyAgreement_PrimeCurve_MatchesKnownSharedSecret( + string curveName, string curveOid, + string qCavsXHex, string qCavsYHex, + string dIutHex, string qIutXHex, string qIutYHex, + string expectedZHex) + { + _ = curveName; + ECCurve curve = ECCurve.CreateFromValue(curveOid); + + using (ECDiffieHellman iut = ECDiffieHellmanFactory.Create()) + using (ECDiffieHellman cavs = ECDiffieHellmanFactory.Create()) + { + iut.ImportParameters(new ECParameters + { + Curve = curve, + D = dIutHex.HexToByteArray(), + Q = new ECPoint + { + X = qIutXHex.HexToByteArray(), + Y = qIutYHex.HexToByteArray(), + }, + }); + + cavs.ImportParameters(new ECParameters + { + Curve = curve, + Q = new ECPoint + { + X = qCavsXHex.HexToByteArray(), + Y = qCavsYHex.HexToByteArray(), + }, + }); + + byte[] derivedZ = iut.DeriveRawSecretAgreement(cavs.PublicKey); + Assert.Equal(expectedZHex.HexToByteArray(), derivedZ); + } + } + + // NIST SP 800-56A ECCCDH vectors (CAVS 14.1), P-256 + // Source: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program + public static TheoryData NistEccCdhPrimeCurveVectors => new() + { + // P-256, COUNT=0 + { + "P-256", "1.2.840.10045.3.1.7", + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287", + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", + "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", + "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230", + "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141", + "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b" + }, + // P-256, COUNT=1 + { + "P-256", "1.2.840.10045.3.1.7", + "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae", + "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", + "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5", + "119f2f047902782ab0c9e27a54aff5eb9b964829ca99c06b02ddba95b0a3f6d0", + "8f52b726664cac366fc98ac7a012b2682cbd962e5acb544671d41b9445704d1d", + "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67" + }, + }; } #endif } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs index baf4d7580fd049..08fe32b17260a6 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs @@ -354,6 +354,90 @@ public static void ImportFromPrivateOnlyKey() } } + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Named_P256() + { + VerifyPrivateKeyDerivesPublicKey(EccTestData.GetNistP256ReferenceKey(), explicitCurve: false); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Named_P521_DiminishedCoords() + { + VerifyPrivateKeyDerivesPublicKey(EccTestData.GetNistP521DiminishedCoordsParameters(), explicitCurve: false); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Named_Sect163k1() + { + if (!ECDsaFactory.IsCurveValid(EccTestData.Sect163k1Key1.Curve.Oid)) + return; + + VerifyPrivateKeyDerivesPublicKey(EccTestData.Sect163k1Key1, explicitCurve: false); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Named_C2pnb163v1() + { + if (!ECDsaFactory.IsCurveValid(EccTestData.C2pnb163v1Key1.Curve.Oid)) + return; + + VerifyPrivateKeyDerivesPublicKey(EccTestData.C2pnb163v1Key1, explicitCurve: false); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Explicit_P256() + { + VerifyPrivateKeyDerivesPublicKey(EccTestData.GetNistP256ReferenceKeyExplicit(), explicitCurve: true); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Explicit_P521_DiminishedCoords() + { + ECParameters p521 = EccTestData.GetNistP521DiminishedCoordsParameters(); + p521.Curve = EccTestData.GetNistP521ExplicitCurve(); + VerifyPrivateKeyDerivesPublicKey(p521, explicitCurve: true); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Explicit_Sect163k1() + { + if (!ECDsaFactory.IsCurveValid(EccTestData.Sect163k1Key1.Curve.Oid)) + return; + + VerifyPrivateKeyDerivesPublicKey(EccTestData.Sect163k1Key1Explicit, explicitCurve: true); + } + + [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))] + public static void DerivePublicKey_Explicit_C2pnb163v1() + { + if (!ECDsaFactory.IsCurveValid(EccTestData.C2pnb163v1Key1.Curve.Oid)) + return; + + VerifyPrivateKeyDerivesPublicKey(EccTestData.C2pnb163v1Key1Explicit, explicitCurve: true); + } + + private static void VerifyPrivateKeyDerivesPublicKey(ECParameters knownKey, bool explicitCurve) + { + ECParameters importParams = new ECParameters + { + Curve = knownKey.Curve, + D = knownKey.D, + Q = default, + }; + + using (ECDsa ecdsa = ECDsaFactory.Create()) + { + ecdsa.ImportParameters(importParams); + + ECParameters exported = explicitCurve + ? ecdsa.ExportExplicitParameters(true) + : ecdsa.ExportParameters(true); + + Assert.Equal(knownKey.Q.X, exported.Q.X); + Assert.Equal(knownKey.Q.Y, exported.Q.Y); + } + } + [Theory] [MemberData(nameof(NamedCurves))] public static void OidPresentOnCurveMiscased(ECCurve curve) diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs index 11ea3645092058..4e4f5ed3f62cd9 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs @@ -129,6 +129,32 @@ public void CtorHandleDuplicate() } } + [Theory] + [InlineData(ECDSA_P256_OID_VALUE, 256)] + [InlineData(ECDSA_P384_OID_VALUE, 384)] + [InlineData(ECDSA_P521_OID_VALUE, 521)] + public void CtorEvpPKeyHandle(string oid, int expectedKeySize) + { + int rc = Interop.Crypto.EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, oid); + + if (!PlatformDetection.IsOpenSsl3) + { + Assert.Equal(0, rc); + pkey.Dispose(); + return; + } + + Assert.Equal(1, rc); + Assert.False(pkey.IsInvalid); + + using (pkey) + using (ECDiffieHellmanOpenSsl e = new ECDiffieHellmanOpenSsl(pkey)) + { + Assert.Equal(expectedKeySize, e.KeySize); + e.Exercise(); + } + } + [Fact] public void KeySizePropWithExercise() { @@ -278,5 +304,128 @@ public void LookupCurveByOidFriendlyName() Assert.Equal("ECDSA_P521", param.Curve.Oid.FriendlyName); // OpenSsl maps secp521r1 to ECDSA_P521 Assert.Equal(ECDSA_P521_OID_VALUE, param.Curve.Oid.Value); } + + [Theory] + [InlineData(ECDSA_P256_OID_VALUE, 256)] + [InlineData(ECDSA_P384_OID_VALUE, 384)] + [InlineData(ECDSA_P521_OID_VALUE, 521)] + public void EcKeyAndEvpPKeyProduceSameExport(string oid, int expectedKeySize) + { + IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey)); + + using (ECDiffieHellmanOpenSsl ecKeyBacked = new ECDiffieHellmanOpenSsl(ecKey)) + { + Assert.Equal(expectedKeySize, ecKeyBacked.KeySize); + + ECParameters privateParams = ecKeyBacked.ExportParameters(true); + + using (ECDiffieHellman evpBacked = ECDiffieHellman.Create(privateParams)) + { + Assert.Equal(expectedKeySize, evpBacked.KeySize); + + ECParameters evpPrivateParams = evpBacked.ExportParameters(true); + + ComparePublicKey(privateParams.Q, evpPrivateParams.Q); + ComparePrivateKey(privateParams, evpPrivateParams); + } + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Theory] + [InlineData(ECDSA_P256_OID_VALUE)] + [InlineData(ECDSA_P384_OID_VALUE)] + [InlineData(ECDSA_P521_OID_VALUE)] + public void EcKeyAndEvpPKeyDeriveCrossCompatible(string oid) + { + IntPtr rawKey1 = Interop.Crypto.EcKeyCreateByOid(oid); + Assert.NotEqual(IntPtr.Zero, rawKey1); + IntPtr rawKey2 = Interop.Crypto.EcKeyCreateByOid(oid); + Assert.NotEqual(IntPtr.Zero, rawKey2); + + try + { + Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(rawKey1)); + Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(rawKey2)); + + using (ECDiffieHellmanOpenSsl ecKeyBacked1 = new ECDiffieHellmanOpenSsl(rawKey1)) + using (ECDiffieHellmanOpenSsl ecKeyBacked2 = new ECDiffieHellmanOpenSsl(rawKey2)) + using (ECDiffieHellman evpBacked1 = ECDiffieHellman.Create(ecKeyBacked1.ExportParameters(true))) + using (ECDiffieHellman evpBacked2 = ECDiffieHellman.Create(ecKeyBacked2.ExportParameters(true))) + using (ECDiffieHellmanPublicKey ecPub2 = ecKeyBacked2.PublicKey) + using (ECDiffieHellmanPublicKey evpPub2 = evpBacked2.PublicKey) + { + byte[] ecEc = ecKeyBacked1.DeriveKeyFromHash(ecPub2, HashAlgorithmName.SHA256); + byte[] ecEvp = ecKeyBacked1.DeriveKeyFromHash(evpPub2, HashAlgorithmName.SHA256); + byte[] evpEc = evpBacked1.DeriveKeyFromHash(ecPub2, HashAlgorithmName.SHA256); + byte[] evpEvp = evpBacked1.DeriveKeyFromHash(evpPub2, HashAlgorithmName.SHA256); + + Assert.Equal(ecEc, ecEvp); + Assert.Equal(ecEc, evpEc); + Assert.Equal(ecEc, evpEvp); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(rawKey1); + Interop.Crypto.EcKeyDestroy(rawKey2); + } + } + + [Fact] + public void ExplicitCurveEcKeyAndEvpPKeyProduceSameExport() + { + ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve(); + + using (ECDiffieHellman ecKeyBacked = ECDiffieHellman.Create(explicitCurve)) + { + ECParameters explicitParams = ecKeyBacked.ExportExplicitParameters(true); + + using (ECDiffieHellman evpBacked = ECDiffieHellman.Create(explicitParams)) + { + ECParameters evpExplicitParams = evpBacked.ExportExplicitParameters(true); + + ComparePublicKey(explicitParams.Q, evpExplicitParams.Q); + ComparePrivateKey(explicitParams, evpExplicitParams); + } + } + } + + [Fact] + public void ExplicitCurveEcKeyAndEvpPKeyDeriveCrossCompatible() + { + ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve(); + + using (ECDiffieHellman key1 = ECDiffieHellman.Create(explicitCurve)) + using (ECDiffieHellman key2 = ECDiffieHellman.Create(explicitCurve)) + { + ECParameters key1Params = key1.ExportExplicitParameters(true); + ECParameters key2Params = key2.ExportExplicitParameters(true); + + using (ECDiffieHellman key1Reimported = ECDiffieHellman.Create(key1Params)) + using (ECDiffieHellman key2Reimported = ECDiffieHellman.Create(key2Params)) + using (ECDiffieHellmanPublicKey pub2 = key2.PublicKey) + using (ECDiffieHellmanPublicKey pub2Reimported = key2Reimported.PublicKey) + { + byte[] derive1 = key1.DeriveKeyFromHash(pub2, HashAlgorithmName.SHA256); + byte[] derive2 = key1.DeriveKeyFromHash(pub2Reimported, HashAlgorithmName.SHA256); + byte[] derive3 = key1Reimported.DeriveKeyFromHash(pub2, HashAlgorithmName.SHA256); + byte[] derive4 = key1Reimported.DeriveKeyFromHash(pub2Reimported, HashAlgorithmName.SHA256); + + Assert.Equal(derive1, derive2); + Assert.Equal(derive1, derive3); + Assert.Equal(derive1, derive4); + } + } + } } } diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs index 6f1139a8630629..da87d0cef76de8 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs @@ -131,6 +131,32 @@ public void CtorHandleDuplicate() } } + [Theory] + [InlineData(ECDSA_P256_OID_VALUE, 256)] + [InlineData(ECDSA_P384_OID_VALUE, 384)] + [InlineData(ECDSA_P521_OID_VALUE, 521)] + public void CtorEvpPKeyHandle(string oid, int expectedKeySize) + { + int rc = Interop.Crypto.EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, oid); + + if (!PlatformDetection.IsOpenSsl3) + { + Assert.Equal(0, rc); + pkey.Dispose(); + return; + } + + Assert.Equal(1, rc); + Assert.False(pkey.IsInvalid); + + using (pkey) + using (ECDsaOpenSsl e = new ECDsaOpenSsl(pkey)) + { + Assert.Equal(expectedKeySize, e.KeySize); + e.Exercise(); + } + } + [Fact] public void KeySizePropWithExercise() { @@ -297,6 +323,113 @@ public void LookupCurveByOidFriendlyName() Assert.Equal("ECDSA_P521", param.Curve.Oid.FriendlyName); // OpenSsl maps secp521r1 to ECDSA_P521 Assert.Equal(ECDSA_P521_OID_VALUE, param.Curve.Oid.Value); } + + [Theory] + [InlineData(ECDSA_P256_OID_VALUE, 256)] + [InlineData(ECDSA_P384_OID_VALUE, 384)] + [InlineData(ECDSA_P521_OID_VALUE, 521)] + public void EcKeyAndEvpPKeyProduceSameExport(string oid, int expectedKeySize) + { + IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey)); + + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + { + Assert.Equal(expectedKeySize, ecKeyBacked.KeySize); + + ECParameters privateParams = ecKeyBacked.ExportParameters(true); + + using (ECDsa evpBacked = ECDsa.Create(privateParams)) + { + Assert.Equal(expectedKeySize, evpBacked.KeySize); + + ECParameters evpPrivateParams = evpBacked.ExportParameters(true); + + ComparePublicKey(privateParams.Q, evpPrivateParams.Q); + ComparePrivateKey(privateParams, evpPrivateParams); + } + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Theory] + [InlineData(ECDSA_P256_OID_VALUE)] + [InlineData(ECDSA_P384_OID_VALUE)] + [InlineData(ECDSA_P521_OID_VALUE)] + public void EcKeyAndEvpPKeySignVerifyCrossCompatible(string oid) + { + byte[] data = ByteUtils.RepeatByte(0x42, 64); + + IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey)); + + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + using (ECDsa evpBacked = ECDsa.Create(ecKeyBacked.ExportParameters(true))) + { + byte[] sig1 = ecKeyBacked.SignData(data, HashAlgorithmName.SHA256); + Assert.True(evpBacked.VerifyData(data, sig1, HashAlgorithmName.SHA256)); + + byte[] sig2 = evpBacked.SignData(data, HashAlgorithmName.SHA256); + Assert.True(ecKeyBacked.VerifyData(data, sig2, HashAlgorithmName.SHA256)); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void ExplicitCurveEcKeyAndEvpPKeyProduceSameExport() + { + ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve(); + + using (ECDsa ecKeyBacked = ECDsa.Create(explicitCurve)) + { + ECParameters explicitParams = ecKeyBacked.ExportExplicitParameters(true); + + using (ECDsa evpBacked = ECDsa.Create(explicitParams)) + { + ECParameters evpExplicitParams = evpBacked.ExportExplicitParameters(true); + + ComparePublicKey(explicitParams.Q, evpExplicitParams.Q); + ComparePrivateKey(explicitParams, evpExplicitParams); + } + } + } + + [Fact] + public void ExplicitCurveEcKeyAndEvpPKeySignVerifyCrossCompatible() + { + byte[] data = ByteUtils.RepeatByte(0x42, 64); + ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve(); + + using (ECDsa key1 = ECDsa.Create(explicitCurve)) + { + ECParameters explicitParams = key1.ExportExplicitParameters(true); + + using (ECDsa key2 = ECDsa.Create(explicitParams)) + { + byte[] sig1 = key1.SignData(data, HashAlgorithmName.SHA256); + Assert.True(key2.VerifyData(data, sig1, HashAlgorithmName.SHA256)); + + byte[] sig2 = key2.SignData(data, HashAlgorithmName.SHA256); + Assert.True(key1.VerifyData(data, sig2, HashAlgorithmName.SHA256)); + } + } + } } } @@ -315,5 +448,8 @@ internal static partial class Crypto [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_OpenSslVersionNumber")] internal static extern uint OpenSslVersionNumber(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcKeyOid", CharSet = CharSet.Ansi)] + internal static extern int EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, string oid); } } diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index 87a567832bfad0..e8c70a778fe21a 100644 --- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c @@ -190,6 +190,11 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_EvpPKeyGetEcKeyParameters) DllImportEntry(CryptoNative_EvpPKeyGetEcGroupNid) DllImportEntry(CryptoNative_EvpPKeyGetEcCurveParameters) + DllImportEntry(CryptoNative_EvpPKeyCreateByEcKeyParameters) + DllImportEntry(CryptoNative_EvpPKeyCreateByEcExplicitParameters) + DllImportEntry(CryptoNative_EvpPKeyGenerateByEcKeyOid) + DllImportEntry(CryptoNative_EvpPKeyEcHasExplicitEncoding) + DllImportEntry(CryptoNative_EvpPKeyGetEcFieldDegree) DllImportEntry(CryptoNative_EvpRC2Cbc) DllImportEntry(CryptoNative_EvpRC2Ecb) DllImportEntry(CryptoNative_EvpSha1) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index c07abd0cca1eb2..1d16e9c7380f36 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #endif @@ -384,6 +385,7 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(EC_GROUP_get0_seed) \ REQUIRED_FUNCTION(EC_GROUP_get_cofactor) \ REQUIRED_FUNCTION(EC_GROUP_get_curve_GFp) \ + REQUIRED_FUNCTION(EC_GROUP_get_curve) \ REQUIRED_FUNCTION(EC_GROUP_get_curve_name) \ REQUIRED_FUNCTION(EC_GROUP_get_degree) \ REQUIRED_FUNCTION(EC_GROUP_get_order) \ @@ -391,8 +393,10 @@ extern bool g_libSslUses32BitTime; LIGHTUP_FUNCTION(EC_GROUP_get_field_type) \ REQUIRED_FUNCTION(EC_GROUP_method_of) \ REQUIRED_FUNCTION(EC_GROUP_new) \ - LIGHTUP_FUNCTION(EC_GROUP_new_by_curve_name) \ + REQUIRED_FUNCTION(EC_GROUP_new_by_curve_name) \ + REQUIRED_FUNCTION(EC_GROUP_new_curve_GFp) \ REQUIRED_FUNCTION(EC_GROUP_set_curve_GFp) \ + REQUIRED_FUNCTION(EC_GROUP_set_curve) \ REQUIRED_FUNCTION(EC_GROUP_set_generator) \ REQUIRED_FUNCTION(EC_GROUP_set_seed) \ REQUIRED_FUNCTION(EC_KEY_check_key) \ @@ -411,10 +415,13 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(EC_METHOD_get_field_type) \ REQUIRED_FUNCTION(EC_POINT_free) \ REQUIRED_FUNCTION(EC_POINT_get_affine_coordinates_GFp) \ + REQUIRED_FUNCTION(EC_POINT_get_affine_coordinates) \ REQUIRED_FUNCTION(EC_POINT_mul) \ REQUIRED_FUNCTION(EC_POINT_new) \ + REQUIRED_FUNCTION(EC_POINT_point2oct) \ REQUIRED_FUNCTION(EC_POINT_set_affine_coordinates_GFp) \ - LIGHTUP_FUNCTION(EC_POINT_oct2point) \ + REQUIRED_FUNCTION(EC_POINT_set_affine_coordinates) \ + REQUIRED_FUNCTION(EC_POINT_oct2point) \ LIGHTUP_FUNCTION(ENGINE_by_id) \ LIGHTUP_FUNCTION(ENGINE_finish) \ LIGHTUP_FUNCTION(ENGINE_free) \ @@ -515,8 +522,7 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \ LIGHTUP_FUNCTION(EVP_PKEY_CTX_new_from_name) \ LIGHTUP_FUNCTION(EVP_PKEY_CTX_new_from_pkey) \ - REQUIRED_FUNCTION(EVP_PKEY_new_raw_private_key) \ - REQUIRED_FUNCTION(EVP_PKEY_new_raw_public_key) \ + LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_group_name) \ LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_params) \ FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_keygen_bits) \ FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_oaep_md) \ @@ -545,11 +551,14 @@ extern bool g_libSslUses32BitTime; LIGHTUP_FUNCTION(EVP_PKEY_get0_RSA) \ LIGHTUP_FUNCTION(EVP_PKEY_get0_type_name) \ REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \ + LIGHTUP_FUNCTION(EVP_PKEY_generate) \ REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \ LIGHTUP_FUNCTION(EVP_PKEY_is_a) \ REQUIRED_FUNCTION(EVP_PKEY_keygen) \ REQUIRED_FUNCTION(EVP_PKEY_keygen_init) \ REQUIRED_FUNCTION(EVP_PKEY_new) \ + REQUIRED_FUNCTION(EVP_PKEY_new_raw_private_key) \ + REQUIRED_FUNCTION(EVP_PKEY_new_raw_public_key) \ REQUIRED_FUNCTION(EVP_PKEY_public_check) \ REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \ REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \ @@ -644,6 +653,13 @@ extern bool g_libSslUses32BitTime; LIGHTUP_FUNCTION(OSSL_PARAM_construct_int) \ LIGHTUP_FUNCTION(OSSL_PARAM_construct_int32) \ LIGHTUP_FUNCTION(OSSL_PARAM_construct_end) \ + LIGHTUP_FUNCTION(OSSL_PARAM_BLD_new) \ + LIGHTUP_FUNCTION(OSSL_PARAM_BLD_free) \ + LIGHTUP_FUNCTION(OSSL_PARAM_BLD_push_utf8_string) \ + LIGHTUP_FUNCTION(OSSL_PARAM_BLD_push_octet_string) \ + LIGHTUP_FUNCTION(OSSL_PARAM_BLD_push_BN) \ + LIGHTUP_FUNCTION(OSSL_PARAM_BLD_to_param) \ + LIGHTUP_FUNCTION(OSSL_PARAM_free) \ REQUIRED_FUNCTION(PKCS8_PRIV_KEY_INFO_free) \ REQUIRED_FUNCTION(PEM_read_bio_PKCS7) \ REQUIRED_FUNCTION(PEM_read_bio_X509) \ @@ -837,6 +853,7 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(X509_VERIFY_PARAM_set_time) \ LIGHTUP_FUNCTION(EC_GF2m_simple_method) \ LIGHTUP_FUNCTION(EC_GROUP_get_curve_GF2m) \ + LIGHTUP_FUNCTION(EC_GROUP_new_curve_GF2m) \ LIGHTUP_FUNCTION(EC_GROUP_set_curve_GF2m) \ LIGHTUP_FUNCTION(EC_POINT_get_affine_coordinates_GF2m) \ LIGHTUP_FUNCTION(EC_POINT_set_affine_coordinates_GF2m) \ @@ -947,6 +964,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EC_GROUP_get0_seed EC_GROUP_get0_seed_ptr #define EC_GROUP_get_cofactor EC_GROUP_get_cofactor_ptr #define EC_GROUP_get_curve_GFp EC_GROUP_get_curve_GFp_ptr +#define EC_GROUP_get_curve EC_GROUP_get_curve_ptr #define EC_GROUP_get_curve_name EC_GROUP_get_curve_name_ptr #define EC_GROUP_get_degree EC_GROUP_get_degree_ptr #define EC_GROUP_get_order EC_GROUP_get_order_ptr @@ -955,7 +973,9 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EC_GROUP_method_of EC_GROUP_method_of_ptr #define EC_GROUP_new EC_GROUP_new_ptr #define EC_GROUP_new_by_curve_name EC_GROUP_new_by_curve_name_ptr +#define EC_GROUP_new_curve_GFp EC_GROUP_new_curve_GFp_ptr #define EC_GROUP_set_curve_GFp EC_GROUP_set_curve_GFp_ptr +#define EC_GROUP_set_curve EC_GROUP_set_curve_ptr #define EC_GROUP_set_generator EC_GROUP_set_generator_ptr #define EC_GROUP_set_seed EC_GROUP_set_seed_ptr #define EC_KEY_check_key EC_KEY_check_key_ptr @@ -974,9 +994,12 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EC_METHOD_get_field_type EC_METHOD_get_field_type_ptr #define EC_POINT_free EC_POINT_free_ptr #define EC_POINT_get_affine_coordinates_GFp EC_POINT_get_affine_coordinates_GFp_ptr +#define EC_POINT_get_affine_coordinates EC_POINT_get_affine_coordinates_ptr #define EC_POINT_mul EC_POINT_mul_ptr #define EC_POINT_new EC_POINT_new_ptr +#define EC_POINT_point2oct EC_POINT_point2oct_ptr #define EC_POINT_set_affine_coordinates_GFp EC_POINT_set_affine_coordinates_GFp_ptr +#define EC_POINT_set_affine_coordinates EC_POINT_set_affine_coordinates_ptr #define EC_POINT_oct2point EC_POINT_oct2point_ptr #define ENGINE_by_id ENGINE_by_id_ptr #define ENGINE_finish ENGINE_finish_ptr @@ -1076,6 +1099,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr #define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr +#define EVP_PKEY_CTX_set_group_name EVP_PKEY_CTX_set_group_name_ptr #define EVP_PKEY_CTX_set_params EVP_PKEY_CTX_set_params_ptr #define EVP_PKEY_CTX_set_rsa_keygen_bits EVP_PKEY_CTX_set_rsa_keygen_bits_ptr #define EVP_PKEY_CTX_set_rsa_oaep_md EVP_PKEY_CTX_set_rsa_oaep_md_ptr @@ -1104,6 +1128,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr #define EVP_PKEY_get0_type_name EVP_PKEY_get0_type_name_ptr #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr +#define EVP_PKEY_generate EVP_PKEY_generate_ptr #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr #define EVP_PKEY_is_a EVP_PKEY_is_a_ptr #define EVP_PKEY_keygen EVP_PKEY_keygen_ptr @@ -1210,6 +1235,13 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define OSSL_PARAM_construct_int OSSL_PARAM_construct_int_ptr #define OSSL_PARAM_construct_int32 OSSL_PARAM_construct_int32_ptr #define OSSL_PARAM_construct_end OSSL_PARAM_construct_end_ptr +#define OSSL_PARAM_BLD_new OSSL_PARAM_BLD_new_ptr +#define OSSL_PARAM_BLD_free OSSL_PARAM_BLD_free_ptr +#define OSSL_PARAM_BLD_push_utf8_string OSSL_PARAM_BLD_push_utf8_string_ptr +#define OSSL_PARAM_BLD_push_octet_string OSSL_PARAM_BLD_push_octet_string_ptr +#define OSSL_PARAM_BLD_push_BN OSSL_PARAM_BLD_push_BN_ptr +#define OSSL_PARAM_BLD_to_param OSSL_PARAM_BLD_to_param_ptr +#define OSSL_PARAM_free OSSL_PARAM_free_ptr #define PKCS8_PRIV_KEY_INFO_free PKCS8_PRIV_KEY_INFO_free_ptr #define PEM_read_bio_PKCS7 PEM_read_bio_PKCS7_ptr #define PEM_read_bio_X509 PEM_read_bio_X509_ptr @@ -1405,6 +1437,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define X509_VERIFY_PARAM_set_time X509_VERIFY_PARAM_set_time_ptr #define EC_GF2m_simple_method EC_GF2m_simple_method_ptr #define EC_GROUP_get_curve_GF2m EC_GROUP_get_curve_GF2m_ptr +#define EC_GROUP_new_curve_GF2m EC_GROUP_new_curve_GF2m_ptr #define EC_GROUP_set_curve_GF2m EC_GROUP_set_curve_GF2m_ptr #define EC_POINT_get_affine_coordinates_GF2m EC_POINT_get_affine_coordinates_GF2m_ptr #define EC_POINT_set_affine_coordinates_GF2m EC_POINT_set_affine_coordinates_GF2m_ptr diff --git a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h index ec8d0c43a6f314..ab0a64445f5652 100644 --- a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h +++ b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h @@ -25,17 +25,21 @@ #define OSSL_MAC_PARAM_XOF "xof" #define OSSL_MAC_PARAM_SIZE "size" -#define OSSL_PKEY_PARAM_GROUP_NAME "group" -#define OSSL_PKEY_PARAM_PRIV_KEY "priv" -#define OSSL_PKEY_PARAM_EC_PUB_X "qx" -#define OSSL_PKEY_PARAM_EC_PUB_Y "qy" -#define OSSL_PKEY_PARAM_EC_P "p" -#define OSSL_PKEY_PARAM_EC_A "a" -#define OSSL_PKEY_PARAM_EC_B "b" -#define OSSL_PKEY_PARAM_EC_GENERATOR "generator" -#define OSSL_PKEY_PARAM_EC_ORDER "order" -#define OSSL_PKEY_PARAM_EC_COFACTOR "cofactor" -#define OSSL_PKEY_PARAM_EC_SEED "seed" +#define OSSL_PKEY_PARAM_GROUP_NAME "group" +#define OSSL_PKEY_PARAM_PRIV_KEY "priv" +#define OSSL_PKEY_PARAM_EC_PUB_X "qx" +#define OSSL_PKEY_PARAM_EC_PUB_Y "qy" +#define OSSL_PKEY_PARAM_EC_P "p" +#define OSSL_PKEY_PARAM_EC_A "a" +#define OSSL_PKEY_PARAM_EC_B "b" +#define OSSL_PKEY_PARAM_EC_GENERATOR "generator" +#define OSSL_PKEY_PARAM_EC_ORDER "order" +#define OSSL_PKEY_PARAM_EC_COFACTOR "cofactor" +#define OSSL_PKEY_PARAM_EC_SEED "seed" +#define OSSL_PKEY_PARAM_EC_ENCODING "encoding" +#define OSSL_PKEY_PARAM_EC_FIELD_TYPE "field-type" + +#define EVP_PKEY_KEY_PARAMETERS 4 #define OSSL_PKEY_PARAM_RSA_N "n" #define OSSL_PKEY_PARAM_RSA_E "e" @@ -52,6 +56,7 @@ typedef struct ossl_lib_ctx_st OSSL_LIB_CTX; typedef struct ossl_param_st OSSL_PARAM; +typedef struct ossl_param_bld_st OSSL_PARAM_BLD; typedef struct ossl_provider_st OSSL_PROVIDER; typedef struct ossl_store_ctx_st OSSL_STORE_CTX; typedef struct ossl_store_info_st OSSL_STORE_INFO; @@ -98,6 +103,7 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_name(OSSL_LIB_CTX *libctx, const char *name, EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_pkey(OSSL_LIB_CTX *libctx, EVP_PKEY *pkey, const char *propquery); int EVP_PKEY_CTX_set_params(EVP_PKEY_CTX *ctx, const OSSL_PARAM *params); int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits); +int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name); int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md); int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode); int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen); @@ -119,6 +125,7 @@ int EVP_PKEY_fromdata(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey, int selection, OSSL_PARAM params[]); +int EVP_PKEY_generate(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey); int EVP_PKEY_get_base_id(const EVP_PKEY* pkey); int EVP_PKEY_get_bits(const EVP_PKEY* pkey); int EVP_PKEY_get_bn_param(const EVP_PKEY *pkey, const char *key_name, BIGNUM **bn); @@ -135,6 +142,14 @@ OSSL_PARAM OSSL_PARAM_construct_int32(const char *key, int32_t *buf); OSSL_PARAM OSSL_PARAM_construct_octet_string(const char *key, void *buf, size_t bsize); OSSL_PARAM OSSL_PARAM_construct_utf8_string(const char *key, char *buf, size_t bsize); +OSSL_PARAM_BLD *OSSL_PARAM_BLD_new(void); +void OSSL_PARAM_BLD_free(OSSL_PARAM_BLD *bld); +int OSSL_PARAM_BLD_push_utf8_string(OSSL_PARAM_BLD *bld, const char *key, const char *buf, size_t bsize); +int OSSL_PARAM_BLD_push_octet_string(OSSL_PARAM_BLD *bld, const char *key, const void *buf, size_t bsize); +int OSSL_PARAM_BLD_push_BN(OSSL_PARAM_BLD *bld, const char *key, const BIGNUM *bn); +OSSL_PARAM *OSSL_PARAM_BLD_to_param(OSSL_PARAM_BLD *bld); +void OSSL_PARAM_free(OSSL_PARAM *params); + void OSSL_LIB_CTX_free(OSSL_LIB_CTX*); OSSL_LIB_CTX* OSSL_LIB_CTX_new(void); OSSL_PROVIDER* OSSL_PROVIDER_load(OSSL_LIB_CTX*, const char* name); diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c index 2dc23d98036760..0b7c9ee598bab0 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c @@ -4,6 +4,57 @@ #include "pal_ecc_import_export.h" #include "pal_utilities.h" +#ifdef NEED_OPENSSL_3_0 + +// Encode two coordinates as an uncompressed EC point: 0x04 || x || y. +// Coordinates are zero-padded on the left to the larger of the two lengths. +// Returns an OPENSSL_zalloc'd buffer (caller must OPENSSL_free), or NULL on failure. +static uint8_t* EncodeEcPointFromCoordinates( + const uint8_t* x, int32_t xLength, + const uint8_t* y, int32_t yLength, + int32_t fieldSize, + int32_t* outLength) +{ + int32_t coordLen = fieldSize; + if (xLength > coordLen || yLength > coordLen) + return NULL; + + int32_t len = 1 + 2 * coordLen; + uint8_t* buf = (uint8_t*)OPENSSL_zalloc((size_t)len); + if (buf == NULL) + return NULL; + + buf[0] = 0x04; + memcpy(buf + 1 + (coordLen - xLength), x, (size_t)xLength); + memcpy(buf + 1 + coordLen + (coordLen - yLength), y, (size_t)yLength); + *outLength = len; + + return buf; +} + +// Serialize an EC_POINT to uncompressed octet format. +// Returns an OPENSSL_zalloc'd buffer (caller must OPENSSL_free), or NULL on failure. +static uint8_t* EncodeEcPointFromPoint(const EC_GROUP* group, const EC_POINT* point, size_t* outLength) +{ + size_t len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + if (len == 0) + return NULL; + + uint8_t* buf = (uint8_t*)OPENSSL_zalloc(len); + if (buf == NULL) + return NULL; + + if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, buf, len, NULL) != len) + { + OPENSSL_free(buf); + return NULL; + } + + *outLength = len; + return buf; +} +#endif + static ECCurveType MethodToCurveType(const EC_METHOD* method) { if (method == EC_GFp_mont_method()) @@ -49,26 +100,9 @@ static ECCurveType EcKeyGetCurveType( return MethodToCurveType(method); } -static int EcPointGetAffineCoordinates(const EC_GROUP *group, ECCurveType curveType, const EC_POINT *p, BIGNUM *x, BIGNUM *y) +static int EcPointGetAffineCoordinates(const EC_GROUP *group, const EC_POINT *p, BIGNUM *x, BIGNUM *y) { -#if HAVE_OPENSSL_EC2M - if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (curveType == Characteristic2)) - { - if (!EC_POINT_get_affine_coordinates_GF2m(group, p, x, y, NULL)) - return 0; - } - else -#endif - { - if (!EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL)) - return 0; - } - -#if !HAVE_OPENSSL_EC2M - (void)curveType; -#endif - - return 1; + return EC_POINT_get_affine_coordinates(group, p, x, y, NULL) ? 1 : 0; } int32_t CryptoNative_GetECKeyParameters( @@ -104,7 +138,7 @@ int32_t CryptoNative_GetECKeyParameters( if (!xBn || !yBn) goto error; - if (!EcPointGetAffineCoordinates(group, curveType, Q, xBn, yBn)) + if (!EcPointGetAffineCoordinates(group, Q, xBn, yBn)) goto error; // Success; assign variables @@ -225,35 +259,13 @@ int32_t CryptoNative_GetECCurveParameters( goto error; // Extract p, a, b -#if HAVE_OPENSSL_EC2M - if (API_EXISTS(EC_GROUP_get_curve_GF2m) && (*curveType == Characteristic2)) - { - // pBn represents the binary polynomial - if (!EC_GROUP_get_curve_GF2m(group, pBn, aBn, bBn, NULL)) - goto error; - } - else -#endif - { - // pBn represents the prime - if (!EC_GROUP_get_curve_GFp(group, pBn, aBn, bBn, NULL)) - goto error; - } + if (!EC_GROUP_get_curve(group, pBn, aBn, bBn, NULL)) + goto error; // Extract gx and gy G = EC_GROUP_get0_generator(group); -#if HAVE_OPENSSL_EC2M - if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (*curveType == Characteristic2)) - { - if (!EC_POINT_get_affine_coordinates_GF2m(group, G, xBn, yBn, NULL)) - goto error; - } - else -#endif - { - if (!EC_POINT_get_affine_coordinates_GFp(group, G, xBn, yBn, NULL)) - goto error; - } + if (!EcPointGetAffineCoordinates(group, G, xBn, yBn)) + goto error; // Extract order (n) if (!EC_GROUP_get_order(group, orderBn, NULL)) @@ -458,6 +470,83 @@ int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName #endif } +int32_t CryptoNative_EvpPKeyEcHasExplicitEncoding(const EVP_PKEY* pkey) +{ + if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC) + return -1; + +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EVP_PKEY_get_utf8_string_param)) + { + return -1; + } +#endif + +#ifdef NEED_OPENSSL_3_0 + char encoding[32] = {0}; + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_ENCODING, encoding, sizeof(encoding), NULL)) + return 0; + + return (strcmp(encoding, "explicit") == 0) ? 1 : 0; +#else + return -1; +#endif +} + +int32_t CryptoNative_EvpPKeyGetEcFieldDegree(const EVP_PKEY* pkey) +{ + if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC) + return 0; + +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EVP_PKEY_get_bn_param) || !API_EXISTS(EVP_PKEY_get_utf8_string_param)) + { + return 0; + } +#endif + +#ifdef NEED_OPENSSL_3_0 + // Determine whether this is a binary field (GF(2^m)) for the degree adjustment. + // Some providers (e.g. TPM2) don't expose OSSL_PKEY_PARAM_EC_FIELD_TYPE, + // so try EC_GROUP from the curve name first, then fall back to the param. + int isChar2 = 0; + + int nid = 0; + if (CryptoNative_EvpPKeyGetEcGroupNid(pkey, &nid) && nid != NID_undef) + { + EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); + if (group) + { + isChar2 = (EC_GROUP_get_field_type(group) == NID_X9_62_characteristic_two_field); + EC_GROUP_free(group); + } + } + else + { + char fieldType[32] = {0}; + if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, sizeof(fieldType), NULL)) + { + isChar2 = (strcmp(fieldType, SN_X9_62_characteristic_two_field) == 0); + } + } + + BIGNUM* p = NULL; + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_P, &p) || !p) + return 0; + + // For GF(2^m): p is the irreducible polynomial, degree = BN_num_bits(p) - 1. + // For GF(p): degree = BN_num_bits(p). + int degree = BN_num_bits(p); + if (isChar2) + degree = degree > 0 ? degree - 1 : 0; + + BN_free(p); + return degree; +#else + return 0; +#endif +} + int32_t CryptoNative_EvpPKeyGetEcKeyParameters( const EVP_PKEY* pkey, int32_t includePrivate, @@ -473,7 +562,9 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters( assert(cbD != NULL); #ifdef FEATURE_DISTRO_AGNOSTIC_SSL - if (!API_EXISTS(EVP_PKEY_get_bn_param)) + if (!API_EXISTS(EVP_PKEY_get_bn_param) || + !API_EXISTS(EVP_PKEY_get_octet_string_param) || + !API_EXISTS(EVP_PKEY_get_utf8_string_param)) { *cbQx = *cbQy = 0; *qx = *qy = 0; @@ -484,26 +575,118 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters( #endif int rc = 0; + +#ifdef NEED_OPENSSL_3_0 BIGNUM *xBn = NULL; BIGNUM *yBn = NULL; BIGNUM *dBn = NULL; + uint8_t* pubKeyBuf = NULL; + size_t pubKeyLen = 0; + EC_GROUP* group = NULL; + EC_POINT* point = NULL; + char curveName[80] = {0}; -#ifdef NEED_OPENSSL_3_0 // Ensure we have an EC key if (EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC) goto error; ERR_clear_error(); - if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &xBn)) + // Get the public key as an encoded point (may be compressed or uncompressed). + // We use OSSL_PKEY_PARAM_PUB_KEY instead of OSSL_PKEY_PARAM_EC_PUB_X/Y + // because the individual X/Y components may not be materialized yet. + if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &pubKeyLen)) + goto error; + + pubKeyBuf = (uint8_t*)OPENSSL_zalloc(pubKeyLen); + if (pubKeyBuf == NULL) + goto error; + + if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen, &pubKeyLen)) + goto error; + + // Decode the encoded point (compressed or uncompressed) to extract X and Y. + // Build an EC_GROUP from the key's parameters to perform the decoding. + + // Try named curve first. + if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curveName, sizeof(curveName), NULL)) + { + int nid = OBJ_txt2nid(curveName); + if (nid != NID_undef) + { + group = EC_GROUP_new_by_curve_name(nid); + } + } + + if (group == NULL) + { + // Explicit curve — build EC_GROUP from the key's field params. + BIGNUM* ecP = NULL; + BIGNUM* ecA = NULL; + BIGNUM* ecB = NULL; + + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_P, &ecP); + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_A, &ecA); + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_B, &ecB); + + if (ecP == NULL || ecA == NULL || ecB == NULL) + { + BN_free(ecP); + BN_free(ecA); + BN_free(ecB); + goto error; + } + + char fieldType[64] = {0}; + int isChar2 = 0; + + if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, sizeof(fieldType), NULL)) + { + isChar2 = (strcmp(fieldType, SN_X9_62_characteristic_two_field) == 0); + } + +#if HAVE_OPENSSL_EC2M + if (isChar2 && API_EXISTS(EC_GROUP_new_curve_GF2m)) + { + group = EC_GROUP_new_curve_GF2m(ecP, ecA, ecB, NULL); + } + else +#endif + if (!isChar2) + { + group = EC_GROUP_new_curve_GFp(ecP, ecA, ecB, NULL); + } + + BN_free(ecP); + BN_free(ecA); + BN_free(ecB); + + if (group == NULL) + goto error; + } + + point = EC_POINT_new(group); + if (point == NULL || + !EC_POINT_oct2point(group, point, pubKeyBuf, pubKeyLen, NULL)) + { + goto error; + } + + xBn = BN_new(); + yBn = BN_new(); + + if (xBn == NULL || yBn == NULL) goto error; - if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &yBn)) + + if (!EcPointGetAffineCoordinates(group, point, xBn, yBn)) goto error; *qx = xBn; - *cbQx = BN_num_bytes(xBn); + xBn = NULL; + *cbQx = BN_num_bytes(*qx); *qy = yBn; - *cbQy = BN_num_bytes(yBn); + yBn = NULL; + *cbQy = BN_num_bytes(*qy); if (includePrivate) { @@ -511,7 +694,8 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters( goto error; *d = dBn; - *cbD = BN_num_bytes(dBn); + dBn = NULL; + *cbD = BN_num_bytes(*d); } else { @@ -519,21 +703,32 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters( *cbD = 0; } - // success - return 1; + rc = 1; + goto exit; error: -#else - (void)pkey; - (void)includePrivate; -#endif *cbQx = *cbQy = 0; *qx = *qy = 0; if (d) *d = NULL; if (cbD) *cbD = 0; + +exit: if (xBn) BN_free(xBn); if (yBn) BN_free(yBn); + if (dBn) BN_clear_free(dBn); + if (pubKeyBuf) OPENSSL_free(pubKeyBuf); + if (point) EC_POINT_free(point); + if (group) EC_GROUP_free(group); return rc; +#else + (void)pkey; + (void)includePrivate; + *cbQx = *cbQy = 0; + *qx = *qy = 0; + if (d) *d = NULL; + if (cbD) *cbD = 0; + return 0; +#endif } EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( @@ -588,34 +783,15 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( aBn = BN_bin2bn(a, aLength, NULL); bBn = BN_bin2bn(b, bLength, NULL); -#if HAVE_OPENSSL_EC2M - if (API_EXISTS(EC_GROUP_set_curve_GF2m) && (curveType == Characteristic2)) - { - if (!EC_GROUP_set_curve_GF2m(group, pBn, aBn, bBn, NULL)) - goto error; - } - else -#endif - { - if (!EC_GROUP_set_curve_GFp(group, pBn, aBn, bBn, NULL)) - goto error; - } + if (!EC_GROUP_set_curve(group, pBn, aBn, bBn, NULL)) + goto error; // Set generator, order and cofactor G = EC_POINT_new(group); gxBn = BN_bin2bn(gx, gxLength, NULL); gyBn = BN_bin2bn(gy, gyLength, NULL); -#if HAVE_OPENSSL_EC2M - if (API_EXISTS(EC_POINT_set_affine_coordinates_GF2m) && (curveType == Characteristic2)) - { - EC_POINT_set_affine_coordinates_GF2m(group, G, gxBn, gyBn, NULL); - } - else -#endif - { - EC_POINT_set_affine_coordinates_GFp(group, G, gxBn, gyBn, NULL); - } + EC_POINT_set_affine_coordinates(group, G, gxBn, gyBn, NULL); orderBn = BN_bin2bn(order, orderLength, NULL); cofactorBn = BN_bin2bn(cofactor, cofactorLength, NULL); @@ -762,10 +938,10 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( assert(cbSeed != NULL); #ifdef FEATURE_DISTRO_AGNOSTIC_SSL - if (!API_EXISTS(EC_GROUP_new_by_curve_name) || - !API_EXISTS(EC_GROUP_get_field_type) || + if (!API_EXISTS(EC_GROUP_get_field_type) || !API_EXISTS(EVP_PKEY_get_octet_string_param) || - !API_EXISTS(EC_POINT_oct2point)) + !API_EXISTS(EVP_PKEY_get_utf8_string_param) || + !API_EXISTS(EVP_PKEY_get_bn_param)) { return 0; } @@ -801,7 +977,30 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( goto error; if (!CryptoNative_EvpPKeyGetEcGroupNid(pkey, &curveTypeNID) || !curveTypeNID) - goto error; + { + // For explicit curves, the group name may not be available. + // Get the field type directly instead. + char fieldTypeStr[32] = {0}; + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldTypeStr, sizeof(fieldTypeStr), NULL)) + goto error; + + fieldTypeNID = OBJ_txt2nid(fieldTypeStr); + if (fieldTypeNID == NID_undef) + goto error; + } + else + { + // Named curve: create group from the curve NID to get the field type. + group = EC_GROUP_new_by_curve_name(curveTypeNID); + if (!group) + goto error; + + // In some cases EVP_PKEY_get_field_type can return NID_undef + // and some providers seem to be ignoring OSSL_PKEY_PARAM_EC_FIELD_TYPE. + // This is specifically true for tpm2 provider. + // We can reliably get the field type from the EC_GROUP. + fieldTypeNID = EC_GROUP_get_field_type(group); + } // Extract p, a, b if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_P, &pBn)) @@ -813,19 +1012,32 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_B, &bBn)) goto error; - // curveTypeNID will be always NID_X9_62_characteristic_two_field or NID_X9_62_prime_field - group = EC_GROUP_new_by_curve_name(curveTypeNID); - - // In some cases EVP_PKEY_get_field_type can return NID_undef - // and some providers seem to be ignoring OSSL_PKEY_PARAM_EC_FIELD_TYPE. - // This is specifically true for tpm2 provider. - // We can reliably get the field type from the EC_GROUP. - fieldTypeNID = EC_GROUP_get_field_type(group); - *curveType = NIDToCurveType(fieldTypeNID); if (*curveType == Unspecified) goto error; + // For explicit curves where group was not created from a curve name, + // build it from the field parameters to decode the generator point. + if (!group) + { +#if HAVE_OPENSSL_EC2M + if (fieldTypeNID == NID_X9_62_characteristic_two_field) + { +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (API_EXISTS(EC_GROUP_new_curve_GF2m)) +#endif + { + group = EC_GROUP_new_curve_GF2m(pBn, aBn, bBn, NULL); + } + } + else +#endif + if (fieldTypeNID == NID_X9_62_prime_field) + { + group = EC_GROUP_new_curve_GFp(pBn, aBn, bBn, NULL); + } + } + if (!group) goto error; @@ -844,7 +1056,7 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( if (!EC_POINT_oct2point(group, G, generatorBuffer, generatorBufferSize, NULL)) goto error; - if (!EcPointGetAffineCoordinates(group, *curveType, G, xBn, yBn)) + if (!EcPointGetAffineCoordinates(group, G, xBn, yBn)) goto error; // Extract order (n) @@ -939,3 +1151,518 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( return 0; #endif } + +int32_t CryptoNative_EvpPKeyGenerateByEcKeyOid( + EVP_PKEY** pkey, + const char* oid) +{ + if (!pkey || !oid) + { + assert(false); + return 0; + } + + *pkey = NULL; + + ERR_clear_error(); + +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EVP_PKEY_CTX_new_from_name) || !API_EXISTS(EVP_PKEY_CTX_set_group_name)) + { + return 0; + } +#endif + +#ifdef NEED_OPENSSL_3_0 + + int nid = OBJ_txt2nid(oid); + if (!nid) + { + return -1; + } + + const char* groupName = OBJ_nid2sn(nid); + if (!groupName) + { + return -1; + } + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (ctx == NULL) + goto error; + + if (EVP_PKEY_keygen_init(ctx) <= 0) + goto error; + + if (EVP_PKEY_CTX_set_group_name(ctx, groupName) <= 0) + goto error; + + if (EVP_PKEY_keygen(ctx, pkey) <= 0) + goto error; + + EVP_PKEY_CTX_free(ctx); + return 1; + +error: + if (ctx != NULL) + EVP_PKEY_CTX_free(ctx); + + if (*pkey != NULL) + { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + + return 0; +#else + (void)oid; + return 0; +#endif +} + +int32_t CryptoNative_EvpPKeyCreateByEcKeyParameters( + EVP_PKEY** pkey, + const char* oid, + const uint8_t* qx, int32_t qxLength, + const uint8_t* qy, int32_t qyLength, + const uint8_t* d, int32_t dLength) +{ + if (!pkey || !oid) + { + assert(false); + return 0; + } + + *pkey = NULL; + + ERR_clear_error(); + +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EVP_PKEY_fromdata) || + !API_EXISTS(EVP_PKEY_fromdata_init) || + !API_EXISTS(EVP_PKEY_CTX_new_from_name) || + !API_EXISTS(OSSL_PARAM_BLD_new) || + !API_EXISTS(OSSL_PARAM_BLD_free) || + !API_EXISTS(OSSL_PARAM_BLD_push_utf8_string) || + !API_EXISTS(OSSL_PARAM_BLD_push_octet_string) || + !API_EXISTS(OSSL_PARAM_BLD_push_BN) || + !API_EXISTS(OSSL_PARAM_BLD_to_param) || + !API_EXISTS(OSSL_PARAM_free)) + { + return 0; + } +#endif + +#ifdef NEED_OPENSSL_3_0 + + // Verify the OID is recognized before doing any work. + int nid = OBJ_txt2nid(oid); + if (!nid) + { + return -1; + } + + // OBJ_nid2sn returns the short name OpenSSL expects for the group name param. + const char* groupName = OBJ_nid2sn(nid); + if (!groupName) + { + return -1; + } + + int ret = 0; + EVP_PKEY_CTX* ctx = NULL; + uint8_t* pubKeyBuf = NULL; + OSSL_PARAM_BLD* bld = NULL; + OSSL_PARAM* params = NULL; + BIGNUM* dBn = NULL; + EC_GROUP* group = NULL; + EC_POINT* pubPoint = NULL; + int32_t fieldSize = 0; + + const int hasPublicKey = (qx != NULL && qy != NULL); + const int hasPrivateKey = (d != NULL && dLength > 0); + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, groupName, 0)) + goto error; + + if (hasPrivateKey) + { + dBn = BN_bin2bn(d, dLength, NULL); + if (dBn == NULL) + goto error; + } + + // Build an EC_GROUP to determine the field size and (if needed) derive the public key. + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) + goto error; + + fieldSize = (EC_GROUP_get_degree(group) + 7) / 8; + + // Push public key, deriving it from the private key if unavailable. + if (hasPublicKey) + { + int32_t pubKeyLen; + + pubKeyBuf = EncodeEcPointFromCoordinates(qx, qxLength, qy, qyLength, fieldSize, &pubKeyLen); + if (pubKeyBuf == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, (size_t)pubKeyLen)) + goto error; + } + else if (hasPrivateKey) + { + // No public key provided, derive Q = d * G using EC_GROUP/EC_POINT (not deprecated). + pubPoint = EC_POINT_new(group); + if (pubPoint == NULL || + !EC_POINT_mul(group, pubPoint, dBn, NULL, NULL, NULL)) + goto error; + + size_t pubKeyLen; + pubKeyBuf = EncodeEcPointFromPoint(group, pubPoint, &pubKeyLen); + if (pubKeyBuf == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen)) + goto error; + } + + if (hasPrivateKey) + { + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, dBn)) + goto error; + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) + goto error; + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (ctx == NULL) + goto error; + + if (EVP_PKEY_fromdata_init(ctx) != 1) + goto error; + + { + int selection = hasPrivateKey ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY; + if (EVP_PKEY_fromdata(ctx, pkey, selection, params) != 1) + goto error; + } + + ret = 1; + goto exit; + +error: + if (*pkey) + { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + +exit: + if (params) OSSL_PARAM_free(params); + if (bld) OSSL_PARAM_BLD_free(bld); + if (ctx) EVP_PKEY_CTX_free(ctx); + if (dBn) BN_clear_free(dBn); + if (pubPoint) EC_POINT_free(pubPoint); + if (group) EC_GROUP_free(group); + if (pubKeyBuf) OPENSSL_free(pubKeyBuf); + return ret; +#else + (void)oid; + (void)qx; (void)qxLength; + (void)qy; (void)qyLength; + (void)d; (void)dLength; + return 0; +#endif +} + +EVP_PKEY* CryptoNative_EvpPKeyCreateByEcExplicitParameters( + ECCurveType curveType, + const uint8_t* qx, int32_t qxLength, + const uint8_t* qy, int32_t qyLength, + const uint8_t* d, int32_t dLength, + const uint8_t* p, int32_t pLength, + const uint8_t* a, int32_t aLength, + const uint8_t* b, int32_t bLength, + const uint8_t* gx, int32_t gxLength, + const uint8_t* gy, int32_t gyLength, + const uint8_t* order, int32_t orderLength, + const uint8_t* cofactor, int32_t cofactorLength, + const uint8_t* seed, int32_t seedLength) +{ + if (!p || !a || !b || !gx || !gy || !order || !cofactor) + { + assert(false); + return NULL; + } + + ERR_clear_error(); + +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EVP_PKEY_fromdata) || + !API_EXISTS(EVP_PKEY_fromdata_init) || + !API_EXISTS(EVP_PKEY_CTX_new_from_name) || + !API_EXISTS(EVP_PKEY_CTX_new_from_pkey) || + !API_EXISTS(EVP_PKEY_generate) || + !API_EXISTS(OSSL_PARAM_BLD_new) || + !API_EXISTS(OSSL_PARAM_BLD_free) || + !API_EXISTS(OSSL_PARAM_BLD_push_utf8_string) || + !API_EXISTS(OSSL_PARAM_BLD_push_octet_string) || + !API_EXISTS(OSSL_PARAM_BLD_push_BN) || + !API_EXISTS(OSSL_PARAM_BLD_to_param) || + !API_EXISTS(OSSL_PARAM_free)) + { + return NULL; + } +#endif + +#ifdef NEED_OPENSSL_3_0 + + EVP_PKEY* pkey = NULL; + EVP_PKEY_CTX* ctx = NULL; + OSSL_PARAM_BLD* bld = NULL; + OSSL_PARAM* params = NULL; + uint8_t* generatorBuf = NULL; + uint8_t* pubKeyBuf = NULL; + BIGNUM* pBn = NULL; + BIGNUM* aBn = NULL; + BIGNUM* bBn = NULL; + BIGNUM* orderBn = NULL; + BIGNUM* cofactorBn = NULL; + BIGNUM* dBn = NULL; + BIGNUM* gxBn = NULL; + BIGNUM* gyBn = NULL; + EC_GROUP* group = NULL; + EC_POINT* G = NULL; + EC_POINT* pubPoint = NULL; + int32_t fieldBits = 0; + int32_t fieldSize = 0; + int32_t genLen = 0; + + const int hasPublicKey = (qx != NULL && qy != NULL); + const int hasPrivateKey = (d != NULL && dLength > 0); + + const char* fieldType = (curveType == Characteristic2) + ? SN_X9_62_characteristic_two_field + : SN_X9_62_prime_field; + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, 0)) + goto error; + + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_ENCODING, "explicit", 0)) + goto error; + + pBn = BN_bin2bn(p, pLength, NULL); + aBn = BN_bin2bn(a, aLength, NULL); + bBn = BN_bin2bn(b, bLength, NULL); + + if (!pBn || !aBn || !bBn) + goto error; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, pBn)) + goto error; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, aBn)) + goto error; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, bBn)) + goto error; + + // For prime curves, BN_num_bits(pBn) is the bit-length of the prime. + // For characteristic-2 curves, p is the irreducible polynomial of degree m, + // so BN_num_bits(pBn) = m + 1. The field size (coordinate length) is ceil(m/8). + fieldBits = BN_num_bits(pBn); + + if (curveType == Characteristic2) + fieldBits--; + + fieldSize = (fieldBits + 7) / 8; + + // Generator as uncompressed point: 0x04 || gx || gy, padded to field size. + generatorBuf = EncodeEcPointFromCoordinates(gx, gxLength, gy, gyLength, fieldSize, &genLen); + if (generatorBuf == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, generatorBuf, (size_t)genLen)) + goto error; + + orderBn = BN_bin2bn(order, orderLength, NULL); + cofactorBn = BN_bin2bn(cofactor, cofactorLength, NULL); + + if (!orderBn || !cofactorBn) + goto error; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, orderBn)) + goto error; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactorBn)) + goto error; + + if (seed && seedLength > 0) + { + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED, seed, (size_t)seedLength)) + goto error; + } + + if (hasPrivateKey) + { + dBn = BN_bin2bn(d, dLength, NULL); + if (dBn == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, dBn)) + goto error; + } + + // Push public key, deriving it from the private key if unavailable. + if (hasPublicKey) + { + int32_t pubKeyLen; + + pubKeyBuf = EncodeEcPointFromCoordinates(qx, qxLength, qy, qyLength, fieldSize, &pubKeyLen); + if (pubKeyBuf == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, (size_t)pubKeyLen)) + goto error; + } + else if (hasPrivateKey) + { + // No public key provided, derive Q = d * G from the explicit curve parameters. +#if HAVE_OPENSSL_EC2M + if (curveType == Characteristic2) + { +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (API_EXISTS(EC_GROUP_new_curve_GF2m)) +#endif + { + group = EC_GROUP_new_curve_GF2m(pBn, aBn, bBn, NULL); + } + } + else +#endif + if (curveType != Characteristic2) + { + group = EC_GROUP_new_curve_GFp(pBn, aBn, bBn, NULL); + } + + if (group == NULL) + goto error; + + // Set the generator + G = EC_POINT_new(group); + gxBn = BN_bin2bn(gx, gxLength, NULL); + gyBn = BN_bin2bn(gy, gyLength, NULL); + + if (G == NULL || gxBn == NULL || gyBn == NULL) + goto error; + + if (!EC_POINT_set_affine_coordinates(group, G, gxBn, gyBn, NULL)) + goto error; + + if (!EC_GROUP_set_generator(group, G, orderBn, cofactorBn)) + goto error; + + // Derive Q = d * G + pubPoint = EC_POINT_new(group); + if (pubPoint == NULL || + !EC_POINT_mul(group, pubPoint, dBn, NULL, NULL, NULL)) + goto error; + + size_t pubKeyLen; + pubKeyBuf = EncodeEcPointFromPoint(group, pubPoint, &pubKeyLen); + if (pubKeyBuf == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen)) + goto error; + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) + goto error; + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (ctx == NULL) + goto error; + + if (!hasPublicKey && !hasPrivateKey) + { + // No key material — generate a new key from the domain parameters. + EVP_PKEY* templateKey = NULL; + + if (EVP_PKEY_fromdata_init(ctx) != 1) + goto error; + + if (EVP_PKEY_fromdata(ctx, &templateKey, EVP_PKEY_KEY_PARAMETERS, params) != 1) + goto error; + + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, templateKey, NULL); + EVP_PKEY_free(templateKey); + + if (ctx == NULL) + goto error; + + if (EVP_PKEY_keygen_init(ctx) != 1) + goto error; + + if (EVP_PKEY_generate(ctx, &pkey) != 1) + goto error; + } + else + { + if (EVP_PKEY_fromdata_init(ctx) != 1) + goto error; + + int selection = hasPrivateKey ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY; + if (EVP_PKEY_fromdata(ctx, &pkey, selection, params) != 1) + goto error; + } + + goto exit; + +error: + if (pkey) EVP_PKEY_free(pkey); + pkey = NULL; + +exit: + if (params) OSSL_PARAM_free(params); + if (bld) OSSL_PARAM_BLD_free(bld); + if (ctx) EVP_PKEY_CTX_free(ctx); + if (generatorBuf) OPENSSL_free(generatorBuf); + if (pubKeyBuf) OPENSSL_free(pubKeyBuf); + if (pBn) BN_free(pBn); + if (aBn) BN_free(aBn); + if (bBn) BN_free(bBn); + if (orderBn) BN_free(orderBn); + if (cofactorBn) BN_free(cofactorBn); + if (dBn) BN_clear_free(dBn); + if (gxBn) BN_free(gxBn); + if (gyBn) BN_free(gyBn); + if (G) EC_POINT_free(G); + if (pubPoint) EC_POINT_free(pubPoint); + if (group) EC_GROUP_free(group); + return pkey; +#else + (void)curveType; + (void)qx; (void)qxLength; (void)qy; (void)qyLength; + (void)d; (void)dLength; + (void)p; (void)pLength; (void)a; (void)aLength; (void)b; (void)bLength; + (void)gx; (void)gxLength; (void)gy; (void)gyLength; + (void)order; (void)orderLength; (void)cofactor; (void)cofactorLength; + (void)seed; (void)seedLength; + return NULL; +#endif +} diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h index ba16b5450c80c8..1353cfe93b5739 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h @@ -89,6 +89,58 @@ PALEXPORT EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( uint8_t* cofactor, int32_t hLength, uint8_t* seed, int32_t sLength); +/* +Generates a new EC key pair for a named curve using EVP_PKEY APIs. +Returns 1 upon success, -1 if oid was not found, otherwise 0. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyGenerateByEcKeyOid( + EVP_PKEY** pkey, + const char* oid); + +/* +Returns 1 if the EVP_PKEY EC key uses explicit encoding, 0 if it uses named curve encoding +or the encoding could not be read (named curve is the default), or -1 if the API is unavailable +(e.g. pre-3.0 OpenSSL) and the caller should use an alternative method. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyEcHasExplicitEncoding(const EVP_PKEY* pkey); + +/* +Returns the field degree (number of bits) of the EC group for the given EVP_PKEY. +For prime fields this is BN_num_bits(p), for binary fields it is BN_num_bits(polynomial) - 1. +Returns 0 on failure. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyGetEcFieldDegree(const EVP_PKEY* pkey); + +/* +Creates a new EVP_PKEY for a named EC curve using the provided key parameters. +qx/qy are the public key coordinates, d is the optional private key. +Returns 1 upon success, -1 if oid was not found, otherwise 0. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyCreateByEcKeyParameters( + EVP_PKEY** pkey, + const char* oid, + const uint8_t* qx, int32_t qxLength, + const uint8_t* qy, int32_t qyLength, + const uint8_t* d, int32_t dLength); + +/* +Creates a new EVP_PKEY for an EC key with explicit curve parameters. +Returns the new EVP_PKEY instance, or NULL on failure. +*/ +PALEXPORT EVP_PKEY* CryptoNative_EvpPKeyCreateByEcExplicitParameters( + ECCurveType curveType, + const uint8_t* qx, int32_t qxLength, + const uint8_t* qy, int32_t qyLength, + const uint8_t* d, int32_t dLength, + const uint8_t* p, int32_t pLength, + const uint8_t* a, int32_t aLength, + const uint8_t* b, int32_t bLength, + const uint8_t* gx, int32_t gxLength, + const uint8_t* gy, int32_t gyLength, + const uint8_t* order, int32_t orderLength, + const uint8_t* cofactor, int32_t cofactorLength, + const uint8_t* seed, int32_t seedLength); + /* Returns the ECC curve parameters of the given EVP_PKEY. */