Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
39 changes: 20 additions & 19 deletions src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,26 @@ internal static partial class BCryptNative
/// </summary>
internal static class AlgorithmName
{
public const string DSA = "DSA"; // BCRYPT_DSA_ALGORITHM
public const string ECDH = "ECDH"; // BCRYPT_ECDH_ALGORITHM
public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM
public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM
public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM
public const string ECDsa = "ECDSA"; // BCRYPT_ECDSA_ALGORITHM
public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM
public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM
public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM
public const string HKDF = "HKDF"; // BCRYPT_HKDF_ALGORITHM
public const string MD5 = "MD5"; // BCRYPT_MD5_ALGORITHM
public const string MLDsa = "ML-DSA"; // BCRYPT_MLDSA_ALGORITHM
public const string MLKem = "ML-KEM"; // BCRYPT_MLKEM_ALGORITHM
public const string RSA = "RSA"; // BCRYPT_RSA_ALGORITHM
public const string Sha1 = "SHA1"; // BCRYPT_SHA1_ALGORITHM
public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM
public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM
public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM
public const string Pbkdf2 = "PBKDF2"; // BCRYPT_PBKDF2_ALGORITHM
public const string CompositeMLDsa = "Composite-ML-DSA"; // BCRYPT_COMPOSITE_MLDSA_ALGORITHM
public const string DSA = "DSA"; // BCRYPT_DSA_ALGORITHM
public const string ECDH = "ECDH"; // BCRYPT_ECDH_ALGORITHM
public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM
public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM
public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM
public const string ECDsa = "ECDSA"; // BCRYPT_ECDSA_ALGORITHM
public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM
public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM
public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM
public const string HKDF = "HKDF"; // BCRYPT_HKDF_ALGORITHM
public const string MD5 = "MD5"; // BCRYPT_MD5_ALGORITHM
public const string MLDsa = "ML-DSA"; // BCRYPT_MLDSA_ALGORITHM
public const string MLKem = "ML-KEM"; // BCRYPT_MLKEM_ALGORITHM
public const string RSA = "RSA"; // BCRYPT_RSA_ALGORITHM
public const string Sha1 = "SHA1"; // BCRYPT_SHA1_ALGORITHM
public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM
public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM
public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM
public const string Pbkdf2 = "PBKDF2"; // BCRYPT_PBKDF2_ALGORITHM
}

internal static class KeyDerivationFunction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ internal static unsafe NTSTATUS BCryptSignHashPss(
}
}

internal static unsafe void BCryptSignHashPqcPure(
internal static unsafe int BCryptSignHashPqcPure(
SafeBCryptKeyHandle key,
ReadOnlySpan<byte> data,
ReadOnlySpan<byte> context,
Expand Down Expand Up @@ -104,12 +104,14 @@ internal static unsafe void BCryptSignHashPqcPure(
BCryptSignVerifyFlags.BCRYPT_PAD_PQDSA);
}

Debug.Assert(bytesWritten == destination.Length);

if (status != Interop.BCrypt.NTSTATUS.STATUS_SUCCESS)
{
throw Interop.BCrypt.CreateCryptographicException(status);
}

Debug.Assert(bytesWritten <= destination.Length);

return bytesWritten;
}

internal static unsafe void BCryptSignHashPqcPreHash(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ internal enum KeyBlobMagicNumber : int
BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC = 0x50444345,
BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC = 0x56444345,

BCRYPT_COMPOSITE_MLDSA_PUBLIC_MAGIC = 0x4B504D43,
BCRYPT_COMPOSITE_MLDSA_PRIVATE_MAGIC = 0x4B534D43,

BCRYPT_MLDSA_PUBLIC_MAGIC = 0x4B505344,
BCRYPT_MLDSA_PRIVATE_MAGIC = 0x4B535344,
BCRYPT_MLDSA_PRIVATE_SEED_MAGIC = 0x53535344,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,11 @@ static void SubjectPublicKeyReader(ReadOnlySpan<byte> key, in ValueAlgorithmIden
{
CompositeMLDsaAlgorithm algorithm = GetAlgorithmIdentifier(in identifier);

if (!IsAlgorithmSupported(algorithm))
{
throw new CryptographicException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
}

if (!algorithm.IsValidPublicKeySize(key.Length))
{
throw new CryptographicException(SR.Argument_PublicKeyWrongSizeForAlgorithm);
Expand Down Expand Up @@ -867,6 +872,11 @@ static void PrivateKeyReader(
{
CompositeMLDsaAlgorithm algorithm = GetAlgorithmIdentifier(in algorithmIdentifier);

if (!IsAlgorithmSupported(algorithm))
{
throw new CryptographicException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
}

if (!algorithm.IsValidPrivateKeySize(privateKeyContents.Length))
{
throw new CryptographicException(SR.Argument_PrivateKeyWrongSizeForAlgorithm);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,81 +1,248 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Formats.Asn1;
using System.Security.Cryptography.Asn1;
using Internal.Cryptography;
using Internal.NativeCrypto;
using Microsoft.Win32.SafeHandles;

using NTSTATUS = Interop.BCrypt.NTSTATUS;

namespace System.Security.Cryptography
{
internal sealed partial class CompositeMLDsaImplementation : CompositeMLDsa
{
private CompositeMLDsaImplementation(CompositeMLDsaAlgorithm algorithm)
private static readonly SafeBCryptAlgorithmHandle? s_algHandle = OpenAlgorithmHandle();

private readonly bool _hasPrivateKey;
private SafeBCryptKeyHandle _key;

private CompositeMLDsaImplementation(
CompositeMLDsaAlgorithm algorithm,
SafeBCryptKeyHandle key,
bool hasPrivateKey)
: base(algorithm)
{
throw new PlatformNotSupportedException();
_key = key;
_hasPrivateKey = hasPrivateKey;
}

internal static partial bool SupportsAny()
[MemberNotNullWhen(true, nameof(s_algHandle))]
internal static partial bool SupportsAny() => s_algHandle is not null;

[MemberNotNullWhen(true, nameof(s_algHandle))]
internal static partial bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm) =>
SupportsAny() && PqcBlobHelpers.TryGetCompositeMLDsaParameterSet(algorithm, out _);

protected override int SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, Span<byte> destination)
{
if (!Helpers.IsOSPlatformWindows)
if (!_hasPrivateKey)
{
return false;
throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable);
}

return CompositeMLDsaManaged.SupportsAny();
return Interop.BCrypt.BCryptSignHashPqcPure(_key, data, context, destination);
}

internal static partial bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm)
protected override bool VerifyDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, ReadOnlySpan<byte> signature) =>
Interop.BCrypt.BCryptVerifySignaturePqcPure(_key, data, context, signature);

internal static partial CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm)
{
if (!Helpers.IsOSPlatformWindows)
Debug.Assert(SupportsAny());

if (!PqcBlobHelpers.TryGetCompositeMLDsaParameterSet(algorithm, out string? parameterSet))
{
return false;
Debug.Fail("Base class should have validated algorithm support.");
throw new CryptographicException();
}

return CompositeMLDsaManaged.IsAlgorithmSupportedImpl(algorithm);
}
SafeBCryptKeyHandle keyHandle = Interop.BCrypt.BCryptGenerateKeyPair(s_algHandle, keyLength: 0);

internal static partial CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm)
{
if (!Helpers.IsOSPlatformWindows)
try
{
Interop.BCrypt.BCryptSetSZProperty(keyHandle, Interop.BCrypt.BCryptPropertyStrings.BCRYPT_PARAMETER_SET_NAME, parameterSet);
Interop.BCrypt.BCryptFinalizeKeyPair(keyHandle);
}
catch
{
throw new PlatformNotSupportedException();
keyHandle?.Dispose();
throw;
}

return CompositeMLDsaManaged.GenerateKeyImpl(algorithm);
return new CompositeMLDsaImplementation(algorithm, keyHandle, hasPrivateKey: true);
}

internal static partial CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan<byte> source)
{
if (!Helpers.IsOSPlatformWindows)
Debug.Assert(SupportsAny());

if (!PqcBlobHelpers.TryGetCompositeMLDsaParameterSet(algorithm, out string? parameterSet))
{
throw new PlatformNotSupportedException();
Debug.Fail("Base class should have validated algorithm support.");
throw new CryptographicException();
}

return CompositeMLDsaManaged.ImportCompositeMLDsaPublicKeyImpl(algorithm, source);
const string PublicBlobType = Interop.BCrypt.KeyBlobType.BCRYPT_PQDSA_PUBLIC_BLOB;

SafeBCryptKeyHandle key =
PqcBlobHelpers.EncodeCompositeMLDsaBlob(
parameterSet,
source,
PublicBlobType,
static blob => Interop.BCrypt.BCryptImportKeyPair(s_algHandle, PublicBlobType, blob));

return new CompositeMLDsaImplementation(algorithm, key, hasPrivateKey: false);
}

internal static partial CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan<byte> source)
{
if (!Helpers.IsOSPlatformWindows)
Debug.Assert(SupportsAny());

if (!PqcBlobHelpers.TryGetCompositeMLDsaParameterSet(algorithm, out string? parameterSet))
{
throw new PlatformNotSupportedException();
Debug.Fail("Base class should have validated algorithm support.");
throw new CryptographicException();
}

return CompositeMLDsaManaged.ImportCompositeMLDsaPrivateKeyImpl(algorithm, source);
const string PrivateBlobType = Interop.BCrypt.KeyBlobType.BCRYPT_PQDSA_PRIVATE_BLOB;

SafeBCryptKeyHandle key =
PqcBlobHelpers.EncodeCompositeMLDsaBlob(
parameterSet,
source,
PrivateBlobType,
static blob => Interop.BCrypt.BCryptImportKeyPair(s_algHandle, PrivateBlobType, blob));

return new CompositeMLDsaImplementation(algorithm, key, hasPrivateKey: true);
}

protected override int SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, Span<byte> destination) =>
throw new PlatformNotSupportedException();
protected override bool TryExportPkcs8PrivateKeyCore(Span<byte> destination, out int bytesWritten)
Comment thread
PranavSenthilnathan marked this conversation as resolved.
{
if (!_hasPrivateKey)
{
throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable);
}

protected override bool VerifyDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, ReadOnlySpan<byte> signature) =>
throw new PlatformNotSupportedException();
ValuePrivateKeyInfoAsn privateKeyInfo = new()
{
PrivateKeyAlgorithm = new ValueAlgorithmIdentifierAsn
{
Algorithm = Algorithm.Oid,
},
};

int written = 0;
byte[] rented = CryptoPool.Rent(Algorithm.MaxPrivateKeySizeInBytes);

try
{
written = ExportKey(Interop.BCrypt.KeyBlobType.BCRYPT_PQDSA_PRIVATE_BLOB, rented);
privateKeyInfo.PrivateKey = rented.AsSpan(0, written);

Comment thread
PranavSenthilnathan marked this conversation as resolved.
Outdated
Comment thread
PranavSenthilnathan marked this conversation as resolved.
Outdated
AsnWriter pkcs8Writer = new(AsnEncodingRules.DER);
privateKeyInfo.Encode(pkcs8Writer);

bool result = pkcs8Writer.TryEncode(destination, out bytesWritten);
pkcs8Writer.Reset();

protected override bool TryExportPkcs8PrivateKeyCore(Span<byte> destination, out int bytesWritten) =>
throw new PlatformNotSupportedException();
return result;
}
finally
{
CryptoPool.Return(rented, clearSize: written);
}
}

protected override int ExportCompositeMLDsaPublicKeyCore(Span<byte> destination) =>
throw new PlatformNotSupportedException();
ExportKey(Interop.BCrypt.KeyBlobType.BCRYPT_PQDSA_PUBLIC_BLOB, destination);

protected override int ExportCompositeMLDsaPrivateKeyCore(Span<byte> destination)
{
if (!_hasPrivateKey)
{
throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable);
}

return ExportKey(
Interop.BCrypt.KeyBlobType.BCRYPT_PQDSA_PRIVATE_BLOB,
destination);
Comment thread
PranavSenthilnathan marked this conversation as resolved.
Outdated
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_key?.Dispose();
_key = null!;
}

base.Dispose(disposing);
}

private int ExportKey(string keyBlobType, Span<byte> destination)
{
ArraySegment<byte> keyBlob = Interop.BCrypt.BCryptExportKey(_key, keyBlobType);

protected override int ExportCompositeMLDsaPrivateKeyCore(Span<byte> destination) =>
throw new PlatformNotSupportedException();
try
{
ReadOnlySpan<byte> keyBytes = PqcBlobHelpers.DecodeCompositeMLDsaBlob(
keyBlob,
out ReadOnlySpan<char> parameterSet,
out string blobType);

if (!PqcBlobHelpers.TryGetCompositeMLDsaParameterSet(Algorithm, out string? expectedParameterSet))
{
Debug.Fail("Unsupported algorithm.");
throw new CryptographicException();
}

if (blobType != keyBlobType ||
keyBytes.Length > destination.Length ||
!parameterSet.SequenceEqual(expectedParameterSet))
{
Comment thread
PranavSenthilnathan marked this conversation as resolved.
Debug.Fail(
$"{nameof(blobType)}: {blobType}, " +
$"{nameof(parameterSet)}: {parameterSet.ToString()}, " +
$"{nameof(keyBytes)}.Length: {keyBytes.Length} / {destination.Length}");

throw new CryptographicException();
}

keyBytes.CopyTo(destination);
return keyBytes.Length;
}
finally
{
CryptoPool.Return(keyBlob);
}
}

private static SafeBCryptAlgorithmHandle? OpenAlgorithmHandle()
{
if (!Helpers.IsOSPlatformWindows)
{
return null;
}

NTSTATUS status = Interop.BCrypt.BCryptOpenAlgorithmProvider(
out SafeBCryptAlgorithmHandle hAlgorithm,
BCryptNative.AlgorithmName.CompositeMLDsa,
pszImplementation: null,
Interop.BCrypt.BCryptOpenAlgorithmProviderFlags.None);

if (status != NTSTATUS.STATUS_SUCCESS)
{
hAlgorithm.Dispose();
return null;
}
else
{
return hAlgorithm;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ protected override void SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte>
throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable);
}

Interop.BCrypt.BCryptSignHashPqcPure(_key, data, context, destination);
int written = Interop.BCrypt.BCryptSignHashPqcPure(_key, data, context, destination);
Debug.Assert(written == destination.Length);
Comment thread
PranavSenthilnathan marked this conversation as resolved.
Outdated
Comment thread
PranavSenthilnathan marked this conversation as resolved.
Outdated
}

protected override bool VerifyDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, ReadOnlySpan<byte> signature) =>
Expand Down
Loading
Loading