rustls/crypto/cipher/
mod.rs

1use alloc::boxed::Box;
2use alloc::string::ToString;
3use core::fmt;
4
5use zeroize::Zeroize;
6
7use crate::enums::{ContentType, ProtocolVersion};
8use crate::error::{ApiMisuse, Error};
9use crate::msgs::codec;
10use crate::suites::ConnectionTrafficSecrets;
11
12mod messages;
13pub use messages::{
14    EncodedMessage, InboundOpaque, MessageError, OutboundOpaque, OutboundPlain, Payload,
15};
16
17mod record_layer;
18pub(crate) use record_layer::{Decrypted, PreEncryptAction, RecordLayer};
19
20/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.3 cipher suite.
21pub trait Tls13AeadAlgorithm: Send + Sync {
22    /// Build a `MessageEncrypter` for the given key/iv.
23    fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>;
24
25    /// Build a `MessageDecrypter` for the given key/iv.
26    fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>;
27
28    /// The length of key in bytes required by `encrypter()` and `decrypter()`.
29    fn key_len(&self) -> usize;
30
31    /// The length of IV in bytes required by `encrypter()` and `decrypter()`.
32    fn iv_len(&self) -> usize {
33        NONCE_LEN
34    }
35
36    /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item.
37    ///
38    /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported
39    /// variant of `ConnectionTrafficSecrets`.
40    fn extract_keys(
41        &self,
42        key: AeadKey,
43        iv: Iv,
44    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
45
46    /// Return `true` if this is backed by a FIPS-approved implementation.
47    fn fips(&self) -> bool {
48        false
49    }
50}
51
52/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.2 cipher suite.
53pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
54    /// Build a `MessageEncrypter` for the given key/iv and extra key block (which can be used for
55    /// improving explicit nonce size security, if needed).
56    ///
57    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
58    ///
59    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
60    ///
61    /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`].
62    fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>;
63
64    /// Build a `MessageDecrypter` for the given key/iv.
65    ///
66    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
67    ///
68    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
69    fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
70
71    /// Return a `KeyBlockShape` that defines how large the `key_block` is and how it
72    /// is split up prior to calling `encrypter()`, `decrypter()` and/or `extract_keys()`.
73    fn key_block_shape(&self) -> KeyBlockShape;
74
75    /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item.
76    ///
77    /// The length of `key` is set by [`KeyBlockShape::enc_key_len`].
78    ///
79    /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`].
80    ///
81    /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`].
82    ///
83    /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported
84    /// variant of `ConnectionTrafficSecrets`.
85    fn extract_keys(
86        &self,
87        key: AeadKey,
88        iv: &[u8],
89        explicit: &[u8],
90    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
91
92    /// Return `true` if this is backed by a FIPS-approved implementation.
93    fn fips(&self) -> bool {
94        false
95    }
96}
97
98/// An error indicating that the AEAD algorithm does not support the requested operation.
99#[expect(clippy::exhaustive_structs)]
100#[derive(Debug, Eq, PartialEq, Clone, Copy)]
101pub struct UnsupportedOperationError;
102
103impl From<UnsupportedOperationError> for Error {
104    fn from(value: UnsupportedOperationError) -> Self {
105        Self::General(value.to_string())
106    }
107}
108
109impl fmt::Display for UnsupportedOperationError {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        write!(f, "operation not supported")
112    }
113}
114
115#[cfg(feature = "std")]
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
173impl dyn MessageEncrypter {
174    pub(crate) fn invalid() -> Box<dyn MessageEncrypter> {
175        Box::new(InvalidMessageEncrypter {})
176    }
177}
178
179impl dyn MessageDecrypter {
180    pub(crate) fn invalid() -> Box<dyn MessageDecrypter> {
181        Box::new(InvalidMessageDecrypter {})
182    }
183}
184
185/// A write or read IV.
186#[derive(Default, Clone)]
187pub struct Iv {
188    buf: [u8; Self::MAX_LEN],
189    used: usize,
190}
191
192impl Iv {
193    /// Create a new `Iv` from a byte slice.
194    ///
195    /// Returns an error if the length of `value` exceeds [`Self::MAX_LEN`].
196    pub fn new(value: &[u8]) -> Result<Self, Error> {
197        if value.len() > Self::MAX_LEN {
198            return Err(ApiMisuse::IvLengthExceedsMaximum {
199                actual: value.len(),
200                maximum: Self::MAX_LEN,
201            }
202            .into());
203        }
204        let mut buf = [0u8; Self::MAX_LEN];
205        buf[..value.len()].copy_from_slice(value);
206        Ok(Self {
207            buf,
208            used: value.len(),
209        })
210    }
211
212    /// Return the IV length.
213    #[expect(clippy::len_without_is_empty)]
214    pub fn len(&self) -> usize {
215        self.used
216    }
217
218    /// Maximum supported IV length.
219    pub const MAX_LEN: usize = 16;
220}
221
222impl From<[u8; NONCE_LEN]> for Iv {
223    fn from(bytes: [u8; NONCE_LEN]) -> Self {
224        Self::new(&bytes).expect("NONCE_LEN is within MAX_LEN")
225    }
226}
227
228impl AsRef<[u8]> for Iv {
229    fn as_ref(&self) -> &[u8] {
230        &self.buf[..self.used]
231    }
232}
233
234/// A nonce.  This is unique for all messages on a connection.
235pub struct Nonce {
236    buf: [u8; Iv::MAX_LEN],
237    len: usize,
238}
239
240impl Nonce {
241    /// Combine an `Iv` and sequence number to produce a unique nonce.
242    ///
243    /// This is `iv ^ seq` where `seq` is encoded as a big-endian integer.
244    #[inline]
245    pub fn new(iv: &Iv, seq: u64) -> Self {
246        Self::new_inner(None, iv, seq)
247    }
248
249    /// Creates a unique nonce based on the multipath `path_id`, the `iv` and packet number `pn`.
250    ///
251    /// The nonce is computed as the XOR between the `iv` and the big-endian integer formed
252    /// by concatenating `path_id` (or 0) and `pn`.
253    pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
254        Self::new_inner(path_id, iv, pn)
255    }
256
257    /// Creates a unique nonce based on the iv and sequence number seq.
258    #[inline]
259    fn new_inner(path_id: Option<u32>, iv: &Iv, seq: u64) -> Self {
260        let iv_len = iv.len();
261        let mut buf = [0u8; Iv::MAX_LEN];
262
263        if iv_len >= 8 {
264            codec::put_u64(seq, &mut buf[iv_len - 8..iv_len]);
265            if let Some(path_id) = path_id {
266                if iv_len >= 12 {
267                    buf[iv_len - 12..iv_len - 8].copy_from_slice(&path_id.to_be_bytes());
268                }
269            }
270        } else {
271            let seq_bytes = seq.to_be_bytes();
272            buf[..iv_len].copy_from_slice(&seq_bytes[8 - iv_len..]);
273        }
274
275        buf[..iv_len]
276            .iter_mut()
277            .zip(iv.as_ref())
278            .for_each(|(s, iv)| *s ^= *iv);
279
280        Self { buf, len: iv_len }
281    }
282
283    /// Convert to a fixed-size array of length `N`.
284    ///
285    /// Returns an error if the nonce length is not `N`.
286    ///
287    /// For standard nonces, use `nonce.to_array::<NONCE_LEN>()?` or just `nonce.to_array()?`
288    /// which defaults to `NONCE_LEN`.
289    pub fn to_array<const N: usize>(&self) -> Result<[u8; N], Error> {
290        if self.len != N {
291            return Err(ApiMisuse::NonceArraySizeMismatch {
292                expected: N,
293                actual: self.len,
294            }
295            .into());
296        }
297        Ok(self.buf[..N]
298            .try_into()
299            .expect("nonce buffer conversion failed"))
300    }
301
302    /// Return the nonce value.
303    pub fn as_bytes(&self) -> &[u8] {
304        &self.buf[..self.len]
305    }
306
307    /// Return the nonce length.
308    #[expect(clippy::len_without_is_empty)]
309    pub fn len(&self) -> usize {
310        self.len
311    }
312}
313
314impl AsRef<[u8]> for Nonce {
315    fn as_ref(&self) -> &[u8] {
316        &self.buf[..self.len]
317    }
318}
319
320/// Size of TLS nonces (incorrectly termed "IV" in standard) for all supported ciphersuites
321/// (AES-GCM, Chacha20Poly1305)
322pub const NONCE_LEN: usize = 12;
323
324/// Returns a TLS1.3 `additional_data` encoding.
325///
326/// See RFC8446 s5.2 for the `additional_data` definition.
327#[inline]
328pub fn make_tls13_aad(payload_len: usize) -> [u8; 5] {
329    let version = ProtocolVersion::TLSv1_2.to_array();
330    [
331        ContentType::ApplicationData.into(),
332        // Note: this is `legacy_record_version`, i.e. TLS1.2 even for TLS1.3.
333        version[0],
334        version[1],
335        (payload_len >> 8) as u8,
336        (payload_len & 0xff) as u8,
337    ]
338}
339
340/// Returns a TLS1.2 `additional_data` encoding.
341///
342/// See RFC5246 s6.2.3.3 for the `additional_data` definition.
343#[inline]
344pub fn make_tls12_aad(
345    seq: u64,
346    typ: ContentType,
347    vers: ProtocolVersion,
348    len: usize,
349) -> [u8; TLS12_AAD_SIZE] {
350    let mut out = [0; TLS12_AAD_SIZE];
351    codec::put_u64(seq, &mut out[0..]);
352    out[8] = typ.into();
353    codec::put_u16(vers.into(), &mut out[9..]);
354    codec::put_u16(len as u16, &mut out[11..]);
355    out
356}
357
358const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
359
360/// A key for an AEAD algorithm.
361///
362/// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length.
363pub struct AeadKey {
364    buf: [u8; Self::MAX_LEN],
365    used: usize,
366}
367
368impl AeadKey {
369    pub(crate) fn new(buf: &[u8]) -> Self {
370        debug_assert!(buf.len() <= Self::MAX_LEN);
371        let mut key = Self::from([0u8; Self::MAX_LEN]);
372        key.buf[..buf.len()].copy_from_slice(buf);
373        key.used = buf.len();
374        key
375    }
376
377    pub(crate) fn with_length(self, len: usize) -> Self {
378        assert!(len <= self.used);
379        Self {
380            buf: self.buf,
381            used: len,
382        }
383    }
384
385    /// Largest possible AEAD key in the ciphersuites we support.
386    pub(crate) const MAX_LEN: usize = 32;
387}
388
389impl Drop for AeadKey {
390    fn drop(&mut self) {
391        self.buf.zeroize();
392    }
393}
394
395impl AsRef<[u8]> for AeadKey {
396    fn as_ref(&self) -> &[u8] {
397        &self.buf[..self.used]
398    }
399}
400
401impl From<[u8; Self::MAX_LEN]> for AeadKey {
402    fn from(bytes: [u8; Self::MAX_LEN]) -> Self {
403        Self {
404            buf: bytes,
405            used: Self::MAX_LEN,
406        }
407    }
408}
409
410/// A `MessageEncrypter` which doesn't work.
411struct InvalidMessageEncrypter {}
412
413impl MessageEncrypter for InvalidMessageEncrypter {
414    fn encrypt(
415        &mut self,
416        _m: EncodedMessage<OutboundPlain<'_>>,
417        _seq: u64,
418    ) -> Result<EncodedMessage<OutboundOpaque>, Error> {
419        Err(Error::EncryptError)
420    }
421
422    fn encrypted_payload_len(&self, payload_len: usize) -> usize {
423        payload_len
424    }
425}
426
427/// A `MessageDecrypter` which doesn't work.
428struct InvalidMessageDecrypter {}
429
430impl MessageDecrypter for InvalidMessageDecrypter {
431    fn decrypt<'a>(
432        &mut self,
433        _m: EncodedMessage<InboundOpaque<'a>>,
434        _seq: u64,
435    ) -> Result<EncodedMessage<&'a [u8]>, Error> {
436        Err(Error::DecryptError)
437    }
438}
439
440#[cfg(all(test, feature = "aws-lc-rs"))]
441pub(crate) struct FakeAead;
442
443#[cfg(all(test, feature = "aws-lc-rs"))]
444impl Tls12AeadAlgorithm for FakeAead {
445    fn encrypter(&self, _: AeadKey, _: &[u8], _: &[u8]) -> Box<dyn MessageEncrypter> {
446        todo!()
447    }
448
449    fn decrypter(&self, _: AeadKey, _: &[u8]) -> Box<dyn MessageDecrypter> {
450        todo!()
451    }
452
453    fn key_block_shape(&self) -> KeyBlockShape {
454        todo!()
455    }
456
457    fn extract_keys(
458        &self,
459        _: AeadKey,
460        _: &[u8],
461        _: &[u8],
462    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
463        Err(UnsupportedOperationError)
464    }
465
466    fn fips(&self) -> bool {
467        false
468    }
469}
470
471#[cfg(test)]
472mod tests {
473    use super::*;
474
475    /// Using test values provided in the spec in
476    /// <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-15.html#section-2.4>
477    #[test]
478    fn multipath_nonce() {
479        const PATH_ID: u32 = 3;
480        const PN: u64 = 54321;
481        const IV: [u8; 16] = 0x6b26114b9cba2b63a9e8dd4fu128.to_be_bytes();
482        const EXPECTED_NONCE: [u8; 16] = 0x6b2611489cba2b63a9e8097eu128.to_be_bytes();
483        let nonce = Nonce::quic(Some(PATH_ID), &Iv::new(&IV[4..]).unwrap(), PN);
484        assert_eq!(&EXPECTED_NONCE[4..], nonce.as_bytes());
485    }
486
487    #[test]
488    fn iv_len() {
489        let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
490        assert_eq!(iv.len(), NONCE_LEN);
491
492        let short_iv = Iv::new(&[1u8, 2, 3]).unwrap();
493        assert_eq!(short_iv.len(), 3);
494
495        let empty_iv = Iv::new(&[]).unwrap();
496        assert_eq!(empty_iv.len(), 0);
497    }
498
499    #[test]
500    fn iv_as_ref() {
501        let iv_data = [1u8, 2, 3, 4, 5];
502        let iv = Iv::new(&iv_data).unwrap();
503        let iv_ref: &[u8] = iv.as_ref();
504        assert_eq!(iv_ref, &iv_data);
505    }
506
507    #[test]
508    fn nonce_with_short_iv() {
509        let short_iv = Iv::new(&[0xAA, 0xBB, 0xCC, 0xDD]).unwrap();
510        let seq = 0x1122334455667788u64;
511        let nonce = Nonce::new(&short_iv, seq);
512
513        // The nonce should XOR the last 4 bytes of seq with the IV
514        assert_eq!(nonce.len(), 4);
515        let seq_bytes = seq.to_be_bytes();
516        let expected = [
517            0xAA ^ seq_bytes[4],
518            0xBB ^ seq_bytes[5],
519            0xCC ^ seq_bytes[6],
520            0xDD ^ seq_bytes[7],
521        ];
522        assert_eq!(nonce.as_bytes(), &expected);
523    }
524
525    #[test]
526    fn nonce_len() {
527        let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
528        let nonce = Nonce::new(&iv, 42);
529        assert_eq!(nonce.len(), NONCE_LEN);
530
531        let short_iv = Iv::new(&[1u8, 2]).unwrap();
532        let short_nonce = Nonce::new(&short_iv, 42);
533        assert_eq!(short_nonce.len(), 2);
534    }
535
536    #[test]
537    fn nonce_as_ref() {
538        let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
539        let nonce = Nonce::new(&iv, 42);
540        let nonce_ref: &[u8] = nonce.as_ref();
541        assert_eq!(nonce_ref.len(), NONCE_LEN);
542    }
543
544    #[test]
545    fn nonce_to_array_correct_size() {
546        let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
547        let nonce = Nonce::new(&iv, 42);
548        let array: [u8; NONCE_LEN] = nonce.to_array().unwrap();
549        assert_eq!(array.len(), NONCE_LEN);
550    }
551
552    #[test]
553    fn nonce_to_array_wrong_size() {
554        let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
555        let nonce = Nonce::new(&iv, 42);
556        let result: Result<[u8; 16], Error> = nonce.to_array();
557        assert!(matches!(
558            result,
559            Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch {
560                expected: 16,
561                actual: NONCE_LEN
562            }))
563        ));
564    }
565
566    #[test]
567    fn nonce_to_array_variable_length_error() {
568        // Create an IV with a non-standard length (8 bytes instead of 12)
569        let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
570        let nonce = Nonce::new(&short_iv, 42);
571
572        // Attempting to convert to standard NONCE_LEN should fail
573        let result: Result<[u8; NONCE_LEN], Error> = nonce.to_array();
574        if let Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch { expected, actual })) =
575            result
576        {
577            assert_eq!(expected, NONCE_LEN);
578            assert_eq!(actual, 8);
579        } else {
580            panic!("Expected Error::ApiMisuse(NonceArraySizeMismatch)");
581        }
582
583        // But converting to the correct length should work
584        let result_correct: Result<[u8; 8], Error> = nonce.to_array();
585        assert!(result_correct.is_ok());
586    }
587
588    #[test]
589    fn nonce_xor_with_iv() {
590        let iv_data = [0xFFu8; NONCE_LEN];
591        let iv = Iv::new(&iv_data).unwrap();
592        let seq = 0x0000000000000001u64;
593        let nonce = Nonce::new(&iv, seq);
594
595        // The last byte should be 0xFF XOR 0x01 = 0xFE
596        let nonce_bytes = nonce.as_bytes();
597        assert_eq!(nonce_bytes[NONCE_LEN - 1], 0xFE);
598    }
599
600    #[test]
601    fn iv_length_exceeds_maximum() {
602        let too_long_iv = [0xAAu8; Iv::MAX_LEN + 1];
603        let result = Iv::new(&too_long_iv);
604
605        assert!(matches!(
606            result,
607            Err(Error::ApiMisuse(ApiMisuse::IvLengthExceedsMaximum {
608                actual: 17,
609                maximum: 16
610            }))
611        ));
612    }
613}