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;
#[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 = [0u8; N];
rng.try_fill_bytes(&mut sk_seed).map_err(|_| "Alg17: rng failed1")?;
let mut sk_prf = [0u8; N];
rng.try_fill_bytes(&mut sk_prf).map_err(|_| "Alg17: rng failed2")?;
let mut pk_seed = [0u8; N];
rng.try_fill_bytes(&mut pk_seed).map_err(|_| "Alg17: 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_root };
let sk = SlhPrivateKey { sk_seed, sk_prf, 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 = sk.pk_seed;
if hedged {
rng.try_fill_bytes(&mut opt_rand).map_err(|_| "Alg17: 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 + 7) / 8;
let md = &digest[0..index1];
let index2 = index1 + (H - H / D + 7) / 8;
let tmp_idx_tree = &digest[index1..index2];
let index3 = index2 + (H + 8 * D - 1) / (8 * D);
let tmp_idx_leaf = &digest[index2..index3];
let idx_tree = helpers::to_int(tmp_idx_tree, (h32 - h32 / d32 + 7) / 8)
& (u64::MAX >> (64 - (h32 - h32 / d32)));
let idx_leaf = helpers::to_int(tmp_idx_leaf, (h32 + 8 * d32 - 1) / (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 + 7) / 8;
let md = &digest[0..index1];
let index2 = index1 + (H - H / D + 7) / 8;
let tmp_idx_tree = &digest[index1..index2];
let index3 = index2 + (H + 8 * D - 1) / (8 * D);
let tmp_idx_leaf = &digest[index2..index3];
let idx_tree = helpers::to_int(tmp_idx_tree, (h32 - h32 / d32 + 7) / 8)
& (u64::MAX >> (64 - (h32 - h32 / d32)));
let idx_leaf = helpers::to_int(tmp_idx_leaf, (h32 + 8 * d32 - 1) / (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,
)
}