Skip to content
Closed
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
19 changes: 19 additions & 0 deletions crypto/src/crypto/parameters/MLDsaParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,36 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// Algorithm parameter set identifiers for ML-DSA (Module-Lattice-Based Digital Signature Algorithm) as
/// specified in <a href="https://csrc.nist.gov/pubs/fips/204/final">FIPS 204</a>.
/// </summary>
/// <remarks>
/// ML-DSA is the NIST-standardized post-quantum signature scheme derived from CRYSTALS-Dilithium. Each
/// parameter set fixes lattice dimensions and security category. The <c>_with_sha512</c> variants wrap the
/// pure scheme in the HashML-DSA pre-hash construction from FIPS 204, §5.4.
/// </remarks>
public sealed class MLDsaParameters
{
/// <summary>Pure ML-DSA-44 (NIST security category 2).</summary>
public static readonly MLDsaParameters ml_dsa_44 = new MLDsaParameters("ML-DSA-44", MLDsaParameterSet.ml_dsa_44,
NistObjectIdentifiers.id_ml_dsa_44, null);
/// <summary>Pure ML-DSA-65 (NIST security category 3).</summary>
public static readonly MLDsaParameters ml_dsa_65 = new MLDsaParameters("ML-DSA-65", MLDsaParameterSet.ml_dsa_65,
NistObjectIdentifiers.id_ml_dsa_65, null);
/// <summary>Pure ML-DSA-87 (NIST security category 5).</summary>
public static readonly MLDsaParameters ml_dsa_87 = new MLDsaParameters("ML-DSA-87", MLDsaParameterSet.ml_dsa_87,
NistObjectIdentifiers.id_ml_dsa_87, null);

/// <summary>HashML-DSA-44 pre-hashed with SHA-512.</summary>
public static readonly MLDsaParameters ml_dsa_44_with_sha512 = new MLDsaParameters("ML-DSA-44-WITH-SHA512",
MLDsaParameterSet.ml_dsa_44, NistObjectIdentifiers.id_hash_ml_dsa_44_with_sha512,
NistObjectIdentifiers.IdSha512);
/// <summary>HashML-DSA-65 pre-hashed with SHA-512.</summary>
public static readonly MLDsaParameters ml_dsa_65_with_sha512 = new MLDsaParameters("ML-DSA-65-WITH-SHA512",
MLDsaParameterSet.ml_dsa_65, NistObjectIdentifiers.id_hash_ml_dsa_65_with_sha512,
NistObjectIdentifiers.IdSha512);
/// <summary>HashML-DSA-87 pre-hashed with SHA-512.</summary>
public static readonly MLDsaParameters ml_dsa_87_with_sha512 = new MLDsaParameters("ML-DSA-87-WITH-SHA512",
MLDsaParameterSet.ml_dsa_87, NistObjectIdentifiers.id_hash_ml_dsa_87_with_sha512,
NistObjectIdentifiers.IdSha512);
Expand Down Expand Up @@ -62,16 +77,20 @@ private MLDsaParameters(string name, MLDsaParameterSet parameterSet, DerObjectId
m_preHashOid = preHashOid;
}

/// <summary><c>true</c> for HashML-DSA variants (pre-hashed input); <c>false</c> for pure ML-DSA.</summary>
public bool IsPreHash => m_preHashOid != null;

/// <summary>The standard algorithm name (e.g. <c>ML-DSA-65</c> or <c>ML-DSA-65-WITH-SHA512</c>).</summary>
public string Name => m_name;

internal DerObjectIdentifier Oid => m_oid;

internal DerObjectIdentifier PreHashOid => m_preHashOid;

/// <summary>The underlying ML-DSA parameter set (lattice dimensions, number-theoretic constants).</summary>
public MLDsaParameterSet ParameterSet => m_parameterSet;

/// <summary>Returns the algorithm name (see <see cref="Name"/>).</summary>
public override string ToString() => Name;
}
}
19 changes: 19 additions & 0 deletions crypto/src/crypto/parameters/MLDsaPublicKeyParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,27 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// An ML-DSA public (verification) key, as specified in FIPS 204.
/// </summary>
/// <remarks>
/// Internally the key is kept split into the <c>rho</c> seed and the <c>t1</c> vector per FIPS 204 §5.2.
/// The full public-key hash <c>tr</c> is computed lazily on first use and cached for subsequent verify
/// operations.
/// </remarks>
public sealed class MLDsaPublicKeyParameters
: MLDsaKeyParameters
{
/// <summary>
/// Create an <see cref="MLDsaPublicKeyParameters"/> from its raw FIPS 204 public key encoding.
/// </summary>
/// <param name="parameters">The ML-DSA algorithm parameters this key belongs to.</param>
/// <param name="encoding">The raw public key bytes. Length must equal the parameter set's
/// <c>PublicKeyLength</c>.</param>
/// <returns>A new <see cref="MLDsaPublicKeyParameters"/>.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> or
/// <paramref name="encoding"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="encoding"/> has the wrong length.</exception>
public static MLDsaPublicKeyParameters FromEncoding(MLDsaParameters parameters, byte[] encoding)
{
if (parameters == null)
Expand All @@ -34,6 +52,7 @@ internal MLDsaPublicKeyParameters(MLDsaParameters parameters, byte[] rho, byte[]
m_t1 = t1;
}

/// <summary>Returns a fresh copy of the FIPS 204 public key encoding (<c>rho || t1</c>).</summary>
public byte[] GetEncoded() => Arrays.Concatenate(m_rho, m_t1);

internal byte[] GetPublicKeyHash() =>
Expand Down
15 changes: 15 additions & 0 deletions crypto/src/crypto/parameters/MLKemParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,24 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// Algorithm parameter set identifiers for ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) as
/// specified in <a href="https://csrc.nist.gov/pubs/fips/203/final">FIPS 203</a>.
/// </summary>
/// <remarks>
/// ML-KEM is a post-quantum key-encapsulation mechanism standardized by NIST, derived from CRYSTALS-Kyber.
/// Each instance binds a human-readable algorithm name, an internal <see cref="MLKemParameterSet"/>, and the
/// NIST-assigned algorithm OID used in X.509/PKIX encodings.
/// </remarks>
public sealed class MLKemParameters
{
/// <summary>ML-KEM-512 parameter set (NIST security category 1).</summary>
public static readonly MLKemParameters ml_kem_512 = new MLKemParameters("ML-KEM-512",
MLKemParameterSet.ml_kem_512, NistObjectIdentifiers.id_alg_ml_kem_512);
/// <summary>ML-KEM-768 parameter set (NIST security category 3).</summary>
public static readonly MLKemParameters ml_kem_768 = new MLKemParameters("ML-KEM-768",
MLKemParameterSet.ml_kem_768, NistObjectIdentifiers.id_alg_ml_kem_768);
/// <summary>ML-KEM-1024 parameter set (NIST security category 5).</summary>
public static readonly MLKemParameters ml_kem_1024 = new MLKemParameters("ML-KEM-1024",
MLKemParameterSet.ml_kem_1024, NistObjectIdentifiers.id_alg_ml_kem_1024);

Expand Down Expand Up @@ -43,12 +55,15 @@ private MLKemParameters(string name, MLKemParameterSet parameterSet, DerObjectId
m_oid = oid ?? throw new ArgumentNullException(nameof(oid));
}

/// <summary>The standard algorithm name identifying this parameter set (e.g. <c>ML-KEM-768</c>).</summary>
public string Name => m_name;

internal DerObjectIdentifier Oid => m_oid;

/// <summary>The underlying ML-KEM parameter set (lattice dimensions, noise bounds, etc.).</summary>
public MLKemParameterSet ParameterSet => m_parameterSet;

/// <summary>Returns the algorithm name (see <see cref="Name"/>).</summary>
public override string ToString() => Name;
}
}
68 changes: 66 additions & 2 deletions crypto/src/crypto/parameters/MLKemPrivateKeyParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,40 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// An ML-KEM private (decapsulation) key, as specified in FIPS 203.
/// </summary>
/// <remarks>
/// A private key may be stored in one of three equivalent forms, selected by <see cref="Format"/>: the
/// 32-byte seed only, the expanded byte encoding only, or both. The seed form is the smallest and is
/// preferred where the runtime can re-expand it on use; the expanded encoding avoids re-expansion but is
/// much larger.
/// </remarks>
public sealed class MLKemPrivateKeyParameters
: MLKemKeyParameters
{
public enum Format { SeedOnly, EncodingOnly, SeedAndEncoding };

/// <summary>Representation format for an ML-KEM private key.</summary>
public enum Format
{
/// <summary>Store the 32-byte seed only; the expanded encoding is regenerated on demand.</summary>
SeedOnly,
/// <summary>Store the full expanded FIPS 203 private key encoding only (no seed available).</summary>
EncodingOnly,
/// <summary>Store both the seed and the expanded encoding.</summary>
SeedAndEncoding
};

/// <summary>
/// Create an <see cref="MLKemPrivateKeyParameters"/> from its expanded FIPS 203 private key encoding.
/// </summary>
/// <param name="parameters">The ML-KEM algorithm parameters this key belongs to.</param>
/// <param name="encoding">The raw decapsulation key bytes. Length must equal the parameter set's
/// <c>SecretKeyBytes</c>.</param>
/// <returns>A private key in <see cref="Format.EncodingOnly"/> form (no seed retained).</returns>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> or
/// <paramref name="encoding"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="encoding"/> has the wrong length or fails the
/// FIPS 203 hash check.</exception>
public static MLKemPrivateKeyParameters FromEncoding(MLKemParameters parameters, byte[] encoding)
{
if (parameters == null)
Expand All @@ -31,9 +60,30 @@ public static MLKemPrivateKeyParameters FromEncoding(MLKemParameters parameters,
return new MLKemPrivateKeyParameters(parameters, seed: null, encoding, Format.EncodingOnly);
}

/// <summary>
/// Derive a private key from its 32-byte seed, defaulting to the <see cref="Format.SeedOnly"/>
/// representation.
/// </summary>
/// <param name="parameters">The ML-KEM algorithm parameters.</param>
/// <param name="seed">The 32-byte FIPS 203 seed <c>(d || z)</c>.</param>
/// <returns>A new private key whose expanded encoding is derived from the seed.</returns>
/// <exception cref="ArgumentNullException">If any argument is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="seed"/> has the wrong length.</exception>
public static MLKemPrivateKeyParameters FromSeed(MLKemParameters parameters, byte[] seed) =>
FromSeed(parameters, seed, preferredFormat: Format.SeedOnly);

/// <summary>
/// Derive a private key from its 32-byte seed, selecting the preferred on-disk representation.
/// </summary>
/// <param name="parameters">The ML-KEM algorithm parameters.</param>
/// <param name="seed">The 32-byte FIPS 203 seed <c>(d || z)</c>.</param>
/// <param name="preferredFormat">The format to report via <see cref="PreferredFormat"/>. Must be
/// <see cref="Format.SeedOnly"/> or <see cref="Format.SeedAndEncoding"/> since a seed is available.</param>
/// <returns>A new private key whose expanded encoding is derived from the seed.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> or
/// <paramref name="seed"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="seed"/> has the wrong length or
/// <paramref name="preferredFormat"/> is not valid.</exception>
public static MLKemPrivateKeyParameters FromSeed(MLKemParameters parameters, byte[] seed,
Format preferredFormat)
{
Expand Down Expand Up @@ -70,19 +120,33 @@ internal MLKemPrivateKeyParameters(MLKemParameters parameters, byte[] seed, byte

internal byte[] Encoding => m_encoding;

/// <summary>Returns a copy of the expanded FIPS 203 private key encoding.</summary>
public byte[] GetEncoded() => Arrays.InternalCopyBuffer(m_encoding);

/// <summary>Extracts the matching <see cref="MLKemPublicKeyParameters"/> from this private key.</summary>
public MLKemPublicKeyParameters GetPublicKey() =>
new MLKemPublicKeyParameters(Parameters, GetPublicKeyEncoded());

/// <summary>Returns the raw public (encapsulation) key bytes embedded in this private key.</summary>
public byte[] GetPublicKeyEncoded() => Parameters.ParameterSet.Engine.CopyEncapKey(decapKey: m_encoding);

/// <summary>
/// Returns a copy of the 32-byte seed, or <c>null</c> if the key was imported without one (i.e. created
/// via <see cref="FromEncoding"/>).
/// </summary>
public byte[] GetSeed() => Arrays.Clone(m_seed);

/// <summary>The caller-preferred encoding format (see <see cref="Format"/>).</summary>
public Format PreferredFormat => m_preferredFormat;

internal byte[] Seed => m_seed;

/// <summary>
/// Returns this key with a different <see cref="PreferredFormat"/>, or the same instance if no change
/// is needed.
/// </summary>
/// <param name="preferredFormat">The new format. Requesting a seed-bearing format when no seed is
/// available throws <see cref="InvalidOperationException"/>.</param>
public MLKemPrivateKeyParameters WithPreferredFormat(Format preferredFormat)
{
if (m_preferredFormat == preferredFormat)
Expand Down
21 changes: 21 additions & 0 deletions crypto/src/crypto/parameters/MLKemPublicKeyParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,29 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// An ML-KEM public (encapsulation) key, represented by the raw byte encoding defined in FIPS 203.
/// </summary>
/// <remarks>
/// Instances are immutable and can be used to perform key encapsulation. Create instances via
/// <see cref="FromEncoding(MLKemParameters, byte[])"/> or by generating a key pair; the constructor is
/// internal.
/// </remarks>
public sealed class MLKemPublicKeyParameters
: MLKemKeyParameters
{
/// <summary>
/// Create an <see cref="MLKemPublicKeyParameters"/> from its raw FIPS 203 public key encoding.
/// </summary>
/// <param name="parameters">The ML-KEM algorithm parameters this key belongs to.</param>
/// <param name="encoding">The raw public key bytes. Length must equal the parameter set's
/// <c>PublicKeyBytes</c>.</param>
/// <returns>A new <see cref="MLKemPublicKeyParameters"/> wrapping a defensive copy of
/// <paramref name="encoding"/>.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> or
/// <paramref name="encoding"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="encoding"/> has the wrong length or fails the
/// FIPS 203 modulus check.</exception>
public static MLKemPublicKeyParameters FromEncoding(MLKemParameters parameters, byte[] encoding)
{
if (parameters == null)
Expand Down Expand Up @@ -38,6 +58,7 @@ internal MLKemPublicKeyParameters(MLKemParameters parameters, byte[] encoding)

internal byte[] Encoding => m_encoding;

/// <summary>Returns a copy of the raw FIPS 203 public key encoding.</summary>
public byte[] GetEncoded() => Arrays.InternalCopyBuffer(m_encoding);

// NB: Don't remove - needed by commented-out test cases
Expand Down