rustls/crypto/aws_lc_rs/
ticketer.rs1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::fmt;
4use core::fmt::{Debug, Formatter};
5use core::sync::atomic::{AtomicUsize, Ordering};
6
7use aws_lc_rs::cipher::{
8 AES_256, AES_256_KEY_LEN, AES_CBC_IV_LEN, DecryptionContext, PaddedBlockDecryptingKey,
9 PaddedBlockEncryptingKey, UnboundCipherKey,
10};
11use aws_lc_rs::{hmac, iv};
12
13use super::ring_like::rand::{SecureRandom, SystemRandom};
14use super::unspecified_err;
15use crate::error::Error;
16#[cfg(debug_assertions)]
17use crate::log::debug;
18use crate::polyfill::try_split_at;
19use crate::rand::GetRandomFailed;
20use crate::server::ProducesTickets;
21use crate::sync::Arc;
22
23#[non_exhaustive]
25pub struct Ticketer {}
26
27impl Ticketer {
28 #[cfg(feature = "std")]
41 pub fn new() -> Result<Arc<dyn ProducesTickets>, Error> {
42 Ok(Arc::new(crate::ticketer::TicketRotator::new(
43 crate::ticketer::TicketRotator::SIX_HOURS,
44 make_ticket_generator,
45 )?))
46 }
47}
48
49fn make_ticket_generator() -> Result<Box<dyn ProducesTickets>, Error> {
50 Ok(Box::new(Rfc5077Ticketer::new()?))
51}
52
53struct Rfc5077Ticketer {
55 aes_encrypt_key: PaddedBlockEncryptingKey,
56 aes_decrypt_key: PaddedBlockDecryptingKey,
57 hmac_key: hmac::Key,
58 key_name: [u8; 16],
59 maximum_ciphertext_len: AtomicUsize,
60}
61
62impl Rfc5077Ticketer {
63 fn new() -> Result<Self, Error> {
64 let rand = SystemRandom::new();
65
66 let mut aes_key = [0u8; AES_256_KEY_LEN];
68 rand.fill(&mut aes_key)
69 .map_err(|_| GetRandomFailed)?;
70
71 let aes_encrypt_key =
76 UnboundCipherKey::new(&AES_256, &aes_key[..]).map_err(unspecified_err)?;
77 let aes_encrypt_key =
78 PaddedBlockEncryptingKey::cbc_pkcs7(aes_encrypt_key).map_err(unspecified_err)?;
79
80 let aes_decrypt_key =
82 UnboundCipherKey::new(&AES_256, &aes_key[..]).map_err(unspecified_err)?;
83 let aes_decrypt_key =
84 PaddedBlockDecryptingKey::cbc_pkcs7(aes_decrypt_key).map_err(unspecified_err)?;
85
86 let hmac_key = hmac::Key::generate(hmac::HMAC_SHA256, &rand).map_err(unspecified_err)?;
88
89 let mut key_name = [0u8; 16];
91 rand.fill(&mut key_name)
92 .map_err(|_| GetRandomFailed)?;
93
94 Ok(Self {
95 aes_encrypt_key,
96 aes_decrypt_key,
97 hmac_key,
98 key_name,
99 maximum_ciphertext_len: AtomicUsize::new(0),
100 })
101 }
102}
103
104impl ProducesTickets for Rfc5077Ticketer {
105 fn enabled(&self) -> bool {
106 true
107 }
108
109 fn lifetime(&self) -> u32 {
110 0
113 }
114
115 fn encrypt(&self, message: &[u8]) -> Option<Vec<u8>> {
117 let mut encrypted_state = Vec::from(message);
120 let dec_ctx = self
121 .aes_encrypt_key
122 .encrypt(&mut encrypted_state)
123 .ok()?;
124 let iv: &[u8] = (&dec_ctx).try_into().ok()?;
125
126 let mut hmac_data =
133 Vec::with_capacity(self.key_name.len() + iv.len() + 2 + encrypted_state.len());
134 hmac_data.extend(&self.key_name);
135 hmac_data.extend(iv);
136 hmac_data.extend(
137 u16::try_from(encrypted_state.len())
138 .ok()?
139 .to_be_bytes(),
140 );
141 hmac_data.extend(&encrypted_state);
142 let tag = hmac::sign(&self.hmac_key, &hmac_data);
143 let tag = tag.as_ref();
144
145 let mut ciphertext =
152 Vec::with_capacity(self.key_name.len() + iv.len() + encrypted_state.len() + tag.len());
153 ciphertext.extend(self.key_name);
154 ciphertext.extend(iv);
155 ciphertext.extend(encrypted_state);
156 ciphertext.extend(tag);
157
158 self.maximum_ciphertext_len
159 .fetch_max(ciphertext.len(), Ordering::SeqCst);
160
161 Some(ciphertext)
162 }
163
164 fn decrypt(&self, ciphertext: &[u8]) -> Option<Vec<u8>> {
165 if ciphertext.len()
166 > self
167 .maximum_ciphertext_len
168 .load(Ordering::SeqCst)
169 {
170 #[cfg(debug_assertions)]
171 debug!("rejected over-length ticket");
172 return None;
173 }
174
175 let (alleged_key_name, ciphertext) = try_split_at(ciphertext, self.key_name.len())?;
177
178 let (iv, ciphertext) = try_split_at(ciphertext, AES_CBC_IV_LEN)?;
180
181 let tag_len = self
183 .hmac_key
184 .algorithm()
185 .digest_algorithm()
186 .output_len();
187 let (enc_state, mac) = try_split_at(ciphertext, ciphertext.len() - tag_len)?;
188
189 let mut hmac_data =
191 Vec::with_capacity(alleged_key_name.len() + iv.len() + 2 + enc_state.len());
192 hmac_data.extend(alleged_key_name);
193 hmac_data.extend(iv);
194 hmac_data.extend(
195 u16::try_from(enc_state.len())
196 .ok()?
197 .to_be_bytes(),
198 );
199 hmac_data.extend(enc_state);
200 hmac::verify(&self.hmac_key, &hmac_data, mac).ok()?;
201
202 let iv = iv::FixedLength::try_from(iv).ok()?;
204 let dec_context = DecryptionContext::Iv128(iv);
205
206 let mut out = Vec::from(enc_state);
208 let plaintext = self
209 .aes_decrypt_key
210 .decrypt(&mut out, dec_context)
211 .ok()?;
212
213 Some(plaintext.into())
214 }
215}
216
217impl Debug for Rfc5077Ticketer {
218 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
219 f.debug_struct("Rfc5077Ticketer")
221 .finish_non_exhaustive()
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use core::time::Duration;
228
229 use pki_types::UnixTime;
230
231 use super::*;
232
233 #[test]
234 fn basic_pairwise_test() {
235 let t = Ticketer::new().unwrap();
236 assert!(t.enabled());
237 let cipher = t.encrypt(b"hello world").unwrap();
238 let plain = t.decrypt(&cipher).unwrap();
239 assert_eq!(plain, b"hello world");
240 }
241
242 #[test]
243 fn refuses_decrypt_before_encrypt() {
244 let t = Ticketer::new().unwrap();
245 assert_eq!(t.decrypt(b"hello"), None);
246 }
247
248 #[test]
249 fn refuses_decrypt_larger_than_largest_encryption() {
250 let t = Ticketer::new().unwrap();
251 let mut cipher = t.encrypt(b"hello world").unwrap();
252 assert_eq!(t.decrypt(&cipher), Some(b"hello world".to_vec()));
253
254 cipher.push(0);
258 assert_eq!(t.decrypt(&cipher), None);
259 }
260
261 #[test]
262 fn ticketrotator_switching_test() {
263 let t = Arc::new(crate::ticketer::TicketRotator::new(1, make_ticket_generator).unwrap());
264 let now = UnixTime::now();
265 let cipher1 = t.encrypt(b"ticket 1").unwrap();
266 assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1");
267 {
268 t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs(
270 now.as_secs() + 10,
271 )));
272 }
273 let cipher2 = t.encrypt(b"ticket 2").unwrap();
274 assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1");
275 assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2");
276 {
277 t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs(
279 now.as_secs() + 20,
280 )));
281 }
282 let cipher3 = t.encrypt(b"ticket 3").unwrap();
283 assert!(t.decrypt(&cipher1).is_none());
284 assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2");
285 assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3");
286 }
287
288 #[test]
289 fn ticketrotator_remains_usable_over_temporary_ticketer_creation_failure() {
290 let mut t = crate::ticketer::TicketRotator::new(1, make_ticket_generator).unwrap();
291 let now = UnixTime::now();
292 let cipher1 = t.encrypt(b"ticket 1").unwrap();
293 assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1");
294 t.generator = fail_generator;
295 {
296 t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs(
299 now.as_secs() + 10,
300 )));
301 }
302
303 let cipher2 = t.encrypt(b"ticket 2").unwrap();
305 assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1");
306 assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2");
307
308 t.generator = make_ticket_generator;
310 {
311 t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs(
312 now.as_secs() + 20,
313 )));
314 }
315 let cipher3 = t.encrypt(b"ticket 3").unwrap();
316 assert!(t.decrypt(&cipher1).is_some());
317 assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2");
318 assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3");
319 }
320
321 #[test]
322 fn rfc5077ticketer_is_debug_and_producestickets() {
323 use alloc::format;
324
325 use super::*;
326
327 let t = make_ticket_generator().unwrap();
328
329 assert_eq!(format!("{t:?}"), "Rfc5077Ticketer { .. }");
330 assert!(t.enabled());
331 assert_eq!(t.lifetime(), 0);
332 }
333
334 fn fail_generator() -> Result<Box<dyn ProducesTickets>, Error> {
335 Err(Error::FailedToGetRandomBytes)
336 }
337}