Begin processing of initation

This commit is contained in:
Mathias Hall-Andersen
2019-07-18 13:20:03 +02:00
parent 4817ca7906
commit 14e9647afd
6 changed files with 223 additions and 47 deletions

View File

@@ -8,6 +8,7 @@ use x25519_dalek::PublicKey;
use x25519_dalek::StaticSecret; use x25519_dalek::StaticSecret;
use crate::noise; use crate::noise;
use crate::messages;
use crate::types::*; use crate::types::*;
use crate::peer::Peer; use crate::peer::Peer;
@@ -109,9 +110,9 @@ impl Device {
/// # Arguments /// # Arguments
/// ///
/// * `pk` - Public key of peer to initiate handshake for /// * `pk` - Public key of peer to initiate handshake for
pub fn begin(&self, pk : PublicKey) -> Result<Vec<u8>, HandshakeError> { pub fn begin(&self, pk : &PublicKey) -> Result<Vec<u8>, HandshakeError> {
match self.pkmap.get(pk.as_bytes()) { match self.pkmap.get(pk.as_bytes()) {
None => Err(HandshakeError::new()), None => Err(HandshakeError::UnknownPublicKey),
Some(&idx) => { Some(&idx) => {
let peer = &self.peers[idx]; let peer = &self.peers[idx];
let id = self.allocate(idx); let id = self.allocate(idx);
@@ -120,15 +121,27 @@ impl Device {
} }
} }
pub fn lookup(&self, pk : &PublicKey) -> Result<&Peer, HandshakeError> {
match self.pkmap.get(pk.as_bytes()) {
Some(&idx) => Ok(&self.peers[idx]),
_ => Err(HandshakeError::UnknownPublicKey)
}
}
/// Process a handshake message. /// Process a handshake message.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `msg` - Byte slice containing the message (untrusted input) /// * `msg` - Byte slice containing the message (untrusted input)
pub fn process(&self, msg : &[u8]) -> Result<Output, HandshakeError> { pub fn process(&self, msg : &[u8]) -> Result<Output, HandshakeError> {
// inspect type field
match msg.get(0) { match msg.get(0) {
_ => Err(HandshakeError::new()) Some(&messages::TYPE_INITIATION) => {
noise::process_initiation(self, msg)
},
Some(&messages::TYPE_RESPONSE) => {
Err(HandshakeError::InvalidMessageFormat)
},
_ => Err(HandshakeError::InvalidMessageFormat)
} }
} }
} }
@@ -147,3 +160,38 @@ impl Device {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handshake() {
// generate new keypairs
let mut rng = OsRng::new().unwrap();
let sk1 = StaticSecret::new(&mut rng);
let pk1 = PublicKey::from(&sk1);
let sk2 = StaticSecret::new(&mut rng);
let pk2 = PublicKey::from(&sk2);
// intialize devices on both ends
let mut dev1 = Device::new(sk1);
let mut dev2 = Device::new(sk2);
dev1.add(pk2).unwrap();
dev2.add(pk1).unwrap();
// create initiation
let msg1 = dev1.begin(&pk2).unwrap();
// process initiation and create response
let out1 = dev2.process(&msg1).unwrap();
}
}

View File

@@ -1,12 +1,16 @@
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::convert::TryFrom;
use crate::types::*;
const SIZE_TAG : usize = 16; const SIZE_TAG : usize = 16;
const SIZE_X25519_POINT : usize = 32; const SIZE_X25519_POINT : usize = 32;
const SIZE_TIMESTAMP : usize = 12; const SIZE_TIMESTAMP : usize = 12;
pub const TYPE_INITIATION : u32 = 1; pub const TYPE_INITIATION : u8 = 1;
pub const TYPE_RESPONSE : u32 = 2; pub const TYPE_RESPONSE : u8 = 2;
/* Wireguard handshake (noise) initiation message /* Wireguard handshake (noise) initiation message
* initator -> responder * initator -> responder
@@ -23,22 +27,42 @@ pub struct Initiation {
pub f_timestamp_tag : [u8; SIZE_TAG], pub f_timestamp_tag : [u8; SIZE_TAG],
} }
impl From<&[u8]> for Initiation { impl TryFrom<&[u8]> for Initiation {
fn from(b: &[u8]) -> Self {
type Error = HandshakeError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
// check length of slice matches message
if value.len() != mem::size_of::<Self>() {
return Err(HandshakeError::InvalidMessageFormat);
}
// create owned copy // create owned copy
let mut owned = [0u8; mem::size_of::<Self>()]; let mut owned = [0u8; mem::size_of::<Self>()];
let mut msg : Self; let mut msg : Self;
owned.copy_from_slice(b); owned.copy_from_slice(value);
// cast to Initiation // cast to Initiation
unsafe { unsafe {
msg = mem::transmute::<[u8; mem::size_of::<Self>()], Self>(owned); msg = mem::transmute::<[u8; mem::size_of::<Self>()], Self>(owned);
}; };
// correct endianness // correct endianness
msg.f_type = msg.f_type.to_le(); msg.f_type = msg.f_type.to_le();
msg.f_sender = msg.f_sender.to_le(); msg.f_sender = msg.f_sender.to_le();
msg
// check type and reserved fields
if msg.f_type != (TYPE_INITIATION as u32) {
return Err(HandshakeError::InvalidMessageFormat);
}
Ok(msg)
} }
} }
@@ -71,7 +95,7 @@ impl fmt::Debug for Initiation {
impl Default for Initiation { impl Default for Initiation {
fn default() -> Self { fn default() -> Self {
Self { Self {
f_type : TYPE_INITIATION, f_type : TYPE_INITIATION as u32,
f_sender : 0, f_sender : 0,
f_ephemeral : [0u8; SIZE_X25519_POINT], f_ephemeral : [0u8; SIZE_X25519_POINT],
f_static : [0u8; SIZE_X25519_POINT], f_static : [0u8; SIZE_X25519_POINT],
@@ -112,23 +136,43 @@ pub struct Response {
pub f_empty_tag : [u8; SIZE_TAG], pub f_empty_tag : [u8; SIZE_TAG],
} }
impl From<&[u8]> for Response { impl TryFrom<&[u8]> for Response {
fn from(b: &[u8]) -> Self {
type Error = HandshakeError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
// check length of slice matches message
if value.len() != mem::size_of::<Self>() {
return Err(HandshakeError::InvalidMessageFormat);
}
// create owned copy // create owned copy
let mut owned = [0u8; mem::size_of::<Self>()]; let mut owned = [0u8; mem::size_of::<Self>()];
let mut msg : Self; let mut msg : Self;
owned.copy_from_slice(b); owned.copy_from_slice(value);
// cast to MessageResponse // cast to MessageResponse
unsafe { unsafe {
msg = mem::transmute::<[u8; mem::size_of::<Self>()], Self>(owned); msg = mem::transmute::<[u8; mem::size_of::<Self>()], Self>(owned);
}; };
// correct endianness // correct endianness
msg.f_type = msg.f_type.to_le(); msg.f_type = msg.f_type.to_le();
msg.f_sender = msg.f_sender.to_le(); msg.f_sender = msg.f_sender.to_le();
msg.f_receiver = msg.f_receiver.to_le(); msg.f_receiver = msg.f_receiver.to_le();
msg
// check type and reserved fields
if msg.f_type != (TYPE_RESPONSE as u32) {
return Err(HandshakeError::InvalidMessageFormat);
}
Ok(msg)
} }
} }
@@ -177,7 +221,7 @@ mod tests {
#[test] #[test]
fn message_response_identity() { fn message_response_identity() {
let msg = Response { let msg = Response {
f_type : TYPE_RESPONSE, f_type : TYPE_RESPONSE as u32,
f_sender : 146252, f_sender : 146252,
f_receiver : 554442, f_receiver : 554442,
f_ephemeral : [ f_ephemeral : [
@@ -195,13 +239,14 @@ mod tests {
}; };
let buf : Vec<u8> = msg.into(); let buf : Vec<u8> = msg.into();
assert_eq!(msg, Response::from(&buf[..])); let msg_p : Response = Response::try_from(&buf[..]).unwrap();
assert_eq!(msg, msg_p);
} }
#[test] #[test]
fn message_initiate_identity() { fn message_initiate_identity() {
let msg = Initiation { let msg = Initiation {
f_type : TYPE_RESPONSE, f_type : TYPE_INITIATION as u32,
f_sender : 575757, f_sender : 575757,
f_ephemeral : [ f_ephemeral : [
// ephemeral public key // ephemeral public key
@@ -218,7 +263,7 @@ mod tests {
0xeb, 0x6a, 0xec, 0xc3, 0x3c, 0xda, 0x47, 0xe1 0xeb, 0x6a, 0xec, 0xc3, 0x3c, 0xda, 0x47, 0xe1
], ],
f_static_tag : [ f_static_tag : [
// tag // poly1305 tag
0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02,
0x45, 0x5d, 0x86, 0x37, 0xee, 0x83, 0x6b, 0x42 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83, 0x6b, 0x42
], ],
@@ -228,13 +273,13 @@ mod tests {
0x78, 0x28, 0x57, 0x42 0x78, 0x28, 0x57, 0x42
], ],
f_timestamp_tag : [ f_timestamp_tag : [
// tag // poly1305 tag
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05,
0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde
] ]
}; };
let buf : Vec<u8> = msg.into(); let buf : Vec<u8> = msg.into();
assert_eq!(msg, Initiation::from(&buf[..])); assert_eq!(msg, Initiation::try_from(&buf[..]).unwrap());
} }
} }

View File

@@ -1,3 +1,5 @@
use std::convert::TryFrom;
// DH // DH
use x25519_dalek::PublicKey; use x25519_dalek::PublicKey;
use x25519_dalek::StaticSecret; use x25519_dalek::StaticSecret;
@@ -15,7 +17,7 @@ use rand::rngs::OsRng;
use crate::types::*; use crate::types::*;
use crate::peer::{State, Peer}; use crate::peer::{State, Peer};
use crate::device::Device; use crate::device::Device;
use crate::messages; use crate::messages::{Initiation, Response};
use crate::timestamp; use crate::timestamp;
type HMACBlake2s = Hmac<Blake2s>; type HMACBlake2s = Hmac<Blake2s>;
@@ -129,6 +131,19 @@ macro_rules! SEAL {
} }
} }
macro_rules! OPEN {
($key:expr, $aead:expr, $pt:expr, $ct:expr, $tag:expr) => {
{
let mut aead = ChaCha20Poly1305::new($key, &ZERO_NONCE, $aead);
if !aead.decrypt($ct, $pt, $tag) {
Err(HandshakeError::DecryptionFailure)
} else {
Ok(())
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -156,7 +171,7 @@ pub fn create_initiation(
id : u32 id : u32
) -> Result<Vec<u8>, HandshakeError> { ) -> Result<Vec<u8>, HandshakeError> {
let mut rng = OsRng::new().unwrap(); let mut rng = OsRng::new().unwrap();
let mut msg : messages::Initiation = Default::default(); let mut msg : Initiation = Default::default();
// initialize state // initialize state
@@ -185,10 +200,7 @@ pub fn create_initiation(
// (C, k) := Kdf2(C, DH(E_priv, S_pub)) // (C, k) := Kdf2(C, DH(E_priv, S_pub))
let (ck, key) = KDF2!( let (ck, key) = KDF2!(&ck, sk.diffie_hellman(&peer.pk).as_bytes());
&ck,
sk.diffie_hellman(&peer.pk).as_bytes()
);
// msg.static := Aead(k, 0, S_pub, H) // msg.static := Aead(k, 0, S_pub, H)
@@ -206,10 +218,7 @@ pub fn create_initiation(
// (C, k) := Kdf2(C, DH(S_priv, S_pub)) // (C, k) := Kdf2(C, DH(S_priv, S_pub))
let (ck, key) = KDF2!( let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
&ck,
peer.ss.as_bytes() // precomputed static-static
);
// msg.timestamp := Aead(k, 0, Timestamp(), H) // msg.timestamp := Aead(k, 0, Timestamp(), H)
@@ -236,9 +245,80 @@ pub fn create_initiation(
// return message as vector // return message as vector
Ok(messages::Initiation::into(msg)) Ok(Initiation::into(msg))
} }
pub fn process_initiation(device : &Device, peer : &Peer) -> Result<Output, ()> { pub fn process_initiation(device : &Device, msg : &[u8]) -> Result<Output, HandshakeError> {
Err(())
// parse message
let msg = Initiation::try_from(msg)?;
// initialize state
let ck = INITIAL_CK;
let hs = INITIAL_HS;
let hs = HASH!(&hs, device.pk.as_bytes());
// C := Kdf(C, E_pub)
let ck = KDF1!(&ck, &msg.f_ephemeral);
// H := HASH(H, msg.ephemeral)
let hs = HASH!(&hs, &msg.f_ephemeral);
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
let eph = PublicKey::from(msg.f_ephemeral);
let (ck, key) = KDF2!(
&ck,
device.sk.diffie_hellman(&eph).as_bytes()
);
// msg.static := Aead(k, 0, S_pub, H)
let mut pk = [0u8; 32];
OPEN!(
&key,
&hs, // ad
&mut pk, // pt
&msg.f_static, // ct
&msg.f_static_tag // tag
)?;
let peer = device.lookup(&PublicKey::from(pk))?;
// H := Hash(H || msg.static)
let hs = HASH!(&hs, &msg.f_static, &msg.f_static_tag);
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
// msg.timestamp := Aead(k, 0, Timestamp(), H)
let mut ts = timestamp::zero();
OPEN!(
&key,
&hs, // ad
&mut ts, // pt
&msg.f_timestamp, // ct
&msg.f_timestamp_tag // tag
)?;
// update state of peer
peer.set_state_timestamp(
State::InitiationSent{
hs : hs,
ck : ck
},
&ts
)?;
Ok(Output(None, None))
} }

View File

@@ -76,7 +76,7 @@ impl Peer {
&self, &self,
state_new : State, state_new : State,
timestamp_new : &timestamp::TAI64N timestamp_new : &timestamp::TAI64N
) -> bool { ) -> Result<(), HandshakeError> {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
let mut timestamp = self.timestamp.lock().unwrap(); let mut timestamp = self.timestamp.lock().unwrap();
match *timestamp { match *timestamp {
@@ -84,15 +84,15 @@ impl Peer {
// no prior timestamp know // no prior timestamp know
*state = state_new; *state = state_new;
*timestamp = Some(*timestamp_new); *timestamp = Some(*timestamp_new);
true Ok(())
}, },
Some(timestamp_old) => if timestamp::compare(&timestamp_old, &timestamp_new) { Some(timestamp_old) => if timestamp::compare(&timestamp_old, &timestamp_new) {
// new timestamp is strictly greater // new timestamp is strictly greater
*state = state_new; *state = state_new;
*timestamp = Some(*timestamp_new); *timestamp = Some(*timestamp_new);
true Ok(())
} else { } else {
false Err(HandshakeError::OldTimestamp)
} }
} }
} }

View File

@@ -1,7 +1,11 @@
pub type TAI64N = [u8; 12]; pub type TAI64N = [u8; 12];
pub fn zero() -> TAI64N {
[0u8; 12]
}
pub fn now() -> TAI64N { pub fn now() -> TAI64N {
[0u8; 12] // TODO [0u8; 12] // TODO, return current timestamp
} }
pub fn compare(old : &TAI64N, new : &TAI64N) -> bool { pub fn compare(old : &TAI64N, new : &TAI64N) -> bool {

View File

@@ -36,12 +36,11 @@ impl Error for ConfigError {
// handshake error // handshake error
#[derive(Debug)] #[derive(Debug)]
pub struct HandshakeError {} pub enum HandshakeError {
DecryptionFailure,
impl HandshakeError { UnknownPublicKey,
pub fn new() -> Self { InvalidMessageFormat,
HandshakeError{} OldTimestamp
}
} }
impl fmt::Display for HandshakeError { impl fmt::Display for HandshakeError {
@@ -74,8 +73,8 @@ pub struct KeyPair {
} }
pub struct Output ( pub struct Output (
Option<KeyPair>, // resulting key-pair of successful handshake pub Option<KeyPair>, // resulting key-pair of successful handshake
Option<Vec<u8>> // message to send pub Option<Vec<u8>> // message to send
); );
// per-peer state machine // per-peer state machine