Skip to content

Commit b8ed7bb

Browse files
Managed Composite ML-DSA + ECDSA for netfx (#118356)
Co-authored-by: Copilot <[email protected]>
1 parent e399fff commit b8ed7bb

File tree

15 files changed

+790
-350
lines changed

15 files changed

+790
-350
lines changed

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.Blobs.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ internal static byte[] Consume(ReadOnlySpan<byte> blob, ref int offset, int coun
7878
return value;
7979
}
8080

81+
/// <summary>
82+
/// Peel off the next "count" bytes in blob and copy them into the destination.
83+
/// </summary>
84+
internal static void Consume(ReadOnlySpan<byte> blob, ref int offset, int count, Span<byte> destination)
85+
{
86+
blob.Slice(offset, count).CopyTo(destination);
87+
offset += count;
88+
}
89+
8190
/// <summary>
8291
/// Magic numbers identifying blob types
8392
/// </summary>

src/libraries/Common/src/System/Security/Cryptography/AsymmetricAlgorithmHelpers.Der.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,109 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using System.Formats.Asn1;
56

67
namespace System.Security.Cryptography
78
{
89
internal static partial class AsymmetricAlgorithmHelpers
910
{
11+
/// <summary>
12+
/// Convert Ieee1363 format of (r, s) to Der format
13+
/// </summary>
14+
public static byte[] ConvertIeee1363ToDer(ReadOnlySpan<byte> input)
15+
{
16+
AsnWriter writer = WriteIeee1363ToDer(input);
17+
return writer.Encode();
18+
}
19+
20+
internal static bool TryConvertIeee1363ToDer(
21+
ReadOnlySpan<byte> input,
22+
Span<byte> destination,
23+
out int bytesWritten)
24+
{
25+
AsnWriter writer = WriteIeee1363ToDer(input);
26+
return writer.TryEncode(destination, out bytesWritten);
27+
}
28+
29+
private static AsnWriter WriteIeee1363ToDer(ReadOnlySpan<byte> input)
30+
{
31+
Debug.Assert(input.Length % 2 == 0);
32+
Debug.Assert(input.Length > 1);
33+
34+
// Input is (r, s), each of them exactly half of the array.
35+
// Output is the DER encoded value of SEQUENCE(INTEGER(r), INTEGER(s)).
36+
int halfLength = input.Length / 2;
37+
38+
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
39+
writer.PushSequence();
40+
writer.WriteKeyParameterInteger(input.Slice(0, halfLength));
41+
writer.WriteKeyParameterInteger(input.Slice(halfLength, halfLength));
42+
writer.PopSequence();
43+
return writer;
44+
}
45+
46+
/// <summary>
47+
/// Convert Der format of (r, s) to Ieee1363 format
48+
/// </summary>
49+
public static byte[] ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits)
50+
{
51+
int fieldSizeBytes = BitsToBytes(fieldSizeBits);
52+
int encodedSize = 2 * fieldSizeBytes;
53+
byte[] response = new byte[encodedSize];
54+
55+
ConvertDerToIeee1363(input, fieldSizeBits, response);
56+
return response;
57+
}
58+
59+
internal static int ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits, Span<byte> destination)
60+
{
61+
int fieldSizeBytes = BitsToBytes(fieldSizeBits);
62+
int encodedSize = 2 * fieldSizeBytes;
63+
64+
Debug.Assert(destination.Length >= encodedSize);
65+
66+
try
67+
{
68+
AsnValueReader reader = new AsnValueReader(input, AsnEncodingRules.DER);
69+
AsnValueReader sequenceReader = reader.ReadSequence();
70+
reader.ThrowIfNotEmpty();
71+
ReadOnlySpan<byte> rDer = sequenceReader.ReadIntegerBytes();
72+
ReadOnlySpan<byte> sDer = sequenceReader.ReadIntegerBytes();
73+
sequenceReader.ThrowIfNotEmpty();
74+
75+
CopySignatureField(rDer, destination.Slice(0, fieldSizeBytes));
76+
CopySignatureField(sDer, destination.Slice(fieldSizeBytes, fieldSizeBytes));
77+
return encodedSize;
78+
}
79+
catch (AsnContentException e)
80+
{
81+
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
82+
}
83+
}
84+
85+
private static void CopySignatureField(ReadOnlySpan<byte> signatureField, Span<byte> response)
86+
{
87+
if (signatureField.Length > response.Length)
88+
{
89+
if (signatureField.Length != response.Length + 1 ||
90+
signatureField[0] != 0 ||
91+
signatureField[1] <= 0x7F)
92+
{
93+
// The only way this should be true is if the value required a zero-byte-pad.
94+
Debug.Fail($"A signature field was longer ({signatureField.Length}) than expected ({response.Length})");
95+
throw new CryptographicException();
96+
}
97+
98+
signatureField = signatureField.Slice(1);
99+
}
100+
101+
// If the field is too short then it needs to be prepended
102+
// with zeroes in the response.
103+
int writeOffset = response.Length - signatureField.Length;
104+
response.Slice(0, writeOffset).Clear();
105+
signatureField.CopyTo(response.Slice(writeOffset));
106+
}
107+
10108
internal static int BitsToBytes(int bitLength)
11109
{
12110
int byteLength = (bitLength + 7) / 8;

src/libraries/Common/src/System/Security/Cryptography/CngHelpers.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ internal static CryptographicException ToCryptographicException(this Interop.NCr
1717
return ((int)errorCode).ToCryptographicException();
1818
}
1919

20+
internal static SafeNCryptProviderHandle OpenStorageProvider(this CngProvider provider)
21+
{
22+
ErrorCode errorCode = Interop.NCrypt.NCryptOpenStorageProvider(
23+
out SafeNCryptProviderHandle providerHandle,
24+
provider.Provider,
25+
0);
26+
27+
if (errorCode != ErrorCode.ERROR_SUCCESS)
28+
{
29+
providerHandle.Dispose();
30+
throw errorCode.ToCryptographicException();
31+
}
32+
33+
return providerHandle;
34+
}
35+
2036
internal static void SetExportPolicy(this SafeNCryptKeyHandle keyHandle, CngExportPolicies exportPolicy)
2137
{
2238
unsafe
@@ -125,6 +141,40 @@ internal static void SetExportPolicy(this SafeNCryptKeyHandle keyHandle, CngExpo
125141
}
126142
}
127143

144+
internal delegate void ExportKeyBlobCallback(ReadOnlySpan<byte> blob);
145+
146+
internal static void ExportKeyBlob(
147+
this SafeNCryptKeyHandle handle,
148+
string blobType,
149+
ExportKeyBlobCallback callback)
150+
{
151+
int numBytesNeeded;
152+
ErrorCode errorCode = Interop.NCrypt.NCryptExportKey(handle, IntPtr.Zero, blobType, IntPtr.Zero, null, 0, out numBytesNeeded, 0);
153+
if (errorCode != ErrorCode.ERROR_SUCCESS)
154+
throw errorCode.ToCryptographicException();
155+
156+
byte[] buffer = CryptoPool.Rent(numBytesNeeded);
157+
158+
try
159+
{
160+
using (PinAndClear.Track(buffer))
161+
{
162+
errorCode = Interop.NCrypt.NCryptExportKey(handle, IntPtr.Zero, blobType, IntPtr.Zero, buffer, buffer.Length, out numBytesNeeded, 0);
163+
if (errorCode != ErrorCode.ERROR_SUCCESS)
164+
throw errorCode.ToCryptographicException();
165+
166+
// The second call to NCryptExportKey should always return the same number of bytes as the first call,
167+
// but we will slice the buffer just in case.
168+
callback(buffer.AsSpan(0, numBytesNeeded));
169+
}
170+
}
171+
finally
172+
{
173+
// Already cleared by PinAndClear.Track above
174+
CryptoPool.Return(buffer, clearSize: 0);
175+
}
176+
}
177+
128178
internal static bool TryExportKeyBlob(
129179
this SafeNCryptKeyHandle handle,
130180
string blobType,

0 commit comments

Comments
 (0)