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