use crate::error::{last_error, Error, Result};
use crate::ffi::Ssl;
#[derive(Debug, Clone, Copy)]
pub(crate) enum Hash {
Sha256,
Sha384,
}
impl Hash {
fn md(self) -> *const aws_lc_sys::EVP_MD {
unsafe {
match self {
Self::Sha256 => aws_lc_sys::EVP_sha256(),
Self::Sha384 => aws_lc_sys::EVP_sha384(),
}
}
}
}
pub(crate) fn hkdf_expand_label(
hash: Hash,
secret: &[u8],
label: &str,
out: &mut [u8],
) -> Result<()> {
let full_label_len = "tls13 ".len() + label.len();
assert!(full_label_len <= 255, "kTLS labels fit in one byte");
let mut info = Vec::with_capacity(2 + 1 + full_label_len + 1);
let len_be = u16::try_from(out.len())
.expect("HKDF-Expand-Label output length fits in u16")
.to_be_bytes();
info.extend_from_slice(&len_be);
#[allow(clippy::cast_possible_truncation)]
info.push(full_label_len as u8);
info.extend_from_slice(b"tls13 ");
info.extend_from_slice(label.as_bytes());
info.push(0);
let rc = unsafe {
aws_lc_sys::HKDF_expand(
out.as_mut_ptr(),
out.len(),
hash.md(),
secret.as_ptr(),
secret.len(),
info.as_ptr(),
info.len(),
)
};
if rc == 1 {
Ok(())
} else {
Err(Error::Init(format!(
"HKDF_expand: rc={rc} {}",
last_error()
)))
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Direction {
Write,
Read,
}
pub(crate) fn tls13_traffic_secret(
ssl: &Ssl,
dir: Direction,
expected_len: usize,
) -> Result<Vec<u8>> {
let mut buf = vec![0u8; 48]; let mut got = buf.len();
let rc = unsafe {
match dir {
Direction::Write => aws_lc_sys::SSL_get_write_traffic_secret(
ssl.as_ptr(),
buf.as_mut_ptr(),
&raw mut got,
),
Direction::Read => aws_lc_sys::SSL_get_read_traffic_secret(
ssl.as_ptr(),
buf.as_mut_ptr(),
&raw mut got,
),
}
};
if rc != 1 || got != expected_len {
return Err(Error::Init(format!(
"SSL_get_{dir:?}_traffic_secret: rc={rc} len={got} want={expected_len}"
)));
}
buf.truncate(got);
Ok(buf)
}
pub(crate) fn tls12_key_block(ssl: &Ssl, len: usize) -> Result<Vec<u8>> {
let mut block = vec![0u8; len];
let rc = unsafe {
aws_lc_sys::SSL_generate_key_block(ssl.as_ptr(), block.as_mut_ptr(), block.len())
};
if rc != 1 {
return Err(Error::Init(format!(
"SSL_generate_key_block: rc={rc} {}",
last_error()
)));
}
Ok(block)
}
pub(crate) fn is_server(ssl: &Ssl) -> bool {
let v = unsafe { aws_lc_sys::SSL_is_server(ssl.as_ptr()) };
v != 0
}
pub(crate) fn sequences(ssl: &Ssl) -> (u64, u64) {
let write = unsafe { aws_lc_sys::SSL_get_write_sequence(ssl.as_ptr()) };
let read = unsafe { aws_lc_sys::SSL_get_read_sequence(ssl.as_ptr()) };
(write, read)
}
pub(crate) fn split_tls12_key_block(
block: &[u8],
key_len: usize,
iv_len: usize,
is_server: bool,
) -> (&[u8], &[u8], &[u8], &[u8]) {
debug_assert_eq!(block.len(), 2 * (key_len + iv_len));
let (client_key, rest) = block.split_at(key_len);
let (server_key, rest) = rest.split_at(key_len);
let (client_iv, server_iv) = rest.split_at(iv_len);
if is_server {
(server_key, server_iv, client_key, client_iv)
} else {
(client_key, client_iv, server_key, server_iv)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hkdf_expand_label_sha256_known_answer_key() {
let secret = [0u8; 32];
let mut out = [0u8; 16];
hkdf_expand_label(Hash::Sha256, &secret, "key", &mut out).unwrap();
let expected: [u8; 16] = [
0xcb, 0xee, 0x75, 0x71, 0xc6, 0x11, 0x03, 0x9c, 0xa3, 0x27, 0xa2, 0xe8, 0x79, 0xdf,
0xcd, 0x45,
];
assert_eq!(out, expected);
}
#[test]
fn hkdf_expand_label_sha384_known_answer_key() {
let secret = [0u8; 48];
let mut out = [0u8; 32];
hkdf_expand_label(Hash::Sha384, &secret, "key", &mut out).unwrap();
let expected: [u8; 32] = [
0xf6, 0x03, 0xd6, 0x8c, 0xd6, 0xfc, 0xca, 0xbb, 0xaa, 0x49, 0x69, 0xa9, 0xa6, 0x66,
0x14, 0x34, 0xe0, 0x18, 0xf2, 0x96, 0xf4, 0xcd, 0x03, 0x69, 0xc9, 0x34, 0x36, 0x3a,
0x58, 0x9a, 0x69, 0xea,
];
assert_eq!(out, expected);
}
#[test]
fn hkdf_expand_label_truncates() {
let secret = [0xab; 32];
let mut out = [0u8; 12];
hkdf_expand_label(Hash::Sha256, &secret, "iv", &mut out).unwrap();
assert!(out.iter().any(|&b| b != 0));
}
#[test]
fn split_tls12_key_block_server_perspective() {
let block: [u8; 12] = [
0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0xaa, 0xaa, 0xbb, 0xbb, ];
let (wk, wi, rk, ri) = split_tls12_key_block(&block, 4, 2, true);
assert_eq!(wk, &[0x22; 4]);
assert_eq!(wi, &[0xbb; 2]);
assert_eq!(rk, &[0x11; 4]);
assert_eq!(ri, &[0xaa; 2]);
}
#[test]
fn split_tls12_key_block_client_perspective() {
let block: [u8; 12] = [
0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0xaa, 0xaa, 0xbb, 0xbb,
];
let (wk, wi, rk, ri) = split_tls12_key_block(&block, 4, 2, false);
assert_eq!(wk, &[0x11; 4]);
assert_eq!(wi, &[0xaa; 2]);
assert_eq!(rk, &[0x22; 4]);
assert_eq!(ri, &[0xbb; 2]);
}
}