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