#![allow(dead_code)]
use crate::hash::{Digest, Md5, Sha1};
use crate::tls::ContentType;
use alloc::vec::Vec;
const MD5_PAD_LEN: usize = 48;
const SHA_PAD_LEN: usize = 40;
pub(crate) const SENDER_CLIENT: [u8; 4] = [0x43, 0x4c, 0x4e, 0x54]; pub(crate) const SENDER_SERVER: [u8; 4] = [0x53, 0x52, 0x56, 0x52];
fn salt(i: usize) -> Vec<u8> {
let letter = b'A' + (i as u8);
alloc::vec![letter; i + 1]
}
fn cascade_block(secret: &[u8], salt: &[u8], rand_a: &[u8; 32], rand_b: &[u8; 32]) -> [u8; 16] {
let mut inner = Sha1::new();
inner.update(salt);
inner.update(secret);
inner.update(rand_a);
inner.update(rand_b);
let inner = inner.finalize();
let mut outer = Md5::new();
outer.update(secret);
outer.update(inner.as_ref());
let mut out = [0u8; 16];
out.copy_from_slice(outer.finalize().as_ref());
out
}
pub(crate) fn ssl3_master_secret(
premaster: &[u8],
client_random: &[u8; 32],
server_random: &[u8; 32],
) -> [u8; 48] {
let mut out = [0u8; 48];
for i in 0..3 {
let block = cascade_block(premaster, &salt(i), client_random, server_random);
out[i * 16..i * 16 + 16].copy_from_slice(&block);
}
out
}
pub(crate) fn ssl3_key_block(
master: &[u8; 48],
server_random: &[u8; 32],
client_random: &[u8; 32],
out: &mut [u8],
) {
let mut written = 0;
let mut i = 0;
while written < out.len() {
let block = cascade_block(master, &salt(i), server_random, client_random);
let n = core::cmp::min(16, out.len() - written);
out[written..written + n].copy_from_slice(&block[..n]);
written += n;
i += 1;
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum Ssl3Mac {
Md5,
Sha1,
}
impl Ssl3Mac {
pub(crate) fn len(self) -> usize {
match self {
Ssl3Mac::Md5 => 16,
Ssl3Mac::Sha1 => 20,
}
}
fn pad_len(self) -> usize {
match self {
Ssl3Mac::Md5 => MD5_PAD_LEN,
Ssl3Mac::Sha1 => SHA_PAD_LEN,
}
}
}
pub(crate) fn ssl3_record_mac(
mac: Ssl3Mac,
secret: &[u8],
seq: u64,
ct: ContentType,
content: &[u8],
) -> Vec<u8> {
let pad1 = alloc::vec![0x36u8; mac.pad_len()];
let pad2 = alloc::vec![0x5cu8; mac.pad_len()];
let mut header = [0u8; 11];
header[..8].copy_from_slice(&seq.to_be_bytes());
header[8] = ct.as_u8();
header[9..11].copy_from_slice(&(content.len() as u16).to_be_bytes());
match mac {
Ssl3Mac::Md5 => {
let mut inner = Md5::new();
inner.update(secret);
inner.update(&pad1);
inner.update(&header);
inner.update(content);
let inner = inner.finalize();
let mut outer = Md5::new();
outer.update(secret);
outer.update(&pad2);
outer.update(inner.as_ref());
outer.finalize().as_ref().to_vec()
}
Ssl3Mac::Sha1 => {
let mut inner = Sha1::new();
inner.update(secret);
inner.update(&pad1);
inner.update(&header);
inner.update(content);
let inner = inner.finalize();
let mut outer = Sha1::new();
outer.update(secret);
outer.update(&pad2);
outer.update(inner.as_ref());
outer.finalize().as_ref().to_vec()
}
}
}
fn finished_half_md5(handshake: &[u8], sender: &[u8; 4], master: &[u8; 48]) -> [u8; 16] {
let pad1 = [0x36u8; MD5_PAD_LEN];
let pad2 = [0x5cu8; MD5_PAD_LEN];
let mut inner = Md5::new();
inner.update(handshake);
inner.update(sender);
inner.update(master);
inner.update(&pad1);
let inner = inner.finalize();
let mut outer = Md5::new();
outer.update(master);
outer.update(&pad2);
outer.update(inner.as_ref());
let mut out = [0u8; 16];
out.copy_from_slice(outer.finalize().as_ref());
out
}
fn finished_half_sha1(handshake: &[u8], sender: &[u8; 4], master: &[u8; 48]) -> [u8; 20] {
let pad1 = [0x36u8; SHA_PAD_LEN];
let pad2 = [0x5cu8; SHA_PAD_LEN];
let mut inner = Sha1::new();
inner.update(handshake);
inner.update(sender);
inner.update(master);
inner.update(&pad1);
let inner = inner.finalize();
let mut outer = Sha1::new();
outer.update(master);
outer.update(&pad2);
outer.update(inner.as_ref());
let mut out = [0u8; 20];
out.copy_from_slice(outer.finalize().as_ref());
out
}
pub(crate) fn ssl3_finished(handshake: &[u8], sender: &[u8; 4], master: &[u8; 48]) -> [u8; 36] {
let mut out = [0u8; 36];
out[..16].copy_from_slice(&finished_half_md5(handshake, sender, master));
out[16..].copy_from_slice(&finished_half_sha1(handshake, sender, master));
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn salts_grow() {
assert_eq!(salt(0), b"A");
assert_eq!(salt(1), b"BB");
assert_eq!(salt(2), b"CCC");
}
#[test]
fn master_secret_is_48_and_deterministic() {
let pm = [0x11u8; 48];
let cr = [0x22u8; 32];
let sr = [0x33u8; 32];
let m1 = ssl3_master_secret(&pm, &cr, &sr);
let m2 = ssl3_master_secret(&pm, &cr, &sr);
assert_eq!(m1, m2);
let m3 = ssl3_master_secret(&pm, &sr, &cr);
assert_ne!(m1, m3);
}
#[test]
fn key_block_fills_and_chains() {
let master = [0x44u8; 48];
let cr = [0x55u8; 32];
let sr = [0x66u8; 32];
let mut kb = [0u8; 72]; ssl3_key_block(&master, &sr, &cr, &mut kb);
let first = cascade_block(&master, b"A", &sr, &cr);
assert_eq!(&kb[..16], &first);
assert!(kb[64..].iter().any(|&b| b != 0));
}
#[test]
fn record_mac_lengths_and_seq_sensitivity() {
let secret = [0x77u8; 20];
let a = ssl3_record_mac(
Ssl3Mac::Sha1,
&secret,
0,
ContentType::ApplicationData,
b"hi",
);
let b = ssl3_record_mac(
Ssl3Mac::Sha1,
&secret,
1,
ContentType::ApplicationData,
b"hi",
);
assert_eq!(a.len(), 20);
assert_ne!(a, b, "sequence number must enter the MAC");
let m = ssl3_record_mac(
Ssl3Mac::Md5,
&[0x77u8; 16],
0,
ContentType::Handshake,
b"hi",
);
assert_eq!(m.len(), 16);
}
#[test]
fn finished_differs_by_sender() {
let master = [0x88u8; 48];
let hs = b"handshake transcript bytes";
let c = ssl3_finished(hs, &SENDER_CLIENT, &master);
let s = ssl3_finished(hs, &SENDER_SERVER, &master);
assert_ne!(c, s, "client and server Finished must differ");
assert_eq!(c.len(), 36);
}
}