use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use crate::Result;
#[cfg(feature = "fetcher-env")]
mod env;
#[cfg(feature = "fetcher-file")]
mod file;
#[cfg(feature = "fetcher-keychain")]
mod keychain;
#[cfg(feature = "fetcher-tpm")]
mod tpm;
#[cfg(feature = "fetcher-env")]
pub use self::env::EnvFetch;
#[cfg(feature = "fetcher-file")]
pub use self::file::FileFetch;
#[cfg(feature = "fetcher-keychain")]
pub use self::keychain::KeychainFetch;
#[cfg(feature = "fetcher-tpm")]
pub use self::tpm::TpmFetch;
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct FetchContext {
pub key_name: String,
}
impl FetchContext {
#[must_use]
pub fn new(key_name: impl Into<String>) -> Self {
Self {
key_name: key_name.into(),
}
}
}
pub struct RawKey {
bytes: Vec<u8>,
}
impl RawKey {
#[must_use]
pub fn new(bytes: Vec<u8>) -> Self {
Self { bytes }
}
#[must_use]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[allow(dead_code)] #[must_use]
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl fmt::Debug for RawKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawKey")
.field("len", &self.bytes.len())
.field("bytes", &"<redacted>")
.finish()
}
}
impl Drop for RawKey {
fn drop(&mut self) {
if !self.bytes.is_empty() {
unsafe {
let ptr = self.bytes.as_mut_ptr();
for i in 0..self.bytes.len() {
core::ptr::write_volatile(ptr.add(i), 0u8);
}
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
}
pub trait KeyFetch: Send + Sync {
fn fetch(&self, ctx: &FetchContext) -> Result<RawKey>;
fn describe(&self) -> Cow<'_, str>;
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn raw_key_debug_is_redacted() {
let key = RawKey::new(alloc::vec![0xaa, 0xbb, 0xcc, 0xdd]);
let rendered = format!("{key:?}");
assert!(rendered.contains("<redacted>"));
assert!(!rendered.contains("aa"));
assert!(!rendered.contains("bb"));
assert!(rendered.contains("len"));
}
#[test]
fn raw_key_len_and_empty() {
let empty = RawKey::new(Vec::new());
assert!(empty.is_empty());
assert_eq!(empty.len(), 0);
let one = RawKey::new(alloc::vec![1, 2, 3]);
assert!(!one.is_empty());
assert_eq!(one.len(), 3);
}
#[test]
fn fetch_context_holds_name() {
let ctx = FetchContext::new("db-primary");
assert_eq!(ctx.key_name, "db-primary");
}
}