| // 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) | |
| } | |
| } |