use super::{codec, hs, CipherSuite};
use crate::crypto::sha2::{HashAlg, Output};
#[inline(never)]
pub(crate) fn expand_label(h: HashAlg, secret: &[u8], label: &[u8], context: &[u8], out: &mut [u8]) {
let mut info = Vec::with_capacity(4 + 6 + label.len() + context.len());
codec::put_u16(&mut info, out.len() as u16);
info.push((6 + label.len()) as u8);
info.extend_from_slice(b"tls13 ");
info.extend_from_slice(label);
codec::put_vec8(&mut info, context);
h.hkdf_expand(secret, &[&info], out);
}
#[inline(never)]
pub(crate) fn expand_label_vec(h: HashAlg, secret: &[u8], label: &[u8], context: &[u8], len: usize) -> Vec<u8> {
let mut out = vec![0u8; len];
expand_label(h, secret, label, context, &mut out);
out
}
#[inline(never)]
pub(crate) fn derive_secret(h: HashAlg, secret: &[u8], label: &[u8], transcript_hash: &[u8]) -> Vec<u8> {
expand_label_vec(h, secret, label, transcript_hash, h.output_len())
}
pub(crate) struct Schedule {
pub h: HashAlg,
pub client_hs: Vec<u8>,
pub server_hs: Vec<u8>,
master: Vec<u8>,
}
impl Drop for Schedule {
fn drop(&mut self) {
crate::util::zeroize(&mut self.client_hs);
crate::util::zeroize(&mut self.server_hs);
crate::util::zeroize(&mut self.master);
}
}
impl Schedule {
pub fn new(h: HashAlg, psk: Option<&[u8]>, shared: &[u8], hello_hash: &[u8]) -> Schedule {
let zeros = [0u8; 64];
let zeros = &zeros[..h.output_len()];
let early = h.hkdf_extract(zeros, psk.unwrap_or(zeros));
let empty = h.hash(&[]);
let derived = derive_secret(h, &early, b"derived", &empty);
let handshake = h.hkdf_extract(&derived, shared);
let client_hs = derive_secret(h, &handshake, b"c hs traffic", hello_hash);
let server_hs = derive_secret(h, &handshake, b"s hs traffic", hello_hash);
let derived2 = derive_secret(h, &handshake, b"derived", &empty);
let master = h.hkdf_extract(&derived2, zeros).to_vec();
Schedule { h, client_hs, server_hs, master }
}
pub fn app_secrets(&self, hash: &[u8]) -> (Vec<u8>, Vec<u8>) {
(derive_secret(self.h, &self.master, b"c ap traffic", hash), derive_secret(self.h, &self.master, b"s ap traffic", hash))
}
pub fn resumption_master(&self, hash: &[u8]) -> Vec<u8> {
derive_secret(self.h, &self.master, b"res master", hash)
}
pub fn exporter_master(&self, hash: &[u8]) -> Vec<u8> {
derive_secret(self.h, &self.master, b"exp master", hash)
}
}
pub(crate) fn finished_mac(h: HashAlg, base_key: &[u8], hash: &[u8]) -> Output {
let fk = expand_label_vec(h, base_key, b"finished", &[], h.output_len());
h.hmac(&fk, &[hash])
}
pub(crate) fn psk_binder(h: HashAlg, psk: &[u8], transcript_hash: &[u8]) -> Output {
let zeros = [0u8; 64];
let early = h.hkdf_extract(&zeros[..h.output_len()], psk);
let binder_key = derive_secret(h, &early, b"res binder", &h.hash(&[]));
finished_mac(h, &binder_key, transcript_hash)
}
pub(crate) fn ticket_psk(h: HashAlg, resumption_master: &[u8], nonce: &[u8]) -> Vec<u8> {
expand_label_vec(h, resumption_master, b"resumption", nonce, h.output_len())
}
pub(crate) fn next_traffic_secret(h: HashAlg, secret: &[u8]) -> Vec<u8> {
expand_label_vec(h, secret, b"traffic upd", &[], h.output_len())
}
pub(crate) fn traffic_key_iv(suite: CipherSuite, secret: &[u8]) -> (Vec<u8>, [u8; 12]) {
let h = suite.hash();
let key = expand_label_vec(h, secret, b"key", &[], suite.aead().key_len());
let mut iv = [0u8; 12];
expand_label(h, secret, b"iv", &[], &mut iv);
(key, iv)
}
pub(crate) fn message_hash(h: HashAlg, ch1: &[u8]) -> Vec<u8> {
codec::handshake_msg(hs::MESSAGE_HASH, &h.hash(ch1))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::{hex, unhex};
#[test]
fn rfc8448_simple_1rtt() {
let shared = unhex("8bd4054fb55b9d63fdfbacf9f04b9f0d35e6d63f537563efd46272900f89492d");
let ch = unhex("010000c00303cb34ecb1e78163ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283024dece7000006130113031302010000910000000b0009000006736572766572ff01000100000a00140012001d0017001800190100010101020103010400230000003300260024001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c002b0003020304000d0020001e040305030603020308040805080604010501060102010402050206020202002d00020101001c00024001");
let sh = unhex("020000560303a6af06a4121860dc5e6e60249cd34c95930c8ac5cb1434dac155772ed3e2692800130100002e00330024001d0020c9828876112095fe66762bdbf7c672e156d6cc253b833df1dd69b1b04e751f0f002b00020304");
let mut t = ch.clone();
t.extend_from_slice(&sh);
let h = HashAlg::Sha256;
let s = Schedule::new(h, None, &shared, &h.hash(&t));
assert_eq!(hex(&s.client_hs), "b3eddb126e067f35a780b3abf45e2d8f3b1a950738f52e9600746a0e27a55a21");
assert_eq!(hex(&s.server_hs), "b67b7d690cc16c4e75e54213cb2d37b4e9c912bcded9105d42befd59d391ad38");
let (key, iv) = traffic_key_iv(CipherSuite::Aes128GcmSha256, &s.server_hs);
assert_eq!(hex(&key), "3fce516009c21727d0f2e4e86ee403bc");
assert_eq!(hex(&iv), "5d313eb2671276ee13000b30");
}
#[test]
fn acvp_tls13_kdf_with_psk() {
let psk = unhex("56288B726C73829F7A3E47B103837C8139ACF552E7530C7A710B35ED41191698");
let dhe = unhex("EFFE9EC26AA29FD750DFA6A10B944D74071595B27EE88887D5E11C84590B5CC3");
let mut transcript = unhex("E9137679E582BA7C1DB41CF725F86C6D09C8C05F297BAD9A65B552EAF524FDE4");
transcript.extend(unhex("23ECCFD030790748C8F8D8A656FD98D717F1B62AF3712F97211D2070B499F98A"));
let h = HashAlg::Sha256;
let s = Schedule::new(h, Some(&psk), &dhe, &h.hash(&transcript));
assert_eq!(hex(&s.client_hs), "b32306c3ce9932c460a1fe6c0f060593974842036b96fa45049b7352e71c2ad2");
assert_eq!(hex(&s.server_hs), "22787f8ca269d34bc549ac8ba19f2040938a3aa370d7cc9d60f720882b88d01b");
transcript.extend(unhex("C750EDA6696CD101B142BD79E00E6AC8C5F2C0ABC78DD64F4D991326659E9299"));
let (c, sv) = s.app_secrets(&h.hash(&transcript));
assert_eq!(hex(&c), "47d7ea08397b5871154b0fe85584bcc30a87c69e84d69b56007c5b21f76493ba");
assert_eq!(hex(&sv), "efbdb0c873c0480da57307083839a8984be25b9a8545e4fca029940fe2800565");
}
}