Skip to content

Commit bb06462

Browse files
committed
MLDsa initial OpenSSL based implementation
1 parent 62bd39a commit bb06462

28 files changed

+1173
-353
lines changed

src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Crypto.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using System.Globalization;
77
using System.Runtime.InteropServices;
8+
using System.Security.Cryptography;
89
using System.Security.Cryptography.X509Certificates;
910
using Microsoft.Win32.SafeHandles;
1011

@@ -102,6 +103,29 @@ private static partial int CryptoNative_X509StoreSetVerifyTime(
102103
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_CheckX509Hostname", StringMarshalling = StringMarshalling.Utf8)]
103104
internal static partial int CheckX509Hostname(SafeX509Handle x509, string hostname, int cchHostname);
104105

106+
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
107+
private static partial int CryptoNative_IsSignatureAlgorithmAvailable(string algorithm);
108+
109+
internal static string? IsSignatureAlgorithmAvailable(string algorithm)
110+
{
111+
const int Available = 1;
112+
const int NotAvailable = 0;
113+
114+
int ret = CryptoNative_IsSignatureAlgorithmAvailable(algorithm);
115+
return ret switch
116+
{
117+
Available => algorithm,
118+
NotAvailable => null,
119+
int other => throw Fail(other),
120+
};
121+
122+
static CryptographicException Fail(int result)
123+
{
124+
Debug.Fail($"Unexpected result {result} from {nameof(CryptoNative_IsSignatureAlgorithmAvailable)}");
125+
return new CryptographicException();
126+
}
127+
}
128+
105129
internal static byte[] GetAsn1StringBytes(IntPtr asn1)
106130
{
107131
return GetDynamicBuffer(GetAsn1StringBytes, asn1);

src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kem.cs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@ internal static void EvpKemDecapsulate(SafeEvpPKeyHandle key, ReadOnlySpan<byte>
107107
}
108108

109109
internal static void EvpKemExportPrivateSeed(SafeEvpPKeyHandle key, Span<byte> destination) =>
110-
ExportKeyContents(key, destination, CryptoNative_EvpKemExportPrivateSeed);
110+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_EvpKemExportPrivateSeed);
111111

112112
internal static void EvpKemExportDecapsulationKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
113-
ExportKeyContents(key, destination, CryptoNative_EvpKemExportDecapsulationKey);
113+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_EvpKemExportDecapsulationKey);
114114

115115
internal static void EvpKemExportEncapsulationKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
116-
ExportKeyContents(key, destination, CryptoNative_EvpKemExportEncapsulationKey);
116+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_EvpKemExportEncapsulationKey);
117117

118118
internal static void EvpKemEncapsulate(SafeEvpPKeyHandle key, Span<byte> ciphertext, Span<byte> sharedSecret)
119119
{
@@ -137,33 +137,5 @@ internal static void EvpKemEncapsulate(SafeEvpPKeyHandle key, Span<byte> ciphert
137137
throw new CryptographicException();
138138
}
139139
}
140-
141-
private static void ExportKeyContents(
142-
SafeEvpPKeyHandle key,
143-
Span<byte> destination,
144-
Func<SafeEvpPKeyHandle, Span<byte>, int, int> action)
145-
{
146-
const int Success = 1;
147-
const int Fail = 0;
148-
const int NotRetrievable = -1;
149-
150-
int ret = action(key, destination, destination.Length);
151-
152-
switch (ret)
153-
{
154-
case Success:
155-
return;
156-
case NotRetrievable:
157-
destination.Clear();
158-
throw new CryptographicException(SR.Cryptography_NotRetrievable);
159-
case Fail:
160-
destination.Clear();
161-
throw CreateOpenSslCryptographicException();
162-
default:
163-
destination.Clear();
164-
Debug.Fail($"Unexpected return value {ret}.");
165-
throw new CryptographicException();
166-
}
167-
}
168140
}
169141
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Runtime.InteropServices;
8+
using System.Security.Cryptography;
9+
using Microsoft.Win32.SafeHandles;
10+
11+
internal static partial class Interop
12+
{
13+
internal static partial class Crypto
14+
{
15+
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
16+
private static partial SafeEvpPKeyHandle CryptoNative_MLDsaGenerateKey(string keyType, ReadOnlySpan<byte> seed, int seedLength);
17+
18+
internal static SafeEvpPKeyHandle MLDsaGenerateKey(string algorithmName, ReadOnlySpan<byte> seed)
19+
{
20+
SafeEvpPKeyHandle handle = CryptoNative_MLDsaGenerateKey(algorithmName, seed, seed.Length);
21+
Debug.Assert(handle != null, "handle != null");
22+
23+
if (handle.IsInvalid)
24+
{
25+
Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
26+
handle.Dispose();
27+
throw ex;
28+
}
29+
30+
return handle;
31+
}
32+
33+
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
34+
private static partial SafeEvpPKeyHandle CryptoNative_MLDsaImportSecretKey(string keyType, ReadOnlySpan<byte> sk, int skLength);
35+
36+
internal static SafeEvpPKeyHandle MLDsaImportSecretKey(string algorithmName, ReadOnlySpan<byte> sk)
37+
{
38+
SafeEvpPKeyHandle? handle = CryptoNative_MLDsaImportSecretKey(algorithmName, sk, sk.Length);
39+
Debug.Assert(handle != null, "handle != null");
40+
41+
if (handle.IsInvalid)
42+
{
43+
Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
44+
handle.Dispose();
45+
throw ex;
46+
}
47+
48+
return handle;
49+
}
50+
51+
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
52+
private static partial SafeEvpPKeyHandle CryptoNative_MLDsaImportPublicKey(string keyType, ReadOnlySpan<byte> pk, int pkLength);
53+
54+
internal static SafeEvpPKeyHandle MLDsaImportPublicKey(string algorithmName, ReadOnlySpan<byte> pk)
55+
{
56+
SafeEvpPKeyHandle handle = CryptoNative_MLDsaImportPublicKey(algorithmName, pk, pk.Length);
57+
Debug.Assert(handle != null, "handle != null");
58+
59+
if (handle.IsInvalid)
60+
{
61+
Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
62+
handle.Dispose();
63+
throw ex;
64+
}
65+
66+
return handle;
67+
}
68+
69+
[LibraryImport(Libraries.CryptoNative)]
70+
private static partial int CryptoNative_MLDsaSignPure(
71+
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
72+
ReadOnlySpan<byte> msg, int msgLength,
73+
ReadOnlySpan<byte> context, int contextLength,
74+
Span<byte> destination, int destinationLength);
75+
76+
internal static void MLDsaSignPure(
77+
SafeEvpPKeyHandle pkey,
78+
ReadOnlySpan<byte> msg,
79+
ReadOnlySpan<byte> context,
80+
Span<byte> destination)
81+
{
82+
int ret = CryptoNative_MLDsaSignPure(
83+
pkey, pkey.ExtraHandle,
84+
msg, msg.Length,
85+
context, context.Length,
86+
destination, destination.Length);
87+
88+
if (ret != 1)
89+
{
90+
throw Interop.Crypto.CreateOpenSslCryptographicException();
91+
}
92+
}
93+
94+
[LibraryImport(Libraries.CryptoNative)]
95+
private static partial int CryptoNative_MLDsaVerifyPure(
96+
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
97+
ReadOnlySpan<byte> msg, int msgLength,
98+
ReadOnlySpan<byte> context, int contextLength,
99+
ReadOnlySpan<byte> signature, int signatureLength);
100+
101+
internal static bool MLDsaVerifyPure(
102+
SafeEvpPKeyHandle pkey,
103+
ReadOnlySpan<byte> msg,
104+
ReadOnlySpan<byte> context,
105+
ReadOnlySpan<byte> signature)
106+
{
107+
int ret = CryptoNative_MLDsaVerifyPure(
108+
pkey, pkey.ExtraHandle,
109+
msg, msg.Length,
110+
context, context.Length,
111+
signature, signature.Length);
112+
113+
if (ret == 1)
114+
{
115+
return true;
116+
}
117+
else if (ret == 0)
118+
{
119+
return false;
120+
}
121+
else
122+
{
123+
throw Interop.Crypto.CreateOpenSslCryptographicException();
124+
}
125+
}
126+
127+
[LibraryImport(Libraries.CryptoNative)]
128+
private static partial int CryptoNative_MLDsaExportSecretKey(SafeEvpPKeyHandle pkey, Span<byte> destination, int destinationLength);
129+
130+
[LibraryImport(Libraries.CryptoNative)]
131+
private static partial int CryptoNative_MLDsaExportSeed(SafeEvpPKeyHandle pkey, Span<byte> destination, int destinationLength);
132+
133+
[LibraryImport(Libraries.CryptoNative)]
134+
private static partial int CryptoNative_MLDsaExportPublicKey(SafeEvpPKeyHandle pkey, Span<byte> destination, int destinationLength);
135+
136+
internal static void MLDsaExportSecretKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
137+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_MLDsaExportSecretKey);
138+
139+
internal static void MLDsaExportSeed(SafeEvpPKeyHandle key, Span<byte> destination) =>
140+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_MLDsaExportSeed);
141+
142+
internal static void MLDsaExportPublicKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
143+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_MLDsaExportPublicKey);
144+
}
145+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Runtime.InteropServices;
8+
using System.Security.Cryptography;
9+
using Microsoft.Win32.SafeHandles;
10+
11+
internal static partial class Interop
12+
{
13+
internal static partial class Crypto
14+
{
15+
internal static partial class EvpPKeyMLDsaAlgs
16+
{
17+
internal static string? MLDsa44 { get; }
18+
internal static string? MLDsa65 { get; }
19+
internal static string? MLDsa87 { get; }
20+
21+
static EvpPKeyMLDsaAlgs()
22+
{
23+
CryptoInitializer.Initialize();
24+
25+
// Do not use property initializers for these because we need to ensure CryptoInitializer.Initialize
26+
// is called first. Property initializers happen before cctors, so instead set the property after the
27+
// initializer is run.
28+
MLDsa44 = IsSignatureAlgorithmAvailable(MLDsaAlgorithm.MLDsa44.Name);
29+
MLDsa65 = IsSignatureAlgorithmAvailable(MLDsaAlgorithm.MLDsa65.Name);
30+
MLDsa87 = IsSignatureAlgorithmAvailable(MLDsaAlgorithm.MLDsa87.Name);
31+
}
32+
}
33+
}
34+
}

src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,34 @@ internal static SafeEvpPKeyHandle LoadKeyFromProvider(
341341
}
342342
}
343343

344+
internal static void ExportKeyContents(
345+
SafeEvpPKeyHandle key,
346+
Span<byte> destination,
347+
Func<SafeEvpPKeyHandle, Span<byte>, int, int> action)
348+
{
349+
const int Success = 1;
350+
const int Fail = 0;
351+
const int NotRetrievable = -1;
352+
353+
int ret = action(key, destination, destination.Length);
354+
355+
switch (ret)
356+
{
357+
case Success:
358+
return;
359+
case NotRetrievable:
360+
destination.Clear();
361+
throw new CryptographicException(SR.Cryptography_NotRetrievable);
362+
case Fail:
363+
destination.Clear();
364+
throw CreateOpenSslCryptographicException();
365+
default:
366+
destination.Clear();
367+
Debug.Fail($"Unexpected return value {ret}.");
368+
throw new CryptographicException();
369+
}
370+
}
371+
344372
internal enum EvpAlgorithmId
345373
{
346374
Unknown = 0,

0 commit comments

Comments
 (0)