use alloc::boxed::Box;
use super::common::{PcStatus, guard, out_write, slice};
use crate::ec::{
BoxedEcdhPrivateKey, BoxedEcdsaPrivateKey, BoxedEcdsaPublicKey, BoxedEcdsaSignature, CurveId,
Ed448PrivateKey, Ed448Signature, Ed25519PrivateKey, Ed25519Signature,
};
use crate::hash::{Sha256, Sha384, Sha512};
use crate::rng::OsRng;
use crate::x509::AnyPublicKey;
pub mod curve {
#![allow(missing_docs)]
pub const P256: i32 = 1;
pub const P384: i32 = 2;
pub const P521: i32 = 3;
pub const SECP256K1: i32 = 4;
}
fn curve_from_id(id: i32) -> Option<CurveId> {
Some(match id {
curve::P256 => CurveId::P256,
curve::P384 => CurveId::P384,
curve::P521 => CurveId::P521,
curve::SECP256K1 => CurveId::Secp256k1,
_ => return None,
})
}
pub struct PcEcKey(BoxedEcdsaPrivateKey);
pub(super) fn pc_ec_inner_key(handle: &PcEcKey) -> &BoxedEcdsaPrivateKey {
&handle.0
}
#[unsafe(no_mangle)]
pub extern "C" fn pc_ec_generate(curve_id: i32) -> *mut PcEcKey {
crate::ffi::common::guard_ptr(|| {
let Some(curve) = curve_from_id(curve_id) else {
return core::ptr::null_mut();
};
let key = BoxedEcdsaPrivateKey::generate(curve, &mut OsRng);
Box::into_raw(Box::new(PcEcKey(key)))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ec_from_pem(pem: *const u8, len: usize) -> *mut PcEcKey {
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 BoxedEcdsaPrivateKey::from_sec1_pem(s) {
Ok(k) => Box::into_raw(Box::new(PcEcKey(k))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ec_private_to_pem(
key: *const PcEcKey,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let pem = unsafe { &*key }.0.to_sec1_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ec_public_to_pem(
key: *const PcEcKey,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let pem = AnyPublicKey::Ecdsa(unsafe { &*key }.0.public_key()).to_spki_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ec_sign(
key: *const PcEcKey,
msg: *const u8,
msg_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let Some(m) = (unsafe { slice(msg, msg_len) }) else {
return PcStatus::NullPointer;
};
let sk = &unsafe { &*key }.0;
let curve = sk.curve();
let sig = match curve {
CurveId::P256 | CurveId::Secp256k1 => sk.sign::<Sha256>(m),
CurveId::P384 => sk.sign::<Sha384>(m),
CurveId::P521 => sk.sign::<Sha512>(m),
CurveId::Sm2p256v1 => return PcStatus::Unsupported,
};
match sig {
Ok(s) => unsafe { out_write(&s.to_der(curve), out, out_len) },
Err(_) => PcStatus::Internal,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ec_verify(
spki: *const u8,
spki_len: usize,
msg: *const u8,
msg_len: usize,
sig: *const u8,
sig_len: usize,
) -> PcStatus {
guard(|| {
let (Some(spki), Some(m), Some(sig)) = (
unsafe { slice(spki, spki_len) },
unsafe { slice(msg, msg_len) },
unsafe { slice(sig, sig_len) },
) else {
return PcStatus::NullPointer;
};
let key = match AnyPublicKey::from_spki_der(spki) {
Ok(AnyPublicKey::Ecdsa(k)) => k,
Ok(_) => return PcStatus::Unsupported,
Err(_) => return PcStatus::BadEncoding,
};
let parsed = match BoxedEcdsaSignature::from_der(sig) {
Ok(s) => s,
Err(_) => return PcStatus::BadEncoding,
};
let ok = match key.curve() {
CurveId::P256 | CurveId::Secp256k1 => key.verify::<Sha256>(m, &parsed),
CurveId::P384 => key.verify::<Sha384>(m, &parsed),
CurveId::P521 => key.verify::<Sha512>(m, &parsed),
CurveId::Sm2p256v1 => return PcStatus::Unsupported,
};
if ok.is_ok() {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ec_free(key: *mut PcEcKey) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
pub struct PcEd25519Key(Ed25519PrivateKey);
#[unsafe(no_mangle)]
pub extern "C" fn pc_ed25519_generate() -> *mut PcEd25519Key {
crate::ffi::common::guard_ptr(|| {
Box::into_raw(Box::new(PcEd25519Key(Ed25519PrivateKey::generate(
&mut OsRng,
))))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed25519_from_pem(pem: *const u8, len: usize) -> *mut PcEd25519Key {
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 Ed25519PrivateKey::from_pkcs8_pem(s) {
Ok(k) => Box::into_raw(Box::new(PcEd25519Key(k))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed25519_private_to_pem(
key: *const PcEd25519Key,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let pem = unsafe { &*key }.0.to_pkcs8_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed25519_public_to_pem(
key: *const PcEd25519Key,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let pem = AnyPublicKey::Ed25519(unsafe { &*key }.0.public_key()).to_spki_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed25519_sign(
key: *const PcEd25519Key,
msg: *const u8,
msg_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let Some(m) = (unsafe { slice(msg, msg_len) }) else {
return PcStatus::NullPointer;
};
let sig = unsafe { &*key }.0.sign(m);
unsafe { out_write(&sig.to_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed25519_verify(
spki: *const u8,
spki_len: usize,
msg: *const u8,
msg_len: usize,
sig: *const u8,
sig_len: usize,
) -> PcStatus {
guard(|| {
let (Some(spki), Some(m), Some(sig)) = (
unsafe { slice(spki, spki_len) },
unsafe { slice(msg, msg_len) },
unsafe { slice(sig, sig_len) },
) else {
return PcStatus::NullPointer;
};
let key = match AnyPublicKey::from_spki_der(spki) {
Ok(AnyPublicKey::Ed25519(k)) => k,
Ok(_) => return PcStatus::Unsupported,
Err(_) => return PcStatus::BadEncoding,
};
let Ok(bytes) = <[u8; 64]>::try_from(sig) else {
return PcStatus::BadEncoding;
};
if key.verify(m, &Ed25519Signature::from_bytes(bytes)).is_ok() {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed25519_free(key: *mut PcEd25519Key) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
pub struct PcEd448Key(Ed448PrivateKey);
#[unsafe(no_mangle)]
pub extern "C" fn pc_ed448_generate() -> *mut PcEd448Key {
crate::ffi::common::guard_ptr(|| {
Box::into_raw(Box::new(PcEd448Key(Ed448PrivateKey::generate(&mut OsRng))))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed448_from_pem(pem: *const u8, len: usize) -> *mut PcEd448Key {
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 Ed448PrivateKey::from_pkcs8_pem(s) {
Ok(k) => Box::into_raw(Box::new(PcEd448Key(k))),
Err(_) => core::ptr::null_mut(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed448_private_to_pem(
key: *const PcEd448Key,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let pem = unsafe { &*key }.0.to_pkcs8_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed448_public_to_pem(
key: *const PcEd448Key,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let pem = AnyPublicKey::Ed448(unsafe { &*key }.0.public_key()).to_spki_pem();
unsafe { out_write(pem.as_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed448_sign(
key: *const PcEd448Key,
msg: *const u8,
msg_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
if key.is_null() {
return PcStatus::NullPointer;
}
let Some(m) = (unsafe { slice(msg, msg_len) }) else {
return PcStatus::NullPointer;
};
let sig = unsafe { &*key }.0.sign(m);
unsafe { out_write(&sig.to_bytes(), out, out_len) }
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed448_verify(
spki: *const u8,
spki_len: usize,
msg: *const u8,
msg_len: usize,
sig: *const u8,
sig_len: usize,
) -> PcStatus {
guard(|| {
let (Some(spki), Some(m), Some(sig)) = (
unsafe { slice(spki, spki_len) },
unsafe { slice(msg, msg_len) },
unsafe { slice(sig, sig_len) },
) else {
return PcStatus::NullPointer;
};
let key = match AnyPublicKey::from_spki_der(spki) {
Ok(AnyPublicKey::Ed448(k)) => k,
Ok(_) => return PcStatus::Unsupported,
Err(_) => return PcStatus::BadEncoding,
};
let Ok(bytes) = <[u8; 114]>::try_from(sig) else {
return PcStatus::BadEncoding;
};
if key.verify(m, &Ed448Signature::from_bytes(bytes)).is_ok() {
PcStatus::Ok
} else {
PcStatus::Verification
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ed448_free(key: *mut PcEd448Key) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pc_ecdh(
curve_id: i32,
priv_be: *const u8,
priv_len: usize,
peer_spki: *const u8,
peer_spki_len: usize,
out: *mut u8,
out_len: *mut usize,
) -> PcStatus {
guard(|| {
let Some(curve) = curve_from_id(curve_id) else {
return PcStatus::Unsupported;
};
let (Some(d), Some(spki)) = (unsafe { slice(priv_be, priv_len) }, unsafe {
slice(peer_spki, peer_spki_len)
}) else {
return PcStatus::NullPointer;
};
let sk = match BoxedEcdhPrivateKey::from_bytes(curve, d) {
Ok(s) => s,
Err(_) => return PcStatus::BadEncoding,
};
let peer: BoxedEcdsaPublicKey = match AnyPublicKey::from_spki_der(spki) {
Ok(AnyPublicKey::Ecdsa(k)) if k.curve() == curve => k,
Ok(AnyPublicKey::Ecdsa(_)) => return PcStatus::Unsupported,
Ok(_) => return PcStatus::Unsupported,
Err(_) => return PcStatus::BadEncoding,
};
match sk.diffie_hellman(&peer) {
Ok(secret) => unsafe { out_write(&secret, out, out_len) },
Err(_) => PcStatus::Verification,
}
})
}