From 5a5d09bb416e06db6559d635dcbde46a52533b3b Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Tue, 16 Jul 2019 23:37:25 +0200 Subject: Create initiation message --- Cargo.lock | 89 +++++++++++++++++++++++--------- Cargo.toml | 4 +- src/device.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +- src/machine.rs | 155 ------------------------------------------------------- src/messages.rs | 79 ++++++++++++++-------------- src/noise.rs | 138 +++++++++++++++++++++++++++++++++++++++---------- src/timestamp.rs | 5 ++ src/types.rs | 84 +++++++++++++++++++++++++++--- 9 files changed, 454 insertions(+), 253 deletions(-) create mode 100644 src/device.rs delete mode 100644 src/machine.rs create mode 100644 src/timestamp.rs diff --git a/Cargo.lock b/Cargo.lock index d8b0c93..2351810 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,14 +36,6 @@ name = "cc" version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "chacha20-poly1305-aead" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "clear_on_drop" version = "0.2.3" @@ -60,11 +52,6 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "constant_time_eq" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "crypto-mac" version = "0.7.0" @@ -100,16 +87,13 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "generic-array" -version = "0.12.0" +name = "gcc" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "generic-array" -version = "0.13.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -134,6 +118,27 @@ name = "opaque-debug" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.6.5" @@ -238,6 +243,28 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "subtle" version = "1.0.0" @@ -248,6 +275,16 @@ name = "subtle" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" @@ -258,10 +295,10 @@ name = "wg-handshake" version = "0.1.0" dependencies = [ "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chacha20-poly1305-aead 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "x25519-dalek 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -301,19 +338,19 @@ dependencies = [ "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0daef304fa0b4238f5f7ed7178774b43b06f6a9b6509f6642bef4ff1f7b9b2" -"checksum chacha20-poly1305-aead 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77d2058ba29594f69c75e8a9018e0485e3914ca5084e3613cd64529042f5423b" "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1f8a6fc0376eb52dc18af94915cc04dfdf8353746c0e8c550ae683a0815e5c1" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" -"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" "checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff" "checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" +"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -325,8 +362,12 @@ dependencies = [ "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "702662512f3ddeb74a64ce2fbbf3707ee1b6bb663d28bb054e0779bbc720d926" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index 8785ef3..354f8aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" rand = "0.6.5" blake2 = "0.8.0" hmac = "0.7.1" -chacha20-poly1305-aead = "0.1.2" -generic-array = "0.13" +rust-crypto = "^0.2" +generic-array = "0.12" [dependencies.x25519-dalek] version = "^0.5" diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 0000000..6107e34 --- /dev/null +++ b/src/device.rs @@ -0,0 +1,150 @@ +use std::sync::Mutex; +use std::collections::HashMap; + +use rand::prelude::*; +use rand::rngs::OsRng; + +use x25519_dalek::PublicKey; +use x25519_dalek::StaticSecret; + +use crate::noise; +use crate::types::*; + +pub struct Device { + pub sk : StaticSecret, // static secret key + pub pk : PublicKey, // static public key + peers : Vec, // peer index -> state + pkmap : HashMap<[u8; 32], usize>, // public key -> peer index + ids : Mutex> // receive ids -> peer index +} + +/* A mutable reference to the state machine needs to be held, + * during configuration. + */ +impl Device { + /// Initialize a new handshake state machine + /// + /// # Arguments + /// + /// * `sk` - x25519 scalar representing the local private key + pub fn new(sk : StaticSecret) -> Device { + Device { + pk : PublicKey::from(&sk), + sk : sk, + peers : vec![], + pkmap : HashMap::new(), + ids : Mutex::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 + pub fn add(&mut self, pk : PublicKey) -> Result<(), ConfigError> { + // check that the pk is not added twice + + if let Some(_) = self.pkmap.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.pkmap.insert(*pk.as_bytes(), self.peers.len()); + + // map : new index -> peer + + self.peers.push(Peer { + state : Mutex::new(State::Reset{ts : None}), + pk : pk, + ss : self.sk.diffie_hellman(&pk), + psk : [0u8; 32] + }); + + 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 psk(&mut self, pk : PublicKey, psk : Option) -> Result<(), ConfigError> { + match self.pkmap.get(pk.as_bytes()) { + Some(&idx) => { + let peer = &mut self.peers[idx]; + peer.psk = match psk { + Some(v) => v, + None => [0u8; 32], + }; + Ok(()) + }, + _ => 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) { + self.ids.lock().unwrap().remove(&id); + } + + /// Begin a new handshake + /// + /// # Arguments + /// + /// * `pk` - Public key of peer to initiate handshake for + pub fn begin(&self, pk : PublicKey) -> Result, HandshakeError> { + match self.pkmap.get(pk.as_bytes()) { + None => Err(HandshakeError::new()), + Some(&idx) => { + let peer = &self.peers[idx]; + let id = self.allocate(idx); + noise::create_initiation(self, peer, id) + } + } + } + + /// Process a handshake message. + /// + /// # Arguments + /// + /// * `msg` - Byte slice containing the message (untrusted input) + pub fn process(&self, msg : &[u8]) -> Result { + // inspect type field + match msg.get(0) { + _ => Err(HandshakeError::new()) + } + } +} + +impl Device { + // allocate a new index (id), for peer with idx + fn allocate(&self, idx : usize) -> u32 { + let mut rng = OsRng::new().unwrap(); + let mut table = self.ids.lock().unwrap(); + loop { + let id = rng.gen(); + if !table.contains_key(&id) { + table.insert(id, idx); + return id; + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 42f8449..2edf0fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod types; mod noise; mod messages; -mod machine; +mod device; +mod timestamp; diff --git a/src/machine.rs b/src/machine.rs deleted file mode 100644 index a056d0c..0000000 --- a/src/machine.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::sync::Mutex; -use std::collections::HashMap; - -use rand::prelude::*; -use rand::rngs::OsRng; - -use x25519_dalek::PublicKey; -use x25519_dalek::StaticSecret; - - -use crate::noise; -use crate::types::*; - -struct Device { - sk : StaticSecret, // static secret key - pk : PublicKey, // static public key - peers : Vec, // peer index -> state - pkmap : HashMap<[u8; 32], usize>, // public key -> peer index - ids : Mutex> // receive ids -> peer index -} - -/* A mutable reference to the state machine needs to be held, - * during configuration. - */ -impl Device { - /// Initialize a new handshake state machine - /// - /// # Arguments - /// - /// * `sk` - x25519 scalar representing the local private key - pub fn new(sk : StaticSecret) -> Device { - Device { - pk : PublicKey::from(&sk), - sk : sk, - peers : vec![], - pkmap : HashMap::new(), - ids : Mutex::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 - /// - /// # Returns - /// - /// The call might fail if the public key corresponds to the secret key of the machine - pub fn add(&mut self, pk : PublicKey) -> Result<(), ()> { - // check that the pk is not added twice - - if let Some(_) = self.pkmap.get(pk.as_bytes()) { - return Err(()); - }; - - // check that the pk is not that of the device - - if *self.pk.as_bytes() == *pk.as_bytes() { - return Err(()); - } - - // map : pk -> new index - - self.pkmap.insert(*pk.as_bytes(), self.peers.len()); - - // map : new index -> peer - - self.peers.push(Peer { - m : Mutex::new(State::Reset), - pk : pk, - ss : self.sk.diffie_hellman(&pk), - psk : [0u8; 32] - }); - - 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 psk(&mut self, pk : PublicKey, psk : Option<[u8; 32]>) -> Result<(), ()> { - match self.pkmap.get(pk.as_bytes()) { - Some(&idx) => { - let peer = &mut self.peers[idx]; - peer.psk = match psk { - Some(v) => v, - None => [0u8; 32], - }; - Ok(()) - }, - _ => Err(()) - } - } - - /// Release an id back to the pool - /// - /// # Arguments - /// - /// * `id` - The (sender) id to release - pub fn release(&self, id : u32) { - self.ids.lock().unwrap().remove(&id); - } - - /// Begin a new handshake - /// - /// # Arguments - /// - /// * `pk` - Public key of peer to initiate handshake for - pub fn begin(&self, pk : PublicKey) -> Result, ()> { - match self.pkmap.get(pk.as_bytes()) { - None => Err(()), - Some(&idx) => { - let peer = &self.peers[idx]; - let id = self.allocate(idx); - noise::create_initiation(peer, id) - } - } - } - - /// Process a handshake message. - /// - /// # Arguments - /// - /// * `msg` - Byte slice containing the message (untrusted input) - pub fn process(&self, msg : &[u8]) -> Result { - // inspect type field - match msg.get(0) { - _ => Err(()) - } - } -} - -impl Device { - // allocate a new index (id), for peer with idx - fn allocate(&self, idx : usize) -> u32 { - let mut rng = OsRng::new().unwrap(); - let mut table = self.ids.lock().unwrap(); - loop { - let id = rng.gen(); - if !table.contains_key(&id) { - table.insert(id, idx); - return id; - } - } - } -} diff --git a/src/messages.rs b/src/messages.rs index 805de8d..340f5e0 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -5,21 +5,22 @@ 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; +pub const TYPE_INITIATION : u32 = 1; +pub const TYPE_RESPONSE : u32 = 2; -/* Wireguard handshake initiation message +/* Wireguard handshake (noise) initiation message * initator -> responder */ #[repr(C)] #[derive(Copy, Clone)] pub struct Initiation { - pub f_type : u8, - f_reserved : [u8; 3], - pub f_sender : u32, - pub f_ephemeral : [u8; SIZE_X25519_POINT], - pub f_static : [u8; SIZE_X25519_POINT + SIZE_TAG], - pub f_timestamp : [u8; SIZE_TIMESTAMP + SIZE_TAG], + f_type : u32, + pub f_sender : u32, + 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 From<&[u8]> for Initiation { @@ -70,12 +71,13 @@ impl fmt::Debug for Initiation { impl Default for Initiation { fn default() -> Self { Self { - f_type : TYPE_INITIATION, - f_reserved : [0u8; 3], - f_sender : 0, - f_ephemeral : [0u8; SIZE_X25519_POINT], - f_static : [0u8; SIZE_X25519_POINT + SIZE_TAG], - f_timestamp : [0u8; SIZE_TIMESTAMP + SIZE_TAG], + f_type : TYPE_INITIATION, + f_sender : 0, + 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] } } } @@ -84,11 +86,12 @@ impl Default for Initiation { impl PartialEq for Initiation { fn eq(&self, other: &Self) -> bool { self.f_type == other.f_type && - self.f_reserved == other.f_reserved && self.f_sender == other.f_sender && self.f_ephemeral[..] == other.f_ephemeral[..] && self.f_static[..] == other.f_static[..] && - self.f_timestamp[..] == other.f_timestamp + self.f_static_tag[..] == other.f_static_tag[..] && + self.f_timestamp[..] == other.f_timestamp && + self.f_timestamp_tag[..] == other.f_timestamp_tag } } @@ -96,18 +99,17 @@ impl PartialEq for Initiation { impl Eq for Initiation {} -/* Wireguard handshake responder message +/* Wireguard handshake (noise) responder message * responder -> initator */ #[repr(C)] #[derive(Copy, Clone)] pub struct Response { - f_type : u8, - f_reserved : [u8; 3], - f_sender : u32, - f_receiver : u32, - f_ephemeral : [u8; SIZE_X25519_POINT], - f_empty : [u8; SIZE_TAG], + f_type : u32, + pub f_sender : u32, + pub f_receiver : u32, + pub f_ephemeral : [u8; SIZE_X25519_POINT], + pub f_empty_tag : [u8; SIZE_TAG], } impl From<&[u8]> for Response { @@ -161,11 +163,10 @@ impl fmt::Debug for Response { impl PartialEq for Response { fn eq(&self, other: &Self) -> bool { self.f_type == other.f_type && - self.f_reserved == other.f_reserved && self.f_sender == other.f_sender && self.f_receiver == other.f_receiver && self.f_ephemeral == other.f_ephemeral && - self.f_empty == other.f_empty + self.f_empty_tag == other.f_empty_tag } } @@ -176,10 +177,9 @@ mod tests { #[test] fn message_response_identity() { let msg = Response { - f_type : TYPE_RESPONSE, - f_reserved : [0u8; 3], - f_sender : 146252, - f_receiver : 554442, + f_type : TYPE_RESPONSE, + f_sender : 146252, + f_receiver : 554442, f_ephemeral : [ // ephemeral public key 0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, @@ -187,7 +187,7 @@ mod tests { 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44, 0x13, 0x56, 0x52, 0x1f ], - f_empty : [ + f_empty_tag : [ // tag 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde @@ -201,9 +201,8 @@ mod tests { #[test] fn message_initiate_identity() { let msg = Initiation { - f_type : TYPE_RESPONSE, - f_reserved : [0u8; 3], - f_sender : 575757, + f_type : TYPE_RESPONSE, + f_sender : 575757, f_ephemeral : [ // ephemeral public key 0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, @@ -211,13 +210,14 @@ mod tests { 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44, 0x13, 0x56, 0x52, 0x1f ], - f_static : [ + f_static : [ // encrypted static public key 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, - + 0xeb, 0x6a, 0xec, 0xc3, 0x3c, 0xda, 0x47, 0xe1 + ], + f_static_tag : [ // tag 0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83, 0x6b, 0x42 @@ -225,8 +225,9 @@ mod tests { f_timestamp : [ // timestamp 0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, - 0x78, 0x28, 0x57, 0x42, - + 0x78, 0x28, 0x57, 0x42 + ], + f_timestamp_tag : [ // tag 0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde diff --git a/src/noise.rs b/src/noise.rs index 4d741c0..73819bf 100644 --- a/src/noise.rs +++ b/src/noise.rs @@ -1,16 +1,21 @@ +// DH +use x25519_dalek::PublicKey; +use x25519_dalek::StaticSecret; + +// HASH & MAC use hmac::{Mac, Hmac}; use blake2::{Blake2s, Digest}; -use x25519_dalek::PublicKey; -use x25519_dalek::StaticSecret; -use x25519_dalek::SharedSecret; +// AEAD +use crypto::chacha20poly1305::ChaCha20Poly1305; +use crypto::aead::{AeadEncryptor,AeadDecryptor}; use rand::rngs::OsRng; -use generic_array::*; - use crate::types::*; +use crate::device::Device; use crate::messages; +use crate::timestamp; type HMACBlake2s = Hmac; @@ -18,6 +23,7 @@ type HMACBlake2s = Hmac; const SIZE_CK : usize = 32; const SIZE_HS : usize = 32; +const SIZE_NONCE : usize = 8; // C := Hash(Construction) const INITIAL_CK : [u8; SIZE_CK] = [ @@ -35,6 +41,10 @@ const INITIAL_HS : [u8; SIZE_HS] = [ 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 { ($input1:expr) => { { @@ -52,19 +62,35 @@ macro_rules! HASH { Digest::result(hsh) } }; + + ($input1:expr, $input2:expr, $input3:expr) => { + { + let mut hsh = ::new(); + Digest::input(&mut hsh, $input1); + Digest::input(&mut hsh, $input2); + Digest::input(&mut hsh, $input3); + Digest::result(hsh) + } + }; } macro_rules! HMAC { - ($key:expr, $input:expr) => { + ($key:expr, $input1:expr) => { { - let mut mac = HMACBlake2s::new($key); - mac.hash($input); - mac.result() + // let mut mac = HMACBlake2s::new($key); + let mut mac = HMACBlake2s::new_varkey($key).unwrap(); + mac.input($input1); + mac.result().code() } }; ($key:expr, $input1:expr, $input2:expr) => { - HMACBlake2s::new($key).hash($input2).hash($input2).result() + { + let mut mac = HMACBlake2s::new_varkey($key).unwrap(); + mac.input($input1); + mac.input($input2); + mac.result().code() + } }; } @@ -72,20 +98,33 @@ macro_rules! KDF1 { ($ck:expr, $input:expr) => { { let t0 = HMAC!($ck, $input); - t0 + 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! KDF2 { - ($ck:expr, $input:expr) => { - +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 + ); + } } } @@ -110,7 +149,11 @@ mod tests { } } -pub fn create_initiation(peer : &Peer, id : u32) -> Result, ()> { +pub fn create_initiation( + device : &Device, + peer : &Peer, + id : u32 +) -> Result, HandshakeError> { let mut rng = OsRng::new().unwrap(); let mut msg : messages::Initiation = Default::default(); @@ -122,25 +165,68 @@ pub fn create_initiation(peer : &Peer, id : u32) -> Result, ()> { msg.f_sender = id; - // token : e - + // (E_priv, E_pub) := DH-Generate() let sk = StaticSecret::new(&mut rng); let pk = PublicKey::from(&sk); - msg.f_ephemeral = *pk.as_bytes(); - - // let ck = KDF1!(&ck, pk.as_bytes()); + // C := Kdf(C, E_pub) + let ck = KDF1!(&ck, pk.as_bytes()); - // token : es + // msg.ephemeral := E_pub + msg.f_ephemeral = *pk.as_bytes(); - // token : s + // 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, + 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, + device.sk.diffie_hellman(&peer.pk).as_bytes() + ); + + // msg.timestamp := Aead(k, 0, Timestamp(), H) + SEAL!( + &key, + &hs, // ad + ×tamp::new(), // 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); + + // mutate state of peer + + let mut st = peer.state.lock().unwrap(); + *st = State::InitiationSent{ + hs : hs, + ck : ck + }; - // token : ss + // return message as vector - Ok(vec![]) + Ok(messages::Initiation::into(msg)) } pub fn process_initiation(peer : &Peer) -> Result { Err(()) } - diff --git a/src/timestamp.rs b/src/timestamp.rs new file mode 100644 index 0000000..ba5c2f1 --- /dev/null +++ b/src/timestamp.rs @@ -0,0 +1,5 @@ +pub type TAI64N = [u8; 12]; + +pub fn new() -> TAI64N { + [0u8; 12] // TODO +} diff --git a/src/types.rs b/src/types.rs index 391b029..54f4801 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,8 +1,71 @@ +use std::fmt; use std::sync::Mutex; +use std::error::Error; use x25519_dalek::PublicKey; use x25519_dalek::SharedSecret; +use generic_array::typenum::U32; +use generic_array::GenericArray; + +use crate::timestamp; + +// 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 struct HandshakeError {} + +impl HandshakeError { + pub fn new() -> Self { + HandshakeError{} + } +} + +impl fmt::Display for HandshakeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "HandshakeError") + } +} + +impl Error for HandshakeError { + fn description(&self) -> &str { + "Generic Handshake Error" + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + +// types for resulting key-material + struct Key { key : [u8; 32], id : u32 @@ -19,17 +82,26 @@ pub struct Output ( Option> // message to send ); +// per-peer state machine + +pub type Psk = [u8; 32]; + pub struct Peer { // mutable state - pub m : Mutex, + pub state : Mutex, // constant state - pub pk : PublicKey, // public key of peer - pub ss : SharedSecret, // precomputed DH(static, static) - pub psk : [u8; 32] // psk of peer + pub pk : PublicKey, // public key of peer + pub ss : SharedSecret, // precomputed DH(static, static) + pub psk : Psk // psk of peer } pub enum State { - Reset, - InitiationSent, + Reset{ + ts : Option + }, + InitiationSent{ + hs : GenericArray, + ck : GenericArray + }, } -- cgit v1.2.3-59-g8ed1b