blob: b41beb0225e32423b077c43e41744b03328e523f [file] [log] [blame]
// Copyright 2026 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Supported cipher suites
use alloc::{boxed::Box, vec::Vec};
use core::marker::PhantomData;
use bssl_crypto::{
digest::{self, Algorithm},
hkdf, hmac,
tls12_prf::Tls12Prf,
};
use rustls::{
Error,
crypto::{
hmac::Tag,
tls12::Prf,
tls13::{Hkdf, HkdfExpander, OkmBlock, OutputLengthError},
},
version::TLS12,
};
pub(crate) struct Tls12PrfImpl<A>(PhantomData<fn() -> A>);
impl<A> Tls12PrfImpl<A> {
pub(crate) const fn new() -> Self {
Self(PhantomData)
}
}
impl<A: digest::Algorithm> Prf for Tls12PrfImpl<A> {
fn for_key_exchange(
&self,
output: &mut [u8; 48],
kx: Box<dyn rustls::crypto::ActiveKeyExchange>,
peer_pub_key: &[u8],
label: &[u8],
seed: &[u8],
) -> Result<(), Error> {
Tls12Prf::<A>::generate_secret(
kx.complete_for_tls_version(peer_pub_key, &TLS12)?
.secret_bytes(),
label,
seed,
None,
output,
)
.map_err(|_| Error::General("PRF failed".into()))
}
fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]) {
Tls12Prf::<A>::generate_secret(secret, label, seed, None, output)
.expect("for_secret should be infallible");
}
}
/// TLS 1.3 HKDF over SHA-256
pub(crate) const TLS13_HKDF_SHA256: &'static dyn Hkdf = &Tls13HkdfImpl::<digest::Sha256>::new();
/// TLS 1.3 HKDF over SHA-384
pub(crate) const TLS13_HKDF_SHA384: &'static dyn Hkdf = &Tls13HkdfImpl::<digest::Sha384>::new();
pub(crate) struct Tls13HkdfImpl<A>(PhantomData<fn() -> A>);
impl<A> Tls13HkdfImpl<A> {
pub(crate) const fn new() -> Self {
Self(PhantomData)
}
}
struct Tls13HkdfExpander<A> {
prk: hkdf::Prk,
_p: PhantomData<fn() -> A>,
}
impl<A: Tls13HkdfDigest> HkdfExpander for Tls13HkdfExpander<A> {
fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError> {
let info: Vec<_> = info.iter().copied().flatten().copied().collect();
self.prk
.expand_into(&info, output)
.map_err(|_| OutputLengthError)
}
fn expand_block(&self, info: &[&[u8]]) -> OkmBlock {
let mut buf = A::zero_secret().to_vec();
let info: Vec<_> = info.iter().copied().flatten().copied().collect();
self.prk
.expand_into(&info, &mut buf)
.expect("digest length should not be too long");
OkmBlock::new(&buf)
}
fn hash_len(&self) -> usize {
A::OUTPUT_LEN
}
}
trait Tls13HkdfDigest: digest::Algorithm {
fn hmac_sign(key: &[u8], data: &[u8]) -> Tag;
fn zero_secret() -> &'static [u8];
}
impl Tls13HkdfDigest for digest::Sha256 {
fn hmac_sign(key: &[u8], data: &[u8]) -> Tag {
Tag::new(&hmac::HmacSha256::mac(key, data))
}
fn zero_secret() -> &'static [u8] {
&[0; Self::OUTPUT_LEN]
}
}
impl Tls13HkdfDigest for digest::Sha384 {
fn hmac_sign(key: &[u8], data: &[u8]) -> Tag {
Tag::new(&hmac::HmacSha384::mac(key, data))
}
fn zero_secret() -> &'static [u8] {
&[0; Self::OUTPUT_LEN]
}
}
impl<A: 'static + Tls13HkdfDigest> Hkdf for Tls13HkdfImpl<A> {
fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander> {
let prk = hkdf::Hkdf::<A>::extract(
A::zero_secret(),
if let Some(salt) = salt {
hkdf::Salt::NonEmpty(salt)
} else {
hkdf::Salt::None
},
);
Box::new(Tls13HkdfExpander::<A> {
prk,
_p: PhantomData,
})
}
fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander> {
let prk = hkdf::Hkdf::<A>::extract(
secret,
if let Some(salt) = salt {
hkdf::Salt::NonEmpty(salt)
} else {
hkdf::Salt::None
},
);
Box::new(Tls13HkdfExpander::<A> {
prk,
_p: PhantomData,
})
}
fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander> {
let okm = okm.as_ref();
Box::new(Tls13HkdfExpander::<A> {
prk: hkdf::Prk::new::<A>(okm).expect("OKM size mismatch"),
_p: PhantomData,
})
}
fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> Tag {
A::hmac_sign(key.as_ref(), message)
}
}