Skip to content

Commit

Permalink
keygen from seed
Browse files Browse the repository at this point in the history
  • Loading branch information
eschorn1 committed Sep 27, 2024
1 parent 678e741 commit 8e1f24d
Show file tree
Hide file tree
Showing 16 changed files with 103 additions and 1,949 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ The Rust [Documentation][docs-link] lives under each **Module** corresponding to

## Notes

* This crate is fully functional and corresponds to FIPS 204 (August 13, 2024).
* This crate is fully functional and corresponds to FIPS 204 (August 13, 2024).
* **BEWARE:** As of September 27, 2024 NIST has not release external/hash test vectors!
* Constant-time assurances target the source-code level only on MSRV, with confirmation via
manual review/inspection, the embedded target, and the `dudect` dynamic tests.
* Note that FIPS 204 places specific requirements on randomness per section 3.5.1, hence the exposed `RNG`.
* Requires Rust **1.70** or higher. The minimum supported Rust version may be changed in the future, but
it will be done with a minor version bump (when the major version is larger than 0)..
* All on-by-default features of this library are covered by `SemVer`.
* The FIPS 204 standard and this software is considered experimental -- USE AT YOUR OWN RISK!
* The FIPS 204 standard and this software should be considered experimental -- USE AT YOUR OWN RISK!

## License

Expand Down
87 changes: 35 additions & 52 deletions src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@
use crate::conversion::{bit_unpack, coeff_from_half_byte, coeff_from_three_bytes};
use crate::helpers::{bit_length, is_in_range};
use crate::types::{Ph, R, R0, T, T0};
use sha2::{Digest, Sha256, Sha512};
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::{Shake128, Shake256};

// use sha3::Shake256Core;
// use sha3::digest::core_api::CoreWrapper;
//
// pub(crate) fn hhh_xof(v: &[&[u8]]) -> CoreWrapper<Shake256Core> {
// let mut hasher = Shake256::default();
// v.iter().for_each(|b| hasher.update(b));
// hasher
// }


/// # Function H(v,d) of (8.1) on page 29.
/// Takes a reference to a list of byte-slice references and runs them through Shake256.
Expand Down Expand Up @@ -343,48 +335,39 @@ pub(crate) fn expand_mask<const L: usize>(gamma1: i32, rho: &[u8; 64], mu: u16)

pub(crate) fn hash_message(message: &[u8], ph: &Ph, phm: &mut [u8; 64]) -> ([u8; 11], usize) {
match ph {
Ph::SHA256 => {
use sha2::{Digest, Sha256};
(
[
0x06u8, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
],
{
let mut hasher = Sha256::new();
Digest::update(&mut hasher, message); //hasher.update(message);
phm[0..32].copy_from_slice(&hasher.finalize());
32
},
)
}
Ph::SHA512 => {
use sha2::{Digest, Sha512};
(
[
0x06u8, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
],
{
let mut hasher = Sha512::new();
Digest::update(&mut hasher, message); //hasher.update(message);
phm.copy_from_slice(&hasher.finalize());
64
},
)
}
Ph::SHAKE128 => {
(
[
0x06u8, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B,
],
{
//use sha3::digest::{ExtendableOutput, Update, XofReader}; // some collide with sha2
let mut hasher = Shake128::default();
hasher.update(message);
let mut reader = hasher.finalize_xof();
reader.read(phm);
64
},
)
}
Ph::SHA256 => (
[
0x06u8, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
],
{
let mut hasher = Sha256::new();
Digest::update(&mut hasher, message);
phm[0..32].copy_from_slice(&hasher.finalize());
32
},
),
Ph::SHA512 => (
[
0x06u8, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
],
{
let mut hasher = Sha512::new();
Digest::update(&mut hasher, message);
phm.copy_from_slice(&hasher.finalize());
64
},
),
Ph::SHAKE128 => (
[
0x06u8, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B,
],
{
let mut hasher = Shake128::default();
hasher.update(message);
let mut reader = hasher.finalize_xof();
reader.read(phm);
64
},
),
}
}
84 changes: 22 additions & 62 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ macro_rules! functionality {
Ok((PublicKey { 0: pk }, PrivateKey { 0: sk }))
}


fn keygen_from_seed(xi: &[u8; 32]) -> (Self::PublicKey, Self::PrivateKey) {
// let (pk, sk) = ml_dsa::key_gen::<CTEST, K, L, PK_LEN, SK_LEN>(rng, ETA)?;
// Ok((PublicKey { 0: pk }, PrivateKey { 0: sk }))
let (pk, sk) = ml_dsa::key_gen_internal::<CTEST, K, L, PK_LEN, SK_LEN>(ETA, xi);
(PublicKey { 0: pk }, PrivateKey { 0: sk })
}

// A portion of algorithm 1 in KeyGen trait -- expanded private key for faster signing
fn gen_expanded_private(sk: &PrivateKey) -> Result<Self::ExpandedPrivateKey, &'static str> {
let esk = ml_dsa::sign_start::<CTEST, K, L, SK_LEN>(ETA, &sk.0)?;
Expand All @@ -247,18 +255,7 @@ macro_rules! functionality {
ensure!(ctx.len() < 256, "ML-DSA.Sign: ctx too long");
let esk = ml_dsa::sign_start::<CTEST, K, L, SK_LEN>(ETA, &self.0)?;
let sig = ml_dsa::sign_finish::<CTEST, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
rng,
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&esk,
message,
ctx,
&[],
&[],
false,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, ctx, &[], &[], false
)?;
Ok(sig)
}
Expand All @@ -272,7 +269,7 @@ macro_rules! functionality {
let mut phm = [0u8; 64]; // hashers don't all play well with each other
let (oid, phm_len) = hash_message(message, ph, &mut phm);
let sig = ml_dsa::sign_finish::<CTEST, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, ctx, &oid, &phm[0..phm_len], false,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &esk, message, ctx, &oid, &phm[0..phm_len], false
)?;
Ok(sig)
}
Expand All @@ -290,18 +287,7 @@ macro_rules! functionality {
) -> Result<Self::Signature, &'static str> {
ensure!(ctx.len() < 256, "ML-DSA.Sign: ctx too long");
let sig = ml_dsa::sign_finish::<CTEST, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
rng,
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&self,
message,
ctx,
&[],
&[],
false,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, message, ctx, &[], &[], false
)?;
Ok(sig)
}
Expand All @@ -316,7 +302,7 @@ macro_rules! functionality {
let mut phm = [0u8; 64]; // hashers don't all play well with each other
let (oid, phm_len) = hash_message(message, ph, &mut phm);
let sig = ml_dsa::sign_finish::<CTEST, K, L, LAMBDA_DIV4, SIG_LEN, SK_LEN, W1_LEN>(
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, message, ctx, &oid, &phm[0..phm_len], false,
rng, BETA, GAMMA1, GAMMA2, OMEGA, TAU, &self, message, ctx, &oid, &phm[0..phm_len], false
)?;
Ok(sig)
}
Expand All @@ -331,59 +317,33 @@ macro_rules! functionality {
if ctx.len() > 255 {
return false;
};
let epk = ml_dsa::verify_start(&self.0);
if epk.is_err() {
let Ok(epk) = ml_dsa::verify_start(&self.0) else {
return false;
};
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&epk.unwrap(),
&message,
&sig,
ctx,
&[],
&[],
false,
);
if res.is_err() {
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &epk, &message, &sig, ctx, &[], &[], false
) else {
return false;
};
res.unwrap()
res
}

// Algorithm 5 in Verifier trait.
fn hash_verify(&self, message: &[u8], sig: &Self::Signature, ctx: &[u8], ph: &Ph) -> bool {
if ctx.len() > 255 {
return false;
};
let epk = ml_dsa::verify_start(&self.0);
if epk.is_err() {
let Ok(epk) = ml_dsa::verify_start(&self.0) else {
return false;
};
let mut phm = [0u8; 64]; // hashers don't all play well with each other
let (oid, phm_len) = hash_message(message, ph, &mut phm);
let res = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA,
GAMMA1,
GAMMA2,
OMEGA,
TAU,
&epk.unwrap(),
&message,
&sig,
ctx,
&oid,
&phm[0..phm_len],
false,
);
if res.is_err() {
let Ok(res) = ml_dsa::verify_finish::<K, L, LAMBDA_DIV4, PK_LEN, SIG_LEN, W1_LEN>(
BETA, GAMMA1, GAMMA2, OMEGA, TAU, &epk, &message, &sig, ctx, &oid, &phm[0..phm_len], false
) else {
return false;
};
res.unwrap()
res
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/ml_dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub(crate) fn key_gen<
let mut xi = [0u8; 32];
rng.try_fill_bytes(&mut xi).map_err(|_| "Random number generator failed")?;

Ok(key_gen_internal::<CTEST, K, L, PK_LEN, SK_LEN>(eta, xi))
Ok(key_gen_internal::<CTEST, K, L, PK_LEN, SK_LEN>(eta, &xi))
}


Expand Down Expand Up @@ -444,7 +444,7 @@ pub(crate) fn key_gen_internal<
const PK_LEN: usize,
const SK_LEN: usize,
>(
eta: i32, xi: [u8; 32],
eta: i32, xi: &[u8; 32],
) -> ([u8; PK_LEN], [u8; SK_LEN]) {
//
// 1: ξ ← {0,1}^{256} ▷ Choose random seed
Expand All @@ -453,7 +453,7 @@ pub(crate) fn key_gen_internal<

// WRONG: 2: (ρ, ρ′, K) ∈ {0,1}^{256} × {0,1}^{512} × {0,1}^{256} ← H(ξ, 1024) ▷ Expand seed
// 1: (𝜌, 𝜌′, 𝐾) ∈ 𝔹32 × 𝔹64 × 𝔹32 ← H(𝜉||IntegerToBytes(𝑘, 1)||IntegerToBytes(ℓ, 1), 128)
let mut h2 = h_xof(&[&xi, &[K.to_le_bytes()[0]], &[L.to_le_bytes()[0]]]);
let mut h2 = h_xof(&[xi, &[K.to_le_bytes()[0]], &[L.to_le_bytes()[0]]]);
let mut rho = [0u8; 32];
h2.read(&mut rho);
let mut rho_prime = [0u8; 64];
Expand Down
34 changes: 34 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,40 @@ pub trait KeyGen {
rng: &mut impl CryptoRngCore,
) -> Result<(Self::PublicKey, Self::PrivateKey), &'static str>;

/// Generates an public and private key key pair specific to this security parameter set
/// based on a provided seed. <br>
/// This function operates in constant-time relative to secret data (which specifically excludes
/// the the `rho` value stored in the public key and the hash-derived `rho_prime` values that are
/// rejection-sampled/expanded into the internal `s_1` and `s_2` values).
/// # Examples
/// ```rust
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # #[cfg(feature = "ml-dsa-44")] {
/// use crate::fips204::RngCore;
/// use fips204::ml_dsa_44; // Could also be ml_dsa_65 or ml_dsa_87.
/// use fips204::traits::{KeyGen, Signer, Verifier};
/// use rand_core::OsRng;
///
/// // The signor gets the xi seed from the OS random number generator
/// let mut xi = [0u8; 32];
/// OsRng.fill_bytes(&mut xi);
/// ///
/// let message = [0u8, 1, 2, 3, 4, 5, 6, 7];
///
/// // Generate key pair and signature
/// let (pk, sk) = ml_dsa_44::KG::keygen_from_seed(&xi); // Generate both public and secret keys
/// let sig = sk.try_sign(&message, &[0])?; // Use the secret key to generate a message signature
///
/// let res = pk.verify(&message, &sig, &[0]);
/// assert!(res); // Signature accepted
/// # }
/// # Ok(())}
/// ```
#[must_use]
fn keygen_from_seed(_xi: &[u8; 32]) -> (Self::PublicKey, Self::PrivateKey);


/// Generates an expanded private key from the normal/compressed private key.
/// This supports improved signing performance. This function operates in constant-time
/// relative to secret data (which specifically excludes the provided random `rho`
Expand Down
Loading

0 comments on commit 8e1f24d

Please sign in to comment.