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 use messages::{
15 EncodedMessage, InboundOpaque, MessageError, OutboundOpaque, OutboundPlain, Payload,
16};
17
18mod record_layer;
19pub(crate) use record_layer::{Decrypted, DecryptionState, EncryptionState, PreEncryptAction};
20
21pub trait Tls13AeadAlgorithm: Send + Sync {
23 fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>;
25
26 fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>;
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 MessageEncrypter>;
64
65 fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
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 MessageDecrypter: Send + Sync {
149 fn decrypt<'a>(
152 &mut self,
153 msg: EncodedMessage<InboundOpaque<'a>>,
154 seq: u64,
155 ) -> Result<EncodedMessage<&'a [u8]>, Error>;
156}
157
158pub trait MessageEncrypter: Send + Sync {
160 fn encrypt(
163 &mut self,
164 msg: EncodedMessage<OutboundPlain<'_>>,
165 seq: u64,
166 ) -> Result<EncodedMessage<OutboundOpaque>, Error>;
167
168 fn encrypted_payload_len(&self, payload_len: usize) -> usize;
171}
172
173#[derive(Default, Clone)]
175pub struct Iv {
176 buf: [u8; Self::MAX_LEN],
177 used: usize,
178}
179
180impl Iv {
181 pub fn new(value: &[u8]) -> Result<Self, Error> {
185 if value.len() > Self::MAX_LEN {
186 return Err(ApiMisuse::IvLengthExceedsMaximum {
187 actual: value.len(),
188 maximum: Self::MAX_LEN,
189 }
190 .into());
191 }
192 let mut buf = [0u8; Self::MAX_LEN];
193 buf[..value.len()].copy_from_slice(value);
194 Ok(Self {
195 buf,
196 used: value.len(),
197 })
198 }
199
200 #[expect(clippy::len_without_is_empty)]
202 pub fn len(&self) -> usize {
203 self.used
204 }
205
206 pub const MAX_LEN: usize = 16;
208}
209
210impl From<[u8; NONCE_LEN]> for Iv {
211 fn from(bytes: [u8; NONCE_LEN]) -> Self {
212 Self::new(&bytes).expect("NONCE_LEN is within MAX_LEN")
213 }
214}
215
216impl AsRef<[u8]> for Iv {
217 fn as_ref(&self) -> &[u8] {
218 &self.buf[..self.used]
219 }
220}
221
222pub struct Nonce {
224 buf: [u8; Iv::MAX_LEN],
225 len: usize,
226}
227
228impl Nonce {
229 #[inline]
233 pub fn new(iv: &Iv, seq: u64) -> Self {
234 Self::new_inner(None, iv, seq)
235 }
236
237 pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
242 Self::new_inner(path_id, iv, pn)
243 }
244
245 #[inline]
247 fn new_inner(path_id: Option<u32>, iv: &Iv, seq: u64) -> Self {
248 let iv_len = iv.len();
249 let mut buf = [0u8; Iv::MAX_LEN];
250
251 if iv_len >= 8 {
252 put_u64(seq, &mut buf[iv_len - 8..iv_len]);
253 if let Some(path_id) = path_id {
254 if iv_len >= 12 {
255 buf[iv_len - 12..iv_len - 8].copy_from_slice(&path_id.to_be_bytes());
256 }
257 }
258 } else {
259 let seq_bytes = seq.to_be_bytes();
260 buf[..iv_len].copy_from_slice(&seq_bytes[8 - iv_len..]);
261 }
262
263 buf[..iv_len]
264 .iter_mut()
265 .zip(iv.as_ref())
266 .for_each(|(s, iv)| *s ^= *iv);
267
268 Self { buf, len: iv_len }
269 }
270
271 pub fn to_array<const N: usize>(&self) -> Result<[u8; N], Error> {
278 if self.len != N {
279 return Err(ApiMisuse::NonceArraySizeMismatch {
280 expected: N,
281 actual: self.len,
282 }
283 .into());
284 }
285 Ok(self.buf[..N]
286 .try_into()
287 .expect("nonce buffer conversion failed"))
288 }
289
290 pub fn as_bytes(&self) -> &[u8] {
292 &self.buf[..self.len]
293 }
294
295 #[expect(clippy::len_without_is_empty)]
297 pub fn len(&self) -> usize {
298 self.len
299 }
300}
301
302impl AsRef<[u8]> for Nonce {
303 fn as_ref(&self) -> &[u8] {
304 &self.buf[..self.len]
305 }
306}
307
308pub const NONCE_LEN: usize = 12;
311
312#[inline]
316pub fn make_tls13_aad(payload_len: usize) -> [u8; 5] {
317 let version = ProtocolVersion::TLSv1_2.to_array();
318 [
319 ContentType::ApplicationData.into(),
320 version[0],
322 version[1],
323 (payload_len >> 8) as u8,
324 (payload_len & 0xff) as u8,
325 ]
326}
327
328#[inline]
332pub fn make_tls12_aad(
333 seq: u64,
334 typ: ContentType,
335 vers: ProtocolVersion,
336 len: usize,
337) -> [u8; TLS12_AAD_SIZE] {
338 let mut out = [0; TLS12_AAD_SIZE];
339 put_u64(seq, &mut out[0..]);
340 out[8] = typ.into();
341 put_u16(vers.into(), &mut out[9..]);
342 put_u16(len as u16, &mut out[11..]);
343 out
344}
345
346const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
347
348pub struct AeadKey {
352 buf: [u8; Self::MAX_LEN],
353 used: usize,
354}
355
356impl AeadKey {
357 pub(crate) fn new(buf: &[u8]) -> Self {
358 debug_assert!(buf.len() <= Self::MAX_LEN);
359 let mut key = Self::from([0u8; Self::MAX_LEN]);
360 key.buf[..buf.len()].copy_from_slice(buf);
361 key.used = buf.len();
362 key
363 }
364
365 pub(crate) fn with_length(self, len: usize) -> Self {
366 assert!(len <= self.used);
367 Self {
368 buf: self.buf,
369 used: len,
370 }
371 }
372
373 pub(crate) const MAX_LEN: usize = 32;
375}
376
377impl Drop for AeadKey {
378 fn drop(&mut self) {
379 self.buf.zeroize();
380 }
381}
382
383impl AsRef<[u8]> for AeadKey {
384 fn as_ref(&self) -> &[u8] {
385 &self.buf[..self.used]
386 }
387}
388
389impl From<[u8; Self::MAX_LEN]> for AeadKey {
390 fn from(bytes: [u8; Self::MAX_LEN]) -> Self {
391 Self {
392 buf: bytes,
393 used: Self::MAX_LEN,
394 }
395 }
396}
397
398impl From<[u8; 16]> for AeadKey {
399 fn from(buf: [u8; 16]) -> Self {
400 Self {
401 buf: array::from_fn(|i| if i < 16 { buf[i] } else { 0 }),
402 used: 16,
403 }
404 }
405}
406
407#[cfg(test)]
408pub(crate) struct FakeAead;
409
410#[cfg(test)]
411impl Tls12AeadAlgorithm for FakeAead {
412 fn encrypter(&self, _: AeadKey, _: &[u8], _: &[u8]) -> Box<dyn MessageEncrypter> {
413 todo!()
414 }
415
416 fn decrypter(&self, _: AeadKey, _: &[u8]) -> Box<dyn MessageDecrypter> {
417 todo!()
418 }
419
420 fn key_block_shape(&self) -> KeyBlockShape {
421 todo!()
422 }
423
424 fn extract_keys(
425 &self,
426 _: AeadKey,
427 _: &[u8],
428 _: &[u8],
429 ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
430 Err(UnsupportedOperationError)
431 }
432
433 fn fips(&self) -> FipsStatus {
434 FipsStatus::Unvalidated
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 #[test]
445 fn multipath_nonce() {
446 const PATH_ID: u32 = 3;
447 const PN: u64 = 54321;
448 const IV: [u8; 16] = 0x6b26114b9cba2b63a9e8dd4fu128.to_be_bytes();
449 const EXPECTED_NONCE: [u8; 16] = 0x6b2611489cba2b63a9e8097eu128.to_be_bytes();
450 let nonce = Nonce::quic(Some(PATH_ID), &Iv::new(&IV[4..]).unwrap(), PN);
451 assert_eq!(&EXPECTED_NONCE[4..], nonce.as_bytes());
452 }
453
454 #[test]
455 fn iv_len() {
456 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
457 assert_eq!(iv.len(), NONCE_LEN);
458
459 let short_iv = Iv::new(&[1u8, 2, 3]).unwrap();
460 assert_eq!(short_iv.len(), 3);
461
462 let empty_iv = Iv::new(&[]).unwrap();
463 assert_eq!(empty_iv.len(), 0);
464 }
465
466 #[test]
467 fn iv_as_ref() {
468 let iv_data = [1u8, 2, 3, 4, 5];
469 let iv = Iv::new(&iv_data).unwrap();
470 let iv_ref: &[u8] = iv.as_ref();
471 assert_eq!(iv_ref, &iv_data);
472 }
473
474 #[test]
475 fn nonce_with_short_iv() {
476 let short_iv = Iv::new(&[0xAA, 0xBB, 0xCC, 0xDD]).unwrap();
477 let seq = 0x1122334455667788u64;
478 let nonce = Nonce::new(&short_iv, seq);
479
480 assert_eq!(nonce.len(), 4);
482 let seq_bytes = seq.to_be_bytes();
483 let expected = [
484 0xAA ^ seq_bytes[4],
485 0xBB ^ seq_bytes[5],
486 0xCC ^ seq_bytes[6],
487 0xDD ^ seq_bytes[7],
488 ];
489 assert_eq!(nonce.as_bytes(), &expected);
490 }
491
492 #[test]
493 fn nonce_len() {
494 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
495 let nonce = Nonce::new(&iv, 42);
496 assert_eq!(nonce.len(), NONCE_LEN);
497
498 let short_iv = Iv::new(&[1u8, 2]).unwrap();
499 let short_nonce = Nonce::new(&short_iv, 42);
500 assert_eq!(short_nonce.len(), 2);
501 }
502
503 #[test]
504 fn nonce_as_ref() {
505 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
506 let nonce = Nonce::new(&iv, 42);
507 let nonce_ref: &[u8] = nonce.as_ref();
508 assert_eq!(nonce_ref.len(), NONCE_LEN);
509 }
510
511 #[test]
512 fn nonce_to_array_correct_size() {
513 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
514 let nonce = Nonce::new(&iv, 42);
515 let array: [u8; NONCE_LEN] = nonce.to_array().unwrap();
516 assert_eq!(array.len(), NONCE_LEN);
517 }
518
519 #[test]
520 fn nonce_to_array_wrong_size() {
521 let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
522 let nonce = Nonce::new(&iv, 42);
523 let result: Result<[u8; 16], Error> = nonce.to_array();
524 assert!(matches!(
525 result,
526 Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch {
527 expected: 16,
528 actual: NONCE_LEN
529 }))
530 ));
531 }
532
533 #[test]
534 fn nonce_to_array_variable_length_error() {
535 let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
537 let nonce = Nonce::new(&short_iv, 42);
538
539 let result: Result<[u8; NONCE_LEN], Error> = nonce.to_array();
541 if let Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch { expected, actual })) =
542 result
543 {
544 assert_eq!(expected, NONCE_LEN);
545 assert_eq!(actual, 8);
546 } else {
547 panic!("Expected Error::ApiMisuse(NonceArraySizeMismatch)");
548 }
549
550 let result_correct: Result<[u8; 8], Error> = nonce.to_array();
552 assert!(result_correct.is_ok());
553 }
554
555 #[test]
556 fn nonce_xor_with_iv() {
557 let iv_data = [0xFFu8; NONCE_LEN];
558 let iv = Iv::new(&iv_data).unwrap();
559 let seq = 0x0000000000000001u64;
560 let nonce = Nonce::new(&iv, seq);
561
562 let nonce_bytes = nonce.as_bytes();
564 assert_eq!(nonce_bytes[NONCE_LEN - 1], 0xFE);
565 }
566
567 #[test]
568 fn iv_length_exceeds_maximum() {
569 let too_long_iv = [0xAAu8; Iv::MAX_LEN + 1];
570 let result = Iv::new(&too_long_iv);
571
572 assert!(matches!(
573 result,
574 Err(Error::ApiMisuse(ApiMisuse::IvLengthExceedsMaximum {
575 actual: 17,
576 maximum: 16
577 }))
578 ));
579 }
580
581 #[test]
582 fn aead_key_16_bytes() {
583 let bytes = [0xABu8; 16];
584 let key = AeadKey::from(bytes);
585 assert_eq!(key.as_ref(), &bytes);
586 }
587}