use rustls::crypto::{ActiveKeyExchange, CompletedKeyExchange, SharedSecret, SupportedKxGroup};
use rustls::{Error as RustlsError, NamedGroup, PeerMisbehaved};
use ferritls_core::ecdh::{p256, p384, x25519};
use ferritls_core::mlkem;
fn map_dh_err(_: ferritls_core::Error) -> RustlsError {
RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare)
}
#[derive(Debug)]
pub struct X25519;
#[derive(Debug)]
pub struct SecP256R1;
#[derive(Debug)]
pub struct SecP384R1;
impl SupportedKxGroup for X25519 {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
let sk = x25519::SecretKey::generate().map_err(map_dh_err)?;
let pk = sk.public_key();
Ok(Box::new(ActiveX25519 { sk, pk }))
}
fn name(&self) -> NamedGroup {
NamedGroup::X25519
}
fn fips(&self) -> bool {
false
}
}
impl SupportedKxGroup for SecP256R1 {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
let sk = p256::SecretKey::generate().map_err(map_dh_err)?;
let pk = sk.public_key();
Ok(Box::new(ActiveSecP256R1 { sk, pk }))
}
fn name(&self) -> NamedGroup {
NamedGroup::secp256r1
}
fn fips(&self) -> bool {
false
}
}
impl SupportedKxGroup for SecP384R1 {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
let sk = p384::SecretKey::generate().map_err(map_dh_err)?;
let pk = sk.public_key();
Ok(Box::new(ActiveSecP384R1 { sk, pk }))
}
fn name(&self) -> NamedGroup {
NamedGroup::secp384r1
}
fn fips(&self) -> bool {
false
}
}
pub(crate) struct ActiveX25519 {
sk: x25519::SecretKey,
pk: [u8; 32],
}
pub(crate) struct ActiveSecP256R1 {
sk: p256::SecretKey,
pk: [u8; p256::PUBLIC_KEY_LEN],
}
pub(crate) struct ActiveSecP384R1 {
sk: p384::SecretKey,
pk: [u8; p384::PUBLIC_KEY_LEN],
}
impl std::fmt::Debug for ActiveX25519 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ActiveX25519")
}
}
impl std::fmt::Debug for ActiveSecP256R1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ActiveSecP256R1")
}
}
impl std::fmt::Debug for ActiveSecP384R1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ActiveSecP384R1")
}
}
impl ActiveKeyExchange for ActiveX25519 {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
let ss = self.sk.diffie_hellman(peer_pub_key).map_err(map_dh_err)?;
Ok(SharedSecret::from(ss.as_bytes().to_vec()))
}
fn pub_key(&self) -> &[u8] {
&self.pk
}
fn group(&self) -> NamedGroup {
NamedGroup::X25519
}
}
impl ActiveKeyExchange for ActiveSecP256R1 {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
let ss = self.sk.diffie_hellman(peer_pub_key).map_err(map_dh_err)?;
Ok(SharedSecret::from(ss.as_bytes().to_vec()))
}
fn pub_key(&self) -> &[u8] {
&self.pk
}
fn group(&self) -> NamedGroup {
NamedGroup::secp256r1
}
}
impl ActiveKeyExchange for ActiveSecP384R1 {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
let ss = self.sk.diffie_hellman(peer_pub_key).map_err(map_dh_err)?;
Ok(SharedSecret::from(ss.as_bytes().to_vec()))
}
fn pub_key(&self) -> &[u8] {
&self.pk
}
fn group(&self) -> NamedGroup {
NamedGroup::secp384r1
}
}
#[derive(Debug)]
pub struct X25519Mlkem768;
pub(crate) struct ActiveX25519Mlkem768 {
dk: mlkem::Mlkem768DecapsKey,
sk: x25519::SecretKey,
share: Vec<u8>,
}
impl SupportedKxGroup for X25519Mlkem768 {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
let (ek, dk) = mlkem::generate_keypair().map_err(map_dh_err)?;
let sk = x25519::SecretKey::generate().map_err(map_dh_err)?;
let mut share = Vec::with_capacity(mlkem::EK_BYTES + 32);
share.extend_from_slice(ek.as_bytes());
share.extend_from_slice(&sk.public_key());
Ok(Box::new(ActiveX25519Mlkem768 { dk, sk, share }))
}
fn start_and_complete(&self, peer_pub_key: &[u8]) -> Result<CompletedKeyExchange, RustlsError> {
let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
if peer_pub_key.len() != mlkem::EK_BYTES + 32 {
return Err(invalid());
}
let ek = mlkem::Mlkem768EncapsKey::from_bytes(&peer_pub_key[..mlkem::EK_BYTES])
.map_err(|_| invalid())?;
let (ct, ss_m) = mlkem::encapsulate(&ek).map_err(map_dh_err)?;
let sk = x25519::SecretKey::generate().map_err(map_dh_err)?;
let ss_x = self_x25519(&sk, &peer_pub_key[mlkem::EK_BYTES..])?;
let mut secret = Vec::with_capacity(mlkem::SS_BYTES + 32);
secret.extend_from_slice(ss_m.expose_bytes());
secret.extend_from_slice(ss_x.as_bytes());
let mut pub_key = Vec::with_capacity(mlkem::CT_BYTES + 32);
pub_key.extend_from_slice(ct.as_bytes());
pub_key.extend_from_slice(&sk.public_key());
Ok(CompletedKeyExchange {
group: NamedGroup::X25519MLKEM768,
pub_key,
secret: SharedSecret::from(secret),
})
}
fn name(&self) -> NamedGroup {
NamedGroup::X25519MLKEM768
}
fn fips(&self) -> bool {
false
}
}
impl ActiveKeyExchange for ActiveX25519Mlkem768 {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
if peer_pub_key.len() != mlkem::CT_BYTES + 32 {
return Err(invalid());
}
let ct = mlkem::Mlkem768Ciphertext::from_bytes(&peer_pub_key[..mlkem::CT_BYTES])
.map_err(|_| invalid())?;
let ss_m = mlkem::decapsulate(&self.dk, &ct);
let ss_x = self_x25519(&self.sk, &peer_pub_key[mlkem::CT_BYTES..])?;
let mut secret = Vec::with_capacity(mlkem::SS_BYTES + 32);
secret.extend_from_slice(ss_m.expose_bytes());
secret.extend_from_slice(ss_x.as_bytes());
Ok(SharedSecret::from(secret))
}
fn pub_key(&self) -> &[u8] {
&self.share
}
fn group(&self) -> NamedGroup {
NamedGroup::X25519MLKEM768
}
}
fn self_x25519(sk: &x25519::SecretKey, peer: &[u8]) -> Result<x25519::SharedSecret, RustlsError> {
sk.diffie_hellman(peer).map_err(map_dh_err)
}
pub static X25519_GROUP: &dyn SupportedKxGroup = &X25519;
pub static SECP256R1_GROUP: &dyn SupportedKxGroup = &SecP256R1;
pub static SECP384R1_GROUP: &dyn SupportedKxGroup = &SecP384R1;
pub static X25519MLKEM768_GROUP: &dyn SupportedKxGroup = &X25519Mlkem768;
pub static ALL_KX_GROUPS: &[&'static dyn SupportedKxGroup] =
&[&X25519Mlkem768, &X25519, &SecP256R1, &SecP384R1];
pub static FIPS_KX_GROUPS: &[&'static dyn SupportedKxGroup] =
&[&X25519Mlkem768, &SecP256R1, &SecP384R1];