use core::cell::UnsafeCell;
use core::ffi::c_void;
use alloc::vec;
use alloc::vec::Vec;
use crate::error::{check, len_as_c_int, WolfCryptError};
use wolfcrypt_rs::{
wc_LmsKey_ExportPubRaw, wc_LmsKey_Free, wc_LmsKey_GetPrivLen, wc_LmsKey_GetPubLen,
wc_LmsKey_GetSigLen, wc_LmsKey_ImportPubRaw, wc_LmsKey_Init, wc_LmsKey_MakeKey,
wc_LmsKey_SetContext, wc_LmsKey_SetParameters, wc_LmsKey_SetReadCb, wc_LmsKey_SetWriteCb,
wc_LmsKey_Sign, wc_LmsKey_SigsLeft, wc_LmsKey_Verify, WcLmsKey,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LmsParams {
pub levels: u32,
pub height: u32,
pub winternitz: u32,
}
impl LmsParams {
pub const L1_H5_W8: Self = Self {
levels: 1,
height: 5,
winternitz: 8,
};
pub const L1_H10_W4: Self = Self {
levels: 1,
height: 10,
winternitz: 4,
};
pub const L2_H5_W8: Self = Self {
levels: 2,
height: 5,
winternitz: 8,
};
pub const L2_H10_W4: Self = Self {
levels: 2,
height: 10,
winternitz: 4,
};
}
pub struct LmsVerifyingKey {
key: UnsafeCell<WcLmsKey>,
params: LmsParams,
pub_bytes: Vec<u8>,
}
unsafe impl Send for LmsVerifyingKey {}
impl LmsVerifyingKey {
pub fn from_public_bytes(params: LmsParams, pub_key: &[u8]) -> Result<Self, WolfCryptError> {
let mut key = WcLmsKey::zeroed();
let rc = unsafe { wc_LmsKey_Init(&mut key, core::ptr::null_mut(), -1) };
check(rc, "wc_LmsKey_Init")?;
let rc = unsafe {
wc_LmsKey_SetParameters(
&mut key,
params.levels as i32,
params.height as i32,
params.winternitz as i32,
)
};
check(rc, "wc_LmsKey_SetParameters")?;
let mut expected_len: u32 = 0;
let rc = unsafe { wc_LmsKey_GetPubLen(&key, &mut expected_len) };
check(rc, "wc_LmsKey_GetPubLen")?;
if pub_key.len() != expected_len as usize {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::InvalidInput);
}
let rc = unsafe { wc_LmsKey_ImportPubRaw(&mut key, pub_key.as_ptr(), expected_len) };
if rc != 0 {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_LmsKey_ImportPubRaw",
});
}
Ok(Self {
key: UnsafeCell::new(key),
params,
pub_bytes: pub_key.to_vec(),
})
}
pub fn params(&self) -> LmsParams {
self.params
}
pub fn as_bytes(&self) -> &[u8] {
&self.pub_bytes
}
pub fn sig_len(&self) -> Result<usize, WolfCryptError> {
let mut len: u32 = 0;
let rc = unsafe { wc_LmsKey_GetSigLen(&*self.key.get(), &mut len) };
check(rc, "wc_LmsKey_GetSigLen")?;
Ok(len as usize)
}
pub fn pub_len(&self) -> Result<usize, WolfCryptError> {
let mut len: u32 = 0;
let rc = unsafe { wc_LmsKey_GetPubLen(&*self.key.get(), &mut len) };
check(rc, "wc_LmsKey_GetPubLen")?;
Ok(len as usize)
}
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), WolfCryptError> {
let sig_len = len_as_c_int(sig.len()) as u32;
let msg_len = len_as_c_int(msg.len());
let rc = unsafe {
wc_LmsKey_Verify(self.key.get(), sig.as_ptr(), sig_len, msg.as_ptr(), msg_len)
};
check(rc, "wc_LmsKey_Verify")
}
pub fn export_public(&self) -> Result<Vec<u8>, WolfCryptError> {
let pub_len = self.pub_len()?;
let mut buf = vec![0u8; pub_len];
let mut out_len = pub_len as u32;
let rc =
unsafe { wc_LmsKey_ExportPubRaw(&*self.key.get(), buf.as_mut_ptr(), &mut out_len) };
check(rc, "wc_LmsKey_ExportPubRaw")?;
buf.truncate(out_len as usize);
Ok(buf)
}
}
impl Drop for LmsVerifyingKey {
fn drop(&mut self) {
unsafe {
wc_LmsKey_Free(self.key.get_mut());
}
}
}
struct PrivKeyStore {
data: Vec<u8>,
}
unsafe extern "C" fn lms_write_cb(priv_: *const u8, priv_sz: u32, context: *mut c_void) -> c_int {
let store = unsafe { &mut *(context as *mut PrivKeyStore) };
let src = unsafe { core::slice::from_raw_parts(priv_, priv_sz as usize) };
store.data.resize(src.len(), 0);
store.data.copy_from_slice(src);
0 }
unsafe extern "C" fn lms_read_cb(priv_: *mut u8, priv_sz: u32, context: *mut c_void) -> c_int {
let store = unsafe { &*(context as *const PrivKeyStore) };
if store.data.len() != priv_sz as usize {
return -1; }
let dst = unsafe { core::slice::from_raw_parts_mut(priv_, priv_sz as usize) };
dst.copy_from_slice(&store.data);
0 }
pub struct LmsSigningKey {
key: UnsafeCell<WcLmsKey>,
params: LmsParams,
store: alloc::boxed::Box<PrivKeyStore>,
}
unsafe impl Send for LmsSigningKey {}
impl LmsSigningKey {
pub fn generate(
params: LmsParams,
rng: &mut crate::rand::WolfRng,
) -> Result<Self, WolfCryptError> {
let mut key = WcLmsKey::zeroed();
let rc = unsafe { wc_LmsKey_Init(&mut key, core::ptr::null_mut(), -1) };
check(rc, "wc_LmsKey_Init")?;
let rc = unsafe {
wc_LmsKey_SetParameters(
&mut key,
params.levels as i32,
params.height as i32,
params.winternitz as i32,
)
};
if rc != 0 {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_LmsKey_SetParameters",
});
}
let mut store = alloc::boxed::Box::new(PrivKeyStore { data: Vec::new() });
let ctx_ptr: *mut c_void = &mut *store as *mut PrivKeyStore as *mut c_void;
let rc = unsafe { wc_LmsKey_SetWriteCb(&mut key, Some(lms_write_cb)) };
if rc != 0 {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_LmsKey_SetWriteCb",
});
}
let rc = unsafe { wc_LmsKey_SetReadCb(&mut key, Some(lms_read_cb)) };
if rc != 0 {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_LmsKey_SetReadCb",
});
}
let rc = unsafe { wc_LmsKey_SetContext(&mut key, ctx_ptr) };
if rc != 0 {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_LmsKey_SetContext",
});
}
let rc = unsafe { wc_LmsKey_MakeKey(&mut key, &mut rng.rng) };
if rc != 0 {
unsafe { wc_LmsKey_Free(&mut key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_LmsKey_MakeKey",
});
}
Ok(Self {
key: UnsafeCell::new(key),
params,
store,
})
}
pub fn sign(&mut self, msg: &[u8]) -> Result<Vec<u8>, WolfCryptError> {
let mut sig_len: u32 = 0;
let rc = unsafe { wc_LmsKey_GetSigLen(&*self.key.get(), &mut sig_len) };
check(rc, "wc_LmsKey_GetSigLen")?;
let mut sig = vec![0u8; sig_len as usize];
let msg_len = len_as_c_int(msg.len());
let rc = unsafe {
wc_LmsKey_Sign(
self.key.get(),
sig.as_mut_ptr(),
&mut sig_len,
msg.as_ptr(),
msg_len,
)
};
check(rc, "wc_LmsKey_Sign")?;
sig.truncate(sig_len as usize);
Ok(sig)
}
pub fn export_public(&self) -> Result<Vec<u8>, WolfCryptError> {
let mut pub_len: u32 = 0;
let rc = unsafe { wc_LmsKey_GetPubLen(&*self.key.get(), &mut pub_len) };
check(rc, "wc_LmsKey_GetPubLen")?;
let mut buf = vec![0u8; pub_len as usize];
let mut out_len = pub_len;
let rc =
unsafe { wc_LmsKey_ExportPubRaw(&*self.key.get(), buf.as_mut_ptr(), &mut out_len) };
check(rc, "wc_LmsKey_ExportPubRaw")?;
buf.truncate(out_len as usize);
Ok(buf)
}
pub fn remaining_signatures(&self) -> i32 {
unsafe { wc_LmsKey_SigsLeft(self.key.get()) }
}
pub fn params(&self) -> LmsParams {
self.params
}
}
impl Drop for LmsSigningKey {
fn drop(&mut self) {
unsafe { wc_LmsKey_Free(self.key.get_mut()) };
}
}