pub mod stub;
#[cfg(not(target_arch = "wasm32"))]
mod credential_store;
use zeroize::Zeroizing;
use crate::error::PqfileError;
pub const HW_TAG_512: &str = "ML-KEM-512 HARDWARE KEY REFERENCE";
pub const HW_TAG_768: &str = "ML-KEM-768 HARDWARE KEY REFERENCE";
pub const HW_TAG_1024: &str = "ML-KEM-1024 HARDWARE KEY REFERENCE";
pub const HW_TAG_HYBRID_768: &str = "X25519+ML-KEM-768 HARDWARE KEY REFERENCE";
pub const HW_TAG_SIGNING: &str = "ML-DSA-65 HARDWARE SIGNING KEY REFERENCE";
pub fn is_hardware_tag(tag: &str) -> bool {
matches!(
tag,
HW_TAG_512 | HW_TAG_768 | HW_TAG_1024 | HW_TAG_HYBRID_768 | HW_TAG_SIGNING
)
}
pub(crate) fn generate_and_store(
label: &str,
seed_len: usize,
backend_id: u8,
) -> Result<(Vec<u8>, Zeroizing<Vec<u8>>), PqfileError> {
#[cfg(target_arch = "wasm32")]
{
let _ = (label, seed_len, backend_id);
return Err(wasm_unsupported());
}
#[cfg(not(target_arch = "wasm32"))]
{
let mut seed = Zeroizing::new(vec![0u8; seed_len]);
getrandom::fill(seed.as_mut()).map_err(|_| PqfileError::EncryptionFailure)?;
let key_ref = stub::HardwareKeyRef {
backend_id,
label: label.to_owned(),
};
dispatch_store(backend_id, &key_ref, &seed)?;
let stub_body = key_ref.encode();
Ok((stub_body, seed))
}
}
pub(crate) fn load_seed(body: &[u8]) -> Result<Zeroizing<Vec<u8>>, PqfileError> {
#[cfg(target_arch = "wasm32")]
{
let _ = body;
return Err(wasm_unsupported());
}
#[cfg(not(target_arch = "wasm32"))]
{
let key_ref = stub::HardwareKeyRef::decode(body)?;
dispatch_load(key_ref.backend_id, &key_ref)
}
}
#[allow(dead_code)]
pub(crate) fn delete_seed(body: &[u8]) -> Result<(), PqfileError> {
#[cfg(target_arch = "wasm32")]
{
let _ = body;
return Err(wasm_unsupported());
}
#[cfg(not(target_arch = "wasm32"))]
{
let key_ref = stub::HardwareKeyRef::decode(body)?;
dispatch_delete(key_ref.backend_id, &key_ref)
}
}
pub fn default_backend_id() -> u8 {
stub::BACKEND_CREDENTIAL_STORE
}
#[cfg(not(target_arch = "wasm32"))]
fn dispatch_store(
backend_id: u8,
key_ref: &stub::HardwareKeyRef,
seed: &[u8],
) -> Result<(), PqfileError> {
match backend_id {
stub::BACKEND_CREDENTIAL_STORE => {
credential_store::CredentialStoreBackend.store_seed(key_ref, seed)
}
id => Err(unknown_backend(id)),
}
}
#[cfg(not(target_arch = "wasm32"))]
fn dispatch_load(
backend_id: u8,
key_ref: &stub::HardwareKeyRef,
) -> Result<Zeroizing<Vec<u8>>, PqfileError> {
match backend_id {
stub::BACKEND_CREDENTIAL_STORE => {
credential_store::CredentialStoreBackend.load_seed(key_ref)
}
id => Err(unknown_backend(id)),
}
}
#[cfg(not(target_arch = "wasm32"))]
#[allow(dead_code)]
fn dispatch_delete(backend_id: u8, key_ref: &stub::HardwareKeyRef) -> Result<(), PqfileError> {
match backend_id {
stub::BACKEND_CREDENTIAL_STORE => {
credential_store::CredentialStoreBackend.delete_seed(key_ref)
}
id => Err(unknown_backend(id)),
}
}
#[cfg(not(target_arch = "wasm32"))]
fn unknown_backend(id: u8) -> PqfileError {
PqfileError::Io(std::io::Error::other(format!(
"unknown hardware key backend: {id:#04x}"
)))
}
#[cfg(target_arch = "wasm32")]
fn wasm_unsupported() -> PqfileError {
PqfileError::Io(std::io::Error::other(
"hardware keys are not supported on WASM builds",
))
}