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}