use super::schedule::HashAlg;
use crate::hash::{Digest, Hmac, Sha256, Sha384};
fn p_hash_impl<D: Digest>(secret: &[u8], seed: &[u8], out: &mut [u8]) {
let mut a = Hmac::<D>::new(secret).chain(seed).finalize();
let mut written = 0usize;
while written < out.len() {
let block = Hmac::<D>::new(secret)
.chain(a.as_ref())
.chain(seed)
.finalize();
let take = (out.len() - written).min(block.as_ref().len());
out[written..written + take].copy_from_slice(&block.as_ref()[..take]);
written += take;
if written >= out.len() {
break;
}
a = Hmac::<D>::new(secret).chain(a.as_ref()).finalize();
}
}
#[allow(dead_code)]
pub(crate) fn p_hash(hash: HashAlg, secret: &[u8], seed: &[u8], out: &mut [u8]) {
match hash {
HashAlg::Sha256 => p_hash_impl::<Sha256>(secret, seed, out),
HashAlg::Sha384 => p_hash_impl::<Sha384>(secret, seed, out),
}
}
#[allow(dead_code)]
pub(crate) fn prf(hash: HashAlg, secret: &[u8], label: &[u8], seed: &[u8], out: &mut [u8]) {
let mut combined = alloc::vec::Vec::with_capacity(label.len() + seed.len());
combined.extend_from_slice(label);
combined.extend_from_slice(seed);
p_hash(hash, secret, &combined, out);
}
#[allow(dead_code)]
pub(crate) fn master_secret(
hash: HashAlg,
premaster: &[u8],
client_random: &[u8; 32],
server_random: &[u8; 32],
) -> [u8; 48] {
let mut seed = [0u8; 64];
seed[..32].copy_from_slice(client_random);
seed[32..].copy_from_slice(server_random);
let mut out = [0u8; 48];
prf(hash, premaster, b"master secret", &seed, &mut out);
out
}
#[allow(dead_code)]
pub(crate) fn extended_master_secret(
hash: HashAlg,
premaster: &[u8],
session_hash: &[u8],
) -> [u8; 48] {
let mut out = [0u8; 48];
prf(
hash,
premaster,
b"extended master secret",
session_hash,
&mut out,
);
out
}
#[allow(dead_code)]
pub(crate) fn key_block(
hash: HashAlg,
master: &[u8; 48],
server_random: &[u8; 32],
client_random: &[u8; 32],
out: &mut [u8],
) {
let mut seed = [0u8; 64];
seed[..32].copy_from_slice(server_random);
seed[32..].copy_from_slice(client_random);
prf(hash, master, b"key expansion", &seed, out);
}
#[allow(dead_code)]
pub(crate) fn finished_verify_data(
hash: HashAlg,
master: &[u8; 48],
label: &[u8],
transcript_hash: &[u8],
) -> [u8; 12] {
let mut out = [0u8; 12];
prf(hash, master, label, transcript_hash, &mut out);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn master_secret_deterministic_and_48_bytes() {
let premaster = [0x42u8; 48];
let cr = [0x11u8; 32];
let sr = [0x22u8; 32];
let ms1 = master_secret(HashAlg::Sha256, &premaster, &cr, &sr);
let ms2 = master_secret(HashAlg::Sha256, &premaster, &cr, &sr);
assert_eq!(ms1, ms2, "master_secret must be deterministic");
assert_eq!(ms1.len(), 48);
let other_pm = [0x43u8; 48];
let ms3 = master_secret(HashAlg::Sha256, &other_pm, &cr, &sr);
assert_ne!(ms1, ms3);
let ms4 = master_secret(HashAlg::Sha256, &premaster, &sr, &cr);
assert_ne!(ms1, ms4);
let ms_sha384 = master_secret(HashAlg::Sha384, &premaster, &cr, &sr);
assert_ne!(ms1, ms_sha384);
}
#[test]
fn extended_master_secret_differs_from_legacy() {
let premaster = [0x42u8; 48];
let session_hash = [0xa5u8; 32];
let ems1 = extended_master_secret(HashAlg::Sha256, &premaster, &session_hash);
let ems2 = extended_master_secret(HashAlg::Sha256, &premaster, &session_hash);
assert_eq!(ems1, ems2, "EMS must be deterministic");
assert_eq!(ems1.len(), 48);
let mut other = session_hash;
other[0] ^= 1;
let ems3 = extended_master_secret(HashAlg::Sha256, &premaster, &other);
assert_ne!(ems1, ems3);
let cr = [0x11u8; 32];
let sr = [0x22u8; 32];
let legacy = master_secret(HashAlg::Sha256, &premaster, &cr, &sr);
let ems_sha384 = extended_master_secret(HashAlg::Sha384, &premaster, &[0xa5u8; 48]);
assert_ne!(ems1, legacy);
assert_ne!(ems1, ems_sha384);
}
#[test]
fn finished_verify_data_is_12_bytes() {
let master = [0x55u8; 48];
let transcript = [0xaau8; 32];
let vd_client =
finished_verify_data(HashAlg::Sha256, &master, b"client finished", &transcript);
let vd_server =
finished_verify_data(HashAlg::Sha256, &master, b"server finished", &transcript);
assert_eq!(vd_client.len(), 12);
assert_eq!(vd_server.len(), 12);
assert_ne!(vd_client, vd_server);
let vd_client_again =
finished_verify_data(HashAlg::Sha256, &master, b"client finished", &transcript);
assert_eq!(vd_client, vd_client_again);
let mut transcript2 = transcript;
transcript2[0] ^= 1;
let vd_other =
finished_verify_data(HashAlg::Sha256, &master, b"client finished", &transcript2);
assert_ne!(vd_client, vd_other);
}
#[test]
fn key_block_expansion() {
let master = [0x33u8; 48];
let cr = [0x77u8; 32];
let sr = [0x88u8; 32];
let mut kb1 = [0u8; 40];
key_block(HashAlg::Sha256, &master, &sr, &cr, &mut kb1);
let mut kb2 = [0u8; 40];
key_block(HashAlg::Sha256, &master, &sr, &cr, &mut kb2);
assert_eq!(kb1, kb2);
let mut kb3 = [0u8; 40];
key_block(HashAlg::Sha256, &master, &cr, &sr, &mut kb3);
assert_ne!(kb1, kb3);
let mut kb_long = [0u8; 80];
key_block(HashAlg::Sha256, &master, &sr, &cr, &mut kb_long);
assert_eq!(&kb_long[..40], &kb1[..]);
let mut kb_sha384 = [0u8; 40];
key_block(HashAlg::Sha384, &master, &sr, &cr, &mut kb_sha384);
assert_ne!(kb1, kb_sha384);
}
#[test]
fn p_hash_prefix_extension() {
let secret = b"secret";
let seed = b"seed-bytes";
let mut short = [0u8; 16];
let mut long = [0u8; 64];
p_hash(HashAlg::Sha256, secret, seed, &mut short);
p_hash(HashAlg::Sha256, secret, seed, &mut long);
assert_eq!(&long[..16], &short[..]);
let mut short_sha384 = [0u8; 16];
p_hash(HashAlg::Sha384, secret, seed, &mut short_sha384);
assert_ne!(short, short_sha384);
}
#[test]
fn prf_equals_p_hash_of_label_concat_seed() {
let secret = b"secret";
let label = b"master secret";
let seed = [0x99u8; 64];
let mut via_prf = [0u8; 48];
prf(HashAlg::Sha256, secret, label, &seed, &mut via_prf);
let mut combined = alloc::vec::Vec::with_capacity(label.len() + seed.len());
combined.extend_from_slice(label);
combined.extend_from_slice(&seed);
let mut via_p_hash = [0u8; 48];
p_hash(HashAlg::Sha256, secret, &combined, &mut via_p_hash);
assert_eq!(via_prf, via_p_hash);
}
}