Skip to content
Draft
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
25 changes: 13 additions & 12 deletions crates/xmss/src/wots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::*;
/// No Debug: `pre_images` are the one-time secret keys.
pub struct WotsSecretKey {
pre_images: [Digest; V],
public_key: WotsPublicKey,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand All @@ -24,21 +23,23 @@ pub struct WotsSignature {
}

impl WotsSecretKey {
pub fn random(rng: &mut impl CryptoRng, public_param: PublicParam, slot: u32) -> Self {
Self::new(rng.random(), public_param, slot)
pub fn random(rng: &mut impl CryptoRng) -> Self {
Self::new(rng.random())
}

pub fn new(pre_images: [Digest; V], public_param: PublicParam, slot: u32) -> Self {
Self {
pre_images,
public_key: WotsPublicKey(std::array::from_fn(|i| {
iterate_hash(&pre_images[i], CHAIN_LENGTH - 1, public_param, slot, i, 0)
})),
}
pub const fn new(pre_images: [Digest; V]) -> Self {
Self { pre_images }
}

pub const fn public_key(&self) -> &WotsPublicKey {
&self.public_key
/// Derives the public key by walking every chain to its final element.
///
/// This is intentionally computed on demand. Signing only needs the secret
/// pre-images, so eagerly deriving and storing these chain ends would add
/// `V * (CHAIN_LENGTH - 1)` unnecessary hashes to every signature.
pub fn public_key(&self, public_param: PublicParam, slot: u32) -> WotsPublicKey {
WotsPublicKey(std::array::from_fn(|i| {
iterate_hash(&self.pre_images[i], CHAIN_LENGTH - 1, public_param, slot, i, 0)
}))
}

pub(crate) fn sign_with_encoding(
Expand Down
10 changes: 5 additions & 5 deletions crates/xmss/src/xmss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ impl XmssPublicKey {
}
}

fn gen_wots_secret_key(seed: &[u8; 32], slot: u32, public_param: PublicParam) -> WotsSecretKey {
fn gen_wots_secret_key(seed: &[u8; 32], slot: u32) -> WotsSecretKey {
let rng_seed_fe = poseidon_prf(PRF_DOMAINSEP_WOTS_SECRET_KEY, seed, [slot as usize, 0]);
let mut rng_seed = [0u8; 32];
for (chunk, f) in rng_seed.as_chunks_mut::<4>().0.iter_mut().zip(rng_seed_fe) {
*chunk = f.as_canonical_u32().to_le_bytes();
}
let mut rng = StdRng::from_seed(rng_seed);
WotsSecretKey::random(&mut rng, public_param, slot)
WotsSecretKey::random(&mut rng)
}

fn gen_public_param(seed: &[u8; 32]) -> PublicParam {
Expand Down Expand Up @@ -150,8 +150,8 @@ fn leaf_layer(seed: &[u8; 32], public_param: &PublicParam, lo: u64, hi: u64, seq
let mut leaves: Vec<Digest> = unsafe { uninitialized_vec((hi - lo + 1) as usize) };
fill(sequential, &mut leaves, |k, out| {
let slot = (lo + k as u64) as u32;
let wots = gen_wots_secret_key(seed, slot, *public_param);
*out = wots.public_key().hash(*public_param, slot);
let wots = gen_wots_secret_key(seed, slot);
*out = wots.public_key(*public_param, slot).hash(*public_param, slot);
});
leaves
}
Expand Down Expand Up @@ -353,7 +353,7 @@ pub fn xmss_sign(
wots_encode(&message_fe, slot, &pub_key, &randomness).map(|encoding| (randomness, encoding))
})
.ok_or(XmssSignatureError::EncodingAttemptsExceeded)?;
let wots_secret_key = gen_wots_secret_key(&secret_key.seed, slot, secret_key.public_param);
let wots_secret_key = gen_wots_secret_key(&secret_key.seed, slot);
let wots_signature = wots_secret_key.sign_with_encoding(randomness, &encoding, secret_key.public_param, slot);
let cache = secret_key.cached_bottom_subtree(slot);
let sub = cache.as_ref().unwrap();
Expand Down