use core::ffi::{c_int, c_void};
use alloc::boxed::Box;
use crate::error::{check, WolfCryptError};
#[non_exhaustive]
#[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 AesGcmEncRequest<'a> {
pub aes: *mut c_void,
pub input: &'a [u8],
pub out: &'a mut [u8],
pub iv: &'a [u8],
pub auth_tag: &'a mut [u8],
pub auth_in: &'a [u8],
}
pub struct AesGcmDecRequest<'a> {
pub aes: *mut c_void,
pub input: &'a [u8],
pub out: &'a mut [u8],
pub iv: &'a [u8],
pub auth_tag: &'a [u8],
pub auth_in: &'a [u8],
}
pub struct AesCbcRequest<'a> {
pub aes: *mut c_void,
pub encrypting: bool,
pub input: &'a [u8],
pub out: &'a mut [u8],
}
#[non_exhaustive]
pub enum CipherRequest<'a> {
AesGcmEncrypt(AesGcmEncRequest<'a>),
AesGcmDecrypt(AesGcmDecRequest<'a>),
AesCbc(AesCbcRequest<'a>),
Unknown {
cipher_type: i32,
encrypting: bool,
},
}
pub struct EcdsaSignRequest<'a> {
pub key: *mut c_void,
pub hash: &'a [u8],
pub out: &'a mut [u8],
pub out_len: *mut u32,
pub rng: *mut c_void,
}
pub struct EcdsaVerifyRequest<'a> {
pub key: *mut c_void,
pub sig: &'a [u8],
pub hash: &'a [u8],
pub result: *mut c_int,
}
pub struct EcdhRequest<'a> {
pub private_key: *mut c_void,
pub public_key: *mut c_void,
pub out: &'a mut [u8],
pub out_len: *mut u32,
}
pub struct EcKeyGenRequest {
pub key: *mut c_void,
pub size: i32,
pub curve_id: i32,
pub rng: *mut c_void,
}
#[non_exhaustive]
pub enum PkRequest<'a> {
EcdsaSign(EcdsaSignRequest<'a>),
EcdsaVerify(EcdsaVerifyRequest<'a>),
Ecdh(EcdhRequest<'a>),
EcKeyGen(EcKeyGenRequest),
Unknown(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_c = info as *const wolfcrypt_rs::wc_CryptoInfo;
let info_m = info;
let algo_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_get_algo_type(info_c) };
let result = match algo_type {
wolfcrypt_rs::WC_ALGO_TYPE_RNG => {
let out_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_rng_out(info_c) };
let sz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_rng_sz(info_c) } as usize;
if out_ptr.is_null() || sz == 0 {
return wolfcrypt_rs::CRYPTOCB_UNAVAILABLE;
}
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_c) };
let in_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_in(info_c) };
let in_sz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_in_sz(info_c) } as usize;
let digest_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hash_digest(info_c) };
let input = if !in_ptr.is_null() && in_sz > 0 {
unsafe { core::slice::from_raw_parts(in_ptr, in_sz) }
} else {
&[]
};
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_c) };
let in_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_in(info_c) };
let in_sz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_in_sz(info_c) } as usize;
let digest_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_hmac_digest(info_c) };
let input = if !in_ptr.is_null() && in_sz > 0 {
unsafe { core::slice::from_raw_parts(in_ptr, in_sz) }
} else {
&[]
};
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_c) };
let enc = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_enc(info_c) };
#[cfg(wolfssl_aes_gcm)]
if cipher_type == wolfcrypt_rs::WC_CIPHER_AES_GCM {
return build_cipher_aesgcm(state, info_c, info_m, enc != 0);
}
if cipher_type == wolfcrypt_rs::WC_CIPHER_AES_CBC {
return build_cipher_aescbc(state, info_c, info_m, enc != 0);
}
state.callbacks.cipher(CipherRequest::Unknown {
cipher_type,
encrypting: enc != 0,
})
}
wolfcrypt_rs::WC_ALGO_TYPE_PK => {
let pk_type = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_type(info_c) };
#[cfg(wolfssl_ecc)]
{
if pk_type == wolfcrypt_rs::WC_PK_TYPE_ECDSA_SIGN {
return build_pk_ecdsasign(state, info_c, info_m);
}
if pk_type == wolfcrypt_rs::WC_PK_TYPE_ECDSA_VERIFY {
return build_pk_ecdsaverify(state, info_c, info_m);
}
if pk_type == wolfcrypt_rs::WC_PK_TYPE_ECDH {
return build_pk_ecdh(state, info_c, info_m);
}
if pk_type == wolfcrypt_rs::WC_PK_TYPE_EC_KEYGEN {
return build_pk_eckg(state, info_c);
}
}
state.callbacks.pk(PkRequest::Unknown(pk_type))
}
_ => CryptoCallbackResult::NotAvailable,
};
result.to_c()
}
#[cfg(wolfssl_aes_gcm)]
unsafe fn build_cipher_aesgcm(
state: &DeviceState,
info_c: *const wolfcrypt_rs::wc_CryptoInfo,
info_m: *mut wolfcrypt_rs::wc_CryptoInfo,
encrypting: bool,
) -> c_int {
if encrypting {
let aes = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_aes(info_c) };
let out = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_out(info_m) };
let inp = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_in(info_c) };
let sz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_sz(info_c) } as usize;
let iv = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_iv(info_c) };
let ivsz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_iv_sz(info_c) }
as usize;
let tag =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_auth_tag(info_m) };
let tagsz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_auth_tag_sz(info_c) }
as usize;
let ain =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_auth_in(info_c) };
let ainsz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_enc_auth_in_sz(info_c) }
as usize;
let input = if !inp.is_null() && sz > 0 {
unsafe { core::slice::from_raw_parts(inp, sz) }
} else {
&[]
};
let out_sl = if !out.is_null() && sz > 0 {
unsafe { core::slice::from_raw_parts_mut(out, sz) }
} else {
&mut []
};
let iv_sl = if !iv.is_null() && ivsz > 0 {
unsafe { core::slice::from_raw_parts(iv, ivsz) }
} else {
&[]
};
let tag_sl = if !tag.is_null() && tagsz > 0 {
unsafe { core::slice::from_raw_parts_mut(tag, tagsz) }
} else {
&mut []
};
let auth_in = if !ain.is_null() && ainsz > 0 {
unsafe { core::slice::from_raw_parts(ain, ainsz) }
} else {
&[]
};
state
.callbacks
.cipher(CipherRequest::AesGcmEncrypt(AesGcmEncRequest {
aes,
input,
out: out_sl,
iv: iv_sl,
auth_tag: tag_sl,
auth_in,
}))
.to_c()
} else {
let aes = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_aes(info_c) };
let out = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_out(info_m) };
let inp = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_in(info_c) };
let sz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_sz(info_c) } as usize;
let iv = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_iv(info_c) };
let ivsz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_iv_sz(info_c) }
as usize;
let tag =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_auth_tag(info_c) };
let tagsz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_auth_tag_sz(info_c) }
as usize;
let ain =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_auth_in(info_c) };
let ainsz =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aesgcm_dec_auth_in_sz(info_c) }
as usize;
let input = if !inp.is_null() && sz > 0 {
unsafe { core::slice::from_raw_parts(inp, sz) }
} else {
&[]
};
let out_sl = if !out.is_null() && sz > 0 {
unsafe { core::slice::from_raw_parts_mut(out, sz) }
} else {
&mut []
};
let iv_sl = if !iv.is_null() && ivsz > 0 {
unsafe { core::slice::from_raw_parts(iv, ivsz) }
} else {
&[]
};
let tag_sl = if !tag.is_null() && tagsz > 0 {
unsafe { core::slice::from_raw_parts(tag, tagsz) }
} else {
&[]
};
let auth_in = if !ain.is_null() && ainsz > 0 {
unsafe { core::slice::from_raw_parts(ain, ainsz) }
} else {
&[]
};
state
.callbacks
.cipher(CipherRequest::AesGcmDecrypt(AesGcmDecRequest {
aes,
input,
out: out_sl,
iv: iv_sl,
auth_tag: tag_sl,
auth_in,
}))
.to_c()
}
}
unsafe fn build_cipher_aescbc(
state: &DeviceState,
info_c: *const wolfcrypt_rs::wc_CryptoInfo,
info_m: *mut wolfcrypt_rs::wc_CryptoInfo,
encrypting: bool,
) -> c_int {
let aes = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aescbc_aes(info_c) };
let out = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aescbc_out(info_m) };
let inp = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aescbc_in(info_c) };
let sz = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_cipher_aescbc_sz(info_c) } as usize;
let input = if !inp.is_null() && sz > 0 {
unsafe { core::slice::from_raw_parts(inp, sz) }
} else {
&[]
};
let out_sl = if !out.is_null() && sz > 0 {
unsafe { core::slice::from_raw_parts_mut(out, sz) }
} else {
&mut []
};
state
.callbacks
.cipher(CipherRequest::AesCbc(AesCbcRequest {
aes,
encrypting,
input,
out: out_sl,
}))
.to_c()
}
#[cfg(wolfssl_ecc)]
unsafe fn build_pk_ecdsasign(
state: &DeviceState,
info_c: *const wolfcrypt_rs::wc_CryptoInfo,
info_m: *mut wolfcrypt_rs::wc_CryptoInfo,
) -> c_int {
let key = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccsign_key(info_c) };
let in_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccsign_in(info_c) };
let inlen = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccsign_inlen(info_c) } as usize;
let out_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccsign_out(info_m) };
let out_len = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccsign_outlen(info_m) };
let rng = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccsign_rng(info_c) };
let hash = if !in_ptr.is_null() && inlen > 0 {
unsafe { core::slice::from_raw_parts(in_ptr, inlen) }
} else {
&[]
};
let cap = if out_len.is_null() {
0
} else {
(unsafe { *out_len }) as usize
};
let out = if !out_ptr.is_null() && cap > 0 {
unsafe { core::slice::from_raw_parts_mut(out_ptr, cap) }
} else {
&mut []
};
state
.callbacks
.pk(PkRequest::EcdsaSign(EcdsaSignRequest {
key,
hash,
out,
out_len,
rng,
}))
.to_c()
}
#[cfg(wolfssl_ecc)]
unsafe fn build_pk_ecdsaverify(
state: &DeviceState,
info_c: *const wolfcrypt_rs::wc_CryptoInfo,
info_m: *mut wolfcrypt_rs::wc_CryptoInfo,
) -> c_int {
let key = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccverify_key(info_c) };
let sig_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccverify_sig(info_c) };
let siglen =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccverify_siglen(info_c) } as usize;
let hash_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccverify_hash(info_c) };
let hashlen =
unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccverify_hashlen(info_c) } as usize;
let result = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eccverify_res(info_m) };
let sig = if !sig_ptr.is_null() && siglen > 0 {
unsafe { core::slice::from_raw_parts(sig_ptr, siglen) }
} else {
&[]
};
let hash = if !hash_ptr.is_null() && hashlen > 0 {
unsafe { core::slice::from_raw_parts(hash_ptr, hashlen) }
} else {
&[]
};
state
.callbacks
.pk(PkRequest::EcdsaVerify(EcdsaVerifyRequest {
key,
sig,
hash,
result,
}))
.to_c()
}
#[cfg(wolfssl_ecc)]
unsafe fn build_pk_ecdh(
state: &DeviceState,
info_c: *const wolfcrypt_rs::wc_CryptoInfo,
info_m: *mut wolfcrypt_rs::wc_CryptoInfo,
) -> c_int {
let private_key = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_ecdh_private_key(info_c) };
let public_key = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_ecdh_public_key(info_c) };
let out_ptr = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_ecdh_out(info_m) };
let out_len = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_ecdh_outlen(info_m) };
let cap = if out_len.is_null() {
0
} else {
(unsafe { *out_len }) as usize
};
let out = if !out_ptr.is_null() && cap > 0 {
unsafe { core::slice::from_raw_parts_mut(out_ptr, cap) }
} else {
&mut []
};
state
.callbacks
.pk(PkRequest::Ecdh(EcdhRequest {
private_key,
public_key,
out,
out_len,
}))
.to_c()
}
#[cfg(wolfssl_ecc)]
unsafe fn build_pk_eckg(state: &DeviceState, info_c: *const wolfcrypt_rs::wc_CryptoInfo) -> c_int {
let key = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eckg_key(info_c) };
let size = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eckg_size(info_c) };
let curve_id = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eckg_curve_id(info_c) };
let rng = unsafe { wolfcrypt_rs::wolfcrypt_cryptocb_info_pk_eckg_rng(info_c) };
state
.callbacks
.pk(PkRequest::EcKeyGen(EcKeyGenRequest {
key,
size,
curve_id,
rng,
}))
.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_CIPHER;
pub use wolfcrypt_rs::WC_ALGO_TYPE_CMAC;
pub use wolfcrypt_rs::WC_ALGO_TYPE_HASH;
pub use wolfcrypt_rs::WC_ALGO_TYPE_HMAC;
pub use wolfcrypt_rs::WC_ALGO_TYPE_NONE;
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 mod cipher_type {
pub use wolfcrypt_rs::WC_CIPHER_AES_CBC;
pub use wolfcrypt_rs::WC_CIPHER_AES_CCM;
pub use wolfcrypt_rs::WC_CIPHER_AES_CTR;
pub use wolfcrypt_rs::WC_CIPHER_AES_ECB;
pub use wolfcrypt_rs::WC_CIPHER_AES_GCM;
}
pub mod pk_type {
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_EC_KEYGEN;
pub use wolfcrypt_rs::WC_PK_TYPE_ED25519_SIGN;
pub use wolfcrypt_rs::WC_PK_TYPE_ED25519_VERIFY;
pub use wolfcrypt_rs::WC_PK_TYPE_RSA;
}