Skip to main content

uselesskey_core_hmac_spec/
lib.rs

1#![forbid(unsafe_code)]
2
3//! Core HMAC algorithm specification model.
4//!
5//! Provides a stable enum used by fixture crates to select HS256/HS384/HS512 and
6//! derive deterministic cache keys.
7
8/// Specification for HMAC secret generation.
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
10pub enum HmacSpec {
11    /// HS256 (HMAC-SHA256)
12    Hs256,
13    /// HS384 (HMAC-SHA384)
14    Hs384,
15    /// HS512 (HMAC-SHA512)
16    Hs512,
17}
18
19impl HmacSpec {
20    /// HS256 (HMAC-SHA256). Produces a 32-byte secret.
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use uselesskey_core_hmac_spec::HmacSpec;
26    /// let spec = HmacSpec::hs256();
27    /// assert_eq!(spec.byte_len(), 32);
28    /// ```
29    pub fn hs256() -> Self {
30        Self::Hs256
31    }
32
33    /// HS384 (HMAC-SHA384). Produces a 48-byte secret.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// use uselesskey_core_hmac_spec::HmacSpec;
39    /// let spec = HmacSpec::hs384();
40    /// assert_eq!(spec.byte_len(), 48);
41    /// ```
42    pub fn hs384() -> Self {
43        Self::Hs384
44    }
45
46    /// HS512 (HMAC-SHA512). Produces a 64-byte secret.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use uselesskey_core_hmac_spec::HmacSpec;
52    /// let spec = HmacSpec::hs512();
53    /// assert_eq!(spec.byte_len(), 64);
54    /// ```
55    pub fn hs512() -> Self {
56        Self::Hs512
57    }
58
59    /// JOSE/JWT `alg` name for this HMAC algorithm.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use uselesskey_core_hmac_spec::HmacSpec;
65    /// assert_eq!(HmacSpec::hs256().alg_name(), "HS256");
66    /// ```
67    pub fn alg_name(&self) -> &'static str {
68        match self {
69            Self::Hs256 => "HS256",
70            Self::Hs384 => "HS384",
71            Self::Hs512 => "HS512",
72        }
73    }
74
75    /// Secret length, in bytes.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use uselesskey_core_hmac_spec::HmacSpec;
81    /// assert_eq!(HmacSpec::hs256().byte_len(), 32);
82    /// assert_eq!(HmacSpec::hs384().byte_len(), 48);
83    /// assert_eq!(HmacSpec::hs512().byte_len(), 64);
84    /// ```
85    pub fn byte_len(&self) -> usize {
86        match self {
87            Self::Hs256 => 32,
88            Self::Hs384 => 48,
89            Self::Hs512 => 64,
90        }
91    }
92
93    /// Stable encoding for cache keys / deterministic derivation.
94    ///
95    /// If you change this, bump the derivation version in `uselesskey-core`.
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// use uselesskey_core_hmac_spec::HmacSpec;
101    /// let bytes = HmacSpec::hs256().stable_bytes();
102    /// assert_eq!(bytes.len(), 4);
103    /// ```
104    pub fn stable_bytes(&self) -> [u8; 4] {
105        match self {
106            Self::Hs256 => [0, 0, 0, 1],
107            Self::Hs384 => [0, 0, 0, 2],
108            Self::Hs512 => [0, 0, 0, 3],
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn alg_name_and_len_match_spec() {
119        let hs256 = HmacSpec::hs256();
120        assert_eq!(hs256.alg_name(), "HS256");
121        assert_eq!(hs256.byte_len(), 32);
122
123        let hs384 = HmacSpec::hs384();
124        assert_eq!(hs384.alg_name(), "HS384");
125        assert_eq!(hs384.byte_len(), 48);
126
127        let hs512 = HmacSpec::hs512();
128        assert_eq!(hs512.alg_name(), "HS512");
129        assert_eq!(hs512.byte_len(), 64);
130    }
131
132    #[test]
133    fn stable_bytes_are_unique() {
134        let hs256 = HmacSpec::hs256().stable_bytes();
135        let hs384 = HmacSpec::hs384().stable_bytes();
136        let hs512 = HmacSpec::hs512().stable_bytes();
137
138        assert_ne!(hs256, hs384);
139        assert_ne!(hs256, hs512);
140        assert_ne!(hs384, hs512);
141    }
142}