Skip to content

Commit

Permalink
Merge e9905a1 into 1a1904e
Browse files Browse the repository at this point in the history
  • Loading branch information
ctz committed May 1, 2017
2 parents 1a1904e + e9905a1 commit 9cc409b
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 62 deletions.
10 changes: 5 additions & 5 deletions src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use msgs::fragmenter::MAX_FRAGMENT_LEN;
use error::TLSError;
use session::SessionSecrets;
use suites::{SupportedCipherSuite, BulkAlgorithm};
use key_schedule::hkdf_expand_label;
use key_schedule::{derive_traffic_key, derive_traffic_iv};

// accum[i] ^= offset[i] for all i in 0..len(accum)
fn xor(accum: &mut [u8], offset: &[u8]) {
Expand Down Expand Up @@ -113,8 +113,8 @@ pub fn new_tls12(scs: &'static SupportedCipherSuite,
pub fn new_tls13_read(scs: &'static SupportedCipherSuite,
secret: &[u8]) -> Box<MessageDecrypter> {
let hash = scs.get_hash();
let key = hkdf_expand_label(hash, secret, b"key", &[], scs.enc_key_len as u16);
let iv = hkdf_expand_label(hash, secret, b"iv", &[], scs.fixed_iv_len as u16);
let key = derive_traffic_key(hash, secret, scs.enc_key_len);
let iv = derive_traffic_iv(hash, secret, scs.fixed_iv_len);
let aead_alg = scs.get_aead_alg();

Box::new(TLS13MessageDecrypter::new(aead_alg, &key, &iv))
Expand All @@ -123,8 +123,8 @@ pub fn new_tls13_read(scs: &'static SupportedCipherSuite,
pub fn new_tls13_write(scs: &'static SupportedCipherSuite,
secret: &[u8]) -> Box<MessageEncrypter> {
let hash = scs.get_hash();
let key = hkdf_expand_label(hash, secret, b"key", &[], scs.enc_key_len as u16);
let iv = hkdf_expand_label(hash, secret, b"iv", &[], scs.fixed_iv_len as u16);
let key = derive_traffic_key(hash, secret, scs.enc_key_len);
let iv = derive_traffic_iv(hash, secret, scs.fixed_iv_len);
let aead_alg = scs.get_aead_alg();

Box::new(TLS13MessageEncrypter::new(aead_alg, &key, &iv))
Expand Down
27 changes: 22 additions & 5 deletions src/client_hs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use handshake::Expectation;

use std::mem;

// draft-ietf-tls-tls13-18
const TLS13_DRAFT: u16 = 0x7f12;
// draft-ietf-tls-tls13-19
const TLS13_DRAFT: u16 = 0x7f13;

macro_rules! extract_handshake(
( $m:expr, $t:path ) => (
Expand Down Expand Up @@ -615,7 +615,6 @@ pub static EXPECT_SERVER_HELLO: State = State {
fn handle_hello_retry_request(sess: &mut ClientSessionImpl,
m: Message) -> StateResult {
let hrr = extract_handshake!(m, HandshakePayload::HelloRetryRequest).unwrap();
sess.handshake_data.transcript.add_message(&m);
debug!("Got HRR {:?}", hrr);

let has_cookie = hrr.get_cookie().is_some();
Expand Down Expand Up @@ -665,6 +664,20 @@ fn handle_hello_retry_request(sess: &mut ClientSessionImpl,
}
}

// Or asks us to use a ciphersuite we didn't offer.
let maybe_cs = sess.find_cipher_suite(hrr.cipher_suite);
let cs = match maybe_cs {
Some(cs) => cs,
None => {
return Err(illegal_param(sess, "server requested unsupported cs in hrr"));
}
};

// This is the draft19 change where the transcript became a tree
sess.handshake_data.transcript.start_hash(cs.get_hash());
sess.handshake_data.transcript.rollup_for_hrr();
sess.handshake_data.transcript.add_message(&m);

Ok(emit_client_hello_for_retry(sess, Some(hrr)))
}

Expand Down Expand Up @@ -1019,7 +1032,9 @@ fn handle_certificate_req_tls13(sess: &mut ClientSessionImpl,
}

let tls13_sign_schemes = SupportedSignatureSchemes::supported_sign_tls13();
let compat_sigschemes = certreq.sigschemes
let no_sigschemes = Vec::new();
let compat_sigschemes = certreq.get_sigalgs_extension()
.unwrap_or(&no_sigschemes)
.iter()
.cloned()
.filter(|scheme| tls13_sign_schemes.contains(scheme))
Expand All @@ -1030,7 +1045,9 @@ fn handle_certificate_req_tls13(sess: &mut ClientSessionImpl,
return Err(TLSError::PeerIncompatibleError("server sent bad certreq schemes".to_string()));
}

let canames = certreq.canames
let no_canames = Vec::new();
let canames = certreq.get_authorities_extension()
.unwrap_or(&no_canames)
.iter()
.map(|p| p.0.as_slice())
.collect::<Vec<&[u8]>>();
Expand Down
12 changes: 12 additions & 0 deletions src/hash_hs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ring::digest;
use std::mem;
use msgs::codec::Codec;
use msgs::message::{Message, MessagePayload};
use msgs::handshake::HandshakeMessagePayload;

/// This deals with keeping a running hash of the handshake
/// payloads. This is computed by buffering initially. Once
Expand Down Expand Up @@ -128,6 +129,17 @@ impl HandshakeHash {
ret
}

/// Take the current hash value, and encapsulate it in a
/// 'handshake_hash' handshake message. Start this hash
/// again, with that message at the front.
pub fn rollup_for_hrr(&mut self) {
let old_hash = self.ctx.take().unwrap().finish();
let old_handshake_hash_msg = HandshakeMessagePayload::build_handshake_hash(old_hash.as_ref());

self.ctx = Some(digest::Context::new(self.alg.unwrap()));
self.update_raw(&old_handshake_hash_msg.get_encoding());
}

/// Get the current hash value.
pub fn get_current_hash(&self) -> Vec<u8> {
let hash = self.ctx.as_ref().unwrap().clone().finish();
Expand Down
194 changes: 169 additions & 25 deletions src/key_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ pub enum SecretKind {
ClientApplicationTrafficSecret,
ServerApplicationTrafficSecret,
ResumptionMasterSecret,
DerivedSecret,
}

impl SecretKind {
fn to_bytes(&self) -> &'static [u8] {
match *self {
SecretKind::ResumptionPSKBinderKey => b"resumption psk binder key",
SecretKind::ClientHandshakeTrafficSecret => b"client handshake traffic secret",
SecretKind::ServerHandshakeTrafficSecret => b"server handshake traffic secret",
SecretKind::ClientApplicationTrafficSecret => b"client application traffic secret",
SecretKind::ServerApplicationTrafficSecret => b"server application traffic secret",
SecretKind::ResumptionMasterSecret => b"resumption master secret",
SecretKind::ResumptionPSKBinderKey => b"res binder",
SecretKind::ClientHandshakeTrafficSecret => b"c hs traffic",
SecretKind::ServerHandshakeTrafficSecret => b"s hs traffic",
SecretKind::ClientApplicationTrafficSecret => b"c ap traffic",
SecretKind::ServerApplicationTrafficSecret => b"s ap traffic",
SecretKind::ResumptionMasterSecret => b"res master",
SecretKind::DerivedSecret => b"derived",
}
}
}
Expand All @@ -32,22 +34,35 @@ impl SecretKind {
/// own lineage of keys over successive key updates.
pub struct KeySchedule {
current: hmac::SigningKey,
need_derive_for_extract: bool,
hash: &'static digest::Algorithm,
hash_of_empty_message: [u8; digest::MAX_OUTPUT_LEN],
pub current_client_traffic_secret: Vec<u8>,
pub current_server_traffic_secret: Vec<u8>,
}

impl KeySchedule {
pub fn new(hash: &'static digest::Algorithm) -> KeySchedule {
let zeroes = [0u8; digest::MAX_OUTPUT_LEN];

let mut empty_hash = [0u8; digest::MAX_OUTPUT_LEN];
empty_hash[..hash.output_len]
.clone_from_slice(digest::digest(hash, &[]).as_ref());

KeySchedule {
current: hmac::SigningKey::new(hash, &zeroes[..hash.output_len]),
need_derive_for_extract: false,
hash: hash,
hash_of_empty_message: empty_hash,
current_server_traffic_secret: Vec::new(),
current_client_traffic_secret: Vec::new(),
}
}

pub fn get_hash_of_empty_message(&self) -> &[u8] {
&self.hash_of_empty_message[..self.hash.output_len]
}

/// Input the empty secret.
pub fn input_empty(&mut self) {
let zeroes = [0u8; digest::MAX_OUTPUT_LEN];
Expand All @@ -57,6 +72,12 @@ impl KeySchedule {

/// Input the given secret.
pub fn input_secret(&mut self, secret: &[u8]) {
if self.need_derive_for_extract {
let derived = self.derive(SecretKind::DerivedSecret,
self.get_hash_of_empty_message());
self.current = hmac::SigningKey::new(self.hash, &derived);
}
self.need_derive_for_extract = true;
let new = hkdf::extract(&self.current, secret);
self.current = new
}
Expand Down Expand Up @@ -94,11 +115,10 @@ impl KeySchedule {
pub fn sign_verify_data(&self, base_key: &[u8], hs_hash: &[u8]) -> Vec<u8> {
debug_assert!(hs_hash.len() == self.hash.output_len);

let hmac_key = hkdf_expand_label(self.hash,
base_key,
b"finished",
&[],
self.hash.output_len as u16);
let hmac_key = _hkdf_expand_label(&hmac::SigningKey::new(self.hash, base_key),
b"finished",
&[],
self.hash.output_len as u16);

hmac::sign(&hmac::SigningKey::new(self.hash, &hmac_key), hs_hash)
.as_ref()
Expand All @@ -109,11 +129,10 @@ impl KeySchedule {
/// it.
pub fn derive_next(&self, kind: SecretKind) -> Vec<u8> {
let base_key = self.current_traffic_secret(kind);
hkdf_expand_label(self.hash,
base_key,
b"application traffic secret",
&[],
self.hash.output_len as u16)
_hkdf_expand_label(&hmac::SigningKey::new(self.hash, base_key),
b"traffic upd",
&[],
self.hash.output_len as u16)
}
}

Expand All @@ -125,7 +144,7 @@ fn _hkdf_expand_label(secret: &hmac::SigningKey,
let mut out = Vec::new();
out.resize(len as usize, 0u8);

let label_prefix = b"TLS 1.3, ";
let label_prefix = b"tls13 ";

let mut hkdflabel = Vec::new();
codec::encode_u16(out.len() as u16, &mut hkdflabel);
Expand All @@ -139,18 +158,17 @@ fn _hkdf_expand_label(secret: &hmac::SigningKey,
out
}

pub fn hkdf_expand_label(hash: &'static digest::Algorithm,
secret: &[u8],
label: &[u8],
context: &[u8],
len: u16)
-> Vec<u8> {
_hkdf_expand_label(&hmac::SigningKey::new(hash, secret), label, context, len)
pub fn derive_traffic_key(hash: &'static digest::Algorithm, secret: &[u8], len: usize) -> Vec<u8> {
_hkdf_expand_label(&hmac::SigningKey::new(hash, secret), b"key", &[], len as u16)
}

pub fn derive_traffic_iv(hash: &'static digest::Algorithm, secret: &[u8], len: usize) -> Vec<u8> {
_hkdf_expand_label(&hmac::SigningKey::new(hash, secret), b"iv", &[], len as u16)
}

#[cfg(test)]
mod test {
use super::{KeySchedule, SecretKind};
use super::{KeySchedule, SecretKind, derive_traffic_key, derive_traffic_iv};
use ring::digest;

#[test]
Expand All @@ -172,4 +190,130 @@ mod test {
&fake_handshake_hash);
ks.derive(SecretKind::ResumptionMasterSecret, &fake_handshake_hash);
}

#[test]
fn test_vectors() {
/* These test vectors generated with OpenSSL. */
let hs_start_hash = [
0xec, 0x14, 0x7a, 0x06, 0xde, 0xa3, 0xc8, 0x84, 0x6c, 0x02, 0xb2, 0x23, 0x8e,
0x41, 0xbd, 0xdc, 0x9d, 0x89, 0xf9, 0xae, 0xa1, 0x7b, 0x5e, 0xfd, 0x4d, 0x74,
0x82, 0xaf, 0x75, 0x88, 0x1c, 0x0a
];

let hs_full_hash = [
0x75, 0x1a, 0x3d, 0x4a, 0x14, 0xdf, 0xab, 0xeb, 0x68, 0xe9, 0x2c, 0xa5, 0x91,
0x8e, 0x24, 0x08, 0xb9, 0xbc, 0xb0, 0x74, 0x89, 0x82, 0xec, 0x9c, 0x32, 0x30,
0xac, 0x30, 0xbb, 0xeb, 0x23, 0xe2
];

let ecdhe_secret = [
0xe7, 0xb8, 0xfe, 0xf8, 0x90, 0x3b, 0x52, 0x0c, 0xb9, 0xa1, 0x89, 0x71, 0xb6,
0x9d, 0xd4, 0x5d, 0xca, 0x53, 0xce, 0x2f, 0x12, 0xbf, 0x3b, 0xef, 0x93, 0x15,
0xe3, 0x12, 0x71, 0xdf, 0x4b, 0x40
];

let client_hts = [
0x61, 0x7b, 0x35, 0x07, 0x6b, 0x9d, 0x0e, 0x08, 0xcf, 0x73, 0x1d, 0x94, 0xa8,
0x66, 0x14, 0x78, 0x41, 0x09, 0xef, 0x25, 0x55, 0x51, 0x92, 0x1d, 0xd4, 0x6e,
0x04, 0x01, 0x35, 0xcf, 0x46, 0xab
];

let client_hts_key = [
0x62, 0xd0, 0xdd, 0x00, 0xf6, 0x96, 0x19, 0xd3, 0xb8, 0x19, 0x3a, 0xb4, 0xa0,
0x95, 0x85, 0xa7
];

let client_hts_iv = [
0xff, 0xf7, 0x5d, 0xf5, 0xad, 0x35, 0xd5, 0xcb, 0x3c, 0x53, 0xf3, 0xa9
];

let server_hts = [
0xfc, 0xf7, 0xdf, 0xe6, 0x4f, 0xa2, 0xc0, 0x4f, 0x62, 0x35, 0x38, 0x7f, 0x43,
0x4e, 0x01, 0x42, 0x23, 0x36, 0xd9, 0xc0, 0x39, 0xde, 0x68, 0x47, 0xa0, 0xb9,
0xdd, 0xcf, 0x29, 0xa8, 0x87, 0x59
];

let server_hts_key = [
0x04, 0x67, 0xf3, 0x16, 0xa8, 0x05, 0xb8, 0xc4, 0x97, 0xee, 0x67, 0x04, 0x7b,
0xbc, 0xbc, 0x54
];

let server_hts_iv = [
0xde, 0x83, 0xa7, 0x3e, 0x9d, 0x81, 0x4b, 0x04, 0xc4, 0x8b, 0x78, 0x09
];

let client_ats = [
0xc1, 0x4a, 0x6d, 0x79, 0x76, 0xd8, 0x10, 0x2b, 0x5a, 0x0c, 0x99, 0x51, 0x49,
0x3f, 0xee, 0x87, 0xdc, 0xaf, 0xf8, 0x2c, 0x24, 0xca, 0xb2, 0x14, 0xe8, 0xbe,
0x71, 0xa8, 0x20, 0x6d, 0xbd, 0xa5
];

let client_ats_key = [
0xcc, 0x9f, 0x5f, 0x98, 0x0b, 0x5f, 0x10, 0x30, 0x6c, 0xba, 0xd7, 0xbe, 0x98,
0xd7, 0x57, 0x2e
];

let client_ats_iv = [
0xb8, 0x09, 0x29, 0xe8, 0xd0, 0x2c, 0x70, 0xf6, 0x11, 0x62, 0xed, 0x6b
];

let server_ats = [
0x2c, 0x90, 0x77, 0x38, 0xd3, 0xf8, 0x37, 0x02, 0xd1, 0xe4, 0x59, 0x8f, 0x48,
0x48, 0x53, 0x1d, 0x9f, 0x93, 0x65, 0x49, 0x1b, 0x9f, 0x7f, 0x52, 0xc8, 0x22,
0x29, 0x0d, 0x4c, 0x23, 0x21, 0x92
];

let server_ats_key = [
0x0c, 0xb2, 0x95, 0x62, 0xd8, 0xd8, 0x8f, 0x48, 0xb0, 0x2c, 0xbf, 0xbe, 0xd7,
0xe6, 0x2b, 0xb3
];

let server_ats_iv = [
0x0d, 0xb2, 0x8f, 0x98, 0x85, 0x86, 0xa1, 0xb7, 0xe4, 0xd5, 0xc6, 0x9c
];

let hash = &digest::SHA256;
let mut ks = KeySchedule::new(hash);
ks.input_empty();
ks.input_secret(&ecdhe_secret);

let got_client_hts = ks.derive(SecretKind::ClientHandshakeTrafficSecret,
&hs_start_hash);
assert_eq!(got_client_hts,
client_hts.to_vec());
assert_eq!(derive_traffic_key(hash, &got_client_hts, client_hts_key.len()),
client_hts_key.to_vec());
assert_eq!(derive_traffic_iv(hash, &got_client_hts, client_hts_iv.len()),
client_hts_iv.to_vec());

let got_server_hts = ks.derive(SecretKind::ServerHandshakeTrafficSecret,
&hs_start_hash);
assert_eq!(got_server_hts,
server_hts.to_vec());
assert_eq!(derive_traffic_key(hash, &got_server_hts, server_hts_key.len()),
server_hts_key.to_vec());
assert_eq!(derive_traffic_iv(hash, &got_server_hts, server_hts_iv.len()),
server_hts_iv.to_vec());

ks.input_empty();

let got_client_ats = ks.derive(SecretKind::ClientApplicationTrafficSecret,
&hs_full_hash);
assert_eq!(got_client_ats,
client_ats.to_vec());
assert_eq!(derive_traffic_key(hash, &got_client_ats, client_ats_key.len()),
client_ats_key.to_vec());
assert_eq!(derive_traffic_iv(hash, &got_client_ats, client_ats_iv.len()),
client_ats_iv.to_vec());

let got_server_ats = ks.derive(SecretKind::ServerApplicationTrafficSecret,
&hs_full_hash);
assert_eq!(got_server_ats,
server_ats.to_vec());
assert_eq!(derive_traffic_key(hash, &got_server_ats, server_ats_key.len()),
server_ats_key.to_vec());
assert_eq!(derive_traffic_iv(hash, &got_server_ats, server_ats_iv.len()),
server_ats_iv.to_vec());

}
}

0 comments on commit 9cc409b

Please sign in to comment.