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,