use alloc::boxed::Box;
use alloc::vec::Vec;
use super::common::{PcStatus, guard, out_write, slice};
use crate::rng::OsRng;
use crate::xmss::{
XmssMtParamSet, XmssMtPrivateKey, XmssMtPublicKey, XmssParamSet, XmssPrivateKey, XmssPublicKey,
};
pub struct PcXmss(Box<XmssPrivateKey>);
pub struct PcXmssMt(Box<XmssMtPrivateKey>);
fn tag_public(oid: u32, raw: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + raw.len());
out.extend_from_slice(&oid.to_be_bytes());
out.extend_from_slice(raw);
out
}
fn untag_public(blob: &[u8]) -> Option<(u32, &[u8])> {
if blob.len() < 4 {
return None;
}
let oid = u32::from_be_bytes([blob[0], blob[1], blob[2], blob[3]]);
Some((oid, &blob[4..]))
}
#[unsafe(no_mangle)]
pub extern "C" fn pc_xmss_generate(oid: u32) -> *mut PcXmss {
crate::ffi::common::guard_ptr(|| {
let Some(set) = XmssParamSet::from_oid(oid) else {
return core::ptr::null_mut();
};
let sk = XmssPrivateKey::generate(set, &mut OsRng);
Box::into_raw(Box::new(PcXmss(Box::new(sk))))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmss_from_bytes(bytes: *const u8, len: usize) -> *mut PcXmss {
crate::ffi::common::guard_ptr(|| {
let Some(b) = (unsafe { slice(bytes, len) }) else {
return core::ptr::null_mut();
};
match XmssPrivateKey::from_bytes(b) {
Ok(k) => Box::into_raw(Box::new(PcXmss(Box::new(k)))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmss_private_to_bytes(
k: *const PcXmss,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
unsafe { out_write(&(*k).0.to_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmss_public_to_bytes(
k: *const PcXmss,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let pk = unsafe { &*k }.0.public_key();
let blob = tag_public(pk.parameter_set().oid(), pk.to_bytes());
unsafe { out_write(&blob, out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmss_sign(
k: *mut PcXmss,
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 = key.0.parameter_set().params().sig_bytes();
if unsafe { *out_len } < expected {
unsafe { *out_len = expected };
return PcStatus::BufferTooSmall;
}
let sig = match key.0.sign(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_xmss_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;
};
let Some((oid, raw)) = untag_public(pk) else {
return PcStatus::BadEncoding;
};
let Some(set) = XmssParamSet::from_oid(oid) else {
return PcStatus::BadEncoding;
};
let pub_key = match XmssPublicKey::from_bytes(set, raw) {
Ok(k) => k,
Err(_) => return PcStatus::BadEncoding,
};
if pub_key.verify(m, s) {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmss_free(k: *mut PcXmss) {
if !k.is_null() {
drop(unsafe { Box::from_raw(k) });
}
}
#[unsafe(no_mangle)]
pub extern "C" fn pc_xmssmt_generate(oid: u32) -> *mut PcXmssMt {
crate::ffi::common::guard_ptr(|| {
let Some(set) = XmssMtParamSet::from_oid(oid) else {
return core::ptr::null_mut();
};
let sk = XmssMtPrivateKey::generate(set, &mut OsRng);
Box::into_raw(Box::new(PcXmssMt(Box::new(sk))))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmssmt_from_bytes(bytes: *const u8, len: usize) -> *mut PcXmssMt {
crate::ffi::common::guard_ptr(|| {
let Some(b) = (unsafe { slice(bytes, len) }) else {
return core::ptr::null_mut();
};
match XmssMtPrivateKey::from_bytes(b) {
Ok(k) => Box::into_raw(Box::new(PcXmssMt(Box::new(k)))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmssmt_private_to_bytes(
k: *const PcXmssMt,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
unsafe { out_write(&(*k).0.to_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmssmt_public_to_bytes(
k: *const PcXmssMt,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let pk = unsafe { &*k }.0.public_key();
let blob = tag_public(pk.parameter_set().oid(), pk.to_bytes());
unsafe { out_write(&blob, out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmssmt_sign(
k: *mut PcXmssMt,
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 = key.0.parameter_set().params().sig_bytes();
if unsafe { *out_len } < expected {
unsafe { *out_len = expected };
return PcStatus::BufferTooSmall;
}
let sig = match key.0.sign(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_xmssmt_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;
};
let Some((oid, raw)) = untag_public(pk) else {
return PcStatus::BadEncoding;
};
let Some(set) = XmssMtParamSet::from_oid(oid) else {
return PcStatus::BadEncoding;
};
let pub_key = match XmssMtPublicKey::from_bytes(set, raw) {
Ok(k) => k,
Err(_) => return PcStatus::BadEncoding,
};
if pub_key.verify(m, s) {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_xmssmt_free(k: *mut PcXmssMt) {
if !k.is_null() {
drop(unsafe { Box::from_raw(k) });
}
}