Skip to content

Commit

Permalink
Merge pull request #608 from cryspen/jonas/pre-hashed
Browse files Browse the repository at this point in the history
[ML-DSA] Add pre-hashed API & domain separation
  • Loading branch information
jschneider-bensch authored Oct 1, 2024
2 parents f8a7d13 + 86e7879 commit fd43045
Show file tree
Hide file tree
Showing 41 changed files with 9,823 additions and 5,697 deletions.
6 changes: 3 additions & 3 deletions libcrux-ml-dsa/benches/bench_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ macro_rules! bench_group_libcrux {
$keypair_t,
[u8; 1023],
[u8; SIGNING_RANDOMNESS_SIZE]
)| { p::sign(&keypair.signing_key, &message, signing_randomness) }
)| { p::sign(&keypair.signing_key, &message, b"", signing_randomness) }
);

bench!(
Expand All @@ -117,11 +117,11 @@ macro_rules! bench_group_libcrux {
let message = bench_utils::random_array::<1023>();
let keypair = p::generate_key_pair(key_generation_seed);
let signature =
p::sign(&keypair.signing_key, &message, signing_randomness).unwrap();
p::sign(&keypair.signing_key, &message, b"", signing_randomness).unwrap();
(keypair, message, signature)
},
|(keypair, message, signature): ($keypair_t, [u8; 1023], $signature_t)| {
p::verify(&keypair.verification_key, &message, &signature).unwrap()
p::verify(&keypair.verification_key, &message, b"", &signature).unwrap()
}
);

Expand Down
7 changes: 4 additions & 3 deletions libcrux-ml-dsa/benches/ml-dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn comparisons_signing(c: &mut Criterion) {

group.bench_function("libcrux (external random)", move |b| {
b.iter(|| {
let _ = ml_dsa_65::sign(&keypair.signing_key, &message, randomness);
let _ = ml_dsa_65::sign(&keypair.signing_key, &message, b"", randomness);
})
});

Expand All @@ -67,11 +67,12 @@ pub fn comparisons_verification(c: &mut Criterion) {
let keypair = ml_dsa_65::generate_key_pair(randomness);

rng.fill_bytes(&mut randomness);
let signature = ml_dsa_65::sign(&keypair.signing_key, &message, randomness).unwrap();
let signature = ml_dsa_65::sign(&keypair.signing_key, &message, b"", randomness).unwrap();

group.bench_function("libcrux", move |b| {
b.iter(|| {
let _ = ml_dsa_65::verify(&keypair.verification_key, &message, &signature).unwrap();
let _ =
ml_dsa_65::verify(&keypair.verification_key, &message, b"", &signature).unwrap();
})
});

Expand Down
7 changes: 6 additions & 1 deletion libcrux-ml-dsa/examples/sign_44.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ fn main() {
let keypair = generate_key_pair(key_generation_seed);

for _i in 0..100_000 {
let _ = core::hint::black_box(sign(&keypair.signing_key, &message, signing_randomness));
let _ = core::hint::black_box(sign(
&keypair.signing_key,
&message,
b"",
signing_randomness,
));
}
}
2 changes: 1 addition & 1 deletion libcrux-ml-dsa/examples/sign_65.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ fn main() {
let keypair = ml_dsa_65::generate_key_pair(key_generation_seed);

for _i in 0..100_000 {
let _ = ml_dsa_65::sign(&keypair.signing_key, &message, signing_randomness);
let _ = ml_dsa_65::sign(&keypair.signing_key, &message, b"", signing_randomness);
}
}
5 changes: 3 additions & 2 deletions libcrux-ml-dsa/examples/verify_65.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ fn main() {
let key_generation_seed = random_array();
let signing_randomness = random_array();
let message = random_array::<1023>();
let context = b"";

let keypair = ml_dsa_65::generate_key_pair(key_generation_seed);
let signature = ml_dsa_65::sign(&keypair.signing_key, &message, signing_randomness)
let signature = ml_dsa_65::sign(&keypair.signing_key, &message, context, signing_randomness)
.expect("Rejection sampling failure probability is < 2⁻¹²⁸");

for _i in 0..100_000 {
let _ = ml_dsa_65::verify(&keypair.verification_key, &message, &signature);
let _ = ml_dsa_65::verify(&keypair.verification_key, &message, context, &signature);
}
}
3 changes: 3 additions & 0 deletions libcrux-ml-dsa/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ pub(crate) const MESSAGE_REPRESENTATIVE_SIZE: usize = 64;
pub(crate) const MASK_SEED_SIZE: usize = 64;

pub(crate) const REJECTION_SAMPLE_BOUND_SIGN: usize = 814;

/// The length of `context` is serialized to a single `u8`.
pub(crate) const CONTEXT_MAX_LEN: usize = 255;
15 changes: 14 additions & 1 deletion libcrux-ml-dsa/src/hash_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ pub(crate) mod shake128 {
pub(crate) const BLOCK_SIZE: usize = 168;
pub(crate) const FIVE_BLOCKS_SIZE: usize = BLOCK_SIZE * 5;

pub(crate) trait Xof {
fn shake128<const OUTPUT_LENGTH: usize>(input: &[u8], out: &mut [u8; OUTPUT_LENGTH]);
}

/// When sampling matrix A we always want to do 4 absorb/squeeze calls in
/// parallel.
pub(crate) trait XofX4 {
Expand All @@ -74,7 +78,7 @@ pub(crate) mod shake128 {
pub(crate) mod portable {
use libcrux_sha3::portable::{
incremental::{self, shake128_absorb_final, shake128_init},
shake256, KeccakState,
shake128, shake256, KeccakState,
};

use super::{shake128, shake256};
Expand Down Expand Up @@ -146,6 +150,15 @@ pub(crate) mod portable {
}
}

/// Portable SHAKE 128 state
pub(crate) struct Shake128 {}

impl shake128::Xof for Shake128 {
fn shake128<const OUTPUT_LENGTH: usize>(input: &[u8], out: &mut [u8; OUTPUT_LENGTH]) {
shake128(out, input);
}
}

/// Portable SHAKE 256 state
pub(crate) struct Shake256 {
state: KeccakState,
Expand Down
2 changes: 1 addition & 1 deletion libcrux-ml-dsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ mod matrix;
mod ml_dsa_generic;
mod ntt;
mod polynomial;
mod pre_hash;
mod sample;
mod samplex4;
mod simd;
mod types;
mod utils;

// Public interface

pub use {
Expand Down
152 changes: 148 additions & 4 deletions libcrux-ml-dsa/src/ml_dsa_44.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,14 @@ macro_rules! instantiate {
}

/// Generate an ML-DSA-44 Signature
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
pub fn sign(
signing_key: &MLDSA44SigningKey,
message: &[u8],
context: &[u8],
randomness: [u8; SIGNING_RANDOMNESS_SIZE],
) -> Result<MLDSA44Signature, SigningError> {
p::sign::<
Expand All @@ -113,13 +118,47 @@ macro_rules! instantiate {
GAMMA1_RING_ELEMENT_SIZE,
SIGNING_KEY_SIZE,
SIGNATURE_SIZE,
>(&signing_key.0, message, randomness)
>(&signing_key.0, message, context, randomness)
}

/// Generate a HashML-DSA-44 Signature, with a SHAKE128 pre-hashing
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
pub fn sign_pre_hashed_shake128(
signing_key: &MLDSA44SigningKey,
message: &[u8],
context: &[u8],
randomness: [u8; SIGNING_RANDOMNESS_SIZE],
) -> Result<MLDSA44Signature, SigningError> {
p::sign_pre_hashed_shake128::<
ROWS_IN_A,
COLUMNS_IN_A,
ETA,
ERROR_RING_ELEMENT_SIZE,
GAMMA1_EXPONENT,
GAMMA2,
COMMITMENT_RING_ELEMENT_SIZE,
COMMITMENT_VECTOR_SIZE,
COMMITMENT_HASH_SIZE,
ONES_IN_VERIFIER_CHALLENGE,
MAX_ONES_IN_HINT,
GAMMA1_RING_ELEMENT_SIZE,
SIGNING_KEY_SIZE,
SIGNATURE_SIZE,
>(&signing_key.0, message, context, randomness)
}

/// Verify an ML-DSA-44 Signature
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
pub fn verify(
verification_key: &MLDSA44VerificationKey,
message: &[u8],
context: &[u8],
signature: &MLDSA44Signature,
) -> Result<(), VerificationError> {
p::verify::<
Expand All @@ -136,7 +175,35 @@ macro_rules! instantiate {
COMMITMENT_HASH_SIZE,
ONES_IN_VERIFIER_CHALLENGE,
MAX_ONES_IN_HINT,
>(&verification_key.0, message, &signature.0)
>(&verification_key.0, message, context, &signature.0)
}

/// Verify a HashML-DSA-44 Signature, with a SHAKE128 pre-hashing
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
pub fn verify_pre_hashed_shake128(
verification_key: &MLDSA44VerificationKey,
message: &[u8],
context: &[u8],
signature: &MLDSA44Signature,
) -> Result<(), VerificationError> {
p::verify_pre_hashed_shake128::<
ROWS_IN_A,
COLUMNS_IN_A,
SIGNATURE_SIZE,
VERIFICATION_KEY_SIZE,
GAMMA1_EXPONENT,
GAMMA1_RING_ELEMENT_SIZE,
GAMMA2,
BETA,
COMMITMENT_RING_ELEMENT_SIZE,
COMMITMENT_VECTOR_SIZE,
COMMITMENT_HASH_SIZE,
ONES_IN_VERIFIER_CHALLENGE,
MAX_ONES_IN_HINT,
>(&verification_key.0, message, context, &signature.0)
}
}
};
Expand Down Expand Up @@ -177,11 +244,16 @@ pub fn generate_key_pair(randomness: [u8; KEY_GENERATION_RANDOMNESS_SIZE]) -> ML
///
/// Sign a `message` with the ML-DSA `signing_key`.
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
///
/// This function returns an [`MLDSA44Signature`].
#[cfg(not(eurydice))]
pub fn sign(
signing_key: &MLDSA44SigningKey,
message: &[u8],
context: &[u8],
randomness: [u8; SIGNING_RANDOMNESS_SIZE],
) -> Result<MLDSA44Signature, SigningError> {
multiplexing::sign::<
Expand All @@ -199,17 +271,22 @@ pub fn sign(
GAMMA1_RING_ELEMENT_SIZE,
SIGNING_KEY_SIZE,
SIGNATURE_SIZE,
>(&signing_key.0, message, randomness)
>(&signing_key.0, message, context, randomness)
}

/// Verify an ML-DSA-44 Signature
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
///
/// Returns `Ok` when the `signature` is valid for the `message` and
/// `verification_key`, and a [`VerificationError`] otherwise.
#[cfg(not(eurydice))]
pub fn verify(
verification_key: &MLDSA44VerificationKey,
message: &[u8],
context: &[u8],
signature: &MLDSA44Signature,
) -> Result<(), VerificationError> {
multiplexing::verify::<
Expand All @@ -226,5 +303,72 @@ pub fn verify(
COMMITMENT_HASH_SIZE,
ONES_IN_VERIFIER_CHALLENGE,
MAX_ONES_IN_HINT,
>(&verification_key.0, message, &signature.0)
>(&verification_key.0, message, context, &signature.0)
}

/// Sign with HashML-DSA 44, with a SHAKE128 pre-hashing
///
/// Sign a digest of `message` derived using `pre_hash` with the
/// ML-DSA `signing_key`.
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
///
/// This function returns an [`MLDSA44Signature`].
#[cfg(not(eurydice))]
pub fn sign_pre_hashed_shake128(
signing_key: &MLDSA44SigningKey,
message: &[u8],
context: &[u8],
randomness: [u8; SIGNING_RANDOMNESS_SIZE],
) -> Result<MLDSA44Signature, SigningError> {
multiplexing::sign_pre_hashed_shake128::<
ROWS_IN_A,
COLUMNS_IN_A,
ETA,
ERROR_RING_ELEMENT_SIZE,
GAMMA1_EXPONENT,
GAMMA2,
COMMITMENT_RING_ELEMENT_SIZE,
COMMITMENT_VECTOR_SIZE,
COMMITMENT_HASH_SIZE,
ONES_IN_VERIFIER_CHALLENGE,
MAX_ONES_IN_HINT,
GAMMA1_RING_ELEMENT_SIZE,
SIGNING_KEY_SIZE,
SIGNATURE_SIZE,
>(&signing_key.0, message, context, randomness)
}

/// Verify a HashML-DSA-44 Signature, with a SHAKE128 pre-hashing
///
/// The parameter `context` is used for domain separation
/// and is a byte string of length at most 255 bytes. It
/// may also be empty.
///
/// Returns `Ok` when the `signature` is valid for the `message` and
/// `verification_key`, and a [`VerificationError`] otherwise.
#[cfg(not(eurydice))]
pub fn verify_pre_hashed_shake128(
verification_key: &MLDSA44VerificationKey,
message: &[u8],
context: &[u8],
signature: &MLDSA44Signature,
) -> Result<(), VerificationError> {
multiplexing::verify_pre_hashed_shake128::<
ROWS_IN_A,
COLUMNS_IN_A,
SIGNATURE_SIZE,
VERIFICATION_KEY_SIZE,
GAMMA1_EXPONENT,
GAMMA1_RING_ELEMENT_SIZE,
GAMMA2,
BETA,
COMMITMENT_RING_ELEMENT_SIZE,
COMMITMENT_VECTOR_SIZE,
COMMITMENT_HASH_SIZE,
ONES_IN_VERIFIER_CHALLENGE,
MAX_ONES_IN_HINT,
>(&verification_key.0, message, context, &signature.0)
}
Loading

0 comments on commit fd43045

Please sign in to comment.