Skip to main content

rustls/crypto/cipher/
mod.rs

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
21/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.3 cipher suite.
22pub trait Tls13AeadAlgorithm: Send + Sync {
23    /// Build a `MessageEncrypter` for the given key/iv.
24    fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>;
25
26    /// Build a `MessageDecrypter` for the given key/iv.
27    fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>;
28
29    /// The length of key in bytes required by `encrypter()` and `decrypter()`.
30    fn key_len(&self) -> usize;
31
32    /// The length of IV in bytes required by `encrypter()` and `decrypter()`.
33    fn iv_len(&self) -> usize {
34        NONCE_LEN
35    }
36
37    /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item.
38    ///
39    /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported
40    /// variant of `ConnectionTrafficSecrets`.
41    fn extract_keys(
42        &self,
43        key: AeadKey,
44        iv: Iv,
45    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
46
47    /// Return `true` if this is backed by a FIPS-approved implementation.
48    fn fips(&self) -> FipsStatus {
49        FipsStatus::Unvalidated
50    }
51}
52
53/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.2 cipher suite.
54pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
55    /// Build a `MessageEncrypter` for the given key/iv and extra key block (which can be used for
56    /// improving explicit nonce size security, if needed).
57    ///
58    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
59    ///
60    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
61    ///
62    /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`].
63    fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>;
64
65    /// Build a `MessageDecrypter` for the given key/iv.
66    ///
67    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
68    ///
69    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
70    fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
71
72    /// Return a `KeyBlockShape` that defines how large the `key_block` is and how it
73    /// is split up prior to calling `encrypter()`, `decrypter()` and/or `extract_keys()`.
74    fn key_block_shape(&self) -> KeyBlockShape;
75
76    /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item.
77    ///
78    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
79    ///
80    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
81    ///
82    /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`].
83    ///
84    /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported
85    /// variant of `ConnectionTrafficSecrets`.
86    fn extract_keys(
87        &self,
88        key: AeadKey,
89        iv: &[u8],
90        explicit: &[u8],
91    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
92
93    /// Return the FIPS validation status of this implementation.
94    fn fips(&self) -> FipsStatus {
95        FipsStatus::Unvalidated
96    }
97}
98
99/// An error indicating that the AEAD algorithm does not support the requested operation.
100#[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/// How a TLS1.2 `key_block` is partitioned.
120///
121/// Note: ciphersuites with non-zero `mac_key_length` are  not currently supported.
122#[expect(clippy::exhaustive_structs)]
123pub struct KeyBlockShape {
124    /// How long keys are.
125    ///
126    /// `enc_key_length` terminology is from the standard ([RFC5246 A.6]).
127    ///
128    /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6>
129    pub enc_key_len: usize,
130
131    /// How long the fixed part of the 'IV' is.
132    ///
133    /// `fixed_iv_length` terminology is from the standard ([RFC5246 A.6]).
134    ///
135    /// This isn't usually an IV, but we continue the
136    /// terminology misuse to match the standard.
137    ///
138    /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6>
139    pub fixed_iv_len: usize,
140
141    /// This is a non-standard extension which extends the
142    /// key block to provide an initial explicit nonce offset,
143    /// in a deterministic and safe way.  GCM needs this,
144    /// chacha20poly1305 works this way by design.
145    pub explicit_nonce_len: usize,
146}
147
148/// Objects with this trait can decrypt TLS messages.
149pub trait MessageDecrypter: Send + Sync {
150    /// Decrypt the given TLS message `msg`, using the sequence number
151    /// `seq` which can be used to derive a unique [`Nonce`].
152    fn decrypt<'a>(
153        &mut self,
154        msg: EncodedMessage<InboundOpaque<'a>>,
155        seq: u64,
156    ) -> Result<EncodedMessage<&'a [u8]>, Error>;
157}
158
159/// Objects with this trait can encrypt TLS messages.
160pub trait MessageEncrypter: Send + Sync {
161    /// Encrypt the given TLS message `msg`, using the sequence number
162    /// `seq` which can be used to derive a unique [`Nonce`].
163    fn encrypt(
164        &mut self,
165        msg: EncodedMessage<OutboundPlain<'_>>,
166        seq: u64,
167    ) -> Result<EncodedMessage<OutboundOpaque>, Error>;
168
169    /// Return the length of the ciphertext that results from encrypting plaintext of
170    /// length `payload_len`
171    fn encrypted_payload_len(&self, payload_len: usize) -> usize;
172}
173
174/// A write or read IV.
175#[derive(Default, Clone)]
176pub struct Iv {
177    buf: [u8; Self::MAX_LEN],
178    used: usize,
179}
180
181impl Iv {
182    /// Create a new `Iv` from a byte slice.
183    ///
184    /// Returns an error if the length of `value` exceeds [`Self::MAX_LEN`].
185    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    /// Return the IV length.
202    #[expect(clippy::len_without_is_empty)]
203    pub fn len(&self) -> usize {
204        self.used
205    }
206
207    /// Maximum supported IV length.
208    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
223/// A nonce.  This is unique for all messages on a connection.
224pub struct Nonce {
225    buf: [u8; Iv::MAX_LEN],
226    len: usize,
227}
228
229impl Nonce {
230    /// Combine an `Iv` and sequence number to produce a unique nonce.
231    ///
232    /// This is `iv ^ seq` where `seq` is encoded as a big-endian integer.
233    #[inline]
234    pub fn new(iv: &Iv, seq: u64) -> Self {
235        Self::new_inner(None, iv, seq)
236    }
237
238    /// Creates a unique nonce based on the multipath `path_id`, the `iv` and packet number `pn`.
239    ///
240    /// The nonce is computed as the XOR between the `iv` and the big-endian integer formed
241    /// by concatenating `path_id` (or 0) and `pn`.
242    pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
243        Self::new_inner(path_id, iv, pn)
244    }
245
246    /// Creates a unique nonce based on the iv and sequence number seq.
247    #[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    /// Convert to a fixed-size array of length `N`.
273    ///
274    /// Returns an error if the nonce length is not `N`.
275    ///
276    /// For standard nonces, use `nonce.to_array::<NONCE_LEN>()?` or just `nonce.to_array()?`
277    /// which defaults to `NONCE_LEN`.
278    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    /// Return the nonce value.
292    pub fn as_bytes(&self) -> &[u8] {
293        &self.buf[..self.len]
294    }
295
296    /// Return the nonce length.
297    #[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
309/// Size of TLS nonces (incorrectly termed "IV" in standard) for all supported ciphersuites
310/// (AES-GCM, Chacha20Poly1305)
311pub const NONCE_LEN: usize = 12;
312
313/// Returns a TLS1.3 `additional_data` encoding.
314///
315/// See RFC8446 s5.2 for the `additional_data` definition.
316#[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        // Note: this is `legacy_record_version`, i.e. TLS1.2 even for TLS1.3.
322        version[0],
323        version[1],
324        (payload_len >> 8) as u8,
325        (payload_len & 0xff) as u8,
326    ]
327}
328
329/// Returns a TLS1.2 `additional_data` encoding.
330///
331/// See RFC5246 s6.2.3.3 for the `additional_data` definition.
332#[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
349/// A key for an AEAD algorithm.
350///
351/// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length.
352pub 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    /// Largest possible AEAD key in the ciphersuites we support.
375    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    /// Using test values provided in the spec in
444    /// <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-15.html#section-2.4>
445    #[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        // The nonce should XOR the last 4 bytes of seq with the IV
482        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        // Create an IV with a non-standard length (8 bytes instead of 12)
537        let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
538        let nonce = Nonce::new(&short_iv, 42);
539
540        // Attempting to convert to standard NONCE_LEN should fail
541        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        // But converting to the correct length should work
552        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        // The last byte should be 0xFF XOR 0x01 = 0xFE
564        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}