use core::ffi::{c_int, c_void};
use alloc::boxed::Box;
use crate::error::{WolfCryptError, check};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CryptoCallbackResult {
Success,
NotAvailable,
Error(i32),
}
impl CryptoCallbackResult {
fn to_c(self) -> c_int {
match self {
Self::Success => 0,
Self::NotAvailable => wolfcrypt_rs::CRYPTOCB_UNAVAILABLE,
Self::Error(code) => code,
}
}
}
pub struct RngRequest<'a> {
pub out: &'a mut [u8],
}
pub struct HashRequest<'a> {
pub hash_type: i32,
pub input: &'a [u8],
pub digest: Option<&'a mut [u8]>,
}
pub struct HmacRequest<'a> {
pub mac_type: i32,
pub input: &'a [u8],
pub digest: Option<&'a mut [u8]>,
}
pub struct CipherRequest {
pub cipher_type: i32,
pub encrypting: bool,
}
pub struct PkRequest {
pub pk_type: i32,
}
pub trait CryptoCallbacks: Send + Sync {
fn rng(&self, _req: RngRequest<'_>) -> CryptoCallbackResult {
CryptoCallbackResult::NotAvailable
}
fn hash(&self, _req: HashRequest<'_>) -> CryptoCallbackResult {
CryptoCallbackResult::NotAvailable
}
fn hmac(&self, _req: HmacRequest<'_>) -> CryptoCallbackResult {
CryptoCallbackResult::NotAvailable
}
fn cipher(&self, _req: CipherRequest) -> CryptoCallbackResult {
CryptoCallbackResult::NotAvailable
}
fn pk(&self, _req: PkRequest) -> CryptoCallbackResult {
CryptoCallbackResult::NotAvailable
}
}
struct DeviceState {
callbacks: Box<dyn CryptoCallbacks>,
}
unsafe extern "C" fn trampoline(
_dev_id: c_int,
info: *mut wolfcrypt_rs::wc_CryptoInfo,
ctx: *mut c_void,
) -> c_int {
if ctx.is_null() || info.is_null() {
return wolfcrypt_rs::CRYPTOCB_UNAVAILABLE;
}
let state = unsafe { &*(ctx as *const DeviceState) };
let info_ptr = info as *const wolfcrypt_rs::wc_CryptoInfo;
let algo_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_get_algo_type(info_ptr) };
let result = match algo_type {
wolfcrypt_rs::WC_ALGO_TYPE_RNG => {
let out_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_rng_out(info_ptr) };
let sz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_rng_sz(info_ptr) } as usize;
if out_ptr.is_null() || sz == 0 {
CryptoCallbackResult::NotAvailable
} else {
let out = unsafe { core::slice::from_raw_parts_mut(out_ptr, sz) };
state.callbacks.rng(RngRequest { out })
}
}
wolfcrypt_rs::WC_ALGO_TYPE_HASH => {
let hash_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_type(info_ptr) };
let in_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_in(info_ptr) };
let in_sz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_in_sz(info_ptr) } as usize;
let digest_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_digest(info_ptr) };
let input = if in_ptr.is_null() || in_sz == 0 {
&[]
} else {
unsafe { core::slice::from_raw_parts(in_ptr, in_sz) }
};
let digest = if digest_ptr.is_null() {
None
} else {
Some(unsafe { core::slice::from_raw_parts_mut(digest_ptr, 64) })
};
state.callbacks.hash(HashRequest { hash_type, input, digest })
}
wolfcrypt_rs::WC_ALGO_TYPE_HMAC => {
let mac_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_mac_type(info_ptr) };
let in_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_in(info_ptr) };
let in_sz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_in_sz(info_ptr) } as usize;
let digest_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_digest(info_ptr) };
let input = if in_ptr.is_null() || in_sz == 0 {
&[]
} else {
unsafe { core::slice::from_raw_parts(in_ptr, in_sz) }
};
let digest = if digest_ptr.is_null() {
None
} else {
Some(unsafe { core::slice::from_raw_parts_mut(digest_ptr, 64) })
};
state.callbacks.hmac(HmacRequest { mac_type, input, digest })
}
wolfcrypt_rs::WC_ALGO_TYPE_CIPHER => {
let cipher_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_type(info_ptr) };
let enc = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_enc(info_ptr) };
state.callbacks.cipher(CipherRequest {
cipher_type,
encrypting: enc != 0,
})
}
wolfcrypt_rs::WC_ALGO_TYPE_PK => {
let pk_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_type(info_ptr) };
state.callbacks.pk(PkRequest { pk_type })
}
_ => CryptoCallbackResult::NotAvailable,
};
result.to_c()
}
pub fn register_device(
dev_id: i32,
callbacks: impl CryptoCallbacks + 'static,
) -> Result<(), WolfCryptError> {
assert!(
dev_id != wolfcrypt_rs::INVALID_DEVID as i32,
"dev_id must not be INVALID_DEVID"
);
let state = Box::new(DeviceState {
callbacks: Box::new(callbacks),
});
let ctx = Box::into_raw(state) as *mut c_void;
let rc = unsafe {
wolfcrypt_rs::wc_CryptoCb_RegisterDevice(dev_id, Some(trampoline), ctx)
};
if rc != 0 {
unsafe { drop(Box::from_raw(ctx as *mut DeviceState)); }
return Err(WolfCryptError::Ffi { code: rc, func: "wc_CryptoCb_RegisterDevice" });
}
Ok(())
}
pub fn unregister_device(dev_id: i32) {
unsafe {
wolfcrypt_rs::wc_CryptoCb_UnRegisterDevice(dev_id);
}
}
pub mod algo_type {
pub use wolfcrypt_rs::WC_ALGO_TYPE_NONE;
pub use wolfcrypt_rs::WC_ALGO_TYPE_HASH;
pub use wolfcrypt_rs::WC_ALGO_TYPE_CIPHER;
pub use wolfcrypt_rs::WC_ALGO_TYPE_PK;
pub use wolfcrypt_rs::WC_ALGO_TYPE_RNG;
pub use wolfcrypt_rs::WC_ALGO_TYPE_SEED;
pub use wolfcrypt_rs::WC_ALGO_TYPE_HMAC;
pub use wolfcrypt_rs::WC_ALGO_TYPE_CMAC;
}
pub mod cipher_type {
pub use wolfcrypt_rs::WC_CIPHER_AES_CBC;
pub use wolfcrypt_rs::WC_CIPHER_AES_GCM;
pub use wolfcrypt_rs::WC_CIPHER_AES_CTR;
pub use wolfcrypt_rs::WC_CIPHER_AES_CCM;
pub use wolfcrypt_rs::WC_CIPHER_AES_ECB;
}
pub mod pk_type {
pub use wolfcrypt_rs::WC_PK_TYPE_RSA;
pub use wolfcrypt_rs::WC_PK_TYPE_EC_KEYGEN;
pub use wolfcrypt_rs::WC_PK_TYPE_ECDH;
pub use wolfcrypt_rs::WC_PK_TYPE_ECDSA_SIGN;
pub use wolfcrypt_rs::WC_PK_TYPE_ECDSA_VERIFY;
pub use wolfcrypt_rs::WC_PK_TYPE_ED25519_SIGN;
pub use wolfcrypt_rs::WC_PK_TYPE_ED25519_VERIFY;
}