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,
&Mlkem768,
&Mlkem1024,
&Mlkem512,
&X25519,
&SecP256R1,
&SecP384R1,
];
pub static FIPS_KX_GROUPS: &[&'static dyn SupportedKxGroup] = &[
&X25519Mlkem768,
&Mlkem768,
&Mlkem1024,
&Mlkem512,
&SecP256R1,
&SecP384R1,
];
macro_rules! mlkem_pure_group {
($group:ident, $active:ident, $sname:ident, $set:ident, $ng:expr, $doc:expr) => {
#[doc = $doc]
#[derive(Debug)]
pub struct $group;
/// 进行中的纯 ML-KEM 客户端侧密钥交换(持有解封装密钥)。
pub(crate) struct $active {
dk: ferritls_core::mlkem::$set::DecapsKey,
share: Vec<u8>,
}
impl std::fmt::Debug for $active {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(stringify!($active))
}
}
impl SupportedKxGroup for $group {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
let (ek, dk) =
ferritls_core::mlkem::$set::generate_keypair().map_err(map_dh_err)?;
let share = ek.as_bytes().to_vec();
Ok(Box::new($active { dk, share }))
}
fn start_and_complete(
&self,
peer_pub_key: &[u8],
) -> Result<CompletedKeyExchange, RustlsError> {
let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
if peer_pub_key.len() != ferritls_core::mlkem::$set::EK_BYTES {
return Err(invalid());
}
let ek = ferritls_core::mlkem::$set::EncapsKey::from_bytes(peer_pub_key)
.map_err(|_| invalid())?;
let (ct, ss) = ferritls_core::mlkem::$set::encapsulate(&ek).map_err(map_dh_err)?;
Ok(CompletedKeyExchange {
group: $ng,
pub_key: ct.as_bytes().to_vec(),
secret: SharedSecret::from(ss.expose_bytes().to_vec()),
})
}
fn name(&self) -> NamedGroup {
$ng
}
fn fips(&self) -> bool {
false
}
}
impl ActiveKeyExchange for $active {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
if peer_pub_key.len() != ferritls_core::mlkem::$set::CT_BYTES {
return Err(invalid());
}
let ct = ferritls_core::mlkem::$set::Ciphertext::from_bytes(peer_pub_key)
.map_err(|_| invalid())?;
let ss = ferritls_core::mlkem::$set::decapsulate(&self.dk, &ct);
Ok(SharedSecret::from(ss.expose_bytes().to_vec()))
}
fn pub_key(&self) -> &[u8] {
&self.share
}
fn group(&self) -> NamedGroup {
$ng
}
}
#[doc = concat!(stringify!($group), " 组单例。")]
pub static $sname: &dyn SupportedKxGroup = &$group;
};
}
mlkem_pure_group!(
Mlkem512,
ActiveMlkem512,
MLKEM512_GROUP,
k512,
NamedGroup::MLKEM512,
"MLKEM512(draft-ietf-tls-mlkem-key-agreement,codepoint 0x0200)纯\nML-KEM 密钥交换组(M8.4):Cat 1 参数集(k = 2,η₁ = 3)。"
);
mlkem_pure_group!(
Mlkem768,
ActiveMlkem768,
MLKEM768_GROUP,
k768,
NamedGroup::MLKEM768,
"MLKEM768(draft-ietf-tls-mlkem-key-agreement,codepoint 0x0201)纯\nML-KEM 密钥交换组(M8.4):Cat 3 参数集(k = 3)。"
);
mlkem_pure_group!(
Mlkem1024,
ActiveMlkem1024,
MLKEM1024_GROUP,
k1024,
NamedGroup::MLKEM1024,
"MLKEM1024(draft-ietf-tls-mlkem-key-agreement,codepoint 0x0202)纯\nML-KEM 密钥交换组(M8.4):Cat 5 参数集(k = 4,du = 11)。"
);