Enough to fully complete client handshake

This commit is contained in:
Joseph Birr-Pixton 2016-05-27 16:41:28 +01:00
parent 7167808b4b
commit 4d165eb06e
12 changed files with 485 additions and 40 deletions

View File

@ -1,12 +1,13 @@
use msgs::enums::CipherSuite;
use session::SessionSecrets;
use session::MessageCipher;
use suites::{SupportedCipherSuite, DEFAULT_CIPHERSUITES};
use msgs::handshake::{SessionID, CertificatePayload};
use msgs::handshake::{ServerNameRequest, SupportedSignatureAlgorithms};
use msgs::handshake::{ClientExtension, DigitallySignedStruct};
use msgs::deframer::MessageDeframer;
use msgs::fragmenter::{MessageFragmenter, MAX_FRAGMENT_LEN};
use msgs::message::Message;
use msgs::base::Payload;
use client_hs;
use hash_hs;
use verify;
@ -17,6 +18,7 @@ use std::sync::Arc;
use std::fmt::Debug;
use std::io;
use std::collections::VecDeque;
use std::mem;
pub struct ClientConfig {
/* List of ciphersuites, in preference order. */
@ -68,8 +70,8 @@ impl ClientHandshakeData {
self.handshake_hash.as_mut().unwrap().update(m);
}
pub fn get_verify_data(&self) -> Payload {
Payload { body: self.handshake_hash.as_ref().unwrap().get_current_hash().into_boxed_slice() }
pub fn get_verify_hash(&self) -> Vec<u8> {
self.handshake_hash.as_ref().unwrap().get_current_hash()
}
}
@ -83,11 +85,26 @@ pub enum ConnState {
Traffic
}
impl ConnState {
fn is_encrypted(&self) -> bool {
match *self {
ConnState::ExpectFinished
| ConnState::Traffic => true,
_ => false
}
}
}
pub struct ClientSession {
pub config: Arc<ClientConfig>,
pub handshake_data: ClientHandshakeData,
pub secrets_current: SessionSecrets,
message_cipher: Box<MessageCipher>,
write_seq: u64,
read_seq: u64,
pub message_deframer: MessageDeframer,
pub message_fragmenter: MessageFragmenter,
pub plain_buf: Vec<u8>,
pub tls_queue: VecDeque<Message>,
pub state: ConnState
}
@ -99,7 +116,12 @@ impl ClientSession {
config: client_config.clone(),
handshake_data: ClientHandshakeData::new(hostname),
secrets_current: SessionSecrets::for_client(),
message_cipher: MessageCipher::invalid(),
write_seq: 0,
read_seq: 0,
message_deframer: MessageDeframer::new(),
message_fragmenter: MessageFragmenter::new(MAX_FRAGMENT_LEN),
plain_buf: Vec::new(),
tls_queue: VecDeque::new(),
state: ConnState::ExpectServerHello
};
@ -121,6 +143,25 @@ impl ClientSession {
ret
}
pub fn start_encryption(&mut self) {
let scs = self.handshake_data.ciphersuite.as_ref().unwrap();
self.message_cipher = MessageCipher::new(scs, &self.secrets_current);
}
pub fn encrypt_outgoing(&mut self, plain: &Message) -> Message {
let seq = self.write_seq;
self.write_seq += 1;
assert!(self.write_seq != 0);
self.message_cipher.encrypt(plain, seq).unwrap()
}
pub fn decrypt_incoming(&mut self, plain: &Message) -> Option<Message> {
let seq = self.read_seq;
self.read_seq += 1;
assert!(self.read_seq != 0);
self.message_cipher.decrypt(plain, seq).ok()
}
pub fn find_cipher_suite(&self, suite: &CipherSuite) -> Option<&'static SupportedCipherSuite> {
let got = suite.clone();
for ref scs in &self.config.ciphersuites {
@ -144,7 +185,9 @@ impl ClientSession {
}
pub fn process_msg(&mut self, msg: &mut Message) -> Result<(), HandshakeError> {
msg.decode_payload();
if !self.state.is_encrypted() {
msg.decode_payload();
}
let handler = self.get_handler();
let expects = (handler.expect)();
@ -161,6 +204,8 @@ impl ClientSession {
ConnState::ExpectCertificate => &client_hs::ExpectCertificate,
ConnState::ExpectServerKX => &client_hs::ExpectServerKX,
ConnState::ExpectServerHelloDone => &client_hs::ExpectServerHelloDone,
ConnState::ExpectCCS => &client_hs::ExpectCCS,
ConnState::ExpectFinished => &client_hs::ExpectFinished,
_ => &client_hs::InvalidState
}
}
@ -195,4 +240,13 @@ impl ClientSession {
wr.write_all(&data)
}
pub fn send_plain(&mut self, data: &[u8]) {
}
pub fn flush(&mut self) {
let buf = mem::replace(&mut self.plain_buf, Vec::new());
self.send_plain(&buf);
}
}

View File

@ -1,6 +1,7 @@
use msgs::enums::{ContentType, HandshakeType};
use msgs::enums::{Compression, ProtocolVersion};
use msgs::message::{Message, MessagePayload};
use msgs::base::Payload;
use msgs::codec::Codec;
use msgs::handshake::{HandshakePayload, HandshakeMessagePayload, ClientHelloPayload};
use msgs::handshake::{SessionID, Random};
@ -214,21 +215,26 @@ fn emit_ccs(sess: &mut ClientSession) {
}
fn emit_finished(sess: &mut ClientSession) {
let verify_data = sess.handshake_data.get_verify_data();
let vh = sess.handshake_data.get_verify_hash();
dumphex("finished vh", &vh);
let verify_data = sess.secrets_current.client_verify_data(&vh);
dumphex("finished verify", &verify_data);
let verify_data_payload = Payload { body: verify_data.into_boxed_slice() };
let mut f = Message {
typ: ContentType::Handshake,
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::Handshake(
HandshakeMessagePayload {
typ: HandshakeType::Finished,
payload: HandshakePayload::Finished(verify_data)
payload: HandshakePayload::Finished(verify_data_payload)
}
)
};
sess.handshake_data.hash_message(&f);
//sess.encrypt_outgoing(&mut f);
sess.tls_queue.push_back(f);
let ef = sess.encrypt_outgoing(&f);
sess.tls_queue.push_back(ef);
}
fn ExpectServerHelloDone_handle(sess: &mut ClientSession, m: &Message) -> Result<ConnState, HandshakeError> {
@ -259,7 +265,7 @@ fn ExpectServerHelloDone_handle(sess: &mut ClientSession, m: &Message) -> Result
dumphex("verify message", &message);
dumphex("verify sig", &sess.handshake_data.server_kx_sig.as_ref().unwrap().sig.body);
try!(verify::verify_kx(&message,
&sess.handshake_data.server_cert_chain[0],
sess.handshake_data.server_kx_sig.as_ref().unwrap()));
@ -277,7 +283,8 @@ fn ExpectServerHelloDone_handle(sess: &mut ClientSession, m: &Message) -> Result
emit_ccs(sess);
/* 3d. Now commit secrets. */
sess.secrets_current.init(sess.handshake_data.ciphersuite.as_ref().unwrap().get_hash(),
sess.secrets_current.init(&sess.handshake_data.secrets,
sess.handshake_data.ciphersuite.as_ref().unwrap().get_hash(),
&kxd.premaster_secret);
sess.start_encryption();
@ -292,6 +299,49 @@ pub static ExpectServerHelloDone: Handler = Handler {
handle: ExpectServerHelloDone_handle
};
/* -- Waiting for their CCS -- */
fn ExpectCCS_expect() -> Expectation {
Expectation {
content_types: vec![ContentType::ChangeCipherSpec],
handshake_types: vec![]
}
}
fn ExpectCCS_handle(sess: &mut ClientSession, m: &Message) -> Result<ConnState, HandshakeError> {
/* nb. msgs layer validates trivial contents of CCS */
println!("got server CCS");
Ok(ConnState::ExpectFinished)
}
pub static ExpectCCS: Handler = Handler {
expect: ExpectCCS_expect,
handle: ExpectCCS_handle
};
/* -- Waiting for their finished -- */
fn ExpectFinished_expect() -> Expectation {
Expectation {
content_types: vec![ContentType::Handshake],
handshake_types: vec![] /* we need to decrypt before we can check this */
}
}
fn ExpectFinished_handle(sess: &mut ClientSession, m: &Message) -> Result<ConnState, HandshakeError> {
let mut dm = try!(sess.decrypt_incoming(m)
.ok_or(HandshakeError::DecryptError));
dm.decode_payload();
let finished = extract_handshake!(dm, HandshakePayload::Finished);
println!("got finished {:?}", finished);
sess.flush();
Ok(ConnState::Traffic)
}
pub static ExpectFinished: Handler = Handler {
expect: ExpectFinished_expect,
handle: ExpectFinished_handle
};
/* -- Generic invalid state -- */
fn InvalidState_expect() -> Expectation {
Expectation {

View File

@ -9,6 +9,7 @@ pub enum HandshakeError {
InappropriateMessage { expect_types: Vec<ContentType>, got_type: ContentType },
InappropriateHandshakeMessage { expect_types: Vec<HandshakeType>, got_type: HandshakeType },
NoCertificatesPresented,
DecryptError,
WebPKIError(webpki::Error),
General(String)
}
@ -29,7 +30,8 @@ impl Expectation {
}
if let MessagePayload::Handshake(ref hsp) = m.payload {
if !self.handshake_types.contains(&hsp.typ) {
if self.handshake_types.len() > 0
&& !self.handshake_types.contains(&hsp.typ) {
return Err(HandshakeError::InappropriateHandshakeMessage {
expect_types: self.handshake_types.clone(),
got_type: hsp.typ.clone()

View File

@ -1,12 +1,22 @@
extern crate ring;
use self::ring::digest;
use msgs::codec::Codec;
use msgs::message::{Message, MessagePayload};
pub struct HandshakeHash {
ctx: digest::Context
}
fn dump(label: &str, bytes: &[u8]) {
print!("{}: ", label);
for b in bytes {
print!("{:02x}", b);
}
println!("");
}
impl HandshakeHash {
pub fn new(alg: &'static digest::Algorithm) -> HandshakeHash {
HandshakeHash { ctx: digest::Context::new(alg) }
@ -14,10 +24,12 @@ impl HandshakeHash {
pub fn update(&mut self, m: &Message) -> &mut HandshakeHash {
match m.payload {
MessagePayload::Handshake(_) => {
MessagePayload::Handshake(ref hs) => {
let mut buf = Vec::new();
m.payload.encode(&mut buf);
self.update_raw(&buf);
hs.encode(&mut buf);
println!("hash msg {:?} {} bytes", hs.typ, buf.len());
dump("hash", &buf);
self.ctx.update(&buf);
},
_ => unreachable!()
};
@ -25,6 +37,8 @@ impl HandshakeHash {
}
pub fn update_raw(&mut self, buf: &[u8]) -> &mut HandshakeHash {
println!("hash raw {} bytes", buf.len());
dump("hash init", buf);
self.ctx.update(buf);
self
}
@ -33,7 +47,6 @@ impl HandshakeHash {
let h = self.ctx.clone().finish();
let mut ret = Vec::new();
ret.extend_from_slice(h.as_ref());
ret.truncate(12);
ret
}
}

View File

@ -108,6 +108,17 @@ pub fn read_u32(r: &mut Reader) -> Option<u32> {
r.take(4).and_then(decode_u32)
}
pub fn encode_u64(v: u64, bytes: &mut Vec<u8>) {
bytes.push((v >> 56) as u8);
bytes.push((v >> 48) as u8);
bytes.push((v >> 40) as u8);
bytes.push((v >> 32) as u8);
bytes.push((v >> 24) as u8);
bytes.push((v >> 16) as u8);
bytes.push((v >> 8) as u8);
bytes.push(v as u8);
}
pub fn encode_vec_u8<T: Codec>(bytes: &mut Vec<u8>, items: &Vec<T>) {
let mut sub: Vec<u8> = Vec::new();
for i in items {

71
src/msgs/fragmenter.rs Normal file
View File

@ -0,0 +1,71 @@
use std::collections::VecDeque;
use msgs::message::{Message, MessagePayload};
pub const MAX_FRAGMENT_LEN: usize = 16384;
pub struct MessageFragmenter {
max_frag: usize
}
impl MessageFragmenter {
pub fn new(max_fragment_len: usize) -> MessageFragmenter {
assert!(max_fragment_len <= MAX_FRAGMENT_LEN);
MessageFragmenter {
max_frag: max_fragment_len
}
}
/// Take the Message `msg` and re-fragment it into new
/// messages whose fragment is no more than max_frag.
/// The new messages are appended to the `out` deque.
pub fn fragment(&self, msg: &Message, out: &mut VecDeque<Message>) {
let mut payload = Vec::new();
msg.payload.encode(&mut payload);
for chunk in payload.chunks(self.max_frag) {
let cm = Message {
typ: msg.typ.clone(),
version: msg.version.clone(),
payload: MessagePayload::opaque(chunk.to_vec())
};
out.push_back(cm);
}
}
}
#[cfg(test)]
mod tests {
use super::MessageFragmenter;
use msgs::message::{MessagePayload, Message};
use msgs::enums::{ContentType, ProtocolVersion};
use std::collections::VecDeque;
fn msg_eq(mm: Option<Message>, typ: &ContentType, version: &ProtocolVersion, bytes: &[u8]) {
let m = mm.unwrap();
assert_eq!(&m.typ, typ);
assert_eq!(&m.version, version);
match m.payload {
MessagePayload::Unknown(ref pl) => assert_eq!(pl.body.to_vec(), bytes.to_vec()),
_ => unreachable!()
};
}
#[test]
fn smoke() {
let m = Message {
typ: ContentType::Handshake,
version: ProtocolVersion::TLSv1_2,
payload: MessagePayload::opaque(b"\x01\x02\x03\x04\x05\x06\x07\x08".to_vec())
};
let frag = MessageFragmenter::new(3);
let mut q = VecDeque::new();
frag.fragment(&m, &mut q);
msg_eq(q.pop_front(), &m.typ, &m.version, b"\x01\x02\x03");
msg_eq(q.pop_front(), &m.typ, &m.version, b"\x04\x05\x06");
msg_eq(q.pop_front(), &m.typ, &m.version, b"\x07\x08");
assert_eq!(q.len(), 0);
}
}

View File

@ -40,7 +40,7 @@ impl Random {
pub fn write_slice(&self, mut bytes: &mut [u8]) {
let mut buf = Vec::new();
self.encode(&mut buf);
bytes.write(&buf);
assert_eq!(bytes.write(&buf).unwrap(), 32);
}
}

View File

@ -1,6 +1,6 @@
use msgs::codec::{Codec, Reader, encode_u16};
use msgs::base::PayloadU16;
use msgs::codec::{Codec, Reader, encode_u16, read_u16};
use msgs::base::Payload;
use msgs::alert::AlertMessagePayload;
use msgs::ccs::ChangeCipherSpecPayload;
use msgs::handshake::HandshakeMessagePayload;
@ -11,7 +11,7 @@ pub enum MessagePayload {
Alert(AlertMessagePayload),
Handshake(HandshakeMessagePayload),
ChangeCipherSpec(ChangeCipherSpecPayload),
Unknown(PayloadU16)
Unknown(Payload)
}
impl MessagePayload {
@ -41,6 +41,10 @@ impl MessagePayload {
None
}
}
pub fn opaque(data: Vec<u8>) -> MessagePayload {
MessagePayload::Unknown(Payload { body: data.into_boxed_slice() })
}
}
/* aka TLSPlaintext */
@ -55,7 +59,10 @@ impl Message {
pub fn read(r: &mut Reader) -> Option<Message> {
let typ = try_ret!(ContentType::read(r));
let version = try_ret!(ProtocolVersion::read(r));
let payload = try_ret!(PayloadU16::read(r));
let len = try_ret!(read_u16(r));
let mut sub = try_ret!(r.sub(len as usize));
let payload = try_ret!(Payload::read(&mut sub));
Some(Message { typ: typ, version: version, payload: MessagePayload::Unknown(payload) })
}

View File

@ -10,6 +10,7 @@ pub mod handshake;
pub mod ccs;
pub mod message;
pub mod deframer;
pub mod fragmenter;
#[cfg(test)]
mod test {

View File

@ -1,16 +1,33 @@
extern crate ring;
use prf;
use rand;
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);
randoms[32..].as_mut().write(second);
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]
@ -18,38 +35,247 @@ impl SessionSecrets {
}
pub fn for_client() -> SessionSecrets {
SessionSecrets::for_server()
}
fn join_randoms(&self) -> [u8; 64] {
let mut randoms = [0u8; 64];
randoms.as_mut().write(&self.client_random);
randoms[32..].as_mut().write(&self.server_random);
randoms
let mut ret = SessionSecrets::for_server();
ret.we_are_client = true;
ret
}
pub fn init(&mut self,
hs_rands: &SessionSecrets,
hashalg: &'static ring::digest::Algorithm,
pms: &[u8]) {
let randoms = self.join_randoms();
/* Copy in randoms. */
self.client_random.as_mut().write(&hs_rands.client_random);
self.server_random.as_mut().write(&hs_rands.server_random);
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 make_key_block(&self, hashalg: &'static ring::digest::Algorithm, len: usize) -> Vec<u8> {
pub fn make_key_block(&self, len: usize) -> Vec<u8> {
let mut out = Vec::new();
out.resize(len, 0u8);
let randoms = self.join_randoms();
/* 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,
hashalg,
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 mut buf = try!({
match msg.payload {
MessagePayload::Unknown(ref payload) => Ok(payload.body.to_vec()),
_ => Err(())
}
});
if buf.len() < GCM_OVERHEAD {
return Err(());
}
let mut nonce = [0u8; 12];
nonce.as_mut().write(&self.dec_salt);
nonce[4..].as_mut().write(&buf);
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, ()> {
let mut nonce = [0u8; 12];
nonce.as_mut().write(&self.enc_salt);
rand::fill_random(&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..]);
println!("payload len {}\n", buf.len());
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);
ret.dec_salt.as_mut().write(dec_iv);
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(())
}
}

View File

@ -30,10 +30,10 @@ impl KeyExchangeResult {
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);
@ -51,7 +51,7 @@ impl KeyExchangeResult {
if secret.is_err() {
return None;
}
Some(KeyExchangeResult { pubkey: pubkey, premaster_secret: secret.unwrap() })
}
@ -70,7 +70,9 @@ pub struct SupportedCipherSuite {
pub kx: KeyExchangeAlgorithm,
pub bulk: BulkAlgorithm,
pub hash: HashAlgorithm,
pub key_block_len: usize
pub mac_key_len: usize,
pub enc_key_len: usize,
pub fixed_iv_len: usize
}
impl PartialEq for SupportedCipherSuite {
@ -104,6 +106,10 @@ impl SupportedCipherSuite {
&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 =
@ -112,7 +118,9 @@ SupportedCipherSuite {
kx: KeyExchangeAlgorithm::ECDHE_RSA,
bulk: BulkAlgorithm::AES_128_GCM,
hash: HashAlgorithm::SHA256,
key_block_len: 0 + 0 + 16 + 16 + 4 + 4
mac_key_len: 0,
enc_key_len: 16,
fixed_iv_len: 4
};
pub static TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite =
@ -121,7 +129,9 @@ SupportedCipherSuite {
kx: KeyExchangeAlgorithm::ECDHE_RSA,
bulk: BulkAlgorithm::AES_256_GCM,
hash: HashAlgorithm::SHA384,
key_block_len: 0 + 0 + 32 + 32 + 4 + 4
mac_key_len: 0,
enc_key_len: 32,
fixed_iv_len: 4
};
pub static DEFAULT_CIPHERSUITES: [&'static SupportedCipherSuite; 2] = [

View File

@ -1,10 +1,10 @@
#!/bin/sh
openssl s_server -www -accept 8443 -key test/end.key -cert test/end.cert -CAfile test/end.chain -msg -debug -state &
openssl s_server -www -accept 8443 -key test/end.key -cert test/end.cert -CAfile test/end.chain -msg -debug -state > server.log 2>&1 &
server=$!
sleep 1
./target/debug/s_client &
./target/debug/s_client > client.log 2>&1 &
client=$!
sleep 5