Bug fixes from compliance tests with WireGuard
This commit is contained in:
@@ -231,11 +231,11 @@ impl Device {
|
|||||||
(_, None) => Err(HandshakeError::UnknownPublicKey),
|
(_, None) => Err(HandshakeError::UnknownPublicKey),
|
||||||
(None, _) => Err(HandshakeError::UnknownPublicKey),
|
(None, _) => Err(HandshakeError::UnknownPublicKey),
|
||||||
(Some(keyst), Some(peer)) => {
|
(Some(keyst), Some(peer)) => {
|
||||||
let sender = self.allocate(rng, peer);
|
let local = self.allocate(rng, peer);
|
||||||
let mut msg = Initiation::default();
|
let mut msg = Initiation::default();
|
||||||
|
|
||||||
// create noise part of initation
|
// create noise part of initation
|
||||||
noise::create_initiation(rng, keyst, peer, sender, &mut msg.noise)?;
|
noise::create_initiation(rng, keyst, peer, local, &mut msg.noise)?;
|
||||||
|
|
||||||
// add macs to initation
|
// add macs to initation
|
||||||
peer.macs
|
peer.macs
|
||||||
@@ -312,18 +312,17 @@ impl Device {
|
|||||||
let (peer, st) = noise::consume_initiation(self, keyst, &msg.noise)?;
|
let (peer, st) = noise::consume_initiation(self, keyst, &msg.noise)?;
|
||||||
|
|
||||||
// allocate new index for response
|
// allocate new index for response
|
||||||
let sender = self.allocate(rng, peer);
|
let local = self.allocate(rng, peer);
|
||||||
|
|
||||||
// prepare memory for response, TODO: take slice for zero allocation
|
// prepare memory for response, TODO: take slice for zero allocation
|
||||||
let mut resp = Response::default();
|
let mut resp = Response::default();
|
||||||
|
|
||||||
// create response (release id on error)
|
// create response (release id on error)
|
||||||
let keys = noise::create_response(rng, peer, sender, st, &mut resp.noise).map_err(
|
let keys =
|
||||||
|e| {
|
noise::create_response(rng, peer, local, st, &mut resp.noise).map_err(|e| {
|
||||||
self.release(sender);
|
self.release(local);
|
||||||
e
|
e
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
// add macs to response
|
// add macs to response
|
||||||
peer.macs
|
peer.macs
|
||||||
@@ -425,190 +424,3 @@ impl Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::super::messages::*;
|
|
||||||
use super::*;
|
|
||||||
use hex;
|
|
||||||
use rand::rngs::OsRng;
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
fn setup_devices<R: RngCore + CryptoRng>(
|
|
||||||
rng: &mut R,
|
|
||||||
) -> (PublicKey, Device, PublicKey, Device) {
|
|
||||||
// generate new keypairs
|
|
||||||
|
|
||||||
let sk1 = StaticSecret::new(rng);
|
|
||||||
let pk1 = PublicKey::from(&sk1);
|
|
||||||
|
|
||||||
let sk2 = StaticSecret::new(rng);
|
|
||||||
let pk2 = PublicKey::from(&sk2);
|
|
||||||
|
|
||||||
// pick random psk
|
|
||||||
|
|
||||||
let mut psk = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut psk[..]);
|
|
||||||
|
|
||||||
// intialize devices on both ends
|
|
||||||
|
|
||||||
let mut dev1 = Device::new();
|
|
||||||
let mut dev2 = Device::new();
|
|
||||||
|
|
||||||
dev1.set_sk(Some(sk1));
|
|
||||||
dev2.set_sk(Some(sk2));
|
|
||||||
|
|
||||||
dev1.add(pk2).unwrap();
|
|
||||||
dev2.add(pk1).unwrap();
|
|
||||||
|
|
||||||
dev1.set_psk(pk2, psk).unwrap();
|
|
||||||
dev2.set_psk(pk1, psk).unwrap();
|
|
||||||
|
|
||||||
(pk1, dev1, pk2, dev2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait() {
|
|
||||||
thread::sleep(Duration::from_millis(20));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test longest possible handshake interaction (7 messages):
|
|
||||||
*
|
|
||||||
* 1. I -> R (initation)
|
|
||||||
* 2. I <- R (cookie reply)
|
|
||||||
* 3. I -> R (initation)
|
|
||||||
* 4. I <- R (response)
|
|
||||||
* 5. I -> R (cookie reply)
|
|
||||||
* 6. I -> R (initation)
|
|
||||||
* 7. I <- R (response)
|
|
||||||
*/
|
|
||||||
#[test]
|
|
||||||
fn handshake_under_load() {
|
|
||||||
let mut rng = OsRng::new().unwrap();
|
|
||||||
let (_pk1, dev1, pk2, dev2) = setup_devices(&mut rng);
|
|
||||||
|
|
||||||
let src1: SocketAddr = "172.16.0.1:8080".parse().unwrap();
|
|
||||||
let src2: SocketAddr = "172.16.0.2:7070".parse().unwrap();
|
|
||||||
|
|
||||||
// 1. device-1 : create first initation
|
|
||||||
let msg_init = dev1.begin(&mut rng, &pk2).unwrap();
|
|
||||||
|
|
||||||
// 2. device-2 : responds with CookieReply
|
|
||||||
let msg_cookie = match dev2.process(&mut rng, &msg_init, Some(&src1)).unwrap() {
|
|
||||||
(None, Some(msg), None) => msg,
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// device-1 : processes CookieReply (no response)
|
|
||||||
match dev1.process(&mut rng, &msg_cookie, Some(&src2)).unwrap() {
|
|
||||||
(None, None, None) => (),
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// avoid initation flood detection
|
|
||||||
wait();
|
|
||||||
|
|
||||||
// 3. device-1 : create second initation
|
|
||||||
let msg_init = dev1.begin(&mut rng, &pk2).unwrap();
|
|
||||||
|
|
||||||
// 4. device-2 : responds with noise response
|
|
||||||
let msg_response = match dev2.process(&mut rng, &msg_init, Some(&src1)).unwrap() {
|
|
||||||
(Some(_), Some(msg), Some(kp)) => {
|
|
||||||
assert_eq!(kp.initiator, false);
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 5. device-1 : responds with CookieReply
|
|
||||||
let msg_cookie = match dev1.process(&mut rng, &msg_response, Some(&src2)).unwrap() {
|
|
||||||
(None, Some(msg), None) => msg,
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// device-2 : processes CookieReply (no response)
|
|
||||||
match dev2.process(&mut rng, &msg_cookie, Some(&src1)).unwrap() {
|
|
||||||
(None, None, None) => (),
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// avoid initation flood detection
|
|
||||||
wait();
|
|
||||||
|
|
||||||
// 6. device-1 : create third initation
|
|
||||||
let msg_init = dev1.begin(&mut rng, &pk2).unwrap();
|
|
||||||
|
|
||||||
// 7. device-2 : responds with noise response
|
|
||||||
let (msg_response, kp1) = match dev2.process(&mut rng, &msg_init, Some(&src1)).unwrap() {
|
|
||||||
(Some(_), Some(msg), Some(kp)) => {
|
|
||||||
assert_eq!(kp.initiator, false);
|
|
||||||
(msg, kp)
|
|
||||||
}
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// device-1 : process noise response
|
|
||||||
let kp2 = match dev1.process(&mut rng, &msg_response, Some(&src2)).unwrap() {
|
|
||||||
(Some(_), None, Some(kp)) => {
|
|
||||||
assert_eq!(kp.initiator, true);
|
|
||||||
kp
|
|
||||||
}
|
|
||||||
_ => panic!("unexpected response"),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(kp1.send, kp2.recv);
|
|
||||||
assert_eq!(kp1.recv, kp2.send);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn handshake_no_load() {
|
|
||||||
let mut rng = OsRng::new().unwrap();
|
|
||||||
let (pk1, mut dev1, pk2, mut dev2) = setup_devices(&mut rng);
|
|
||||||
|
|
||||||
// do a few handshakes (every handshake should succeed)
|
|
||||||
|
|
||||||
for i in 0..10 {
|
|
||||||
println!("handshake : {}", i);
|
|
||||||
|
|
||||||
// create initiation
|
|
||||||
|
|
||||||
let msg1 = dev1.begin(&mut rng, &pk2).unwrap();
|
|
||||||
|
|
||||||
println!("msg1 = {} : {} bytes", hex::encode(&msg1[..]), msg1.len());
|
|
||||||
println!("msg1 = {:?}", Initiation::parse(&msg1[..]).unwrap());
|
|
||||||
|
|
||||||
// process initiation and create response
|
|
||||||
|
|
||||||
let (_, msg2, ks_r) = dev2.process(&mut rng, &msg1, None).unwrap();
|
|
||||||
|
|
||||||
let ks_r = ks_r.unwrap();
|
|
||||||
let msg2 = msg2.unwrap();
|
|
||||||
|
|
||||||
println!("msg2 = {} : {} bytes", hex::encode(&msg2[..]), msg2.len());
|
|
||||||
println!("msg2 = {:?}", Response::parse(&msg2[..]).unwrap());
|
|
||||||
|
|
||||||
assert!(!ks_r.initiator, "Responders key-pair is confirmed");
|
|
||||||
|
|
||||||
// process response and obtain confirmed key-pair
|
|
||||||
|
|
||||||
let (_, msg3, ks_i) = dev1.process(&mut rng, &msg2, None).unwrap();
|
|
||||||
let ks_i = ks_i.unwrap();
|
|
||||||
|
|
||||||
assert!(msg3.is_none(), "Returned message after response");
|
|
||||||
assert!(ks_i.initiator, "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);
|
|
||||||
|
|
||||||
// avoid initation flood detection
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
dev1.remove(pk2).unwrap();
|
|
||||||
dev2.remove(pk1).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ mod ratelimiter;
|
|||||||
mod timestamp;
|
mod timestamp;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
// publicly exposed interface
|
// publicly exposed interface
|
||||||
|
|
||||||
pub use device::Device;
|
pub use device::Device;
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ pub fn create_initiation<R: RngCore + CryptoRng>(
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
keyst: &KeyState,
|
keyst: &KeyState,
|
||||||
peer: &Peer,
|
peer: &Peer,
|
||||||
sender: u32,
|
local: u32,
|
||||||
msg: &mut NoiseInitiation,
|
msg: &mut NoiseInitiation,
|
||||||
) -> Result<(), HandshakeError> {
|
) -> Result<(), HandshakeError> {
|
||||||
debug!("create initation");
|
debug!("create initation");
|
||||||
@@ -233,7 +233,7 @@ pub fn create_initiation<R: RngCore + CryptoRng>(
|
|||||||
let hs = HASH!(&hs, peer.pk.as_bytes());
|
let hs = HASH!(&hs, peer.pk.as_bytes());
|
||||||
|
|
||||||
msg.f_type.set(TYPE_INITIATION as u32);
|
msg.f_type.set(TYPE_INITIATION as u32);
|
||||||
msg.f_sender.set(sender);
|
msg.f_sender.set(local); // from us
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
// (E_priv, E_pub) := DH-Generate()
|
||||||
|
|
||||||
@@ -292,7 +292,7 @@ pub fn create_initiation<R: RngCore + CryptoRng>(
|
|||||||
hs,
|
hs,
|
||||||
ck,
|
ck,
|
||||||
eph_sk,
|
eph_sk,
|
||||||
sender,
|
local,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -378,7 +378,7 @@ pub fn consume_initiation<'a>(
|
|||||||
pub fn create_response<R: RngCore + CryptoRng>(
|
pub fn create_response<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
peer: &Peer,
|
peer: &Peer,
|
||||||
sender: u32, // sending identifier
|
local: u32, // sending identifier
|
||||||
state: TemporaryState, // state from "consume_initiation"
|
state: TemporaryState, // state from "consume_initiation"
|
||||||
msg: &mut NoiseResponse, // resulting response
|
msg: &mut NoiseResponse, // resulting response
|
||||||
) -> Result<KeyPair, HandshakeError> {
|
) -> Result<KeyPair, HandshakeError> {
|
||||||
@@ -389,8 +389,8 @@ pub fn create_response<R: RngCore + CryptoRng>(
|
|||||||
let (receiver, eph_r_pk, hs, ck) = state;
|
let (receiver, eph_r_pk, hs, ck) = state;
|
||||||
|
|
||||||
msg.f_type.set(TYPE_RESPONSE as u32);
|
msg.f_type.set(TYPE_RESPONSE as u32);
|
||||||
msg.f_sender.set(sender);
|
msg.f_sender.set(local); // from us
|
||||||
msg.f_receiver.set(receiver);
|
msg.f_receiver.set(receiver); // to the sender of the initation
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
// (E_priv, E_pub) := DH-Generate()
|
||||||
|
|
||||||
@@ -447,11 +447,11 @@ pub fn create_response<R: RngCore + CryptoRng>(
|
|||||||
birth: Instant::now(),
|
birth: Instant::now(),
|
||||||
initiator: false,
|
initiator: false,
|
||||||
send: Key {
|
send: Key {
|
||||||
id: sender,
|
id: receiver,
|
||||||
key: key_send.into(),
|
key: key_send.into(),
|
||||||
},
|
},
|
||||||
recv: Key {
|
recv: Key {
|
||||||
id: receiver,
|
id: local,
|
||||||
key: key_recv.into(),
|
key: key_recv.into(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -472,13 +472,13 @@ pub fn consume_response(
|
|||||||
// retrieve peer and copy initiation state
|
// retrieve peer and copy initiation state
|
||||||
let peer = device.lookup_id(msg.f_receiver.get())?;
|
let peer = device.lookup_id(msg.f_receiver.get())?;
|
||||||
|
|
||||||
let (hs, ck, sender, eph_sk) = match *peer.state.lock() {
|
let (hs, ck, local, eph_sk) = match *peer.state.lock() {
|
||||||
State::InitiationSent {
|
State::InitiationSent {
|
||||||
hs,
|
hs,
|
||||||
ck,
|
ck,
|
||||||
sender,
|
local,
|
||||||
ref eph_sk,
|
ref eph_sk,
|
||||||
} => Ok((hs, ck, sender, StaticSecret::from(eph_sk.to_bytes()))),
|
} => Ok((hs, ck, local, StaticSecret::from(eph_sk.to_bytes()))),
|
||||||
_ => Err(HandshakeError::InvalidState),
|
_ => Err(HandshakeError::InvalidState),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
@@ -535,6 +535,7 @@ pub fn consume_response(
|
|||||||
// null the initiation state
|
// null the initiation state
|
||||||
// (to avoid replay of this response message)
|
// (to avoid replay of this response message)
|
||||||
*state = State::Reset;
|
*state = State::Reset;
|
||||||
|
let remote = msg.f_sender.get();
|
||||||
|
|
||||||
// return confirmed key-pair
|
// return confirmed key-pair
|
||||||
Ok((
|
Ok((
|
||||||
@@ -544,11 +545,11 @@ pub fn consume_response(
|
|||||||
birth,
|
birth,
|
||||||
initiator: true,
|
initiator: true,
|
||||||
send: Key {
|
send: Key {
|
||||||
id: sender,
|
id: remote,
|
||||||
key: key_send.into(),
|
key: key_send.into(),
|
||||||
},
|
},
|
||||||
recv: Key {
|
recv: Key {
|
||||||
id: msg.f_sender.get(),
|
id: local,
|
||||||
key: key_recv.into(),
|
key: key_recv.into(),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub struct Peer {
|
|||||||
pub enum State {
|
pub enum State {
|
||||||
Reset,
|
Reset,
|
||||||
InitiationSent {
|
InitiationSent {
|
||||||
sender: u32, // assigned sender id
|
local: u32, // local id assigned
|
||||||
eph_sk: StaticSecret,
|
eph_sk: StaticSecret,
|
||||||
hs: GenericArray<u8, U32>,
|
hs: GenericArray<u8, U32>,
|
||||||
ck: GenericArray<u8, U32>,
|
ck: GenericArray<u8, U32>,
|
||||||
@@ -83,7 +83,7 @@ impl Peer {
|
|||||||
|
|
||||||
pub fn reset_state(&self) -> Option<u32> {
|
pub fn reset_state(&self) -> Option<u32> {
|
||||||
match mem::replace(&mut *self.state.lock(), State::Reset) {
|
match mem::replace(&mut *self.state.lock(), State::Reset) {
|
||||||
State::InitiationSent { sender, .. } => Some(sender),
|
State::InitiationSent { local, .. } => Some(local),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ impl Peer {
|
|||||||
|
|
||||||
// reset state
|
// reset state
|
||||||
match *state {
|
match *state {
|
||||||
State::InitiationSent { sender, .. } => device.release(sender),
|
State::InitiationSent { local, .. } => device.release(local),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
197
src/wireguard/handshake/tests.rs
Normal file
197
src/wireguard/handshake/tests.rs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
use super::*;
|
||||||
|
use hex;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
|
use x25519_dalek::PublicKey;
|
||||||
|
use x25519_dalek::StaticSecret;
|
||||||
|
|
||||||
|
use super::messages::{Initiation, Response};
|
||||||
|
|
||||||
|
fn setup_devices<R: RngCore + CryptoRng>(rng: &mut R) -> (PublicKey, Device, PublicKey, Device) {
|
||||||
|
// generate new keypairs
|
||||||
|
|
||||||
|
let sk1 = StaticSecret::new(rng);
|
||||||
|
let pk1 = PublicKey::from(&sk1);
|
||||||
|
|
||||||
|
let sk2 = StaticSecret::new(rng);
|
||||||
|
let pk2 = PublicKey::from(&sk2);
|
||||||
|
|
||||||
|
// pick random psk
|
||||||
|
|
||||||
|
let mut psk = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut psk[..]);
|
||||||
|
|
||||||
|
// intialize devices on both ends
|
||||||
|
|
||||||
|
let mut dev1 = Device::new();
|
||||||
|
let mut dev2 = Device::new();
|
||||||
|
|
||||||
|
dev1.set_sk(Some(sk1));
|
||||||
|
dev2.set_sk(Some(sk2));
|
||||||
|
|
||||||
|
dev1.add(pk2).unwrap();
|
||||||
|
dev2.add(pk1).unwrap();
|
||||||
|
|
||||||
|
dev1.set_psk(pk2, psk).unwrap();
|
||||||
|
dev2.set_psk(pk1, psk).unwrap();
|
||||||
|
|
||||||
|
(pk1, dev1, pk2, dev2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait() {
|
||||||
|
thread::sleep(Duration::from_millis(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test longest possible handshake interaction (7 messages):
|
||||||
|
*
|
||||||
|
* 1. I -> R (initation)
|
||||||
|
* 2. I <- R (cookie reply)
|
||||||
|
* 3. I -> R (initation)
|
||||||
|
* 4. I <- R (response)
|
||||||
|
* 5. I -> R (cookie reply)
|
||||||
|
* 6. I -> R (initation)
|
||||||
|
* 7. I <- R (response)
|
||||||
|
*/
|
||||||
|
#[test]
|
||||||
|
fn handshake_under_load() {
|
||||||
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
let (_pk1, dev1, pk2, dev2) = setup_devices(&mut rng);
|
||||||
|
|
||||||
|
let src1: SocketAddr = "172.16.0.1:8080".parse().unwrap();
|
||||||
|
let src2: SocketAddr = "172.16.0.2:7070".parse().unwrap();
|
||||||
|
|
||||||
|
// 1. device-1 : create first initation
|
||||||
|
let msg_init = dev1.begin(&mut rng, &pk2).unwrap();
|
||||||
|
|
||||||
|
// 2. device-2 : responds with CookieReply
|
||||||
|
let msg_cookie = match dev2.process(&mut rng, &msg_init, Some(&src1)).unwrap() {
|
||||||
|
(None, Some(msg), None) => msg,
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// device-1 : processes CookieReply (no response)
|
||||||
|
match dev1.process(&mut rng, &msg_cookie, Some(&src2)).unwrap() {
|
||||||
|
(None, None, None) => (),
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid initation flood detection
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// 3. device-1 : create second initation
|
||||||
|
let msg_init = dev1.begin(&mut rng, &pk2).unwrap();
|
||||||
|
|
||||||
|
// 4. device-2 : responds with noise response
|
||||||
|
let msg_response = match dev2.process(&mut rng, &msg_init, Some(&src1)).unwrap() {
|
||||||
|
(Some(_), Some(msg), Some(kp)) => {
|
||||||
|
assert_eq!(kp.initiator, false);
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5. device-1 : responds with CookieReply
|
||||||
|
let msg_cookie = match dev1.process(&mut rng, &msg_response, Some(&src2)).unwrap() {
|
||||||
|
(None, Some(msg), None) => msg,
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// device-2 : processes CookieReply (no response)
|
||||||
|
match dev2.process(&mut rng, &msg_cookie, Some(&src1)).unwrap() {
|
||||||
|
(None, None, None) => (),
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid initation flood detection
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// 6. device-1 : create third initation
|
||||||
|
let msg_init = dev1.begin(&mut rng, &pk2).unwrap();
|
||||||
|
|
||||||
|
// 7. device-2 : responds with noise response
|
||||||
|
let (msg_response, kp1) = match dev2.process(&mut rng, &msg_init, Some(&src1)).unwrap() {
|
||||||
|
(Some(_), Some(msg), Some(kp)) => {
|
||||||
|
assert_eq!(kp.initiator, false);
|
||||||
|
(msg, kp)
|
||||||
|
}
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// device-1 : process noise response
|
||||||
|
let kp2 = match dev1.process(&mut rng, &msg_response, Some(&src2)).unwrap() {
|
||||||
|
(Some(_), None, Some(kp)) => {
|
||||||
|
assert_eq!(kp.initiator, true);
|
||||||
|
kp
|
||||||
|
}
|
||||||
|
_ => panic!("unexpected response"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(kp1.send, kp2.recv);
|
||||||
|
assert_eq!(kp1.recv, kp2.send);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handshake_no_load() {
|
||||||
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
let (pk1, mut dev1, pk2, mut dev2) = setup_devices(&mut rng);
|
||||||
|
|
||||||
|
// do a few handshakes (every handshake should succeed)
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
println!("handshake : {}", i);
|
||||||
|
|
||||||
|
// create initiation
|
||||||
|
|
||||||
|
let msg1 = dev1.begin(&mut rng, &pk2).unwrap();
|
||||||
|
|
||||||
|
println!("msg1 = {} : {} bytes", hex::encode(&msg1[..]), msg1.len());
|
||||||
|
println!(
|
||||||
|
"msg1 = {:?}",
|
||||||
|
Initiation::parse(&msg1[..]).expect("failed to parse initiation")
|
||||||
|
);
|
||||||
|
|
||||||
|
// process initiation and create response
|
||||||
|
|
||||||
|
let (_, msg2, ks_r) = dev2
|
||||||
|
.process(&mut rng, &msg1, None)
|
||||||
|
.expect("failed to process initiation");
|
||||||
|
|
||||||
|
let ks_r = ks_r.unwrap();
|
||||||
|
let msg2 = msg2.unwrap();
|
||||||
|
|
||||||
|
println!("msg2 = {} : {} bytes", hex::encode(&msg2[..]), msg2.len());
|
||||||
|
println!(
|
||||||
|
"msg2 = {:?}",
|
||||||
|
Response::parse(&msg2[..]).expect("failed to parse response")
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!ks_r.initiator, "Responders key-pair is confirmed");
|
||||||
|
|
||||||
|
// process response and obtain confirmed key-pair
|
||||||
|
|
||||||
|
let (_, msg3, ks_i) = dev1
|
||||||
|
.process(&mut rng, &msg2, None)
|
||||||
|
.expect("failed to process response");
|
||||||
|
let ks_i = ks_i.unwrap();
|
||||||
|
|
||||||
|
assert!(msg3.is_none(), "Returned message after response");
|
||||||
|
assert!(ks_i.initiator, "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.local_id());
|
||||||
|
dev2.release(ks_r.local_id());
|
||||||
|
|
||||||
|
// avoid initation flood detection
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
dev1.remove(pk2).unwrap();
|
||||||
|
dev2.remove(pk1).unwrap();
|
||||||
|
}
|
||||||
@@ -147,6 +147,12 @@ impl<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Device<E, C,
|
|||||||
/// - msg: IP packet to crypt-key route
|
/// - msg: IP packet to crypt-key route
|
||||||
///
|
///
|
||||||
pub fn send(&self, msg: Vec<u8>) -> Result<(), RouterError> {
|
pub fn send(&self, msg: Vec<u8>) -> Result<(), RouterError> {
|
||||||
|
debug_assert!(msg.len() > SIZE_MESSAGE_PREFIX);
|
||||||
|
log::trace!(
|
||||||
|
"Router, outbound packet = {}",
|
||||||
|
hex::encode(&msg[SIZE_MESSAGE_PREFIX..])
|
||||||
|
);
|
||||||
|
|
||||||
// ignore header prefix (for in-place transport message construction)
|
// ignore header prefix (for in-place transport message construction)
|
||||||
let packet = &msg[SIZE_MESSAGE_PREFIX..];
|
let packet = &msg[SIZE_MESSAGE_PREFIX..];
|
||||||
|
|
||||||
@@ -182,12 +188,20 @@ impl<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Device<E, C,
|
|||||||
return Err(RouterError::MalformedTransportMessage);
|
return Err(RouterError::MalformedTransportMessage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let header: LayoutVerified<&[u8], TransportHeader> = header;
|
let header: LayoutVerified<&[u8], TransportHeader> = header;
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
header.f_type.get() == TYPE_TRANSPORT as u32,
|
header.f_type.get() == TYPE_TRANSPORT as u32,
|
||||||
"this should be checked by the message type multiplexer"
|
"this should be checked by the message type multiplexer"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"Router, handle transport message: (receiver = {}, counter = {})",
|
||||||
|
header.f_receiver,
|
||||||
|
header.f_counter
|
||||||
|
);
|
||||||
|
|
||||||
// lookup peer based on receiver id
|
// lookup peer based on receiver id
|
||||||
let dec = self.state.recv.read();
|
let dec = self.state.recv.read();
|
||||||
let dec = dec
|
let dec = dec
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ impl<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>> Peer<E, C, T
|
|||||||
/// since the only way to add additional keys to the peer is by using this method
|
/// since the only way to add additional keys to the peer is by using this method
|
||||||
/// and a peer can have at most 3 keys allocated in the router at any time.
|
/// and a peer can have at most 3 keys allocated in the router at any time.
|
||||||
pub fn add_keypair(&self, new: KeyPair) -> Vec<u32> {
|
pub fn add_keypair(&self, new: KeyPair) -> Vec<u32> {
|
||||||
debug!("peer.add_keypair");
|
log::trace!("Router, add_keypair: {:?}", new);
|
||||||
|
|
||||||
let initiator = new.initiator;
|
let initiator = new.initiator;
|
||||||
let release = {
|
let release = {
|
||||||
|
|||||||
@@ -18,12 +18,15 @@ pub fn get_route<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>>(
|
|||||||
) -> Option<Arc<PeerInner<E, C, T, B>>> {
|
) -> Option<Arc<PeerInner<E, C, T, B>>> {
|
||||||
match packet.get(0)? >> 4 {
|
match packet.get(0)? >> 4 {
|
||||||
VERSION_IP4 => {
|
VERSION_IP4 => {
|
||||||
trace!("cryptokey router, get route for IPv4 packet");
|
|
||||||
|
|
||||||
// check length and cast to IPv4 header
|
// check length and cast to IPv4 header
|
||||||
let (header, _): (LayoutVerified<&[u8], IPv4Header>, _) =
|
let (header, _): (LayoutVerified<&[u8], IPv4Header>, _) =
|
||||||
LayoutVerified::new_from_prefix(packet)?;
|
LayoutVerified::new_from_prefix(packet)?;
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"Router, get route for IPv4 destination: {:?}",
|
||||||
|
Ipv4Addr::from(header.f_destination)
|
||||||
|
);
|
||||||
|
|
||||||
// check IPv4 source address
|
// check IPv4 source address
|
||||||
device
|
device
|
||||||
.ipv4
|
.ipv4
|
||||||
@@ -32,12 +35,15 @@ pub fn get_route<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>>(
|
|||||||
.and_then(|(_, _, p)| Some(p.clone()))
|
.and_then(|(_, _, p)| Some(p.clone()))
|
||||||
}
|
}
|
||||||
VERSION_IP6 => {
|
VERSION_IP6 => {
|
||||||
trace!("cryptokey router, get route for IPv6 packet");
|
|
||||||
|
|
||||||
// check length and cast to IPv6 header
|
// check length and cast to IPv6 header
|
||||||
let (header, _): (LayoutVerified<&[u8], IPv6Header>, _) =
|
let (header, _): (LayoutVerified<&[u8], IPv6Header>, _) =
|
||||||
LayoutVerified::new_from_prefix(packet)?;
|
LayoutVerified::new_from_prefix(packet)?;
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"Router, get route for IPv6 destination: {:?}",
|
||||||
|
Ipv6Addr::from(header.f_destination)
|
||||||
|
);
|
||||||
|
|
||||||
// check IPv6 source address
|
// check IPv6 source address
|
||||||
device
|
device
|
||||||
.ipv6
|
.ipv6
|
||||||
@@ -57,12 +63,15 @@ pub fn check_route<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>
|
|||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
match packet.get(0)? >> 4 {
|
match packet.get(0)? >> 4 {
|
||||||
VERSION_IP4 => {
|
VERSION_IP4 => {
|
||||||
trace!("cryptokey route, check route for IPv4 packet");
|
|
||||||
|
|
||||||
// check length and cast to IPv4 header
|
// check length and cast to IPv4 header
|
||||||
let (header, _): (LayoutVerified<&[u8], IPv4Header>, _) =
|
let (header, _): (LayoutVerified<&[u8], IPv4Header>, _) =
|
||||||
LayoutVerified::new_from_prefix(packet)?;
|
LayoutVerified::new_from_prefix(packet)?;
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"Router, check route for IPv4 source: {:?}",
|
||||||
|
Ipv4Addr::from(header.f_source)
|
||||||
|
);
|
||||||
|
|
||||||
// check IPv4 source address
|
// check IPv4 source address
|
||||||
device
|
device
|
||||||
.ipv4
|
.ipv4
|
||||||
@@ -77,12 +86,15 @@ pub fn check_route<E: Endpoint, C: Callbacks, T: tun::Writer, B: bind::Writer<E>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
VERSION_IP6 => {
|
VERSION_IP6 => {
|
||||||
trace!("cryptokey route, check route for IPv6 packet");
|
|
||||||
|
|
||||||
// check length and cast to IPv6 header
|
// check length and cast to IPv6 header
|
||||||
let (header, _): (LayoutVerified<&[u8], IPv6Header>, _) =
|
let (header, _): (LayoutVerified<&[u8], IPv6Header>, _) =
|
||||||
LayoutVerified::new_from_prefix(packet)?;
|
LayoutVerified::new_from_prefix(packet)?;
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"Router, check route for IPv6 source: {:?}",
|
||||||
|
Ipv6Addr::from(header.f_source)
|
||||||
|
);
|
||||||
|
|
||||||
// check IPv6 source address
|
// check IPv6 source address
|
||||||
device
|
device
|
||||||
.ipv6
|
.ipv6
|
||||||
|
|||||||
@@ -24,20 +24,17 @@ use super::super::{bind, tun, Endpoint};
|
|||||||
|
|
||||||
pub const SIZE_TAG: usize = 16;
|
pub const SIZE_TAG: usize = 16;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct JobEncryption {
|
pub struct JobEncryption {
|
||||||
pub msg: Vec<u8>,
|
pub msg: Vec<u8>,
|
||||||
pub keypair: Arc<KeyPair>,
|
pub keypair: Arc<KeyPair>,
|
||||||
pub counter: u64,
|
pub counter: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct JobDecryption {
|
pub struct JobDecryption {
|
||||||
pub msg: Vec<u8>,
|
pub msg: Vec<u8>,
|
||||||
pub keypair: Arc<KeyPair>,
|
pub keypair: Arc<KeyPair>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum JobParallel {
|
pub enum JobParallel {
|
||||||
Encryption(oneshot::Sender<JobEncryption>, JobEncryption),
|
Encryption(oneshot::Sender<JobEncryption>, JobEncryption),
|
||||||
Decryption(oneshot::Sender<Option<JobDecryption>>, JobDecryption),
|
Decryption(oneshot::Sender<Option<JobDecryption>>, JobDecryption),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use clear_on_drop::clear::Clear;
|
use clear_on_drop::clear::Clear;
|
||||||
|
use std::fmt;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -28,7 +29,7 @@ pub fn dummy_keypair(initiator: bool) -> KeyPair {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
pub key: [u8; 32],
|
pub key: [u8; 32],
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
@@ -48,7 +49,13 @@ impl PartialEq for Key {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl fmt::Debug for Key {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Key {{ id = {} }}", self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct KeyPair {
|
pub struct KeyPair {
|
||||||
pub birth: Instant, // when was the key-pair created
|
pub birth: Instant, // when was the key-pair created
|
||||||
pub initiator: bool, // has the key-pair been confirmed?
|
pub initiator: bool, // has the key-pair been confirmed?
|
||||||
@@ -56,6 +63,19 @@ pub struct KeyPair {
|
|||||||
pub recv: Key, // key for inbound messages
|
pub recv: Key, // key for inbound messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for KeyPair {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"KeyPair {{ initator = {}, age = {} secs, send = {:?}, recv = {:?}}}",
|
||||||
|
self.initiator,
|
||||||
|
self.birth.elapsed().as_secs(),
|
||||||
|
self.send,
|
||||||
|
self.recv
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl KeyPair {
|
impl KeyPair {
|
||||||
pub fn local_id(&self) -> u32 {
|
pub fn local_id(&self) -> u32 {
|
||||||
self.recv.id
|
self.recv.id
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ pub struct Wireguard<T: Tun, B: Bind> {
|
|||||||
const fn padding(size: usize, mtu: usize) -> usize {
|
const fn padding(size: usize, mtu: usize) -> usize {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
const fn min(a: usize, b: usize) -> usize {
|
const fn min(a: usize, b: usize) -> usize {
|
||||||
let m = (a > b) as usize;
|
let m = (a < b) as usize;
|
||||||
a * m + (1 - m) * b
|
a * m + (1 - m) * b
|
||||||
}
|
}
|
||||||
let pad = MESSAGE_PADDING_MULTIPLE;
|
let pad = MESSAGE_PADDING_MULTIPLE;
|
||||||
@@ -491,11 +491,16 @@ impl<T: Tun, B: Bind> Wireguard<T, B> {
|
|||||||
debug!("TUN worker, IP packet of {} bytes (MTU = {})", payload, mtu);
|
debug!("TUN worker, IP packet of {} bytes (MTU = {})", payload, mtu);
|
||||||
|
|
||||||
// truncate padding
|
// truncate padding
|
||||||
let payload = padding(payload, mtu);
|
let padded = padding(payload, mtu);
|
||||||
msg.truncate(router::SIZE_MESSAGE_PREFIX + payload);
|
log::trace!(
|
||||||
debug_assert!(payload <= mtu);
|
"TUN worker, payload length = {}, padded length = {}",
|
||||||
|
payload,
|
||||||
|
padded
|
||||||
|
);
|
||||||
|
msg.truncate(router::SIZE_MESSAGE_PREFIX + padded);
|
||||||
|
debug_assert!(padded <= mtu);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
if payload < mtu {
|
if padded < mtu {
|
||||||
(msg.len() - router::SIZE_MESSAGE_PREFIX) % MESSAGE_PADDING_MULTIPLE
|
(msg.len() - router::SIZE_MESSAGE_PREFIX) % MESSAGE_PADDING_MULTIPLE
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|||||||
Reference in New Issue
Block a user