use crate::Result;
use crate::format::AuthBlob;
use crate::key::MasterKey;
use crate::traits::{AuthBoundKeyStore, Availability, Backend};
use std::sync::Arc;
pub trait PlatformSealer: core::fmt::Debug + Send + Sync {
fn backend(&self) -> Backend;
fn availability(&self) -> Availability;
fn seal(&self, plaintext: &[u8]) -> Result<Vec<u8>>;
fn unseal(&self, envelope: &[u8]) -> Result<Vec<u8>>;
fn delete(&self) -> Result<()>;
}
#[derive(Debug)]
pub struct DelegatingKeyStore {
sealer: Arc<dyn PlatformSealer>,
}
impl DelegatingKeyStore {
#[must_use]
pub fn new(sealer: Arc<dyn PlatformSealer>) -> Self {
Self { sealer }
}
}
impl AuthBoundKeyStore for DelegatingKeyStore {
fn availability(&self) -> Availability {
self.sealer.availability()
}
fn create(&self) -> Result<()> {
Ok(())
}
fn wrap(&self, master: &MasterKey) -> Result<Vec<u8>> {
let envelope = self.sealer.seal(master.expose_secret())?;
let blob = AuthBlob::new(self.sealer.backend(), envelope)?;
Ok(blob.encode())
}
fn unwrap(&self, blob: &[u8]) -> Result<MasterKey> {
let blob = AuthBlob::parse(blob)?;
blob.expect_backend(self.sealer.backend())?;
let plaintext = self.sealer.unseal(blob.wrapped())?;
MasterKey::from_bytes(&plaintext)
}
fn delete_blob(&self, blob: &[u8]) -> Result<()> {
let blob = AuthBlob::parse(blob)?;
blob.expect_backend(self.sealer.backend())?;
self.sealer.delete()
}
fn delete(&self) -> Result<()> {
self.sealer.delete()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error;
use crate::key::MASTER_KEY_LEN;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug, Default)]
struct FakeSealer {
present: AtomicBool,
cancelled: AtomicBool,
sealed: Mutex<Option<Vec<u8>>>,
}
impl PlatformSealer for FakeSealer {
fn backend(&self) -> Backend {
Backend::AndroidKeystore
}
fn availability(&self) -> Availability {
Availability::Available {
backend: Backend::AndroidKeystore,
hardware_backed: true,
}
}
fn seal(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
if self.cancelled.load(Ordering::SeqCst) {
return Err(Error::AuthenticationFailed);
}
let sealed: Vec<u8> = plaintext.iter().map(|byte| byte ^ 0x5A).collect();
*self.sealed.lock().expect("lock") = Some(sealed.clone());
self.present.store(true, Ordering::SeqCst);
Ok(sealed)
}
fn unseal(&self, envelope: &[u8]) -> Result<Vec<u8>> {
if self.cancelled.load(Ordering::SeqCst) {
return Err(Error::AuthenticationFailed);
}
if !self.present.load(Ordering::SeqCst) {
return Err(Error::AuthenticationFailed);
}
Ok(envelope.iter().map(|byte| byte ^ 0x5A).collect())
}
fn delete(&self) -> Result<()> {
self.present.store(false, Ordering::SeqCst);
Ok(())
}
}
fn store() -> (DelegatingKeyStore, Arc<FakeSealer>) {
let sealer = Arc::new(FakeSealer::default());
(DelegatingKeyStore::new(sealer.clone()), sealer)
}
#[test]
fn a_wrap_roundtrips_through_the_host() {
let (store, sealer) = store();
let master = MasterKey::from_bytes(&[7_u8; MASTER_KEY_LEN]).expect("master key");
let blob = store.wrap(&master).expect("wrap");
assert!(blob.starts_with(b"Q2FABIO1"));
assert_eq!(
AuthBlob::parse(&blob).expect("parse").backend(),
Backend::AndroidKeystore
);
assert_ne!(&blob[blob.len() - MASTER_KEY_LEN..], master.expose_secret());
assert!(sealer.present.load(Ordering::SeqCst));
assert_eq!(store.unwrap(&blob).expect("unwrap"), master);
}
#[test]
fn a_cancelled_gesture_is_authentication_failed() {
let (store, sealer) = store();
let master = MasterKey::from_bytes(&[1_u8; MASTER_KEY_LEN]).expect("master key");
let blob = store.wrap(&master).expect("wrap");
sealer.cancelled.store(true, Ordering::SeqCst);
assert!(matches!(
store.unwrap(&blob),
Err(Error::AuthenticationFailed)
));
}
#[test]
fn a_windows_blob_is_refused() {
let (store, _sealer) = store();
let blob = AuthBlob::new(Backend::WindowsWebAuthn, vec![1, 2, 3])
.expect("blob")
.encode();
assert!(matches!(
store.unwrap(&blob),
Err(Error::WrongBackend { .. })
));
}
#[test]
fn a_short_plaintext_is_rejected() {
let sealer = Arc::new(FakeSealer::default());
sealer.present.store(true, Ordering::SeqCst);
let store = DelegatingKeyStore::new(sealer);
let blob = AuthBlob::new(Backend::AndroidKeystore, vec![0_u8; 5])
.expect("blob")
.encode();
assert!(matches!(
store.unwrap(&blob),
Err(Error::MasterKeyWrongLength { .. })
));
}
#[test]
fn delete_blob_removes_the_platform_key() {
let (store, sealer) = store();
let master = MasterKey::from_bytes(&[3_u8; MASTER_KEY_LEN]).expect("master key");
let blob = store.wrap(&master).expect("wrap");
store.delete_blob(&blob).expect("delete");
assert!(!sealer.present.load(Ordering::SeqCst));
assert!(store.unwrap(&blob).is_err());
}
#[test]
fn create_does_not_prompt() {
let (store, sealer) = store();
store.create().expect("create");
assert!(!sealer.present.load(Ordering::SeqCst));
}
}