s2n_quic_core/crypto/
header_crypto.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    crypto::{EncryptedPayload, ProtectedPayload},
6    packet::number::{PacketNumberSpace, TruncatedPacketNumber},
7};
8use s2n_codec::{DecoderBuffer, DecoderError};
9
10/// Types for which are able to perform header cryptography.
11pub trait HeaderKey: Send {
12    /// Derives a header protection mask from a sample buffer, to be
13    /// used for opening a packet.
14    ///
15    /// The sample size is determined by the key function.
16    fn opening_header_protection_mask(&self, ciphertext_sample: &[u8]) -> HeaderProtectionMask;
17
18    /// Returns the sample size needed for the header protection
19    /// buffer
20    fn opening_sample_len(&self) -> usize;
21
22    /// Derives a header protection mask from a sample buffer, to be
23    /// used for sealing a packet.
24    ///
25    /// The sample size is determined by the key function.
26    fn sealing_header_protection_mask(&self, ciphertext_sample: &[u8]) -> HeaderProtectionMask;
27
28    /// Returns the sample size needed for the header protection
29    /// buffer
30    fn sealing_sample_len(&self) -> usize;
31}
32
33//= https://www.rfc-editor.org/rfc/rfc9001#section-5.4.1
34//# The output of this algorithm is a 5 byte mask that is applied to the
35//# protected header fields using exclusive OR.
36
37pub const HEADER_PROTECTION_MASK_LEN: usize = 5;
38pub type HeaderProtectionMask = [u8; HEADER_PROTECTION_MASK_LEN];
39
40//= https://www.rfc-editor.org/rfc/rfc9001#section-5.4.1
41//# Figure 6 shows a sample algorithm for applying header protection.
42//# Removing header protection only differs in the order in which the
43//# packet number length (pn_length) is determined (here "^" is used to
44//# represent exclusive OR).
45//#
46//# mask = header_protection(hp_key, sample)
47//#
48//# pn_length = (packet[0] & 0x03) + 1
49//# if (packet[0] & 0x80) == 0x80:
50//# # Long header: 4 bits masked
51//# packet[0] ^= mask[0] & 0x0f
52//# else:
53//# # Short header: 5 bits masked
54//# packet[0] ^= mask[0] & 0x1f
55//#
56//# # pn_offset is the start of the Packet Number field.
57//# packet[pn_offset:pn_offset+pn_length] ^= mask[1:1+pn_length]
58
59const LONG_HEADER_TAG: u8 = 0x80;
60pub(crate) const LONG_HEADER_MASK: u8 = 0x0f;
61pub(crate) const SHORT_HEADER_MASK: u8 = 0x1f;
62
63#[inline(always)]
64fn mask_from_packet_tag(tag: u8) -> u8 {
65    if tag & LONG_HEADER_TAG == LONG_HEADER_TAG {
66        LONG_HEADER_MASK
67    } else {
68        SHORT_HEADER_MASK
69    }
70}
71
72#[inline(always)]
73fn xor_mask(payload: &mut [u8], mask: &[u8]) {
74    for (payload_byte, mask_byte) in payload.iter_mut().zip(&mask[1..]) {
75        *payload_byte ^= mask_byte;
76    }
77}
78
79#[inline]
80pub(crate) fn apply_header_protection(
81    mask: HeaderProtectionMask,
82    payload: EncryptedPayload,
83) -> ProtectedPayload {
84    let header_len = payload.header_len;
85    let packet_number_len = payload.packet_number_len;
86    let payload = payload.buffer.into_less_safe_slice();
87
88    payload[0] ^= mask[0] & mask_from_packet_tag(payload[0]);
89
90    let header_with_pn_len = packet_number_len.bytesize() + header_len;
91    let packet_number_bytes = &mut payload[header_len..header_with_pn_len];
92    xor_mask(packet_number_bytes, &mask);
93
94    ProtectedPayload::new(header_len, payload)
95}
96
97#[inline]
98pub(crate) fn remove_header_protection(
99    space: PacketNumberSpace,
100    mask: HeaderProtectionMask,
101    payload: ProtectedPayload,
102) -> Result<(TruncatedPacketNumber, EncryptedPayload), DecoderError> {
103    let header_len = payload.header_len;
104    let payload = payload.buffer.into_less_safe_slice();
105
106    payload[0] ^= mask[0] & mask_from_packet_tag(payload[0]);
107    let packet_number_len = space.new_packet_number_len(payload[0]);
108
109    let header_with_pn_len = packet_number_len.bytesize() + header_len;
110    let packet_number = {
111        let packet_number_bytes = &mut payload[header_len..header_with_pn_len];
112        xor_mask(packet_number_bytes, &mask);
113
114        let (packet_number, _) = packet_number_len
115            .decode_truncated_packet_number(DecoderBuffer::new(packet_number_bytes))?;
116        packet_number
117    };
118
119    Ok((
120        packet_number,
121        EncryptedPayload::new(header_len, packet_number_len, payload),
122    ))
123}