use crate::hashers::Hashers;
use crate::types::{Adrs, SlhDsaSig, SlhPrivateKey, SlhPublicKey};
use crate::types::{Auth, ForsSig, HtSig, WotsSig, XmssSig, FORS_TREE};
use crate::{fors, helpers, hypertree, xmss};
use rand_core::CryptoRngCore;
use zeroize::Zeroizing;
#[allow(clippy::similar_names)] pub(crate) fn slh_keygen_with_rng<
const D: usize,
const H: usize,
const HP: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
rng: &mut impl CryptoRngCore, hashers: &Hashers<K, LEN, M, N>,
) -> Result<(SlhPrivateKey<N>, SlhPublicKey<N>), &'static str> {
let mut sk_seed = Zeroizing::new([0u8; N]);
rng.try_fill_bytes(&mut sk_seed[..]).map_err(|_| "Alg 21: rng failed1")?;
let mut sk_prf = Zeroizing::new([0u8; N]);
rng.try_fill_bytes(&mut sk_prf[..]).map_err(|_| "Alg 21: rng failed2")?;
let mut pk_seed = [0u8; N];
rng.try_fill_bytes(&mut pk_seed).map_err(|_| "Alg 21: rng failed3")?;
Ok(slh_keygen_internal::<D, H, HP, K, LEN, M, N>(
hashers, &sk_seed, &sk_prf, &pk_seed,
))
}
#[allow(clippy::similar_names)] pub(crate) fn slh_keygen_internal<
const D: usize,
const H: usize,
const HP: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, sk_seed: &[u8; N], sk_prf: &[u8; N], pk_seed: &[u8; N],
) -> (SlhPrivateKey<N>, SlhPublicKey<N>) {
let (d32, hp32) = (u32::try_from(D).unwrap(), u32::try_from(HP).unwrap());
let mut adrs = Adrs::default();
adrs.set_layer_address(d32 - 1);
let pk_root = xmss::xmss_node::<H, HP, K, LEN, M, N>(hashers, sk_seed, 0, hp32, pk_seed, &adrs);
let pk = SlhPublicKey { pk_seed: *pk_seed, pk_root };
let sk = SlhPrivateKey {
sk_seed: *sk_seed,
sk_prf: *sk_prf,
pk_seed: *pk_seed,
pk_root,
};
(sk, pk)
}
#[allow(clippy::similar_names)]
#[allow(clippy::cast_possible_truncation)] pub(crate) fn slh_sign_with_rng<
const A: usize,
const D: usize,
const H: usize,
const HP: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
rng: &mut impl CryptoRngCore, hashers: &Hashers<K, LEN, M, N>, mp: &[&[u8]],
sk: &SlhPrivateKey<N>, hedged: bool,
) -> Result<SlhDsaSig<A, D, HP, K, LEN, N>, &'static str> {
let mut opt_rand = Zeroizing::new(sk.pk_seed);
if hedged {
rng.try_fill_bytes(&mut opt_rand[..]).map_err(|_| "Alg 22: rng failed")?;
}
slh_sign_internal::<A, D, H, HP, K, LEN, M, N>(hashers, mp, sk, &opt_rand)
}
#[allow(clippy::similar_names)]
#[allow(clippy::cast_possible_truncation)] pub(crate) fn slh_sign_internal<
const A: usize,
const D: usize,
const H: usize,
const HP: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, m: &[&[u8]], sk: &SlhPrivateKey<N>, opt_rand: &[u8; N],
) -> Result<SlhDsaSig<A, D, HP, K, LEN, N>, &'static str> {
let (d32, h32) = (u32::try_from(D).unwrap(), u32::try_from(H).unwrap());
let mut adrs = Adrs::default();
let r = (hashers.prf_msg)(&sk.sk_prf, opt_rand, m);
let mut sig = SlhDsaSig {
randomness: r, fors_sig: ForsSig {
private_key_value: [[0u8; N]; K],
auth: core::array::from_fn(|_| Auth { tree: [[0u8; N]; A] }),
},
ht_sig: HtSig {
xmss_sigs: core::array::from_fn(|_| XmssSig {
sig_wots: WotsSig { data: [[0u8; N]; LEN] },
auth: [[0u8; N]; HP],
}),
},
};
let digest = (hashers.h_msg)(&r, &sk.pk_seed, &sk.pk_root, m);
let index1 = (K * A).div_ceil(8);
let md = &digest[0..index1];
let index2 = index1 + (H - H / D).div_ceil(8);
let tmp_idx_tree = &digest[index1..index2];
let index3 = index2 + H.div_ceil(8 * D);
let tmp_idx_leaf = &digest[index2..index3];
let idx_tree = helpers::to_int(tmp_idx_tree, (h32 - h32 / d32).div_ceil(8))
& (u64::MAX >> (64 - (h32 - h32 / d32)));
let idx_leaf =
helpers::to_int(tmp_idx_leaf, h32.div_ceil(8 * d32)) & (u64::MAX >> (64 - h32 / d32));
adrs.set_tree_address(idx_tree);
adrs.set_type_and_clear(FORS_TREE);
adrs.set_key_pair_address(idx_leaf as u32);
sig.fors_sig = fors::fors_sign(hashers, md, &sk.sk_seed, &adrs, &sk.pk_seed)?;
let pk_fors =
fors::fors_pk_from_sig::<A, K, LEN, M, N>(hashers, &sig.fors_sig, md, &sk.pk_seed, &adrs);
sig.ht_sig = hypertree::ht_sign::<D, H, HP, K, LEN, M, N>(
hashers, &pk_fors.key, &sk.sk_seed, &sk.pk_seed, idx_tree, idx_leaf as u32,
)?;
Ok(sig)
}
#[allow(clippy::similar_names)]
pub(crate) fn slh_verify<
const A: usize,
const D: usize,
const H: usize,
const HP: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, mp: &[&[u8]], sig: &SlhDsaSig<A, D, HP, K, LEN, N>,
pk: &SlhPublicKey<N>,
) -> bool {
slh_verify_internal::<A, D, H, HP, K, LEN, M, N>(hashers, mp, sig, pk)
}
#[allow(clippy::similar_names)]
pub(crate) fn slh_verify_internal<
const A: usize,
const D: usize,
const H: usize,
const HP: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, m: &[&[u8]], sig: &SlhDsaSig<A, D, HP, K, LEN, N>,
pk: &SlhPublicKey<N>,
) -> bool {
let (d32, h32) = (u32::try_from(D).unwrap(), u32::try_from(H).unwrap());
let mut adrs = Adrs::default();
let r = &sig.randomness;
let sig_fors = &sig.fors_sig;
let sig_ht = &sig.ht_sig;
let digest = (hashers.h_msg)(r, &pk.pk_seed, &pk.pk_root, m);
let index1 = (K * A).div_ceil(8);
let md = &digest[0..index1];
let index2 = index1 + (H - H / D).div_ceil(8);
let tmp_idx_tree = &digest[index1..index2];
let index3 = index2 + H.div_ceil(8 * D);
let tmp_idx_leaf = &digest[index2..index3];
let idx_tree = helpers::to_int(tmp_idx_tree, (h32 - h32 / d32).div_ceil(8))
& (u64::MAX >> (64 - (h32 - h32 / d32)));
let idx_leaf =
helpers::to_int(tmp_idx_leaf, h32.div_ceil(8 * d32)) & (u64::MAX >> (64 - h32 / d32));
adrs.set_tree_address(idx_tree);
adrs.set_type_and_clear(FORS_TREE);
let Ok(idx_leaf_u32) = u32::try_from(idx_leaf) else { return false }; adrs.set_key_pair_address(idx_leaf_u32);
let pk_fors =
fors::fors_pk_from_sig::<A, K, LEN, M, N>(hashers, sig_fors, md, &pk.pk_seed, &adrs);
hypertree::ht_verify::<D, HP, K, LEN, M, N>(
hashers, &pk_fors.key, sig_ht, &pk.pk_seed, idx_tree, idx_leaf_u32, &pk.pk_root,
)
}