1use alloc::boxed::Box;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::{array, fmt};
5
6use pki_types::FipsStatus;
7use zeroize::Zeroize;
8
9use crate::crypto::tls13::{HkdfExpander, HkdfLabel, OutputLengthError};
10use crate::enums::{ContentType, ProtocolVersion};
11use crate::error::{ApiMisuse, Error};
12use crate::msgs::{put_u16, put_u64};
13use crate::suites::ConnectionTrafficSecrets;
14
15mod messages;
16pub use messages::{
17 EncodableVersion, InboundOpaque, OutboundPlain, Payload, Record, RecordError,
18 encode_record_header,
19};
20
21pub trait Tls13AeadAlgorithm: Send + Sync {
23 fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn RecordEncrypter>;
25
26 fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn RecordDecrypter>;
28
29 fn key_len(&self) -> usize;
31
32 fn iv_len(&self) -> usize {
34 NONCE_LEN
35 }
36
37 fn extract_keys(
42 &self,
43 key: AeadKey,
44 iv: Iv,
45 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
46
47 fn fips(&self) -> FipsStatus {
49 FipsStatus::Unvalidated
50 }
51}
52
53pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
55 fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn RecordEncrypter>;
64
65 fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn RecordDecrypter>;
71
72 fn key_block_shape(&self) -> KeyBlockShape;
75
76 fn extract_keys(
87 &self,
88 key: AeadKey,
89 iv: &[u8],
90 explicit: &[u8],
91 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
92
93 fn fips(&self) -> FipsStatus {
95 FipsStatus::Unvalidated
96 }
97}
98
99#[expect(clippy::exhaustive_structs)]
101#[derive(Debug, Eq, PartialEq, Clone, Copy)]
102pub struct UnsupportedOperationError;
103
104impl From<UnsupportedOperationError> for Error {
105 fn from(value: UnsupportedOperationError) -> Self {
106 Self::General(value.to_string())
107 }
108}
109
110impl fmt::Display for UnsupportedOperationError {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(f, "operation not supported")
113 }
114}
115
116impl core::error::Error for UnsupportedOperationError {}
117
118#[expect(clippy::exhaustive_structs)]
122pub struct KeyBlockShape {
123 pub enc_key_len: usize,
129
130 pub fixed_iv_len: usize,
139
140 pub explicit_nonce_len: usize,
145}
146
147pub trait RecordDecrypter: Send + Sync {
149 fn decrypt<'a>(
152 &mut self,
153 record: Record<InboundOpaque<'a>>,
154 seq: u64,
155 ) -> Result<Record<&'a [u8]>, Error>;
156}
157
158pub trait RecordEncrypter: Send + Sync {
160 fn encrypt(
168 &mut self,
169 record: Record<OutboundPlain<'_>>,
170 seq: u64,
171 out: &mut Vec<u8>,
172 ) -> Result<(), Error>;
173
174 fn encrypted_payload_len(&self, payload_len: usize) -> usize;
182}
183
184#[derive(Default, Clone)]
186pub struct Iv {
187 buf: [u8; Self::MAX_LEN],
188 used: usize,
189}
190
191impl Iv {
192 pub fn new(value: &[u8]) -> Result<Self, Error> {
196 if value.len() > Self::MAX_LEN {
197 return Err(ApiMisuse::IvLengthExceedsMaximum {
198 actual: value.len(),
199 maximum: Self::MAX_LEN,
200 }
201 .into());
202 }
203 let mut buf = [0u8; Self::MAX_LEN];
204 buf[..value.len()].copy_from_slice(value);
205 Ok(Self {
206 buf,
207 used: value.len(),
208 })
209 }
210
211 #[expect(clippy::len_without_is_empty)]
213 pub fn len(&self) -> usize {
214 self.used
215 }
216
217 pub const MAX_LEN: usize = 16;
219}
220
221impl From<[u8; NONCE_LEN]> for Iv {
222 fn from(bytes: [u8; NONCE_LEN]) -> Self {
223 Self::new(&bytes).expect("NONCE_LEN is within MAX_LEN")
224 }
225}
226
227impl AsRef<[u8]> for Iv {
228 fn as_ref(&self) -> &[u8] {
229 &self.buf[..self.used]
230 }
231}
232
233pub struct Nonce {
235 buf: [u8; Iv::MAX_LEN],
236 len: usize,
237}
238
239impl Nonce {
240 #[inline]
244 pub fn new(iv: &Iv, seq: u64) -> Self {
245 Self::new_inner(None, iv, seq)
246 }
247
248 pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
253 Self::new_inner(path_id, iv, pn)
254 }
255
256 #[inline]
258 fn new_inner(path_id: Option<u32>, iv: &Iv, seq: u64) -> Self {
259 let iv_len = iv.len();
260 let mut buf = [0u8; Iv::MAX_LEN];
261
262 if iv_len >= 8 {
263 put_u64(seq, &mut buf[iv_len - 8..iv_len]);
264 if let Some(path_id) = path_id {
265 if iv_len >= 12 {
266 buf[iv_len - 12..iv_len - 8].copy_from_slice(&path_id.to_be_bytes());
267 }
268 }
269 } else {
270 let seq_bytes = seq.to_be_bytes();
271 buf[..iv_len].copy_from_slice(&seq_bytes[8 - iv_len..]);
272 }
273
274 buf[..iv_len]
275 .iter_mut()
276 .zip(iv.as_ref())
277 .for_each(|(s, iv)| *s ^= *iv);
278
279 Self { buf, len: iv_len }
280 }
281
282 pub fn to_array<const N: usize>(&self) -> Result<[u8; N], Error> {
289 if self.len != N {
290 return Err(ApiMisuse::NonceArraySizeMismatch {
291 expected: N,
292 actual: self.len,
293 }
294 .into());
295 }
296 Ok(self.buf[..N]
297 .try_into()
298 .expect("nonce buffer conversion failed"))
299 }
300
301 pub fn as_bytes(&self) -> &[u8] {
303 &self.buf[..self.len]
304 }
305
306 #[expect(clippy::len_without_is_empty)]
308 pub fn len(&self) -> usize {
309 self.len
310 }
311}
312
313impl AsRef<[u8]> for Nonce {
314 fn as_ref(&self) -> &[u8] {
315 &self.buf[..self.len]
316 }
317}
318
319pub const NONCE_LEN: usize = 12;
322
323#[inline]
330pub fn make_tls13_aad(typ: ContentType, version: ProtocolVersion, payload_len: usize) -> [u8; 5] {
331 let version = version.to_array();
332 [
333 typ.into(),
334 version[0],
335 version[1],
336 (payload_len >> 8) as u8,
337 (payload_len & 0xff) as u8,
338 ]
339}
340
341#[inline]
345pub fn make_tls12_aad(
346 seq: u64,
347 typ: ContentType,
348 vers: ProtocolVersion,
349 len: usize,
350) -> [u8; TLS12_AAD_SIZE] {
351 let mut out = [0; TLS12_AAD_SIZE];
352 put_u64(seq, &mut out[0..]);
353 out[8] = typ.into();
354 put_u16(vers.into(), &mut out[9..]);
355 put_u16(len as u16, &mut out[11..]);
356 out
357}
358
359const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
360
361pub struct AeadKey {
365 buf: [u8; Self::MAX_LEN],
366 used: usize,
367}
368
369impl AeadKey {
370 pub(crate) fn new(buf: &[u8]) -> Self {
371 debug_assert!(buf.len() <= Self::MAX_LEN);
372 let mut key = Self::from([0u8; Self::MAX_LEN]);
373 key.buf[..buf.len()].copy_from_slice(buf);
374 key.used = buf.len();
375 key
376 }
377
378 pub(crate) fn expand(
380 expander: &dyn HkdfExpander,
381 label: &HkdfLabel<'_>,
382 len: usize,
383 ) -> Result<Self, OutputLengthError> {
384 debug_assert!(len <= Self::MAX_LEN);
385 let mut key = Self {
386 buf: [0u8; Self::MAX_LEN],
387 used: len,
388 };
389
390 expander.expand_slice(&label.info(), &mut key.buf[..len])?;
391 Ok(key)
392 }
393
394 pub(crate) const MAX_LEN: usize = 32;
396}
397
398impl Drop for AeadKey {
399 #[inline(never)]
400 fn drop(&mut self) {
401 self.buf[..self.used].zeroize();
402 }
403}
404
405impl AsRef<[u8]> for AeadKey {
406 fn as_ref(&self) -> &[u8] {
407 &self.buf[..self.used]
408 }
409}
410
411impl From<[u8; Self::MAX_LEN]> for AeadKey {
412 fn from(bytes: [u8; Self::MAX_LEN]) -> Self {
413 Self {
414 buf: bytes,
415 used: Self::MAX_LEN,
416 }
417 }
418}
419
420impl From<[u8; 16]> for AeadKey {
421 fn from(buf: [u8; 16]) -> Self {
422 Self {
423 buf: array::from_fn(|i| if i < 16 { buf[i] } else { 0 }),
424 used: 16,
425 }
426 }
427}
428
429#[cfg(test)]
430pub(crate) struct FakeAead;
431
432#[cfg(test)]
433impl Tls12AeadAlgorithm for FakeAead {
434 fn encrypter(&self, _: AeadKey, _: &[u8], _: &[u8]) -> Box<dyn RecordEncrypter> {
435 todo!()
436 }
437
438 fn decrypter(&self, _: AeadKey, _: &[u8]) -> Box<dyn RecordDecrypter> {
439 todo!()
440 }
441
442 fn key_block_shape(&self) -> KeyBlockShape {
443 todo!()
444 }
445
446 fn extract_keys(
447 &self,
448 _: AeadKey,
449 _: &[u8],
450 _: &[u8],
451 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
452 Err(UnsupportedOperationError)
453 }
454
455 fn fips(&self) -> FipsStatus {
456 FipsStatus::Unvalidated
457 }
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 #[test]
467 fn multipath_nonce() {
468 const PATH_ID: u32 = 3;
469 const PN: u64 = 54321;
470 const IV: [u8; 16] = 0x6b26114b9cba2b63a9e8dd4fu128.to_be_bytes();
471 const EXPECTED_NONCE: [u8; 16] = 0x6b2611489cba2b63a9e8097eu128.to_be_bytes();
472 let nonce = Nonce::quic(Some(PATH_ID), &Iv::new(&IV[4..]).unwrap(), PN);
473 assert_eq!(&EXPECTED_NONCE[4..], nonce.as_bytes());
474 }
475
476 #[test]
477 fn iv_len() {
478 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
479 assert_eq!(iv.len(), NONCE_LEN);
480
481 let short_iv = Iv::new(&[1u8, 2, 3]).unwrap();
482 assert_eq!(short_iv.len(), 3);
483
484 let empty_iv = Iv::new(&[]).unwrap();
485 assert_eq!(empty_iv.len(), 0);
486 }
487
488 #[test]
489 fn iv_as_ref() {
490 let iv_data = [1u8, 2, 3, 4, 5];
491 let iv = Iv::new(&iv_data).unwrap();
492 let iv_ref: &[u8] = iv.as_ref();
493 assert_eq!(iv_ref, &iv_data);
494 }
495
496 #[test]
497 fn nonce_with_short_iv() {
498 let short_iv = Iv::new(&[0xAA, 0xBB, 0xCC, 0xDD]).unwrap();
499 let seq = 0x1122334455667788u64;
500 let nonce = Nonce::new(&short_iv, seq);
501
502 assert_eq!(nonce.len(), 4);
504 let seq_bytes = seq.to_be_bytes();
505 let expected = [
506 0xAA ^ seq_bytes[4],
507 0xBB ^ seq_bytes[5],
508 0xCC ^ seq_bytes[6],
509 0xDD ^ seq_bytes[7],
510 ];
511 assert_eq!(nonce.as_bytes(), &expected);
512 }
513
514 #[test]
515 fn nonce_len() {
516 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
517 let nonce = Nonce::new(&iv, 42);
518 assert_eq!(nonce.len(), NONCE_LEN);
519
520 let short_iv = Iv::new(&[1u8, 2]).unwrap();
521 let short_nonce = Nonce::new(&short_iv, 42);
522 assert_eq!(short_nonce.len(), 2);
523 }
524
525 #[test]
526 fn nonce_as_ref() {
527 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
528 let nonce = Nonce::new(&iv, 42);
529 let nonce_ref: &[u8] = nonce.as_ref();
530 assert_eq!(nonce_ref.len(), NONCE_LEN);
531 }
532
533 #[test]
534 fn nonce_to_array_correct_size() {
535 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
536 let nonce = Nonce::new(&iv, 42);
537 let array: [u8; NONCE_LEN] = nonce.to_array().unwrap();
538 assert_eq!(array.len(), NONCE_LEN);
539 }
540
541 #[test]
542 fn nonce_to_array_wrong_size() {
543 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
544 let nonce = Nonce::new(&iv, 42);
545 let result: Result<[u8; 16], Error> = nonce.to_array();
546 assert!(matches!(
547 result,
548 Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch {
549 expected: 16,
550 actual: NONCE_LEN
551 }))
552 ));
553 }
554
555 #[test]
556 fn nonce_to_array_variable_length_error() {
557 let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
559 let nonce = Nonce::new(&short_iv, 42);
560
561 let result: Result<[u8; NONCE_LEN], Error> = nonce.to_array();
563 if let Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch { expected, actual })) =
564 result
565 {
566 assert_eq!(expected, NONCE_LEN);
567 assert_eq!(actual, 8);
568 } else {
569 panic!("Expected Error::ApiMisuse(NonceArraySizeMismatch)");
570 }
571
572 let result_correct: Result<[u8; 8], Error> = nonce.to_array();
574 assert!(result_correct.is_ok());
575 }
576
577 #[test]
578 fn nonce_xor_with_iv() {
579 let iv_data = [0xFFu8; NONCE_LEN];
580 let iv = Iv::new(&iv_data).unwrap();
581 let seq = 0x0000000000000001u64;
582 let nonce = Nonce::new(&iv, seq);
583
584 let nonce_bytes = nonce.as_bytes();
586 assert_eq!(nonce_bytes[NONCE_LEN - 1], 0xFE);
587 }
588
589 #[test]
590 fn iv_length_exceeds_maximum() {
591 let too_long_iv = [0xAAu8; Iv::MAX_LEN + 1];
592 let result = Iv::new(&too_long_iv);
593
594 assert!(matches!(
595 result,
596 Err(Error::ApiMisuse(ApiMisuse::IvLengthExceedsMaximum {
597 actual: 17,
598 maximum: 16
599 }))
600 ));
601 }
602
603 #[test]
604 fn aead_key_16_bytes() {
605 let bytes = [0xABu8; 16];
606 let key = AeadKey::from(bytes);
607 assert_eq!(key.as_ref(), &bytes);
608 }
609}