Skip to content

Commit

Permalink
v0.4.2 RC1
Browse files Browse the repository at this point in the history
  • Loading branch information
eschorn1 committed Oct 5, 2024
1 parent 14abbe7 commit 499df9c
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 11 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.4.2 (2024-10-05)

- Fixed size of SHAKE128 digest in `hash_message()`
- Added sk.get_public_key()


## 0.4.1 (2024-09-30)

- Now exports the pre-hash function enum
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ workspace = { exclude = ["ct_cm4", "dudect", "fuzz", "wasm"] }

[package]
name = "fips204"
version = "0.4.1"
version = "0.4.2"
authors = ["Eric Schorn <[email protected]>"]
description = "FIPS 204: Module-Lattice-Based Digital Signature"
categories = ["cryptography", "no-std"]
Expand Down
4 changes: 2 additions & 2 deletions src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ pub(crate) fn hash_message(message: &[u8], ph: &Ph, phm: &mut [u8; 64]) -> ([u8;
let mut hasher = Shake128::default();
hasher.update(message);
let mut reader = hasher.finalize_xof();
reader.read(phm);
64
reader.read(&mut phm[0..32]);
32
},
),
}
Expand Down
29 changes: 22 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ macro_rules! functionality {

impl Signer for PrivateKey {
type Signature = [u8; SIG_LEN];
type PublicKey = PublicKey;

// Algorithm 2 in Signer trait.
fn try_sign_with_rng(
Expand All @@ -276,11 +277,18 @@ macro_rules! functionality {
)?;
Ok(sig)
}

// Documented in traits.rs
fn get_public_key(&self) -> Self::PublicKey {
let pk = crate::ml_dsa::private_to_public::<CTEST, K, L, PK_LEN, SK_LEN>(ETA, &self.0);
PublicKey { 0: pk }
}
}


impl Signer for ExpandedPrivateKey {
type Signature = [u8; SIG_LEN];
type PublicKey = [u8; PK_LEN];

// Algorithm 2 in Signer trait. Rather than an external+internal split, this split of
// start+finish enables the ability of signing with a pre-computeed expanded private
Expand Down Expand Up @@ -309,6 +317,11 @@ macro_rules! functionality {
)?;
Ok(sig)
}

// Documented in traits.rs
fn get_public_key(&self) -> Self::PublicKey {
unimplemented!() // Note that ExpandedPublicKey does not currently contain Rho
}
}


Expand Down Expand Up @@ -420,18 +433,20 @@ macro_rules! functionality {
#[test]
fn smoke_test() {
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
let message = [0u8, 1, 2, 3, 4, 5, 6, 7];
let message1 = [0u8, 1, 2, 3, 4, 5, 6, 7];
let message2 = [7u8, 7, 7, 7, 7, 7, 7, 7];

for _i in 0..100 {
for _i in 0..32 {
let (pk, sk) = try_keygen_with_rng(&mut rng).unwrap();
let sig = sk.try_sign_with_rng(&mut rng, &message, &[]).unwrap();
let v = pk.verify(&message, &sig, &[]);
assert!(v);
let sig = sk.try_sign_with_rng(&mut rng, &message1, &[]).unwrap();
assert!(pk.verify(&message1, &sig, &[]));
assert!(!pk.verify(&message2, &sig, &[]));
for ph in [Ph::SHA256, Ph::SHA512, Ph::SHAKE128] {
let sig = sk.try_hash_sign_with_rng(&mut rng, &message, &[], &ph).unwrap();
let v = pk.hash_verify(&message, &sig, &[], &ph);
let sig = sk.try_hash_sign_with_rng(&mut rng, &message1, &[], &ph).unwrap();
let v = pk.hash_verify(&message1, &sig, &[], &ph);
assert!(v);
}
assert_eq!(pk.into_bytes(), sk.get_public_key().into_bytes());
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions src/ml_dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ pub(crate) fn verify_finish<
Ok(left && center && right)
}


/// Algorithm: 6 `ML-DSA.KeyGen_internal()` on page 15.
/// Generates a public-private key pair.
///
Expand Down Expand Up @@ -492,3 +493,40 @@ pub(crate) fn key_gen_internal<
// 10: return (pk, sk)
(pk, sk)
}


pub(crate) fn private_to_public<
const CTEST: bool,
const K: usize,
const L: usize,
const PK_LEN: usize,
const SK_LEN: usize,
>(
eta: i32, sk: &[u8; SK_LEN],
) -> [u8; PK_LEN] {
//
// 1: (ρ, K, tr, s_1, s_2, t_0) ← skDecode(sk)
// Code can only arrive here from keygen or a deserialized and validated sk
let (rho, _cap_k, _tr, s_1, s_2, sk_t_0) = sk_decode(eta, sk).unwrap();

// 3: cap_a_hat ← ExpandA(ρ) ▷ A is generated and stored in NTT representation as Â
let cap_a_hat: [[T; L]; K] = expand_a::<CTEST, K, L>(rho);

// 5: t ← NTT−1(cap_a_hat ◦ NTT(s_1)) + s_2 ▷ Compute t = As1 + s2
let t: [R; K] = {
let s_1_hat: [T; L] = ntt(&s_1);
let as1_hat: [T; K] = mat_vec_mul(&cap_a_hat, &s_1_hat);
let t_not_reduced: [R; K] = vec_add(&inv_ntt(&as1_hat), &s_2);
core::array::from_fn(|k| R(core::array::from_fn(|n| full_reduce32(t_not_reduced[k].0[n]))))
};

// 6: (t_1, t_0) ← Power2Round(t, d) ▷ Compress t
let (t_1, pk_t_0): ([R; K], [R; K]) = power2round(&t);
debug_assert_eq!(sk_t_0, pk_t_0); // fuzz target

// 7: pk ← pkEncode(ρ, t_1)
let pk: [u8; PK_LEN] = pk_encode(rho, &t_1);

// 10: return (pk) # , sk)
pk
}
24 changes: 24 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ pub trait KeyGen {
pub trait Signer {
/// The signature is specific to the chosen security parameter set, e.g., ml-dsa-44, ml-dsa-65 or ml-dsa-87
type Signature;
/// The public key that corresponds to the private/secret key
type PublicKey;

/// Attempt to sign the given message, returning a digital signature on success, or an error if
/// something went wrong. This function utilizes the **OS default** random number generator.
Expand Down Expand Up @@ -215,6 +217,28 @@ pub trait Signer {
fn try_hash_sign_with_rng(
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph,
) -> Result<Self::Signature, &'static str>;


/// Retrieves the public key associated with this private/secret key
/// # Examples
/// ```rust
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use fips204::ml_dsa_65; // Could also be ml_dsa_44 or ml_dsa_87.
/// use fips204::traits::{KeyGen, SerDes, Signer, Verifier};
///
///
/// // Generate both public and secret keys
/// let (pk1, sk) = ml_dsa_65::KG::try_keygen()?; // Generate both public and secret keys
///
///
/// // The public key can be derived from the secret key
/// let pk2 = sk.get_public_key();
/// assert_eq!(pk1.into_bytes(), pk2.into_bytes());
/// # Ok(())
/// # }
/// ```
fn get_public_key(&self) -> Self::PublicKey;
}


Expand Down
2 changes: 1 addition & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub struct ExpandedPublicKey<const K: usize, const L: usize> {


/// Polynomial coefficients in R, with default R0
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
#[derive(Clone, Debug, PartialEq, Zeroize, ZeroizeOnDrop)]
#[repr(align(8))]
pub(crate) struct R(pub(crate) [i32; 256]);
pub(crate) const R0: R = R([0i32; 256]);
Expand Down

0 comments on commit 499df9c

Please sign in to comment.