use core::fmt;
use crate::keystore::traits::{
Backend, DeviceKeyStore, DirectMasterKeyStore, MasterKey, WrappedMasterKey,
WrappedMasterKeyStore,
};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, Default)]
pub struct StubProvider;
impl StubProvider {
const fn platform_name() -> &'static str {
crate::keystore::platform::platform_identity::name()
}
const fn planned_backend() -> &'static str {
crate::keystore::platform::platform_identity::planned_backend()
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StubKeyStore;
impl StubKeyStore {
#[must_use]
pub const fn open_best_available() -> Self {
Self
}
fn unsupported() -> Error {
Error::UnsupportedPlatform {
platform: StubProvider::platform_name(),
}
}
}
impl DeviceKeyStore for StubKeyStore {
fn backend(&self) -> Backend {
Backend::Unavailable
}
fn open(&self, _label: &str) -> Result<Box<dyn crate::DeviceSigner>> {
Err(Self::unsupported())
}
fn create(&self, _label: &str) -> Result<Box<dyn crate::DeviceSigner>> {
Err(Self::unsupported())
}
fn delete(&self, _label: &str) -> Result<()> {
Err(Self::unsupported())
}
}
impl fmt::Display for StubKeyStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} has no device identity key store backend yet (planned: {})",
StubProvider::platform_name(),
StubProvider::planned_backend()
)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StubDirectMasterKeyStore;
impl StubDirectMasterKeyStore {
#[must_use]
pub const fn open_best_available() -> Self {
Self
}
}
impl DirectMasterKeyStore for StubDirectMasterKeyStore {
fn backend(&self) -> Backend {
Backend::Unavailable
}
fn store(&self, _label: &str, _key: &MasterKey) -> Result<()> {
Err(StubKeyStore::unsupported())
}
fn load(&self, _label: &str) -> Result<MasterKey> {
Err(StubKeyStore::unsupported())
}
fn delete(&self, _label: &str) -> Result<()> {
Err(StubKeyStore::unsupported())
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StubWrappedMasterKeyStore;
impl StubWrappedMasterKeyStore {
#[must_use]
pub const fn open_best_available() -> Self {
Self
}
}
impl WrappedMasterKeyStore for StubWrappedMasterKeyStore {
fn backend(&self) -> Backend {
Backend::Unavailable
}
fn store(&self, _label: &str, _key: &MasterKey) -> Result<WrappedMasterKey> {
Err(StubKeyStore::unsupported())
}
fn load(&self, _label: &str, _wrapped: &WrappedMasterKey) -> Result<MasterKey> {
Err(StubKeyStore::unsupported())
}
fn delete(&self, _label: &str) -> Result<()> {
Err(StubKeyStore::unsupported())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stub_reports_unavailable() {
let store = StubKeyStore::open_best_available();
assert_eq!(store.backend(), Backend::Unavailable);
assert!(!store.backend().is_hardware_backed());
}
#[test]
fn every_operation_fails_with_unsupported_platform() {
let store = StubKeyStore::open_best_available();
for result in [
store.open("x").map(|_| ()),
store.create("x").map(|_| ()),
store.delete("x"),
] {
match result {
Err(Error::UnsupportedPlatform { platform }) => {
assert!(!platform.is_empty(), "the platform name must not be empty");
}
other => panic!("expected UnsupportedPlatform, got {other:?}"),
}
}
}
#[test]
fn open_or_create_also_fails() {
let store = StubKeyStore::open_best_available();
assert!(store.open_or_create("x").is_err());
}
#[test]
fn display_explains_the_situation() {
let store = StubKeyStore::open_best_available();
let text = store.to_string();
assert!(
text.contains("has no device identity key store backend yet"),
"the text does not say the backend is missing: {text}"
);
assert!(!text.is_empty());
}
#[test]
fn master_key_forms_report_unsupported() {
let direct = StubDirectMasterKeyStore::open_best_available();
assert_eq!(direct.backend(), Backend::Unavailable);
assert!(!direct.backend().is_hardware_backed());
let key = MasterKey::from_bytes([0x01_u8; crate::keystore::MASTER_KEY_LEN]);
let results: [Result<()>; 4] = [
direct.store("account-1", &key),
direct.load("account-1").map(|_| ()),
direct.delete("account-1"),
StubWrappedMasterKeyStore::open_best_available().delete("account-1"),
];
for result in results {
match result {
Err(Error::UnsupportedPlatform { platform }) => {
assert!(!platform.is_empty(), "the platform name must not be empty");
}
other => panic!("expected UnsupportedPlatform, got {other:?}"),
}
}
let wrapped = StubWrappedMasterKeyStore::open_best_available();
assert_eq!(wrapped.backend(), Backend::Unavailable);
let error = wrapped
.store("account-1", &key)
.expect_err("must be unsupported");
assert!(matches!(error, Error::UnsupportedPlatform { .. }));
let empty = WrappedMasterKey::from_bytes(Vec::new());
let error = wrapped
.load("account-1", &empty)
.expect_err("must be unsupported");
assert!(matches!(error, Error::UnsupportedPlatform { .. }));
}
}