Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/rust/src/quic/crypto.rs
Line
Count
Source
1
/* Copyright (C) 2021 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
use aes::cipher::generic_array::GenericArray;
19
use aes::Aes128;
20
use aes::BlockEncrypt;
21
use aes::NewBlockCipher;
22
use aes_gcm::AeadInPlace;
23
use aes_gcm::Aes128Gcm;
24
use aes_gcm::NewAead;
25
use hkdf::Hkdf;
26
use sha2::Sha256;
27
28
pub const AES128_KEY_LEN: usize = 16;
29
pub const AES128_TAG_LEN: usize = 16;
30
pub const AES128_IV_LEN: usize = 12;
31
32
pub struct HeaderProtectionKey(Aes128);
33
34
impl HeaderProtectionKey {
35
1.11k
    fn new(secret: &[u8], version: u32) -> Self {
36
1.11k
        let hk = Hkdf::<Sha256>::from_prk(secret).unwrap();
37
1.11k
        let mut secret = [0u8; AES128_KEY_LEN];
38
1.11k
        let quichp = if version == 0x6b3343cf {
39
66
            b"quicv2 hp" as &[u8]
40
        } else {
41
1.04k
            b"quic hp" as &[u8]
42
        };
43
1.11k
        hkdf_expand_label(&hk, quichp, &mut secret, AES128_KEY_LEN as u16);
44
1.11k
        return Self(Aes128::new(GenericArray::from_slice(&secret)));
45
1.11k
    }
46
47
306
    pub fn decrypt_in_place(
48
306
        &self, sample: &[u8], first: &mut u8, packet_number: &mut [u8],
49
306
    ) -> Result<(), ()> {
50
306
        let mut mask = GenericArray::clone_from_slice(sample);
51
306
        self.0.encrypt_block(&mut mask);
52
53
306
        let (first_mask, pn_mask) = mask.split_first().unwrap();
54
55
306
        let bits = if (*first & 0x80) != 0 {
56
59
            0x0f // Long header: 4 bits masked
57
        } else {
58
247
            0x1f // Short header: 5 bits masked
59
        };
60
61
306
        *first ^= first_mask & bits;
62
306
        let pn_len = (*first & 0x03) as usize + 1;
63
64
783
        for (dst, m) in packet_number.iter_mut().zip(pn_mask).take(pn_len) {
65
783
            *dst ^= m;
66
783
        }
67
68
306
        Ok(())
69
306
    }
70
}
71
72
pub struct PacketKey {
73
    key: Aes128Gcm,
74
    iv: [u8; AES128_IV_LEN],
75
}
76
77
impl PacketKey {
78
1.11k
    fn new(secret: &[u8], version: u32) -> Self {
79
1.11k
        let hk = Hkdf::<Sha256>::from_prk(secret).unwrap();
80
1.11k
        let mut secret = [0u8; AES128_KEY_LEN];
81
1.11k
        let quickey = if version == 0x6b3343cf {
82
66
            b"quicv2 key" as &[u8]
83
        } else {
84
1.04k
            b"quic key" as &[u8]
85
        };
86
1.11k
        hkdf_expand_label(&hk, quickey, &mut secret, AES128_KEY_LEN as u16);
87
1.11k
        let key = Aes128Gcm::new(GenericArray::from_slice(&secret));
88
89
1.11k
        let mut r = PacketKey {
90
1.11k
            key,
91
1.11k
            iv: [0u8; AES128_IV_LEN],
92
1.11k
        };
93
1.11k
        let quiciv = if version == 0x6b3343cf {
94
66
            b"quicv2 iv" as &[u8]
95
        } else {
96
1.04k
            b"quic iv" as &[u8]
97
        };
98
1.11k
        hkdf_expand_label(&hk, quiciv, &mut r.iv, AES128_IV_LEN as u16);
99
1.11k
        return r;
100
1.11k
    }
101
102
306
    pub fn decrypt_in_place<'a>(
103
306
        &self, packet_number: u64, header: &[u8], payload: &'a mut [u8],
104
306
    ) -> Result<&'a [u8], ()> {
105
306
        if payload.len() < AES128_TAG_LEN {
106
0
            return Err(());
107
306
        }
108
306
        let mut nonce = [0; AES128_IV_LEN];
109
306
        nonce[4..].copy_from_slice(&packet_number.to_be_bytes());
110
3.67k
        for (nonce, inp) in nonce.iter_mut().zip(self.iv.iter()) {
111
3.67k
            *nonce ^= inp;
112
3.67k
        }
113
306
        let tag_pos = payload.len() - AES128_TAG_LEN;
114
306
        let (buffer, tag) = payload.split_at_mut(tag_pos);
115
306
        let taga = GenericArray::from_slice(tag);
116
306
        self.key
117
306
            .decrypt_in_place_detached(GenericArray::from_slice(&nonce), header, buffer, taga)
118
306
            .map_err(|_| ())?;
119
0
        Ok(&payload[..tag_pos])
120
306
    }
121
}
122
123
pub struct DirectionalKeys {
124
    pub header: HeaderProtectionKey,
125
    pub packet: PacketKey,
126
}
127
128
impl DirectionalKeys {
129
1.11k
    fn new(secret: &[u8], version: u32) -> Self {
130
1.11k
        Self {
131
1.11k
            header: HeaderProtectionKey::new(secret, version),
132
1.11k
            packet: PacketKey::new(secret, version),
133
1.11k
        }
134
1.11k
    }
135
}
136
137
pub struct QuicKeys {
138
    pub local: DirectionalKeys,
139
    pub remote: DirectionalKeys,
140
}
141
142
4.45k
fn hkdf_expand_label(hk: &Hkdf<Sha256>, label: &[u8], okm: &mut [u8], olen: u16) {
143
    const LABEL_PREFIX: &[u8] = b"tls13 ";
144
145
4.45k
    let output_len = u16::to_be_bytes(olen);
146
4.45k
    let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8);
147
4.45k
    let context_len = u8::to_be_bytes(0);
148
149
4.45k
    let info = &[
150
4.45k
        &output_len[..],
151
4.45k
        &label_len[..],
152
4.45k
        LABEL_PREFIX,
153
4.45k
        label,
154
4.45k
        &context_len[..],
155
4.45k
    ];
156
157
4.45k
    hk.expand_multi_info(info, okm).unwrap();
158
4.45k
}
159
160
472k
pub fn quic_keys_initial(version: u32, client_dst_connection_id: &[u8]) -> Option<QuicKeys> {
161
472k
    let salt = match version {
162
4
        0x51303530 => &[
163
4
            0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, 0x5C, 0xFC, 0xDB, 0xD3,
164
4
            0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45,
165
4
        ],
166
852
        0xff00_001d..=0xff00_0020 => &[
167
3
            // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#section-5.2
168
3
            0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
169
3
            0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
170
3
        ],
171
854
        0xfaceb002 | 0xff00_0017..=0xff00_001c => &[
172
49
            // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-23#section-5.2
173
49
            0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4,
174
49
            0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
175
49
        ],
176
849
        0x0000_0001 | 0xff00_0021..=0xff00_0022 => &[
177
468
            // https://www.rfc-editor.org/rfc/rfc9001.html#name-initial-secrets
178
468
            0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
179
468
            0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
180
468
        ],
181
33
        0x6b3343cf => &[
182
33
            // https://www.rfc-editor.org/rfc/rfc9369.html#section-3.3.1
183
33
            0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26,
184
33
            0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9,
185
33
        ],
186
        _ => {
187
472k
            return None;
188
        }
189
    };
190
557
    let hk = Hkdf::<Sha256>::new(Some(salt), client_dst_connection_id);
191
557
    let mut client_secret = [0u8; 32];
192
557
    hkdf_expand_label(&hk, b"client in", &mut client_secret, 32);
193
557
    let mut server_secret = [0u8; 32];
194
557
    hkdf_expand_label(&hk, b"server in", &mut server_secret, 32);
195
196
557
    return Some(QuicKeys {
197
557
        local: DirectionalKeys::new(&server_secret, version),
198
557
        remote: DirectionalKeys::new(&client_secret, version),
199
557
    });
200
472k
}