#[cfg(not(target_arch = "wasm32"))]
use crate::error::PqfileError;
pub const BACKEND_CREDENTIAL_STORE: u8 = 0x01;
pub const BACKEND_PKCS11: u8 = 0x02;
#[cfg(not(target_arch = "wasm32"))]
const STUB_VERSION: u8 = 0x01;
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Clone)]
pub(crate) struct HardwareKeyRef {
pub backend_id: u8,
pub label: String,
}
#[cfg(not(target_arch = "wasm32"))]
impl HardwareKeyRef {
pub fn encode(&self) -> Vec<u8> {
let label_bytes = self.label.as_bytes();
let mut out = Vec::with_capacity(2 + label_bytes.len());
out.push(STUB_VERSION);
out.push(self.backend_id);
out.extend_from_slice(label_bytes);
out
}
pub fn decode(body: &[u8]) -> Result<Self, PqfileError> {
if body.len() < 2 {
return Err(PqfileError::InvalidPem(
"hardware key stub body too short".into(),
));
}
if body[0] != STUB_VERSION {
return Err(PqfileError::InvalidPem(format!(
"unsupported hardware key stub version: {:#04x}",
body[0]
)));
}
let backend_id = body[1];
let label = String::from_utf8(body[2..].to_vec()).map_err(|_| {
PqfileError::InvalidPem("hardware key stub label is not valid UTF-8".into())
})?;
if label.is_empty() {
return Err(PqfileError::InvalidPem(
"hardware key stub label is empty".into(),
));
}
Ok(Self { backend_id, label })
}
}
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use super::*;
#[test]
fn encode_decode_roundtrip() {
let r = HardwareKeyRef {
backend_id: BACKEND_CREDENTIAL_STORE,
label: "my-key".into(),
};
let body = r.encode();
let decoded = HardwareKeyRef::decode(&body).unwrap();
assert_eq!(decoded.backend_id, BACKEND_CREDENTIAL_STORE);
assert_eq!(decoded.label, "my-key");
}
#[test]
fn decode_rejects_empty_body() {
assert!(HardwareKeyRef::decode(&[]).is_err());
}
#[test]
fn decode_rejects_unknown_version() {
let body = [0xFF, BACKEND_CREDENTIAL_STORE, b'k', b'e', b'y'];
assert!(HardwareKeyRef::decode(&body).is_err());
}
#[test]
fn decode_rejects_empty_label() {
let body = [STUB_VERSION, BACKEND_CREDENTIAL_STORE];
assert!(HardwareKeyRef::decode(&body).is_err());
}
#[test]
fn encode_decode_unicode_label() {
let r = HardwareKeyRef {
backend_id: BACKEND_CREDENTIAL_STORE,
label: "🔑 my key".into(),
};
let body = r.encode();
let decoded = HardwareKeyRef::decode(&body).unwrap();
assert_eq!(decoded.label, "🔑 my key");
}
}