use alloc::boxed::Box;
use super::common::{PcStatus, guard, out_write, slice, wipe_vec};
use crate::lms::{HssPrivateKey, LmotsType, LmsPrivateKey, LmsType, verify_hss, verify_lms};
use crate::rng::OsRng;
pub mod lms_id {
#![allow(missing_docs)]
pub const SHA256_M32_H5: i32 = 5;
pub const SHA256_M32_H10: i32 = 6;
pub const SHA256_M32_H15: i32 = 7;
pub const SHA256_M32_H20: i32 = 8;
pub const SHA256_M32_H25: i32 = 9;
}
pub mod lmots_id {
#![allow(missing_docs)]
pub const SHA256_N32_W1: i32 = 1;
pub const SHA256_N32_W2: i32 = 2;
pub const SHA256_N32_W4: i32 = 3;
pub const SHA256_N32_W8: i32 = 4;
}
fn lms_type(v: i32) -> Option<LmsType> {
Some(match v {
lms_id::SHA256_M32_H5 => LmsType::Sha256M32H5,
lms_id::SHA256_M32_H10 => LmsType::Sha256M32H10,
lms_id::SHA256_M32_H15 => LmsType::Sha256M32H15,
lms_id::SHA256_M32_H20 => LmsType::Sha256M32H20,
lms_id::SHA256_M32_H25 => LmsType::Sha256M32H25,
_ => return None,
})
}
fn lmots_type(v: i32) -> Option<LmotsType> {
Some(match v {
lmots_id::SHA256_N32_W1 => LmotsType::Sha256N32W1,
lmots_id::SHA256_N32_W2 => LmotsType::Sha256N32W2,
lmots_id::SHA256_N32_W4 => LmotsType::Sha256N32W4,
lmots_id::SHA256_N32_W8 => LmotsType::Sha256N32W8,
_ => return None,
})
}
pub struct PcLms(Box<LmsPrivateKey>);
pub struct PcHss(Box<HssPrivateKey>);
const N: usize = 32;
fn lms_sig_len(lms: LmsType, ots: LmotsType) -> usize {
4 + ots.sig_len() + 4 + lms.h() as usize * N
}
fn hss_sig_len(key: &HssPrivateKey) -> Option<usize> {
let mut ser = key.to_bytes();
let result = hss_sig_len_from_private_bytes(&ser);
wipe_vec(&mut ser);
result
}
fn hss_sig_len_from_private_bytes(ser: &[u8]) -> Option<usize> {
const LEGACY_LEVEL_BYTES: usize = 4 + 4 + 16 + N + 4;
const NEW_LEVEL_BYTES: usize = LEGACY_LEVEL_BYTES + N;
let l = u32::from_be_bytes(ser.get(..4)?.try_into().ok()?) as usize;
if l == 0 {
return None;
}
let level_bytes = if ser.len() == 4 + l * NEW_LEVEL_BYTES {
NEW_LEVEL_BYTES
} else if ser.len() == 4 + l * LEGACY_LEVEL_BYTES {
LEGACY_LEVEL_BYTES
} else {
return None;
};
let mut total = 4; for i in 0..l {
let off = 4 + i * level_bytes;
let lms = LmsType::from_u32(u32::from_be_bytes(ser[off..off + 4].try_into().ok()?))?;
let ots = LmotsType::from_u32(u32::from_be_bytes(ser[off + 4..off + 8].try_into().ok()?))?;
total += lms_sig_len(lms, ots);
if i + 1 < l {
total += 24 + N; }
}
Some(total)
}
#[unsafe(no_mangle)]
pub extern "C" fn pc_lms_generate(lms_param: i32, lmots_param: i32) -> *mut PcLms {
crate::ffi::common::guard_ptr(|| {
let (Some(lms), Some(ots)) = (lms_type(lms_param), lmots_type(lmots_param)) else {
return core::ptr::null_mut();
};
let sk = LmsPrivateKey::generate(lms, ots, &mut OsRng);
Box::into_raw(Box::new(PcLms(Box::new(sk))))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_lms_from_bytes(bytes: *const u8, len: usize) -> *mut PcLms {
crate::ffi::common::guard_ptr(|| {
let Some(b) = (unsafe { slice(bytes, len) }) else {
return core::ptr::null_mut();
};
match LmsPrivateKey::from_bytes(b) {
Ok(k) => Box::into_raw(Box::new(PcLms(Box::new(k)))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_lms_private_to_bytes(
k: *const PcLms,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let mut ser = unsafe { &*k }.0.to_bytes();
let st = unsafe { out_write(&ser, out, out_len) };
wipe_vec(&mut ser);
st
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_lms_public_to_bytes(
k: *const PcLms,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let pk = unsafe { &*k }.0.public_key();
unsafe { out_write(pk.to_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_lms_sign(
k: *mut PcLms,
msg: *const u8,
msg_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() || out_len.is_null() {
return PcStatus::NullPointer;
}
let Some(m) = (unsafe { slice(msg, msg_len) }) else {
return PcStatus::NullPointer;
};
let key = unsafe { &mut *k };
let expected = lms_sig_len(key.0.lms_type(), key.0.ots_type());
if unsafe { *out_len } < expected {
unsafe { *out_len = expected };
return PcStatus::BufferTooSmall;
}
let sig = match key.0.sign(&mut OsRng, m) {
Ok(s) => s,
Err(_) => return PcStatus::Internal,
};
debug_assert_eq!(sig.len(), expected);
unsafe { out_write(&sig, out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_lms_verify(
pubkey: *const u8,
pubkey_len: usize,
msg: *const u8,
msg_len: usize,
sig: *const u8,
sig_len: usize,
) -> PcStatus {
guard(|| {
let (Some(pk), Some(m), Some(s)) = (
unsafe { slice(pubkey, pubkey_len) },
unsafe { slice(msg, msg_len) },
unsafe { slice(sig, sig_len) },
) else {
return PcStatus::NullPointer;
};
if verify_lms(pk, m, s) {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_lms_free(k: *mut PcLms) {
if !k.is_null() {
drop(unsafe { Box::from_raw(k) });
}
}
#[unsafe(no_mangle)]
pub extern "C" fn pc_hss_generate(levels: usize, lms_param: i32, lmots_param: i32) -> *mut PcHss {
crate::ffi::common::guard_ptr(|| {
let (Some(lms), Some(ots)) = (lms_type(lms_param), lmots_type(lmots_param)) else {
return core::ptr::null_mut();
};
if !(1..=8).contains(&levels) {
return core::ptr::null_mut();
}
let params = alloc::vec![(lms, ots); levels];
match HssPrivateKey::generate(¶ms, &mut OsRng) {
Ok(sk) => Box::into_raw(Box::new(PcHss(Box::new(sk)))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_hss_from_bytes(bytes: *const u8, len: usize) -> *mut PcHss {
crate::ffi::common::guard_ptr(|| {
let Some(b) = (unsafe { slice(bytes, len) }) else {
return core::ptr::null_mut();
};
match HssPrivateKey::from_bytes(b) {
Ok(k) => Box::into_raw(Box::new(PcHss(Box::new(k)))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_hss_private_to_bytes(
k: *const PcHss,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let mut ser = unsafe { &*k }.0.to_bytes();
let st = unsafe { out_write(&ser, out, out_len) };
wipe_vec(&mut ser);
st
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_hss_public_to_bytes(
k: *const PcHss,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let pk = unsafe { &*k }.0.public_key();
unsafe { out_write(pk.to_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_hss_sign(
k: *mut PcHss,
msg: *const u8,
msg_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() || out_len.is_null() {
return PcStatus::NullPointer;
}
let Some(m) = (unsafe { slice(msg, msg_len) }) else {
return PcStatus::NullPointer;
};
let key = unsafe { &mut *k };
let Some(expected) = hss_sig_len(&key.0) else {
return PcStatus::Internal;
};
if unsafe { *out_len } < expected {
unsafe { *out_len = expected };
return PcStatus::BufferTooSmall;
}
let sig = match key.0.sign(&mut OsRng, m) {
Ok(s) => s,
Err(_) => return PcStatus::Internal,
};
debug_assert_eq!(sig.len(), expected);
unsafe { out_write(&sig, out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_hss_verify(
pubkey: *const u8,
pubkey_len: usize,
msg: *const u8,
msg_len: usize,
sig: *const u8,
sig_len: usize,
) -> PcStatus {
guard(|| {
let (Some(pk), Some(m), Some(s)) = (
unsafe { slice(pubkey, pubkey_len) },
unsafe { slice(msg, msg_len) },
unsafe { slice(sig, sig_len) },
) else {
return PcStatus::NullPointer;
};
if verify_hss(pk, m, s) {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_hss_free(k: *mut PcHss) {
if !k.is_null() {
drop(unsafe { Box::from_raw(k) });
}
}