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}