use crate::hashers::Hashers;
use crate::helpers::base_2b;
use crate::types::{Adrs, Auth, ForsPk, ForsSig, FORS_PRF, FORS_ROOTS};
#[allow(clippy::similar_names)] pub(crate) fn fors_sk_gen<const K: usize, const LEN: usize, const M: usize, const N: usize>(
hashers: &Hashers<K, LEN, M, N>, sk_seed: &[u8], pk_seed: &[u8], adrs: &Adrs, idx: u32,
) -> [u8; N] {
let mut sk_adrs = adrs.clone();
sk_adrs.set_type_and_clear(FORS_PRF);
sk_adrs.set_key_pair_address(adrs.get_key_pair_address());
sk_adrs.set_tree_index(idx);
(hashers.prf)(pk_seed, sk_seed, &sk_adrs)
}
#[allow(clippy::similar_names)] pub(crate) fn fors_node<
const A: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, sk_seed: &[u8], i: u32, z: u32, pk_seed: &[u8], adrs: &Adrs,
) -> Result<[u8; N], &'static str> {
let mut adrs = adrs.clone();
let node = if z == 0 {
let sk = fors_sk_gen(hashers, sk_seed, pk_seed, &adrs, i);
adrs.set_tree_height(0);
adrs.set_tree_index(i);
(hashers.f)(pk_seed, &adrs, &sk)
} else {
let lnode = fors_node::<A, K, LEN, M, N>(hashers, sk_seed, 2 * i, z - 1, pk_seed, &adrs)?;
let rnode =
fors_node::<A, K, LEN, M, N>(hashers, sk_seed, 2 * i + 1, z - 1, pk_seed, &adrs)?;
adrs.set_tree_height(z);
adrs.set_tree_index(i);
(hashers.h)(pk_seed, &adrs, &lnode, &rnode)
};
Ok(node)
}
#[allow(clippy::similar_names)] pub(crate) fn fors_sign<
const A: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, md: &[u8], sk_seed: &[u8], adrs: &Adrs, pk_seed: &[u8],
) -> Result<ForsSig<A, K, N>, &'static str> {
let (a32, k32) = (u32::try_from(A).unwrap(), u32::try_from(K).unwrap());
let mut sig_fors = ForsSig {
private_key_value: [[0u8; N]; K],
auth: core::array::from_fn(|_| Auth { tree: [[0u8; N]; A] }),
};
let mut indices = [0u32; K];
base_2b(md, a32, k32, &mut indices);
for i in 0..k32 {
sig_fors.private_key_value[i as usize] = fors_sk_gen::<K, LEN, M, N>(
hashers,
sk_seed,
pk_seed,
adrs,
(i << a32) + indices[i as usize],
);
for j in 0..a32 {
let s = (indices[i as usize] >> j) ^ 1;
sig_fors.auth[i as usize].tree[j as usize] = fors_node::<A, K, LEN, M, N>(
hashers,
sk_seed,
(i << (a32 - j)) + s,
j,
pk_seed,
adrs,
)?;
}
}
Ok(sig_fors)
}
#[allow(clippy::similar_names)]
pub(crate) fn fors_pk_from_sig<
const A: usize,
const K: usize,
const LEN: usize,
const M: usize,
const N: usize,
>(
hashers: &Hashers<K, LEN, M, N>, sig_fors: &ForsSig<A, K, N>, md: &[u8], pk_seed: &[u8],
adrs: &Adrs,
) -> ForsPk<N> {
let (a32, k32) = (u32::try_from(A).unwrap(), u32::try_from(K).unwrap());
let mut adrs = adrs.clone();
let mut indices = [0u32; K];
base_2b(md, a32, k32, &mut indices);
let mut root = [[0u8; N]; K];
for i in 0..k32 {
let sk = sig_fors.private_key_value[i as usize];
adrs.set_tree_height(0);
adrs.set_tree_index((i << a32) + indices[i as usize]);
let mut node_0 = (hashers.f)(pk_seed, &adrs, &sk);
let auth = sig_fors.auth[i as usize].clone();
for j in 0..a32 {
adrs.set_tree_height(j + 1);
let node_1 = if ((indices[i as usize] >> j) & 0x01) == 0 {
let tmp = adrs.get_tree_index() / 2;
adrs.set_tree_index(tmp);
(hashers.h)(pk_seed, &adrs, &node_0, &auth.tree[j as usize])
} else {
let tmp = (adrs.get_tree_index() - 1) / 2;
adrs.set_tree_index(tmp);
(hashers.h)(pk_seed, &adrs, &auth.tree[j as usize], &node_0)
};
node_0 = node_1;
}
root[i as usize] = node_0;
}
let mut fors_pk_adrs = adrs.clone();
fors_pk_adrs.set_type_and_clear(FORS_ROOTS);
fors_pk_adrs.set_key_pair_address(adrs.get_key_pair_address());
let pk = (hashers.t_len)(pk_seed, &fors_pk_adrs, &root);
ForsPk { key: pk }
}