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(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
23/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.3 cipher suite.
24pub trait Tls13AeadAlgorithm: Send + Sync {
25    /// Build a `MessageEncrypter` for the given key/iv.
26    fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>;
27
28    /// Build a `MessageDecrypter` for the given key/iv.
29    fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>;
30
31    /// The length of key in bytes required by `encrypter()` and `decrypter()`.
32    fn key_len(&self) -> usize;
33
34    /// The length of IV in bytes required by `encrypter()` and `decrypter()`.
35    fn iv_len(&self) -> usize {
36        NONCE_LEN
37    }
38
39    /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item.
40    ///
41    /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported
42    /// variant of `ConnectionTrafficSecrets`.
43    fn extract_keys(
44        &self,
45        key: AeadKey,
46        iv: Iv,
47    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
48
49    /// Return `true` if this is backed by a FIPS-approved implementation.
50    fn fips(&self) -> FipsStatus {
51        FipsStatus::Unvalidated
52    }
53}
54
55/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.2 cipher suite.
56pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
57    /// Build a `MessageEncrypter` for the given key/iv and extra key block (which can be used for
58    /// improving explicit nonce size security, if needed).
59    ///
60    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
61    ///
62    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
63    ///
64    /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`].
65    fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>;
66
67    /// Build a `MessageDecrypter` for the given key/iv.
68    ///
69    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
70    ///
71    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
72    fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
73
74    /// Return a `KeyBlockShape` that defines how large the `key_block` is and how it
75    /// is split up prior to calling `encrypter()`, `decrypter()` and/or `extract_keys()`.
76    fn key_block_shape(&self) -> KeyBlockShape;
77
78    /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item.
79    ///
80    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
81    ///
82    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
83    ///
84    /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`].
85    ///
86    /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported
87    /// variant of `ConnectionTrafficSecrets`.
88    fn extract_keys(
89        &self,
90        key: AeadKey,
91        iv: &[u8],
92        explicit: &[u8],
93    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
94
95    /// Return the FIPS validation status of this implementation.
96    fn fips(&self) -> FipsStatus {
97        FipsStatus::Unvalidated
98    }
99}
100
101/// An error indicating that the AEAD algorithm does not support the requested operation.
102#[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/// How a TLS1.2 `key_block` is partitioned.
121///
122/// Note: ciphersuites with non-zero `mac_key_length` are  not currently supported.
123#[expect(clippy::exhaustive_structs)]
124pub struct KeyBlockShape {
125    /// How long keys are.
126    ///
127    /// `enc_key_length` terminology is from the standard ([RFC 5246 A.6]).
128    ///
129    /// [RFC 5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6>
130    pub enc_key_len: usize,
131
132    /// How long the fixed part of the 'IV' is.
133    ///
134    /// `fixed_iv_length` terminology is from the standard ([RFC 5246 A.6]).
135    ///
136    /// This isn't usually an IV, but we continue the
137    /// terminology misuse to match the standard.
138    ///
139    /// [RFC 5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6>
140    pub fixed_iv_len: usize,
141
142    /// This is a non-standard extension which extends the
143    /// key block to provide an initial explicit nonce offset,
144    /// in a deterministic and safe way.  GCM needs this,
145    /// chacha20poly1305 works this way by design.
146    pub explicit_nonce_len: usize,
147}
148
149/// Objects with this trait can decrypt TLS messages.
150pub trait MessageDecrypter: Send + Sync {
151    /// Decrypt the given TLS message `msg`, using the sequence number
152    /// `seq` which can be used to derive a unique [`Nonce`].
153    fn decrypt<'a>(
154        &mut self,
155        msg: EncodedMessage<InboundOpaque<'a>>,
156        seq: u64,
157    ) -> Result<EncodedMessage<&'a [u8]>, Error>;
158}
159
160/// Objects with this trait can encrypt TLS messages.
161pub trait MessageEncrypter: Send + Sync {
162    /// Encrypt the given TLS message `msg` into `out`, using the sequence number
163    /// `seq` which can be used to derive a unique [`Nonce`].
164    ///
165    /// The encrypted payload including all framing the ciphersuite requires, such
166    /// as any explicit nonce, padding and/or authentication tag, is written to the
167    /// front of `out`. `out` must be at least [`Self::encrypted_payload_len()`] bytes
168    /// long. See [`EncryptBuffer`] for a convenient wrapper.
169    ///
170    /// The returned message describes the resulting record: its payload borrows the
171    /// written prefix of `out`, and its `typ` and `version` are what the record
172    /// header should carry on the wire. Encoding the record header is the caller's
173    /// responsibility and implementations of the `MessageEncrypter` trait must not
174    /// write it to `out` themselves.
175    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    /// Return the length of the ciphertext that results from encrypting plaintext of length `payload_len`.
183    ///
184    /// For a zero `payload_len` this should return the _minimum_ overhead for any
185    /// message.  Then, to fragment a long message into chunks of length `F`,
186    /// Rustls will first set `A := encrypted_payload_len(0)` and then supply the
187    /// message to [`Self::encrypt()`] in chunks of length `F - A`.  Each `encrypt()`
188    /// is then free to pad or otherwise transform the length at its option.
189    fn encrypted_payload_len(&self, payload_len: usize) -> usize;
190}
191
192/// A write or read IV.
193#[derive(Default, Clone)]
194pub struct Iv {
195    buf: [u8; Self::MAX_LEN],
196    used: usize,
197}
198
199impl Iv {
200    /// Create a new `Iv` from a byte slice.
201    ///
202    /// Returns an error if the length of `value` exceeds [`Self::MAX_LEN`].
203    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    /// Return the IV length.
220    #[expect(clippy::len_without_is_empty)]
221    pub fn len(&self) -> usize {
222        self.used
223    }
224
225    /// Maximum supported IV length.
226    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
241/// A nonce.  This is unique for all messages on a connection.
242pub struct Nonce {
243    buf: [u8; Iv::MAX_LEN],
244    len: usize,
245}
246
247impl Nonce {
248    /// Combine an `Iv` and sequence number to produce a unique nonce.
249    ///
250    /// This is `iv ^ seq` where `seq` is encoded as a big-endian integer.
251    #[inline]
252    pub fn new(iv: &Iv, seq: u64) -> Self {
253        Self::new_inner(None, iv, seq)
254    }
255
256    /// Creates a unique nonce based on the multipath `path_id`, the `iv` and packet number `pn`.
257    ///
258    /// The nonce is computed as the XOR between the `iv` and the big-endian integer formed
259    /// by concatenating `path_id` (or 0) and `pn`.
260    pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
261        Self::new_inner(path_id, iv, pn)
262    }
263
264    /// Creates a unique nonce based on the iv and sequence number seq.
265    #[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    /// Convert to a fixed-size array of length `N`.
291    ///
292    /// Returns an error if the nonce length is not `N`.
293    ///
294    /// For standard nonces, use `nonce.to_array::<NONCE_LEN>()?` or just `nonce.to_array()?`
295    /// which defaults to `NONCE_LEN`.
296    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    /// Return the nonce value.
310    pub fn as_bytes(&self) -> &[u8] {
311        &self.buf[..self.len]
312    }
313
314    /// Return the nonce length.
315    #[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
327/// Size of TLS nonces (incorrectly termed "IV" in standard) for all supported ciphersuites
328/// (AES-GCM, Chacha20Poly1305)
329pub const NONCE_LEN: usize = 12;
330
331/// Returns a TLS1.3 `additional_data` encoding.
332///
333/// For decryption, the parameters should be those that were received on the wire.
334/// For encryption, the parameters should be those that will be put on the wire.
335///
336/// See RFC 9846 s5.2 for the `additional_data` definition.
337#[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/// Returns a TLS1.2 `additional_data` encoding.
350///
351/// See RFC 5246 s6.2.3.3 for the `additional_data` definition.
352#[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
369/// A key for an AEAD algorithm.
370///
371/// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length.
372pub 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    /// Largest possible AEAD key in the ciphersuites we support.
395    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    /// Using test values provided in the spec in
465    /// <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-15.html#section-2.4>
466    #[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        // The nonce should XOR the last 4 bytes of seq with the IV
503        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        // Create an IV with a non-standard length (8 bytes instead of 12)
558        let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
559        let nonce = Nonce::new(&short_iv, 42);
560
561        // Attempting to convert to standard NONCE_LEN should fail
562        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        // But converting to the correct length should work
573        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        // The last byte should be 0xFF XOR 0x01 = 0xFE
585        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}