use core::mem::MaybeUninit;
use crate::{WinternitzError, WinternitzPubkey, WinternitzRoot};
#[repr(C)]
pub struct WinternitzSignature<const N: usize> {
scalars: [[u8; 32]; N],
checksum: [[u8; 32]; 2],
}
impl<const N: usize> WinternitzSignature<N> {
pub fn new(scalars: [[u8; 32]; N], checksum: [[u8; 32]; 2]) -> Self {
const { crate::assert_n::<N>() };
Self { scalars, checksum }
}
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
self as *const Self as *const u8,
core::mem::size_of::<Self>(),
)
}
}
pub fn verify(&self, message: &[&[u8]], root: &WinternitzRoot) -> bool {
self.recover_pubkey(message).merklize() == *root
}
#[inline(always)]
pub fn verify_prehashed(&self, hash: &[u8; N], root: &WinternitzRoot) -> bool {
self.recover_pubkey_prehashed(hash).merklize() == *root
}
#[inline(always)]
pub fn recover_pubkey(&self, message: &[&[u8]]) -> WinternitzPubkey<N> {
const { crate::assert_n::<N>() };
let h = crate::hash::<N>(message);
self.recover_pubkey_prehashed(&h)
}
#[inline(always)]
pub fn recover_pubkey_prehashed(&self, hash: &[u8; N]) -> WinternitzPubkey<N> {
const { crate::assert_n::<N>() };
let mut pk_scalars: [MaybeUninit<[u8; 32]>; N] = [const { MaybeUninit::uninit() }; N];
let mut checksum_sum: u16 = 0;
for i in 0..N {
let b = hash[i];
pk_scalars[i].write(crate::chain(&self.scalars[i], 255 - b));
checksum_sum += 255u16 - b as u16;
}
let pk_checksum = [
crate::chain(&self.checksum[0], 255 - (checksum_sum >> 8) as u8),
crate::chain(&self.checksum[1], 255 - checksum_sum as u8),
];
let pk_scalars: [[u8; 32]; N] =
unsafe { core::ptr::read(pk_scalars.as_ptr() as *const [[u8; 32]; N]) };
WinternitzPubkey::new(pk_scalars, pk_checksum)
}
#[inline(always)]
pub fn hash(message: &[&[u8]]) -> [u8; N] {
crate::hash::<N>(message)
}
}
impl<const N: usize> core::fmt::Display for WinternitzSignature<N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "0x")?;
for s in self.scalars.iter().chain(self.checksum.iter()) {
for b in s {
write!(f, "{:02x}", b)?;
}
}
Ok(())
}
}
impl<const N: usize> core::fmt::Debug for WinternitzSignature<N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "WinternitzSignature {{")?;
for (i, s) in self.scalars.iter().enumerate() {
write!(f, " scalars[{}] = 0x", i)?;
for b in s {
write!(f, "{:02x}", b)?;
}
writeln!(f)?;
}
for (i, s) in self.checksum.iter().enumerate() {
write!(f, " checksum[{}] = 0x", i)?;
for b in s {
write!(f, "{:02x}", b)?;
}
writeln!(f)?;
}
write!(f, "}}")
}
}
impl<'a, const N: usize> TryFrom<&'a [u8]> for &'a WinternitzSignature<N> {
type Error = WinternitzError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
const { crate::assert_n::<N>() };
if value.len() != (N + 2) * 32 {
return Err(WinternitzError::InvalidLength);
}
Ok(unsafe { &*value.as_ptr().cast::<WinternitzSignature<N>>() })
}
}