tower_image_xform/
key.rs

1// Copied and adapted from: https://github.com/rwf2/cookie-rs/blob/ba46fc5e97a1271435f38509d109e125a473bc82/src/secure/key.rs
2use std::convert::TryFrom;
3
4const KEY_LENGTH: usize = 64;
5
6/// Cryptographically-secure key used for signing URLs.
7#[derive(Clone)]
8pub struct Key([u8; KEY_LENGTH]);
9
10impl PartialEq for Key {
11    fn eq(&self, other: &Self) -> bool {
12        use subtle::ConstantTimeEq;
13
14        self.0.ct_eq(&other.0).into()
15    }
16}
17
18impl std::fmt::Debug for Key {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.debug_struct("Key").finish()
21    }
22}
23
24impl Key {
25    // An empty key structure, to be filled.
26    const fn zero() -> Self {
27        Key([0; KEY_LENGTH])
28    }
29
30    /// Creates a new `Key` from a 512-bit cryptographically random string.
31    ///
32    /// The supplied key must be at least 512-bits (64 bytes). For security, the
33    /// master key _must_ be cryptographically random.
34    ///
35    /// # Panics
36    ///
37    /// Panics if `key` is less than 64 bytes in length.
38    ///
39    /// For a non-panicking version, use [`Key::try_from()`] or generate a key
40    /// with [`Key::generate()`] or [`Key::try_generate()`].
41    ///
42    /// # Example
43    ///
44    /// ```rust
45    /// use tower_image_xform::Key;
46    ///
47    /// # /*
48    /// let key = { /* a cryptographically random key >= 64 bytes */ };
49    /// # */
50    /// # let key: &Vec<u8> = &(0..64).collect();
51    ///
52    /// let key = Key::from(key);
53    /// ```
54    #[inline]
55    pub fn from(key: &[u8]) -> Key {
56        Key::try_from(key).unwrap()
57    }
58
59    /// Generates signing/encryption keys from a secure, random source. Keys are
60    /// generated nondeterministically.
61    ///
62    /// # Panics
63    ///
64    /// Panics if randomness cannot be retrieved from the operating system. See
65    /// [`Key::try_generate()`] for a non-panicking version.
66    ///
67    /// # Example
68    ///
69    /// ```rust
70    /// use tower_image_xform::Key;
71    ///
72    /// let key = Key::generate();
73    /// ```
74    pub fn generate() -> Key {
75        Self::try_generate().expect("failed to generate `Key` from randomness")
76    }
77
78    /// Attempts to generate signing/encryption keys from a secure, random
79    /// source. Keys are generated nondeterministically. If randomness cannot be
80    /// retrieved from the underlying operating system, returns `None`.
81    ///
82    /// # Example
83    ///
84    /// ```rust
85    /// use tower_image_xform::Key;
86    ///
87    /// let key = Key::try_generate();
88    /// ```
89    pub fn try_generate() -> Option<Key> {
90        use rand::RngCore;
91
92        let mut rng = rand::thread_rng();
93        let mut key = Key::zero();
94        rng.try_fill_bytes(&mut key.0).ok()?;
95        Some(key)
96    }
97
98    /// Returns the key as a slice.
99    pub fn as_slice(&self) -> &[u8] {
100        &self.0
101    }
102}
103
104#[derive(Debug)]
105#[non_exhaustive]
106pub enum KeyError {
107    /// Too few bytes (`.0`) were provided to generate a key.
108    ///
109    /// See [`Key::from()`] for minimum requirements.
110    TooShort(usize),
111}
112
113impl std::error::Error for KeyError {}
114
115impl std::fmt::Display for KeyError {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        match self {
118            KeyError::TooShort(n) => {
119                write!(
120                    f,
121                    "key material is too short: expected >= {} bytes, got {} bytes",
122                    KEY_LENGTH, n
123                )
124            }
125        }
126    }
127}
128
129impl TryFrom<&[u8]> for Key {
130    type Error = KeyError;
131
132    /// A fallible version of [`Key::from()`].
133    ///
134    /// Succeeds when [`Key::from()`] succeds and returns an error where
135    /// [`Key::from()`] panics, namely, if `key` is too short.
136    ///
137    /// # Example
138    ///
139    /// ```rust
140    /// # use std::convert::TryFrom;
141    /// use tower_image_xform::Key;
142    ///
143    /// # /*
144    /// let key = { /* a cryptographically random key >= 64 bytes */ };
145    /// # */
146    /// # let key: &Vec<u8> = &(0..64).collect();
147    /// # let key: &[u8] = &key[..];
148    /// assert!(Key::try_from(key).is_ok());
149    ///
150    /// // A key that's far too short to use.
151    /// let key = &[1, 2, 3, 4][..];
152    /// assert!(Key::try_from(key).is_err());
153    /// ```
154    fn try_from(key: &[u8]) -> Result<Self, Self::Error> {
155        if key.len() < KEY_LENGTH {
156            Err(KeyError::TooShort(key.len()))
157        } else {
158            let mut output = Key::zero();
159            output.0.copy_from_slice(&key[..KEY_LENGTH]);
160            Ok(output)
161        }
162    }
163}