mirror of https://github.com/ctz/rustls
232 lines
6.7 KiB
Rust
232 lines
6.7 KiB
Rust
use msgs::enums::{CipherSuite, HashAlgorithm, NamedCurve};
|
|
use msgs::handshake::KeyExchangeAlgorithm;
|
|
use msgs::handshake::CertificatePayload;
|
|
use msgs::handshake::ServerECDHParams;
|
|
use msgs::base::{Payload, PayloadU8};
|
|
use msgs::codec::{Reader, Codec};
|
|
|
|
extern crate ring;
|
|
extern crate untrusted;
|
|
|
|
#[allow(non_camel_case_types)]
|
|
#[derive(Debug)]
|
|
pub enum BulkAlgorithm {
|
|
AES_128_GCM,
|
|
AES_256_GCM
|
|
}
|
|
|
|
/* The result of a key exchange. This has our public key,
|
|
* and the agreed premaster secret. */
|
|
pub struct KeyExchangeResult {
|
|
pub pubkey: Vec<u8>,
|
|
pub premaster_secret: Vec<u8>
|
|
}
|
|
|
|
impl KeyExchangeResult {
|
|
pub fn ecdhe(kx_params: &Vec<u8>) -> Option<KeyExchangeResult> {
|
|
let mut rd = Reader::init(&kx_params);
|
|
let ecdh_params = ServerECDHParams::read(&mut rd).unwrap();
|
|
|
|
let alg = match ecdh_params.curve_params.named_curve {
|
|
NamedCurve::X25519 => &ring::agreement::X25519,
|
|
NamedCurve::secp256r1 => &ring::agreement::ECDH_P256,
|
|
NamedCurve::secp384r1 => &ring::agreement::ECDH_P384,
|
|
_ => unreachable!()
|
|
};
|
|
|
|
let rng = ring::rand::SystemRandom::new();
|
|
let ours = ring::agreement::EphemeralPrivateKey::generate(alg, &rng).unwrap();
|
|
|
|
/* Encode our public key. */
|
|
let mut pubkey = Vec::new();
|
|
pubkey.resize(ours.public_key_len(), 0u8);
|
|
ours.compute_public_key(pubkey.as_mut_slice()).unwrap();
|
|
|
|
/* Do the key agreement. */
|
|
let secret = ring::agreement::agree_ephemeral(
|
|
ours,
|
|
alg,
|
|
untrusted::Input::new(&ecdh_params.public.body).unwrap(),
|
|
(),
|
|
|v| { let mut r = Vec::new(); r.extend_from_slice(v); Ok(r) }
|
|
);
|
|
|
|
if secret.is_err() {
|
|
return None;
|
|
}
|
|
|
|
Some(KeyExchangeResult { pubkey: pubkey, premaster_secret: secret.unwrap() })
|
|
}
|
|
|
|
pub fn encode_public(&self) -> Payload {
|
|
/* This is a bodgey way of making an ECPoint. */
|
|
let ecpoint = PayloadU8 { body: self.pubkey.clone().into_boxed_slice() };
|
|
let mut body = Vec::new();
|
|
ecpoint.encode(&mut body);
|
|
Payload { body: body.into_boxed_slice() }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct SupportedCipherSuite {
|
|
pub suite: CipherSuite,
|
|
pub kx: KeyExchangeAlgorithm,
|
|
pub bulk: BulkAlgorithm,
|
|
pub hash: HashAlgorithm,
|
|
pub mac_key_len: usize,
|
|
pub enc_key_len: usize,
|
|
pub fixed_iv_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: &Vec<u8>) -> Option<KeyExchangeResult> {
|
|
match &self.kx {
|
|
&KeyExchangeAlgorithm::ECDHE_ECDSA |
|
|
&KeyExchangeAlgorithm::ECDHE_RSA => KeyExchangeResult::ecdhe(kx_params),
|
|
_ => unreachable!()
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
pub fn key_block_len(&self) -> usize {
|
|
(self.mac_key_len + self.enc_key_len + self.fixed_iv_len) * 2
|
|
}
|
|
}
|
|
|
|
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_RSA,
|
|
bulk: BulkAlgorithm::AES_128_GCM,
|
|
hash: HashAlgorithm::SHA256,
|
|
mac_key_len: 0,
|
|
enc_key_len: 16,
|
|
fixed_iv_len: 4
|
|
};
|
|
|
|
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_RSA,
|
|
bulk: BulkAlgorithm::AES_256_GCM,
|
|
hash: HashAlgorithm::SHA384,
|
|
mac_key_len: 0,
|
|
enc_key_len: 32,
|
|
fixed_iv_len: 4
|
|
};
|
|
|
|
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_ECDSA,
|
|
bulk: BulkAlgorithm::AES_128_GCM,
|
|
hash: HashAlgorithm::SHA256,
|
|
mac_key_len: 0,
|
|
enc_key_len: 16,
|
|
fixed_iv_len: 4
|
|
};
|
|
|
|
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_ECDSA,
|
|
bulk: BulkAlgorithm::AES_256_GCM,
|
|
hash: HashAlgorithm::SHA384,
|
|
mac_key_len: 0,
|
|
enc_key_len: 32,
|
|
fixed_iv_len: 4
|
|
};
|
|
|
|
pub static DEFAULT_CIPHERSUITES: [&'static SupportedCipherSuite; 4] = [
|
|
&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
|
|
];
|
|
|
|
/* These both O(N^2)! */
|
|
pub fn choose_ciphersuite_preferring_client(
|
|
client_suites: &Vec<CipherSuite>,
|
|
server_suites: &Vec<&'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: &Vec<CipherSuite>,
|
|
server_suites: &Vec<&'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_cert(all: &Vec<&'static SupportedCipherSuite>, _certs: &CertificatePayload)
|
|
-> Vec<&'static SupportedCipherSuite> {
|
|
/* NYI */
|
|
all.clone()
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
}
|