Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions docs/project/list-of-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,4 @@ Diagnostic id values for experimental APIs must not be recycled, as that could s
| __`SYSLIB5006`__ | .NET 10 | TBD | Types for Post-Quantum Cryptography (PQC) are experimental. |
| __`SYSLIB5007`__ | .NET 11 | TBD | Low-level TLS engine types (`TlsContext`, `TlsSession`) in `System.Net.Security` are experimental. |
| __`SYSLIB5008`__ | .NET 11 | TBD | `SocketsHttpHandler` connection eviction control and `HttpRequestMessage.ConnectionId` APIs are experimental. |
| __`SYSLIB5009`__ | .NET 11 | TBD | Types for HPKE (Hybrid Public Key Encryption) are experimental. |
3 changes: 3 additions & 0 deletions src/libraries/Common/src/System/Experimentals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ internal static class Experimentals
// SocketsHttpHandler connection eviction control and HttpRequestMessage.ConnectionId APIs are experimental.
internal const string SocketsHttpHandlerExperimentalDiagId = "SYSLIB5008";

// Types for HPKE (Hybrid Public Key Encryption) are experimental.
internal const string HpkeExperimentalDiagId = "SYSLIB5009";

// When adding a new diagnostic ID, add it to the table in docs\project\list-of-diagnostics.md as well.
// Keep new const identifiers above this comment.
}
Expand Down
1,519 changes: 1,519 additions & 0 deletions src/libraries/Common/src/System/Security/Cryptography/Hpke.cs

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions src/libraries/Common/src/System/Security/Cryptography/HpkeAead.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.CodeAnalysis;

namespace System.Security.Cryptography
{
/// <summary>
/// Specifies an authenticated encryption with associated data (AEAD) algorithm for an HPKE cipher suite.
/// </summary>
/// <seealso cref="HpkeSuite" />
[Experimental(Experimentals.HpkeExperimentalDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public enum HpkeAead
{
/// <summary>
/// Indicates that authenticated encryption uses AES-GCM with a 128-bit key.
/// </summary>
AES_128_GCM = 0x0001,

/// <summary>
/// Indicates that authenticated encryption uses AES-GCM with a 256-bit key.
/// </summary>
AES_256_GCM = 0x0002,

/// <summary>
/// Indicates that authenticated encryption uses ChaCha20-Poly1305.
/// </summary>
ChaCha20Poly1305 = 0x0003,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Security.Cryptography
{
internal sealed partial class HpkeAeadMetadata
{
internal HpkeAead Aead { get; }
internal int Nk { get; }
internal int Nn { get; }
internal int Nt { get; }
internal string Name { get; }

private HpkeAeadMetadata(HpkeAead aead, int nk, int nn, int nt, string name)
{
Aead = aead;
Nk = nk;
Nn = nn;
Nt = nt;
Name = name;
}

internal static HpkeAeadMetadata? Create(HpkeAead aead)
{
// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#section-7.3
switch (aead)
{
case HpkeAead.AES_128_GCM:
return new HpkeAeadMetadata(aead, nk: 16, nn: 12, nt: 16, name: "AES-128-GCM");
case HpkeAead.AES_256_GCM:
return new HpkeAeadMetadata(aead, nk: 32, nn: 12, nt: 16, name: "AES-256-GCM");
case HpkeAead.ChaCha20Poly1305:
return new HpkeAeadMetadata(aead, nk: 32, nn: 12, nt: 16, name: "ChaCha20Poly1305");
default:
return null;
}
}
}
}
40 changes: 40 additions & 0 deletions src/libraries/Common/src/System/Security/Cryptography/HpkeKdf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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.CodeAnalysis;

namespace System.Security.Cryptography
{
/// <summary>
/// Specifies a key derivation function (KDF) for an HPKE cipher suite.
/// </summary>
/// <seealso cref="HpkeSuite" />
[Experimental(Experimentals.HpkeExperimentalDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public enum HpkeKdf
{
/// <summary>
/// Indicates that key derivation uses HKDF with SHA-256.
/// </summary>
HKDF_SHA256 = 0x0001,

/// <summary>
/// Indicates that key derivation uses HKDF with SHA-384.
/// </summary>
HKDF_SHA384 = 0x0002,

/// <summary>
/// Indicates that key derivation uses HKDF with SHA-512.
/// </summary>
HKDF_SHA512 = 0x0003,

/// <summary>
/// Indicates that key derivation uses SHAKE128.
/// </summary>
SHAKE128 = 0x0010,

/// <summary>
/// Indicates that key derivation uses SHAKE256.
/// </summary>
SHAKE256 = 0x0011
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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;

namespace System.Security.Cryptography
{
internal sealed partial class HpkeKdfMetadata
{
internal HpkeKdf Kdf { get; }
internal int Nh { get; }
internal bool IsTwoStage { get; }
internal string Name { get; }
internal int MaximumExporterContextLength { get; }
internal int MaximumInfoLength { get; }
internal int MaximumPskLength { get; }
internal int MaximumPskIdLength { get; }

// HKDF is limited to 255 hash blocks; HPKE encodes SHAKE output lengths in two bytes.
// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#section-4.4
internal int MaximumExportLength => IsTwoStage ? 255 * Nh : ushort.MaxValue;

private HpkeKdfMetadata(HpkeKdf kdf, int nh, bool isTwoStage, string name)
{
Debug.Assert(nh <= 64, "Nh value is larger than 64.");

Kdf = kdf;
Nh = nh;
IsTwoStage = isTwoStage;
Name = name;
MaximumExporterContextLength = Hpke.MaximumInputSizeInBytes;

if (IsTwoStage)
{
MaximumInfoLength = Hpke.MaximumInputSizeInBytes;
MaximumPskLength = Hpke.MaximumInputSizeInBytes;
MaximumPskIdLength = Hpke.MaximumInputSizeInBytes;
}
else
{
// One-stage KDFs length-prefix each of these inputs with a 16-bit length.
// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#section-5.1
MaximumInfoLength = ushort.MaxValue;
MaximumPskLength = ushort.MaxValue;
MaximumPskIdLength = ushort.MaxValue;
}
}

internal static HpkeKdfMetadata? Create(HpkeKdf kdf)
{
switch (kdf)
{
// HKDF's limits exceed the maximum input size supported by the HPKE API.
// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#section-7.2
case HpkeKdf.HKDF_SHA256:
return new HpkeKdfMetadata(kdf, nh: 32, isTwoStage: true, name: "HKDF-SHA256");
case HpkeKdf.HKDF_SHA384:
return new HpkeKdfMetadata(kdf, nh: 48, isTwoStage: true, name: "HKDF-SHA384");
case HpkeKdf.HKDF_SHA512:
return new HpkeKdfMetadata(kdf, nh: 64, isTwoStage: true, name: "HKDF-SHA512");
case HpkeKdf.SHAKE128:
return new HpkeKdfMetadata(kdf, nh: 32, isTwoStage: false, name: "SHAKE128");
case HpkeKdf.SHAKE256:
return new HpkeKdfMetadata(kdf, nh: 64, isTwoStage: false, name: "SHAKE256");

default:
return null;
}
}
}
}
60 changes: 60 additions & 0 deletions src/libraries/Common/src/System/Security/Cryptography/HpkeKem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.CodeAnalysis;

namespace System.Security.Cryptography
{
/// <summary>
/// Specifies a key encapsulation mechanism (KEM) for an HPKE cipher suite.
/// </summary>
/// <seealso cref="HpkeSuite" />
[Experimental(Experimentals.HpkeExperimentalDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public enum HpkeKem
{
/// <summary>
/// Indicates that key encapsulation uses DHKEM with the NIST P-256 curve and HKDF-SHA-256.
/// </summary>
DHKEM_P256_HKDF_SHA256 = 0x0010,

/// <summary>
/// Indicates that key encapsulation uses DHKEM with the NIST P-384 curve and HKDF-SHA-384.
/// </summary>
DHKEM_P384_HKDF_SHA384 = 0x0011,

/// <summary>
/// Indicates that key encapsulation uses DHKEM with the NIST P-521 curve and HKDF-SHA-512.
/// </summary>
DHKEM_P521_HKDF_SHA512 = 0x0012,

/// <summary>
/// Indicates that key encapsulation uses DHKEM with X25519 and HKDF-SHA-256.
/// </summary>
DHKEM_X25519_HKDF_SHA256 = 0x0020,

/// <summary>
/// Indicates that key encapsulation uses ML-KEM-512.
/// </summary>
MLKEM_512 = 0x0040,

/// <summary>
/// Indicates that key encapsulation uses ML-KEM-768.
/// </summary>
MLKEM_768 = 0x0041,

/// <summary>
/// Indicates that key encapsulation uses ML-KEM-1024.
/// </summary>
MLKEM_1024 = 0x0042,

/// <summary>
/// Indicates that key encapsulation combines ML-KEM-768 with ECDH using the NIST P-256 curve.
/// </summary>
MLKEM768_P256 = 0x0050,

/// <summary>
/// Indicates that key encapsulation combines ML-KEM-1024 with ECDH using the NIST P-384 curve.
/// </summary>
MLKEM1024_P384 = 0x0051,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Security.Cryptography
{
internal sealed partial class HpkeKemMetadata
{
internal const int MaximumInputKeyingMaterialLength = Hpke.MaximumInputSizeInBytes;

internal HpkeKem Kem { get; }
internal int Nsk { get; }
internal int Npk { get; }
internal int Nenc { get; }
internal int Nsecret { get; }
internal string Name { get; }

private HpkeKemMetadata(HpkeKem kem, int nsecret, int nenc, int npk, int nsk, string name)
{
Kem = kem;
Nsk = nsk;
Npk = npk;
Nenc = nenc;
Nsecret = nsecret;
Name = name;
Setup();
}

partial void Setup();

internal static HpkeKemMetadata? Create(HpkeKem kem)
{
switch (kem)
{
// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#section-7.1
case HpkeKem.DHKEM_P256_HKDF_SHA256:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 65, npk: 65, nsk: 32, name: "DHKEM(P-256, HKDF-SHA256)");
case HpkeKem.DHKEM_P384_HKDF_SHA384:
return new HpkeKemMetadata(kem, nsecret: 48, nenc: 97, npk: 97, nsk: 48, name: "DHKEM(P-384, HKDF-SHA384)");
case HpkeKem.DHKEM_P521_HKDF_SHA512:
return new HpkeKemMetadata(kem, nsecret: 64, nenc: 133, npk: 133, nsk: 66, name: "DHKEM(P-521, HKDF-SHA512)");
case HpkeKem.DHKEM_X25519_HKDF_SHA256:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 32, npk: 32, nsk: 32, name: "DHKEM(X25519, HKDF-SHA256)");

// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-pq-05#section-8.1
// Nsk is the 64-byte seed, not the expanded ML-KEM decapsulation key.
case HpkeKem.MLKEM_512:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 768, npk: 800, nsk: 64, name: "ML-KEM-512");
case HpkeKem.MLKEM_768:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 1088, npk: 1184, nsk: 64, name: "ML-KEM-768");
case HpkeKem.MLKEM_1024:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 1568, npk: 1568, nsk: 64, name: "ML-KEM-1024");

// https://datatracker.ietf.org/doc/html/draft-ietf-hpke-pq-05#section-8.2
// Nsk is the 32-byte seed used to derive both component key pairs.
case HpkeKem.MLKEM768_P256:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 1153, npk: 1249, nsk: 32, name: "MLKEM768-P256");
case HpkeKem.MLKEM1024_P384:
return new HpkeKemMetadata(kem, nsecret: 32, nenc: 1665, npk: 1665, nsk: 32, name: "MLKEM1024-P384");

default:
return null;
}
}
}
}
Loading
Loading