Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/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::{BlockEncrypt, KeyInit};
19
use aes::Aes128;
20
use aes_gcm::aead::AeadInPlace;
21
use aes_gcm::Aes128Gcm;
22
use hkdf::Hkdf;
23
use sha2::Sha256;
24
use std::convert::TryInto;
25
26
pub const AES128_KEY_LEN: usize = 16;
27
pub const AES128_TAG_LEN: usize = 16;
28
pub const AES128_IV_LEN: usize = 12;
29
30
pub struct HeaderProtectionKey(Aes128);
31
32
impl HeaderProtectionKey {
33
1.55k
    fn new(secret: &[u8], version: u32) -> Self {
34
1.55k
        let hk = Hkdf::<Sha256>::from_prk(secret).unwrap();
35
1.55k
        let mut secret = [0u8; AES128_KEY_LEN];
36
1.55k
        let quichp = if version == 0x6b3343cf {
37
234
            b"quicv2 hp" as &[u8]
38
        } else {
39
1.32k
            b"quic hp" as &[u8]
40
        };
41
1.55k
        hkdf_expand_label(&hk, quichp, &mut secret, AES128_KEY_LEN as u16);
42
1.55k
        return Self(Aes128::new((&secret).into()));
43
1.55k
    }
44
45
1.23k
    pub fn decrypt_in_place(
46
1.23k
        &self, sample: &[u8], first: &mut u8, packet_number: &mut [u8],
47
1.23k
    ) -> Result<(), ()> {
48
1.23k
        debug_validate_bug_on!(sample.len() != AES128_KEY_LEN);
49
1.23k
        let mut mask: [u8; AES128_KEY_LEN] = sample[..AES128_KEY_LEN].try_into().map_err(|_| ())?;
50
1.23k
        self.0.encrypt_block((&mut mask).into());
51
52
1.23k
        let (first_mask, pn_mask) = mask.split_first().unwrap();
53
54
1.23k
        let bits = if (*first & 0x80) != 0 {
55
1.08k
            0x0f // Long header: 4 bits masked
56
        } else {
57
148
            0x1f // Short header: 5 bits masked
58
        };
59
60
1.23k
        *first ^= first_mask & bits;
61
1.23k
        let pn_len = (*first & 0x03) as usize + 1;
62
63
1.94k
        for (dst, m) in packet_number.iter_mut().zip(pn_mask).take(pn_len) {
64
1.94k
            *dst ^= m;
65
1.94k
        }
66
67
1.23k
        Ok(())
68
1.23k
    }
69
}
70
71
pub struct PacketKey {
72
    key: Aes128Gcm,
73
    iv: [u8; AES128_IV_LEN],
74
}
75
76
impl PacketKey {
77
1.55k
    fn new(secret: &[u8], version: u32) -> Self {
78
1.55k
        let hk = Hkdf::<Sha256>::from_prk(secret).unwrap();
79
1.55k
        let mut secret = [0u8; AES128_KEY_LEN];
80
1.55k
        let quickey = if version == 0x6b3343cf {
81
234
            b"quicv2 key" as &[u8]
82
        } else {
83
1.32k
            b"quic key" as &[u8]
84
        };
85
1.55k
        hkdf_expand_label(&hk, quickey, &mut secret, AES128_KEY_LEN as u16);
86
1.55k
        let key = Aes128Gcm::new((&secret).into());
87
88
1.55k
        let mut r = PacketKey {
89
1.55k
            key,
90
1.55k
            iv: [0u8; AES128_IV_LEN],
91
1.55k
        };
92
1.55k
        let quiciv = if version == 0x6b3343cf {
93
234
            b"quicv2 iv" as &[u8]
94
        } else {
95
1.32k
            b"quic iv" as &[u8]
96
        };
97
1.55k
        hkdf_expand_label(&hk, quiciv, &mut r.iv, AES128_IV_LEN as u16);
98
1.55k
        return r;
99
1.55k
    }
100
101
1.23k
    pub fn decrypt_in_place<'a>(
102
1.23k
        &self, packet_number: u64, header: &[u8], payload: &'a mut [u8],
103
1.23k
    ) -> Result<&'a [u8], ()> {
104
1.23k
        if payload.len() < AES128_TAG_LEN {
105
0
            return Err(());
106
1.23k
        }
107
1.23k
        let mut nonce = [0; AES128_IV_LEN];
108
1.23k
        nonce[4..].copy_from_slice(&packet_number.to_be_bytes());
109
14.8k
        for (nonce, inp) in nonce.iter_mut().zip(self.iv.iter()) {
110
14.8k
            *nonce ^= inp;
111
14.8k
        }
112
1.23k
        let tag_pos = payload.len() - AES128_TAG_LEN;
113
1.23k
        let (buffer, tag) = payload.split_at_mut(tag_pos);
114
1.23k
        self.key
115
1.23k
            .decrypt_in_place_detached((&nonce).into(), header, buffer, (&*tag).into())
116
1.23k
            .map_err(|_| ())?;
117
482
        Ok(&payload[..tag_pos])
118
1.23k
    }
119
}
120
121
pub struct DirectionalKeys {
122
    pub header: HeaderProtectionKey,
123
    pub packet: PacketKey,
124
}
125
126
impl DirectionalKeys {
127
1.55k
    fn new(secret: &[u8], version: u32) -> Self {
128
1.55k
        Self {
129
1.55k
            header: HeaderProtectionKey::new(secret, version),
130
1.55k
            packet: PacketKey::new(secret, version),
131
1.55k
        }
132
1.55k
    }
133
}
134
135
pub struct QuicKeys {
136
    pub local: DirectionalKeys,
137
    pub remote: DirectionalKeys,
138
}
139
140
6.22k
fn hkdf_expand_label(hk: &Hkdf<Sha256>, label: &[u8], okm: &mut [u8], olen: u16) {
141
    const LABEL_PREFIX: &[u8] = b"tls13 ";
142
143
6.22k
    let output_len = u16::to_be_bytes(olen);
144
6.22k
    let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8);
145
6.22k
    let context_len = u8::to_be_bytes(0);
146
147
6.22k
    let info = &[
148
6.22k
        &output_len[..],
149
6.22k
        &label_len[..],
150
6.22k
        LABEL_PREFIX,
151
6.22k
        label,
152
6.22k
        &context_len[..],
153
6.22k
    ];
154
155
6.22k
    hk.expand_multi_info(info, okm).unwrap();
156
6.22k
}
157
158
1.42M
pub fn quic_keys_initial(version: u32, client_dst_connection_id: &[u8]) -> Option<QuicKeys> {
159
1.42M
    let salt = match version {
160
37
        0x51303530 => &[
161
37
            0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, 0x5C, 0xFC, 0xDB, 0xD3,
162
37
            0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45,
163
37
        ],
164
337
        0xff00_001d..=0xff00_0020 => &[
165
9
            // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#section-5.2
166
9
            0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
167
9
            0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
168
9
        ],
169
340
        0xfaceb002 | 0xff00_0017..=0xff00_001c => &[
170
63
            // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-23#section-5.2
171
63
            0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4,
172
63
            0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
173
63
        ],
174
552
        0x0000_0001 | 0xff00_0021..=0xff00_0022 => &[
175
552
            // https://www.rfc-editor.org/rfc/rfc9001.html#name-initial-secrets
176
552
            0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
177
552
            0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
178
552
        ],
179
117
        0x6b3343cf => &[
180
117
            // https://www.rfc-editor.org/rfc/rfc9369.html#section-3.3.1
181
117
            0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26,
182
117
            0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9,
183
117
        ],
184
        _ => {
185
1.42M
            return None;
186
        }
187
    };
188
778
    let hk = Hkdf::<Sha256>::new(Some(salt), client_dst_connection_id);
189
778
    let mut client_secret = [0u8; 32];
190
778
    hkdf_expand_label(&hk, b"client in", &mut client_secret, 32);
191
778
    let mut server_secret = [0u8; 32];
192
778
    hkdf_expand_label(&hk, b"server in", &mut server_secret, 32);
193
194
778
    return Some(QuicKeys {
195
778
        local: DirectionalKeys::new(&server_secret, version),
196
778
        remote: DirectionalKeys::new(&client_secret, version),
197
778
    });
198
1.42M
}