mirror of https://github.com/ctz/rustls
Enough to fully complete client handshake
This commit is contained in:
parent
7167808b4b
commit
4d165eb06e
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) })
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ pub mod handshake;
|
|||
pub mod ccs;
|
||||
pub mod message;
|
||||
pub mod deframer;
|
||||
pub mod fragmenter;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
|
250
src/session.rs
250
src/session.rs
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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] = [
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue