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, EncodedMessage, EncryptBuffer, InboundOpaque, MessageError, OutboundPlain,
17 Payload,
18};
19
20mod record_layer;
21pub(crate) use record_layer::{Decrypted, DecryptionState, EncryptionState, PreEncryptAction};
22
23pub trait Tls13AeadAlgorithm: Send + Sync {
25 fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>;
27
28 fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>;
30
31 fn key_len(&self) -> usize;
33
34 fn iv_len(&self) -> usize {
36 NONCE_LEN
37 }
38
39 fn extract_keys(
44 &self,
45 key: AeadKey,
46 iv: Iv,
47 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
48
49 fn fips(&self) -> FipsStatus {
51 FipsStatus::Unvalidated
52 }
53}
54
55pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
57 fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>;
66
67 fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
73
74 fn key_block_shape(&self) -> KeyBlockShape;
77
78 fn extract_keys(
89 &self,
90 key: AeadKey,
91 iv: &[u8],
92 explicit: &[u8],
93 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
94
95 fn fips(&self) -> FipsStatus {
97 FipsStatus::Unvalidated
98 }
99}
100
101#[expect(clippy::exhaustive_structs)]
103#[derive(Debug, Eq, PartialEq, Clone, Copy)]
104pub struct UnsupportedOperationError;
105
106impl From<UnsupportedOperationError> for Error {
107 fn from(value: UnsupportedOperationError) -> Self {
108 Self::General(value.to_string())
109 }
110}
111
112impl fmt::Display for UnsupportedOperationError {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "operation not supported")
115 }
116}
117
118impl core::error::Error for UnsupportedOperationError {}
119
120#[expect(clippy::exhaustive_structs)]
124pub struct KeyBlockShape {
125 pub enc_key_len: usize,
131
132 pub fixed_iv_len: usize,
141
142 pub explicit_nonce_len: usize,
147}
148
149pub trait MessageDecrypter: Send + Sync {
151 fn decrypt<'a>(
154 &mut self,
155 msg: EncodedMessage<InboundOpaque<'a>>,
156 seq: u64,
157 ) -> Result<EncodedMessage<&'a [u8]>, Error>;
158}
159
160pub trait MessageEncrypter: Send + Sync {
162 fn encrypt<'a>(
176 &mut self,
177 msg: EncodedMessage<OutboundPlain<'_>>,
178 seq: u64,
179 out: &'a mut [u8],
180 ) -> Result<EncodedMessage<&'a [u8]>, Error>;
181
182 fn encrypted_payload_len(&self, payload_len: usize) -> usize;
190}
191
192#[derive(Default, Clone)]
194pub struct Iv {
195 buf: [u8; Self::MAX_LEN],
196 used: usize,
197}
198
199impl Iv {
200 pub fn new(value: &[u8]) -> Result<Self, Error> {
204 if value.len() > Self::MAX_LEN {
205 return Err(ApiMisuse::IvLengthExceedsMaximum {
206 actual: value.len(),
207 maximum: Self::MAX_LEN,
208 }
209 .into());
210 }
211 let mut buf = [0u8; Self::MAX_LEN];
212 buf[..value.len()].copy_from_slice(value);
213 Ok(Self {
214 buf,
215 used: value.len(),
216 })
217 }
218
219 #[expect(clippy::len_without_is_empty)]
221 pub fn len(&self) -> usize {
222 self.used
223 }
224
225 pub const MAX_LEN: usize = 16;
227}
228
229impl From<[u8; NONCE_LEN]> for Iv {
230 fn from(bytes: [u8; NONCE_LEN]) -> Self {
231 Self::new(&bytes).expect("NONCE_LEN is within MAX_LEN")
232 }
233}
234
235impl AsRef<[u8]> for Iv {
236 fn as_ref(&self) -> &[u8] {
237 &self.buf[..self.used]
238 }
239}
240
241pub struct Nonce {
243 buf: [u8; Iv::MAX_LEN],
244 len: usize,
245}
246
247impl Nonce {
248 #[inline]
252 pub fn new(iv: &Iv, seq: u64) -> Self {
253 Self::new_inner(None, iv, seq)
254 }
255
256 pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
261 Self::new_inner(path_id, iv, pn)
262 }
263
264 #[inline]
266 fn new_inner(path_id: Option<u32>, iv: &Iv, seq: u64) -> Self {
267 let iv_len = iv.len();
268 let mut buf = [0u8; Iv::MAX_LEN];
269
270 if iv_len >= 8 {
271 put_u64(seq, &mut buf[iv_len - 8..iv_len]);
272 if let Some(path_id) = path_id {
273 if iv_len >= 12 {
274 buf[iv_len - 12..iv_len - 8].copy_from_slice(&path_id.to_be_bytes());
275 }
276 }
277 } else {
278 let seq_bytes = seq.to_be_bytes();
279 buf[..iv_len].copy_from_slice(&seq_bytes[8 - iv_len..]);
280 }
281
282 buf[..iv_len]
283 .iter_mut()
284 .zip(iv.as_ref())
285 .for_each(|(s, iv)| *s ^= *iv);
286
287 Self { buf, len: iv_len }
288 }
289
290 pub fn to_array<const N: usize>(&self) -> Result<[u8; N], Error> {
297 if self.len != N {
298 return Err(ApiMisuse::NonceArraySizeMismatch {
299 expected: N,
300 actual: self.len,
301 }
302 .into());
303 }
304 Ok(self.buf[..N]
305 .try_into()
306 .expect("nonce buffer conversion failed"))
307 }
308
309 pub fn as_bytes(&self) -> &[u8] {
311 &self.buf[..self.len]
312 }
313
314 #[expect(clippy::len_without_is_empty)]
316 pub fn len(&self) -> usize {
317 self.len
318 }
319}
320
321impl AsRef<[u8]> for Nonce {
322 fn as_ref(&self) -> &[u8] {
323 &self.buf[..self.len]
324 }
325}
326
327pub const NONCE_LEN: usize = 12;
330
331#[inline]
338pub fn make_tls13_aad(typ: ContentType, version: ProtocolVersion, payload_len: usize) -> [u8; 5] {
339 let version = version.to_array();
340 [
341 typ.into(),
342 version[0],
343 version[1],
344 (payload_len >> 8) as u8,
345 (payload_len & 0xff) as u8,
346 ]
347}
348
349#[inline]
353pub fn make_tls12_aad(
354 seq: u64,
355 typ: ContentType,
356 vers: ProtocolVersion,
357 len: usize,
358) -> [u8; TLS12_AAD_SIZE] {
359 let mut out = [0; TLS12_AAD_SIZE];
360 put_u64(seq, &mut out[0..]);
361 out[8] = typ.into();
362 put_u16(vers.into(), &mut out[9..]);
363 put_u16(len as u16, &mut out[11..]);
364 out
365}
366
367const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
368
369pub struct AeadKey {
373 buf: [u8; Self::MAX_LEN],
374 used: usize,
375}
376
377impl AeadKey {
378 pub(crate) fn new(buf: &[u8]) -> Self {
379 debug_assert!(buf.len() <= Self::MAX_LEN);
380 let mut key = Self::from([0u8; Self::MAX_LEN]);
381 key.buf[..buf.len()].copy_from_slice(buf);
382 key.used = buf.len();
383 key
384 }
385
386 pub(crate) fn with_length(self, len: usize) -> Self {
387 assert!(len <= self.used);
388 Self {
389 buf: self.buf,
390 used: len,
391 }
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.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 MessageEncrypter> {
435 todo!()
436 }
437
438 fn decrypter(&self, _: AeadKey, _: &[u8]) -> Box<dyn MessageDecrypter> {
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}