use msgs::enums::{CipherSuite, HashAlgorithm, SignatureAlgorithm, NamedCurve};
use msgs::handshake::{SignatureAndHashAlgorithm, KeyExchangeAlgorithm};
use msgs::handshake::SupportedSignatureAlgorithms;
use msgs::handshake::{ClientECDHParams, ServerECDHParams};
use msgs::codec::{Reader, Codec};
use util;
use ring;
use untrusted;
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq)]
pub enum BulkAlgorithm {
AES_128_GCM,
AES_256_GCM,
CHACHA20_POLY1305
}
pub struct KeyExchangeResult {
pub pubkey: Vec<u8>,
pub premaster_secret: Vec<u8>
}
pub struct KeyExchange {
alg: &'static ring::agreement::Algorithm,
privkey: ring::agreement::EphemeralPrivateKey,
pub pubkey: Vec<u8>
}
impl KeyExchange {
pub fn named_curve_to_ecdh_alg(named_curve: &NamedCurve) -> Option<&'static ring::agreement::Algorithm> {
match named_curve {
&NamedCurve::X25519 => Some(&ring::agreement::X25519),
&NamedCurve::secp256r1 => Some(&ring::agreement::ECDH_P256),
&NamedCurve::secp384r1 => Some(&ring::agreement::ECDH_P384),
_ => None
}
}
pub fn client_ecdhe(kx_params: &[u8]) -> Option<KeyExchangeResult> {
let mut rd = Reader::init(&kx_params);
let ecdh_params = try_ret!(ServerECDHParams::read(&mut rd));
try_ret!(KeyExchange::start_ecdhe(&ecdh_params.curve_params.named_curve))
.complete(&ecdh_params.public.0)
}
pub fn start_ecdhe(named_curve: &NamedCurve) -> Option<KeyExchange> {
let alg = try_ret!(KeyExchange::named_curve_to_ecdh_alg(named_curve));
let rng = ring::rand::SystemRandom::new();
let ours = ring::agreement::EphemeralPrivateKey::generate(alg, &rng)
.unwrap();
let mut pubkey = Vec::new();
pubkey.resize(ours.public_key_len(), 0u8);
ours.compute_public_key(pubkey.as_mut_slice()).unwrap();
Some(KeyExchange { alg: alg, privkey: ours, pubkey: pubkey })
}
pub fn server_complete(self, kx_params: &[u8]) -> Option<KeyExchangeResult> {
let mut rd = Reader::init(kx_params);
let ecdh_params = ClientECDHParams::read(&mut rd).unwrap();
self.complete(&ecdh_params.public.0)
}
fn complete(self, peer: &[u8]) -> Option<KeyExchangeResult> {
let secret = ring::agreement::agree_ephemeral(
self.privkey,
self.alg,
untrusted::Input::from(peer),
(),
|v| { let mut r = Vec::new(); r.extend_from_slice(v); Ok(r) }
);
if secret.is_err() {
return None;
}
Some(KeyExchangeResult { pubkey: self.pubkey, premaster_secret: secret.unwrap() })
}
}
#[derive(Debug)]
pub struct SupportedCipherSuite {
pub suite: CipherSuite,
pub kx: KeyExchangeAlgorithm,
pub bulk: BulkAlgorithm,
pub hash: HashAlgorithm,
pub sign: SignatureAlgorithm,
pub mac_key_len: usize,
pub enc_key_len: usize,
pub fixed_iv_len: usize,
pub explicit_nonce_len: usize
}
impl PartialEq for SupportedCipherSuite {
fn eq(&self, other: &SupportedCipherSuite) -> bool {
self.suite == other.suite
}
}
impl SupportedCipherSuite {
pub fn get_hash(&self) -> &'static ring::digest::Algorithm {
match &self.hash {
&HashAlgorithm::SHA1 => &ring::digest::SHA1,
&HashAlgorithm::SHA256 => &ring::digest::SHA256,
&HashAlgorithm::SHA384 => &ring::digest::SHA384,
&HashAlgorithm::SHA512 => &ring::digest::SHA512,
_ => unreachable!()
}
}
pub fn do_client_kx(&self, kx_params: &[u8]) -> Option<KeyExchangeResult> {
match &self.kx {
&KeyExchangeAlgorithm::ECDHE => KeyExchange::client_ecdhe(kx_params),
_ => None
}
}
pub fn start_server_kx(&self, named_curve: &NamedCurve) -> Option<KeyExchange> {
match &self.kx {
&KeyExchangeAlgorithm::ECDHE => KeyExchange::start_ecdhe(named_curve),
_ => None
}
}
pub fn resolve_sig_alg(&self, sigalgs: &SupportedSignatureAlgorithms) -> Option<SignatureAndHashAlgorithm> {
let our_preference = vec![
SignatureAndHashAlgorithm { hash: self.hash, sign: self.sign },
SignatureAndHashAlgorithm { hash: HashAlgorithm::SHA512, sign: self.sign },
SignatureAndHashAlgorithm { hash: HashAlgorithm::SHA384, sign: self.sign },
SignatureAndHashAlgorithm { hash: HashAlgorithm::SHA256, sign: self.sign }
];
util::first_in_both(our_preference.as_slice(),
sigalgs.as_slice())
}
pub fn get_aead_alg(&self) -> &'static ring::aead::Algorithm {
match &self.bulk {
&BulkAlgorithm::AES_128_GCM => &ring::aead::AES_128_GCM,
&BulkAlgorithm::AES_256_GCM => &ring::aead::AES_256_GCM,
&BulkAlgorithm::CHACHA20_POLY1305 => &ring::aead::CHACHA20_POLY1305
}
}
pub fn key_block_len(&self) -> usize {
(self.mac_key_len + self.enc_key_len + self.fixed_iv_len) * 2 + self.explicit_nonce_len
}
}
pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite =
SupportedCipherSuite {
suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
kx: KeyExchangeAlgorithm::ECDHE,
sign: SignatureAlgorithm::ECDSA,
bulk: BulkAlgorithm::CHACHA20_POLY1305,
hash: HashAlgorithm::SHA256,
mac_key_len: 0,
enc_key_len: 32,
fixed_iv_len: 12,
explicit_nonce_len: 0
};
pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite =
SupportedCipherSuite {
suite: CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
kx: KeyExchangeAlgorithm::ECDHE,
sign: SignatureAlgorithm::RSA,
bulk: BulkAlgorithm::CHACHA20_POLY1305,
hash: HashAlgorithm::SHA256,
mac_key_len: 0,
enc_key_len: 32,
fixed_iv_len: 12,
explicit_nonce_len: 0
};
pub static TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite =
SupportedCipherSuite {
suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
kx: KeyExchangeAlgorithm::ECDHE,
sign: SignatureAlgorithm::RSA,
bulk: BulkAlgorithm::AES_128_GCM,
hash: HashAlgorithm::SHA256,
mac_key_len: 0,
enc_key_len: 16,
fixed_iv_len: 4,
explicit_nonce_len: 8
};
pub static TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite =
SupportedCipherSuite {
suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
kx: KeyExchangeAlgorithm::ECDHE,
sign: SignatureAlgorithm::RSA,
bulk: BulkAlgorithm::AES_256_GCM,
hash: HashAlgorithm::SHA384,
mac_key_len: 0,
enc_key_len: 32,
fixed_iv_len: 4,
explicit_nonce_len: 8
};
pub static TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite =
SupportedCipherSuite {
suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
kx: KeyExchangeAlgorithm::ECDHE,
sign: SignatureAlgorithm::ECDSA,
bulk: BulkAlgorithm::AES_128_GCM,
hash: HashAlgorithm::SHA256,
mac_key_len: 0,
enc_key_len: 16,
fixed_iv_len: 4,
explicit_nonce_len: 8
};
pub static TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite =
SupportedCipherSuite {
suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
kx: KeyExchangeAlgorithm::ECDHE,
sign: SignatureAlgorithm::ECDSA,
bulk: BulkAlgorithm::AES_256_GCM,
hash: HashAlgorithm::SHA384,
mac_key_len: 0,
enc_key_len: 32,
fixed_iv_len: 4,
explicit_nonce_len: 8
};
pub static ALL_CIPHERSUITES: [&'static SupportedCipherSuite; 6] = [
&TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
&TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
&TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
&TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
&TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
&TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
];
pub fn choose_ciphersuite_preferring_client(
client_suites: &[CipherSuite],
server_suites: &[&'static SupportedCipherSuite]) -> Option<&'static SupportedCipherSuite> {
for client_suite in client_suites {
if let Some(selected) = server_suites.iter().find(|x| *client_suite == x.suite) {
return Some(*selected);
}
}
None
}
pub fn choose_ciphersuite_preferring_server(
client_suites: &[CipherSuite],
server_suites: &[&'static SupportedCipherSuite]) -> Option<&'static SupportedCipherSuite> {
if let Some(selected) = server_suites.iter().find(|x| client_suites.contains(&x.suite)) {
return Some(*selected);
}
None
}
pub fn reduce_given_sigalg(all: &[&'static SupportedCipherSuite], sigalg: &SignatureAlgorithm)
-> Vec<&'static SupportedCipherSuite> {
all.iter()
.filter(|&&suite| &suite.sign == sigalg)
.cloned()
.collect()
}
#[cfg(test)]
mod test {
use msgs::enums::CipherSuite;
#[test]
fn test_client_pref() {
let client = vec![
CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
];
let server = vec![
&super::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
&super::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
];
let chosen = super::choose_ciphersuite_preferring_client(&client, &server);
assert!(chosen.is_some());
assert_eq!(chosen.unwrap(), &super::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
}
#[test]
fn test_server_pref() {
let client = vec![
CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
];
let server = vec![
&super::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
&super::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
];
let chosen = super::choose_ciphersuite_preferring_server(&client, &server);
assert!(chosen.is_some());
assert_eq!(chosen.unwrap(), &super::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384);
}
}