use rustls::crypto::{ActiveKeyExchange, SharedSecret, SupportedKxGroup};
use rustls::{Error, NamedGroup};
use symcrypt::ecc::{CurveType, EcKey, EcKeyUsage};
#[derive(Debug)]
pub struct KxGroup {
name: NamedGroup,
curve_type: CurveType,
}
pub struct KeyExchange {
state: EcKey,
name: NamedGroup,
curve_type: CurveType,
pub_key: Vec<u8>,
}
pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[
SECP384R1,
SECP256R1,
#[cfg(feature = "x25519")]
X25519,
];
#[cfg(feature = "x25519")]
pub const X25519: &dyn SupportedKxGroup = &KxGroup {
name: NamedGroup::X25519,
curve_type: CurveType::Curve25519,
};
pub const SECP256R1: &dyn SupportedKxGroup = &KxGroup {
name: NamedGroup::secp256r1,
curve_type: CurveType::NistP256,
};
pub const SECP384R1: &dyn SupportedKxGroup = &KxGroup {
name: NamedGroup::secp384r1,
curve_type: CurveType::NistP384,
};
impl SupportedKxGroup for KxGroup {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
let ec_key = EcKey::generate_key_pair(self.curve_type, EcKeyUsage::EcDh)
.map_err(|e| Error::General(format!("SymCrypt key generation failed: {:?}", e)))?;
let mut pub_key = ec_key
.export_public_key()
.map_err(|e| Error::General(format!("SymCrypt public key export failed: {:?}", e)))?;
match ec_key.get_curve_type() {
CurveType::NistP256 | CurveType::NistP384 => {
pub_key.insert(0, 0x04); }
CurveType::Curve25519 => {
}
CurveType::NistP521 => {
return Err(Error::General(
"NistP521 is not supported for key exchange".to_string(),
));
}
}
Ok(Box::new(KeyExchange {
state: ec_key,
name: self.name,
curve_type: self.curve_type,
pub_key,
}))
}
fn name(&self) -> NamedGroup {
self.name
}
}
impl ActiveKeyExchange for KeyExchange {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
let new_peer_pub_key = match self.curve_type {
CurveType::NistP256 | CurveType::NistP384 => {
if peer_pub_key.starts_with(&[0x04]) {
&peer_pub_key[1..] } else {
return Err(Error::General("Invalid public key".to_string()));
}
}
CurveType::Curve25519 => {
peer_pub_key
}
CurveType::NistP521 => {
return Err(Error::General(
"NistP521 is not supported for key exchange".to_string(),
));
}
};
let peer_ecdh =
match EcKey::set_public_key(self.curve_type, new_peer_pub_key, EcKeyUsage::EcDh) {
Ok(peer_ecdh) => peer_ecdh,
Err(symcrypt_error) => {
let custom_error_message = format!(
"SymCryptError: {}",
symcrypt_error );
return Err(Error::General(custom_error_message));
}
};
let secret_agreement = match EcKey::ecdh_secret_agreement(&self.state, peer_ecdh) {
Ok(secret_agreement) => secret_agreement,
Err(symcrypt_error) => {
let custom_error_message = format!(
"SymCryptError: {}",
symcrypt_error );
return Err(Error::General(custom_error_message));
}
};
Ok(SharedSecret::from(secret_agreement.as_slice()))
}
fn pub_key(&self) -> &[u8] {
self.pub_key.as_ref()
}
fn group(&self) -> NamedGroup {
self.name
}
}