Coverage Report

Created: 2026-05-23 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.16.2/src/aead/quic.rs
Line
Count
Source
1
// Copyright 2018 Brian Smith.
2
// SPDX-License-Identifier: ISC
3
// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6
//! QUIC Header Protection.
7
//!
8
//! See draft-ietf-quic-tls.
9
10
use crate::cipher::aes::encrypt_block;
11
use crate::cipher::block;
12
use crate::cipher::chacha::encrypt_block_chacha20;
13
use crate::cipher::key::SymmetricCipherKey;
14
use crate::hkdf::KeyType;
15
use crate::{derive_debug_via_id, error, hkdf};
16
17
/// A key for generating QUIC Header Protection masks.
18
pub struct HeaderProtectionKey {
19
    inner: SymmetricCipherKey,
20
    algorithm: &'static Algorithm,
21
}
22
23
impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
24
0
    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
25
0
        let mut key_bytes = [0; super::MAX_KEY_LEN];
26
0
        let algorithm = *okm.len();
27
0
        let key_bytes = &mut key_bytes[..algorithm.key_len()];
28
0
        okm.fill(key_bytes).unwrap();
29
0
        Self::new(algorithm, key_bytes).unwrap()
30
0
    }
31
}
32
33
impl HeaderProtectionKey {
34
    /// Create a new header protection key.
35
    ///
36
    /// # Errors
37
    /// `error::Unspecified` when `key_bytes` length is not `algorithm.key_len`
38
0
    pub fn new(
39
0
        algorithm: &'static Algorithm,
40
0
        key_bytes: &[u8],
41
0
    ) -> Result<Self, error::Unspecified> {
42
        Ok(Self {
43
0
            inner: (algorithm.init)(key_bytes)?,
44
0
            algorithm,
45
        })
46
0
    }
47
48
    /// Generate a new QUIC Header Protection mask.
49
    ///
50
    /// # Errors
51
    /// `error::Unspecified` when `sample` length is not `self.algorithm().sample_len()`.
52
    #[inline]
53
0
    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified> {
54
0
        let sample = <&[u8; SAMPLE_LEN]>::try_from(sample)?;
55
56
0
        cipher_new_mask(&self.inner, *sample)
57
0
    }
58
59
    /// The key's algorithm.
60
    #[inline]
61
    #[must_use]
62
0
    pub fn algorithm(&self) -> &'static Algorithm {
63
0
        self.algorithm
64
0
    }
65
}
66
67
const SAMPLE_LEN: usize = super::TAG_LEN;
68
69
/// QUIC sample for new key masks
70
pub type Sample = [u8; SAMPLE_LEN];
71
72
/// A QUIC Header Protection Algorithm.
73
pub struct Algorithm {
74
    init: fn(key: &[u8]) -> Result<SymmetricCipherKey, error::Unspecified>,
75
76
    key_len: usize,
77
    id: AlgorithmID,
78
}
79
80
impl KeyType for &'static Algorithm {
81
    #[inline]
82
0
    fn len(&self) -> usize {
83
0
        self.key_len()
84
0
    }
85
}
86
87
impl Algorithm {
88
    /// The length of the key.
89
    #[inline]
90
    #[must_use]
91
0
    pub fn key_len(&self) -> usize {
92
0
        self.key_len
93
0
    }
94
95
    /// The required sample length.
96
    #[inline]
97
    #[must_use]
98
0
    pub fn sample_len(&self) -> usize {
99
0
        SAMPLE_LEN
100
0
    }
101
}
102
103
derive_debug_via_id!(Algorithm);
104
105
#[derive(Debug, Eq, PartialEq)]
106
#[allow(non_camel_case_types)]
107
enum AlgorithmID {
108
    AES_128,
109
    AES_256,
110
    CHACHA20,
111
}
112
113
impl PartialEq for Algorithm {
114
    #[inline]
115
0
    fn eq(&self, other: &Self) -> bool {
116
0
        self.id == other.id
117
0
    }
118
}
119
120
impl Eq for Algorithm {}
121
122
/// AES-128.
123
pub const AES_128: Algorithm = Algorithm {
124
    key_len: 16,
125
    init: SymmetricCipherKey::aes128,
126
    id: AlgorithmID::AES_128,
127
};
128
129
/// AES-256.
130
pub const AES_256: Algorithm = Algorithm {
131
    key_len: 32,
132
    init: SymmetricCipherKey::aes256,
133
    id: AlgorithmID::AES_256,
134
};
135
136
/// `ChaCha20`.
137
pub const CHACHA20: Algorithm = Algorithm {
138
    key_len: 32,
139
    init: SymmetricCipherKey::chacha20,
140
    id: AlgorithmID::CHACHA20,
141
};
142
143
#[inline]
144
0
fn cipher_new_mask(
145
0
    cipher_key: &SymmetricCipherKey,
146
0
    sample: Sample,
147
0
) -> Result<[u8; 5], error::Unspecified> {
148
0
    let block = block::Block::from(sample);
149
150
0
    let encrypted_block = match cipher_key {
151
0
        SymmetricCipherKey::Aes128 { enc_key, .. }
152
0
        | SymmetricCipherKey::Aes192 { enc_key, .. }
153
0
        | SymmetricCipherKey::Aes256 { enc_key, .. } => encrypt_block(enc_key, block),
154
0
        SymmetricCipherKey::ChaCha20 { raw_key } => {
155
0
            let plaintext = block.as_ref();
156
0
            let counter_bytes: &[u8; 4] = plaintext[0..=3]
157
0
                .try_into()
158
0
                .map_err(|_| error::Unspecified)?;
159
0
            let nonce: &[u8; 12] = plaintext[4..=15]
160
0
                .try_into()
161
0
                .map_err(|_| error::Unspecified)?;
162
0
            let input = block::Block::zero();
163
0
            let counter = u32::from_ne_bytes(*counter_bytes).to_le();
164
0
            encrypt_block_chacha20(raw_key, input, nonce, counter)?
165
        }
166
    };
167
168
0
    let mut out: [u8; 5] = [0; 5];
169
0
    out.copy_from_slice(&encrypted_block.as_ref()[..5]);
170
0
    Ok(out)
171
0
}
172
173
#[cfg(test)]
174
mod test {
175
    use crate::aead::quic::{Algorithm, HeaderProtectionKey};
176
    use crate::test;
177
178
    #[test]
179
    fn test_types() {
180
        test::compile_time_assert_send::<Algorithm>();
181
        test::compile_time_assert_sync::<Algorithm>();
182
183
        test::compile_time_assert_send::<HeaderProtectionKey>();
184
        test::compile_time_assert_sync::<HeaderProtectionKey>();
185
    }
186
}