Tested full handshake
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -99,6 +99,11 @@ dependencies = [
|
|||||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -301,6 +306,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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 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 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 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 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 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"
|
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ edition = "2018"
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
hex = "0.3"
|
||||||
spin = "0.5.0"
|
spin = "0.5.0"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
blake2 = "0.8.0"
|
blake2 = "0.8.0"
|
||||||
|
|||||||
@@ -102,7 +102,9 @@ impl Device {
|
|||||||
///
|
///
|
||||||
/// * `id` - The (sender) id to release
|
/// * `id` - The (sender) id to release
|
||||||
pub fn release(&self, id : u32) {
|
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
|
/// Begin a new handshake
|
||||||
@@ -136,12 +138,13 @@ impl Device {
|
|||||||
let sender = self.allocate(peer.idx);
|
let sender = self.allocate(peer.idx);
|
||||||
|
|
||||||
// create response
|
// create response
|
||||||
noise::create_response(self, peer, sender, st).map_err(|e| {
|
noise::create_response(peer, sender, st).map_err(|e| {
|
||||||
self.release(sender);
|
self.release(sender);
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Some(&messages::TYPE_RESPONSE) => noise::consume_response(self, msg),
|
Some(&messages::TYPE_RESPONSE) =>
|
||||||
|
noise::consume_response(self, msg),
|
||||||
_ => Err(HandshakeError::InvalidMessageFormat)
|
_ => Err(HandshakeError::InvalidMessageFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +177,10 @@ impl Device {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use hex;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use messages::*;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handshake() {
|
fn handshake() {
|
||||||
@@ -196,13 +202,44 @@ mod tests {
|
|||||||
dev1.add(pk2).unwrap();
|
dev1.add(pk2).unwrap();
|
||||||
dev2.add(pk1).unwrap();
|
dev2.add(pk1).unwrap();
|
||||||
|
|
||||||
|
// do a few handshakes
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
|
||||||
|
println!("handshake : {}", i);
|
||||||
|
|
||||||
// create initiation
|
// create initiation
|
||||||
|
|
||||||
let msg1 = dev1.begin(&pk2).unwrap();
|
let msg1 = dev1.begin(&pk2).unwrap();
|
||||||
|
|
||||||
|
println!("msg1 = {}", hex::encode(&msg1[..]));
|
||||||
|
println!("msg1 = {:?}", Initiation::try_from(&msg1[..]).unwrap());
|
||||||
|
|
||||||
// process initiation and create response
|
// process initiation and create response
|
||||||
|
|
||||||
let out1 = dev2.process(&msg1).unwrap();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::fmt;
|
use hex;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::fmt;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
const SIZE_TAG : usize = 16;
|
const SIZE_TAG : usize = 16;
|
||||||
@@ -102,8 +102,14 @@ impl Default for Initiation {
|
|||||||
impl fmt::Debug for Initiation {
|
impl fmt::Debug for Initiation {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f,
|
write!(f,
|
||||||
"MessageInitiation {{ type = {} }}",
|
"MessageInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}",
|
||||||
self.f_type
|
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 {
|
impl fmt::Debug for Response {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f,
|
write!(f,
|
||||||
"MessageResponse {{ type = {} }}",
|
"MessageResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}",
|
||||||
self.f_type
|
self.f_type,
|
||||||
|
self.f_sender,
|
||||||
|
self.f_receiver,
|
||||||
|
hex::encode(self.f_ephemeral),
|
||||||
|
hex::encode(self.f_empty_tag)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
src/noise.rs
68
src/noise.rs
@@ -332,13 +332,16 @@ pub fn consume_initiation<'a>(
|
|||||||
|
|
||||||
peer.check_timestamp(device, &ts)?;
|
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)
|
// return state (to create response)
|
||||||
|
|
||||||
Ok((peer, (msg.f_sender, eph_r_pk, hs, ck)))
|
Ok((peer, (msg.f_sender, eph_r_pk, hs, ck)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_response(
|
pub fn create_response(
|
||||||
device : &Device,
|
|
||||||
peer : &Peer,
|
peer : &Peer,
|
||||||
sender : u32, // sending identifier
|
sender : u32, // sending identifier
|
||||||
state : TemporaryState // state from "consume_initiation"
|
state : TemporaryState // state from "consume_initiation"
|
||||||
@@ -349,6 +352,9 @@ pub fn create_response(
|
|||||||
|
|
||||||
let (receiver, eph_r_pk, hs, ck) = state;
|
let (receiver, eph_r_pk, hs, ck) = state;
|
||||||
|
|
||||||
|
msg.f_sender = sender;
|
||||||
|
msg.f_receiver = receiver;
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
// (E_priv, E_pub) := DH-Generate()
|
||||||
|
|
||||||
let eph_sk = StaticSecret::new(&mut rng);
|
let eph_sk = StaticSecret::new(&mut rng);
|
||||||
@@ -392,15 +398,38 @@ pub fn create_response(
|
|||||||
&mut msg.f_empty_tag // tag
|
&mut msg.f_empty_tag // tag
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* not strictly needed
|
||||||
// H := Hash(H || msg.empty)
|
// H := Hash(H || msg.empty)
|
||||||
|
let hs = HASH!(&hs, &msg.f_empty_tag);
|
||||||
// let hs = HASH!(&hs, &msg.f_empty_tag); // not strictly needed
|
*/
|
||||||
|
|
||||||
// derive key-pair
|
// derive key-pair
|
||||||
|
// (verbose code, due to GenericArray -> [u8; 32] conversion)
|
||||||
|
|
||||||
let (key_recv, key_send) = KDF2!(&ck, &[]);
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
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(
|
pub fn consume_response(
|
||||||
@@ -435,7 +464,7 @@ pub fn consume_response(
|
|||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, S_pub))
|
// 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)
|
// (C, tau, k) := Kdf3(C, Q)
|
||||||
|
|
||||||
@@ -453,11 +482,32 @@ pub fn consume_response(
|
|||||||
&mut [], // pt
|
&mut [], // pt
|
||||||
&[], // ct
|
&[], // ct
|
||||||
&msg.f_empty_tag // tag
|
&msg.f_empty_tag // tag
|
||||||
);
|
)?;
|
||||||
|
|
||||||
// derive key-pair
|
// 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/peer.rs
11
src/peer.rs
@@ -1,4 +1,4 @@
|
|||||||
use std::sync::Mutex;
|
use spin::Mutex;
|
||||||
|
|
||||||
use generic_array::typenum::U32;
|
use generic_array::typenum::U32;
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
@@ -75,7 +75,7 @@ impl Peer {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
pub fn get_state(&self) -> State {
|
pub fn get_state(&self) -> State {
|
||||||
self.state.lock().unwrap().clone()
|
self.state.lock().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the state of the peer unconditionally
|
/// Set the state of the peer unconditionally
|
||||||
@@ -86,8 +86,7 @@ impl Peer {
|
|||||||
&self,
|
&self,
|
||||||
state_new : State
|
state_new : State
|
||||||
) {
|
) {
|
||||||
let mut state = self.state.lock().unwrap();
|
*self.state.lock() = state_new;
|
||||||
*state = state_new;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the mutable state of the peer conditioned on the timestamp being newer
|
/// Set the mutable state of the peer conditioned on the timestamp being newer
|
||||||
@@ -102,8 +101,8 @@ impl Peer {
|
|||||||
timestamp_new : ×tamp::TAI64N
|
timestamp_new : ×tamp::TAI64N
|
||||||
) -> Result<(), HandshakeError> {
|
) -> Result<(), HandshakeError> {
|
||||||
|
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock();
|
||||||
let mut timestamp = self.timestamp.lock().unwrap();
|
let mut timestamp = self.timestamp.lock();
|
||||||
|
|
||||||
let update = match *timestamp {
|
let update = match *timestamp {
|
||||||
None => true,
|
None => true,
|
||||||
|
|||||||
@@ -1,11 +1,27 @@
|
|||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
pub type TAI64N = [u8; 12];
|
pub type TAI64N = [u8; 12];
|
||||||
|
|
||||||
|
const TAI64_EPOCH : u64 = 4000000000000000;
|
||||||
|
|
||||||
pub fn zero() -> TAI64N {
|
pub fn zero() -> TAI64N {
|
||||||
[0u8; 12]
|
[0u8; 12]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn now() -> TAI64N {
|
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 {
|
pub fn compare(old : &TAI64N, new : &TAI64N) -> bool {
|
||||||
|
|||||||
27
src/types.rs
27
src/types.rs
@@ -71,20 +71,29 @@ impl Error for HandshakeError {
|
|||||||
|
|
||||||
// types for resulting key-material
|
// types for resulting key-material
|
||||||
|
|
||||||
struct Key {
|
#[derive(Debug)]
|
||||||
key : [u8; 32],
|
pub struct Key {
|
||||||
id : u32
|
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 {
|
pub struct KeyPair {
|
||||||
confimed : bool, // has the key-pair been confirmed?
|
pub confirmed : bool, // has the key-pair been confirmed?
|
||||||
send : Key, // key for outbound messages
|
pub send : Key, // key for outbound messages
|
||||||
recv : Key // key for inbound messages
|
pub recv : Key // key for inbound messages
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Output (
|
pub type Output = (
|
||||||
pub Option<KeyPair>, // resulting key-pair of successful handshake
|
Option<Vec<u8>>, // message to send
|
||||||
pub Option<Vec<u8>> // message to send
|
Option<KeyPair> // resulting key-pair of successful handshake
|
||||||
);
|
);
|
||||||
|
|
||||||
// per-peer state machine
|
// per-peer state machine
|
||||||
|
|||||||
Reference in New Issue
Block a user