mirror of https://github.com/ctz/rustls
304 lines
9.1 KiB
Rust
304 lines
9.1 KiB
Rust
extern crate ring;
|
|
use prf;
|
|
use std::io::Write;
|
|
use msgs::codec;
|
|
use msgs::codec::Codec;
|
|
use msgs::message::{Message, MessagePayload};
|
|
use suites::SupportedCipherSuite;
|
|
|
|
pub struct SessionSecrets {
|
|
pub we_are_client: bool,
|
|
pub client_random: [u8; 32],
|
|
pub server_random: [u8; 32],
|
|
hash: Option<&'static ring::digest::Algorithm>,
|
|
master_secret: [u8; 48]
|
|
}
|
|
|
|
fn join_randoms(first: &[u8], second: &[u8]) -> [u8; 64] {
|
|
let mut randoms = [0u8; 64];
|
|
randoms.as_mut().write(first).unwrap();
|
|
randoms[32..].as_mut().write(second).unwrap();
|
|
randoms
|
|
}
|
|
|
|
|
|
impl SessionSecrets {
|
|
pub fn for_server() -> SessionSecrets {
|
|
SessionSecrets {
|
|
we_are_client: false,
|
|
hash: None,
|
|
client_random: [0u8; 32],
|
|
server_random: [0u8; 32],
|
|
master_secret: [0u8; 48]
|
|
}
|
|
}
|
|
|
|
pub fn for_client() -> SessionSecrets {
|
|
let mut ret = SessionSecrets::for_server();
|
|
ret.we_are_client = true;
|
|
ret
|
|
}
|
|
|
|
pub fn get_master_secret(&self) -> Vec<u8> {
|
|
let mut ret = Vec::new();
|
|
ret.extend_from_slice(&self.master_secret);
|
|
ret
|
|
}
|
|
|
|
pub fn init(&mut self,
|
|
hs_rands: &SessionSecrets,
|
|
hashalg: &'static ring::digest::Algorithm,
|
|
pms: &[u8]) {
|
|
/* Copy in randoms. */
|
|
self.client_random.as_mut().write(&hs_rands.client_random).unwrap();
|
|
self.server_random.as_mut().write(&hs_rands.server_random).unwrap();
|
|
|
|
self.hash = Some(hashalg);
|
|
|
|
let randoms = join_randoms(&self.client_random, &self.server_random);
|
|
dumphex("clientrand", &self.client_random);
|
|
dumphex("serverrand", &self.server_random);
|
|
dumphex("premaster", pms);
|
|
prf::prf(&mut self.master_secret,
|
|
hashalg,
|
|
pms,
|
|
b"master secret",
|
|
&randoms);
|
|
dumphex("master", &self.master_secret);
|
|
}
|
|
|
|
pub fn init_resume(&mut self,
|
|
hs_rands: &SessionSecrets,
|
|
hashalg: &'static ring::digest::Algorithm,
|
|
master_secret: &[u8]) {
|
|
self.client_random.as_mut().write(&hs_rands.client_random).unwrap();
|
|
self.server_random.as_mut().write(&hs_rands.server_random).unwrap();
|
|
self.hash = Some(hashalg);
|
|
self.master_secret.as_mut().write(master_secret).unwrap();
|
|
|
|
dumphex("client_random", &self.client_random);
|
|
dumphex("server_random", &self.server_random);
|
|
dumphex("master_secret", &self.master_secret);
|
|
}
|
|
|
|
pub fn make_key_block(&self, len: usize) -> Vec<u8> {
|
|
let mut out = Vec::new();
|
|
out.resize(len, 0u8);
|
|
|
|
/* NOTE: opposite order to above for no good reason.
|
|
* Don't design security protocols on drugs, kids. */
|
|
let randoms = join_randoms(&self.server_random, &self.client_random);
|
|
prf::prf(&mut out,
|
|
self.hash.unwrap(),
|
|
&self.master_secret,
|
|
b"key expansion",
|
|
&randoms);
|
|
|
|
dumphex("key block", &out);
|
|
out
|
|
}
|
|
|
|
pub fn make_verify_data(&self, handshake_hash: &Vec<u8>, label: &[u8]) -> Vec<u8> {
|
|
let mut out = Vec::new();
|
|
out.resize(12, 0u8);
|
|
|
|
dumphex("label", label);
|
|
dumphex("master secret", &self.master_secret);
|
|
dumphex("handshake hash", &handshake_hash);
|
|
|
|
prf::prf(&mut out,
|
|
self.hash.unwrap(),
|
|
&self.master_secret,
|
|
label,
|
|
&handshake_hash);
|
|
dumphex("fin", &out);
|
|
out
|
|
}
|
|
|
|
pub fn client_verify_data(&self, handshake_hash: &Vec<u8>) -> Vec<u8> {
|
|
self.make_verify_data(handshake_hash, b"client finished")
|
|
}
|
|
|
|
pub fn server_verify_data(&self, handshake_hash: &Vec<u8>) -> Vec<u8> {
|
|
self.make_verify_data(handshake_hash, b"server finished")
|
|
}
|
|
}
|
|
|
|
|
|
pub trait MessageCipher {
|
|
fn decrypt(&self, m: &Message, seq: u64) -> Result<Message, ()>;
|
|
fn encrypt(&self, m: &Message, seq: u64) -> Result<Message, ()>;
|
|
}
|
|
|
|
impl MessageCipher {
|
|
pub fn invalid() -> Box<MessageCipher> {
|
|
Box::new(InvalidMessageCipher {})
|
|
}
|
|
|
|
pub fn new(scs: &'static SupportedCipherSuite, secrets: &SessionSecrets) -> Box<MessageCipher> {
|
|
/* Make a key block, and chop it up. */
|
|
let key_block = secrets.make_key_block(scs.key_block_len());
|
|
|
|
let mut offs = 0;
|
|
let client_write_mac_key = &key_block[offs..offs+scs.mac_key_len]; offs += scs.mac_key_len;
|
|
let server_write_mac_key = &key_block[offs..offs+scs.mac_key_len]; offs += scs.mac_key_len;
|
|
let client_write_key = &key_block[offs..offs+scs.enc_key_len]; offs += scs.enc_key_len;
|
|
let server_write_key = &key_block[offs..offs+scs.enc_key_len]; offs += scs.enc_key_len;
|
|
let client_write_iv = &key_block[offs..offs+scs.fixed_iv_len]; offs += scs.fixed_iv_len;
|
|
let server_write_iv = &key_block[offs..offs+scs.fixed_iv_len];
|
|
|
|
let aead_alg = scs.get_aead_alg();
|
|
|
|
if secrets.we_are_client {
|
|
Box::new(GCMMessageCipher::new(aead_alg,
|
|
client_write_mac_key, client_write_key, client_write_iv,
|
|
server_write_mac_key, server_write_key, server_write_iv))
|
|
} else {
|
|
Box::new(GCMMessageCipher::new(aead_alg,
|
|
server_write_mac_key, server_write_key, server_write_iv,
|
|
client_write_mac_key, client_write_key, client_write_iv))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct GCMMessageCipher {
|
|
alg: &'static ring::aead::Algorithm,
|
|
enc_key: ring::aead::SealingKey,
|
|
enc_salt: [u8; 4],
|
|
dec_key: ring::aead::OpeningKey,
|
|
dec_salt: [u8; 4]
|
|
}
|
|
|
|
const EXPLICIT_NONCE_LEN: usize = 8;
|
|
const GCM_OVERHEAD: usize = EXPLICIT_NONCE_LEN + 16;
|
|
|
|
fn dumphex(_why: &str, _buf: &[u8]) {
|
|
/*
|
|
print!("{}: ", _why);
|
|
|
|
for b in _buf {
|
|
print!("{:02x}", b);
|
|
}
|
|
println!("");
|
|
*/
|
|
}
|
|
|
|
impl MessageCipher for GCMMessageCipher {
|
|
fn decrypt(&self, msg: &Message, seq: u64) -> Result<Message, ()> {
|
|
let payload = try!(msg.get_opaque_payload().ok_or(()));
|
|
let mut buf = payload.body.to_vec();
|
|
|
|
if buf.len() < GCM_OVERHEAD {
|
|
return Err(());
|
|
}
|
|
|
|
let mut nonce = [0u8; 12];
|
|
nonce.as_mut().write(&self.dec_salt).unwrap();
|
|
nonce[4..].as_mut().write(&buf).unwrap();
|
|
|
|
let mut aad = Vec::new();
|
|
codec::encode_u64(seq, &mut aad);
|
|
msg.typ.encode(&mut aad);
|
|
msg.version.encode(&mut aad);
|
|
codec::encode_u16((buf.len() - GCM_OVERHEAD) as u16, &mut aad);
|
|
|
|
let plain_len = try!(ring::aead::open_in_place(&self.dec_key,
|
|
&nonce,
|
|
EXPLICIT_NONCE_LEN,
|
|
&mut buf,
|
|
&aad));
|
|
|
|
buf.truncate(plain_len);
|
|
|
|
Ok(
|
|
Message {
|
|
typ: msg.typ.clone(),
|
|
version: msg.version.clone(),
|
|
payload: MessagePayload::opaque(buf)
|
|
}
|
|
)
|
|
}
|
|
|
|
fn encrypt(&self, msg: &Message, seq: u64) -> Result<Message, ()> {
|
|
/* The GCM nonce is constructed from a 32-bit 'salt' derived
|
|
* from the master-secret, and a 64-bit explicit part,
|
|
* with no specified construction. Thanks for that.
|
|
*
|
|
* We use the sequence number, which is the only safe-
|
|
* by-construction option. */
|
|
let mut nonce = [0u8; 12];
|
|
nonce.as_mut().write(&self.enc_salt).unwrap();
|
|
codec::put_u64(seq, &mut nonce[4..]);
|
|
|
|
let mut buf = Vec::new();
|
|
buf.resize(EXPLICIT_NONCE_LEN, 0u8);
|
|
msg.payload.encode(&mut buf);
|
|
let payload_len = buf.len() - EXPLICIT_NONCE_LEN;
|
|
|
|
/* make room for tag */
|
|
let tag_len = self.alg.max_overhead_len();
|
|
let want_len = buf.len() + tag_len;
|
|
buf.resize(want_len, 0u8);
|
|
|
|
let mut aad = Vec::new();
|
|
codec::encode_u64(seq, &mut aad);
|
|
msg.typ.encode(&mut aad);
|
|
msg.version.encode(&mut aad);
|
|
codec::encode_u16(payload_len as u16, &mut aad);
|
|
|
|
dumphex("plain", &buf[EXPLICIT_NONCE_LEN..want_len - tag_len]);
|
|
dumphex("aad", &aad);
|
|
|
|
try!(ring::aead::seal_in_place(&self.enc_key,
|
|
&nonce,
|
|
&mut buf[EXPLICIT_NONCE_LEN..],
|
|
tag_len,
|
|
&aad));
|
|
|
|
buf[0..8].as_mut().write(&nonce[4..]).unwrap();
|
|
dumphex("outgoing", &buf);
|
|
|
|
Ok(Message {
|
|
typ: msg.typ.clone(),
|
|
version: msg.version.clone(),
|
|
payload: MessagePayload::opaque(buf)
|
|
})
|
|
}
|
|
}
|
|
|
|
impl GCMMessageCipher {
|
|
fn new(alg: &'static ring::aead::Algorithm,
|
|
_enc_mac_key: &[u8], enc_key: &[u8], enc_iv: &[u8],
|
|
_dec_mac_key: &[u8], dec_key: &[u8], dec_iv: &[u8]) -> GCMMessageCipher {
|
|
let mut ret = GCMMessageCipher {
|
|
alg: alg,
|
|
enc_key: ring::aead::SealingKey::new(alg, enc_key).unwrap(),
|
|
enc_salt: [0u8; 4],
|
|
dec_key: ring::aead::OpeningKey::new(alg, dec_key).unwrap(),
|
|
dec_salt: [0u8; 4]
|
|
};
|
|
|
|
dumphex("enc_key", enc_key);
|
|
dumphex("enc_iv ", enc_iv);
|
|
dumphex("dec_key", dec_key);
|
|
dumphex("dec_iv ", dec_iv);
|
|
|
|
ret.enc_salt.as_mut().write(enc_iv).unwrap();
|
|
ret.dec_salt.as_mut().write(dec_iv).unwrap();
|
|
ret
|
|
}
|
|
}
|
|
|
|
/* A MessageCipher which doesn't work. */
|
|
pub struct InvalidMessageCipher {}
|
|
|
|
impl MessageCipher for InvalidMessageCipher {
|
|
fn decrypt(&self, _m: &Message, _seq: u64) -> Result<Message, ()> {
|
|
Err(())
|
|
}
|
|
|
|
fn encrypt(&self, _m: &Message, _seq: u64) -> Result<Message, ()> {
|
|
Err(())
|
|
}
|
|
}
|