From 95080c870f5cf45ab99f6177877c361fd5e9c3e5 Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Wed, 24 Jul 2019 18:47:33 +0200 Subject: Tested full handshake --- Cargo.lock | 7 ++++++ Cargo.toml | 1 + src/device.rs | 51 +++++++++++++++++++++++++++++++++++------ src/messages.rs | 23 +++++++++++++------ src/noise.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++-------- src/peer.rs | 11 ++++----- src/timestamp.rs | 18 ++++++++++++++- src/types.rs | 27 ++++++++++++++-------- 8 files changed, 168 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1833136..85e6cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,6 +99,11 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "hmac" version = "0.7.1" @@ -301,6 +306,7 @@ version = "0.1.0" dependencies = [ "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -352,6 +358,7 @@ dependencies = [ "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff" "checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" diff --git a/Cargo.toml b/Cargo.toml index 7631157..de66caa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "GPL-3.0" [dependencies] +hex = "0.3" spin = "0.5.0" rand = "0.6.5" blake2 = "0.8.0" diff --git a/src/device.rs b/src/device.rs index 1acbbd4..a604ebd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -102,7 +102,9 @@ impl Device { /// /// * `id` - The (sender) id to release pub fn release(&self, id : u32) { - self.id_map.write().remove(&id); + let mut m =self.id_map.write(); + debug_assert!(m.contains_key(&id), "Releasing id not allocated"); + m.remove(&id); } /// Begin a new handshake @@ -136,12 +138,13 @@ impl Device { let sender = self.allocate(peer.idx); // create response - noise::create_response(self, peer, sender, st).map_err(|e| { + noise::create_response(peer, sender, st).map_err(|e| { self.release(sender); e }) }, - Some(&messages::TYPE_RESPONSE) => noise::consume_response(self, msg), + Some(&messages::TYPE_RESPONSE) => + noise::consume_response(self, msg), _ => Err(HandshakeError::InvalidMessageFormat) } } @@ -174,7 +177,10 @@ impl Device { #[cfg(test)] mod tests { + use hex; use super::*; + use messages::*; + use std::convert::TryFrom; #[test] fn handshake() { @@ -196,13 +202,44 @@ mod tests { dev1.add(pk2).unwrap(); dev2.add(pk1).unwrap(); - // create initiation + // do a few handshakes - let msg1 = dev1.begin(&pk2).unwrap(); + for i in 0..10 { - // process initiation and create response + println!("handshake : {}", i); - let out1 = dev2.process(&msg1).unwrap(); + // create initiation + let msg1 = dev1.begin(&pk2).unwrap(); + + println!("msg1 = {}", hex::encode(&msg1[..])); + println!("msg1 = {:?}", Initiation::try_from(&msg1[..]).unwrap()); + + // process initiation and create response + + let (msg2, ks_r) = dev2.process(&msg1).unwrap(); + + let ks_r = ks_r.unwrap(); + let msg2 = msg2.unwrap(); + + println!("msg2 = {}", hex::encode(&msg2[..])); + println!("msg2 = {:?}", Response::try_from(&msg2[..]).unwrap()); + + assert!(!ks_r.confirmed, "Responders key-pair is confirmed"); + + // process response and obtain confirmed key-pair + + let (msg3, ks_i) = dev1.process(&msg2).unwrap(); + let ks_i = ks_i.unwrap(); + + assert!(msg3.is_none(), "Returned message after response"); + assert!(ks_i.confirmed, "Initiators key-pair is not confirmed"); + + assert_eq!(ks_i.send, ks_r.recv, "KeyI.send != KeyR.recv"); + assert_eq!(ks_i.recv, ks_r.send, "KeyI.recv != KeyR.send"); + + dev1.release(ks_i.send.id); + dev2.release(ks_r.send.id); + } } } diff --git a/src/messages.rs b/src/messages.rs index 52a3393..b1d1659 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -1,7 +1,7 @@ -use std::fmt; +use hex; use std::mem; +use std::fmt; use std::convert::TryFrom; - use crate::types::*; const SIZE_TAG : usize = 16; @@ -102,8 +102,14 @@ impl Default for Initiation { impl fmt::Debug for Initiation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, - "MessageInitiation {{ type = {} }}", - self.f_type + "MessageInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}", + self.f_type, + self.f_sender, + hex::encode(self.f_ephemeral), + hex::encode(self.f_static), + hex::encode(self.f_static_tag), + hex::encode(self.f_timestamp), + hex::encode(self.f_timestamp_tag) ) } } @@ -204,12 +210,15 @@ impl Default for Response { } } - impl fmt::Debug for Response { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, - "MessageResponse {{ type = {} }}", - self.f_type + "MessageResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}", + self.f_type, + self.f_sender, + self.f_receiver, + hex::encode(self.f_ephemeral), + hex::encode(self.f_empty_tag) ) } } diff --git a/src/noise.rs b/src/noise.rs index f5d1dc1..f26a7df 100644 --- a/src/noise.rs +++ b/src/noise.rs @@ -332,13 +332,16 @@ pub fn consume_initiation<'a>( peer.check_timestamp(device, &ts)?; + // H := Hash(H || msg.timestamp) + + let hs = HASH!(&hs, &msg.f_timestamp, &msg.f_timestamp_tag); + // return state (to create response) Ok((peer, (msg.f_sender, eph_r_pk, hs, ck))) } pub fn create_response( - device : &Device, peer : &Peer, sender : u32, // sending identifier state : TemporaryState // state from "consume_initiation" @@ -349,6 +352,9 @@ pub fn create_response( let (receiver, eph_r_pk, hs, ck) = state; + msg.f_sender = sender; + msg.f_receiver = receiver; + // (E_priv, E_pub) := DH-Generate() let eph_sk = StaticSecret::new(&mut rng); @@ -392,15 +398,38 @@ pub fn create_response( &mut msg.f_empty_tag // tag ); + /* not strictly needed // H := Hash(H || msg.empty) - - // let hs = HASH!(&hs, &msg.f_empty_tag); // not strictly needed + let hs = HASH!(&hs, &msg.f_empty_tag); + */ // derive key-pair + // (verbose code, due to GenericArray -> [u8; 32] conversion) + + let (key_recv, key_send) = { + let (k1, k2) = KDF2!(&ck, &[]); + let (mut d1, mut d2) = ([0u8; 32], [0u8; 32]); + d1.clone_from_slice(&k1); + d2.clone_from_slice(&k2); + (d1, d2) + }; - let (key_recv, key_send) = KDF2!(&ck, &[]); - - Ok(Output(None, None)) + // return response and unconfirmed key-pair + + Ok(( + Some(Response::into(msg)), + Some(KeyPair{ + confirmed : false, + send : Key{ + id : sender, + key : key_send + }, + recv : Key{ + id : receiver, + key : key_recv + } + }) + )) } pub fn consume_response( @@ -435,7 +464,7 @@ pub fn consume_response( // C := Kdf1(C, DH(E_priv, S_pub)) - let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes()); + let ck = KDF1!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes()); // (C, tau, k) := Kdf3(C, Q) @@ -453,11 +482,32 @@ pub fn consume_response( &mut [], // pt &[], // ct &msg.f_empty_tag // tag - ); + )?; // derive key-pair - let (key_send, key_recv) = KDF2!(&ck, &[]); + let (key_send, key_recv) = { + let (k1, k2) = KDF2!(&ck, &[]); + let (mut d1, mut d2) = ([0u8; 32], [0u8; 32]); + d1.clone_from_slice(&k1); + d2.clone_from_slice(&k2); + (d1, d2) + }; - Ok(Output(None, None)) + // return response and unconfirmed key-pair + + Ok(( + None, + Some(KeyPair{ + confirmed : true, + send : Key{ + id : sender, + key : key_send + }, + recv : Key{ + id : msg.f_sender, + key : key_recv + } + }) + )) } diff --git a/src/peer.rs b/src/peer.rs index 7678e85..d846a21 100644 --- a/src/peer.rs +++ b/src/peer.rs @@ -1,4 +1,4 @@ -use std::sync::Mutex; +use spin::Mutex; use generic_array::typenum::U32; use generic_array::GenericArray; @@ -75,7 +75,7 @@ impl Peer { /// /// # Arguments pub fn get_state(&self) -> State { - self.state.lock().unwrap().clone() + self.state.lock().clone() } /// Set the state of the peer unconditionally @@ -86,8 +86,7 @@ impl Peer { &self, state_new : State ) { - let mut state = self.state.lock().unwrap(); - *state = state_new; + *self.state.lock() = state_new; } /// Set the mutable state of the peer conditioned on the timestamp being newer @@ -102,8 +101,8 @@ impl Peer { timestamp_new : ×tamp::TAI64N ) -> Result<(), HandshakeError> { - let mut state = self.state.lock().unwrap(); - let mut timestamp = self.timestamp.lock().unwrap(); + let mut state = self.state.lock(); + let mut timestamp = self.timestamp.lock(); let update = match *timestamp { None => true, diff --git a/src/timestamp.rs b/src/timestamp.rs index 52ab154..73fe91f 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -1,11 +1,27 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + pub type TAI64N = [u8; 12]; +const TAI64_EPOCH : u64 = 4000000000000000; + pub fn zero() -> TAI64N { [0u8; 12] } pub fn now() -> TAI64N { - [0u8; 12] // TODO, return current timestamp + // get system time as duration + let sysnow = SystemTime::now(); + let delta = sysnow.duration_since(UNIX_EPOCH).unwrap(); + + // convert to tai64n + let tai64_secs = delta.as_secs() + TAI64_EPOCH; + let tai64_nano = delta.subsec_nanos(); + + // serialize + let mut res = [0u8; 12]; + res[..8].copy_from_slice(&tai64_secs.to_be_bytes()[..]); + res[8..].copy_from_slice(&tai64_nano.to_be_bytes()[..]); + res } pub fn compare(old : &TAI64N, new : &TAI64N) -> bool { diff --git a/src/types.rs b/src/types.rs index 5500ffc..9ab28f5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -71,20 +71,29 @@ impl Error for HandshakeError { // types for resulting key-material -struct Key { - key : [u8; 32], - id : u32 +#[derive(Debug)] +pub struct Key { + pub key : [u8; 32], + pub id : u32 } +#[cfg(test)] +impl PartialEq for Key { + fn eq(&self, other: &Self) -> bool { + self.id == other.id && self.key[..] == other.key[..] + } +} + +#[derive(Debug)] pub struct KeyPair { - confimed : bool, // has the key-pair been confirmed? - send : Key, // key for outbound messages - recv : Key // key for inbound messages + pub confirmed : bool, // has the key-pair been confirmed? + pub send : Key, // key for outbound messages + pub recv : Key // key for inbound messages } -pub struct Output ( - pub Option, // resulting key-pair of successful handshake - pub Option> // message to send +pub type Output = ( + Option>, // message to send + Option // resulting key-pair of successful handshake ); // per-peer state machine -- cgit v1.2.3-59-g8ed1b