1use crypto_core::{CryptoError, HkdfFailureKind, HkdfHash};
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum HkdfSuite {
11 Sha2_256,
13 Sha2_384,
15 Sha3_256,
17}
18
19impl HkdfSuite {
20 pub const fn hash(self) -> HkdfHash {
22 match self {
23 Self::Sha2_256 => HkdfHash::Sha2_256,
24 Self::Sha2_384 => HkdfHash::Sha2_384,
25 Self::Sha3_256 => HkdfHash::Sha3_256,
26 }
27 }
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub enum DomainKeyPurpose {
33 AeadContentKey,
35 AeadWrapKey,
37 AuthProofKey,
39 ManifestCommitmentKey,
41}
42
43impl DomainKeyPurpose {
44 pub(crate) const fn as_bytes(self) -> &'static [u8] {
45 match self {
46 Self::AeadContentKey => b"aead_content_key",
47 Self::AeadWrapKey => b"aead_wrap_key",
48 Self::AuthProofKey => b"auth_proof_key",
49 Self::ManifestCommitmentKey => b"manifest_commitment_key",
50 }
51 }
52}
53
54#[derive(Zeroize, ZeroizeOnDrop, PartialEq, Eq)]
59pub struct DomainTag {
60 bytes: Vec<u8>,
61}
62
63impl core::fmt::Debug for DomainTag {
64 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65 formatter.write_str("DomainTag(<redacted>)")
66 }
67}
68
69impl DomainTag {
70 pub fn from_slice(input: &[u8]) -> Result<Self, CryptoError> {
73 if input.is_empty() || input.len() > 48 {
74 return Err(CryptoError::Hkdf {
75 hash: HkdfHash::Sha3_256,
76 kind: HkdfFailureKind::InvalidDomainTagLength,
77 });
78 }
79
80 for byte in input {
81 let is_lower_alpha = byte.is_ascii_lowercase();
82 let is_digit = byte.is_ascii_digit();
83 let is_punctuation = matches!(*byte, b'/' | b'_' | b'-');
84 if !(is_lower_alpha || is_digit || is_punctuation) {
85 return Err(CryptoError::Hkdf {
86 hash: HkdfHash::Sha3_256,
87 kind: HkdfFailureKind::InvalidDomainTagByte,
88 });
89 }
90 }
91
92 Ok(Self {
93 bytes: input.to_vec(),
94 })
95 }
96
97 pub fn as_bytes(&self) -> &[u8] {
99 &self.bytes
100 }
101}