use zeroize::Zeroizing;
use std::sync::Arc;
use parking_lot::Mutex;
use super::provider::{ContentKey, HardwareProvider, KeyCustody, CONTENT_KEY_LEN};
use super::tier::{HardwareKind, HardwareProbe};
use crate::cipher;
use crate::error::{KeystoreError, Result};
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WrapBehaviour {
#[default]
Honest,
FailWrap,
FailUnwrap,
Passthrough,
WrongKeyOnUnwrap,
EmptyWrapWithRecall,
}
#[derive(Debug, Clone)]
pub struct FakeDevice {
kind: HardwareKind,
probe: HardwareProbe,
custody: KeyCustody,
behaviour: WrapBehaviour,
device_key: Arc<Mutex<[u8; 32]>>,
unwraps_left: Arc<Mutex<Option<usize>>>,
last_wrapped: Arc<Mutex<Option<[u8; CONTENT_KEY_LEN]>>>,
}
impl FakeDevice {
pub fn working(kind: HardwareKind, device_id: u8) -> Self {
Self {
kind,
probe: HardwareProbe::Available(kind),
custody: KeyCustody::NonExportable,
behaviour: WrapBehaviour::Honest,
device_key: Arc::new(Mutex::new([device_id; 32])),
unwraps_left: Arc::default(),
last_wrapped: Arc::default(),
}
}
pub fn last_wrapped_content_key(&self) -> Option<[u8; CONTENT_KEY_LEN]> {
*self.last_wrapped.lock()
}
pub fn absent(kind: HardwareKind) -> Self {
Self {
probe: HardwareProbe::Absent,
..Self::working(kind, 0)
}
}
pub fn indeterminate(kind: HardwareKind, detail: &str) -> Self {
Self {
probe: HardwareProbe::indeterminate(detail),
..Self::working(kind, 0)
}
}
pub fn with_probe(mut self, probe: HardwareProbe) -> Self {
self.probe = probe;
self
}
pub fn with_custody(mut self, custody: KeyCustody) -> Self {
self.custody = custody;
self
}
pub fn with_behaviour(mut self, behaviour: WrapBehaviour) -> Self {
self.behaviour = behaviour;
self
}
pub fn with_kind(mut self, kind: HardwareKind) -> Self {
self.kind = kind;
self
}
pub fn rotate_device_key(&self, device_id: u8) {
*self.device_key.lock() = [device_id; 32];
}
pub fn failing_unwrap_after(self, n: usize) -> Self {
*self.unwraps_left.lock() = Some(n);
self
}
const NONCE_LEN: usize = 12;
fn fresh_nonce() -> [u8; Self::NONCE_LEN] {
let mut nonce = <[u8; Self::NONCE_LEN]>::default();
rand_core::RngCore::fill_bytes(&mut rand_core::OsRng, &mut nonce);
nonce
}
}
impl HardwareProvider for FakeDevice {
fn kind(&self) -> HardwareKind {
self.kind
}
fn probe(&self) -> HardwareProbe {
self.probe.clone()
}
fn custody(&self) -> KeyCustody {
self.custody
}
fn wrap_key(&self, content_key: &ContentKey) -> Result<Vec<u8>> {
*self.last_wrapped.lock() = Some(**content_key);
match self.behaviour {
WrapBehaviour::FailWrap => Err(KeystoreError::HardwareWrapFailed {
detail: "fake device refuses to wrap".to_owned(),
}),
WrapBehaviour::Passthrough => Ok(content_key.to_vec()),
WrapBehaviour::EmptyWrapWithRecall => Ok(Vec::new()),
_ => {
let nonce = Self::fresh_nonce();
let device_key = *self.device_key.lock();
let sealed = cipher::encrypt(&device_key, &nonce, content_key.as_slice(), b"")?;
let mut out = Vec::with_capacity(nonce.len() + sealed.len());
out.extend_from_slice(&nonce);
out.extend_from_slice(&sealed);
Ok(out)
}
}
}
fn unwrap_key(&self, wrapped: &[u8]) -> Result<ContentKey> {
{
let mut left = self.unwraps_left.lock();
if let Some(remaining) = left.as_mut() {
if *remaining == 0 {
return Err(KeystoreError::HardwareUnwrapFailed {
detail: "fake device stopped unwrapping".to_owned(),
});
}
*remaining -= 1;
}
}
match self.behaviour {
WrapBehaviour::FailUnwrap => {
return Err(KeystoreError::HardwareUnwrapFailed {
detail: "fake device refuses to unwrap".to_owned(),
})
}
WrapBehaviour::WrongKeyOnUnwrap => {
let mut wrong = <[u8; CONTENT_KEY_LEN]>::default();
rand_core::RngCore::fill_bytes(&mut rand_core::OsRng, &mut wrong);
return Ok(Zeroizing::new(wrong));
}
WrapBehaviour::EmptyWrapWithRecall => {
return self.last_wrapped.lock().map(Zeroizing::new).ok_or_else(|| {
KeystoreError::HardwareUnwrapFailed {
detail: "recall device has wrapped nothing yet".to_owned(),
}
})
}
WrapBehaviour::Passthrough => {
let bytes: [u8; CONTENT_KEY_LEN] =
wrapped
.try_into()
.map_err(|_| KeystoreError::HardwareUnwrapFailed {
detail: "passthrough device got a non-key blob".to_owned(),
})?;
return Ok(Zeroizing::new(bytes));
}
_ => {}
}
if wrapped.len() <= Self::NONCE_LEN {
return Err(KeystoreError::HardwareUnwrapFailed {
detail: "wrapped blob is too short to carry a nonce".to_owned(),
});
}
let (nonce, sealed) = wrapped.split_at(Self::NONCE_LEN);
let nonce: [u8; Self::NONCE_LEN] = nonce.try_into().expect("checked length");
let device_key = *self.device_key.lock();
let plain = cipher::decrypt(&device_key, &nonce, sealed, b"").map_err(|_| {
KeystoreError::HardwareUnwrapFailed {
detail: "wrapped key was not sealed by this device".to_owned(),
}
})?;
let bytes: [u8; CONTENT_KEY_LEN] =
plain
.as_slice()
.try_into()
.map_err(|_| KeystoreError::HardwareUnwrapFailed {
detail: "unwrapped key has the wrong length".to_owned(),
})?;
Ok(Zeroizing::new(bytes))
}
}