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
116impl core::error::Error for UnsupportedOperationError {}
117
118/// How a TLS1.2 `key_block` is partitioned.
119///
120/// Note: ciphersuites with non-zero `mac_key_length` are  not currently supported.
121#[expect(clippy::exhaustive_structs)]
122pub struct KeyBlockShape {
123    /// How long keys are.
124    ///
125    /// `enc_key_length` terminology is from the standard ([RFC5246 A.6]).
126    ///
127    /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6>
128    pub enc_key_len: usize,
129
130    /// How long the fixed part of the 'IV' is.
131    ///
132    /// `fixed_iv_length` terminology is from the standard ([RFC5246 A.6]).
133    ///
134    /// This isn't usually an IV, but we continue the
135    /// terminology misuse to match the standard.
136    ///
137    /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6>
138    pub fixed_iv_len: usize,
139
140    /// This is a non-standard extension which extends the
141    /// key block to provide an initial explicit nonce offset,
142    /// in a deterministic and safe way.  GCM needs this,
143    /// chacha20poly1305 works this way by design.
144    pub explicit_nonce_len: usize,
145}
146
147/// Objects with this trait can decrypt TLS messages.
148pub trait MessageDecrypter: Send + Sync {
149    /// Decrypt the given TLS message `msg`, using the sequence number
150    /// `seq` which can be used to derive a unique [`Nonce`].
151    fn decrypt<'a>(
152        &mut self,
153        msg: EncodedMessage<InboundOpaque<'a>>,
154        seq: u64,
155    ) -> Result<EncodedMessage<&'a [u8]>, Error>;
156}
157
158/// Objects with this trait can encrypt TLS messages.
159pub trait MessageEncrypter: Send + Sync {
160    /// Encrypt the given TLS message `msg`, using the sequence number
161    /// `seq` which can be used to derive a unique [`Nonce`].
162    fn encrypt(
163        &mut self,
164        msg: EncodedMessage<OutboundPlain<'_>>,
165        seq: u64,
166    ) -> Result<EncodedMessage<OutboundOpaque>, Error>;
167
168    /// Return the length of the ciphertext that results from encrypting plaintext of
169    /// length `payload_len`
170    fn encrypted_payload_len(&self, payload_len: usize) -> usize;
171}
172
173/// A write or read IV.
174#[derive(Default, Clone)]
175pub struct Iv {
176    buf: [u8; Self::MAX_LEN],
177    used: usize,
178}
179
180impl Iv {
181    /// Create a new `Iv` from a byte slice.
182    ///
183    /// Returns an error if the length of `value` exceeds [`Self::MAX_LEN`].
184    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    /// Return the IV length.
201    #[expect(clippy::len_without_is_empty)]
202    pub fn len(&self) -> usize {
203        self.used
204    }
205
206    /// Maximum supported IV length.
207    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
222/// A nonce.  This is unique for all messages on a connection.
223pub struct Nonce {
224    buf: [u8; Iv::MAX_LEN],
225    len: usize,
226}
227
228impl Nonce {
229    /// Combine an `Iv` and sequence number to produce a unique nonce.
230    ///
231    /// This is `iv ^ seq` where `seq` is encoded as a big-endian integer.
232    #[inline]
233    pub fn new(iv: &Iv, seq: u64) -> Self {
234        Self::new_inner(None, iv, seq)
235    }
236
237    /// Creates a unique nonce based on the multipath `path_id`, the `iv` and packet number `pn`.
238    ///
239    /// The nonce is computed as the XOR between the `iv` and the big-endian integer formed
240    /// by concatenating `path_id` (or 0) and `pn`.
241    pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
242        Self::new_inner(path_id, iv, pn)
243    }
244
245    /// Creates a unique nonce based on the iv and sequence number seq.
246    #[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    /// Convert to a fixed-size array of length `N`.
272    ///
273    /// Returns an error if the nonce length is not `N`.
274    ///
275    /// For standard nonces, use `nonce.to_array::<NONCE_LEN>()?` or just `nonce.to_array()?`
276    /// which defaults to `NONCE_LEN`.
277    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    /// Return the nonce value.
291    pub fn as_bytes(&self) -> &[u8] {
292        &self.buf[..self.len]
293    }
294
295    /// Return the nonce length.
296    #[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
308/// Size of TLS nonces (incorrectly termed "IV" in standard) for all supported ciphersuites
309/// (AES-GCM, Chacha20Poly1305)
310pub const NONCE_LEN: usize = 12;
311
312/// Returns a TLS1.3 `additional_data` encoding.
313///
314/// See RFC8446 s5.2 for the `additional_data` definition.
315#[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        // Note: this is `legacy_record_version`, i.e. TLS1.2 even for TLS1.3.
321        version[0],
322        version[1],
323        (payload_len >> 8) as u8,
324        (payload_len & 0xff) as u8,
325    ]
326}
327
328/// Returns a TLS1.2 `additional_data` encoding.
329///
330/// See RFC5246 s6.2.3.3 for the `additional_data` definition.
331#[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
348/// A key for an AEAD algorithm.
349///
350/// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length.
351pub 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    /// Largest possible AEAD key in the ciphersuites we support.
374    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    /// Using test values provided in the spec in
443    /// <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-15.html#section-2.4>
444    #[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        // The nonce should XOR the last 4 bytes of seq with the IV
481        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        // Create an IV with a non-standard length (8 bytes instead of 12)
536        let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
537        let nonce = Nonce::new(&short_iv, 42);
538
539        // Attempting to convert to standard NONCE_LEN should fail
540        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        // But converting to the correct length should work
551        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        // The last byte should be 0xFF XOR 0x01 = 0xFE
563        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}