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}