requiem_http/cookie/secure/key.rs
1use ring::hkdf::{Algorithm, KeyType, Prk, HKDF_SHA256};
2use ring::rand::{SecureRandom, SystemRandom};
3
4use super::private::KEY_LEN as PRIVATE_KEY_LEN;
5use super::signed::KEY_LEN as SIGNED_KEY_LEN;
6
7static HKDF_DIGEST: Algorithm = HKDF_SHA256;
8const KEYS_INFO: &[&[u8]] = &[b"COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM"];
9
10/// A cryptographic master key for use with `Signed` and/or `Private` jars.
11///
12/// This structure encapsulates secure, cryptographic keys for use with both
13/// [PrivateJar](struct.PrivateJar.html) and [SignedJar](struct.SignedJar.html).
14/// It can be derived from a single master key via
15/// [from_master](#method.from_master) or generated from a secure random source
16/// via [generate](#method.generate). A single instance of `Key` can be used for
17/// both a `PrivateJar` and a `SignedJar`.
18///
19/// This type is only available when the `secure` feature is enabled.
20#[derive(Clone)]
21pub struct Key {
22 signing_key: [u8; SIGNED_KEY_LEN],
23 encryption_key: [u8; PRIVATE_KEY_LEN],
24}
25
26impl KeyType for &Key {
27 #[inline]
28 fn len(&self) -> usize {
29 SIGNED_KEY_LEN + PRIVATE_KEY_LEN
30 }
31}
32
33impl Key {
34 /// Derives new signing/encryption keys from a master key.
35 ///
36 /// The master key must be at least 256-bits (32 bytes). For security, the
37 /// master key _must_ be cryptographically random. The keys are derived
38 /// deterministically from the master key.
39 ///
40 /// # Panics
41 ///
42 /// Panics if `key` is less than 32 bytes in length.
43 ///
44 /// # Example
45 ///
46 /// ```rust
47 /// use requiem_http::cookie::Key;
48 ///
49 /// # /*
50 /// let master_key = { /* a cryptographically random key >= 32 bytes */ };
51 /// # */
52 /// # let master_key: &Vec<u8> = &(0..32).collect();
53 ///
54 /// let key = Key::from_master(master_key);
55 /// ```
56 pub fn from_master(key: &[u8]) -> Key {
57 if key.len() < 32 {
58 panic!(
59 "bad master key length: expected at least 32 bytes, found {}",
60 key.len()
61 );
62 }
63
64 // An empty `Key` structure; will be filled in with HKDF derived keys.
65 let mut output_key = Key {
66 signing_key: [0; SIGNED_KEY_LEN],
67 encryption_key: [0; PRIVATE_KEY_LEN],
68 };
69
70 // Expand the master key into two HKDF generated keys.
71 let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
72 let prk = Prk::new_less_safe(HKDF_DIGEST, key);
73 let okm = prk.expand(KEYS_INFO, &output_key).expect("okm expand");
74 okm.fill(&mut both_keys).expect("fill keys");
75
76 // Copy the key parts into their respective fields.
77 output_key
78 .signing_key
79 .copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
80 output_key
81 .encryption_key
82 .copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
83 output_key
84 }
85
86 /// Generates signing/encryption keys from a secure, random source. Keys are
87 /// generated nondeterministically.
88 ///
89 /// # Panics
90 ///
91 /// Panics if randomness cannot be retrieved from the operating system. See
92 /// [try_generate](#method.try_generate) for a non-panicking version.
93 ///
94 /// # Example
95 ///
96 /// ```rust
97 /// use requiem_http::cookie::Key;
98 ///
99 /// let key = Key::generate();
100 /// ```
101 pub fn generate() -> Key {
102 Self::try_generate().expect("failed to generate `Key` from randomness")
103 }
104
105 /// Attempts to generate signing/encryption keys from a secure, random
106 /// source. Keys are generated nondeterministically. If randomness cannot be
107 /// retrieved from the underlying operating system, returns `None`.
108 ///
109 /// # Example
110 ///
111 /// ```rust
112 /// use requiem_http::cookie::Key;
113 ///
114 /// let key = Key::try_generate();
115 /// ```
116 pub fn try_generate() -> Option<Key> {
117 let mut sign_key = [0; SIGNED_KEY_LEN];
118 let mut enc_key = [0; PRIVATE_KEY_LEN];
119
120 let rng = SystemRandom::new();
121 if rng.fill(&mut sign_key).is_err() || rng.fill(&mut enc_key).is_err() {
122 return None;
123 }
124
125 Some(Key {
126 signing_key: sign_key,
127 encryption_key: enc_key,
128 })
129 }
130
131 /// Returns the raw bytes of a key suitable for signing cookies.
132 ///
133 /// # Example
134 ///
135 /// ```rust
136 /// use requiem_http::cookie::Key;
137 ///
138 /// let key = Key::generate();
139 /// let signing_key = key.signing();
140 /// ```
141 pub fn signing(&self) -> &[u8] {
142 &self.signing_key[..]
143 }
144
145 /// Returns the raw bytes of a key suitable for encrypting cookies.
146 ///
147 /// # Example
148 ///
149 /// ```rust
150 /// use requiem_http::cookie::Key;
151 ///
152 /// let key = Key::generate();
153 /// let encryption_key = key.encryption();
154 /// ```
155 pub fn encryption(&self) -> &[u8] {
156 &self.encryption_key[..]
157 }
158}
159
160#[cfg(test)]
161mod test {
162 use super::Key;
163
164 #[test]
165 fn deterministic_from_master() {
166 let master_key: Vec<u8> = (0..32).collect();
167
168 let key_a = Key::from_master(&master_key);
169 let key_b = Key::from_master(&master_key);
170
171 assert_eq!(key_a.signing(), key_b.signing());
172 assert_eq!(key_a.encryption(), key_b.encryption());
173 assert_ne!(key_a.encryption(), key_a.signing());
174
175 let master_key_2: Vec<u8> = (32..64).collect();
176 let key_2 = Key::from_master(&master_key_2);
177
178 assert_ne!(key_2.signing(), key_a.signing());
179 assert_ne!(key_2.encryption(), key_a.encryption());
180 }
181
182 #[test]
183 fn non_deterministic_generate() {
184 let key_a = Key::generate();
185 let key_b = Key::generate();
186
187 assert_ne!(key_a.signing(), key_b.signing());
188 assert_ne!(key_a.encryption(), key_b.encryption());
189 }
190}