Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ML-KEM: OpenSSL Part 1 #113719

Merged
merged 50 commits into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ebb5a1a
Initial scaffolding and algorithm fetching
vcsjones Mar 18, 2025
122010a
Key generation
vcsjones Mar 18, 2025
e541327
Allow importing seeds into ML-KEM
vcsjones Mar 18, 2025
b3dd3ba
Encapsulate
vcsjones Mar 18, 2025
dc93837
Public fields. Maybe a good idea
vcsjones Mar 18, 2025
b421b4a
Export decapsulation key
vcsjones Mar 18, 2025
8e603a3
Export encaps key
vcsjones Mar 18, 2025
50e57db
NIST vectors for generation
vcsjones Mar 19, 2025
dfdb8e4
Minor tweaks
vcsjones Mar 19, 2025
8400cea
Round trip on encapsulation and decapsulation
vcsjones Mar 19, 2025
d1d6b68
Reorder
vcsjones Mar 19, 2025
cc76d55
Merge remote-tracking branch 'ms/main' into ml-kem
vcsjones Mar 19, 2025
5c69aa3
Rework some key import and export things
vcsjones Mar 19, 2025
a6670c1
Encapsulation roundtrip
vcsjones Mar 19, 2025
3e56b46
Import Decapsulation key
vcsjones Mar 19, 2025
6d568d7
Check encapsulation key
vcsjones Mar 19, 2025
da5b60d
More tests and resource strings
vcsjones Mar 19, 2025
f9aa1be
Keep working on tests
vcsjones Mar 19, 2025
90b0ac5
Actual encapsulation tests
vcsjones Mar 19, 2025
5a36083
Get tests building in OOB
vcsjones Mar 19, 2025
c647045
Improve test execution time
vcsjones Mar 19, 2025
970c144
Remove redundant MLKem from member names
vcsjones Mar 19, 2025
f0a8ea0
Add tests for MLKemAlgorithm
vcsjones Mar 19, 2025
0b6d963
Test and prohibit overlapping buffers
vcsjones Mar 20, 2025
ff75b39
Fixup docs
vcsjones Mar 20, 2025
dc96bae
Fix doc contents
vcsjones Mar 20, 2025
981750c
Remove duplicate define
vcsjones Mar 20, 2025
9757879
Fix error queue handling
vcsjones Mar 20, 2025
c257e5e
Fix build downlevel
vcsjones Mar 20, 2025
4915388
Merge branch 'main' into ml-kem
vcsjones Mar 20, 2025
393b11f
Fixup compat
vcsjones Mar 20, 2025
703ac70
Remove held EVP-KEM handles.
vcsjones Mar 20, 2025
8d3da2d
Make key importing generic for any algorithm using fromdata
vcsjones Mar 20, 2025
d0fce2c
Remove experimental downlevel
vcsjones Mar 20, 2025
1819618
Simplify ML-KEM availability check.
vcsjones Mar 20, 2025
74f8a67
Re-add ExperimentalAttribute as internal on downlevel platforms
vcsjones Mar 20, 2025
5df8232
Code review feedback.
vcsjones Mar 20, 2025
fe5a6ad
Fix typos.
vcsjones Mar 20, 2025
720722a
Remove Dispose checks from Core. The public APIs do that already
vcsjones Mar 20, 2025
c790ffa
Code review feedback
vcsjones Mar 20, 2025
0f35e5a
Move seed size to MLKemAlgorithm
vcsjones Mar 20, 2025
9f929c4
Fixup docs from code review
vcsjones Mar 20, 2025
96c5388
Add comment about test vector source
vcsjones Mar 20, 2025
9056df5
Add key gen tests that have zero prefixes
vcsjones Mar 20, 2025
72eafa4
Add zero vectors for encap and decap
vcsjones Mar 20, 2025
abbfe6d
Run clang-format
vcsjones Mar 20, 2025
19fc181
Merge remote-tracking branch 'ms/main' into ml-kem
vcsjones Mar 20, 2025
7f5e97f
Fix formatting in header
vcsjones Mar 20, 2025
6b40a16
Merge remote-tracking branch 'ms/main' into ml-kem
vcsjones Mar 20, 2025
edac6bf
Actually fix the thing I said I fixed
vcsjones Mar 20, 2025
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
@@ -0,0 +1,169 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class Crypto
{
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemDecapsulate")]
private static partial int CryptoNative_EvpKemDecapsulate(
SafeEvpPKeyHandle kem,
ReadOnlySpan<byte> ciphertext,
int ciphertextLength,
Span<byte> sharedSecret,
int sharedSecretLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemGeneratePkey", StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpPKeyHandle CryptoNative_EvpKemGeneratePkey(
string kemName,
ReadOnlySpan<byte> seed,
int seedLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemExportPrivateSeed")]
private static partial int CryptoNative_EvpKemExportPrivateSeed(
SafeEvpPKeyHandle key,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemExportDecapsulationKey")]
private static partial int CryptoNative_EvpKemExportDecapsulationKey(
SafeEvpPKeyHandle key,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemExportEncapsulationKey")]
private static partial int CryptoNative_EvpKemExportEncapsulationKey(
SafeEvpPKeyHandle key,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemEncapsulate")]
private static partial int CryptoNative_EvpKemEncapsulate(
SafeEvpPKeyHandle kem,
Span<byte> ciphertext,
int ciphertextLength,
Span<byte> sharedSecret,
int sharedSecretLength);

internal static SafeEvpPKeyHandle EvpKemGeneratePkey(string kemName)
{
SafeEvpPKeyHandle handle = CryptoNative_EvpKemGeneratePkey(kemName, ReadOnlySpan<byte>.Empty, 0);

if (handle.IsInvalid)
{
Exception ex = CreateOpenSslCryptographicException();
handle.Dispose();
throw ex;
}

return handle;
}

internal static SafeEvpPKeyHandle EvpKemGeneratePkey(string kemName, ReadOnlySpan<byte> seed)
{
if (seed.IsEmpty)
{
Debug.Fail("Generating a key with a seed requires a non-empty seed.");
throw new CryptographicException();
}

SafeEvpPKeyHandle handle = CryptoNative_EvpKemGeneratePkey(kemName, seed, seed.Length);

if (handle.IsInvalid)
{
Exception ex = CreateOpenSslCryptographicException();
handle.Dispose();
throw ex;
}

return handle;
}

internal static void EvpKemDecapsulate(SafeEvpPKeyHandle key, ReadOnlySpan<byte> ciphertext, Span<byte> sharedSecret)
{
const int Success = 1;
const int Fail = 0;

int ret = CryptoNative_EvpKemDecapsulate(key, ciphertext, ciphertext.Length, sharedSecret, sharedSecret.Length);

switch (ret)
{
case Success:
return;
case Fail:
sharedSecret.Clear();
throw CreateOpenSslCryptographicException();
default:
sharedSecret.Clear();
Debug.Fail($"Unexpected return value {ret} from {nameof(CryptoNative_EvpKemDecapsulate)}.");
throw new CryptographicException();
}
}

internal static void EvpKemExportPrivateSeed(SafeEvpPKeyHandle key, Span<byte> destination) =>
ExportKeyContents(key, destination, CryptoNative_EvpKemExportPrivateSeed);

internal static void EvpKemExportDecapsulationKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
ExportKeyContents(key, destination, CryptoNative_EvpKemExportDecapsulationKey);

internal static void EvpKemExportEncapsulationKey(SafeEvpPKeyHandle key, Span<byte> destination) =>
ExportKeyContents(key, destination, CryptoNative_EvpKemExportEncapsulationKey);

internal static void EvpKemEncapsulate(SafeEvpPKeyHandle key, Span<byte> ciphertext, Span<byte> sharedSecret)
{
const int Success = 1;
const int Fail = 0;

int ret = CryptoNative_EvpKemEncapsulate(key, ciphertext, ciphertext.Length, sharedSecret, sharedSecret.Length);

switch (ret)
{
case Success:
return;
case Fail:
ciphertext.Clear();
sharedSecret.Clear();
throw CreateOpenSslCryptographicException();
default:
ciphertext.Clear();
sharedSecret.Clear();
Debug.Fail($"Unexpected return value {ret} from {nameof(CryptoNative_EvpKemEncapsulate)}.");
throw new CryptographicException();
}
}

private static void ExportKeyContents(
SafeEvpPKeyHandle key,
Span<byte> destination,
Func<SafeEvpPKeyHandle, Span<byte>, int, int> action)
{
const int Success = 1;
const int Fail = 0;
const int NotRetrievable = -1;

int ret = action(key, destination, destination.Length);

switch (ret)
{
case Success:
return;
case NotRetrievable:
destination.Clear();
throw new CryptographicException(SR.Cryptography_NotRetrievable);
case Fail:
destination.Clear();
throw CreateOpenSslCryptographicException();
default:
destination.Clear();
Debug.Fail($"Unexpected return value {ret}.");
throw new CryptographicException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.Runtime.InteropServices;
using System.Security.Cryptography;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class Crypto
{
internal static partial class EvpKemAlgs
{
internal static string? MlKem512 { get; }
internal static string? MlKem768 { get; }
internal static string? MlKem1024 { get; }

static EvpKemAlgs()
{
CryptoInitializer.Initialize();

// Do not use property initializers for these because we need to ensure CryptoInitializer.Initialize
// is called first. Property initializers happen before cctors, so instead set the property after the
// initializer is run.
MlKem512 = EvpKemAvailable(MLKemAlgorithm.MLKem512.Name);
MlKem768 = EvpKemAvailable(MLKemAlgorithm.MLKem768.Name);
MlKem1024 = EvpKemAvailable(MLKemAlgorithm.MLKem1024.Name);
}

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemAvailable", StringMarshalling = StringMarshalling.Utf8)]
private static partial int CryptoNative_EvpKemAvailable(string algorithm);

private static string? EvpKemAvailable(string algorithm)
{
const int Available = 1;
const int NotAvailable = 0;

int ret = CryptoNative_EvpKemAvailable(algorithm);
return ret switch
{
Available => algorithm,
NotAvailable => null,
int other => throw Fail(other),
};

static CryptographicException Fail(int result)
{
Debug.Fail($"Unexpected result {result} from {nameof(CryptoNative_EvpKemAvailable)}");
return new CryptographicException();
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@ internal static partial class Crypto
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyBits")]
internal static partial int EvpPKeyBits(SafeEvpPKeyHandle pkey);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyFromData", StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpPKeyHandle CryptoNative_EvpPKeyFromData(
string algorithmName,
ReadOnlySpan<byte> key,
int keyLength,
[MarshalAs(UnmanagedType.Bool)] bool privateKey);

internal static SafeEvpPKeyHandle EvpPKeyFromData(string algorithmName, ReadOnlySpan<byte> key, bool privateKey)
{
SafeEvpPKeyHandle handle = CryptoNative_EvpPKeyFromData(algorithmName, key, key.Length, privateKey);

if (handle.IsInvalid)
{
Exception ex = CreateOpenSslCryptographicException();
handle.Dispose();
throw ex;
}

return handle;
}

internal static int GetEvpPKeySizeBytes(SafeEvpPKeyHandle pkey)
{
// EVP_PKEY_size returns the maximum suitable size for the output buffers for almost all operations that can be done with the key.
Expand Down
Loading
Loading