Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand All @@ -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)]
Expand All @@ -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;
}

/// <summary>
Expand All @@ -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);

/// <summary>
/// Returns <see langword="true"/> if the key has explicit encoding, <see langword="false"/> if named.
/// </summary>
/// <exception cref="CryptographicException">The encoding could not be determined from the key.</exception>
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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
{
Expand All @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace System.Security.Cryptography
{
internal sealed class ECDiffieHellmanOpenSslPublicKey : ECDiffieHellmanPublicKey
{
private ECOpenSsl? _key;
private SafeEvpPKeyHandle? _key;

internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle)
{
Expand All @@ -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.
Expand All @@ -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;
}
}

Expand All @@ -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))]
Expand All @@ -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;
}
}
}
Loading
Loading