Skip to content
Open
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
12 changes: 12 additions & 0 deletions crypto/src/crypto/parameters/MLDsaKeyGenerationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// Key generation parameters for ML-DSA (FIPS 204). Carries the <see cref="SecureRandom"/> used to
/// draw the seed material together with the chosen <see cref="MLDsaParameters"/> selector. Strength
/// is implied by the parameter set, so the base <c>strength</c> field is left at zero.
/// </summary>
public sealed class MLDsaKeyGenerationParameters
: KeyGenerationParameters
{
private readonly MLDsaParameters m_parameters;

/// <summary>Construct using <paramref name="parameters"/> directly.</summary>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> is <c>null</c>.</exception>
public MLDsaKeyGenerationParameters(SecureRandom random, MLDsaParameters parameters)
: base(random, 0)
{
m_parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
}

/// <summary>Construct by looking up the parameter set for <paramref name="parametersOid"/>.</summary>
/// <exception cref="ArgumentNullException">If <paramref name="parametersOid"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="parametersOid"/> is not a recognised
/// ML-DSA parameter OID (pure or HashML-DSA).</exception>
public MLDsaKeyGenerationParameters(SecureRandom random, DerObjectIdentifier parametersOid)
: base(random, 0)
{
Expand All @@ -25,6 +36,7 @@ public MLDsaKeyGenerationParameters(SecureRandom random, DerObjectIdentifier par
throw new ArgumentException("unrecognised ML-DSA parameters OID", nameof(parametersOid));
}

/// <summary>The ML-DSA parameter set the generated key pair will be bound to.</summary>
public MLDsaParameters Parameters => m_parameters;
}
}
5 changes: 5 additions & 0 deletions crypto/src/crypto/parameters/MLDsaKeyParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// Common base for ML-DSA (FIPS 204) public and private key parameters; carries the
/// <see cref="MLDsaParameters"/> selector that the key was generated for.
/// </summary>
public abstract class MLDsaKeyParameters
: AsymmetricKeyParameter
{
Expand All @@ -13,6 +17,7 @@ internal MLDsaKeyParameters(bool isPrivate, MLDsaParameters parameters)
m_parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
}

/// <summary>The parameter set this key is bound to.</summary>
public MLDsaParameters Parameters => m_parameters;
}
}
10 changes: 10 additions & 0 deletions crypto/src/crypto/parameters/MLDsaParameterSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// The three ML-DSA parameter sets defined by FIPS 204, identified by the matrix dimensions
/// <c>(k, ℓ)</c> embedded in the name. Each entry binds the textual name to the engine mode that
/// produces the standardised public-key, private-key and signature sizes.
/// </summary>
public sealed class MLDsaParameterSet
{
/// <summary>ML-DSA-44 (NIST Category 2, mode 2).</summary>
public static readonly MLDsaParameterSet ml_dsa_44 = new MLDsaParameterSet("ML-DSA-44", 2);
/// <summary>ML-DSA-65 (NIST Category 3, mode 3).</summary>
public static readonly MLDsaParameterSet ml_dsa_65 = new MLDsaParameterSet("ML-DSA-65", 3);
/// <summary>ML-DSA-87 (NIST Category 5, mode 5).</summary>
public static readonly MLDsaParameterSet ml_dsa_87 = new MLDsaParameterSet("ML-DSA-87", 5);

private static readonly Dictionary<string, MLDsaParameterSet> ByName =
Expand All @@ -34,6 +42,7 @@ private MLDsaParameterSet(string name, int mode)

internal MLDsaEngine GetEngine(SecureRandom random) => new MLDsaEngine(m_mode, random);

/// <summary>The textual name of this parameter set (e.g. <c>"ML-DSA-44"</c>).</summary>
public string Name => m_name;

internal int PrivateKeyLength
Expand Down Expand Up @@ -68,6 +77,7 @@ internal int PublicKeyLength

internal int SeedLength => MLDsaEngine.SeedBytes;

/// <inheritdoc/>
public override string ToString() => Name;
}
}
58 changes: 57 additions & 1 deletion crypto/src/crypto/parameters/MLDsaPrivateKeyParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,43 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// ML-DSA private key (FIPS 204). The expanded key holds <c>(ρ, K, tr, s₁, s₂, t₀)</c> derived from
/// a 32-byte seed; the corresponding <c>t₁</c> half of the public key is cached for fast public-key
/// recovery. A key built from a seed retains the seed so it can be re-encoded in the seed format.
/// </summary>
public sealed class MLDsaPrivateKeyParameters
: MLDsaKeyParameters
{
public enum Format { SeedOnly, EncodingOnly, SeedAndEncoding };
/// <summary>
/// Storage format selector for serialised private keys. Per RFC 9881 §8.1 the seed form is
/// preferred for storage efficiency.
/// </summary>
public enum Format
{
/// <summary>Serialise as the 32-byte seed only; requires that the seed is available.</summary>
SeedOnly,
/// <summary>Serialise as the expanded encoding only; works for keys imported by encoding.</summary>
EncodingOnly,
/// <summary>Carry both the seed and the expanded encoding.</summary>
SeedAndEncoding,
};

/*
* RFC 9881 8.1. [..] the seed format is RECOMMENDED for storage efficiency.
*/
/// <summary>Default <see cref="Format"/> applied when a key is constructed from a seed.</summary>
public static readonly Format DefaultFormat = Format.SeedOnly;

/// <summary>
/// Decode a private key from its expanded byte representation. The expected length is the
/// parameter set's <c>PrivateKeyLength</c>; the returned key has <see cref="Format.EncodingOnly"/>
/// as its preferred format because the seed is not recoverable from the encoding.
/// </summary>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> or
/// <paramref name="encoding"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="encoding"/> length does not match the
/// parameter set's private-key length.</exception>
public static MLDsaPrivateKeyParameters FromEncoding(MLDsaParameters parameters, byte[] encoding)
{
if (parameters == null)
Expand Down Expand Up @@ -56,9 +83,20 @@ public static MLDsaPrivateKeyParameters FromEncoding(MLDsaParameters parameters,
return new MLDsaPrivateKeyParameters(parameters, rho, k, tr, s1, s2, t0, t1, seed, Format.EncodingOnly);
}

/// <summary>
/// Expand a 32-byte seed into a private key, defaulting to <see cref="DefaultFormat"/>
/// (<see cref="Format.SeedOnly"/>).
/// </summary>
public static MLDsaPrivateKeyParameters FromSeed(MLDsaParameters parameters, byte[] seed) =>
FromSeed(parameters, seed, preferredFormat: DefaultFormat);

/// <summary>
/// Expand a 32-byte seed into a private key with the supplied <paramref name="preferredFormat"/>.
/// </summary>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> or
/// <paramref name="seed"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="seed"/> length is not the parameter
/// set's <c>SeedLength</c>, or <paramref name="preferredFormat"/> is unrecognised.</exception>
public static MLDsaPrivateKeyParameters FromSeed(MLDsaParameters parameters, byte[] seed,
Format preferredFormat)
{
Expand Down Expand Up @@ -107,14 +145,24 @@ internal MLDsaPrivateKeyParameters(MLDsaParameters parameters, byte[] rho, byte[
m_preferredFormat = preferredFormat;
}

/// <summary>
/// Return a fresh copy of the expanded <c>ρ || K || tr || s₁ || s₂ || t₀</c> encoding.
/// </summary>
public byte[] GetEncoded() => Arrays.ConcatenateAll(m_rho, m_k, m_tr, m_s1, m_s2, m_t0);

/// <summary>Return the public key derived during key expansion.</summary>
public MLDsaPublicKeyParameters GetPublicKey() => new MLDsaPublicKeyParameters(Parameters, m_rho, m_t1);

/// <summary>Return a fresh copy of the public key encoding (<c>ρ || t₁</c>).</summary>
public byte[] GetPublicKeyEncoded() => Arrays.Concatenate(m_rho, m_t1);

/// <summary>
/// Return a fresh copy of the 32-byte seed if one was retained at construction; <c>null</c> for
/// keys imported via <see cref="FromEncoding"/>.
/// </summary>
public byte[] GetSeed() => Arrays.Clone(m_seed);

/// <summary>The serialisation format this key was last asked to prefer.</summary>
public Format PreferredFormat => m_preferredFormat;

internal byte[] Seed => m_seed;
Expand All @@ -129,6 +177,14 @@ internal byte[] SignInternal(byte[] rnd, byte[] msg, int msgOff, int msgLen)
return sig;
}

/// <summary>
/// Return a key equivalent to this one but with <see cref="PreferredFormat"/> changed to
/// <paramref name="preferredFormat"/>. Returns the receiver when the format is unchanged.
/// </summary>
/// <exception cref="InvalidOperationException">If the requested format requires a seed but no
/// seed is available (i.e. the key was imported via <see cref="FromEncoding"/>).</exception>
/// <exception cref="ArgumentException">If <paramref name="preferredFormat"/> is not a recognised
/// <see cref="Format"/> value.</exception>
public MLDsaPrivateKeyParameters WithPreferredFormat(Format preferredFormat)
{
if (m_preferredFormat == preferredFormat)
Expand Down
12 changes: 12 additions & 0 deletions crypto/src/crypto/parameters/MLKemKeyGenerationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// Key generation parameters for ML-KEM (FIPS 203). Carries the <see cref="SecureRandom"/> used to
/// draw the seed material together with the chosen <see cref="MLKemParameters"/> selector. Strength
/// is implied by the parameter set, so the base <c>strength</c> field is left at zero.
/// </summary>
public sealed class MLKemKeyGenerationParameters
: KeyGenerationParameters
{
private readonly MLKemParameters m_parameters;

/// <summary>Construct using <paramref name="parameters"/> directly.</summary>
/// <exception cref="ArgumentNullException">If <paramref name="parameters"/> is <c>null</c>.</exception>
public MLKemKeyGenerationParameters(SecureRandom random, MLKemParameters parameters)
: base(random, 0)
{
m_parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
}

/// <summary>Construct by looking up the parameter set for <paramref name="parametersOid"/>.</summary>
/// <exception cref="ArgumentNullException">If <paramref name="parametersOid"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="parametersOid"/> is not a recognised
/// ML-KEM parameter OID.</exception>
public MLKemKeyGenerationParameters(SecureRandom random, DerObjectIdentifier parametersOid)
: base(random, 0)
{
Expand All @@ -25,6 +36,7 @@ public MLKemKeyGenerationParameters(SecureRandom random, DerObjectIdentifier par
throw new ArgumentException("unrecognised ML-KEM parameters OID", nameof(parametersOid));
}

/// <summary>The ML-KEM parameter set the generated key pair will be bound to.</summary>
public MLKemParameters Parameters => m_parameters;
}
}
5 changes: 5 additions & 0 deletions crypto/src/crypto/parameters/MLKemKeyParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// Common base for ML-KEM (FIPS 203) public and private key parameters; carries the
/// <see cref="MLKemParameters"/> selector that the key was generated for.
/// </summary>
public abstract class MLKemKeyParameters
: AsymmetricKeyParameter
{
Expand All @@ -13,6 +17,7 @@ internal MLKemKeyParameters(bool isPrivate, MLKemParameters parameters)
m_parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
}

/// <summary>The parameter set this key is bound to.</summary>
public MLKemParameters Parameters => m_parameters;
}
}
12 changes: 12 additions & 0 deletions crypto/src/crypto/parameters/MLKemParameterSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@

namespace Org.BouncyCastle.Crypto.Parameters
{
/// <summary>
/// The three ML-KEM parameter sets defined by FIPS 203, distinguished by the module-lattice rank
/// <c>k</c>. Each entry binds the textual name to a configured engine that produces the standardised
/// ciphertext and shared-secret sizes.
/// </summary>
public sealed class MLKemParameterSet
{
/// <summary>ML-KEM-512 (NIST Category 1, <c>k = 2</c>).</summary>
public static readonly MLKemParameterSet ml_kem_512 = new MLKemParameterSet("ML-KEM-512", 2);
/// <summary>ML-KEM-768 (NIST Category 3, <c>k = 3</c>).</summary>
public static readonly MLKemParameterSet ml_kem_768 = new MLKemParameterSet("ML-KEM-768", 3);
/// <summary>ML-KEM-1024 (NIST Category 5, <c>k = 4</c>).</summary>
public static readonly MLKemParameterSet ml_kem_1024 = new MLKemParameterSet("ML-KEM-1024", 4);

private static readonly Dictionary<string, MLKemParameterSet> ByName =
Expand All @@ -31,14 +39,18 @@ private MLKemParameterSet(string name, int k)
m_engine = new MLKemEngine(k);
}

/// <summary>Length in bytes of an ML-KEM encapsulation (ciphertext) for this parameter set.</summary>
public int EncapsulationLength => m_engine.CipherTextBytes;

internal MLKemEngine Engine => m_engine;

/// <summary>Length in bytes of the shared secret produced by encapsulation/decapsulation (32).</summary>
public int SecretLength => MLKemEngine.SharedSecretBytes;

/// <summary>The textual name of this parameter set (e.g. <c>"ML-KEM-512"</c>).</summary>
public string Name => m_name;

/// <inheritdoc/>
public override string ToString() => Name;
}
}