Skip to content

Commit 15bcd62

Browse files
SLH-DSA implementation for OpenSSL (#114060)
Co-authored-by: Jeremy Barton <[email protected]>
1 parent e1702c3 commit 15bcd62

File tree

53 files changed

+2349
-660
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2349
-660
lines changed

docs/workflow/building/libraries/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ The libraries build contains some native code. This includes shims over libc, op
128128

129129
- Building and updating the binplace (for e.g. the testhost), which is needed when iterating on native components
130130
```bash
131-
dotnet.sh build src/native/libraries/build-native.proj
131+
dotnet.sh build src/native/libs/build-native.proj
132132
```
133133

134134
- The following example shows how you would do an arm cross-compile build

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ internal static partial class Interop
1111
{
1212
internal static partial class Crypto
1313
{
14+
/// <summary>
15+
/// Gets the extra handle associated with the EVP_PKEY. Some tests need to access
16+
/// the interop layer and achieve this by adding the relevant classes to the test
17+
/// project as links. However, accesses to internal members like <see cref="SafeEvpPKeyHandle.ExtraHandle"/>
18+
/// in the product project will not work in the test project. In this particular case,
19+
/// the test project does not need the value of the handle, so it can implement this
20+
/// method to return a null pointer.
21+
/// </summary>
22+
/// <param name="handle">
23+
/// The extra handle associated with the EVP_PKEY.</param>
24+
/// <returns>
25+
/// The extra handle associated with the EVP_PKEY.
26+
/// </returns>
1427
private static partial IntPtr GetExtraHandle(SafeEvpPKeyHandle handle);
1528

1629
// Must be kept in sync with PalKemId in native shim.

src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPKey.MLDsa.cs renamed to src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.MLDsa.cs

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -64,42 +64,6 @@ internal static SafeEvpPKeyHandle MLDsaGenerateKey(string algorithmName, ReadOnl
6464
return handle;
6565
}
6666

67-
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
68-
private static partial SafeEvpPKeyHandle CryptoNative_MLDsaImportSecretKey(string keyType, ReadOnlySpan<byte> sk, int skLength);
69-
70-
internal static SafeEvpPKeyHandle MLDsaImportSecretKey(string algorithmName, ReadOnlySpan<byte> sk)
71-
{
72-
SafeEvpPKeyHandle? handle = CryptoNative_MLDsaImportSecretKey(algorithmName, sk, sk.Length);
73-
Debug.Assert(handle != null, "handle != null");
74-
75-
if (handle.IsInvalid)
76-
{
77-
Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
78-
handle.Dispose();
79-
throw ex;
80-
}
81-
82-
return handle;
83-
}
84-
85-
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
86-
private static partial SafeEvpPKeyHandle CryptoNative_MLDsaImportPublicKey(string keyType, ReadOnlySpan<byte> pk, int pkLength);
87-
88-
internal static SafeEvpPKeyHandle MLDsaImportPublicKey(string algorithmName, ReadOnlySpan<byte> pk)
89-
{
90-
SafeEvpPKeyHandle handle = CryptoNative_MLDsaImportPublicKey(algorithmName, pk, pk.Length);
91-
Debug.Assert(handle != null, "handle != null");
92-
93-
if (handle.IsInvalid)
94-
{
95-
Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
96-
handle.Dispose();
97-
throw ex;
98-
}
99-
100-
return handle;
101-
}
102-
10367
[LibraryImport(Libraries.CryptoNative)]
10468
private static partial int CryptoNative_MLDsaSignPure(
10569
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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_SlhDsaGenerateKey(string keyType);
17+
18+
internal static SafeEvpPKeyHandle SlhDsaGenerateKey(string algorithmName)
19+
{
20+
SafeEvpPKeyHandle handle = CryptoNative_SlhDsaGenerateKey(algorithmName);
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+
// Must be kept in sync with PalSlhDsaId in native shim.
34+
internal enum PalSlhDsaAlgorithmId
35+
{
36+
Unknown = 0,
37+
SlhDsaSha2_128s = 1,
38+
SlhDsaShake128s = 2,
39+
SlhDsaSha2_128f = 3,
40+
SlhDsaShake128f = 4,
41+
SlhDsaSha2_192s = 5,
42+
SlhDsaShake192s = 6,
43+
SlhDsaSha2_192f = 7,
44+
SlhDsaShake192f = 8,
45+
SlhDsaSha2_256s = 9,
46+
SlhDsaShake256s = 10,
47+
SlhDsaSha2_256f = 11,
48+
SlhDsaShake256f = 12,
49+
}
50+
51+
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SlhDsaGetPalId")]
52+
private static partial int CryptoNative_SlhDsaGetPalId(
53+
SafeEvpPKeyHandle slhDsa,
54+
out PalSlhDsaAlgorithmId slhDsaId);
55+
56+
internal static PalSlhDsaAlgorithmId GetSlhDsaAlgorithmId(SafeEvpPKeyHandle key)
57+
{
58+
const int Success = 1;
59+
const int Fail = 0;
60+
int result = CryptoNative_SlhDsaGetPalId(key, out PalSlhDsaAlgorithmId slhDsaId);
61+
62+
return result switch
63+
{
64+
Success => slhDsaId,
65+
Fail => throw CreateOpenSslCryptographicException(),
66+
int other => throw FailThrow(other),
67+
};
68+
69+
static Exception FailThrow(int result)
70+
{
71+
Debug.Fail($"Unexpected return value {result} from {nameof(CryptoNative_SlhDsaGetPalId)}.");
72+
return new CryptographicException();
73+
}
74+
}
75+
76+
[LibraryImport(Libraries.CryptoNative)]
77+
private static partial int CryptoNative_SlhDsaSignPure(
78+
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
79+
ReadOnlySpan<byte> msg, int msgLength,
80+
ReadOnlySpan<byte> context, int contextLength,
81+
Span<byte> destination, int destinationLength);
82+
83+
internal static void SlhDsaSignPure(
84+
SafeEvpPKeyHandle pkey,
85+
ReadOnlySpan<byte> msg,
86+
ReadOnlySpan<byte> context,
87+
Span<byte> destination)
88+
{
89+
int ret = CryptoNative_SlhDsaSignPure(
90+
pkey, GetExtraHandle(pkey),
91+
msg, msg.Length,
92+
context, context.Length,
93+
destination, destination.Length);
94+
95+
if (ret != 1)
96+
{
97+
throw Interop.Crypto.CreateOpenSslCryptographicException();
98+
}
99+
}
100+
101+
[LibraryImport(Libraries.CryptoNative)]
102+
private static partial int CryptoNative_SlhDsaVerifyPure(
103+
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
104+
ReadOnlySpan<byte> msg, int msgLength,
105+
ReadOnlySpan<byte> context, int contextLength,
106+
ReadOnlySpan<byte> signature, int signatureLength);
107+
108+
internal static bool SlhDsaVerifyPure(
109+
SafeEvpPKeyHandle pkey,
110+
ReadOnlySpan<byte> msg,
111+
ReadOnlySpan<byte> context,
112+
ReadOnlySpan<byte> signature)
113+
{
114+
int ret = CryptoNative_SlhDsaVerifyPure(
115+
pkey, GetExtraHandle(pkey),
116+
msg, msg.Length,
117+
context, context.Length,
118+
signature, signature.Length);
119+
120+
if (ret == 1)
121+
{
122+
return true;
123+
}
124+
else if (ret == 0)
125+
{
126+
return false;
127+
}
128+
else
129+
{
130+
throw Interop.Crypto.CreateOpenSslCryptographicException();
131+
}
132+
}
133+
134+
[LibraryImport(Libraries.CryptoNative)]
135+
private static partial int CryptoNative_SlhDsaExportSecretKey(SafeEvpPKeyHandle pkey, Span<byte> destination, int destinationLength);
136+
137+
[LibraryImport(Libraries.CryptoNative)]
138+
private static partial int CryptoNative_SlhDsaExportPublicKey(SafeEvpPKeyHandle pkey, Span<byte> destination, int destinationLength);
139+
140+
internal static void SlhDsaExportSecretKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
141+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_SlhDsaExportSecretKey);
142+
143+
internal static void SlhDsaExportPublicKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
144+
Interop.Crypto.ExportKeyContents(key, destination, CryptoNative_SlhDsaExportPublicKey);
145+
}
146+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 EvpPKeySlhDsaAlgs
16+
{
17+
internal static string? SlhDsaSha2_128s { get; }
18+
internal static string? SlhDsaShake128s { get; }
19+
internal static string? SlhDsaSha2_128f { get; }
20+
internal static string? SlhDsaShake128f { get; }
21+
internal static string? SlhDsaSha2_192s { get; }
22+
internal static string? SlhDsaShake192s { get; }
23+
internal static string? SlhDsaSha2_192f { get; }
24+
internal static string? SlhDsaShake192f { get; }
25+
internal static string? SlhDsaSha2_256s { get; }
26+
internal static string? SlhDsaShake256s { get; }
27+
internal static string? SlhDsaSha2_256f { get; }
28+
internal static string? SlhDsaShake256f { get; }
29+
30+
static EvpPKeySlhDsaAlgs()
31+
{
32+
CryptoInitializer.Initialize();
33+
34+
// Do not use property initializers for these because we need to ensure CryptoInitializer.Initialize
35+
// is called first. Property initializers happen before cctors, so instead set the property after the
36+
// initializer is run.
37+
SlhDsaSha2_128s = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaSha2_128s.Name);
38+
SlhDsaShake128s = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaShake128s.Name);
39+
SlhDsaSha2_128f = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaSha2_128f.Name);
40+
SlhDsaShake128f = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaShake128f.Name);
41+
SlhDsaSha2_192s = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaSha2_192s.Name);
42+
SlhDsaShake192s = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaShake192s.Name);
43+
SlhDsaSha2_192f = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaSha2_192f.Name);
44+
SlhDsaShake192f = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaShake192f.Name);
45+
SlhDsaSha2_256s = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaSha2_256s.Name);
46+
SlhDsaShake256s = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaShake256s.Name);
47+
SlhDsaSha2_256f = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaSha2_256f.Name);
48+
SlhDsaShake256f = IsSignatureAlgorithmAvailable(SlhDsaAlgorithm.SlhDsaShake256f.Name);
49+
}
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)