diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/handshake/device.rs | 35 | ||||
-rw-r--r-- | src/handshake/macs.rs | 18 | ||||
-rw-r--r-- | src/handshake/messages.rs | 290 | ||||
-rw-r--r-- | src/handshake/noise.rs | 23 | ||||
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/noise/device.rs | 315 | ||||
-rw-r--r-- | src/noise/messages.rs | 208 | ||||
-rw-r--r-- | src/noise/mod.rs | 18 | ||||
-rw-r--r-- | src/noise/noise.rs | 458 | ||||
-rw-r--r-- | src/noise/peer.rs | 136 | ||||
-rw-r--r-- | src/noise/timestamp.rs | 34 | ||||
-rw-r--r-- | src/noise/types.rs | 81 |
12 files changed, 244 insertions, 1376 deletions
diff --git a/src/handshake/device.rs b/src/handshake/device.rs index 04e00f9..b28613a 100644 --- a/src/handshake/device.rs +++ b/src/handshake/device.rs @@ -7,7 +7,8 @@ use rand::rngs::OsRng; use x25519_dalek::PublicKey; use x25519_dalek::StaticSecret; -use super::messages; +use super::messages::{CookieReply, Initiation, Response}; +use super::messages::{TYPE_COOKIEREPLY, TYPE_INITIATION, TYPE_RESPONSE}; use super::noise; use super::peer::Peer; use super::types::*; @@ -170,20 +171,40 @@ where /// * `msg` - Byte slice containing the message (untrusted input) pub fn process(&self, msg: &[u8]) -> Result<Output<T>, HandshakeError> { match msg.get(0) { - Some(&messages::TYPE_INITIATION) => { + Some(&TYPE_INITIATION) => { + let msg = Initiation::parse(msg)?; + + // check mac footer and ratelimiter + // consume the initiation - let (peer, st) = noise::consume_initiation(self, msg)?; + let (peer, st) = noise::consume_initiation(self, &msg.noise)?; // allocate new index for response let sender = self.allocate(peer); - // create response (release id on error) - noise::create_response(peer, sender, st).map_err(|e| { + // create response (release id on error), TODO: take slice + let mut resp = Response::default(); + noise::create_response(peer, sender, st, &mut resp.noise).map_err(|e| { self.release(sender); e }) } - Some(&messages::TYPE_RESPONSE) => noise::consume_response(self, msg), + Some(&TYPE_RESPONSE) => { + let msg = Response::parse(msg)?; + + // check mac footer and ratelimiter + + noise::consume_response(self, &msg.noise) + } + Some(&TYPE_COOKIEREPLY) => { + let msg = CookieReply::parse(msg)?; + + // validate cookie reply + + // update cookie generator for peer + + unimplemented!() + } _ => Err(HandshakeError::InvalidMessageFormat), } } @@ -235,9 +256,9 @@ where #[cfg(test)] mod tests { + use super::super::messages::*; use super::*; use hex; - use messages::*; #[test] fn handshake() { diff --git a/src/handshake/macs.rs b/src/handshake/macs.rs index c8b97e3..05546a7 100644 --- a/src/handshake/macs.rs +++ b/src/handshake/macs.rs @@ -4,7 +4,7 @@ use blake2::Blake2s; use subtle::ConstantTimeEq; use x25519_dalek::PublicKey; -use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified}; +use super::messages::{CookieReply, MacsFooter}; const LABEL_MAC1: &[u8] = b"mac1----"; const LABEL_COOKIE: &[u8] = b"cookie--"; @@ -38,22 +38,6 @@ macro_rules! MAC { }}; } -#[repr(C)] -#[derive(Copy, Clone, FromBytes, AsBytes)] -pub struct MacsFooter { - pub f_mac1: [u8; SIZE_MAC], - pub f_mac2: [u8; SIZE_MAC], -} - -impl Default for MacsFooter { - fn default() -> Self { - Self { - f_mac1: [0u8; SIZE_MAC], - f_mac2: [0u8; SIZE_MAC], - } - } -} - struct Generator { mac1_key: [u8; 32], cookie_value: [u8; 16], diff --git a/src/handshake/messages.rs b/src/handshake/messages.rs index 8fa08b3..a2ff19a 100644 --- a/src/handshake/messages.rs +++ b/src/handshake/messages.rs @@ -8,10 +8,10 @@ use byteorder::LittleEndian; use zerocopy::byteorder::U32; use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified}; -use super::macs::MacsFooter; use super::timestamp; use super::types::*; +const SIZE_MAC: usize = 16; const SIZE_TAG: usize = 16; // poly1305 tag const SIZE_NONCE: usize = 16; // xchacha20 nonce const SIZE_COOKIE: usize = 16; // @@ -21,28 +21,20 @@ pub const TYPE_INITIATION: u8 = 1; pub const TYPE_RESPONSE: u8 = 2; pub const TYPE_COOKIEREPLY: u8 = 3; +/* Handshake messsages */ + #[repr(C)] #[derive(Copy, Clone, FromBytes, AsBytes)] -pub struct Initiation { - f_type: U32<LittleEndian>, - pub f_sender: U32<LittleEndian>, - pub f_ephemeral: [u8; SIZE_X25519_POINT], - pub f_static: [u8; SIZE_X25519_POINT], - pub f_static_tag: [u8; SIZE_TAG], - pub f_timestamp: timestamp::TAI64N, - pub f_timestamp_tag: [u8; SIZE_TAG], - pub f_macs: MacsFooter, +pub struct Response { + pub noise: NoiseResponse, // inner message covered by macs + pub macs: MacsFooter, } #[repr(C)] #[derive(Copy, Clone, FromBytes, AsBytes)] -pub struct Response { - f_type: U32<LittleEndian>, - pub f_sender: U32<LittleEndian>, - pub f_receiver: U32<LittleEndian>, - pub f_ephemeral: [u8; SIZE_X25519_POINT], - pub f_empty_tag: [u8; SIZE_TAG], - pub f_macs: MacsFooter, +pub struct Initiation { + pub noise: NoiseInitiation, // inner message covered by macs + pub macs: MacsFooter, } #[repr(C)] @@ -55,28 +47,45 @@ pub struct CookieReply { pub f_cookie_tag: [u8; SIZE_TAG], } -impl Default for Initiation { - fn default() -> Self { - Self { - f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32), +/* Inner sub-messages */ - f_sender: <U32<LittleEndian>>::ZERO, - f_ephemeral: [0u8; SIZE_X25519_POINT], - f_static: [0u8; SIZE_X25519_POINT], - f_static_tag: [0u8; SIZE_TAG], - f_timestamp: timestamp::ZERO, - f_timestamp_tag: [0u8; SIZE_TAG], - f_macs: Default::default(), - } - } +#[repr(C)] +#[derive(Copy, Clone, FromBytes, AsBytes)] +pub struct MacsFooter { + pub f_mac1: [u8; SIZE_MAC], + pub f_mac2: [u8; SIZE_MAC], } +#[repr(C)] +#[derive(Copy, Clone, FromBytes, AsBytes)] +pub struct NoiseInitiation { + f_type: U32<LittleEndian>, + pub f_sender: U32<LittleEndian>, + pub f_ephemeral: [u8; SIZE_X25519_POINT], + pub f_static: [u8; SIZE_X25519_POINT], + pub f_static_tag: [u8; SIZE_TAG], + pub f_timestamp: timestamp::TAI64N, + pub f_timestamp_tag: [u8; SIZE_TAG] +} + +#[repr(C)] +#[derive(Copy, Clone, FromBytes, AsBytes)] +pub struct NoiseResponse { + f_type: U32<LittleEndian>, + pub f_sender: U32<LittleEndian>, + pub f_receiver: U32<LittleEndian>, + pub f_ephemeral: [u8; SIZE_X25519_POINT], + pub f_empty_tag: [u8; SIZE_TAG] +} + +/* Zero copy parsing of handshake messages */ + impl Initiation { pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> { let msg: LayoutVerified<B, Self> = LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?; - if msg.f_type.get() != (TYPE_INITIATION as u32) { + if msg.noise.f_type.get() != (TYPE_INITIATION as u32) { return Err(HandshakeError::InvalidMessageFormat); } @@ -84,44 +93,25 @@ impl Initiation { } } -#[cfg(test)] -impl fmt::Debug for Initiation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, - "MessageInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}", - self.f_type.get(), - self.f_sender.get(), - 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) - ) - } -} +impl Response { + pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> { + let msg: LayoutVerified<B, Self> = + LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?; -#[cfg(test)] -impl PartialEq for Initiation { - fn eq(&self, other: &Self) -> bool { - self.f_type.get() == other.f_type.get() - && self.f_sender.get() == other.f_sender.get() - && self.f_ephemeral[..] == other.f_ephemeral[..] - && self.f_static[..] == other.f_static[..] - && self.f_static_tag[..] == other.f_static_tag[..] - && self.f_timestamp[..] == other.f_timestamp - && self.f_timestamp_tag[..] == other.f_timestamp_tag + if msg.noise.f_type.get() != (TYPE_RESPONSE as u32) { + return Err(HandshakeError::InvalidMessageFormat); + } + + Ok(msg) } } -#[cfg(test)] -impl Eq for Initiation {} - -impl Response { +impl CookieReply { pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> { let msg: LayoutVerified<B, Self> = LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?; - if msg.f_type.get() != (TYPE_RESPONSE as u32) { + if msg.f_type.get() != (TYPE_COOKIEREPLY as u32) { return Err(HandshakeError::InvalidMessageFormat); } @@ -129,24 +119,125 @@ impl Response { } } +/* Default values */ + impl Default for Response { fn default() -> Self { Self { + noise: Default::default(), + macs: Default::default(), + } + } +} + +impl Default for Initiation { + fn default() -> Self { + Self { + noise: Default::default(), + macs: Default::default(), + } + } +} + +impl Default for CookieReply { + fn default() -> Self { + Self { + f_type: <U32<LittleEndian>>::new(TYPE_COOKIEREPLY as u32), + f_receiver: <U32<LittleEndian>>::ZERO, + f_nonce: [0u8; SIZE_NONCE], + f_cookie: [0u8; SIZE_COOKIE], + f_cookie_tag: [0u8; SIZE_TAG], + } + } +} + +impl Default for MacsFooter { + fn default() -> Self { + Self { + f_mac1: [0u8; SIZE_MAC], + f_mac2: [0u8; SIZE_MAC], + } + } +} + +impl Default for NoiseInitiation { + fn default() -> Self { + Self { + f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32), + + f_sender: <U32<LittleEndian>>::ZERO, + f_ephemeral: [0u8; SIZE_X25519_POINT], + f_static: [0u8; SIZE_X25519_POINT], + f_static_tag: [0u8; SIZE_TAG], + f_timestamp: timestamp::ZERO, + f_timestamp_tag: [0u8; SIZE_TAG] + } + } +} + +impl Default for NoiseResponse { + fn default() -> Self { + Self { f_type: <U32<LittleEndian>>::new(TYPE_RESPONSE as u32), f_sender: <U32<LittleEndian>>::ZERO, f_receiver: <U32<LittleEndian>>::ZERO, f_ephemeral: [0u8; SIZE_X25519_POINT], f_empty_tag: [0u8; SIZE_TAG], - f_macs: Default::default(), } } } +/* Debug formatting (for testing purposes) */ + +#[cfg(test)] +impl fmt::Debug for Initiation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Initiation {{ {:?} || {:?} }}", self.noise, self.macs) + } +} + #[cfg(test)] impl fmt::Debug for Response { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Response {{ {:?} || {:?} }}", self.noise, self.macs) + } +} + +#[cfg(test)] +impl fmt::Debug for CookieReply { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, - "MessageResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}", + "CookieReply {{ type = {}, receiver = {}, nonce = {}, cookie = {}|{} }}", + self.f_type, + self.f_receiver, + hex::encode(self.f_nonce), + hex::encode(self.f_cookie), + hex::encode(self.f_cookie_tag) + ) + } +} + +#[cfg(test)] +impl fmt::Debug for NoiseInitiation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, + "NoiseInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}", + self.f_type.get(), + self.f_sender.get(), + 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) + ) + } +} + +#[cfg(test)] +impl fmt::Debug for NoiseResponse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, + "NoiseResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}", self.f_type, self.f_sender, self.f_receiver, @@ -157,17 +248,48 @@ impl fmt::Debug for Response { } #[cfg(test)] -impl PartialEq for Response { - fn eq(&self, other: &Self) -> bool { - self.f_type == other.f_type - && self.f_sender == other.f_sender - && self.f_receiver == other.f_receiver - && self.f_ephemeral == other.f_ephemeral - && self.f_empty_tag == other.f_empty_tag +impl fmt::Debug for MacsFooter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, + "Macs {{ mac1 = {}, mac2 = {} }}", + hex::encode(self.f_mac1), + hex::encode(self.f_mac2) + ) + } +} + +/* Equality (for testing purposes) */ + +macro_rules! eq_as_bytes { + ($type:path) => { + impl PartialEq for $type { + fn eq(&self, other: &Self) -> bool { + self.as_bytes() == other.as_bytes() + } + } + impl Eq for $type {} } } #[cfg(test)] +eq_as_bytes!(Initiation); + +#[cfg(test)] +eq_as_bytes!(Response); + +#[cfg(test)] +eq_as_bytes!(CookieReply); + +#[cfg(test)] +eq_as_bytes!(MacsFooter); + +#[cfg(test)] +eq_as_bytes!(NoiseInitiation); + +#[cfg(test)] +eq_as_bytes!(NoiseResponse); + +#[cfg(test)] mod tests { use super::*; @@ -175,22 +297,22 @@ mod tests { fn message_response_identity() { let mut msg: Response = Default::default(); - msg.f_sender.set(146252); - msg.f_receiver.set(554442); - msg.f_ephemeral = [ + msg.noise.f_sender.set(146252); + msg.noise.f_receiver.set(554442); + msg.noise.f_ephemeral = [ 0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c, 0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44, 0x13, 0x56, 0x52, 0x1f, ]; - msg.f_empty_tag = [ + msg.noise.f_empty_tag = [ 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde, ]; - msg.f_macs.f_mac1 = [ + msg.macs.f_mac1 = [ 0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54, 0x69, 0x29, ]; - msg.f_macs.f_mac2 = [ + msg.macs.f_mac2 = [ 0x4f, 0xd2, 0x1b, 0xfe, 0x77, 0xe6, 0x2e, 0xc9, 0x07, 0xe2, 0x87, 0x17, 0xbb, 0xe5, 0xdf, 0xbb, ]; @@ -204,33 +326,33 @@ mod tests { fn message_initiate_identity() { let mut msg: Initiation = Default::default(); - msg.f_sender.set(575757); - msg.f_ephemeral = [ + msg.noise.f_sender.set(575757); + msg.noise.f_ephemeral = [ 0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c, 0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44, 0x13, 0x56, 0x52, 0x1f, ]; - msg.f_static = [ + msg.noise.f_static = [ 0xdc, 0x33, 0x90, 0x15, 0x8f, 0x82, 0x3e, 0x06, 0x44, 0xa0, 0xde, 0x4c, 0x15, 0x6c, 0x5d, 0xa4, 0x65, 0x99, 0xf6, 0x6c, 0xa1, 0x14, 0x77, 0xf9, 0xeb, 0x6a, 0xec, 0xc3, 0x3c, 0xda, 0x47, 0xe1, ]; - msg.f_static_tag = [ + msg.noise.f_static_tag = [ 0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83, 0x6b, 0x42, ]; - msg.f_timestamp = [ + msg.noise.f_timestamp = [ 0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42, ]; - msg.f_timestamp_tag = [ + msg.noise.f_timestamp_tag = [ 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde, ]; - msg.f_macs.f_mac1 = [ + msg.macs.f_mac1 = [ 0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54, 0x69, 0x29, ]; - msg.f_macs.f_mac2 = [ + msg.macs.f_mac2 = [ 0x4f, 0xd2, 0x1b, 0xfe, 0x77, 0xe6, 0x2e, 0xc9, 0x07, 0xe2, 0x87, 0x17, 0xbb, 0xe5, 0xdf, 0xbb, ]; diff --git a/src/handshake/noise.rs b/src/handshake/noise.rs index a03cae2..72e0f47 100644 --- a/src/handshake/noise.rs +++ b/src/handshake/noise.rs @@ -18,7 +18,7 @@ use generic_array::GenericArray; use zerocopy::AsBytes; use super::device::Device; -use super::messages::{Initiation, Response}; +use super::messages::{NoiseInitiation, NoiseResponse}; use super::peer::{Peer, State}; use super::timestamp; use super::types::*; @@ -142,7 +142,7 @@ pub fn create_initiation<T: Copy>( sender: u32, ) -> Result<Vec<u8>, HandshakeError> { let mut rng = OsRng::new().unwrap(); - let mut msg: Initiation = Default::default(); + let mut msg: NoiseInitiation = Default::default(); // initialize state @@ -221,12 +221,8 @@ pub fn create_initiation<T: Copy>( pub fn consume_initiation<'a, T: Copy>( device: &'a Device<T>, - msg: &[u8], + msg: &NoiseInitiation, ) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> { - // parse message - - let msg = Initiation::parse(msg)?; - // initialize state let ck = INITIAL_CK; @@ -295,12 +291,11 @@ pub fn consume_initiation<'a, T: Copy>( pub fn create_response<T: Copy>( peer: &Peer<T>, - sender: u32, // sending identifier - state: TemporaryState, // state from "consume_initiation" + sender: u32, // sending identifier + state: TemporaryState, // state from "consume_initiation" + msg: &mut NoiseResponse, // resulting response ) -> Result<Output<T>, HandshakeError> { let mut rng = OsRng::new().unwrap(); - let mut msg: Response = Default::default(); - let (receiver, eph_r_pk, hs, ck) = state; msg.f_sender.set(sender); @@ -380,12 +375,8 @@ pub fn create_response<T: Copy>( pub fn consume_response<T: Copy>( device: &Device<T>, - msg: &[u8], + msg: &NoiseResponse, ) -> Result<Output<T>, HandshakeError> { - // parse message - - let msg = Response::parse(msg)?; - // retrieve peer and associated state let peer = device.lookup_id(msg.f_receiver.get())?; diff --git a/src/main.rs b/src/main.rs index b2995e7..f408d49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -mod noise; +mod handshake; mod types; -use noise::Device; +use handshake::Device; use types::KeyPair; fn main() {} diff --git a/src/noise/device.rs b/src/noise/device.rs deleted file mode 100644 index 04e00f9..0000000 --- a/src/noise/device.rs +++ /dev/null @@ -1,315 +0,0 @@ -use spin::RwLock; -use std::collections::HashMap; - -use rand::prelude::*; -use rand::rngs::OsRng; - -use x25519_dalek::PublicKey; -use x25519_dalek::StaticSecret; - -use super::messages; -use super::noise; -use super::peer::Peer; -use super::types::*; - -pub struct Device<T> { - pub sk: StaticSecret, // static secret key - pub pk: PublicKey, // static public key - pk_map: HashMap<[u8; 32], Peer<T>>, // public key -> peer state - id_map: RwLock<HashMap<u32, [u8; 32]>>, // receiver ids -> public key -} - -/* A mutable reference to the device needs to be held during configuration. - * Wrapping the device in a RwLock enables peer config after "configuration time" - */ -impl<T> Device<T> -where - T: Copy, -{ - /// Initialize a new handshake state machine - /// - /// # Arguments - /// - /// * `sk` - x25519 scalar representing the local private key - pub fn new(sk: StaticSecret) -> Device<T> { - Device { - pk: PublicKey::from(&sk), - sk: sk, - pk_map: HashMap::new(), - id_map: RwLock::new(HashMap::new()), - } - } - - /// Add a new public key to the state machine - /// To remove public keys, you must create a new machine instance - /// - /// # Arguments - /// - /// * `pk` - The public key to add - /// * `identifier` - Associated identifier which can be used to distinguish the peers - pub fn add(&mut self, pk: PublicKey, identifier: T) -> Result<(), ConfigError> { - // check that the pk is not added twice - - if let Some(_) = self.pk_map.get(pk.as_bytes()) { - return Err(ConfigError::new("Duplicate public key")); - }; - - // check that the pk is not that of the device - - if *self.pk.as_bytes() == *pk.as_bytes() { - return Err(ConfigError::new( - "Public key corresponds to secret key of interface", - )); - } - - // map : pk -> new index - - self.pk_map.insert( - *pk.as_bytes(), - Peer::new(identifier, pk, self.sk.diffie_hellman(&pk)), - ); - - Ok(()) - } - - /// Remove a peer by public key - /// To remove public keys, you must create a new machine instance - /// - /// # Arguments - /// - /// * `pk` - The public key of the peer to remove - /// - /// # Returns - /// - /// The call might fail if the public key is not found - pub fn remove(&mut self, pk: PublicKey) -> Result<(), ConfigError> { - // take write-lock on receive id table - let mut id_map = self.id_map.write(); - - // remove the peer - self.pk_map - .remove(pk.as_bytes()) - .ok_or(ConfigError::new("Public key not in device"))?; - - // pruge the id map (linear scan) - id_map.retain(|_, v| v != pk.as_bytes()); - Ok(()) - } - - /// Add a psk to the peer - /// - /// # Arguments - /// - /// * `pk` - The public key of the peer - /// * `psk` - The psk to set / unset - /// - /// # Returns - /// - /// The call might fail if the public key is not found - pub fn set_psk(&mut self, pk: PublicKey, psk: Option<Psk>) -> Result<(), ConfigError> { - match self.pk_map.get_mut(pk.as_bytes()) { - Some(mut peer) => { - peer.psk = match psk { - Some(v) => v, - None => [0u8; 32], - }; - Ok(()) - } - _ => Err(ConfigError::new("No such public key")), - } - } - - /// Return the psk for the peer - /// - /// # Arguments - /// - /// * `pk` - The public key of the peer - /// - /// # Returns - /// - /// A 32 byte array holding the PSK - /// - /// The call might fail if the public key is not found - pub fn get_psk(&self, pk: PublicKey) -> Result<Psk, ConfigError> { - match self.pk_map.get(pk.as_bytes()) { - Some(peer) => Ok(peer.psk), - _ => Err(ConfigError::new("No such public key")), - } - } - - /// Release an id back to the pool - /// - /// # Arguments - /// - /// * `id` - The (sender) id to release - pub fn release(&self, id: u32) { - let mut m = self.id_map.write(); - debug_assert!(m.contains_key(&id), "Releasing id not allocated"); - m.remove(&id); - } - - /// Begin a new handshake - /// - /// # Arguments - /// - /// * `pk` - Public key of peer to initiate handshake for - pub fn begin(&self, pk: &PublicKey) -> Result<Vec<u8>, HandshakeError> { - match self.pk_map.get(pk.as_bytes()) { - None => Err(HandshakeError::UnknownPublicKey), - Some(peer) => { - let sender = self.allocate(peer); - noise::create_initiation(self, peer, sender) - } - } - } - - /// Process a handshake message. - /// - /// # Arguments - /// - /// * `msg` - Byte slice containing the message (untrusted input) - pub fn process(&self, msg: &[u8]) -> Result<Output<T>, HandshakeError> { - match msg.get(0) { - Some(&messages::TYPE_INITIATION) => { - // consume the initiation - let (peer, st) = noise::consume_initiation(self, msg)?; - - // allocate new index for response - let sender = self.allocate(peer); - - // create response (release id on error) - noise::create_response(peer, sender, st).map_err(|e| { - self.release(sender); - e - }) - } - Some(&messages::TYPE_RESPONSE) => noise::consume_response(self, msg), - _ => Err(HandshakeError::InvalidMessageFormat), - } - } - - // Internal function - // - // Return the peer associated with the public key - pub(crate) fn lookup_pk(&self, pk: &PublicKey) -> Result<&Peer<T>, HandshakeError> { - self.pk_map - .get(pk.as_bytes()) - .ok_or(HandshakeError::UnknownPublicKey) - } - - // Internal function - // - // Return the peer currently associated with the receiver identifier - pub(crate) fn lookup_id(&self, id: u32) -> Result<&Peer<T>, HandshakeError> { - let im = self.id_map.read(); - let pk = im.get(&id).ok_or(HandshakeError::UnknownReceiverId)?; - match self.pk_map.get(pk) { - Some(peer) => Ok(peer), - _ => unreachable!(), // if the id-lookup succeeded, the peer should exist - } - } - - // Internal function - // - // Allocated a new receiver identifier for the peer - fn allocate(&self, peer: &Peer<T>) -> u32 { - let mut rng = OsRng::new().unwrap(); - - loop { - let id = rng.gen(); - - // check membership with read lock - if self.id_map.read().contains_key(&id) { - continue; - } - - // take write lock and add index - let mut m = self.id_map.write(); - if !m.contains_key(&id) { - m.insert(id, *peer.pk.as_bytes()); - return id; - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use hex; - use messages::*; - - #[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); - - // pick random psk - - let mut psk = [0u8; 32]; - rng.fill_bytes(&mut psk[..]); - - // intialize devices on both ends - - let mut dev1 = Device::new(sk1); - let mut dev2 = Device::new(sk2); - - dev1.add(pk2, 1337).unwrap(); - dev2.add(pk1, 2600).unwrap(); - - dev1.set_psk(pk2, Some(psk)).unwrap(); - dev2.set_psk(pk1, Some(psk)).unwrap(); - - // do a few handshakes - - for i in 0..10 { - println!("handshake : {}", i); - - // create initiation - - let msg1 = dev1.begin(&pk2).unwrap(); - - println!("msg1 = {}", hex::encode(&msg1[..])); - println!("msg1 = {:?}", Initiation::parse(&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::parse(&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); - } - - assert_eq!(dev1.get_psk(pk2).unwrap(), psk); - assert_eq!(dev2.get_psk(pk1).unwrap(), psk); - - dev1.remove(pk2).unwrap(); - dev2.remove(pk1).unwrap(); - } -} diff --git a/src/noise/messages.rs b/src/noise/messages.rs deleted file mode 100644 index dca49b9..0000000 --- a/src/noise/messages.rs +++ /dev/null @@ -1,208 +0,0 @@ -#[cfg(test)] -use hex; - -#[cfg(test)] -use std::fmt; - -use byteorder::LittleEndian; -use zerocopy::byteorder::U32; -use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified}; - -use super::types::*; - -const SIZE_TAG: usize = 16; -const SIZE_X25519_POINT: usize = 32; -const SIZE_TIMESTAMP: usize = 12; - -pub const TYPE_INITIATION: u8 = 1; -pub const TYPE_RESPONSE: u8 = 2; - -#[repr(C)] -#[derive(Copy, Clone, FromBytes, AsBytes)] -pub struct Initiation { - f_type: U32<LittleEndian>, - pub f_sender: U32<LittleEndian>, - pub f_ephemeral: [u8; SIZE_X25519_POINT], - pub f_static: [u8; SIZE_X25519_POINT], - pub f_static_tag: [u8; SIZE_TAG], - pub f_timestamp: [u8; SIZE_TIMESTAMP], - pub f_timestamp_tag: [u8; SIZE_TAG], -} - -impl Default for Initiation { - fn default() -> Self { - Self { - f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32), - - f_sender: <U32<LittleEndian>>::ZERO, - f_ephemeral: [0u8; SIZE_X25519_POINT], - f_static: [0u8; SIZE_X25519_POINT], - f_static_tag: [0u8; SIZE_TAG], - f_timestamp: [0u8; SIZE_TIMESTAMP], - f_timestamp_tag: [0u8; SIZE_TAG], - } - } -} - -impl Initiation { - pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> { - let msg: LayoutVerified<B, Self> = - LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?; - - if msg.f_type.get() != (TYPE_INITIATION as u32) { - return Err(HandshakeError::InvalidMessageFormat); - } - - Ok(msg) - } -} - -#[cfg(test)] -impl fmt::Debug for Initiation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, - "MessageInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}", - self.f_type.get(), - self.f_sender.get(), - 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) - ) - } -} - -#[cfg(test)] -impl PartialEq for Initiation { - fn eq(&self, other: &Self) -> bool { - self.f_type.get() == other.f_type.get() - && self.f_sender.get() == other.f_sender.get() - && self.f_ephemeral[..] == other.f_ephemeral[..] - && self.f_static[..] == other.f_static[..] - && self.f_static_tag[..] == other.f_static_tag[..] - && self.f_timestamp[..] == other.f_timestamp - && self.f_timestamp_tag[..] == other.f_timestamp_tag - } -} - -#[cfg(test)] -impl Eq for Initiation {} - -#[repr(C)] -#[derive(Copy, Clone, FromBytes, AsBytes)] -pub struct Response { - f_type: U32<LittleEndian>, - pub f_sender: U32<LittleEndian>, - pub f_receiver: U32<LittleEndian>, - pub f_ephemeral: [u8; SIZE_X25519_POINT], - pub f_empty_tag: [u8; SIZE_TAG], -} - -impl Response { - pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> { - let msg: LayoutVerified<B, Self> = - LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?; - - if msg.f_type.get() != (TYPE_RESPONSE as u32) { - return Err(HandshakeError::InvalidMessageFormat); - } - - Ok(msg) - } -} - -impl Default for Response { - fn default() -> Self { - Self { - f_type: <U32<LittleEndian>>::new(TYPE_RESPONSE as u32), - f_sender: <U32<LittleEndian>>::ZERO, - f_receiver: <U32<LittleEndian>>::ZERO, - f_ephemeral: [0u8; SIZE_X25519_POINT], - f_empty_tag: [0u8; SIZE_TAG], - } - } -} - -#[cfg(test)] -impl fmt::Debug for Response { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, - "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) - ) - } -} - -#[cfg(test)] -impl PartialEq for Response { - fn eq(&self, other: &Self) -> bool { - self.f_type == other.f_type - && self.f_sender == other.f_sender - && self.f_receiver == other.f_receiver - && self.f_ephemeral == other.f_ephemeral - && self.f_empty_tag == other.f_empty_tag - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn message_response_identity() { - let mut msg: Response = Default::default(); - - msg.f_sender.set(146252); - msg.f_receiver.set(554442); - msg.f_ephemeral = [ - 0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c, - 0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44, - 0x13, 0x56, 0x52, 0x1f, - ]; - msg.f_empty_tag = [ - 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, - 0x2f, 0xde, - ]; - - let buf: Vec<u8> = msg.as_bytes().to_vec(); - let msg_p = Response::parse(&buf[..]).unwrap(); - assert_eq!(msg, *msg_p.into_ref()); - } - - #[test] - fn message_initiate_identity() { - let mut msg: Initiation = Default::default(); - - msg.f_sender.set(575757); - msg.f_ephemeral = [ - 0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c, - 0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44, - 0x13, 0x56, 0x52, 0x1f, - ]; - msg.f_static = [ - 0xdc, 0x33, 0x90, 0x15, 0x8f, 0x82, 0x3e, 0x06, 0x44, 0xa0, 0xde, 0x4c, 0x15, 0x6c, - 0x5d, 0xa4, 0x65, 0x99, 0xf6, 0x6c, 0xa1, 0x14, 0x77, 0xf9, 0xeb, 0x6a, 0xec, 0xc3, - 0x3c, 0xda, 0x47, 0xe1, - ]; - msg.f_static_tag = [ - 0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83, - 0x6b, 0x42, - ]; - msg.f_timestamp = [ - 0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42, - ]; - msg.f_timestamp_tag = [ - 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, - 0x2f, 0xde, - ]; - - let buf: Vec<u8> = msg.as_bytes().to_vec(); - let msg_p = Initiation::parse(&buf[..]).unwrap(); - assert_eq!(msg, *msg_p.into_ref()); - } -} diff --git a/src/noise/mod.rs b/src/noise/mod.rs deleted file mode 100644 index d48b5e0..0000000 --- a/src/noise/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* Implementation of the: - * - * Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s - * - * Protocol pattern, see: http://www.noiseprotocol.org/noise.html. - * For documentation. - */ - -mod device; -mod messages; -mod noise; -mod peer; -mod timestamp; -mod types; - -// publicly exposed interface - -pub use device::Device; diff --git a/src/noise/noise.rs b/src/noise/noise.rs deleted file mode 100644 index 980f1db..0000000 --- a/src/noise/noise.rs +++ /dev/null @@ -1,458 +0,0 @@ -// DH -use x25519_dalek::PublicKey; -use x25519_dalek::StaticSecret; - -// HASH & MAC -use blake2::Blake2s; -use hmac::Hmac; - -// AEAD -use crypto::aead::{AeadDecryptor, AeadEncryptor}; -use crypto::chacha20poly1305::ChaCha20Poly1305; - -use rand::rngs::OsRng; - -use generic_array::typenum::*; -use generic_array::GenericArray; - -use zerocopy::AsBytes; - -use super::device::Device; -use super::messages::{Initiation, Response}; -use super::peer::{Peer, State}; -use super::timestamp; -use super::types::*; - -use crate::types::{Key, KeyPair}; - -// HMAC hasher (generic construction) - -type HMACBlake2s = Hmac<Blake2s>; - -// convenient alias to pass state temporarily into device.rs and back - -type TemporaryState = (u32, PublicKey, GenericArray<u8, U32>, GenericArray<u8, U32>); - -const SIZE_CK: usize = 32; -const SIZE_HS: usize = 32; -const SIZE_NONCE: usize = 8; - -// C := Hash(Construction) -const INITIAL_CK: [u8; SIZE_CK] = [ - 0x60, 0xe2, 0x6d, 0xae, 0xf3, 0x27, 0xef, 0xc0, 0x2e, 0xc3, 0x35, 0xe2, 0xa0, 0x25, 0xd2, 0xd0, - 0x16, 0xeb, 0x42, 0x06, 0xf8, 0x72, 0x77, 0xf5, 0x2d, 0x38, 0xd1, 0x98, 0x8b, 0x78, 0xcd, 0x36, -]; - -// H := Hash(C || Identifier) -const INITIAL_HS: [u8; SIZE_HS] = [ - 0x22, 0x11, 0xb3, 0x61, 0x08, 0x1a, 0xc5, 0x66, 0x69, 0x12, 0x43, 0xdb, 0x45, 0x8a, 0xd5, 0x32, - 0x2d, 0x9c, 0x6c, 0x66, 0x22, 0x93, 0xe8, 0xb7, 0x0e, 0xe1, 0x9c, 0x65, 0xba, 0x07, 0x9e, 0xf3, -]; - -const ZERO_NONCE: [u8; SIZE_NONCE] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - -macro_rules! HASH { - ( $($input:expr),* ) => {{ - use blake2::Digest; - let mut hsh = Blake2s::new(); - $( - hsh.input($input); - )* - hsh.result() - }}; -} - -macro_rules! HMAC { - ($key:expr, $($input:expr),*) => {{ - use hmac::Mac; - let mut mac = HMACBlake2s::new_varkey($key).unwrap(); - $( - mac.input($input); - )* - mac.result().code() - }}; -} - -macro_rules! KDF1 { - ($ck:expr, $input:expr) => {{ - let t0 = HMAC!($ck, $input); - let t1 = HMAC!(&t0, &[0x1]); - t1 - }}; -} - -macro_rules! KDF2 { - ($ck:expr, $input:expr) => {{ - let t0 = HMAC!($ck, $input); - let t1 = HMAC!(&t0, &[0x1]); - let t2 = HMAC!(&t0, &t1, &[0x2]); - (t1, t2) - }}; -} - -macro_rules! KDF3 { - ($ck:expr, $input:expr) => {{ - let t0 = HMAC!($ck, $input); - let t1 = HMAC!(&t0, &[0x1]); - let t2 = HMAC!(&t0, &t1, &[0x2]); - let t3 = HMAC!(&t0, &t2, &[0x3]); - (t1, t2, t3) - }}; -} - -macro_rules! SEAL { - ($key:expr, $aead:expr, $pt:expr, $ct:expr, $tag:expr) => {{ - let mut aead = ChaCha20Poly1305::new($key, &ZERO_NONCE, $aead); - aead.encrypt($pt, $ct, $tag); - }}; -} - -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)] -mod tests { - use super::*; - - const IDENTIFIER: &[u8] = b"WireGuard v1 zx2c4 Jason@zx2c4.com"; - const CONSTRUCTION: &[u8] = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; - - #[test] - fn precomputed_chain_key() { - assert_eq!(INITIAL_CK[..], HASH!(CONSTRUCTION)[..]); - } - - #[test] - fn precomputed_hash() { - assert_eq!(INITIAL_HS[..], HASH!(INITIAL_CK, IDENTIFIER)[..]); - } -} - -pub fn create_initiation<T: Copy>( - device: &Device<T>, - peer: &Peer<T>, - sender: u32, -) -> Result<Vec<u8>, HandshakeError> { - let mut rng = OsRng::new().unwrap(); - let mut msg: Initiation = Default::default(); - - // initialize state - - let ck = INITIAL_CK; - let hs = INITIAL_HS; - let hs = HASH!(&hs, peer.pk.as_bytes()); - - msg.f_sender.set(sender); - - // (E_priv, E_pub) := DH-Generate() - - let eph_sk = StaticSecret::new(&mut rng); - let eph_pk = PublicKey::from(&eph_sk); - - // C := Kdf(C, E_pub) - - let ck = KDF1!(&ck, eph_pk.as_bytes()); - - // msg.ephemeral := E_pub - - msg.f_ephemeral = *eph_pk.as_bytes(); - - // H := HASH(H, msg.ephemeral) - - let hs = HASH!(&hs, msg.f_ephemeral); - - // (C, k) := Kdf2(C, DH(E_priv, S_pub)) - - let (ck, key) = KDF2!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes()); - - // msg.static := Aead(k, 0, S_pub, H) - - SEAL!( - &key, - &hs, // ad - device.pk.as_bytes(), // pt - &mut msg.f_static, // ct - &mut msg.f_static_tag // tag - ); - - // 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) - - SEAL!( - &key, - &hs, // ad - ×tamp::now(), // pt - &mut msg.f_timestamp, // ct - &mut msg.f_timestamp_tag // tag - ); - - // H := Hash(H || msg.timestamp) - - let hs = HASH!(&hs, &msg.f_timestamp, &msg.f_timestamp_tag); - - // update state of peer - - peer.set_state(State::InitiationSent { - hs, - ck, - eph_sk, - sender, - }); - - // return message as vector - - Ok(msg.as_bytes().to_vec()) -} - -pub fn consume_initiation<'a, T: Copy>( - device: &'a Device<T>, - msg: &[u8], -) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> { - // parse message - - let msg = Initiation::parse(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_r_pk = PublicKey::from(msg.f_ephemeral); - let (ck, key) = KDF2!(&ck, device.sk.diffie_hellman(&eph_r_pk).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_pk(&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 - )?; - - // check and update timestamp - - 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.get(), eph_r_pk, hs, ck))) -} - -pub fn create_response<T: Copy>( - peer: &Peer<T>, - sender: u32, // sending identifier - state: TemporaryState, // state from "consume_initiation" -) -> Result<Output<T>, HandshakeError> { - let mut rng = OsRng::new().unwrap(); - let mut msg: Response = Default::default(); - - let (receiver, eph_r_pk, hs, ck) = state; - - msg.f_sender.set(sender); - msg.f_receiver.set(receiver); - - // (E_priv, E_pub) := DH-Generate() - - let eph_sk = StaticSecret::new(&mut rng); - let eph_pk = PublicKey::from(&eph_sk); - - // C := Kdf1(C, E_pub) - - let ck = KDF1!(&ck, eph_pk.as_bytes()); - - // msg.ephemeral := E_pub - - msg.f_ephemeral = *eph_pk.as_bytes(); - - // H := Hash(H || msg.ephemeral) - - let hs = HASH!(&hs, &msg.f_ephemeral); - - // C := Kdf1(C, DH(E_priv, E_pub)) - - let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes()); - - // C := Kdf1(C, DH(E_priv, S_pub)) - - let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes()); - - // (C, tau, k) := Kdf3(C, Q) - - let (ck, tau, key) = KDF3!(&ck, &peer.psk); - - // H := Hash(H || tau) - - let hs = HASH!(&hs, tau); - - // msg.empty := Aead(k, 0, [], H) - - SEAL!( - &key, - &hs, // ad - &[], // pt - &mut [], // ct - &mut msg.f_empty_tag // tag - ); - - /* not strictly needed - * // H := Hash(H || msg.empty) - * let hs = HASH!(&hs, &msg.f_empty_tag); - */ - - // derive key-pair - // (verbose code, due to GenericArray -> [u8; 32] conversion) - - let (key_recv, key_send) = KDF2!(&ck, &[]); - - // return response and unconfirmed key-pair - - Ok(( - peer.identifier, - Some(msg.as_bytes().to_vec()), - Some(KeyPair { - confirmed: false, - send: Key { - id: sender, - key: key_send.into(), - }, - recv: Key { - id: receiver, - key: key_recv.into(), - }, - }), - )) -} - -pub fn consume_response<T: Copy>( - device: &Device<T>, - msg: &[u8], -) -> Result<Output<T>, HandshakeError> { - // parse message - - let msg = Response::parse(msg)?; - - // retrieve peer and associated state - - let peer = device.lookup_id(msg.f_receiver.get())?; - let (hs, ck, sender, eph_sk) = match peer.get_state() { - State::Reset => Err(HandshakeError::InvalidState), - State::InitiationSent { - hs, - ck, - sender, - eph_sk, - } => Ok((hs, ck, sender, eph_sk)), - }?; - - // C := Kdf1(C, E_pub) - - let ck = KDF1!(&ck, &msg.f_ephemeral); - - // H := Hash(H || msg.ephemeral) - - let hs = HASH!(&hs, &msg.f_ephemeral); - - // C := Kdf1(C, DH(E_priv, E_pub)) - - let eph_r_pk = PublicKey::from(msg.f_ephemeral); - let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes()); - - // C := Kdf1(C, DH(E_priv, S_pub)) - - let ck = KDF1!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes()); - - // (C, tau, k) := Kdf3(C, Q) - - let (ck, tau, key) = KDF3!(&ck, &peer.psk); - - // H := Hash(H || tau) - - let hs = HASH!(&hs, tau); - - // msg.empty := Aead(k, 0, [], H) - - OPEN!( - &key, - &hs, // ad - &mut [], // pt - &[], // ct - &msg.f_empty_tag // tag - )?; - - // derive key-pair - - let (key_send, key_recv) = KDF2!(&ck, &[]); - - // return confirmed key-pair - - Ok(( - peer.identifier, - None, - Some(KeyPair { - confirmed: true, - send: Key { - id: sender, - key: key_send.into(), - }, - recv: Key { - id: msg.f_sender.get(), - key: key_recv.into(), - }, - }), - )) -} diff --git a/src/noise/peer.rs b/src/noise/peer.rs deleted file mode 100644 index 5b01d75..0000000 --- a/src/noise/peer.rs +++ /dev/null @@ -1,136 +0,0 @@ -use spin::Mutex; - -use generic_array::typenum::U32; -use generic_array::GenericArray; - -use x25519_dalek::PublicKey; -use x25519_dalek::SharedSecret; -use x25519_dalek::StaticSecret; - -use super::device::Device; -use super::timestamp; -use super::types::*; - -/* Represents the recomputation and state of a peer. - * - * This type is only for internal use and not exposed. - */ - -pub struct Peer<T> { - // external identifier - pub(crate) identifier: T, - - // mutable state - state: Mutex<State>, - timestamp: Mutex<Option<timestamp::TAI64N>>, - - // constant state - pub(crate) pk: PublicKey, // public key of peer - pub(crate) ss: SharedSecret, // precomputed DH(static, static) - pub(crate) psk: Psk, // psk of peer -} - -pub enum State { - Reset, - InitiationSent { - sender: u32, // assigned sender id - eph_sk: StaticSecret, - hs: GenericArray<u8, U32>, - ck: GenericArray<u8, U32>, - }, -} - -impl Clone for State { - fn clone(&self) -> State { - match self { - State::Reset => State::Reset, - State::InitiationSent { - sender, - eph_sk, - hs, - ck, - } => State::InitiationSent { - sender: *sender, - eph_sk: StaticSecret::from(eph_sk.to_bytes()), - hs: *hs, - ck: *ck, - }, - } - } -} - -impl<T> Peer<T> -where - T: Copy, -{ - pub fn new( - identifier: T, // external identifier - pk: PublicKey, // public key of peer - ss: SharedSecret, // precomputed DH(static, static) - ) -> Self { - Self { - identifier: identifier, - state: Mutex::new(State::Reset), - timestamp: Mutex::new(None), - pk: pk, - ss: ss, - psk: [0u8; 32], - } - } - - /// Return the state of the peer - /// - /// # Arguments - pub fn get_state(&self) -> State { - self.state.lock().clone() - } - - /// Set the state of the peer unconditionally - /// - /// # Arguments - /// - pub fn set_state(&self, state_new: State) { - *self.state.lock() = state_new; - } - - /// Set the mutable state of the peer conditioned on the timestamp being newer - /// - /// # Arguments - /// - /// * st_new - The updated state of the peer - /// * ts_new - The associated timestamp - pub fn check_timestamp( - &self, - device: &Device<T>, - timestamp_new: ×tamp::TAI64N, - ) -> Result<(), HandshakeError> { - let mut state = self.state.lock(); - let mut timestamp = self.timestamp.lock(); - - let update = match *timestamp { - None => true, - Some(timestamp_old) => { - if timestamp::compare(×tamp_old, ×tamp_new) { - true - } else { - false - } - } - }; - - if update { - // release existing identifier - match *state { - State::InitiationSent { sender, .. } => device.release(sender), - _ => (), - } - - // reset state and update timestamp - *state = State::Reset; - *timestamp = Some(*timestamp_new); - Ok(()) - } else { - Err(HandshakeError::OldTimestamp) - } - } -} diff --git a/src/noise/timestamp.rs b/src/noise/timestamp.rs deleted file mode 100644 index 0996f8b..0000000 --- a/src/noise/timestamp.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::time::{SystemTime, UNIX_EPOCH}; - -const TAI64_EPOCH: u64 = 0x4000000000000000; - -pub type TAI64N = [u8; 12]; - -pub fn zero() -> TAI64N { - [0u8; 12] -} - -pub fn now() -> TAI64N { - // 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 { - for i in 0..12 { - if new[i] > old[i] { - return true; - } - } - return false; -} diff --git a/src/noise/types.rs b/src/noise/types.rs deleted file mode 100644 index 0d9a5d3..0000000 --- a/src/noise/types.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::error::Error; -use std::fmt; - -use crate::types::KeyPair; - -/* Internal types for the noise IKpsk2 implementation */ - -// config error - -#[derive(Debug)] -pub struct ConfigError(String); - -impl ConfigError { - pub fn new(s: &str) -> Self { - ConfigError(s.to_string()) - } -} - -impl fmt::Display for ConfigError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ConfigError({})", self.0) - } -} - -impl Error for ConfigError { - fn description(&self) -> &str { - &self.0 - } - - fn source(&self) -> Option<&(dyn Error + 'static)> { - None - } -} - -// handshake error - -#[derive(Debug)] -pub enum HandshakeError { - DecryptionFailure, - UnknownPublicKey, - UnknownReceiverId, - InvalidMessageFormat, - OldTimestamp, - InvalidState, -} - -impl fmt::Display for HandshakeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - HandshakeError::DecryptionFailure => write!(f, "Failed to AEAD:OPEN"), - HandshakeError::UnknownPublicKey => write!(f, "Unknown public key"), - HandshakeError::UnknownReceiverId => { - write!(f, "Receiver id not allocated to any handshake") - } - HandshakeError::InvalidMessageFormat => write!(f, "Invalid handshake message format"), - HandshakeError::OldTimestamp => write!(f, "Timestamp is less/equal to the newest"), - HandshakeError::InvalidState => write!(f, "Message does not apply to handshake state"), - } - } -} - -impl Error for HandshakeError { - fn description(&self) -> &str { - "Generic Handshake Error" - } - - fn source(&self) -> Option<&(dyn Error + 'static)> { - None - } -} - -pub type Output<T> = ( - T, // external identifier associated with peer - // (e.g. a reference or vector index) - Option<Vec<u8>>, // message to send - Option<KeyPair>, // resulting key-pair of successful handshake -); - -// preshared key - -pub type Psk = [u8; 32]; |