1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use s2n_quic_core::crypto::{OneRttHeaderKey, OneRttKey};
header_key!(RingOneRttHeaderKey);
negotiated_crypto!(RingOneRttKey, RingOneRttHeaderKey);
impl OneRttKey for RingOneRttKey {
#[inline]
#[must_use]
fn derive_next_key(&self) -> Self {
Self(self.0.update())
}
}
impl OneRttHeaderKey for RingOneRttHeaderKey {}
#[cfg(test)]
mod tests {
use crate::cipher_suite::TLS_CHACHA20_POLY1305_SHA256;
use hex_literal::hex;
use ring::hkdf;
use s2n_quic_core::crypto::Key;
//= https://www.rfc-editor.org/rfc/rfc9001#section-A.5
//# In this example, TLS produces an application write secret from which
//# a server uses HKDF-Expand-Label to produce four values: a key, an IV,
//# a header protection key, and the secret that will be used after keys
//# are updated (this last value is not used further in this example).
//#
//# secret
//# = 9ac312a7f877468ebe69422748ad00a1
//# 5443f18203a07d6060f688f30f21632b
//#
//# key = HKDF-Expand-Label(secret, "quic key", "", 32)
//# = c6d98ff3441c3fe1b2182094f69caa2e
//# d4b716b65488960a7a984979fb23e1c8
//#
//# iv = HKDF-Expand-Label(secret, "quic iv", "", 12)
//# = e0459b3474bdd0e44a41c144
//#
//# hp = HKDF-Expand-Label(secret, "quic hp", "", 32)
//# = 25a282b9e82f06f21f488917a4fc8f1b
//# 73573685608597d0efcb076b0ab7a7a4
//#
//# ku = HKDF-Expand-Label(secret, "quic ku", "", 32)
//# = 1223504755036d556342ee9361d25342
//# 1a826c9ecdf3c7148684b36b714881f9
const SECRET: [u8; 32] =
hex!("9ac312a7f877468ebe69422748ad00a15443f18203a07d6060f688f30f21632b");
const KU_SECRET: [u8; 32] =
hex!("1223504755036d556342ee9361d253421a826c9ecdf3c7148684b36b714881f9");
// Prevent trivial success
const INVALID_SECRET: [u8; 32] =
hex!("0000000000000000000000000000000000000000000000000000000000000000");
/// Return ChaCha20 ciphers because these are the ciphers given in the RFC. The other cipher
/// implementations don't have RFC values we can test.
/// This is not exhaustive, but it does show that we are using the KDF and label correctly.
fn generate_ciphers(
secret: &[u8],
next_secret: &[u8],
) -> (TLS_CHACHA20_POLY1305_SHA256, TLS_CHACHA20_POLY1305_SHA256) {
// Create a cipher based on the initial secret
let key = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, secret);
let cipher = TLS_CHACHA20_POLY1305_SHA256::new(key);
// Create the cipher after a Key Update has occurred
let next_cipher = cipher.0.update();
// Create a cipher based on the expected post-update secret
let next_key = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, next_secret);
let expected_next_cipher = TLS_CHACHA20_POLY1305_SHA256::new(next_key);
(next_cipher, expected_next_cipher.0)
}
#[test]
fn test_key_update() {
let (next_cipher, expected_next_cipher) = generate_ciphers(&SECRET, &KU_SECRET);
// Encrypt two empty blocks to verify the ciphers are the same
let mut next_cipher_output = [0; 32];
let mut expected_cipher_output = [0; 32];
next_cipher
.encrypt(0, &[], &mut next_cipher_output[..])
.unwrap();
expected_next_cipher
.encrypt(0, &[], &mut expected_cipher_output[..])
.unwrap();
assert_eq!(next_cipher_output, expected_cipher_output);
}
#[test]
fn test_key_update_failure() {
let (next_cipher, expected_next_cipher) = generate_ciphers(&INVALID_SECRET, &KU_SECRET);
// Encrypt two empty blocks to verify the ciphers are the same
let mut next_cipher_output = [0; 32];
let mut expected_cipher_output = [0; 32];
next_cipher
.encrypt(0, &[], &mut next_cipher_output[..])
.unwrap();
expected_next_cipher
.encrypt(0, &[], &mut expected_cipher_output[..])
.unwrap();
assert!(next_cipher_output != expected_cipher_output);
}
}