s2n_quic_core/crypto/
mod.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#![forbid(unsafe_code)]
5
6//! QUIC cryptography primitives and traits
7//!
8//! ## Decryption flow
9//!
10//! The lifecycle of a protected and encrypted payload follows the following flow:
11//!
12//! ```text
13//!             +----------------+
14//!             |ProtectedPayload|
15//!             +-------+--------+
16//!                     |
17//!                     | unprotect()
18//!                     |
19//!          +----------+------------+
20//!          |                       |
21//!          |                       v
22//!          |             +---------+-----------+
23//!          |             |TruncatedPacketNumber|
24//!          v             +---------+-----------+
25//! +--------+--------+              |
26//! |EncryptedPayload |              | expand(largest_acknowledged_packet_number)
27//! +--------+--------+              v
28//!          |                 +-----+------+
29//!          |                 |PacketNumber|
30//!          |                 +-----+------+
31//!          |                       |
32//!          +----------+------------+
33//!                     |
34//!                     |  decrypt()
35//!                     v
36//!              +------+---------+
37//!              |CleartextPayload|
38//!              +----------------+
39//! ```
40//!
41//! The implementation of the decryption flow looks like the following:
42//!
43//! ```rust,ignore
44//! let crypto = ..; // initialize crypto keys
45//! let protected_payload = ..; // decode the payload from the incoming packet
46//! let header_len = ..; // decode packet to derive header_len
47//! let largest_acknowledged_packet_number = ..; // fetch the largest packet number from connection state
48//!
49//! let (truncated_packet_number, encrypted_payload) = crate::crypto::unprotect(
50//!     &crypto,
51//!     largest_acknowledged_packet_number.space(),
52//!     header_len,
53//!     protected_payload,
54//! )?;
55//!
56//! let packet_number = truncated_packet_number.expand(largest_acknowledged_packet_number)?;
57//!
58//! let cleartext_payload = crate::crypto::decrypt(
59//!     &crypto,
60//!     packet_number,
61//!     encrypted_payload,
62//! )?;
63//! ```
64//!
65//! ## Encryption flow
66//!
67//! Inversely, a cleartext payload follows the following flow:
68//!
69//! ```text
70//! +----------------+                        +------------+
71//! |CleartextPayload|                        |PacketNumber|
72//! +-------+--------+                        +------+-----+
73//!         |                                        |
74//!         |                         +--------------+-----------------------------------+
75//!         |                         |                                                  |
76//!         |                         |   truncate(largest_acknowledged_packet_number)   |
77//!         |                         |                                                  |
78//!         |                         |                                                  |
79//!         |   encode()   +----------+----------+                                       |
80//!         +<-------------+TruncatedPacketNumber|                                       |
81//!         |              +----------+----------+                                       |
82//!         |                         |                                                  |
83//!         |                         | len()                                            |
84//!         |                         v                                                  |
85//!         |   apply_mask()  +-------+-------+                                          |
86//!         <-----------------+PacketNumberLen|                                          |
87//!         |                 +--+----+-------+                                          |
88//!         |                    |    |                                                  |
89//!         +--------------------(----+---------------+----------------------------------+
90//!                              |                    |
91//!                              |                    |  encrypt()
92//!                              |                    |
93//!                              |           +--------+-------+
94//!                              |           |EncryptedPayload|
95//!                              |           +--------+-------+
96//!                              |                    |
97//!                              |                    |
98//!                              +-----------+--------+
99//!                                          |
100//!                                          | protect()
101//!                                          |
102//!                                  +-------+--------+
103//!                                  |ProtectedPayload|
104//!                                  +----------------+
105//! ```
106//!
107//! The implementation of the encryption flow looks like the following:
108//!
109//! ```rust,ignore
110//! let crypto = ..; // initialize crypto keys
111//! let cleartext_payload = ..; // encode an outgoing packet
112//! let header_len = ..; // encode packet to derive header_len
113//! let packet_number = ..; // use the packet number from the outgoing packet
114//! let largest_acknowledged_packet_number = ..; // fetch the largest packet number from connection state
115//!
116//! let truncated_packet_number = packet_number.truncate(largest_acknowledged_packet_number).unwrap();
117//! cleartext_payload[header_len..].encode(truncated_packet_number);
118//! let packet_number_len = truncated_packet_number.len();
119//! cleartext_payload[0] &= packet_number_len.into_packet_tag_mask();
120//!
121//! let (encrypted_payload, remaining_payload) = crate::crypto::encrypt(
122//!     &crypto,
123//!     packet_number,
124//!     packet_number_len,
125//!     header_len,
126//!     cleartext_payload,
127//! )?;
128//!
129//! let protected_payload =
130//!     crate::crypto::protect(&crypto, encrypted_payload)?;
131//! ```
132//!
133
134pub mod application;
135pub mod handshake;
136pub mod header_crypto;
137pub mod initial;
138pub mod key;
139pub mod label;
140pub mod one_rtt;
141pub mod packet_protection;
142pub mod payload;
143pub mod retry;
144pub mod tls;
145pub mod zero_rtt;
146
147#[cfg(test)]
148mod tests;
149
150pub use application::*;
151pub use handshake::*;
152pub use header_crypto::*;
153pub use initial::*;
154pub use key::*;
155pub use one_rtt::*;
156pub use packet_protection::*;
157pub use payload::*;
158pub use retry::RetryKey;
159pub use zero_rtt::*;
160
161/// Trait which aggregates all Crypto types
162pub trait CryptoSuite {
163    type HandshakeKey: HandshakeKey;
164    type HandshakeHeaderKey: HandshakeHeaderKey;
165    type InitialKey: InitialKey<HeaderKey = Self::InitialHeaderKey>;
166    type InitialHeaderKey: InitialHeaderKey;
167    type OneRttKey: OneRttKey;
168    type OneRttHeaderKey: OneRttHeaderKey;
169    type ZeroRttKey: ZeroRttKey;
170    type ZeroRttHeaderKey: ZeroRttHeaderKey;
171    type RetryKey: RetryKey;
172}
173
174use crate::packet::number::{
175    PacketNumber, PacketNumberLen, PacketNumberSpace, TruncatedPacketNumber,
176};
177pub use s2n_codec::encoder::scatter;
178use s2n_codec::{DecoderBufferMut, DecoderError, Encoder, EncoderBuffer};
179
180/// Protects an `EncryptedPayload` into a `ProtectedPayload`
181#[inline]
182pub fn protect<'a, K: HeaderKey>(
183    crypto: &K,
184    payload: EncryptedPayload<'a>,
185) -> Result<ProtectedPayload<'a>, DecoderError> {
186    let sample = payload.header_protection_sample(crypto.sealing_sample_len())?;
187    let mask = crypto.sealing_header_protection_mask(sample);
188
189    Ok(apply_header_protection(mask, payload))
190}
191
192/// Removes packet protection from a `ProtectedPayload` into a `EncryptedPayload`
193/// and associated `TruncatedPacketNumber`
194#[inline]
195pub fn unprotect<'a, K: HeaderKey>(
196    crypto: &K,
197    space: PacketNumberSpace,
198    payload: ProtectedPayload<'a>,
199) -> Result<(TruncatedPacketNumber, EncryptedPayload<'a>), DecoderError> {
200    let sample = payload.header_protection_sample(crypto.opening_sample_len())?;
201    let mask = crypto.opening_header_protection_mask(sample);
202
203    remove_header_protection(space, mask, payload)
204}
205
206/// Encrypts a cleartext payload with a crypto key into a `EncryptedPayload`
207#[inline]
208pub fn encrypt<'a, K: Key>(
209    key: &mut K,
210    packet_number: PacketNumber,
211    packet_number_len: PacketNumberLen,
212    header_len: usize,
213    payload: scatter::Buffer<'a>,
214) -> Result<(EncryptedPayload<'a>, EncoderBuffer<'a>), packet_protection::Error> {
215    let header_with_pn_len = packet_number_len.bytesize() + header_len;
216
217    let (mut payload, extra) = payload.into_inner();
218
219    let inline_len = payload.len() - header_with_pn_len;
220
221    // reserve bytes for the extra chunk at the end
222    if let Some(extra) = extra.as_ref() {
223        payload.advance_position(extra.len());
224    }
225
226    // reserve bytes for the tag
227    payload.advance_position(key.tag_len());
228
229    let (payload, remaining) = payload.split_off();
230
231    debug_assert!(
232        header_with_pn_len < payload.len(),
233        "header len ({}) should be less than payload ({})",
234        header_with_pn_len,
235        payload.len()
236    );
237    let (header, body) = payload.split_at_mut(header_with_pn_len);
238    let mut body = EncoderBuffer::new(body);
239    body.advance_position(inline_len);
240    let mut body = scatter::Buffer::new_with_extra(body, extra);
241    key.encrypt(packet_number.as_crypto_nonce(), header, &mut body)?;
242
243    let encrypted_payload = EncryptedPayload::new(header_len, packet_number_len, payload);
244    let remaining = EncoderBuffer::new(remaining);
245
246    Ok((encrypted_payload, remaining))
247}
248
249/// Decrypts a `EncryptedPayload` into clear text
250#[inline]
251pub fn decrypt<'a, K: Key>(
252    key: &K,
253    packet_number: PacketNumber,
254    payload: EncryptedPayload<'a>,
255) -> Result<(DecoderBufferMut<'a>, DecoderBufferMut<'a>), packet_protection::Error> {
256    let (header, payload) = payload.split_mut();
257    key.decrypt(packet_number.as_crypto_nonce(), header, payload)?;
258
259    // remove the key tag from payload
260    let payload_len = payload.len() - key.tag_len();
261    let payload = &mut payload[0..payload_len];
262
263    Ok((header.into(), payload.into()))
264}