Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions litebox/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,3 +754,65 @@ pub trait CrngProvider {
/// failures.
fn fill_bytes_crng(&self, buf: &mut [u8]);
}

/// Provider of derived device-specific keys.
///
/// Some shims need support for deriving keys that survives past reboots (for example, to support
/// secure storage). Such keys are derived from some device-specific secret (called `root_key`) and
/// some `context`, and a key derivation function (KDF).
///
/// Platforms might differ drastically on their own notion of device-specific secrets, and what
/// "reboot-surviving" means. Some platforms might have a real never-revealed never-modified root
/// key (e.g., TPMs), while others might maintain a persistent key across LiteBox invocations,
/// but that persistent-key might be re-initialized to a new value every "real" reboot, etc.
///
/// Concretely, no shim can depend _directly_ on the existence of a device-specific secret. However,
/// interestingly, for cryptographically strong KDFs where you do not know the root key,
/// `KDF(root_key, context)` is indistinguishable from `KDF(KDF'(root_key, context'), context)`, etc.
/// Thus, while some shims might be particular about their choice of KDF, and platforms might be
/// particular about their choice of KDFs, they can mutually-distrustingly just simply run two KDFs
/// if needed. For performance reasons however, this is not ideal to be forced always, and thus this
/// specific provider supports a model that allows for more pragmatic choices, while making sure
/// that the platform has final say on the total strictness of the root key (since it is what
/// finally owns the root key).
#[expect(
clippy::type_complexity,
reason = "separating the KDF fn into its own type makes it harder to read"
)]
pub trait DerivedKeyProvider {
/// Derive a new key using the `shim_kdf` (if provided) and the current context
/// (`params.context`), and place it into `params.output`.
///
/// The platform is allowed to completely ignore the provided `shim_kdf` and use its own KDF
/// instead if it chooses to; alternatively, some platforms might not have their own KDFs, and
/// only run if the shim provides a KDF.
///
/// The `shim_kdf` is a `fn` not a `Fn`/`FnMut`/`FnOnce` in order to incentivize usage of pure
/// functions.
fn derive_key<E>(
&self,
shim_kdf: Option<fn(&[u8], KDFParams) -> Result<(), E>>,
params: KDFParams,
) -> Result<(), DerivedKeyError<E>>;
}

/// Input and output parameters to a KDF other than the secret itself.
pub struct KDFParams<'a> {
/// The input context provided to the KDF. The output is guaranteed to be the same if the same
/// input context is provided.
pub context: &'a [u8],
/// The output of the KDF produces a key of the exact length of the buffer provided as a
/// parameter.
pub output: &'a mut [u8],
}

#[derive(Debug, Error)]
/// Errors that might be returned upon attempting to derive a key.
pub enum DerivedKeyError<ShimKDFError> {
#[error("platform does not support purely-platform KDFs")]
ShimKDFRequired,
#[error(transparent)]
ShimKDFError(#[from] ShimKDFError),
#[error("this platform does not support reboot-persistent keys")]
UnsupportedRebootPersistentKey,
}
58 changes: 58 additions & 0 deletions litebox_platform_linux_userland/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ pub struct LinuxUserland {
/// CoW-eligible memory regions. Maps start address of the static slice, to the info needed to
/// re-mmap the file.
cow_regions: std::sync::RwLock<std::collections::BTreeMap<usize, CowRegionInfo>>,
/// If [`Self::initialize_boot_specific_kdf_support`] has been run, this is set to a value that
/// is persistent across multiple process executions, however, it is ephemeral across true
/// reboots.
boot_id: std::sync::OnceLock<Vec<u8>>,
}

impl core::fmt::Debug for LinuxUserland {
Expand Down Expand Up @@ -258,10 +262,37 @@ impl LinuxUserland {
reserved_pages,
vdso_address,
cow_regions: std::sync::RwLock::new(std::collections::BTreeMap::new()),
boot_id: std::sync::OnceLock::new(),
};
Box::leak(Box::new(platform))
}

/// Initializes support for KDFs by using boot-specific uniqueness.
///
/// NOTE: The boot-specific uniqueness is NOT secure against an adversary with code execution or
/// file read permissions on the host file system, since other processes on the same system can
/// also derive the exact same keys.
///
/// # Panics
///
/// Panics if some standard Linux kernel-provided files are not available/accessible.
///
/// Panics if run more than once on the same platform instance.
pub fn initialize_boot_specific_kdf_support(&self) {
let parsed: Vec<u8> = std::fs::read("/proc/sys/kernel/random/boot_id")
.unwrap()
.trim_ascii()
.split(|&x| x == b'-')
.flat_map(|chunk| {
chunk
.chunks(2)
.map(|t| u8::from_str_radix(str::from_utf8(t).unwrap(), 16).unwrap())
})
.collect();
assert_eq!(parsed.len(), 16);
self.boot_id.set(parsed).unwrap();
}

/// Register a CoW-eligible memory region backed by a file.
///
/// # Panics
Expand Down Expand Up @@ -2747,6 +2778,33 @@ impl litebox::platform::CrngProvider for LinuxUserland {
}
}

impl litebox::platform::DerivedKeyProvider for LinuxUserland {
fn derive_key<E>(
&self,
shim_kdf: Option<fn(&[u8], litebox::platform::KDFParams) -> Result<(), E>>,
params: litebox::platform::KDFParams,
) -> Result<(), litebox::platform::DerivedKeyError<E>> {
let Some(boot_id) = self.boot_id.get() else {
return Err(litebox::platform::DerivedKeyError::UnsupportedRebootPersistentKey);
};
match shim_kdf {
None => {
// TODO: Ideally, we'd use something like argon2 or such here to support more shims,
// but for now, we just return an error.
Err(litebox::platform::DerivedKeyError::ShimKDFRequired)
}
Some(shim_kdf) => {
// We trust the shim in this platform, since it is in the same trust boundary as us.
// Thus (unlike some other platforms) we do not need to manually hide the "key", and
// can just run the KDF as-is.
//
// Our key is actually just the boot ID itself.
Ok(shim_kdf(boot_id, params)?)
}
}
}
}

/// Dummy `VmapManager`.
///
/// In general, userland platforms do not support `vmap` and `vunmap` (which are kernel functions).
Expand Down
Loading