use super::address::{Adrs, WOTS_PK, WOTS_PRF};
use super::hash;
use super::params::Params;
use alloc::vec::Vec;
pub fn base_2b(x: &[u8], b: usize, out_len: usize) -> Vec<u32> {
let mut result = Vec::with_capacity(out_len);
let mut in_idx = 0usize;
let mut bits = 0u32;
let mut total = 0usize;
let mask = (1u32 << b) - 1;
for _ in 0..out_len {
while total < b {
bits = (bits << 8) | (x[in_idx] as u32);
in_idx += 1;
total += 8;
}
total -= b;
result.push((bits >> total) & mask);
}
result
}
fn gen_len2<P: Params>(msg_digits: &[u32]) -> Vec<u32> {
let w = P::W as u32;
let mut csum: u32 = 0;
for &d in msg_digits.iter() {
csum += w - 1 - d;
}
let shift = (8 - ((P::LEN2 * P::LG_W) % 8)) % 8;
csum <<= shift;
let num_bytes = ((P::LEN2 * P::LG_W) + shift + 7) / 8;
let csum_bytes = to_byte(csum as u64, num_bytes);
base_2b(&csum_bytes, P::LG_W, P::LEN2)
}
pub fn chain<P: Params>(x: &[u8], i: u32, s: u32, pk_seed: &[u8], adrs: &mut Adrs) -> Vec<u8> {
if s == 0 {
return x.to_vec();
}
let mut tmp = chain::<P>(x, i, s - 1, pk_seed, adrs);
adrs.set_hash_address(i + s - 1);
tmp = hash::f_hash::<P>(pk_seed, adrs, &tmp);
tmp
}
fn chain_iter<P: Params>(x: &[u8], i: u32, s: u32, pk_seed: &[u8], adrs: &mut Adrs) -> Vec<u8> {
let n = P::N;
let mut buf_a = [0u8; 32];
let mut buf_b = [0u8; 32];
buf_a[..n].copy_from_slice(&x[..n]);
for j in 0..s {
adrs.set_hash_address(i + j);
if j % 2 == 0 {
hash::f_hash_into::<P>(pk_seed, adrs, &buf_a[..n], &mut buf_b[..n]);
} else {
hash::f_hash_into::<P>(pk_seed, adrs, &buf_b[..n], &mut buf_a[..n]);
}
}
if s == 0 {
buf_a[..n].to_vec()
} else if s % 2 == 1 {
buf_b[..n].to_vec()
} else {
buf_a[..n].to_vec()
}
}
pub fn wots_pk_gen<P: Params>(sk_seed: &[u8], pk_seed: &[u8], adrs: &mut Adrs) -> Vec<u8> {
let mut sk_adrs = adrs.clone();
sk_adrs.set_type_and_clear(WOTS_PRF);
sk_adrs.set_key_pair_address(adrs.get_key_pair_address());
let mut tmp = Vec::with_capacity(P::LEN * P::N);
for i in 0..P::LEN {
sk_adrs.set_chain_address(i as u32);
let sk = hash::prf::<P>(pk_seed, sk_seed, &sk_adrs);
adrs.set_chain_address(i as u32);
let pk_i = chain_iter::<P>(&sk, 0, (P::W - 1) as u32, pk_seed, adrs);
tmp.extend_from_slice(&pk_i);
}
let mut wots_pk_adrs = adrs.clone();
wots_pk_adrs.set_type_and_clear(WOTS_PK);
wots_pk_adrs.set_key_pair_address(adrs.get_key_pair_address());
hash::t_l::<P>(pk_seed, &wots_pk_adrs, &tmp)
}
pub fn wots_sign<P: Params>(m: &[u8], sk_seed: &[u8], pk_seed: &[u8], adrs: &mut Adrs) -> Vec<u8> {
let mut sig = alloc::vec![0u8; P::LEN * P::N];
wots_sign_into::<P>(m, sk_seed, pk_seed, adrs, &mut sig);
sig
}
pub fn wots_sign_into<P: Params>(m: &[u8], sk_seed: &[u8], pk_seed: &[u8], adrs: &mut Adrs, out: &mut [u8]) {
debug_assert!(out.len() >= P::LEN * P::N);
let msg_digits = base_2b(m, P::LG_W, P::LEN1);
let csum_digits = gen_len2::<P>(&msg_digits);
let mut digits = msg_digits;
digits.extend_from_slice(&csum_digits);
let mut sk_adrs = adrs.clone();
sk_adrs.set_type_and_clear(WOTS_PRF);
sk_adrs.set_key_pair_address(adrs.get_key_pair_address());
for i in 0..P::LEN {
sk_adrs.set_chain_address(i as u32);
let sk = hash::prf::<P>(pk_seed, sk_seed, &sk_adrs);
adrs.set_chain_address(i as u32);
let sig_i = chain_iter::<P>(&sk, 0, digits[i], pk_seed, adrs);
out[i * P::N..(i + 1) * P::N].copy_from_slice(&sig_i);
}
}
pub fn wots_pk_from_sig<P: Params>(sig: &[u8], m: &[u8], pk_seed: &[u8], adrs: &mut Adrs) -> Vec<u8> {
let msg_digits = base_2b(m, P::LG_W, P::LEN1);
let csum_digits = gen_len2::<P>(&msg_digits);
let mut digits = msg_digits;
digits.extend_from_slice(&csum_digits);
let mut tmp = Vec::with_capacity(P::LEN * P::N);
for i in 0..P::LEN {
adrs.set_chain_address(i as u32);
let sig_i = &sig[i * P::N..(i + 1) * P::N];
let w_minus_1 = (P::W - 1) as u32;
let pk_i = chain_iter::<P>(sig_i, digits[i], w_minus_1 - digits[i], pk_seed, adrs);
tmp.extend_from_slice(&pk_i);
}
let mut wots_pk_adrs = adrs.clone();
wots_pk_adrs.set_type_and_clear(WOTS_PK);
wots_pk_adrs.set_key_pair_address(adrs.get_key_pair_address());
hash::t_l::<P>(pk_seed, &wots_pk_adrs, &tmp)
}
pub fn to_byte(val: u64, n: usize) -> Vec<u8> {
let mut result = vec![0u8; n];
let mut v = val;
for i in (0..n).rev() {
result[i] = (v & 0xff) as u8;
v >>= 8;
}
result
}
pub fn to_byte_stack(val: u64, n: usize) -> [u8; 8] {
let mut result = [0u8; 8];
let mut v = val;
let start = 8 - n;
for i in (start..8).rev() {
result[i] = (v & 0xff) as u8;
v >>= 8;
}
result
}