pub mod aes;
mod signing;
pub mod v3;
pub mod v4;
pub mod v5;
use hmac::{Hmac, KeyInit, Mac};
use sha2::Sha256;
use std::{
fmt::{Display, Formatter, Result as DisplayResult},
sync::{Mutex, MutexGuard},
};
use thiserror::Error;
use v5::key_id_header::KEY_ID_HEADER_LEN;
pub(crate) type Result<T> = core::result::Result<T, Error>;
type HmacSha256 = Hmac<Sha256>;
const CRATE_DEBUG_KEY: &str = match option_env!("MY_CRATE_DEBUG_KEY") {
Some(val) => val,
None => "2025-10-28T17:23:36",
};
pub fn redacted_hash(data: &[u8]) -> String {
let mut mac = HmacSha256::new_from_slice(CRATE_DEBUG_KEY.as_ref())
.expect("HMAC can take key of any size");
mac.update(data);
hex::encode(mac.finalize().into_bytes())[..8].to_string()
}
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
#[derive(Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error {
EdocTooShort(usize),
HeaderParseErr(String),
InvalidVersion(u8),
NoIronCoreMagic,
SpecifiedLengthTooLong(u32),
ProtoSerializationErr(String),
HeaderLengthOverflow(u64),
EncryptError(String),
DecryptError(String),
EdekTypeError(String),
PayloadTypeError(String),
KeyIdHeaderTooShort(usize),
KeyIdHeaderMalformed(String),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> DisplayResult {
match self {
Error::EdocTooShort(x) => write!(f, "EDOC too short. Found {x} bytes."),
Error::HeaderParseErr(x) => write!(f, "Header parse error: '{x}'"),
Error::InvalidVersion(x) => write!(f, "Invalid EDOC version: {x}"),
Error::NoIronCoreMagic => write!(f, "Missing IronCore Magic bytes in header."),
Error::SpecifiedLengthTooLong(x) => {
write!(f, "Header too short for specified length: {x} bytes")
}
Error::ProtoSerializationErr(x) => write!(f, "Protobuf serialization error: '{x}'"),
Error::HeaderLengthOverflow(x) => write!(f, "Header length too long: {x} bytes"),
Error::EncryptError(x) => write!(f, "{x}"),
Error::DecryptError(x) => write!(f, "{x}"),
Error::KeyIdHeaderTooShort(x) => write!(
f,
"Key ID header too short. Found: {x} bytes. Required: {KEY_ID_HEADER_LEN} bytes."
),
Error::EdekTypeError(x) => write!(f, "EDEK type error: '{x}'"),
Error::PayloadTypeError(x) => write!(f, "Payload type error: '{x}'"),
Error::KeyIdHeaderMalformed(x) => write!(f, "Malformed key ID header: '{x}'"),
}
}
}
pub fn take_lock<T>(m: &Mutex<T>) -> MutexGuard<'_, T> {
m.lock().unwrap_or_else(|e| {
let error = format!("Error when acquiring lock: {e}");
panic!("{error}");
})
}
#[macro_export]
macro_rules! impl_secret_debug {
($t:ty) => {
impl std::fmt::Debug for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}(<redacted:{}...>)",
stringify!($t),
$crate::redacted_hash(self.0.as_ref())
)
}
}
};
}
#[macro_export]
macro_rules! impl_secret_debug_named {
($t:ty, $field:ident) => {
impl std::fmt::Debug for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}{{<redacted:{}...>}}",
stringify!($t),
$crate::redacted_hash(self.$field.as_ref())
)
}
}
};
}
#[cfg(test)]
mod test {
#[test]
fn impl_secret_debug_works() {
struct FooSecret([u8; 32]);
impl_secret_debug!(FooSecret);
let debug_str = format!("{:?}", FooSecret([1; 32]));
assert_eq!(debug_str, "FooSecret(<redacted:633f9067...>)");
}
#[test]
fn impl_secret_debug_named_works() {
struct FooSecret {
secret: String,
}
impl_secret_debug_named!(FooSecret, secret);
let debug_str = format!(
"{:?}",
FooSecret {
secret: "yes".to_string()
}
);
assert_eq!(debug_str, "FooSecret{<redacted:bda33dd3...>}");
}
}