/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 | } |