pub mod aead;
pub mod hash;
pub mod kdf;
pub use aead::{open, seal, AuthError, SealError};
pub use hash::{audit_chain_hash, AUDIT_CHAIN_DOMAIN, AUDIT_CHAIN_OUT_BYTES};
use zeroize::{Zeroize, ZeroizeOnDrop};
pub const KEY_BYTES: usize = 32;
pub const NONCE_BYTES: usize = 12;
pub const TAG_BYTES: usize = 16;
pub const SECURE_FRAME_BYTES: usize = 60;
pub const SECURE_FRAME_MASTER_BYTES: usize = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KdfError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyError {
InvalidLength(usize),
InvalidCharacter(usize, char),
}
impl core::fmt::Display for KeyError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
KeyError::InvalidLength(len) => {
write!(f, "key hex must be 64 characters, got {len}")
}
KeyError::InvalidCharacter(pos, ch) => {
write!(f, "invalid hex character '{ch}' at position {pos}")
}
}
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct Key {
pub(crate) bytes: [u8; KEY_BYTES],
}
impl Key {
pub fn from_bytes(bytes: [u8; KEY_BYTES]) -> Self {
Key { bytes }
}
pub fn from_hex(hex: &str) -> Result<Self, KeyError> {
let bytes = crate::util::decode_hex_32(hex.as_bytes()).map_err(|e| match e {
crate::util::HexDecodeError::InvalidLength(n) => KeyError::InvalidLength(n),
crate::util::HexDecodeError::InvalidCharacter(pos, ch) => {
KeyError::InvalidCharacter(pos, ch)
}
})?;
Ok(Key { bytes })
}
pub fn as_bytes(&self) -> &[u8; KEY_BYTES] {
&self.bytes
}
}
#[cfg(feature = "std")]
impl Key {
pub fn from_file(path: &std::path::Path) -> std::io::Result<Self> {
let hex = std::fs::read_to_string(path)?;
let hex = hex.trim();
Key::from_hex(hex).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("failed to parse key file {}: {e}", path.display()),
)
})
}
}
impl core::fmt::Debug for Key {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Key").finish_non_exhaustive()
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct BearerToken {
bytes: [u8; 32],
}
impl BearerToken {
pub fn from_bytes(bytes: [u8; 32]) -> Self {
BearerToken { bytes }
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.bytes
}
}
impl core::fmt::Debug for BearerToken {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BearerToken").finish_non_exhaustive()
}
}
pub use crate::util::{ct_eq, decode_hex_32, HexDecodeError};
#[cfg(all(doc, feature = "crypto"))]
pub fn _key_must_not_be_clone() {}
#[cfg(all(doc, feature = "crypto"))]
pub fn _bearer_token_must_not_be_clone() {}
#[cfg(test)]
mod tests {
use super::*;
use std::format;
#[test]
fn key_from_hex_valid() {
let hex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
let key = Key::from_hex(hex).expect("valid hex key should parse");
assert_eq!(key.bytes[0], 0x00);
assert_eq!(key.bytes[1], 0x01);
assert_eq!(key.bytes[31], 0x1f);
}
#[test]
fn key_from_hex_invalid_length() {
let hex = "00";
let err = Key::from_hex(hex).unwrap_err();
assert!(matches!(err, KeyError::InvalidLength(2)));
}
#[test]
fn key_from_hex_invalid_char() {
let hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
let err = Key::from_hex(hex).unwrap_err();
assert!(matches!(err, KeyError::InvalidCharacter(..)));
}
#[test]
fn key_debug_format_hides_secret() {
let key = Key::from_bytes([0x42; 32]);
let debug_str = format!("{:?}", key);
assert!(!debug_str.contains("42"))
}
}