Skip to main content

cookie/secure/
key.rs

1use std::convert::TryFrom;
2
3const SIGNING_KEY_LEN: usize = 32;
4const ENCRYPTION_KEY_LEN: usize = 32;
5const COMBINED_KEY_LENGTH: usize = SIGNING_KEY_LEN + ENCRYPTION_KEY_LEN;
6
7// Statically ensure the numbers above are in-sync.
8#[cfg(feature = "signed")]
9const_assert!(crate::secure::signed::KEY_LEN == SIGNING_KEY_LEN);
10#[cfg(feature = "private")]
11const_assert!(crate::secure::private::KEY_LEN == ENCRYPTION_KEY_LEN);
12
13/// A cryptographic master key for use with `Signed` and/or `Private` jars.
14///
15/// This structure encapsulates secure, cryptographic keys for use with both
16/// [`PrivateJar`](crate::PrivateJar) and [`SignedJar`](crate::SignedJar). A
17/// single instance of a `Key` can be used for both a `PrivateJar` and a
18/// `SignedJar` simultaneously with no notable security implications.
19#[cfg_attr(all(nightly, doc), doc(cfg(any(feature = "private", feature = "signed"))))]
20#[derive(Clone)]
21pub struct Key([u8; COMBINED_KEY_LENGTH /* SIGNING | ENCRYPTION */]);
22
23impl PartialEq for Key {
24    fn eq(&self, other: &Self) -> bool {
25        use subtle::ConstantTimeEq;
26
27        self.0.ct_eq(&other.0).into()
28    }
29}
30
31impl std::fmt::Debug for Key {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        f.debug_struct("Key").finish()
34    }
35}
36
37impl Key {
38    // An empty key structure, to be filled.
39    const fn zero() -> Self {
40        Key([0; COMBINED_KEY_LENGTH])
41    }
42
43    /// Creates a new `Key` from a 512-bit cryptographically random string.
44    ///
45    /// The supplied key must be at least 512-bits (64 bytes). For security, the
46    /// master key _must_ be cryptographically random.
47    ///
48    /// # Panics
49    ///
50    /// Panics if `key` is less than 64 bytes in length.
51    ///
52    /// For a non-panicking version, use [`Key::try_from()`] or generate a key with
53    /// [`Key::generate()`] or [`Key::try_generate()`].
54    ///
55    /// # Example
56    ///
57    /// ```rust
58    /// use cookie::Key;
59    ///
60    /// # /*
61    /// let key = { /* a cryptographically random key >= 64 bytes */ };
62    /// # */
63    /// # let key: &Vec<u8> = &(0..64).collect();
64    ///
65    /// let key = Key::from(key);
66    /// ```
67    #[inline]
68    pub fn from(key: &[u8]) -> Key {
69        Key::try_from(key).unwrap()
70    }
71
72    /// Derives new signing/encryption keys from a master key.
73    ///
74    /// The master key must be at least 256-bits (32 bytes). For security, the
75    /// master key _must_ be cryptographically random. The keys are derived
76    /// deterministically from the master key.
77    ///
78    /// # Panics
79    ///
80    /// Panics if `key` is less than 32 bytes in length.
81    ///
82    /// # Example
83    ///
84    /// ```rust
85    /// use cookie::Key;
86    ///
87    /// # /*
88    /// let master_key = { /* a cryptographically random key >= 32 bytes */ };
89    /// # */
90    /// # let master_key: &Vec<u8> = &(0..32).collect();
91    ///
92    /// let key = Key::derive_from(master_key);
93    /// ```
94    #[cfg(feature = "key-expansion")]
95    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "key-expansion")))]
96    pub fn derive_from(master_key: &[u8]) -> Self {
97        if master_key.len() < 32 {
98            panic!("bad master key length: expected >= 32 bytes, found {}", master_key.len());
99        }
100
101        // Expand the master key into two HKDF generated keys.
102        const KEYS_INFO: &[u8] = b"COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM";
103        let mut both_keys = [0; COMBINED_KEY_LENGTH];
104        let hk = hkdf::Hkdf::<sha2::Sha256>::from_prk(master_key).expect("key length prechecked");
105        hk.expand(KEYS_INFO, &mut both_keys).expect("expand into keys");
106        Key::from(&both_keys)
107    }
108
109    /// Generates signing/encryption keys from a secure, random source. Keys are
110    /// generated nondeterministically.
111    ///
112    /// # Panics
113    ///
114    /// Panics if randomness cannot be retrieved from the operating system. See
115    /// [`Key::try_generate()`] for a non-panicking version.
116    ///
117    /// # Example
118    ///
119    /// ```rust
120    /// use cookie::Key;
121    ///
122    /// let key = Key::generate();
123    /// ```
124    pub fn generate() -> Key {
125        Self::try_generate().expect("failed to generate `Key` from randomness")
126    }
127
128    /// Attempts to generate signing/encryption keys from a secure, random
129    /// source. Keys are generated nondeterministically. If randomness cannot be
130    /// retrieved from the underlying operating system, returns `None`.
131    ///
132    /// # Example
133    ///
134    /// ```rust
135    /// use cookie::Key;
136    ///
137    /// let key = Key::try_generate();
138    /// ```
139    pub fn try_generate() -> Option<Key> {
140        use crate::secure::rand::RngCore;
141
142        let mut rng = crate::secure::rand::thread_rng();
143        let mut key = Key::zero();
144        rng.try_fill_bytes(&mut key.0).ok()?;
145        Some(key)
146    }
147
148    /// Returns the raw bytes of a key suitable for signing cookies. Guaranteed
149    /// to be at least 32 bytes.
150    ///
151    /// # Example
152    ///
153    /// ```rust
154    /// use cookie::Key;
155    ///
156    /// let key = Key::generate();
157    /// let signing_key = key.signing();
158    /// ```
159    pub fn signing(&self) -> &[u8] {
160        &self.0[..SIGNING_KEY_LEN]
161    }
162
163    /// Returns the raw bytes of a key suitable for encrypting cookies.
164    /// Guaranteed to be at least 32 bytes.
165    ///
166    /// # Example
167    ///
168    /// ```rust
169    /// use cookie::Key;
170    ///
171    /// let key = Key::generate();
172    /// let encryption_key = key.encryption();
173    /// ```
174    pub fn encryption(&self) -> &[u8] {
175        &self.0[SIGNING_KEY_LEN..]
176    }
177
178    /// Returns the raw bytes of the master key. Guaranteed to be at least 64
179    /// bytes.
180    ///
181    /// # Example
182    ///
183    /// ```rust
184    /// use cookie::Key;
185    ///
186    /// let key = Key::generate();
187    /// let master_key = key.master();
188    /// ```
189    pub fn master(&self) -> &[u8] {
190        &self.0
191    }
192}
193
194/// An error indicating an issue with generating or constructing a key.
195#[cfg_attr(all(nightly, doc), doc(cfg(any(feature = "private", feature = "signed"))))]
196#[derive(Debug)]
197#[non_exhaustive]
198pub enum KeyError {
199    /// Too few bytes (`.0`) were provided to generate a key.
200    ///
201    /// See [`Key::from()`] for minimum requirements.
202    TooShort(usize),
203}
204
205impl std::error::Error for KeyError { }
206
207impl std::fmt::Display for KeyError {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        match self {
210            KeyError::TooShort(n) => {
211                write!(f, "key material is too short: expected >= {} bytes, got {} bytes",
212                       COMBINED_KEY_LENGTH, n)
213            }
214        }
215    }
216}
217
218impl TryFrom<&[u8]> for Key {
219    type Error = KeyError;
220
221    /// A fallible version of [`Key::from()`].
222    ///
223    /// Succeeds when [`Key::from()`] succeds and returns an error where
224    /// [`Key::from()`] panics, namely, if `key` is too short.
225    ///
226    /// # Example
227    ///
228    /// ```rust
229    /// # use std::convert::TryFrom;
230    /// use cookie::Key;
231    ///
232    /// # /*
233    /// let key = { /* a cryptographically random key >= 64 bytes */ };
234    /// # */
235    /// # let key: &Vec<u8> = &(0..64).collect();
236    /// # let key: &[u8] = &key[..];
237    /// assert!(Key::try_from(key).is_ok());
238    ///
239    /// // A key that's far too short to use.
240    /// let key = &[1, 2, 3, 4][..];
241    /// assert!(Key::try_from(key).is_err());
242    /// ```
243    fn try_from(key: &[u8]) -> Result<Self, Self::Error> {
244        if key.len() < COMBINED_KEY_LENGTH {
245            Err(KeyError::TooShort(key.len()))
246        } else {
247            let mut output = Key::zero();
248            output.0.copy_from_slice(&key[..COMBINED_KEY_LENGTH]);
249            Ok(output)
250        }
251    }
252}
253
254#[cfg(test)]
255mod test {
256    use super::Key;
257
258    #[test]
259    fn from_works() {
260        let key = Key::from(&(0..64).collect::<Vec<_>>());
261
262        let signing: Vec<u8> = (0..32).collect();
263        assert_eq!(key.signing(), &*signing);
264
265        let encryption: Vec<u8> = (32..64).collect();
266        assert_eq!(key.encryption(), &*encryption);
267    }
268
269    #[test]
270    fn try_from_works() {
271        use core::convert::TryInto;
272        let data = (0..64).collect::<Vec<_>>();
273        let key_res: Result<Key, _> = data[0..63].try_into();
274        assert!(key_res.is_err());
275
276        let key_res: Result<Key, _> = data.as_slice().try_into();
277        assert!(key_res.is_ok());
278    }
279
280    #[test]
281    #[cfg(feature = "key-expansion")]
282    fn deterministic_derive() {
283        let master_key: Vec<u8> = (0..32).collect();
284
285        let key_a = Key::derive_from(&master_key);
286        let key_b = Key::derive_from(&master_key);
287
288        assert_eq!(key_a.signing(), key_b.signing());
289        assert_eq!(key_a.encryption(), key_b.encryption());
290        assert_ne!(key_a.encryption(), key_a.signing());
291
292        let master_key_2: Vec<u8> = (32..64).collect();
293        let key_2 = Key::derive_from(&master_key_2);
294
295        assert_ne!(key_2.signing(), key_a.signing());
296        assert_ne!(key_2.encryption(), key_a.encryption());
297    }
298
299    #[test]
300    fn non_deterministic_generate() {
301        let key_a = Key::generate();
302        let key_b = Key::generate();
303
304        assert_ne!(key_a.signing(), key_b.signing());
305        assert_ne!(key_a.encryption(), key_b.encryption());
306    }
307
308    #[test]
309    fn debug_does_not_leak_key() {
310        let key = Key::generate();
311
312        assert_eq!(format!("{:?}", key), "Key");
313    }
314}