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}