use alloc::boxed::Box;
use super::common::{PcStatus, guard, out_write, slice, wipe_vec};
use crate::ec::sm2::{DEFAULT_ID, Sm2PrivateKey, Sm2PublicKey, Sm2Signature};
use crate::rng::OsRng;
pub struct PcSm2(Box<Sm2PrivateKey>);
unsafe fn id_or_default<'a>(id: *const u8, id_len: usize) -> Option<&'a [u8]> {
if id.is_null() && id_len == 0 {
return Some(DEFAULT_ID);
}
unsafe { slice(id, id_len) }
}
#[unsafe(no_mangle)]
pub extern "C" fn pc_sm2_generate() -> *mut PcSm2 {
crate::ffi::common::guard_ptr(|| {
let sk = Sm2PrivateKey::generate(&mut OsRng);
Box::into_raw(Box::new(PcSm2(Box::new(sk))))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_from_pem(pem: *const u8, len: usize) -> *mut PcSm2 {
crate::ffi::common::guard_ptr(|| {
let Some(bytes) = (unsafe { slice(pem, len) }) else {
return core::ptr::null_mut();
};
let Ok(s) = core::str::from_utf8(bytes) else {
return core::ptr::null_mut();
};
match Sm2PrivateKey::from_sec1_pem(s) {
Ok(k) => Box::into_raw(Box::new(PcSm2(Box::new(k)))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_private_to_pem(
k: *const PcSm2,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let mut pem = unsafe { &*k }.0.to_sec1_pem().into_bytes();
let st = unsafe { out_write(&pem, out, out_len) };
wipe_vec(&mut pem);
st
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_public_to_pem(
k: *const PcSm2,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let pem = unsafe { &*k }.0.public_key().to_spki_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_sign(
k: *const PcSm2,
id: *const u8,
id_len: usize,
msg: *const u8,
msg_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let (Some(id), Some(m)) = (unsafe { id_or_default(id, id_len) }, unsafe {
slice(msg, msg_len)
}) else {
return PcStatus::NullPointer;
};
let sig = match unsafe { &*k }.0.sign(m, id, &mut OsRng) {
Ok(s) => s,
Err(_) => return PcStatus::Internal,
};
unsafe { out_write(&sig.to_der(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_verify(
spki: *const u8,
spki_len: usize,
id: *const u8,
id_len: usize,
msg: *const u8,
msg_len: usize,
sig: *const u8,
sig_len: usize,
) -> PcStatus {
guard(|| {
let (Some(spki), Some(id), Some(m), Some(sig)) = (
unsafe { slice(spki, spki_len) },
unsafe { id_or_default(id, id_len) },
unsafe { slice(msg, msg_len) },
unsafe { slice(sig, sig_len) },
) else {
return PcStatus::NullPointer;
};
let pk = match Sm2PublicKey::from_spki_der(spki) {
Ok(k) => k,
Err(_) => return PcStatus::BadEncoding,
};
let parsed = match Sm2Signature::from_der(sig) {
Ok(s) => s,
Err(_) => return PcStatus::BadEncoding,
};
match pk.verify(m, &parsed, id) {
Ok(()) => PcStatus::Ok,
Err(_) => PcStatus::Verification,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_encrypt(
spki: *const u8,
spki_len: usize,
pt: *const u8,
pt_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
let (Some(spki), Some(p)) = (unsafe { slice(spki, spki_len) }, unsafe {
slice(pt, pt_len)
}) else {
return PcStatus::NullPointer;
};
let pk = match Sm2PublicKey::from_spki_der(spki) {
Ok(k) => k,
Err(_) => return PcStatus::BadEncoding,
};
let ct = match pk.encrypt(p, &mut OsRng) {
Ok(c) => c,
Err(_) => return PcStatus::Internal,
};
unsafe { out_write(&ct, out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_decrypt(
k: *const PcSm2,
ct: *const u8,
ct_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if k.is_null() {
return PcStatus::NullPointer;
}
let Some(c) = (unsafe { slice(ct, ct_len) }) else {
return PcStatus::NullPointer;
};
let mut pt = match unsafe { &*k }.0.decrypt(c) {
Ok(p) => p,
Err(_) => return PcStatus::Verification,
};
let st = unsafe { out_write(&pt, out, out_len) };
wipe_vec(&mut pt);
st
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_sm2_free(k: *mut PcSm2) {
if !k.is_null() {
drop(unsafe { Box::from_raw(k) });
}
}