aws_lc_rs/aead/
quic.rs

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