use crypto_core::{CryptoError, HkdfFailureKind, HkdfHash};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HkdfSuite {
Sha2_256,
Sha2_384,
Sha3_256,
}
impl HkdfSuite {
pub const fn hash(self) -> HkdfHash {
match self {
Self::Sha2_256 => HkdfHash::Sha2_256,
Self::Sha2_384 => HkdfHash::Sha2_384,
Self::Sha3_256 => HkdfHash::Sha3_256,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DomainKeyPurpose {
AeadContentKey,
AeadWrapKey,
AuthProofKey,
ManifestCommitmentKey,
}
impl DomainKeyPurpose {
pub(crate) const fn as_bytes(self) -> &'static [u8] {
match self {
Self::AeadContentKey => b"aead_content_key",
Self::AeadWrapKey => b"aead_wrap_key",
Self::AuthProofKey => b"auth_proof_key",
Self::ManifestCommitmentKey => b"manifest_commitment_key",
}
}
}
#[derive(Zeroize, ZeroizeOnDrop, PartialEq, Eq)]
pub struct DomainTag {
bytes: Vec<u8>,
}
impl core::fmt::Debug for DomainTag {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("DomainTag(<redacted>)")
}
}
impl DomainTag {
pub fn from_slice(input: &[u8]) -> Result<Self, CryptoError> {
if input.is_empty() || input.len() > 48 {
return Err(CryptoError::Hkdf {
hash: HkdfHash::Sha3_256,
kind: HkdfFailureKind::InvalidDomainTagLength,
});
}
for byte in input {
let is_lower_alpha = byte.is_ascii_lowercase();
let is_digit = byte.is_ascii_digit();
let is_punctuation = matches!(*byte, b'/' | b'_' | b'-');
if !(is_lower_alpha || is_digit || is_punctuation) {
return Err(CryptoError::Hkdf {
hash: HkdfHash::Sha3_256,
kind: HkdfFailureKind::InvalidDomainTagByte,
});
}
}
Ok(Self {
bytes: input.to_vec(),
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}