Skip to main content

uselesskey_hmac/srp/
spec.rs

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