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..3563e43af40cf4 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 @@ -11,34 +11,53 @@ internal static partial class Interop { internal static partial class Crypto { - [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByKeyParameters", StringMarshalling = StringMarshalling.Utf8)] - private static partial int EcKeyCreateByKeyParameters( - out SafeEcKeyHandle key, + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcParameters", StringMarshalling = StringMarshalling.Utf8)] + private static partial int EvpPKeyCreateByEcParameters( + out SafeEvpPKeyHandle pkey, string oid, byte[]? qx, int qxLength, byte[]? qy, int qyLength, - byte[]? d, int dLength); + byte[]? d, int dLength, + out int keySize); - internal static SafeEcKeyHandle EcKeyCreateByKeyParameters( + internal static SafeEvpPKeyHandle EvpPKeyCreateByEcParameters( string oid, - byte[]? qx, int qxLength, - byte[]? qy, int qyLength, - byte[]? d, int dLength) + byte[]? qx, + byte[]? qy, + byte[]? d, + out int keySize) { - SafeEcKeyHandle key; - int rc = EcKeyCreateByKeyParameters(out key, oid, qx, qxLength, qy, qyLength, d, dLength); - if (rc == -1) + SafeEvpPKeyHandle pkey; + int rc = EvpPKeyCreateByEcParameters( + out pkey, oid, + qx, qx?.Length ?? 0, + qy, qy?.Length ?? 0, + d, d?.Length ?? 0, + out keySize); + + if (rc == 2) { - key?.Dispose(); + pkey?.Dispose(); Interop.Crypto.ErrClearError(); - throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid)); } - return key; + + if (rc != 1 || pkey == null || pkey.IsInvalid) + { + Exception ex = Interop.Crypto.CreateOpenSslCryptographicException(); + pkey?.Dispose(); + throw ex; + } + + // EvpPKeyCreateByEcParameters may have polluted the error queue, but key was good in the end. + // Clean up the error queue. + Interop.Crypto.ErrClearError(); + + return pkey; } - [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByExplicitParameters")] - internal static partial SafeEcKeyHandle EcKeyCreateByExplicitParameters( + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcExplicitParameters")] + private static partial int EvpPKeyCreateByEcExplicitParameters( ECCurve.ECCurveType curveType, byte[]? qx, int qxLength, byte[]? qy, int qyLength, @@ -48,11 +67,18 @@ internal static partial SafeEcKeyHandle EcKeyCreateByExplicitParameters( byte[] b, int bLength, byte[] gx, int gxLength, byte[] gy, int gyLength, - byte[] order, int nLength, + byte[] order, int orderLength, byte[]? cofactor, int cofactorLength, - byte[]? seed, int seedLength); - - internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve) + byte[]? seed, int seedLength, + out SafeEvpPKeyHandle pkey, + out int keySize); + + internal static SafeEvpPKeyHandle EvpPKeyCreateByEcExplicitParameters( + ECCurve curve, + byte[]? qx, + byte[]? qy, + byte[]? d, + out int keySize) { byte[] p; if (curve.IsPrime) @@ -68,32 +94,65 @@ internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve) throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, curve.CurveType.ToString())); } - SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters( + int rc = EvpPKeyCreateByEcExplicitParameters( curve.CurveType, - null, 0, - null, 0, - null, 0, + qx, qx?.Length ?? 0, + qy, qy?.Length ?? 0, + d, d?.Length ?? 0, p, p.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 == null ? 0 : curve.Seed.Length); + curve.Cofactor, curve.Cofactor?.Length ?? 0, + curve.Seed, curve.Seed?.Length ?? 0, + out SafeEvpPKeyHandle pkey, + out keySize); - if (key == null || key.IsInvalid) + if (rc != 1 || pkey == null || pkey.IsInvalid) { - Exception e = Interop.Crypto.CreateOpenSslCryptographicException(); - key?.Dispose(); - throw e; + Exception ex = Interop.Crypto.CreateOpenSslCryptographicException(); + pkey?.Dispose(); + throw ex; } - // EcKeyCreateByExplicitParameters may have polluted the error queue, but key was good in the end. + // EvpPKeyCreateByEcExplicitParameters may have polluted the error queue, but key was good in the end. // Clean up the error queue. Interop.Crypto.ErrClearError(); - return key; + return pkey; + } + + [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcCurveOid", StringMarshalling = StringMarshalling.Utf8)] + private static partial int EvpPKeyGenerateByEcCurveOid( + out SafeEvpPKeyHandle pkey, + string oid, + out int keySize); + + internal static SafeEvpPKeyHandle EvpPKeyGenerateByEcCurveOid(string oid, out int keySize) + { + int rc = EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, oid, out keySize); + + if (rc == 2) + { + pkey?.Dispose(); + Interop.Crypto.ErrClearError(); + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid)); + } + + if (rc != 1 || pkey == null || pkey.IsInvalid) + { + Exception ex = Interop.Crypto.CreateOpenSslCryptographicException(); + pkey?.Dispose(); + throw ex; + } + + // EvpPKeyGenerateByEcCurveOid may have polluted the error queue, but key was good in the end. + // Clean up the error queue. + Interop.Crypto.ErrClearError(); + + return pkey; } [LibraryImport(Libraries.CryptoNative)] @@ -104,11 +163,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 +182,36 @@ 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 if the key has explicit encoding, if named. + /// + /// The encoding could not be determined from the key. + internal static bool EvpPKeyEcHasExplicitEncoding(SafeEvpPKeyHandle pkey) + { + int result = CryptoNative_EvpPKeyEcHasExplicitEncoding(pkey); + + if (result < 0) + { + throw CreateOpenSslCryptographicException(); + } + + return result == 1; + } + + [LibraryImport(Libraries.CryptoNative)] + private static partial int CryptoNative_EvpPKeyGetEcKeySize(SafeEvpPKeyHandle pkey); + + internal static int EvpPKeyGetEcKeySize(SafeEvpPKeyHandle pkey) + { + return CryptoNative_EvpPKeyGetEcKeySize(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 1f65d98a5bfa92..9efaddc6e8e4ad 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs @@ -88,20 +88,7 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa Debug.Assert(otherPartyPublicKey != null); 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) - { - // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible. - thisIsNamed = Interop.Crypto.EvpPKeyHasCurveName(_key.Value); - } - else - { - thisIsNamed = Interop.Crypto.EcKeyHasCurveName(ecKey); - } - } + bool thisIsNamed = !Interop.Crypto.EvpPKeyEcHasExplicitEncoding(_key.Value); ECDiffieHellmanOpenSslPublicKey? otherKey = otherPartyPublicKey as ECDiffieHellmanOpenSslPublicKey; bool disposeOtherKey = false; @@ -118,7 +105,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 +130,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 +139,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..cf8f670bbfe3af 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,31 @@ 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(); + return Interop.Crypto.EvpPKeyEcHasExplicitEncoding(GetKey()); + } + } internal int KeySize { get { ThrowIfDisposed(); - return _key.KeySize; + + int keySize = Interop.Crypto.EvpPKeyGetEcKeySize(_key); + + if (keySize == 0) + { + throw new CryptographicException(SR.Cryptography_InvalidHandle); + } + + return keySize; } } @@ -84,8 +86,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 +95,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..4c887fd999892c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs @@ -6,56 +6,16 @@ namespace System.Security.Cryptography { - internal sealed partial class ECOpenSsl + internal static partial class ECOpenSsl { internal const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // Also called nistP256 or secP256r1 internal const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1 internal const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1 - public int ImportParameters(ECParameters parameters) - { - SafeEcKeyHandle key; - - parameters.Validate(); - - if (parameters.Curve.IsPrime) - { - key = ImportPrimeCurveParameters(parameters); - } - else if (parameters.Curve.IsCharacteristic2) - { - key = ImportCharacteristic2CurveParameters(parameters); - } - else if (parameters.Curve.IsNamed) - { - key = ImportNamedCurveParameters(parameters); - } - else - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.CurveType.ToString())); - } - - if (key == null || key.IsInvalid) - { - Exception e = Interop.Crypto.CreateOpenSslCryptographicException(); - key?.Dispose(); - throw e; - } - - // The Import* methods above may have polluted the error queue even if in the end they succeeded. - // Clean up the error queue. - Interop.Crypto.ErrClearError(); - - FreeKey(); - _key = new Lazy(key); - return KeySize; - } - - public static ECParameters ExportExplicitParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) => + private static ECParameters ExportExplicitParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) => ExportExplicitCurveParameters(currentKey, includePrivateParameters); - public static ECParameters ExportParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) + private static ECParameters ExportParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) { ECParameters ecparams; if (Interop.Crypto.EcKeyHasCurveName(currentKey)) @@ -104,63 +64,6 @@ private static ECParameters ExportExplicitCurveParameters(SafeEcKeyHandle key, b return parameters; } - private static SafeEcKeyHandle ImportNamedCurveParameters(ECParameters parameters) - { - Debug.Assert(parameters.Curve.IsNamed); - - // Use oid Value first if present, otherwise FriendlyName - string oid = !string.IsNullOrEmpty(parameters.Curve.Oid.Value) ? - parameters.Curve.Oid.Value : parameters.Curve.Oid.FriendlyName!; - - SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByKeyParameters( - oid, - parameters.Q.X, parameters.Q.X?.Length ?? 0, - parameters.Q.Y, parameters.Q.Y?.Length ?? 0, - parameters.D, parameters.D == null ? 0 : parameters.D.Length); - - return key; - } - - private static SafeEcKeyHandle ImportPrimeCurveParameters(ECParameters parameters) - { - Debug.Assert(parameters.Curve.IsPrime); - SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters( - parameters.Curve.CurveType, - parameters.Q.X, parameters.Q.X?.Length ?? 0, - parameters.Q.Y, parameters.Q.Y?.Length ?? 0, - parameters.D, parameters.D == null ? 0 : parameters.D.Length, - parameters.Curve.Prime!, parameters.Curve.Prime!.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 == null ? 0 : parameters.Curve.Seed.Length); - - return key; - } - - private static SafeEcKeyHandle ImportCharacteristic2CurveParameters(ECParameters parameters) - { - Debug.Assert(parameters.Curve.IsCharacteristic2); - SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters( - parameters.Curve.CurveType, - parameters.Q.X, parameters.Q.X?.Length ?? 0, - parameters.Q.Y, parameters.Q.Y?.Length ?? 0, - parameters.D, parameters.D == null ? 0 : parameters.D.Length, - parameters.Curve.Polynomial!, parameters.Curve.Polynomial!.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 == null ? 0 : parameters.Curve.Seed.Length); - - return key; - } - private static void CheckInvalidKey(SafeEcKeyHandle key) { if (key == null || key.IsInvalid) @@ -181,14 +84,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 +99,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 +117,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)) { 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) @@ -257,35 +164,5 @@ private static ECParameters ExportExplicitCurveParametersFromEvpPKeyUsingParams( return parameters; } - - public static SafeEcKeyHandle GenerateKeyByKeySize(int keySize) - { - string oid; - switch (keySize) - { - case 256: oid = ECDSA_P256_OID_VALUE; break; - case 384: oid = ECDSA_P384_OID_VALUE; break; - case 521: oid = ECDSA_P521_OID_VALUE; break; - default: - // Only above three sizes supported for backwards compatibility; named curves should be used instead - throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); - } - - SafeEcKeyHandle? key = Interop.Crypto.EcKeyCreateByOid(oid); - - if (key == null || key.IsInvalid) - { - key?.Dispose(); - throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid)); - } - - if (!Interop.Crypto.EcKeyGenerateKey(key)) - { - key.Dispose(); - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } - - return key; - } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs index 20fd8788b1053e..7a6f1861d9d696 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs @@ -6,140 +6,79 @@ namespace System.Security.Cryptography { - internal sealed partial class ECOpenSsl : IDisposable + internal static partial class ECOpenSsl { - private Lazy _key = null!; // Always initialized - - public ECOpenSsl(ECCurve curve) - { - GenerateKey(curve); - } - - public ECOpenSsl(int keySizeBits) - { - _key = new Lazy(() => GenerateKeyByKeySize(keySizeBits)); - } - - public ECOpenSsl(ECParameters ecParameters) - { - ImportParameters(ecParameters); - } - - // Takes ownership of the key - public ECOpenSsl(SafeEcKeyHandle key) - { - _key = new Lazy(key); - } - - internal SafeEcKeyHandle Value => _key.Value; - - public void Dispose() - { - FreeKey(); - } - - internal int KeySize => Interop.Crypto.EcKeyGetSize(_key.Value); - - internal SafeEvpPKeyHandle CreateEvpPKeyHandle() - { - SafeEcKeyHandle currentKey = _key.Value; - Debug.Assert(currentKey != null, "key is null"); - - return Interop.Crypto.CreateEvpPkeyFromEcKey(currentKey); - } - - private void SetKey(SafeEcKeyHandle key) + internal static SafeEvpPKeyHandle GenerateECKey(int keySize) { - Debug.Assert(key != null); - Debug.Assert(!key.IsInvalid); - Debug.Assert(!key.IsClosed); + 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), + }; - FreeKey(); - _key = new Lazy(key); + SafeEvpPKeyHandle pkey = Interop.Crypto.EvpPKeyGenerateByEcCurveOid(oid, out int createdKeySize); + Debug.Assert(keySize == createdKeySize); + return pkey; } - internal int GenerateKey(ECCurve curve) + internal static SafeEvpPKeyHandle GenerateECKey(ECCurve curve, out int keySize) { curve.Validate(); - FreeKey(); if (curve.IsNamed) { - // Use oid Value first if present, otherwise FriendlyName because Oid maintains a hard-coded - // cache that may have different casing for FriendlyNames than OpenSsl string oid = !string.IsNullOrEmpty(curve.Oid.Value) ? curve.Oid.Value : curve.Oid.FriendlyName!; - SafeEcKeyHandle? key = Interop.Crypto.EcKeyCreateByOid(oid); - - if (key == null || key.IsInvalid) - { - key?.Dispose(); - throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid)); - } - - if (!Interop.Crypto.EcKeyGenerateKey(key)) - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } - - SetKey(key); + return Interop.Crypto.EvpPKeyGenerateByEcCurveOid(oid, out keySize); } else if (curve.IsExplicit) { - SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitCurve(curve); - - if (!Interop.Crypto.EcKeyGenerateKey(key)) - throw Interop.Crypto.CreateOpenSslCryptographicException(); - - SetKey(key); + // Pass null Q and null D to trigger key generation instead of import. + return Interop.Crypto.EvpPKeyCreateByEcExplicitParameters( + curve, + null, + null, + null, + out keySize); } else { throw new PlatformNotSupportedException( SR.Format(SR.Cryptography_CurveNotSupported, curve.CurveType.ToString())); } - - return KeySize; - } - - private void FreeKey() - { - if (_key != null) - { - if (_key.IsValueCreated) - { - _key.Value?.Dispose(); - } - - _key = null!; - } - } - - internal static SafeEvpPKeyHandle GenerateECKey(int keySize) - { - SafeEvpPKeyHandle ret = ImportECKeyCore(new ECOpenSsl(keySize), out int createdKeySize); - Debug.Assert(keySize == createdKeySize); - return ret; - } - - internal static SafeEvpPKeyHandle GenerateECKey(ECCurve curve, out int keySize) - { - return ImportECKeyCore(new ECOpenSsl(curve), out keySize); } internal static SafeEvpPKeyHandle ImportECKey(ECParameters parameters, out int keySize) { - return ImportECKeyCore(new ECOpenSsl(parameters), out keySize); - } + parameters.Validate(); - // Note: This method takes ownership of ecOpenSsl and disposes it - private static SafeEvpPKeyHandle ImportECKeyCore(ECOpenSsl ecOpenSsl, out int keySize) - { - using (ECOpenSsl ec = ecOpenSsl) + if (parameters.Curve.IsNamed) { - SafeEvpPKeyHandle handle = Interop.Crypto.CreateEvpPkeyFromEcKey(ec.Value); - keySize = ec.KeySize; - return handle; + string oid = !string.IsNullOrEmpty(parameters.Curve.Oid.Value) ? + parameters.Curve.Oid.Value : parameters.Curve.Oid.FriendlyName!; + + return Interop.Crypto.EvpPKeyCreateByEcParameters( + oid, + parameters.Q.X, + parameters.Q.Y, + parameters.D, + out keySize); + } + else if (parameters.Curve.IsExplicit) + { + return Interop.Crypto.EvpPKeyCreateByEcExplicitParameters( + parameters.Curve, + parameters.Q.X, + parameters.Q.Y, + parameters.D, + out keySize); + } + else + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.CurveType.ToString())); } } } 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..59de9b95f11208 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Security.Cryptography.EcDiffieHellman.Tests; +using System.Security.Cryptography.Tests; using Test.Cryptography; using Xunit; @@ -129,6 +130,26 @@ 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.EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, oid, out int keySize); + + Assert.Equal(1, rc); + Assert.False(pkey.IsInvalid); + Assert.Equal(expectedKeySize, keySize); + + using (pkey) + using (ECDiffieHellmanOpenSsl e = new ECDiffieHellmanOpenSsl(pkey)) + { + Assert.Equal(expectedKeySize, e.KeySize); + e.Exercise(); + } + } + [Fact] public void KeySizePropWithExercise() { @@ -278,5 +299,221 @@ 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 CtorEcKeyExportMatchesReimport(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 CtorEcKeyDeriveCrossCompatibleWithReimport(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 CtorEcKeyDeriveLeftAndRightSide() + { + ECParameters testData = EccTestData.GetNistP256ReferenceKey(); + + IntPtr ecKey; + int rc = Interop.Crypto.EcKeyCreateByKeyParameters( + out ecKey, + testData.Curve.Oid.Value!, + testData.Q.X, testData.Q.X!.Length, + testData.Q.Y, testData.Q.Y!.Length, + testData.D, testData.D!.Length); + + Assert.Equal(1, rc); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDiffieHellmanOpenSsl ecKeyBacked = new ECDiffieHellmanOpenSsl(ecKey)) + using (ECDiffieHellman peer = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256)) + using (ECDiffieHellmanPublicKey ecKeyPub = ecKeyBacked.PublicKey) + using (ECDiffieHellmanPublicKey peerPub = peer.PublicKey) + { + byte[] derivedLeft = ecKeyBacked.DeriveKeyFromHash(peerPub, HashAlgorithmName.SHA256); + byte[] derivedRight = peer.DeriveKeyFromHash(ecKeyPub, HashAlgorithmName.SHA256); + + Assert.Equal(derivedLeft, derivedRight); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void CtorEcKeyPublicOnlyFailsDerive() + { + ECParameters testData = EccTestData.GetNistP256ReferenceKey(); + + IntPtr ecKey; + int rc = Interop.Crypto.EcKeyCreateByKeyParameters( + out ecKey, + testData.Curve.Oid.Value!, + testData.Q.X, testData.Q.X!.Length, + testData.Q.Y, testData.Q.Y!.Length, + null, 0); + + Assert.Equal(1, rc); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDiffieHellmanOpenSsl ecKeyBacked = new ECDiffieHellmanOpenSsl(ecKey)) + using (ECDiffieHellman peer = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256)) + using (ECDiffieHellmanPublicKey peerPub = peer.PublicKey) + { + Assert.ThrowsAny(() => + ecKeyBacked.DeriveKeyFromHash(peerPub, HashAlgorithmName.SHA256)); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void ExplicitCurveImportExportProducesSameExplicitParams() + { + ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve(); + + using (ECDiffieHellman original = ECDiffieHellman.Create(explicitCurve)) + { + ECParameters explicitParams = original.ExportExplicitParameters(true); + + using (ECDiffieHellman reimported = ECDiffieHellman.Create(explicitParams)) + { + ECParameters reimportedParams = reimported.ExportExplicitParameters(true); + + ComparePublicKey(explicitParams.Q, reimportedParams.Q); + ComparePrivateKey(explicitParams, reimportedParams); + } + } + } + + [Fact] + public void ExplicitCurveImportAndOriginalDeriveCrossCompatible() + { + 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); + } + } + } + + [Fact] + public void GenerateKeyImplicitCurveThrows() + { + ECCurve implicitCurve = default; + + using (ECDiffieHellman ecdh = new ECDiffieHellmanOpenSsl()) + { + Assert.Throws(() => ecdh.GenerateKey(implicitCurve)); + } + } + + [Fact] + public void ImportParametersImplicitCurveThrows() + { + ECParameters parameters = new ECParameters + { + Curve = default, + D = new byte[32], + }; + + using (ECDiffieHellman ecdh = new ECDiffieHellmanOpenSsl()) + { + Assert.Throws(() => ecdh.ImportParameters(parameters)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs index 6f1139a8630629..83fc751ca5a829 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs @@ -3,7 +3,9 @@ using System; using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Security.Cryptography.EcDsa.Tests; +using System.Security.Cryptography.Tests; using Test.Cryptography; using Xunit; @@ -131,6 +133,26 @@ 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.EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, oid, out int keySize); + + Assert.Equal(1, rc); + Assert.False(pkey.IsInvalid); + Assert.Equal(expectedKeySize, keySize); + + using (pkey) + using (ECDsaOpenSsl e = new ECDsaOpenSsl(pkey)) + { + Assert.Equal(expectedKeySize, e.KeySize); + e.Exercise(); + } + } + [Fact] public void KeySizePropWithExercise() { @@ -297,6 +319,397 @@ 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 CtorEcKeyExportMatchesReimport(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 CtorEcKeySignVerifyCrossCompatible(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); + } + } + + [Theory] + [InlineData(ECDSA_P256_OID_VALUE, 256)] + [InlineData(ECDSA_P384_OID_VALUE, 384)] + [InlineData(ECDSA_P521_OID_VALUE, 521)] + [InlineData("2.23.43.1.4.7", 160)] // wap-wsg-idm-ecid-wtls7: field=160 bits, order=161 bits + public void CtorEcKeyNamedCurveExportIsCorrect(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 exportedParams = ecKeyBacked.ExportParameters(true); + + Assert.True(exportedParams.Curve.IsNamed); + Assert.Equal(oid, exportedParams.Curve.Oid.Value); + Assert.NotNull(exportedParams.Q.X); + Assert.NotNull(exportedParams.Q.Y); + Assert.NotNull(exportedParams.D); + exportedParams.Validate(); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void CtorEcKeyNamedCurveImportExportIsCorrect() + { + ECParameters testData = EccTestData.GetNistP256ReferenceKey(); + + IntPtr ecKey; + int rc = Interop.Crypto.EcKeyCreateByKeyParameters( + out ecKey, + testData.Curve.Oid.Value!, + testData.Q.X, testData.Q.X!.Length, + testData.Q.Y, testData.Q.Y!.Length, + testData.D, testData.D!.Length); + + Assert.Equal(1, rc); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + { + Assert.Equal(256, ecKeyBacked.KeySize); + + ECParameters exported = ecKeyBacked.ExportParameters(true); + + Assert.True(exported.Curve.IsNamed); + Assert.Equal(testData.Curve.Oid.Value, exported.Curve.Oid.Value); + + ComparePublicKey(testData.Q, exported.Q); + ComparePrivateKey(testData, exported); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [ConditionalFact(nameof(ECExplicitCurvesSupported))] + public void CtorEcKeyExplicitPrimeCurveExportIsCorrect() + { + ECParameters testData = EccTestData.GetNistP256ReferenceKeyExplicit(); + ECCurve curve = testData.Curve; + + IntPtr ecKey = Interop.Crypto.EcKeyCreateByExplicitParameters( + curve.CurveType, + testData.Q.X, testData.Q.X!.Length, + testData.Q.Y, testData.Q.Y!.Length, + testData.D, testData.D?.Length ?? 0, + curve.Prime, curve.Prime!.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?.Length ?? 0); + + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + { + Assert.Equal(256, ecKeyBacked.KeySize); + + ECParameters exported = ecKeyBacked.ExportExplicitParameters(true); + + Assert.True(exported.Curve.IsPrime); + Assert.Equal(curve.Prime, exported.Curve.Prime); + Assert.Equal(curve.A, exported.Curve.A); + Assert.Equal(curve.B, exported.Curve.B); + Assert.Equal(curve.G.X, exported.Curve.G.X); + Assert.Equal(curve.G.Y, exported.Curve.G.Y); + Assert.Equal(curve.Order, exported.Curve.Order); + Assert.Equal(curve.Cofactor, exported.Curve.Cofactor); + + ComparePublicKey(testData.Q, exported.Q); + ComparePrivateKey(testData, exported); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [ConditionalFact(nameof(ECExplicitCurvesSupported))] + public void CtorEcKeyExplicitChar2CurveExportIsCorrect() + { + ECParameters testData = EccTestData.Sect163k1Key1Explicit; + + if (!ECDsaFactory.IsCurveValid(EccTestData.Sect163k1Key1.Curve.Oid)) + { + return; + } + + ECCurve curve = testData.Curve; + + IntPtr ecKey = Interop.Crypto.EcKeyCreateByExplicitParameters( + curve.CurveType, + testData.Q.X, testData.Q.X!.Length, + testData.Q.Y, testData.Q.Y!.Length, + testData.D, testData.D?.Length ?? 0, + curve.Polynomial, curve.Polynomial!.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, + null, 0); + + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + { + Assert.Equal(163, ecKeyBacked.KeySize); + + ECParameters exported = ecKeyBacked.ExportExplicitParameters(true); + + Assert.True(exported.Curve.IsCharacteristic2); + Assert.Equal(curve.Polynomial, exported.Curve.Polynomial); + Assert.Equal(curve.A, exported.Curve.A); + Assert.Equal(curve.B, exported.Curve.B); + Assert.Equal(curve.G.X, exported.Curve.G.X); + Assert.Equal(curve.G.Y, exported.Curve.G.Y); + Assert.Equal(curve.Order, exported.Curve.Order); + Assert.Equal(curve.Cofactor, exported.Curve.Cofactor); + + ComparePublicKey(testData.Q, exported.Q); + ComparePrivateKey(testData, exported); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void CtorEcKeyNamedCurveImportPublicKey() + { + ECParameters testData = EccTestData.GetNistP256ReferenceKey(); + byte[] data = ByteUtils.RepeatByte(0x42, 64); + + byte[] signature; + using (ECDsa signer = ECDsa.Create(testData)) + { + signature = signer.SignData(data, HashAlgorithmName.SHA256); + } + + ECParameters publicOnly = new ECParameters + { + Curve = testData.Curve, + Q = testData.Q, + }; + + IntPtr ecKey; + int rc = Interop.Crypto.EcKeyCreateByKeyParameters( + out ecKey, + publicOnly.Curve.Oid.Value!, + publicOnly.Q.X, publicOnly.Q.X!.Length, + publicOnly.Q.Y, publicOnly.Q.Y!.Length, + null, 0); + + Assert.Equal(1, rc); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + { + Assert.True(ecKeyBacked.VerifyData(data, signature, HashAlgorithmName.SHA256)); + + byte[] wrongData = ByteUtils.RepeatByte(0x43, 64); + Assert.False(ecKeyBacked.VerifyData(wrongData, signature, HashAlgorithmName.SHA256)); + + Assert.ThrowsAny(() => + ecKeyBacked.SignData(data, HashAlgorithmName.SHA256)); + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void CtorEcKeyNamedCurveImportPrivateKey() + { + ECParameters testData = EccTestData.GetNistP256ReferenceKey(); + byte[] data = ByteUtils.RepeatByte(0x42, 64); + + IntPtr ecKey; + int rc = Interop.Crypto.EcKeyCreateByKeyParameters( + out ecKey, + testData.Curve.Oid.Value!, + testData.Q.X, testData.Q.X!.Length, + testData.Q.Y, testData.Q.Y!.Length, + testData.D, testData.D!.Length); + + Assert.Equal(1, rc); + Assert.NotEqual(IntPtr.Zero, ecKey); + + try + { + using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey)) + { + byte[] signature = ecKeyBacked.SignData(data, HashAlgorithmName.SHA256); + + using (ECDsa verifier = ECDsa.Create(testData)) + { + Assert.True(verifier.VerifyData(data, signature, HashAlgorithmName.SHA256)); + } + } + } + finally + { + Interop.Crypto.EcKeyDestroy(ecKey); + } + } + + [Fact] + public void ExplicitCurveImportExportProducesSameExplicitParams() + { + ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve(); + + using (ECDsa original = ECDsa.Create(explicitCurve)) + { + ECParameters explicitParams = original.ExportExplicitParameters(true); + + using (ECDsa reimported = ECDsa.Create(explicitParams)) + { + ECParameters reimportedParams = reimported.ExportExplicitParameters(true); + + ComparePublicKey(explicitParams.Q, reimportedParams.Q); + ComparePrivateKey(explicitParams, reimportedParams); + } + } + } + + [Fact] + public void ExplicitCurveImportAndOriginalSignVerifyCrossCompatible() + { + 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)); + } + } + } + + [Fact] + public void GenerateKeyImplicitCurveThrows() + { + ECCurve implicitCurve = default; + + using (ECDsa ecdsa = new ECDsaOpenSsl()) + { + Assert.Throws(() => ecdsa.GenerateKey(implicitCurve)); + } + } + + [Fact] + public void ImportParametersImplicitCurveThrows() + { + ECParameters parameters = new ECParameters + { + Curve = default, + D = new byte[32], + }; + + using (ECDsa ecdsa = new ECDsaOpenSsl()) + { + Assert.Throws(() => ecdsa.ImportParameters(parameters)); + } + } } } @@ -307,6 +720,14 @@ internal static partial class Crypto [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByOid")] internal static extern IntPtr EcKeyCreateByOid(string oid); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByKeyParameters")] + internal static extern int EcKeyCreateByKeyParameters( + out IntPtr key, + string oid, + byte[]? qx, int qxLength, + byte[]? qy, int qyLength, + byte[]? d, int dLength); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyGenerateKey")] internal static extern int EcKeyGenerateKey(IntPtr ecKey); @@ -315,5 +736,23 @@ internal static partial class Crypto [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_OpenSslVersionNumber")] internal static extern uint OpenSslVersionNumber(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcCurveOid")] + internal static extern int EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, string oid, out int keySize); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByExplicitParameters")] + internal static extern IntPtr EcKeyCreateByExplicitParameters( + 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); } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs index 20f6d539f708fc..7358d245b1f896 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs @@ -37,7 +37,7 @@ public ECDiffieHellmanOpenSsl(SafeEvpPKeyHandle pkeyHandle) } _key = new Lazy(pkeyHandle.DuplicateHandle()); - KeySizeValue = Interop.Crypto.EvpPKeyBits(_key.Value); + KeySizeValue = Interop.Crypto.EvpPKeyGetEcKeySize(_key.Value); } /// @@ -68,7 +68,7 @@ public ECDiffieHellmanOpenSsl(IntPtr handle) _key = new Lazy(Interop.Crypto.CreateEvpPkeyFromEcKey(ecKeyHandle)); } - KeySizeValue = Interop.Crypto.EvpPKeyBits(_key.Value); + KeySizeValue = Interop.Crypto.EvpPKeyGetEcKeySize(_key.Value); } /// diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs index b8318f56d2ff5e..ed39b301bcbd51 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs @@ -37,7 +37,7 @@ public ECDsaOpenSsl(SafeEvpPKeyHandle pkeyHandle) } _key = new Lazy(pkeyHandle.DuplicateHandle()); - ForceSetKeySize(Interop.Crypto.EvpPKeyBits(pkeyHandle)); + ForceSetKeySize(Interop.Crypto.EvpPKeyGetEcKeySize(pkeyHandle)); } /// @@ -68,7 +68,7 @@ public ECDsaOpenSsl(IntPtr handle) _key = new Lazy(Interop.Crypto.CreateEvpPkeyFromEcKey(ecKeyHandle)); } - ForceSetKeySize(Interop.Crypto.EvpPKeyBits(_key.Value)); + ForceSetKeySize(Interop.Crypto.EvpPKeyGetEcKeySize(_key.Value)); } /// diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index 59b0a6207f815b..55b9ea4bf95357 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_EvpPKeyCreateByEcParameters) + DllImportEntry(CryptoNative_EvpPKeyCreateByEcExplicitParameters) + DllImportEntry(CryptoNative_EvpPKeyGenerateByEcCurveOid) + DllImportEntry(CryptoNative_EvpPKeyEcHasExplicitEncoding) + DllImportEntry(CryptoNative_EvpPKeyGetEcKeySize) 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..99d6324c003ec5 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,30 +393,35 @@ 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) \ - REQUIRED_FUNCTION(EC_KEY_free) \ - REQUIRED_FUNCTION(EC_KEY_generate_key) \ - REQUIRED_FUNCTION(EC_KEY_get0_group) \ - REQUIRED_FUNCTION(EC_KEY_get0_private_key) \ - REQUIRED_FUNCTION(EC_KEY_get0_public_key) \ - REQUIRED_FUNCTION(EC_KEY_new) \ - REQUIRED_FUNCTION(EC_KEY_new_by_curve_name) \ - REQUIRED_FUNCTION(EC_KEY_set_group) \ - REQUIRED_FUNCTION(EC_KEY_set_private_key) \ - REQUIRED_FUNCTION(EC_KEY_set_public_key) \ - REQUIRED_FUNCTION(EC_KEY_set_public_key_affine_coordinates) \ - REQUIRED_FUNCTION(EC_KEY_up_ref) \ - REQUIRED_FUNCTION(EC_METHOD_get_field_type) \ + LIGHTUP_FUNCTION(EC_KEY_check_key) \ + LIGHTUP_FUNCTION(EC_KEY_free) \ + LIGHTUP_FUNCTION(EC_KEY_generate_key) \ + LIGHTUP_FUNCTION(EC_KEY_get0_group) \ + LIGHTUP_FUNCTION(EC_KEY_get0_private_key) \ + LIGHTUP_FUNCTION(EC_KEY_get0_public_key) \ + LIGHTUP_FUNCTION(EC_KEY_new) \ + LIGHTUP_FUNCTION(EC_KEY_new_by_curve_name) \ + LIGHTUP_FUNCTION(EC_KEY_set_group) \ + LIGHTUP_FUNCTION(EC_KEY_set_private_key) \ + LIGHTUP_FUNCTION(EC_KEY_set_public_key) \ + LIGHTUP_FUNCTION(EC_KEY_set_public_key_affine_coordinates) \ + LIGHTUP_FUNCTION(EC_KEY_up_ref) \ + LIGHTUP_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,6 +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) \ + LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_group_name) \ REQUIRED_FUNCTION(EVP_PKEY_new_raw_private_key) \ REQUIRED_FUNCTION(EVP_PKEY_new_raw_public_key) \ LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_params) \ @@ -543,8 +551,10 @@ extern bool g_libSslUses32BitTime; REQUIRED_FUNCTION(EVP_PKEY_get_raw_private_key) \ REQUIRED_FUNCTION(EVP_PKEY_get_raw_public_key) \ LIGHTUP_FUNCTION(EVP_PKEY_get0_RSA) \ + LIGHTUP_FUNCTION(EVP_PKEY_get0_provider) \ 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) \ @@ -644,6 +654,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 +854,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 +965,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 +974,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 +995,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 +1100,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 @@ -1102,8 +1127,10 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EVP_PKEY_get_raw_private_key EVP_PKEY_get_raw_private_key_ptr #define EVP_PKEY_get_raw_public_key EVP_PKEY_get_raw_public_key_ptr #define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr +#define EVP_PKEY_get0_provider EVP_PKEY_get0_provider_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 +1237,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 +1439,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..7a7bfa5b76f58b 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,24 @@ #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 OSSL_PKEY_EC_ENCODING_EXPLICIT "explicit" +#define OSSL_PKEY_EC_ENCODING_GROUP "named_curve" + +#define EVP_PKEY_KEY_PARAMETERS 4 #define OSSL_PKEY_PARAM_RSA_N "n" #define OSSL_PKEY_PARAM_RSA_E "e" @@ -52,6 +59,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 +106,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 +128,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 +145,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..115db4c94e5a03 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 @@ -2,8 +2,121 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_ecc_import_export.h" +#include "pal_eckey.h" #include "pal_utilities.h" +#ifdef NEED_OPENSSL_3_0 + +// 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; +} + +// Encode two coordinate byte arrays as an uncompressed EC point via EC_POINT. +// Uses EC_POINT_set_affine_coordinates + EC_POINT_point2oct so OpenSSL handles +// field-size-aware serialization (tolerates leading-zero-padded inputs). +// 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, + const EC_GROUP* group, + size_t* outLength) +{ + uint8_t* buf = NULL; + BIGNUM* xBn = BN_bin2bn(x, xLength, NULL); + BIGNUM* yBn = BN_bin2bn(y, yLength, NULL); + EC_POINT* point = NULL; + + if (!xBn || !yBn) + goto done; + + point = EC_POINT_new(group); + if (!point) + goto done; + + if (!EC_POINT_set_affine_coordinates(group, point, xBn, yBn, NULL)) + { + goto done; + } + + buf = EncodeEcPointFromPoint(group, point, outLength); + +done: + if (point) EC_POINT_free(point); + if (xBn) BN_free(xBn); + if (yBn) BN_free(yBn); + return buf; +} + +// Encodes the public key (from coordinates or derived from private key) and pushes +// it to the OSSL_PARAM_BLD. Returns 1 on success, 0 on failure. +// If a public-key buffer is allocated (either on success or if the push fails), +// *outPubKeyBuf is assigned and the caller must OPENSSL_free it. +static int PushPublicKeyParam( + OSSL_PARAM_BLD* bld, + const EC_GROUP* group, + const uint8_t* qx, int32_t qxLength, + const uint8_t* qy, int32_t qyLength, + const BIGNUM* dBn, + uint8_t** outPubKeyBuf) +{ + int hasPublicKey = (qx != NULL && qy != NULL); + int hasPrivateKey = (dBn != NULL); + + if (!hasPublicKey && !hasPrivateKey) + return 1; // Nothing to push + + uint8_t* pubKeyBuf = NULL; + size_t pubKeyLen = 0; + + if (hasPublicKey) + { + pubKeyBuf = EncodeEcPointFromCoordinates(qx, qxLength, qy, qyLength, group, &pubKeyLen); + } + else + { + // Derive Q = d * G + EC_POINT* pubPoint = EC_POINT_new(group); + if (pubPoint == NULL || + !EC_POINT_mul(group, pubPoint, dBn, NULL, NULL, NULL)) + { + EC_POINT_free(pubPoint); + return 0; + } + + pubKeyBuf = EncodeEcPointFromPoint(group, pubPoint, &pubKeyLen); + EC_POINT_free(pubPoint); + } + + if (pubKeyBuf == NULL) + return 0; + + *outPubKeyBuf = pubKeyBuf; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen)) + return 0; + + return 1; +} +#endif + static ECCurveType MethodToCurveType(const EC_METHOD* method) { if (method == EC_GFp_mont_method()) @@ -49,26 +162,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 +200,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 +321,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)) @@ -322,7 +396,7 @@ int32_t CryptoNative_GetECCurveParameters( return rc; } -int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, uint8_t* qx, int32_t qxLength, uint8_t* qy, int32_t qyLength, uint8_t* d, int32_t dLength) +int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, 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 (!key || !oid) { @@ -336,7 +410,7 @@ int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, u // oid can be friendly name or value int nid = OBJ_txt2nid(oid); - if (!nid) + if (nid == NID_undef) return -1; EC_KEY* tmpKey = EC_KEY_new_by_curve_name(nid); @@ -424,7 +498,7 @@ int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, u return ret; } -int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName) +int32_t CryptoNative_EvpPKeyGetEcGroupNid(EVP_PKEY *pkey, int32_t* nidName) { if (!nidName) return 0; @@ -434,28 +508,146 @@ int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC) return 0; +#if NEED_OPENSSL_3_0 #ifdef FEATURE_DISTRO_AGNOSTIC_SSL - if (!API_EXISTS(EVP_PKEY_get_utf8_string_param)) + if (API_EXISTS(EVP_PKEY_get_utf8_string_param)) +#endif { + // Retrieve the textual name of the EC group (e.g., "prime256v1") + // In all known cases this should be exactly 10 characters + 1 null byte but leaving some room in case it changes in the future + // versions of OpenSSL. This length also matches with what OpenSSL uses in their demo code: + // https://github.com/openssl/openssl/blob/ac80e1e15dcd13c61392a706170c427250c7bb69/demos/pkey/EVP_PKEY_EC_keygen.c#L88 + char curveName[80] = {0}; + + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curveName, sizeof(curveName), NULL)) + return 0; + + *nidName = OBJ_txt2nid(curveName); + return 1; + } +#endif + +#if NEED_OPENSSL_1_1 + EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecKey) return 0; + + const EC_GROUP* group = EC_KEY_get0_group(ecKey); + if (group) + { + *nidName = EC_GROUP_get_curve_name(group); } + + EC_KEY_free(ecKey); + return (*nidName != NID_undef) ? 1 : 0; +#else + return 0; #endif +} -#ifdef NEED_OPENSSL_3_0 - // Retrieve the textual name of the EC group (e.g., "prime256v1") - // In all known cases this should be exactly 10 characters + 1 null byte but leaving some room in case it changes in the future - // versions of OpenSSL. This length also matches with what OpenSSL uses in their demo code: - // https://github.com/openssl/openssl/blob/ac80e1e15dcd13c61392a706170c427250c7bb69/demos/pkey/EVP_PKEY_EC_keygen.c#L88 - char curveName[80] = {0}; +int32_t CryptoNative_EvpPKeyEcHasExplicitEncoding(EVP_PKEY* pkey) +{ + if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC) + return -1; + +#if NEED_OPENSSL_3_0 +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (API_EXISTS(EVP_PKEY_get_utf8_string_param)) +#endif + { + 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, OSSL_PKEY_EC_ENCODING_EXPLICIT) == 0) ? 1 : 0; + } +#endif + +#if NEED_OPENSSL_1_1 + EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecKey) + return -1; + + const EC_GROUP* group = EC_KEY_get0_group(ecKey); + int nid = group ? EC_GROUP_get_curve_name(group) : NID_undef; + EC_KEY_free(ecKey); + + return (nid == NID_undef) ? 1 : 0; +#else + return -1; +#endif +} - if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curveName, sizeof(curveName), NULL)) +// Returns the EC field degree (bits in the underlying field) for the specified EVP_PKEY. +// This is the value used as KeySize for EC keys in .NET, matching Windows CNG's +// BCRYPT_PUBLIC_KEY_LENGTH semantics for EC keys (field modulus bit-size, e.g. 256 +// for P-256). For some uncommon curves this differs from the subgroup order's bit length +// (e.g. wap-wsg-idm-ecid-wtls7 has field=160, order=161). +// Works for both provider-backed and legacy EC_KEY-backed EVP_PKEYs. +int32_t CryptoNative_EvpPKeyGetEcKeySize(EVP_PKEY* pkey) +{ + if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC) return 0; - *nidName = OBJ_txt2nid(curveName); - return 1; +#ifdef NEED_OPENSSL_3_0 +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (API_EXISTS(EVP_PKEY_get_bn_param) && + API_EXISTS(EVP_PKEY_get_utf8_string_param) && + API_EXISTS(EVP_PKEY_get0_provider) && + EVP_PKEY_get0_provider(pkey)) #else - return 0; + if (EVP_PKEY_get0_provider(pkey)) +#endif + { + // Provider-backed key: use OSSL 3.0 params API. + // 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. + + // For named curves, EC_GROUP_get_degree returns the field degree directly. + int nid = 0; + if (CryptoNative_EvpPKeyGetEcGroupNid(pkey, &nid) && nid != NID_undef) + { + EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); + if (group) + { + int degree = EC_GROUP_get_degree(group); + EC_GROUP_free(group); + return degree; + } + } + + // Fallback for explicit curves: fetch p and compute degree manually. + 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); + + char fieldType[32] = {0}; + if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, sizeof(fieldType), NULL)) + { + if (strcmp(fieldType, SN_X9_62_characteristic_two_field) == 0) + degree = degree > 0 ? degree - 1 : 0; + } + + BN_free(p); + return degree; + } #endif + + // Legacy or OSSL 1.1 path: get degree from the EC_KEY's group directly. + EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey); + if (ecKey) + { + const EC_GROUP* group = EC_KEY_get0_group(ecKey); + int degree = group ? EC_GROUP_get_degree(group) : 0; + EC_KEY_free(ecKey); + return degree; + } + + return 0; } int32_t CryptoNative_EvpPKeyGetEcKeyParameters( @@ -473,7 +665,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,34 +678,111 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters( #endif int rc = 0; + +#ifdef NEED_OPENSSL_3_0 BIGNUM *xBn = NULL; BIGNUM *yBn = NULL; BIGNUM *dBn = NULL; + BIGNUM *ecP = NULL; + BIGNUM *ecA = NULL; + BIGNUM *ecB = 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; - if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &yBn)) + + pubKeyBuf = (uint8_t*)OPENSSL_zalloc(pubKeyLen); + if (pubKeyBuf == NULL) goto error; - *qx = xBn; - *cbQx = BN_num_bytes(xBn); - *qy = yBn; - *cbQy = BN_num_bytes(yBn); + if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen, &pubKeyLen)) + goto error; - if (includePrivate) + // 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) { - if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &dBn)) + // Explicit curve — build EC_GROUP from the key's field params. + if (!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)) + { + 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); + } + + 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 (!EcPointGetAffineCoordinates(group, point, xBn, yBn)) + goto error; + + if (includePrivate && !EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &dBn)) + goto error; + + // Success; assign variables + if (includePrivate) + { *d = dBn; - *cbD = BN_num_bytes(dBn); + dBn = NULL; + *cbD = BN_num_bytes(*d); } else { @@ -519,36 +790,57 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters( *cbD = 0; } - // success - return 1; + *qx = xBn; + xBn = NULL; + *cbQx = BN_num_bytes(*qx); + *qy = yBn; + yBn = NULL; + *cbQy = BN_num_bytes(*qy); + + 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); + if (ecP) BN_free(ecP); + if (ecA) BN_free(ecA); + if (ecB) BN_free(ecB); 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( ECCurveType curveType, - uint8_t* qx, int32_t qxLength, - uint8_t* qy, int32_t qyLength, - uint8_t* d, int32_t dLength, - uint8_t* p, int32_t pLength, - uint8_t* a, int32_t aLength, - uint8_t* b, int32_t bLength, - uint8_t* gx, int32_t gxLength, - uint8_t* gy, int32_t gyLength, - uint8_t* order, int32_t orderLength, - uint8_t* cofactor, int32_t cofactorLength, - uint8_t* seed, int32_t seedLength) + 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) { @@ -588,38 +880,20 @@ 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); - EC_GROUP_set_generator(group, G, orderBn, cofactorBn); + if (!EC_GROUP_set_generator(group, G, orderBn, cofactorBn)) + goto error; // Set seed (optional) if (seed && seedLength > 0) @@ -728,8 +1002,12 @@ static ECCurveType NIDToCurveType(int fieldType) return Unspecified; } -int32_t CryptoNative_EvpPKeyGetEcCurveParameters( - const EVP_PKEY* pkey, +#if NEED_OPENSSL_3_0 +// Extracts EC curve parameters from a legacy EC_KEY-backed EVP_PKEY by getting +// the EC_KEY and delegating to CryptoNative_GetECCurveParameters. +// On success, the caller owns the returned BIGNUMs and must free them. +static int32_t EvpPKeyGetEcCurveParameters_FromEcKey( + EVP_PKEY* pkey, int32_t includePrivate, ECCurveType* curveType, BIGNUM** qx, int32_t* cbQx, @@ -744,37 +1022,139 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( BIGNUM** cofactor, int32_t* cbCofactor, BIGNUM** seed, int32_t* cbSeed) { - assert(p != NULL); - assert(cbP != NULL); - assert(a != NULL); - assert(cbA != NULL); - assert(b != NULL); - assert(cbB != NULL); - assert(gx != NULL); - assert(cbGx != NULL); - assert(gy != NULL); - assert(cbGy != NULL); - assert(order != NULL); - assert(cbOrder != NULL); - assert(cofactor != NULL); - assert(cbCofactor != NULL); - assert(seed != NULL); - assert(cbSeed != NULL); - -#ifdef FEATURE_DISTRO_AGNOSTIC_SSL - if (!API_EXISTS(EC_GROUP_new_by_curve_name) || - !API_EXISTS(EC_GROUP_get_field_type) || - !API_EXISTS(EVP_PKEY_get_octet_string_param) || - !API_EXISTS(EC_POINT_oct2point)) - { + EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecKey) return 0; - } -#endif -#ifdef NEED_OPENSSL_3_0 - ERR_clear_error(); + // CryptoNative_GetECCurveParameters returns freshly-allocated BIGNUMs for everything + // except d, which is a borrowed reference into the EC_KEY (EC_KEY_get0_private_key). + const BIGNUM* qxOwned = NULL; + const BIGNUM* qyOwned = NULL; + const BIGNUM* dBorrowed = NULL; + const BIGNUM* pOwned = NULL; + const BIGNUM* aOwned = NULL; + const BIGNUM* bOwned = NULL; + const BIGNUM* gxOwned = NULL; + const BIGNUM* gyOwned = NULL; + const BIGNUM* orderOwned = NULL; + const BIGNUM* cofactorOwned = NULL; + const BIGNUM* seedOwned = NULL; + + int32_t rc = CryptoNative_GetECCurveParameters( + ecKey, includePrivate, curveType, + &qxOwned, cbQx, + &qyOwned, cbQy, + &dBorrowed, cbD, + &pOwned, cbP, + &aOwned, cbA, + &bOwned, cbB, + &gxOwned, cbGx, + &gyOwned, cbGy, + &orderOwned, cbOrder, + &cofactorOwned, cbCofactor, + &seedOwned, cbSeed); + + if (rc == 1) + { + // Transfer ownership of the allocated BIGNUMs (cast away const via uintptr_t to avoid -Wcast-qual). + *qx = (BIGNUM*)(uintptr_t)qxOwned; + *qy = (BIGNUM*)(uintptr_t)qyOwned; + *p = (BIGNUM*)(uintptr_t)pOwned; + *a = (BIGNUM*)(uintptr_t)aOwned; + *b = (BIGNUM*)(uintptr_t)bOwned; + *gx = (BIGNUM*)(uintptr_t)gxOwned; + *gy = (BIGNUM*)(uintptr_t)gyOwned; + *order = (BIGNUM*)(uintptr_t)orderOwned; + *cofactor = (BIGNUM*)(uintptr_t)cofactorOwned; + *seed = (BIGNUM*)(uintptr_t)seedOwned; + + // d is borrowed from the EC_KEY, so we must duplicate before EC_KEY_free below. + if (includePrivate && dBorrowed) + { + *d = BN_dup(dBorrowed); + if (*d == NULL) + { + BN_free(*qx); BN_free(*qy); BN_free(*p); BN_free(*a); BN_free(*b); + BN_free(*gx); BN_free(*gy); BN_free(*order); BN_free(*cofactor); + if (*seed) BN_free(*seed); + *qx = *qy = *p = *a = *b = *gx = *gy = *order = *cofactor = *seed = NULL; + *cbQx = *cbQy = *cbP = *cbA = *cbB = *cbGx = *cbGy = *cbOrder = *cbCofactor = *cbSeed = 0; + if (cbD) *cbD = 0; + rc = 0; + } + } + else + { + *d = NULL; + } + } - // Get the public key parameters first in case any of its 'out' parameters are not initialized + EC_KEY_free(ecKey); + return rc; +} +#endif + +int32_t CryptoNative_EvpPKeyGetEcCurveParameters( + EVP_PKEY* pkey, + int32_t includePrivate, + ECCurveType* curveType, + BIGNUM** qx, int32_t* cbQx, + BIGNUM** qy, int32_t* cbQy, + BIGNUM** d, int32_t* cbD, + BIGNUM** p, int32_t* cbP, + BIGNUM** a, int32_t* cbA, + BIGNUM** b, int32_t* cbB, + BIGNUM** gx, int32_t* cbGx, + BIGNUM** gy, int32_t* cbGy, + BIGNUM** order, int32_t* cbOrder, + BIGNUM** cofactor, int32_t* cbCofactor, + BIGNUM** seed, int32_t* cbSeed) +{ + assert(p != NULL); + assert(cbP != NULL); + assert(a != NULL); + assert(cbA != NULL); + assert(b != NULL); + assert(cbB != NULL); + assert(gx != NULL); + assert(cbGx != NULL); + assert(gy != NULL); + assert(cbGy != NULL); + assert(order != NULL); + assert(cbOrder != NULL); + assert(cofactor != NULL); + assert(cbCofactor != NULL); + assert(seed != NULL); + assert(cbSeed != NULL); + +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EC_GROUP_get_field_type) || + !API_EXISTS(EVP_PKEY_get_octet_string_param) || + !API_EXISTS(EVP_PKEY_get_utf8_string_param) || + !API_EXISTS(EVP_PKEY_get_bn_param) || + !API_EXISTS(EVP_PKEY_get0_provider)) + { + return 0; + } +#endif + +#ifdef NEED_OPENSSL_3_0 + // For legacy EC_KEY-backed EVP_PKEYs, the OSSL 3.0 params API (get_bn_param etc.) + // does not expose curve parameters. Use EC_KEY APIs directly instead. + if (!EVP_PKEY_get0_provider(pkey)) + { + return EvpPKeyGetEcCurveParameters_FromEcKey( + pkey, includePrivate, curveType, + qx, cbQx, qy, cbQy, d, cbD, + p, cbP, a, cbA, b, cbB, + gx, cbGx, gy, cbGy, + order, cbOrder, cofactor, cbCofactor, + seed, cbSeed); + } + + ERR_clear_error(); + + // Provider-backed key: use OSSL 3.0 params API int32_t rc = CryptoNative_EvpPKeyGetEcKeyParameters(pkey, includePrivate, qx, cbQx, qy, cbQy, d, cbD); EC_POINT* G = NULL; @@ -800,8 +1180,31 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( if (!xBn || !yBn) goto error; - if (!CryptoNative_EvpPKeyGetEcGroupNid(pkey, &curveTypeNID) || !curveTypeNID) - goto error; + if (!CryptoNative_EvpPKeyGetEcGroupNid(pkey, &curveTypeNID) || curveTypeNID == NID_undef) + { + // 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 +1216,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 +1260,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 +1355,735 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters( return 0; #endif } + +#if NEED_OPENSSL_1_1 +// Returns 1 on success, 2 if OID not recognized, and 0 on failure. +static int32_t EvpPKeyGenerateByEcCurveOid_Legacy( + EVP_PKEY** pkey, + const char* oid, + int32_t* keySize) +{ + int rc = 0; + EC_KEY* ecKey = NULL; + + ecKey = CryptoNative_EcKeyCreateByOid(oid); + if (ecKey == NULL) + { + rc = 2; + goto error; + } + + if (!CryptoNative_EcKeyGenerateKey(ecKey)) + goto error; + + if (keySize != NULL) + { + const EC_GROUP* group = EC_KEY_get0_group(ecKey); + *keySize = group ? EC_GROUP_get_degree(group) : 0; + if (*keySize == 0) + goto error; + } + + *pkey = EVP_PKEY_new(); + if (*pkey == NULL) + goto error; + + if (!EVP_PKEY_set1_EC_KEY(*pkey, ecKey)) + goto error; + + rc = 1; + goto exit; + +error: + if (keySize) *keySize = 0; + if (*pkey) + { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + +exit: + if (ecKey) EC_KEY_free(ecKey); + + return rc; +} +#endif + +#if NEED_OPENSSL_3_0 +// Returns 1 on success, 2 if OID not recognized, 3 if the required OSSL 3.0+ APIs aren't present, and 0 on failure. +static int32_t EvpPKeyGenerateByEcCurveOid( + EVP_PKEY** pkey, + const char* oid, + int32_t* keySize) +{ +#ifdef FEATURE_DISTRO_AGNOSTIC_SSL + if (!API_EXISTS(EVP_PKEY_CTX_new_from_name) || !API_EXISTS(EVP_PKEY_CTX_set_group_name)) + { + return 3; + } +#endif + int rc = 0; + EVP_PKEY_CTX* ctx = NULL; + const char* groupName = NULL; + + int nid = OBJ_txt2nid(oid); + if (nid == NID_undef) + { + rc = 2; + goto error; + } + + // OpenSSL canonicalizes the nid to a short name internally, so we do the same: + // https://github.com/openssl/openssl/blob/7836b7d5b6a6b27a441c4e4c8564be6b270580c4/crypto/evp/ctrl_params_translate.c#L1154 + groupName = OBJ_nid2sn(nid); + if (!groupName) + goto error; + + 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; + + if (keySize != NULL) + { + *keySize = CryptoNative_EvpPKeyGetEcKeySize(*pkey); + } + + rc = 1; + goto exit; + +error: + if (keySize) *keySize = 0; + if (*pkey) + { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + +exit: + if (ctx) EVP_PKEY_CTX_free(ctx); + + return rc; +} +#endif + +int32_t CryptoNative_EvpPKeyGenerateByEcCurveOid( + EVP_PKEY** pkey, + const char* oid, + int32_t* keySize) +{ + if (!pkey || !oid) + { + assert(false); + return 0; + } + + *pkey = NULL; + + ERR_clear_error(); + + int rc = 0; + +#if NEED_OPENSSL_3_0 + rc = EvpPKeyGenerateByEcCurveOid(pkey, oid, keySize); +#endif + +#if NEED_OPENSSL_1_1 + // The portable build should only use the legacy OSSL 1.1 code path if OSSL 3.0+ APIs aren't present. +#if FEATURE_DISTRO_AGNOSTIC_SSL + if (rc == 3) +#endif + { + rc = EvpPKeyGenerateByEcCurveOid_Legacy(pkey, oid, keySize); + } +#endif + + return rc; +} + +#if NEED_OPENSSL_1_1 +static int32_t EvpPKeyCreateByEcParameters_Legacy( + 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, + int32_t* keySize) +{ + int rc = 0; + EC_KEY* ecKey = NULL; + + rc = CryptoNative_EcKeyCreateByKeyParameters(&ecKey, oid, qx, qxLength, qy, qyLength, d, dLength); + if (rc == -1) + { + rc = 2; + goto error; + } + + if (rc != 1 || ecKey == NULL) + goto error; + + if (keySize != NULL) + { + const EC_GROUP* group = EC_KEY_get0_group(ecKey); + *keySize = group ? EC_GROUP_get_degree(group) : 0; + if (*keySize == 0) + goto error; + } + + *pkey = EVP_PKEY_new(); + if (*pkey == NULL) + goto error; + + if (!EVP_PKEY_set1_EC_KEY(*pkey, ecKey)) + goto error; + + rc = 1; + goto exit; + +error: + if (keySize) *keySize = 0; + if (*pkey) + { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + +exit: + if (ecKey) EC_KEY_free(ecKey); + + return rc; +} +#endif + +#if NEED_OPENSSL_3_0 +// Returns 1 on success, 2 if OID not recognized, 3 if the required OSSL 3.0+ APIs aren't present, and 0 on failure. +static int32_t EvpPKeyCreateByEcParameters( + 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, + int32_t* keySize) +{ +#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 3; + } +#endif + + int nid = OBJ_txt2nid(oid); + if (nid == NID_undef) + return 2; + + // OpenSSL canonicalizes the nid to a short name internally, so we do the same: + // https://github.com/openssl/openssl/blob/7836b7d5b6a6b27a441c4e4c8564be6b270580c4/crypto/evp/ctrl_params_translate.c#L1154 + const char* groupName = OBJ_nid2sn(nid); + if (!groupName) + return 0; + + 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; + 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; + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, dBn)) + goto error; + } + + // Build an EC_GROUP to (if needed) derive the public key or encode coordinates. + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) + goto error; + + // Push public key, deriving it from the private key if unavailable. + if (!PushPublicKeyParam(bld, group, qx, qxLength, qy, qyLength, dBn, &pubKeyBuf)) + 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; + } + + if (keySize != NULL) + { + *keySize = CryptoNative_EvpPKeyGetEcKeySize(*pkey); + } + + ret = 1; + goto exit; + +error: + if (keySize) *keySize = 0; + 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 (group) EC_GROUP_free(group); + if (pubKeyBuf) OPENSSL_free(pubKeyBuf); + return ret; +} +#endif + +int32_t CryptoNative_EvpPKeyCreateByEcParameters( + 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, + int32_t* keySize) +{ + if (!pkey || !oid) + { + assert(false); + return 0; + } + + *pkey = NULL; + + ERR_clear_error(); + + int rc = 0; + +#if NEED_OPENSSL_3_0 + rc = EvpPKeyCreateByEcParameters(pkey, oid, qx, qxLength, qy, qyLength, d, dLength, keySize); +#endif + +#if NEED_OPENSSL_1_1 + // The portable build should only use the legacy OSSL 1.1 code path if OSSL 3.0+ APIs aren't present. +#if FEATURE_DISTRO_AGNOSTIC_SSL + if (rc == 3) +#endif + { + rc = EvpPKeyCreateByEcParameters_Legacy(pkey, oid, qx, qxLength, qy, qyLength, d, dLength, keySize); + } +#endif + + return rc; +} + +#if NEED_OPENSSL_1_1 +static int32_t EvpPKeyCreateByEcExplicitParameters_Legacy( + 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, + EVP_PKEY** pkey, + int32_t* keySize) +{ + int rc = 0; + EC_KEY* ecKey = NULL; + + ecKey = CryptoNative_EcKeyCreateByExplicitParameters( + curveType, + qx, qxLength, qy, qyLength, + d, dLength, + p, pLength, a, aLength, b, bLength, + gx, gxLength, gy, gyLength, + order, orderLength, cofactor, cofactorLength, + seed, seedLength); + + if (ecKey == NULL) + goto error; + + // If no public key and no private key were provided, generate a new key pair. + if (!qx && !d) + { + if (!EC_KEY_generate_key(ecKey)) + goto error; + } + + if (keySize != NULL) + { + const EC_GROUP* group = EC_KEY_get0_group(ecKey); + *keySize = group ? EC_GROUP_get_degree(group) : 0; + if (*keySize == 0) + goto error; + } + + *pkey = EVP_PKEY_new(); + if (*pkey == NULL) + goto error; + + if (!EVP_PKEY_set1_EC_KEY(*pkey, ecKey)) + goto error; + + rc = 1; + goto exit; + +error: + if (keySize) *keySize = 0; + if (*pkey) + { + EVP_PKEY_free(*pkey); + *pkey = NULL; + } + +exit: + if (ecKey) EC_KEY_free(ecKey); + + return rc; +} +#endif + +#if NEED_OPENSSL_3_0 +// Returns 1 on success, 3 if the required OSSL 3.0+ APIs aren't present, and 0 on failure. +static int32_t 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, + EVP_PKEY** pkey, + int32_t* keySize) +{ +#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 3; + } +#endif + + int ret = 0; + 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; + size_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, OSSL_PKEY_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; + + // Construct the EC_GROUP from the explicit curve parameters so we can use + // EC_POINT_set_affine_coordinates for proper point encoding. +#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); + } + + // When HAVE_OPENSSL_EC2M is not defined and curveType is Characteristic2, + // group remains NULL here. This matches the old behavior where the legacy + // EcKeyCreateByExplicitParameters also returned NULL, surfacing as a + // CryptographicException on the managed side. + if (group == NULL) + goto error; + + // Set the generator on the group (needed for point encoding and key derivation). + 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; + + orderBn = BN_bin2bn(order, orderLength, NULL); + cofactorBn = BN_bin2bn(cofactor, cofactorLength, NULL); + + if (!orderBn || !cofactorBn) + goto error; + + if (!EC_GROUP_set_generator(group, G, orderBn, cofactorBn)) + goto error; + + // Encode generator as uncompressed point for OSSL_PARAM. + generatorBuf = EncodeEcPointFromPoint(group, G, &genLen); + if (generatorBuf == NULL) + goto error; + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, generatorBuf, genLen)) + 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 (!PushPublicKeyParam(bld, group, qx, qxLength, qy, qyLength, dBn, &pubKeyBuf)) + 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; + } + + if (keySize != NULL) + { + *keySize = CryptoNative_EvpPKeyGetEcKeySize(*pkey); + } + + ret = 1; + goto exit; + +error: + if (keySize) *keySize = 0; + 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 (group) EC_GROUP_free(group); + return ret; +} +#endif + +int32_t 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, + EVP_PKEY** pkey, + int32_t* keySize) +{ + if (!p || !a || !b || !gx || !gy || !order || !cofactor || !pkey) + { + assert(false); + return 0; + } + + *pkey = NULL; + + ERR_clear_error(); + + int rc = 0; + +#if NEED_OPENSSL_3_0 + rc = EvpPKeyCreateByEcExplicitParameters( + curveType, qx, qxLength, qy, qyLength, d, dLength, + p, pLength, a, aLength, b, bLength, + gx, gxLength, gy, gyLength, + order, orderLength, cofactor, cofactorLength, seed, seedLength, + pkey, keySize); +#endif + +#if NEED_OPENSSL_1_1 + // The portable build should only use the legacy OSSL 1.1 code path if OSSL 3.0+ APIs aren't present. +#if FEATURE_DISTRO_AGNOSTIC_SSL + if (rc == 3) +#endif + { + rc = EvpPKeyCreateByEcExplicitParameters_Legacy( + curveType, qx, qxLength, qy, qyLength, d, dLength, + p, pLength, a, aLength, b, bLength, + gx, gxLength, gy, gyLength, + order, orderLength, cofactor, cofactorLength, seed, seedLength, + pkey, keySize); + } +#endif + + return rc; +} 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..8ed7505c292ec5 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 @@ -51,19 +51,20 @@ Returns 1 upon success, -1 if oid was not found, otherwise 0. PALEXPORT int32_t CryptoNative_EcKeyCreateByKeyParameters( EC_KEY** key, const char* oid, - uint8_t* qx, int32_t qxLength, - uint8_t* qy, int32_t qyLength, - uint8_t* d, int32_t dLength); + const uint8_t* qx, int32_t qxLength, + const uint8_t* qy, int32_t qyLength, + const uint8_t* d, int32_t dLength); /* Gets the NID of the curve name as an oid value for the specified EVP_PKEY. Returns 1 upon success, otherwise 0. */ -PALEXPORT int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName); +PALEXPORT int32_t CryptoNative_EvpPKeyGetEcGroupNid(EVP_PKEY *pkey, int32_t* nidName); /* -Returns the EC curve type of the given EVP_PKEY. +Returns the EC key parameters (public coordinates and optional private key) from the given EVP_PKEY. +Returns 1 upon success, otherwise 0. */ PALEXPORT int32_t CryptoNative_EvpPKeyGetEcKeyParameters( const EVP_PKEY* pkey, @@ -77,23 +78,87 @@ Returns the new EC_KEY instance using the explicit parameters. */ PALEXPORT EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( ECCurveType curveType, - uint8_t* qx, int32_t qxLength, - uint8_t* qy, int32_t qyLength, - uint8_t* d, int32_t dLength, - uint8_t* p, int32_t pLength, - uint8_t* a, int32_t aLength, - uint8_t* b, int32_t bLength, - uint8_t* gx, int32_t gxLength, - uint8_t* gy, int32_t gyLength, - uint8_t* order, int32_t nLength, - uint8_t* cofactor, int32_t hLength, - uint8_t* seed, int32_t sLength); + 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 nLength, + const uint8_t* cofactor, int32_t hLength, + const uint8_t* seed, int32_t sLength); + +/* +Generates a new EC key pair for a named curve using EVP_PKEY APIs. +On OpenSSL 3.0+ uses EVP_PKEY_CTX, otherwise falls back to EC_KEY APIs. +If keySize is not NULL, it receives the key size in bits. +Returns 1 upon success, -1 if oid was not found, otherwise 0. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyGenerateByEcCurveOid( + EVP_PKEY** pkey, + const char* oid, + int32_t* keySize); + +/* +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(EVP_PKEY* pkey); + +/* +Returns the EC field degree (bits in the underlying field) for the specified EVP_PKEY. +This is the value used as KeySize for EC keys in .NET, matching Windows CNG's +BCRYPT_PUBLIC_KEY_LENGTH semantics for EC keys (field modulus bit-size, e.g. 256 +for P-256). Note that for some uncommon curves this differs from the subgroup +order's bit length (e.g. wap-wsg-idm-ecid-wtls7 has field=160, order=161). +Works for both provider-backed and legacy EC_KEY-backed EVP_PKEYs. +Returns 0 on failure. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyGetEcKeySize(EVP_PKEY* pkey); + +/* +Creates a new EVP_PKEY for a named EC curve using the provided key parameters. +On success, *pkey receives the new key and *keySize receives the key size in bits. +Returns 1 upon success, -1 if oid was not found, otherwise 0. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeyCreateByEcParameters( + 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, + int32_t* keySize); + +/* +Creates a new EVP_PKEY for an EC key with explicit curve parameters. +On success, *pkey receives the new key and *keySize receives the key size in bits. +Returns 1 upon success, otherwise 0. +*/ +PALEXPORT int32_t 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, + EVP_PKEY** pkey, + int32_t* keySize); /* Returns the ECC curve parameters of the given EVP_PKEY. +Returns 1 upon success, otherwise 0. */ PALEXPORT int32_t CryptoNative_EvpPKeyGetEcCurveParameters( - const EVP_PKEY* pkey, + EVP_PKEY* pkey, int32_t includePrivate, ECCurveType* curveType, BIGNUM** qx, int32_t* cbQx,