boring2/quinn-boring/src/hkdf.rs

130 lines
4.6 KiB
Rust

use crate::error::{map_result, Error, Result};
use boring::hash::MessageDigest;
use boring_sys as bffi;
use bytes::{BufMut, BytesMut};
use std::sync::LazyLock;
/// The block size used by the supported digest algorithms (64).
pub(crate) const DIGEST_BLOCK_LEN: usize = bffi::SHA_CBLOCK as _;
// /// The digest size for SHA256 (32).
// pub(crate) const SHA256_DIGEST_LEN: usize = bffi::SHA256_DIGEST_LENGTH as _;
//
// /// The digest size for SHA384 (48).
// pub(crate) const SHA384_DIGEST_LEN: usize = bffi::SHA384_DIGEST_LENGTH as _;
/// Implementation of [HKDF](https://www.rfc-editor.org/rfc/rfc5869) used for
/// creating the initial secrets for
/// [QUIC](https://www.rfc-editor.org/rfc/rfc9001#section-5.2) and
/// [TLS_1.3](https://datatracker.ietf.org/doc/html/rfc9001#name-initial-secrets).
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) struct Hkdf(MessageDigest);
static SHA256: LazyLock<Hkdf> = LazyLock::new(|| Hkdf(MessageDigest::sha256()));
static SHA384: LazyLock<Hkdf> = LazyLock::new(|| Hkdf(MessageDigest::sha384()));
impl Hkdf {
pub(crate) fn sha256() -> Hkdf {
*SHA256
}
pub(crate) fn sha384() -> Hkdf {
*SHA384
}
/// The size of the digest in bytes.
#[inline]
pub(crate) fn digest_size(self) -> usize {
self.0.size()
}
/// Performs an HKDF extract (<https://tools.ietf.org/html/rfc5869#section-2.2>),
/// given the salt and the initial key material (IKM). Returns the slice of the 'out'
/// array containing the generated pseudorandom key (PRK).
#[inline]
pub(crate) fn extract(self, salt: &[u8], ikm: &[u8], out: &mut [u8]) -> Result<usize> {
if out.len() < self.digest_size() {
return Err(Error::invalid_input(format!(
"HKDF extract output array invalid size: {}",
out.len()
)));
}
let mut out_len = out.len();
unsafe {
map_result(bffi::HKDF_extract(
out.as_mut_ptr(),
&mut out_len,
self.0.as_ptr(),
ikm.as_ptr(),
ikm.len(),
salt.as_ptr(),
salt.len(),
))?;
Ok(out_len)
}
}
/// Performs the HKDF-Expand-Label function as defined in the
/// [TLS-1.3 spec](https://datatracker.ietf.org/doc/html/rfc8446#section-7.1). The
/// [HKDF-Expand-Label function](https://www.rfc-editor.org/rfc/rfc5869#section-2.3) takes
/// 4 explicit arguments (Secret, Label, Context, and Length), as well as implicit PRF
/// which is the hash function negotiated by TLS.
///
/// Its use in QUIC is only for deriving initial secrets for obfuscation, for calculating
/// packet protection keys and IVs from the corresponding packet protection secret and
/// key update in the same quic session. None of these uses need a Context (a zero-length
/// context is provided), so this argument is omitted here.
#[inline]
pub(crate) fn expand_label(self, secret: &[u8], label: &[u8], out: &mut [u8]) -> Result<()> {
// Convert the label to a structure required by HKDF_expand. Doing
// this inline rather than using the complex openssl Crypto ByteBuilder (CBB).
let label = {
const TLS_VERSION_LABEL: &[u8] = b"tls13 ";
// Initialize the builder for the label structure. Breakdown of the required capacity:
// 2-byte total length field +
// 1-byte for the length of the label +
// Label length +
// 1-byte for the length of the Context +
// 0-bytes for an empty context (QUIC does not use context).
let label_len = TLS_VERSION_LABEL.len() + label.len();
let builder_capacity = label_len + 4;
let mut builder = BytesMut::with_capacity(builder_capacity);
// Add the length of the output key in big-endian byte order.
builder.put_u16(out.len() as u16);
// Add a child containing the label.
builder.put_u8(label_len as u8);
builder.put(TLS_VERSION_LABEL);
builder.put(label);
// Add a child containing a zero hash.
builder.put_u8(0);
builder
};
self.expand(secret, &label, out)
}
#[inline]
pub(crate) fn expand(&self, prk: &[u8], info: &[u8], out: &mut [u8]) -> Result<()> {
unsafe {
map_result(bffi::HKDF_expand(
out.as_mut_ptr(),
out.len(),
self.0.as_ptr(),
prk.as_ptr(),
prk.len(),
info.as_ptr(),
info.len(),
))?;
}
Ok(())
}
}