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