Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
@@ -1,7 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Diagnostics;
using System.Formats.Asn1;
using System.Runtime.InteropServices;
using System.Security.Cryptography.Asn1;

namespace System.Security.Cryptography
{
Expand Down Expand Up @@ -37,79 +41,88 @@ public static bool IsAlgorithmSupported(ECDsaAlgorithm algorithm) =>
public static ECDsaComponent GenerateKey(ECDsaAlgorithm algorithm)
{
#if NET
ECDsa? ecdsa = null;

try
{
ecdsa = algorithm.CurveOid switch
{
Oids.secp256r1 => ECDsa.Create(ECCurve.NamedCurves.nistP256),
Oids.secp384r1 => ECDsa.Create(ECCurve.NamedCurves.nistP384),
Oids.secp521r1 => ECDsa.Create(ECCurve.NamedCurves.nistP521),
string oid => FailAndThrow<ECDsa>(oid)
};

static T FailAndThrow<T>(string oid)
{
Debug.Fail($"EC-DSA curve not supported ({oid})");
throw new CryptographicException();
}

// DSA key generation is lazy, so we need to force it to happen eagerly.
ecdsa.ExportParameters(includePrivateParameters: false);

return new ECDsaComponent(ecdsa, algorithm);
}
catch (CryptographicException)
{
ecdsa?.Dispose();
throw;
}
return new ECDsaComponent(ECDsa.Create(algorithm.Curve), algorithm);
#else
throw new PlatformNotSupportedException();
#endif
}

public static ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, ReadOnlySpan<byte> source)
public static unsafe ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, ReadOnlySpan<byte> source)
{
#if NET
ECDsa? ecdsa = null;

try
{
ecdsa = ECDsa.Create();
ecdsa.ImportECPrivateKey(source, out int bytesRead);

if (bytesRead != source.Length)
AsnDecoder.ReadEncodedValue(
source,
AsnEncodingRules.BER,
out _,
out _,
out int firstValueLength);

if (firstValueLength != source.Length)
{
throw new CryptographicException(SR.Argument_PrivateKeyWrongSizeForAlgorithm);
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}

ECParameters parameters = ecdsa.ExportParameters(includePrivateParameters: false);

if (!parameters.Curve.IsNamed || parameters.Curve.Oid.Value != algorithm.CurveOid)
fixed (byte* ptr = &MemoryMarshal.GetReference(source))
{
// The curve specified in ECDomainParameters of ECPrivateKey do not match the required curve for
// the Composite ML-DSA algorithm.
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, firstValueLength))
{
ECPrivateKey ecPrivateKey = ECPrivateKey.Decode(manager.Memory, AsnEncodingRules.BER);

if (ecPrivateKey.Version != 1)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}

// If domain parameters are present, validate that they match the composite ML-DSA algorithm.
if (ecPrivateKey.Parameters is ECDomainParameters domainParameters)
{
if (domainParameters.Named is not string curveOid || curveOid != algorithm.CurveOid)
{
// The curve specified must be named and match the required curve for the composite ML-DSA algorithm.
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
}

ECParameters parameters = new ECParameters
{
Curve = algorithm.Curve,
};

// If public key is present, add it to the parameters.
if (ecPrivateKey.PublicKey is ReadOnlyMemory<byte> publicKey)
{
parameters.Q = EccKeyFormatHelper.GetECPointFromUncompressedPublicKey(publicKey.Span, algorithm.KeySizeInBytes);
}

byte[] d = new byte[ecPrivateKey.PrivateKey.Length];

using (PinAndClear.Track(d))
{
ecPrivateKey.PrivateKey.CopyTo(d);
parameters.D = d;

parameters.Validate();

return new ECDsaComponent(ecdsa, algorithm);
#if NET
return new ECDsaComponent(ECDsa.Create(parameters), algorithm);
#else
throw new PlatformNotSupportedException();
#endif
}
}
}
}
catch (CryptographicException)
catch (AsnContentException e)
{
ecdsa?.Dispose();
throw;
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
#else
throw new PlatformNotSupportedException();
#endif

}

public static unsafe ECDsaComponent ImportPublicKey(ECDsaAlgorithm algorithm, ReadOnlySpan<byte> source)
{
#if NET
int fieldWidth = (algorithm.KeySizeInBits + 7) / 8;
int fieldWidth = algorithm.KeySizeInBytes;

if (source.Length != 1 + fieldWidth * 2)
{
Expand All @@ -134,6 +147,7 @@ public static unsafe ECDsaComponent ImportPublicKey(ECDsaAlgorithm algorithm, Re
}
};

#if NET
ECDsa? ecdsa = null;

try
Expand Down Expand Up @@ -163,7 +177,7 @@ internal override bool TryExportPrivateKey(Span<byte> destination, out int bytes
internal override bool TryExportPublicKey(Span<byte> destination, out int bytesWritten)
{
#if NET
int fieldWidth = (_algorithm.KeySizeInBits + 7) / 8;
int fieldWidth = _algorithm.KeySizeInBytes;

if (destination.Length < 1 + 2 * fieldWidth)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,28 @@ private sealed class ECDsaAlgorithm(int keySizeInBits, string curveOid, HashAlgo
internal int KeySizeInBits { get; } = keySizeInBits;
internal HashAlgorithmName HashAlgorithmName { get; } = hashAlgorithmName;
internal string CurveOid { get; } = curveOid;

internal int KeySizeInBytes => (KeySizeInBits + 7) / 8;

internal ECCurve Curve
{
get
{
return CurveOid switch
{
Oids.secp256r1 => ECCurve.NamedCurves.nistP256,
Oids.secp384r1 => ECCurve.NamedCurves.nistP384,
Oids.secp521r1 => ECCurve.NamedCurves.nistP521,
string oid => FailAndThrow(oid)
};

static ECCurve FailAndThrow(string oid)
{
Debug.Fail($"EC-DSA curve not supported ({oid})");
throw new CryptographicException();
}
}
}
}

private sealed class EdDsaAlgorithm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

namespace System.Security.Cryptography
{
public partial struct ECCurve
#if NETFRAMEWORK
internal
#else
public
#endif
partial struct ECCurve
{
/// <summary>
/// Represents the type of elliptic curve.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

namespace System.Security.Cryptography
{
public partial struct ECCurve
#if NETFRAMEWORK
internal
#else
public
#endif
partial struct ECCurve
{
/// <summary>
/// Factory class for creating named curves.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ namespace System.Security.Cryptography
/// which is either a prime curve or a characteristic-2 curve.
/// </remarks>
[DebuggerDisplay("ECCurve = {Oid}")]
public partial struct ECCurve
#if NETFRAMEWORK
#pragma warning disable CS0649 // Field 'ECCurve.G' is never assigned to, and will always have its default value
internal
#else
public
#endif
partial struct ECCurve
{
/// <summary>
/// Coefficient A. Applies only to Explicit curves.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ namespace System.Security.Cryptography
/// <summary>
/// Represents the public and private key of the specified elliptic curve.
/// </summary>
public struct ECParameters
#if NETFRAMEWORK
internal
#else
public
#endif
struct ECParameters
{
/// <summary>
/// Public point.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ namespace System.Security.Cryptography
/// <summary>
/// Represents a point on an elliptic curve.
/// </summary>
public struct ECPoint
#if NETFRAMEWORK
internal
#else
public
#endif
struct ECPoint
{
public byte[]? X;
public byte[]? Y;
Expand Down
Loading
Loading