use crate::ledger::sha512;
use alloc::vec::Vec;
const N: usize = 64; const W: usize = 16; const MSG_DIGITS: usize = 128; const CKSUM_DIGITS: usize = 3; pub const LEN: usize = MSG_DIGITS + CKSUM_DIGITS;
fn chain(mut x: [u8; N], iters: usize) -> [u8; N] {
for _ in 0..iters {
x = sha512(&x);
}
x
}
fn sk_element(seed: &[u8; N], i: usize) -> [u8; N] {
let mut buf = Vec::with_capacity(N + 8);
buf.extend_from_slice(seed);
buf.extend_from_slice(&(i as u64).to_be_bytes());
sha512(&buf)
}
fn digits(msg: &[u8; N]) -> [usize; LEN] {
let mut d = [0usize; LEN];
for (i, &b) in msg.iter().enumerate() {
d[i * 2] = (b >> 4) as usize;
d[i * 2 + 1] = (b & 0x0F) as usize;
}
let mut cksum = 0usize;
for &di in d.iter().take(MSG_DIGITS) {
cksum += (W - 1) - di;
}
d[MSG_DIGITS] = (cksum >> 8) & 0x0F;
d[MSG_DIGITS + 1] = (cksum >> 4) & 0x0F;
d[MSG_DIGITS + 2] = cksum & 0x0F;
d
}
pub fn keygen(seed: &[u8; N]) -> [u8; N] {
let mut concat = Vec::with_capacity(LEN * N);
for i in 0..LEN {
concat.extend_from_slice(&chain(sk_element(seed, i), W - 1));
}
sha512(&concat)
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Signature(pub Vec<[u8; N]>);
pub fn sign(seed: &[u8; N], msg: &[u8; N]) -> Signature {
let d = digits(msg);
let mut sig = Vec::with_capacity(LEN);
for (i, &di) in d.iter().enumerate() {
sig.push(chain(sk_element(seed, i), di));
}
Signature(sig)
}
pub fn pk_from_sig(msg: &[u8; N], sig: &Signature) -> Option<[u8; N]> {
if sig.0.len() != LEN {
return None;
}
let d = digits(msg);
let mut concat = Vec::with_capacity(LEN * N);
for (i, &di) in d.iter().enumerate() {
concat.extend_from_slice(&chain(sig.0[i], (W - 1) - di));
}
Some(sha512(&concat))
}
pub fn verify(public_key: &[u8; N], msg: &[u8; N], sig: &Signature) -> bool {
pk_from_sig(msg, sig).is_some_and(|pk| pk == *public_key)
}