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
19pub trait Tls13AeadAlgorithm: Send + Sync {
21 fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn RecordEncrypter>;
23
24 fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn RecordDecrypter>;
26
27 fn key_len(&self) -> usize;
29
30 fn iv_len(&self) -> usize {
32 NONCE_LEN
33 }
34
35 fn extract_keys(
40 &self,
41 key: AeadKey,
42 iv: Iv,
43 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
44
45 fn fips(&self) -> FipsStatus {
47 FipsStatus::Unvalidated
48 }
49}
50
51pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
53 fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn RecordEncrypter>;
62
63 fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn RecordDecrypter>;
69
70 fn key_block_shape(&self) -> KeyBlockShape;
73
74 fn extract_keys(
85 &self,
86 key: AeadKey,
87 iv: &[u8],
88 explicit: &[u8],
89 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
90
91 fn fips(&self) -> FipsStatus {
93 FipsStatus::Unvalidated
94 }
95}
96
97#[expect(clippy::exhaustive_structs)]
99#[derive(Debug, Eq, PartialEq, Clone, Copy)]
100pub struct UnsupportedOperationError;
101
102impl From<UnsupportedOperationError> for Error {
103 fn from(value: UnsupportedOperationError) -> Self {
104 Self::General(value.to_string())
105 }
106}
107
108impl fmt::Display for UnsupportedOperationError {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 write!(f, "operation not supported")
111 }
112}
113
114impl core::error::Error for UnsupportedOperationError {}
115
116#[expect(clippy::exhaustive_structs)]
120pub struct KeyBlockShape {
121 pub enc_key_len: usize,
127
128 pub fixed_iv_len: usize,
137
138 pub explicit_nonce_len: usize,
143}
144
145pub trait RecordDecrypter: Send + Sync {
147 fn decrypt<'a>(
150 &mut self,
151 record: Record<InboundOpaque<'a>>,
152 seq: u64,
153 ) -> Result<Record<&'a [u8]>, Error>;
154}
155
156pub trait RecordEncrypter: Send + Sync {
158 fn encrypt<'a>(
172 &mut self,
173 record: Record<OutboundPlain<'_>>,
174 seq: u64,
175 out: &'a mut [u8],
176 ) -> Result<Record<&'a [u8]>, Error>;
177
178 fn encrypted_payload_len(&self, payload_len: usize) -> usize;
186}
187
188#[derive(Default, Clone)]
190pub struct Iv {
191 buf: [u8; Self::MAX_LEN],
192 used: usize,
193}
194
195impl Iv {
196 pub fn new(value: &[u8]) -> Result<Self, Error> {
200 if value.len() > Self::MAX_LEN {
201 return Err(ApiMisuse::IvLengthExceedsMaximum {
202 actual: value.len(),
203 maximum: Self::MAX_LEN,
204 }
205 .into());
206 }
207 let mut buf = [0u8; Self::MAX_LEN];
208 buf[..value.len()].copy_from_slice(value);
209 Ok(Self {
210 buf,
211 used: value.len(),
212 })
213 }
214
215 #[expect(clippy::len_without_is_empty)]
217 pub fn len(&self) -> usize {
218 self.used
219 }
220
221 pub const MAX_LEN: usize = 16;
223}
224
225impl From<[u8; NONCE_LEN]> for Iv {
226 fn from(bytes: [u8; NONCE_LEN]) -> Self {
227 Self::new(&bytes).expect("NONCE_LEN is within MAX_LEN")
228 }
229}
230
231impl AsRef<[u8]> for Iv {
232 fn as_ref(&self) -> &[u8] {
233 &self.buf[..self.used]
234 }
235}
236
237pub struct Nonce {
239 buf: [u8; Iv::MAX_LEN],
240 len: usize,
241}
242
243impl Nonce {
244 #[inline]
248 pub fn new(iv: &Iv, seq: u64) -> Self {
249 Self::new_inner(None, iv, seq)
250 }
251
252 pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
257 Self::new_inner(path_id, iv, pn)
258 }
259
260 #[inline]
262 fn new_inner(path_id: Option<u32>, iv: &Iv, seq: u64) -> Self {
263 let iv_len = iv.len();
264 let mut buf = [0u8; Iv::MAX_LEN];
265
266 if iv_len >= 8 {
267 put_u64(seq, &mut buf[iv_len - 8..iv_len]);
268 if let Some(path_id) = path_id {
269 if iv_len >= 12 {
270 buf[iv_len - 12..iv_len - 8].copy_from_slice(&path_id.to_be_bytes());
271 }
272 }
273 } else {
274 let seq_bytes = seq.to_be_bytes();
275 buf[..iv_len].copy_from_slice(&seq_bytes[8 - iv_len..]);
276 }
277
278 buf[..iv_len]
279 .iter_mut()
280 .zip(iv.as_ref())
281 .for_each(|(s, iv)| *s ^= *iv);
282
283 Self { buf, len: iv_len }
284 }
285
286 pub fn to_array<const N: usize>(&self) -> Result<[u8; N], Error> {
293 if self.len != N {
294 return Err(ApiMisuse::NonceArraySizeMismatch {
295 expected: N,
296 actual: self.len,
297 }
298 .into());
299 }
300 Ok(self.buf[..N]
301 .try_into()
302 .expect("nonce buffer conversion failed"))
303 }
304
305 pub fn as_bytes(&self) -> &[u8] {
307 &self.buf[..self.len]
308 }
309
310 #[expect(clippy::len_without_is_empty)]
312 pub fn len(&self) -> usize {
313 self.len
314 }
315}
316
317impl AsRef<[u8]> for Nonce {
318 fn as_ref(&self) -> &[u8] {
319 &self.buf[..self.len]
320 }
321}
322
323pub const NONCE_LEN: usize = 12;
326
327#[inline]
334pub fn make_tls13_aad(typ: ContentType, version: ProtocolVersion, payload_len: usize) -> [u8; 5] {
335 let version = version.to_array();
336 [
337 typ.into(),
338 version[0],
339 version[1],
340 (payload_len >> 8) as u8,
341 (payload_len & 0xff) as u8,
342 ]
343}
344
345#[inline]
349pub fn make_tls12_aad(
350 seq: u64,
351 typ: ContentType,
352 vers: ProtocolVersion,
353 len: usize,
354) -> [u8; TLS12_AAD_SIZE] {
355 let mut out = [0; TLS12_AAD_SIZE];
356 put_u64(seq, &mut out[0..]);
357 out[8] = typ.into();
358 put_u16(vers.into(), &mut out[9..]);
359 put_u16(len as u16, &mut out[11..]);
360 out
361}
362
363const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
364
365pub struct AeadKey {
369 buf: [u8; Self::MAX_LEN],
370 used: usize,
371}
372
373impl AeadKey {
374 pub(crate) fn new(buf: &[u8]) -> Self {
375 debug_assert!(buf.len() <= Self::MAX_LEN);
376 let mut key = Self::from([0u8; Self::MAX_LEN]);
377 key.buf[..buf.len()].copy_from_slice(buf);
378 key.used = buf.len();
379 key
380 }
381
382 pub(crate) fn with_length(self, len: usize) -> Self {
383 let Self { mut buf, used } = self;
384 assert!(len <= used);
385 buf[len..used].zeroize();
386 Self { buf, used: len }
387 }
388
389 pub(crate) const MAX_LEN: usize = 32;
391}
392
393impl Drop for AeadKey {
394 #[inline(never)]
395 fn drop(&mut self) {
396 self.buf[..self.used].zeroize();
397 }
398}
399
400impl AsRef<[u8]> for AeadKey {
401 fn as_ref(&self) -> &[u8] {
402 &self.buf[..self.used]
403 }
404}
405
406impl From<[u8; Self::MAX_LEN]> for AeadKey {
407 fn from(bytes: [u8; Self::MAX_LEN]) -> Self {
408 Self {
409 buf: bytes,
410 used: Self::MAX_LEN,
411 }
412 }
413}
414
415impl From<[u8; 16]> for AeadKey {
416 fn from(buf: [u8; 16]) -> Self {
417 Self {
418 buf: array::from_fn(|i| if i < 16 { buf[i] } else { 0 }),
419 used: 16,
420 }
421 }
422}
423
424#[cfg(test)]
425pub(crate) struct FakeAead;
426
427#[cfg(test)]
428impl Tls12AeadAlgorithm for FakeAead {
429 fn encrypter(&self, _: AeadKey, _: &[u8], _: &[u8]) -> Box<dyn RecordEncrypter> {
430 todo!()
431 }
432
433 fn decrypter(&self, _: AeadKey, _: &[u8]) -> Box<dyn RecordDecrypter> {
434 todo!()
435 }
436
437 fn key_block_shape(&self) -> KeyBlockShape {
438 todo!()
439 }
440
441 fn extract_keys(
442 &self,
443 _: AeadKey,
444 _: &[u8],
445 _: &[u8],
446 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
447 Err(UnsupportedOperationError)
448 }
449
450 fn fips(&self) -> FipsStatus {
451 FipsStatus::Unvalidated
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458
459 #[test]
462 fn multipath_nonce() {
463 const PATH_ID: u32 = 3;
464 const PN: u64 = 54321;
465 const IV: [u8; 16] = 0x6b26114b9cba2b63a9e8dd4fu128.to_be_bytes();
466 const EXPECTED_NONCE: [u8; 16] = 0x6b2611489cba2b63a9e8097eu128.to_be_bytes();
467 let nonce = Nonce::quic(Some(PATH_ID), &Iv::new(&IV[4..]).unwrap(), PN);
468 assert_eq!(&EXPECTED_NONCE[4..], nonce.as_bytes());
469 }
470
471 #[test]
472 fn iv_len() {
473 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
474 assert_eq!(iv.len(), NONCE_LEN);
475
476 let short_iv = Iv::new(&[1u8, 2, 3]).unwrap();
477 assert_eq!(short_iv.len(), 3);
478
479 let empty_iv = Iv::new(&[]).unwrap();
480 assert_eq!(empty_iv.len(), 0);
481 }
482
483 #[test]
484 fn iv_as_ref() {
485 let iv_data = [1u8, 2, 3, 4, 5];
486 let iv = Iv::new(&iv_data).unwrap();
487 let iv_ref: &[u8] = iv.as_ref();
488 assert_eq!(iv_ref, &iv_data);
489 }
490
491 #[test]
492 fn nonce_with_short_iv() {
493 let short_iv = Iv::new(&[0xAA, 0xBB, 0xCC, 0xDD]).unwrap();
494 let seq = 0x1122334455667788u64;
495 let nonce = Nonce::new(&short_iv, seq);
496
497 assert_eq!(nonce.len(), 4);
499 let seq_bytes = seq.to_be_bytes();
500 let expected = [
501 0xAA ^ seq_bytes[4],
502 0xBB ^ seq_bytes[5],
503 0xCC ^ seq_bytes[6],
504 0xDD ^ seq_bytes[7],
505 ];
506 assert_eq!(nonce.as_bytes(), &expected);
507 }
508
509 #[test]
510 fn nonce_len() {
511 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
512 let nonce = Nonce::new(&iv, 42);
513 assert_eq!(nonce.len(), NONCE_LEN);
514
515 let short_iv = Iv::new(&[1u8, 2]).unwrap();
516 let short_nonce = Nonce::new(&short_iv, 42);
517 assert_eq!(short_nonce.len(), 2);
518 }
519
520 #[test]
521 fn nonce_as_ref() {
522 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
523 let nonce = Nonce::new(&iv, 42);
524 let nonce_ref: &[u8] = nonce.as_ref();
525 assert_eq!(nonce_ref.len(), NONCE_LEN);
526 }
527
528 #[test]
529 fn nonce_to_array_correct_size() {
530 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
531 let nonce = Nonce::new(&iv, 42);
532 let array: [u8; NONCE_LEN] = nonce.to_array().unwrap();
533 assert_eq!(array.len(), NONCE_LEN);
534 }
535
536 #[test]
537 fn nonce_to_array_wrong_size() {
538 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
539 let nonce = Nonce::new(&iv, 42);
540 let result: Result<[u8; 16], Error> = nonce.to_array();
541 assert!(matches!(
542 result,
543 Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch {
544 expected: 16,
545 actual: NONCE_LEN
546 }))
547 ));
548 }
549
550 #[test]
551 fn nonce_to_array_variable_length_error() {
552 let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
554 let nonce = Nonce::new(&short_iv, 42);
555
556 let result: Result<[u8; NONCE_LEN], Error> = nonce.to_array();
558 if let Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch { expected, actual })) =
559 result
560 {
561 assert_eq!(expected, NONCE_LEN);
562 assert_eq!(actual, 8);
563 } else {
564 panic!("Expected Error::ApiMisuse(NonceArraySizeMismatch)");
565 }
566
567 let result_correct: Result<[u8; 8], Error> = nonce.to_array();
569 assert!(result_correct.is_ok());
570 }
571
572 #[test]
573 fn nonce_xor_with_iv() {
574 let iv_data = [0xFFu8; NONCE_LEN];
575 let iv = Iv::new(&iv_data).unwrap();
576 let seq = 0x0000000000000001u64;
577 let nonce = Nonce::new(&iv, seq);
578
579 let nonce_bytes = nonce.as_bytes();
581 assert_eq!(nonce_bytes[NONCE_LEN - 1], 0xFE);
582 }
583
584 #[test]
585 fn iv_length_exceeds_maximum() {
586 let too_long_iv = [0xAAu8; Iv::MAX_LEN + 1];
587 let result = Iv::new(&too_long_iv);
588
589 assert!(matches!(
590 result,
591 Err(Error::ApiMisuse(ApiMisuse::IvLengthExceedsMaximum {
592 actual: 17,
593 maximum: 16
594 }))
595 ));
596 }
597
598 #[test]
599 fn aead_key_16_bytes() {
600 let bytes = [0xABu8; 16];
601 let key = AeadKey::from(bytes);
602 assert_eq!(key.as_ref(), &bytes);
603 }
604}