aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMathias Hall-Andersen <mathias@hall-andersen.dk>2019-07-24 18:47:33 +0200
committerMathias Hall-Andersen <mathias@hall-andersen.dk>2019-07-24 18:47:33 +0200
commit95080c870f5cf45ab99f6177877c361fd5e9c3e5 (patch)
treecf0645643f08ac8fb1527665ae1bcc87f2a88e13 /src
parentFinish handshake exchange (diff)
downloadwireguard-rs-95080c870f5cf45ab99f6177877c361fd5e9c3e5.tar.xz
wireguard-rs-95080c870f5cf45ab99f6177877c361fd5e9c3e5.zip
Tested full handshake
Diffstat (limited to 'src')
-rw-r--r--src/device.rs51
-rw-r--r--src/messages.rs23
-rw-r--r--src/noise.rs70
-rw-r--r--src/peer.rs11
-rw-r--r--src/timestamp.rs18
-rw-r--r--src/types.rs27
6 files changed, 160 insertions, 40 deletions
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 : &timestamp::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<KeyPair>, // resulting key-pair of successful handshake
- pub Option<Vec<u8>> // message to send
+pub type Output = (
+ Option<Vec<u8>>, // message to send
+ Option<KeyPair> // resulting key-pair of successful handshake
);
// per-peer state machine