From 0f92468f6978c7ce13b33558e5bc36f94a2b7e4e Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Thu, 1 Aug 2019 13:25:50 +0200 Subject: Successfully validate mac1 field --- src/handshake/device.rs | 25 +++++++++---- src/handshake/macs.rs | 93 +++++++++++++++++++++++++++++++++++++++---------- src/handshake/peer.rs | 4 +-- src/handshake/types.rs | 2 ++ 4 files changed, 97 insertions(+), 27 deletions(-) diff --git a/src/handshake/device.rs b/src/handshake/device.rs index f3458ae..2a676a8 100644 --- a/src/handshake/device.rs +++ b/src/handshake/device.rs @@ -8,6 +8,7 @@ use rand::rngs::OsRng; use x25519_dalek::PublicKey; use x25519_dalek::StaticSecret; +use super::macs; use super::messages::{CookieReply, Initiation, Response}; use super::messages::{TYPE_COOKIEREPLY, TYPE_INITIATION, TYPE_RESPONSE}; use super::noise; @@ -17,6 +18,7 @@ use super::types::*; pub struct Device { pub sk: StaticSecret, // static secret key pub pk: PublicKey, // static public key + macs: macs::Validator, // validator for the mac fields pk_map: HashMap<[u8; 32], Peer>, // public key -> peer state id_map: RwLock>, // receiver ids -> public key } @@ -34,9 +36,11 @@ where /// /// * `sk` - x25519 scalar representing the local private key pub fn new(sk: StaticSecret) -> Device { + let pk = PublicKey::from(&sk); Device { - pk: PublicKey::from(&sk), - sk: sk, + pk, + sk, + macs: macs::Validator::new(pk), pk_map: HashMap::new(), id_map: RwLock::new(HashMap::new()), } @@ -167,7 +171,9 @@ where // add macs to initation - peer.macs.generate(msg.noise.as_bytes(), &mut msg.macs); + peer.macs + .lock() + .generate(msg.noise.as_bytes(), &mut msg.macs); Ok(msg.as_bytes().to_owned()) } @@ -184,8 +190,10 @@ where Some(&TYPE_INITIATION) => { let msg = Initiation::parse(msg)?; - // check mac footer and ratelimiter for initiation + // check mac1 field + self.macs.check_mac1(msg.noise.as_bytes(), &msg.macs)?; + // check ratelimiter // consume the initiation let (peer, st) = noise::consume_initiation(self, &msg.noise)?; @@ -203,9 +211,11 @@ where })?; // add macs to response + peer.macs + .lock() + .generate(resp.noise.as_bytes(), &mut resp.macs); - resp.macs.f_mac1 = [8u8; 16]; - + // return unconfirmed keypair and the response as vector Ok(( peer.identifier, Some(resp.as_bytes().to_owned()), @@ -215,7 +225,8 @@ where Some(&TYPE_RESPONSE) => { let msg = Response::parse(msg)?; - // check mac footer and ratelimiter + // check mac1 field + self.macs.check_mac1(msg.noise.as_bytes(), &msg.macs)?; noise::consume_response(self, &msg.noise) } diff --git a/src/handshake/macs.rs b/src/handshake/macs.rs index 9092daa..f558362 100644 --- a/src/handshake/macs.rs +++ b/src/handshake/macs.rs @@ -1,10 +1,11 @@ -use std::time::Instant; +use std::time::{Duration, Instant}; use blake2::Blake2s; use subtle::ConstantTimeEq; use x25519_dalek::PublicKey; use super::messages::{CookieReply, MacsFooter}; +use super::types::HandshakeError; const LABEL_MAC1: &[u8] = b"mac1----"; const LABEL_COOKIE: &[u8] = b"cookie--"; @@ -12,6 +13,8 @@ const LABEL_COOKIE: &[u8] = b"cookie--"; const SIZE_COOKIE: usize = 16; const SIZE_MAC: usize = 16; // blake2s-mac128 +const SECS_COOKIE_UPDATE: u64 = 120; + macro_rules! HASH { ( $($input:expr),* ) => {{ use blake2::Digest; @@ -38,39 +41,93 @@ macro_rules! MAC { }}; } +struct Cookie { + value: [u8; 16], + birth: Instant, +} + pub struct Generator { mac1_key: [u8; 32], - cookie_value: [u8; 16], - cookie_birth: Option, // when was the cookie set? + last_mac1: Option<[u8; 16]>, + cookie: Option, } impl Generator { + /// Initalize a new mac field generator + /// + /// # Arguments + /// + /// - pk: The public key of the peer to which the generator is associated + /// + /// # Returns + /// + /// A freshly initated generator pub fn new(pk: PublicKey) -> Generator { Generator { mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), - cookie_value: [0u8; SIZE_COOKIE], - cookie_birth: None, + last_mac1: None, + cookie: None, } } - fn mac1(&self, msg: &[u8]) -> [u8; SIZE_MAC] { - MAC!(&self.mac1_key, msg) + /// Process a CookieReply message + /// + /// # Arguments + /// + /// - reply: CookieReply to process + /// + /// # Returns + /// + /// Can fail if the cookie reply fails to validate + /// (either indicating that it is outdated or malformed) + pub fn process(&mut self, reply: &CookieReply) -> Option { + self.cookie = Some(Cookie { + birth: Instant::now(), + value: reply.f_cookie, + }); + None } - fn mac2(&self, msg: &[u8]) -> [u8; SIZE_MAC] { - MAC!(&self.cookie_value, msg) + /// Generate both mac fields for an inner message + /// + /// # Arguments + /// + /// - inner: A byteslice representing the inner message to be covered + /// - macs: The destination mac footer for the resulting macs + pub fn generate(&mut self, inner: &[u8], macs: &mut MacsFooter) { + macs.f_mac1 = MAC!(&self.mac1_key, inner); + macs.f_mac2 = match &self.cookie { + Some(cookie) => { + if cookie.birth.elapsed() > Duration::from_secs(SECS_COOKIE_UPDATE) { + self.cookie = None; + [0u8; SIZE_MAC] + } else { + MAC!(&cookie.value, inner, macs.f_mac1) + } + } + None => [0u8; SIZE_MAC], + }; + self.last_mac1 = Some(macs.f_mac1); } +} - pub fn set_cookie(&mut self, cookie: &[u8; SIZE_COOKIE]) { - self.cookie_birth = Some(Instant::now()); - self.cookie_value = *cookie; - } +pub struct Validator { + mac1_key: [u8; 32], +} - pub fn generate(&self, inner: &[u8], macs : &mut MacsFooter) { +impl Validator { + pub fn new(pk: PublicKey) -> Validator { + Validator { + mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), + } + } + pub fn check_mac1(&self, inner: &[u8], macs: &MacsFooter) -> Result<(), HandshakeError> { + let valid_mac1: bool = MAC!(&self.mac1_key, inner).ct_eq(&macs.f_mac1).into(); + if !valid_mac1 { + Err(HandshakeError::InvalidMac1) + } else { + Ok(()) + } } } - -pub struct Validator {} - -impl Validator {} diff --git a/src/handshake/peer.rs b/src/handshake/peer.rs index 309a9cf..9645799 100644 --- a/src/handshake/peer.rs +++ b/src/handshake/peer.rs @@ -26,7 +26,7 @@ pub struct Peer { timestamp: Mutex>, // state related to DoS mitigation fields - pub(crate) macs: macs::Generator, + pub(crate) macs: Mutex, // constant state pub(crate) pk: PublicKey, // public key of peer @@ -73,7 +73,7 @@ where ss: SharedSecret, // precomputed DH(static, static) ) -> Self { Self { - macs: macs::Generator::new(pk), + macs: Mutex::new(macs::Generator::new(pk)), identifier: identifier, state: Mutex::new(State::Reset), timestamp: Mutex::new(None), diff --git a/src/handshake/types.rs b/src/handshake/types.rs index 0d9a5d3..6a35e1c 100644 --- a/src/handshake/types.rs +++ b/src/handshake/types.rs @@ -42,6 +42,7 @@ pub enum HandshakeError { InvalidMessageFormat, OldTimestamp, InvalidState, + InvalidMac1, } impl fmt::Display for HandshakeError { @@ -55,6 +56,7 @@ impl fmt::Display for HandshakeError { 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"), + HandshakeError::InvalidMac1 => write!(f, "Message has invalid mac1 field"), } } } -- cgit v1.2.3-59-g8ed1b