pub struct HardwareBoundBackend { /* private fields */ }Expand description
A KeychainBackend that hardware-binds every blob it stores, degrading to
the underlying software envelope when the host has no usable hardware.
§Example
use std::sync::Arc;
use dig_keystore::backend::{BackendKey, FileBackend, KeychainBackend};
use dig_keystore::hardware::{HardwareBoundBackend, HardwarePolicy};
let inner = FileBackend::new("/var/lib/dig/keys");
// No provider available in this build: opens, and says so honestly.
let backend = HardwareBoundBackend::new(inner, None, HardwarePolicy::Optional)?;
// What the HOST can do — the tier a new write would get.
println!("host: {}", backend.tier());
// What protects THIS key — the only answer fit to show a user, because a
// capable host can still hold a keystore that was never wrapped.
let key = BackendKey::new("identity");
let tier = backend.blob_tier(&key)?;
if tier.is_hardware_bound() {
println!("this key is {tier}");
} else {
// Never claim protection this key does not have.
println!("this key is {tier}");
}Implementations§
Source§impl HardwareBoundBackend
impl HardwareBoundBackend
Sourcepub fn new<B: KeychainBackend>(
inner: B,
provider: Option<Arc<dyn HardwareProvider>>,
policy: HardwarePolicy,
) -> Result<Self>
pub fn new<B: KeychainBackend>( inner: B, provider: Option<Arc<dyn HardwareProvider>>, policy: HardwarePolicy, ) -> Result<Self>
Decorate inner, resolving the protection tier once.
Pass provider = None to store through inner unchanged while reporting
DegradeReason::NotRequested.
§Errors
Fails closed rather than silently degrading, per policy:
HardwarePolicy::Required— any outcome short of self-tested hardware isKeystoreError::HardwareRequired.HardwarePolicy::Preferred(default) — a confident absence degrades, but anIndeterminateprobe isKeystoreError::HardwareProbeIndeterminate: “I could not determine whether this host has hardware” must not be downgraded into “it has none”, which would quietly strip protection from a machine that has it.HardwarePolicy::Optional— always opens, always reports the reason.
Sourcepub fn with_inner(
inner: Arc<dyn KeychainBackend>,
provider: Option<Arc<dyn HardwareProvider>>,
policy: HardwarePolicy,
) -> Result<Self>
pub fn with_inner( inner: Arc<dyn KeychainBackend>, provider: Option<Arc<dyn HardwareProvider>>, policy: HardwarePolicy, ) -> Result<Self>
As new, for an already shared backend.
Sourcepub fn tier(&self) -> &ProtectionTier
pub fn tier(&self) -> &ProtectionTier
What this host is bound to — the tier every newly written blob gets.
This is a statement about the machine, not about any particular stored
key. On a hardware-capable host it reports Hardware even if a given
keystore predates hardware binding and is still a bare §3 blob, because a
capable host does not retroactively protect bytes already at rest.
Before telling a user that a specific key is hardware-protected, ask
blob_tier instead. Rendering “protected by your
TPM” from this method would claim copy-resistance that an unwrapped
legacy blob does not have.
Sourcepub fn blob_tier(&self, key: &BackendKey) -> Result<ProtectionTier>
pub fn blob_tier(&self, key: &BackendKey) -> Result<ProtectionTier>
What protects the key material stored at key, read from the blob
itself.
This is the question a UI actually has, and it is not the same as
tier: a hardware-capable host can hold a keystore written
before hardware binding existed, which is protected by the passphrase
envelope alone and does open on another machine. Answering from the
stored bytes is what keeps that distinction honest.
The tier reported is the blob’s own, independent of this host: a blob
sealed by an Apple Secure Enclave reads as Hardware(MacSecureEnclave)
even on Windows. Whether this host can open it is a separate question,
answered by read.
§Errors
- The inner backend’s error if
keycannot be read (e.g.NotFound). KeystoreError::MalformedEnvelopeif the blob claims to be an envelope but is structurally invalid.KeystoreError::UnknownHardwareClassif it was sealed by hardware this build cannot name.
Both error cases fail closed: a wrapped blob is never reported as software-protected just because this build cannot fully classify it. Guessing in either direction is what this method exists to avoid.
Sourcepub fn inner(&self) -> &Arc<dyn KeychainBackend>
pub fn inner(&self) -> &Arc<dyn KeychainBackend>
The underlying storage, for callers that need it directly.
Trait Implementations§
Source§impl Debug for HardwareBoundBackend
impl Debug for HardwareBoundBackend
Source§impl KeychainBackend for HardwareBoundBackend
impl KeychainBackend for HardwareBoundBackend
Source§fn read(&self, key: &BackendKey) -> Result<Vec<u8>>
fn read(&self, key: &BackendKey) -> Result<Vec<u8>>
Read a blob, unwrapping it when it carries a hardware envelope.
A blob without the envelope prefix is returned untouched, which is what lets every keystore written before this feature — and any future inner format — keep opening (§5.1).
A blob with an envelope that this host cannot open is an error, never the raw envelope bytes: an envelope copied to a machine without the sealing hardware must fail loudly rather than hand back ciphertext that a caller would then try to parse as a keystore.
Source§fn write(&self, key: &BackendKey, data: &[u8]) -> Result<()>
fn write(&self, key: &BackendKey, data: &[u8]) -> Result<()>
Write a blob, sealing it into a hardware envelope in the hardware tier and passing the software-sealed bytes straight through otherwise.
Source§fn delete(&self, key: &BackendKey) -> Result<()>
fn delete(&self, key: &BackendKey) -> Result<()>
key. Implementations should best-effort overwrite
the storage before removing so residual disk sectors do not retain the
ciphertext.