Skip to main content

basil_core/core/seal/unlock/
mod.rs

1// SPDX-FileCopyrightText: 2026 OpenBasil Contributors
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Unlock-method abstraction (§3 of `designs/unlock-and-bundle.html`).
6//!
7//! One [`UnlockMethod`] = the logic behind one slot kind. A method turns its
8//! non-secret [`MethodParams`](super::format::MethodParams) plus an out-of-band
9//! secret (`YubiKey` touch / BIP39 phrase / passphrase file) into the ability to unwrap
10//! *this slot's* `KekWrap`, recovering the 32-byte master KEK. Every fallible
11//! step returns a `Result`; there is no `unwrap`/`expect`/panicking index on any
12//! path (§1.3).
13
14use super::{MasterKek, format};
15
16#[cfg(feature = "unlock-age-yubikey")]
17pub mod age_yubikey;
18#[cfg(feature = "unlock-bip39")]
19pub mod bip39;
20mod kdf;
21pub mod passphrase;
22pub mod tpm;
23
24/// Errors any unlock method may return (§3).
25#[derive(Debug, thiserror::Error)]
26pub enum UnlockError {
27    /// Method not usable right now on this host (no token, no key file, …).
28    #[error("method unavailable: {0}")]
29    Unavailable(String),
30
31    /// Wrong PIN / phrase / passphrase: authentication of the wrap failed.
32    #[error("authentication failed")]
33    AuthFailed,
34
35    /// Reserved method (e.g. TPM) not yet implemented. Fails closed.
36    #[error("method not implemented: {0}")]
37    NotImplemented(format::MethodKind),
38
39    /// The slot params did not match the method (malformed bundle).
40    #[error("slot params mismatch: {0}")]
41    ParamsMismatch(String),
42
43    /// A crypto/KDF failure.
44    #[error("crypto: {0}")]
45    Crypto(String),
46
47    /// An I/O failure (reading a key file, invoking a plugin).
48    #[error("io: {0}")]
49    Io(#[from] std::io::Error),
50}
51
52/// One unlock method = the logic behind one slot kind (§3).
53///
54/// Implementations are stateless w.r.t. the master KEK; they only know how to
55/// turn their `params` + an out-of-band secret into the ability to unwrap this
56/// slot's `KekWrap`, or to build a fresh wrap over a given KEK.
57pub trait UnlockMethod: Send + Sync {
58    /// Which slot kind this method handles.
59    fn kind(&self) -> format::MethodKind;
60
61    /// Is this method usable right now on this host? (token present, key file
62    /// readable, phrase source available). Used to order attempts and report
63    /// status.
64    fn available(&self) -> bool;
65
66    /// Recover the master KEK from one slot, performing whatever interaction the
67    /// method needs. Returns the unwrapped KEK or an [`UnlockError`] (no panic).
68    ///
69    /// # Errors
70    /// Any unlock failure (auth, unavailable, crypto, …), failing closed.
71    fn recover_kek(&self, slot: &format::Slot, header_aad: &[u8])
72    -> Result<MasterKek, UnlockError>;
73
74    /// Build a fresh `(params, wrap)` for this method over an existing master
75    /// KEK (used by `init` / add-slot / re-seal). `slot_id` is the id the new
76    /// slot will carry; it is bound into the KEK-wrap AAD (`header || slot_id`,
77    /// §2.4) so `recover_kek` for the same slot reproduces the AAD exactly.
78    ///
79    /// # Errors
80    /// Any wrap failure (unavailable method, crypto): fail closed.
81    fn wrap_kek(
82        &self,
83        kek: &MasterKek,
84        header_aad: &[u8],
85        slot_id: u32,
86    ) -> Result<(format::MethodParams, format::KekWrap), UnlockError>;
87}