use crate::crypto::ellipticcurve::{Curve, PrivateKey};
use crate::crypto::CipherSuite;
use crate::net::alert::TlsError;
use crate::net::client::ClientHello;
use crate::net::extensions::{
server::ServerExtension, ClientExtension, KeyShare, KeyShareEntry, NamedGroup,
ServerExtensions, SignatureScheme, SupportedVersions,
};
use crate::net::server::ServerConfig;
use crate::rand::RngCore;
use crate::utils::bytes::{self, ByteOrder};
use crate::utils::log;
use ibig::IBig;
use std::result::Result;
pub struct ServerHello<'a> {
pub random: [u8; 32],
pub legacy_session_id_echo: Option<&'a [u8]>,
pub cipher_suite: CipherSuite,
pub extensions: ServerExtensions,
}
impl<'a> ServerHello<'a> {
pub fn from_raw(buf: &[u8]) -> Result<ServerHello, TlsError> {
if buf.len() < 36 {
return Err(TlsError::IllegalParameter);
}
let legacy_version = ((buf[0] as u16) << 8) | buf[1] as u16;
if legacy_version != 0x0303 {
return Err(TlsError::ProtocolVersion);
}
let random: [u8; 32] = buf[2..34].try_into().unwrap();
let session_id_length = buf[34];
let mut consumed = 35;
let mut legacy_session_id_echo = None;
if session_id_length != 0 {
consumed += 32;
legacy_session_id_echo = Some(&buf[35..(35 + 32)]);
}
let cipher_suite =
CipherSuite::new(((buf[consumed] as u16) << 8) | (buf[consumed + 1] as u16))?;
consumed += 3;
let extensions_len = ((buf[consumed] as usize) << 8) | (buf[consumed + 1] as usize);
consumed += 2;
let extensions =
ServerExtensions::from_server_hello(&buf[consumed..(consumed + extensions_len)])?;
let mut tls13_is_supported = false;
for ext in extensions.as_vec().iter() {
if let ServerExtension::SupportedVersions(ext) = ext {
if ext.is_tls13_supported() {
tls13_is_supported = true;
}
break;
}
}
if !tls13_is_supported {
return Err(TlsError::ProtocolVersion);
}
Ok(ServerHello {
random,
legacy_session_id_echo,
cipher_suite,
extensions,
})
}
pub fn from_client_hello(
client_hello: &'a ClientHello,
rng: &mut dyn RngCore<IBig>,
config: &'a ServerConfig,
) -> Result<(ServerHello<'a>, PrivateKey), TlsError> {
let mut extensions = ServerExtensions::new();
let mut private_key = None;
let mut named_group = None;
let random: [u8; 32] = rng.bytes(32).try_into().unwrap();
for ext in client_hello.extensions.as_vec().iter() {
match ext {
ClientExtension::SupportedVersion(version) => {
if !version.is_tls13_supported() {
return Err(TlsError::InsufficientSecurity);
}
}
ClientExtension::KeyShare(key_share) => {
for key in key_share.0.iter() {
match key.group {
NamedGroup::X25519 => {
log::debug!("TLS key exchange using ECDHE with Curve25519");
let secret = rng.between(1, 32);
let pk = PrivateKey::new(Curve::curve25519(), secret);
let key_share_data = bytes::ibig_to_32bytes(
pk.get_public_key().point.x,
ByteOrder::Little,
);
private_key = Some(pk);
let kse =
KeyShareEntry::new(NamedGroup::X25519, key_share_data.to_vec());
extensions.push(ServerExtension::KeyShare(KeyShare::new(kse)));
named_group = Some(NamedGroup::X25519);
break;
}
NamedGroup::Secp256r1 => {
todo!("Add support for secp256 key exchange");
}
_ => {}
}
}
}
ClientExtension::ServerName(server_name) => {
if let Some(expected_server_name) = &config.server_name {
if expected_server_name != server_name.get() {
return Err(TlsError::UnrecognizedName);
}
}
}
ClientExtension::SignatureAlgorithms(sa) => {
let mut supported = false;
for sig_algo in sa.0.iter() {
if matches!(sig_algo, SignatureScheme::ecdsa_secp256r1_sha256) {
supported = true;
break;
}
}
if !supported {
return Err(TlsError::InsufficientSecurity);
}
}
ClientExtension::SupportedGroups(_sg) => (),
}
}
if named_group.is_none() {
log::error!("No supported group -> Hello Retry Request");
return Err(TlsError::InsufficientSecurity);
}
extensions.push(ServerExtension::SupportedVersions(
SupportedVersions::default(),
));
let mut cipher_suite_to_use = None;
for cs in client_hello.cipher_suites.iter() {
cipher_suite_to_use = Some(match cs {
CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => {
CipherSuite::TLS_CHACHA20_POLY1305_SHA256
}
CipherSuite::TLS_AES_256_GCM_SHA384 => CipherSuite::TLS_AES_256_GCM_SHA384,
CipherSuite::TLS_AES_128_GCM_SHA256 => CipherSuite::TLS_AES_128_GCM_SHA256,
CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV => continue,
});
if cipher_suite_to_use.unwrap() == CipherSuite::TLS_CHACHA20_POLY1305_SHA256 {
break;
}
}
let cipher_suite = match cipher_suite_to_use {
Some(cs) => cs,
None => return Err(TlsError::HandshakeFailure),
};
let private_key = match private_key {
Some(pk) => pk,
None => return Err(TlsError::HandshakeFailure),
};
log::debug!("TLS connection using {cipher_suite:?}");
Ok((
ServerHello {
random,
legacy_session_id_echo: client_hello.legacy_session_id_echo,
cipher_suite,
extensions,
},
private_key,
))
}
pub fn as_bytes(&self) -> Vec<u8> {
let mut out = vec![0x3, 0x3]; out.extend_from_slice(&self.random);
if let Some(session_id) = self.legacy_session_id_echo {
out.push(session_id.len() as u8);
out.extend_from_slice(session_id);
} else {
out.push(0);
}
let cs = self.cipher_suite as u16;
out.push((cs >> 8) as u8);
out.push(cs as u8);
out.push(00);
out.extend(self.extensions.as_bytes());
out
}
pub fn get_public_key_share(&self) -> Option<&KeyShareEntry> {
for ext in self.extensions.as_vec().iter() {
if let ServerExtension::KeyShare(key_share) = ext {
if !key_share.0.is_empty() {
return Some(&key_share.0[0]);
}
}
}
None
}
}