/* Copyright (C) 2021 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ use aes::cipher::generic_array::GenericArray; use aes::Aes128; use aes::BlockEncrypt; use aes::NewBlockCipher; use aes_gcm::AeadInPlace; use aes_gcm::Aes128Gcm; use aes_gcm::NewAead; use hkdf::Hkdf; use sha2::Sha256; pub const AES128_KEY_LEN: usize = 16; pub const AES128_TAG_LEN: usize = 16; pub const AES128_IV_LEN: usize = 12; pub struct HeaderProtectionKey(Aes128); impl HeaderProtectionKey { fn new(secret: &[u8], version: u32) -> Self { let hk = Hkdf::::from_prk(secret).unwrap(); let mut secret = [0u8; AES128_KEY_LEN]; let quichp = if version == 0x6b3343cf { b"quicv2 hp" as &[u8] } else { b"quic hp" as &[u8] }; hkdf_expand_label(&hk, quichp, &mut secret, AES128_KEY_LEN as u16); return Self(Aes128::new(GenericArray::from_slice(&secret))); } pub fn decrypt_in_place( &self, sample: &[u8], first: &mut u8, packet_number: &mut [u8], ) -> Result<(), ()> { let mut mask = GenericArray::clone_from_slice(sample); self.0.encrypt_block(&mut mask); let (first_mask, pn_mask) = mask.split_first().unwrap(); let bits = if (*first & 0x80) != 0 { 0x0f // Long header: 4 bits masked } else { 0x1f // Short header: 5 bits masked }; *first ^= first_mask & bits; let pn_len = (*first & 0x03) as usize + 1; for (dst, m) in packet_number.iter_mut().zip(pn_mask).take(pn_len) { *dst ^= m; } Ok(()) } } pub struct PacketKey { key: Aes128Gcm, iv: [u8; AES128_IV_LEN], } impl PacketKey { fn new(secret: &[u8], version: u32) -> Self { let hk = Hkdf::::from_prk(secret).unwrap(); let mut secret = [0u8; AES128_KEY_LEN]; let quickey = if version == 0x6b3343cf { b"quicv2 key" as &[u8] } else { b"quic key" as &[u8] }; hkdf_expand_label(&hk, quickey, &mut secret, AES128_KEY_LEN as u16); let key = Aes128Gcm::new(GenericArray::from_slice(&secret)); let mut r = PacketKey { key, iv: [0u8; AES128_IV_LEN], }; let quiciv = if version == 0x6b3343cf { b"quicv2 iv" as &[u8] } else { b"quic iv" as &[u8] }; hkdf_expand_label(&hk, quiciv, &mut r.iv, AES128_IV_LEN as u16); return r; } pub fn decrypt_in_place<'a>( &self, packet_number: u64, header: &[u8], payload: &'a mut [u8], ) -> Result<&'a [u8], ()> { if payload.len() < AES128_TAG_LEN { return Err(()); } let mut nonce = [0; AES128_IV_LEN]; nonce[4..].copy_from_slice(&packet_number.to_be_bytes()); for (nonce, inp) in nonce.iter_mut().zip(self.iv.iter()) { *nonce ^= inp; } let tag_pos = payload.len() - AES128_TAG_LEN; let (buffer, tag) = payload.split_at_mut(tag_pos); let taga = GenericArray::from_slice(tag); self.key .decrypt_in_place_detached(GenericArray::from_slice(&nonce), header, buffer, taga) .map_err(|_| ())?; Ok(&payload[..tag_pos]) } } pub struct DirectionalKeys { pub header: HeaderProtectionKey, pub packet: PacketKey, } impl DirectionalKeys { fn new(secret: &[u8], version: u32) -> Self { Self { header: HeaderProtectionKey::new(secret, version), packet: PacketKey::new(secret, version), } } } pub struct QuicKeys { pub local: DirectionalKeys, pub remote: DirectionalKeys, } fn hkdf_expand_label(hk: &Hkdf, label: &[u8], okm: &mut [u8], olen: u16) { const LABEL_PREFIX: &[u8] = b"tls13 "; let output_len = u16::to_be_bytes(olen); let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8); let context_len = u8::to_be_bytes(0); let info = &[ &output_len[..], &label_len[..], LABEL_PREFIX, label, &context_len[..], ]; hk.expand_multi_info(info, okm).unwrap(); } pub fn quic_keys_initial(version: u32, client_dst_connection_id: &[u8]) -> Option { let salt = match version { 0x51303530 => &[ 0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, 0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45, ], 0xff00_001d..=0xff00_0020 => &[ // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#section-5.2 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99, ], 0xfaceb002 | 0xff00_0017..=0xff00_001c => &[ // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-23#section-5.2 0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02, ], 0x0000_0001 | 0xff00_0021..=0xff00_0022 => &[ // https://www.rfc-editor.org/rfc/rfc9001.html#name-initial-secrets 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a, ], 0x6b3343cf => &[ // https://www.rfc-editor.org/rfc/rfc9369.html#section-3.3.1 0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9, ], _ => { return None; } }; let hk = Hkdf::::new(Some(salt), client_dst_connection_id); let mut client_secret = [0u8; 32]; hkdf_expand_label(&hk, b"client in", &mut client_secret, 32); let mut server_secret = [0u8; 32]; hkdf_expand_label(&hk, b"server in", &mut server_secret, 32); return Some(QuicKeys { local: DirectionalKeys::new(&server_secret, version), remote: DirectionalKeys::new(&client_secret, version), }); }