Coverage Report

Created: 2025-08-29 06:30

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/aead/quic.rs
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2018 Brian Smith.
2
//
3
// Permission to use, copy, modify, and/or distribute this software for any
4
// purpose with or without fee is hereby granted, provided that the above
5
// copyright notice and this permission notice appear in all copies.
6
//
7
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15
//! QUIC Header Protection.
16
//!
17
//! See draft-ietf-quic-tls.
18
19
use crate::{
20
    aead::{aes, chacha},
21
    cpu, error, hkdf,
22
};
23
24
/// A key for generating QUIC Header Protection masks.
25
pub struct HeaderProtectionKey {
26
    inner: KeyInner,
27
    algorithm: &'static Algorithm,
28
}
29
30
#[allow(clippy::large_enum_variant, variant_size_differences)]
31
enum KeyInner {
32
    Aes(aes::Key),
33
    ChaCha20(chacha::Key),
34
}
35
36
impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
37
0
    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
38
0
        let mut key_bytes = [0; super::MAX_KEY_LEN];
39
0
        let algorithm = *okm.len();
40
0
        let key_bytes = &mut key_bytes[..algorithm.key_len()];
41
0
        okm.fill(key_bytes).unwrap();
42
0
        Self::new(algorithm, key_bytes).unwrap()
43
0
    }
44
}
45
46
impl HeaderProtectionKey {
47
    /// Create a new header protection key.
48
    ///
49
    /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
50
0
    pub fn new(
51
0
        algorithm: &'static Algorithm,
52
0
        key_bytes: &[u8],
53
0
    ) -> Result<Self, error::Unspecified> {
54
0
        Ok(Self {
55
0
            inner: (algorithm.init)(key_bytes, cpu::features())?,
56
0
            algorithm,
57
        })
58
0
    }
59
60
    /// Generate a new QUIC Header Protection mask.
61
    ///
62
    /// `sample` must be exactly `self.algorithm().sample_len()` bytes long.
63
0
    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified> {
64
0
        let sample = <&[u8; SAMPLE_LEN]>::try_from(sample)?;
65
66
0
        let out = (self.algorithm.new_mask)(&self.inner, *sample);
67
0
        Ok(out)
68
0
    }
69
70
    /// The key's algorithm.
71
    #[inline(always)]
72
0
    pub fn algorithm(&self) -> &'static Algorithm {
73
0
        self.algorithm
74
0
    }
75
}
76
77
const SAMPLE_LEN: usize = super::TAG_LEN;
78
79
/// QUIC sample for new key masks
80
pub type Sample = [u8; SAMPLE_LEN];
81
82
/// A QUIC Header Protection Algorithm.
83
pub struct Algorithm {
84
    init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
85
86
    new_mask: fn(key: &KeyInner, sample: Sample) -> [u8; 5],
87
88
    key_len: usize,
89
    id: AlgorithmID,
90
}
91
92
impl hkdf::KeyType for &'static Algorithm {
93
    #[inline]
94
0
    fn len(&self) -> usize {
95
0
        self.key_len()
96
0
    }
97
}
98
99
impl Algorithm {
100
    /// The length of the key.
101
    #[inline(always)]
102
0
    pub fn key_len(&self) -> usize {
103
0
        self.key_len
104
0
    }
105
106
    /// The required sample length.
107
    #[inline(always)]
108
0
    pub fn sample_len(&self) -> usize {
109
0
        SAMPLE_LEN
110
0
    }
111
}
112
113
derive_debug_via_id!(Algorithm);
114
115
#[derive(Debug, Eq, PartialEq)]
116
enum AlgorithmID {
117
    AES_128,
118
    AES_256,
119
    CHACHA20,
120
}
121
122
impl PartialEq for Algorithm {
123
0
    fn eq(&self, other: &Self) -> bool {
124
0
        self.id == other.id
125
0
    }
126
}
127
128
impl Eq for Algorithm {}
129
130
/// AES-128.
131
pub static AES_128: Algorithm = Algorithm {
132
    key_len: 16,
133
    init: aes_init_128,
134
    new_mask: aes_new_mask,
135
    id: AlgorithmID::AES_128,
136
};
137
138
/// AES-256.
139
pub static AES_256: Algorithm = Algorithm {
140
    key_len: 32,
141
    init: aes_init_256,
142
    new_mask: aes_new_mask,
143
    id: AlgorithmID::AES_256,
144
};
145
146
0
fn aes_init_128(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
147
0
    let key = key.try_into().map_err(|_| error::Unspecified)?;
148
0
    let aes_key = aes::Key::new(aes::KeyBytes::AES_128(key), cpu_features)?;
149
0
    Ok(KeyInner::Aes(aes_key))
150
0
}
151
152
0
fn aes_init_256(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
153
0
    let key = key.try_into().map_err(|_| error::Unspecified)?;
154
0
    let aes_key = aes::Key::new(aes::KeyBytes::AES_256(key), cpu_features)?;
155
0
    Ok(KeyInner::Aes(aes_key))
156
0
}
157
158
0
fn aes_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
159
0
    let aes_key = match key {
160
0
        KeyInner::Aes(key) => key,
161
0
        _ => unreachable!(),
162
    };
163
164
0
    aes_key.new_mask(sample)
165
0
}
166
167
/// ChaCha20.
168
pub static CHACHA20: Algorithm = Algorithm {
169
    key_len: chacha::KEY_LEN,
170
    init: chacha20_init,
171
    new_mask: chacha20_new_mask,
172
    id: AlgorithmID::CHACHA20,
173
};
174
175
0
fn chacha20_init(key: &[u8], _cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
176
0
    let chacha20_key: [u8; chacha::KEY_LEN] = key.try_into()?;
177
0
    Ok(KeyInner::ChaCha20(chacha::Key::new(chacha20_key)))
178
0
}
179
180
0
fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
181
0
    let chacha20_key = match key {
182
0
        KeyInner::ChaCha20(key) => key,
183
0
        _ => unreachable!(),
184
    };
185
186
0
    chacha20_key.new_mask(sample)
187
0
}