Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b8e3500
Put ACVP KATs repository in gitignore list
itzmeanjan Sep 25, 2025
bb72832
Add script for parsing ML-DSA keygen ACVP KATs
itzmeanjan Sep 25, 2025
83b3d2a
Parse and extract ML-DSA keygen ACVP KATs into desired format
itzmeanjan Sep 25, 2025
52ec078
Reformat source tree
itzmeanjan Sep 25, 2025
fefb25c
Simplify logic for parsing KAT files - introduce little helper functions
itzmeanjan Sep 25, 2025
032012e
Test against ACVP KATs for ML-DSA keygen
itzmeanjan Sep 25, 2025
ae36244
Add script for parsing ACVP KATs for ML-DSA signing
itzmeanjan Sep 25, 2025
f5e4bf3
Parse and extract ML-DSA sign ACVP KATs into desired format
itzmeanjan Sep 25, 2025
ab1e4e4
Test against ACVP KATs for ML-DSA sign
itzmeanjan Sep 25, 2025
bd4b7bc
Add script for parsing and extracting ML-DSA signature-verification K…
itzmeanjan Sep 25, 2025
009179d
Parse and extract ML-DSA verify ACVP KATs into desired format
itzmeanjan Sep 25, 2025
03164c6
Test against ACVP KATs for ML-DSA verify
itzmeanjan Sep 25, 2025
835a84e
Expose internal sign and verify functions which takes externally supp…
itzmeanjan Sep 25, 2025
5e94a4d
Add script for parsing ACVP KATs for internal sign API of ML-DSA
itzmeanjan Sep 25, 2025
3f19b1c
Parse and extract ML-DSA sign_internal ACVP KATs into desired format
itzmeanjan Sep 25, 2025
aa6742c
Test against ACVP KATs for ML-DSA sign-internal
itzmeanjan Sep 25, 2025
1d50c43
Add script for extracting and parsing NIST ACVP KATs for ML-DSA verif…
itzmeanjan Sep 25, 2025
a9a6cbb
Parse and extract ML-DSA verify_internal ACVP KATs into desired format
itzmeanjan Sep 25, 2025
efc103d
Test against ACVP KATs for ML-DSA verify-internal
itzmeanjan Sep 25, 2025
a4ae235
Get rid of redundant expansion of public matrix A in external `sign` API
itzmeanjan Sep 26, 2025
cc17cc4
Mention availability of sign_internal API in example program
itzmeanjan Sep 26, 2025
448f14c
Update project documentation reflecting latest state
itzmeanjan Sep 26, 2025
ec9f6ad
Pin git submodule `gtest-parallel` to latest commit
itzmeanjan Sep 26, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@
# For Clangd
compile_commands.json
.cache

# ACVP server
ACVP-Server/
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ clean: ## Remove build directory
.PHONY: format
format: $(ML_DSA_SOURCES) $(TEST_SOURCES) $(TEST_HEADERS) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS) $(EXAMPLE_SOURCES) $(EXAMPLE_HEADERS) ## Format source code
clang-format -i $^

.PHONY: sync_acvp_kats
sync_acvp_kats: ## Downloads NIST ACVP KAT vectors and updates local KATs
cd kats/scripts && ./sync_acvp_kats.sh && cd -
152 changes: 88 additions & 64 deletions README.md

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions examples/ml_dsa_44.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ main()

ml_dsa_44::keygen(seed_span, pubkey_span, seckey_span);
const bool has_signed = ml_dsa_44::sign(rnd_span, seckey_span, msg_span, ctx_span, sig_span);

// ML-DSA exposes an internal signing API, where one can pass an externally computed `mu` of 64 -bytes i.e. the message representative
// to be signed, instead of passing message and optional context string. How is `mu` computed ?
//
// ```cpp
// std::array<uint8_t, MU_BYTE_LEN> mu{};
// auto mu_span = std::span(mu);
//
// shake256::shake256_t hasher;
//
// hasher.absorb(tr); // `tr` is hash of public key. It is embedded in the secret key.
// hasher.absorb(domain_separator);
// hasher.absorb(ctx);
// hasher.absorb(msg);
// hasher.finalize();
//
// hasher.squeeze(mu_span);
// ```
// const bool has_signed = ml_dsa_44::sign_internal(rnd_span, seckey_span, mu_span, sig_span);

const bool is_valid = ml_dsa_44::verify(pubkey_span, msg_span, ctx_span, sig_span);

std::cout << "ML-DSA-44 @ NIST security level 2\n";
Expand Down
2 changes: 1 addition & 1 deletion gtest-parallel
158 changes: 102 additions & 56 deletions include/ml_dsa/internals/ml_dsa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <array>
#include <cstdint>
#include <limits>
#include <span>

// ML-DSA FIPS 204
namespace ml_dsa {
Expand All @@ -18,6 +19,9 @@ static constexpr size_t KEYGEN_SEED_BYTE_LEN = 32;
// Byte length of randomness, required for hedged signing.
static constexpr size_t RND_BYTE_LEN = 32;

// Byte length of message representative, which is to be signed.
static constexpr size_t MU_BYTE_LEN = 64;

// Given seed ξ, this routine generates a public key and secret key pair, using deterministic key generation algorithm.
//
// See algorithm 1 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
Expand Down Expand Up @@ -113,28 +117,23 @@ keygen(std::span<const uint8_t, KEYGEN_SEED_BYTE_LEN> ξ,
ml_dsa_polyvec::encode<k, d>(t0, seckey.template subspan<skoff5, skoff6 - skoff5>());
}

// Given a ML-DSA secret key, message (can be empty too) and context (optional, but if given, length must be capped at 255 -bytes),
// this routine computes a hedged/ deterministic signature.
// Given a ML-DSA secret key and 64 -bytes message representative, this routine computes a hedged/ deterministic signature.
//
// Notice, first parameter of this function, `rnd`, which lets you pass 32 -bytes randomness for generating default
// "hedged" signature. In case you don't need randomized message signature, you can instead fill `rnd` with zeros, and
// it'll generate a deterministic signature.
//
// Note, hedged signing is the default and recommended version.
//
// See algorithm 2 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
// See algorithm 7 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
template<size_t k, size_t l, size_t d, uint32_t eta, uint32_t gamma1, uint32_t gamma2, uint32_t tau, uint32_t beta, size_t omega, size_t lambda>
static inline constexpr bool
sign(std::span<const uint8_t, RND_BYTE_LEN> rnd,
std::span<const uint8_t, ml_dsa_utils::sec_key_len(k, l, eta, d)> seckey,
std::span<const uint8_t> msg,
std::span<const uint8_t> ctx,
std::span<uint8_t, ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda)> sig)
sign_internal(std::span<const uint8_t, RND_BYTE_LEN> rnd,
std::span<const uint8_t, ml_dsa_utils::sec_key_len(k, l, eta, d)> seckey,
std::span<const uint8_t, MU_BYTE_LEN> mu,
std::span<uint8_t, ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda)> sig)
requires(ml_dsa_params::check_signing_params(k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda))
{
if (ctx.size() > std::numeric_limits<uint8_t>::max()) {
return false;
}
constexpr uint32_t t0_rng = 1u << (d - 1);

constexpr size_t eta_bw = std::bit_width(2 * eta);
Expand All @@ -150,30 +149,17 @@ sign(std::span<const uint8_t, RND_BYTE_LEN> rnd,

auto rho = seckey.template subspan<skoff0, skoff1 - skoff0>();
auto key = seckey.template subspan<skoff1, skoff2 - skoff1>();
auto tr = seckey.template subspan<skoff2, skoff3 - skoff2>();

std::array<ml_dsa_field::zq_t, k * l * ml_dsa_ntt::N> A{};
ml_dsa_sampling::expand_a<k, l>(rho, A);

std::array<uint8_t, 64> mu{};
auto mu_span = std::span(mu);

const std::array<uint8_t, 2> domain_separator{ 0, static_cast<uint8_t>(ctx.size()) };

shake256::shake256_t hasher;
hasher.absorb(tr);
hasher.absorb(domain_separator);
hasher.absorb(ctx);
hasher.absorb(msg);
hasher.finalize();
hasher.squeeze(mu_span);

std::array<uint8_t, 64> rho_prime{};

shake256::shake256_t hasher;
hasher.reset();
hasher.absorb(key);
hasher.absorb(rnd);
hasher.absorb(mu_span);
hasher.absorb(mu);
hasher.finalize();
hasher.squeeze(rho_prime);

Expand Down Expand Up @@ -226,7 +212,7 @@ sign(std::span<const uint8_t, RND_BYTE_LEN> rnd,
ml_dsa_polyvec::encode<k, w1bw>(w1, w1_encoded);

hasher.reset();
hasher.absorb(mu_span);
hasher.absorb(mu);
hasher.absorb(w1_encoded);
hasher.finalize();
hasher.squeeze(c_tilda_span);
Expand Down Expand Up @@ -302,23 +288,63 @@ sign(std::span<const uint8_t, RND_BYTE_LEN> rnd,
return has_signed;
}

// Given a ML-DSA public key, message (can be empty too), context (optional, but if given, length must be capped at 255 -bytes)
// and serialized signature, this routine verifies validity of the signature, returning boolean result, denoting status
// of signature verification. For example, say it returns true, it means signature is valid for given message and public key.
// Given a ML-DSA secret key, message (can be empty too) and context (optional, but if given, length must be capped at 255 -bytes),
// this routine computes a hedged/ deterministic signature.
//
// See algorithm 3 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
template<size_t k, size_t l, size_t d, uint32_t gamma1, uint32_t gamma2, uint32_t tau, uint32_t beta, size_t omega, size_t lambda>
// Notice, first parameter of this function, `rnd`, which lets you pass 32 -bytes randomness for generating default
// "hedged" signature. In case you don't need randomized message signature, you can instead fill `rnd` with zeros, and
// it'll generate a deterministic signature.
//
// Note, hedged signing is the default and recommended version.
//
// See algorithm 2 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
template<size_t k, size_t l, size_t d, uint32_t eta, uint32_t gamma1, uint32_t gamma2, uint32_t tau, uint32_t beta, size_t omega, size_t lambda>
static inline constexpr bool
verify(std::span<const uint8_t, ml_dsa_utils::pub_key_len(k, d)> pubkey,
std::span<const uint8_t> msg,
std::span<const uint8_t> ctx,
std::span<const uint8_t, ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda)> sig)
requires(ml_dsa_params::check_verify_params(k, l, d, gamma1, gamma2, tau, beta, omega, lambda))
sign(std::span<const uint8_t, RND_BYTE_LEN> rnd,
std::span<const uint8_t, ml_dsa_utils::sec_key_len(k, l, eta, d)> seckey,
std::span<const uint8_t> msg,
std::span<const uint8_t> ctx,
std::span<uint8_t, ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda)> sig)
requires(ml_dsa_params::check_signing_params(k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda))
{
if (ctx.size() > std::numeric_limits<uint8_t>::max()) {
return false;
}

constexpr size_t skoff0 = 0;
constexpr size_t skoff1 = skoff0 + 32;
constexpr size_t skoff2 = skoff1 + 32;
constexpr size_t skoff3 = skoff2 + 64;

auto tr = seckey.template subspan<skoff2, skoff3 - skoff2>();
const std::array<uint8_t, 2> domain_separator{ 0, static_cast<uint8_t>(ctx.size()) };

std::array<uint8_t, MU_BYTE_LEN> mu{};
auto mu_span = std::span(mu);

shake256::shake256_t hasher;
hasher.absorb(tr);
hasher.absorb(domain_separator);
hasher.absorb(ctx);
hasher.absorb(msg);
hasher.finalize();
hasher.squeeze(mu_span);

return sign_internal<k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda>(rnd, seckey, mu_span, sig);
}

// Given a ML-DSA public key, 64 -bytes message representative and serialized signature, this routine verifies validity of the signature,
// returning boolean result, denoting status of signature verification. For example, say it returns true, it means signature is valid for
// given message and public key.
//
// See algorithm 8 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
template<size_t k, size_t l, size_t d, uint32_t gamma1, uint32_t gamma2, uint32_t tau, uint32_t beta, size_t omega, size_t lambda>
static inline constexpr bool
verify_internal(std::span<const uint8_t, ml_dsa_utils::pub_key_len(k, d)> pubkey,
std::span<const uint8_t, MU_BYTE_LEN> mu,
std::span<const uint8_t, ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda)> sig)
requires(ml_dsa_params::check_verify_params(k, l, d, gamma1, gamma2, tau, beta, omega, lambda))
{
constexpr size_t t1_bw = std::bit_width(ml_dsa_field::Q) - d;
constexpr size_t gamma1_bw = std::bit_width(gamma1);

Expand Down Expand Up @@ -370,24 +396,6 @@ verify(std::span<const uint8_t, ml_dsa_utils::pub_key_len(k, d)> pubkey,
ml_dsa_sampling::expand_a<k, l>(rho, A);
ml_dsa_polyvec::decode<k, t1_bw>(t1_encoded, t1);

std::array<uint8_t, 64> tr{};
std::array<uint8_t, 64> mu{};

shake256::shake256_t hasher;
hasher.absorb(pubkey);
hasher.finalize();
hasher.squeeze(tr);

const std::array<uint8_t, 2> domain_separator{ 0, static_cast<uint8_t>(ctx.size()) };

hasher.reset();
hasher.absorb(tr);
hasher.absorb(domain_separator);
hasher.absorb(ctx);
hasher.absorb(msg);
hasher.finalize();
hasher.squeeze(mu);

std::array<ml_dsa_field::zq_t, k * ml_dsa_ntt::N> w0{};
std::array<ml_dsa_field::zq_t, k * ml_dsa_ntt::N> w1{};
std::array<ml_dsa_field::zq_t, k * ml_dsa_ntt::N> w2{};
Expand All @@ -414,7 +422,7 @@ verify(std::span<const uint8_t, ml_dsa_utils::pub_key_len(k, d)> pubkey,

std::array<uint8_t, c_tilda.size()> c_tilda_prime{};

hasher.reset();
shake256::shake256_t hasher;
hasher.absorb(mu);
hasher.absorb(w1_encoded);
hasher.finalize();
Expand All @@ -423,4 +431,42 @@ verify(std::span<const uint8_t, ml_dsa_utils::pub_key_len(k, d)> pubkey,
return std::equal(c_tilda.begin(), c_tilda.end(), c_tilda_prime.begin());
}

// Given a ML-DSA public key, message (can be empty too), context (optional, but if given, length must be capped at 255 -bytes)
// and serialized signature, this routine verifies validity of the signature, returning boolean result, denoting status
// of signature verification. For example, say it returns true, it means signature is valid for given message and public key.
//
// See algorithm 3 of ML-DSA standard @ https://doi.org/10.6028/NIST.FIPS.204.
template<size_t k, size_t l, size_t d, uint32_t gamma1, uint32_t gamma2, uint32_t tau, uint32_t beta, size_t omega, size_t lambda>
static inline constexpr bool
verify(std::span<const uint8_t, ml_dsa_utils::pub_key_len(k, d)> pubkey,
std::span<const uint8_t> msg,
std::span<const uint8_t> ctx,
std::span<const uint8_t, ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda)> sig)
requires(ml_dsa_params::check_verify_params(k, l, d, gamma1, gamma2, tau, beta, omega, lambda))
{
if (ctx.size() > std::numeric_limits<uint8_t>::max()) {
return false;
}

std::array<uint8_t, 64> mu{};
std::array<uint8_t, 64> tr{};

shake256::shake256_t hasher;
hasher.absorb(pubkey);
hasher.finalize();
hasher.squeeze(tr);

const std::array<uint8_t, 2> domain_separator{ 0, static_cast<uint8_t>(ctx.size()) };

hasher.reset();
hasher.absorb(tr);
hasher.absorb(domain_separator);
hasher.absorb(ctx);
hasher.absorb(msg);
hasher.finalize();
hasher.squeeze(mu);

return verify_internal<k, l, d, gamma1, gamma2, tau, beta, omega, lambda>(pubkey, mu, sig);
}

}
12 changes: 6 additions & 6 deletions include/ml_dsa/internals/utility/params.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ check_signing_params(const size_t k,
const size_t omega,
const size_t lambda)
{
return ((k == 4) && (l == 4) && (d == 13) && (eta == 2) && (gamma1 == (1u << 17)) && (gamma2 == ((ml_dsa_field::Q - 1) / 88)) && (tau == 39) && (beta == tau * eta) && (omega == 80) &&
(lambda == 128)) || // ML-DSA-44
((k == 6) && (l == 5) && (d == 13) && (eta == 4) && (gamma1 == (1u << 19)) && (gamma2 == ((ml_dsa_field::Q - 1) / 32)) && (tau == 49) && (beta == tau * eta) && (omega == 55) &&
(lambda == 192)) || // ML-DSA-65
((k == 8) && (l == 7) && (d == 13) && (eta == 2) && (gamma1 == (1u << 19)) && (gamma2 == ((ml_dsa_field::Q - 1) / 32)) && (tau == 60) && (beta == tau * eta) && (omega == 75) &&
(lambda == 256)); // ML-DSA-87
return ((k == 4) && (l == 4) && (d == 13) && (eta == 2) && (gamma1 == (1u << 17)) && (gamma2 == ((ml_dsa_field::Q - 1) / 88)) && (tau == 39) && (beta == tau * eta) &&
(omega == 80) && (lambda == 128)) || // ML-DSA-44
((k == 6) && (l == 5) && (d == 13) && (eta == 4) && (gamma1 == (1u << 19)) && (gamma2 == ((ml_dsa_field::Q - 1) / 32)) && (tau == 49) && (beta == tau * eta) &&
(omega == 55) && (lambda == 192)) || // ML-DSA-65
((k == 8) && (l == 7) && (d == 13) && (eta == 2) && (gamma1 == (1u << 19)) && (gamma2 == ((ml_dsa_field::Q - 1) / 32)) && (tau == 60) && (beta == tau * eta) &&
(omega == 75) && (lambda == 256)); // ML-DSA-87
}

// Compile-time executable constraints for ensuring that ML-DSA verification algorithm is only invoked with arguments
Expand Down
26 changes: 26 additions & 0 deletions include/ml_dsa/ml_dsa_44.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static constexpr size_t SecKeyByteLen = ml_dsa_utils::sec_key_len(k, l, eta, d);
// Byte length ( = 32 ) of ML-DSA-44 signing seed.
static constexpr size_t SigningSeedByteLen = ml_dsa::RND_BYTE_LEN;

// Byte length ( = 64 ) of ML-DSA-44 message representative.
static constexpr size_t MessageRepresentativeByteLen = ml_dsa::MU_BYTE_LEN;

// Byte length ( = 2420 ) of ML-DSA-44 signature.
static constexpr size_t SigByteLen = ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda);

Expand All @@ -53,6 +56,20 @@ sign(std::span<const uint8_t, SigningSeedByteLen> rnd,
return ml_dsa::sign<k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda>(rnd, seckey, msg, ctx, sig);
}

// Given a 32 -bytes seed `rnd` and ML-DSA-44 secret key, this routine can be used for signing a 64 -bytes message representative,
// producing a ML-DSA-44 signature S.
//
// Default (and recommended) signing mode is "hedged" i.e. using 32B input randomness for signing, results into
// randomized signature. For "deterministic" signing mode, simply fill `rnd` with zero bytes.
constexpr bool
sign_internal(std::span<const uint8_t, SigningSeedByteLen> rnd,
std::span<const uint8_t, SecKeyByteLen> seckey,
std::span<const uint8_t, MessageRepresentativeByteLen> mu,
std::span<uint8_t, SigByteLen> sig)
{
return ml_dsa::sign_internal<k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda>(rnd, seckey, mu, sig);
}

// Given a ML-DSA-44 public key, a message M, an optional context C (of length at max 255 -bytes) and a signature S,
// this routine can be used for verifying if the signature is valid for the provided message or not, returning truth
// value only in case of successful signature verification, otherwise false is returned.
Expand All @@ -62,4 +79,13 @@ verify(std::span<const uint8_t, PubKeyByteLen> pubkey, std::span<const uint8_t>
return ml_dsa::verify<k, l, d, gamma1, gamma2, tau, beta, omega, lambda>(pubkey, msg, ctx, sig);
}

// Given a ML-DSA-44 public key, a message representative mu and a signature S,
// this routine can be used for verifying if the signature is valid for the provided message or not, returning truth
// value only in case of successful signature verification, otherwise false is returned.
constexpr bool
verify_internal(std::span<const uint8_t, PubKeyByteLen> pubkey, std::span<const uint8_t, MessageRepresentativeByteLen> mu, std::span<const uint8_t, SigByteLen> sig)
{
return ml_dsa::verify_internal<k, l, d, gamma1, gamma2, tau, beta, omega, lambda>(pubkey, mu, sig);
}

}
26 changes: 26 additions & 0 deletions include/ml_dsa/ml_dsa_65.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static constexpr size_t SecKeyByteLen = ml_dsa_utils::sec_key_len(k, l, eta, d);
// Byte length ( = 32 ) of ML-DSA-65 signing seed.
static constexpr size_t SigningSeedByteLen = ml_dsa::RND_BYTE_LEN;

// Byte length ( = 64 ) of ML-DSA-65 message representative.
static constexpr size_t MessageRepresentativeByteLen = ml_dsa::MU_BYTE_LEN;

// Byte length ( = 3309 ) of ML-DSA-65 signature.
static constexpr size_t SigByteLen = ml_dsa_utils::sig_len(k, l, gamma1, omega, lambda);

Expand Down Expand Up @@ -54,6 +57,20 @@ sign(std::span<const uint8_t, SigningSeedByteLen> rnd,
return ml_dsa::sign<k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda>(rnd, seckey, msg, ctx, sig);
}

// Given a 32 -bytes seed `rnd` and ML-DSA-65 secret key, this routine can be used for signing a 64 -bytes message representative,
// producing a ML-DSA-65 signature S.
//
// Default (and recommended) signing mode is "hedged" i.e. using 32B input randomness for signing, results into
// randomized signature. For "deterministic" signing mode, simply fill `rnd` with zero bytes.
constexpr bool
sign_internal(std::span<const uint8_t, SigningSeedByteLen> rnd,
std::span<const uint8_t, SecKeyByteLen> seckey,
std::span<const uint8_t, MessageRepresentativeByteLen> mu,
std::span<uint8_t, SigByteLen> sig)
{
return ml_dsa::sign_internal<k, l, d, eta, gamma1, gamma2, tau, beta, omega, lambda>(rnd, seckey, mu, sig);
}

// Given a ML-DSA-65 public key, a message M, an optional context C (of length at max 255 -bytes) and a signature S,
// this routine can be used for verifying if the signature is valid for the provided message or not, returning truth
// value only in case of successful signature verification, otherwise false is returned.
Expand All @@ -63,4 +80,13 @@ verify(std::span<const uint8_t, PubKeyByteLen> pubkey, std::span<const uint8_t>
return ml_dsa::verify<k, l, d, gamma1, gamma2, tau, beta, omega, lambda>(pubkey, msg, ctx, sig);
}

// Given a ML-DSA-65 public key, a message representative mu and a signature S,
// this routine can be used for verifying if the signature is valid for the provided message or not, returning truth
// value only in case of successful signature verification, otherwise false is returned.
constexpr bool
verify_internal(std::span<const uint8_t, PubKeyByteLen> pubkey, std::span<const uint8_t, MessageRepresentativeByteLen> mu, std::span<const uint8_t, SigByteLen> sig)
{
return ml_dsa::verify_internal<k, l, d, gamma1, gamma2, tau, beta, omega, lambda>(pubkey, mu, sig);
}

}
Loading