Successfully validate mac1 field

This commit is contained in:
Mathias Hall-Andersen
2019-08-01 13:25:50 +02:00
parent ab98d9dced
commit 0f92468f69
4 changed files with 99 additions and 29 deletions

View File

@@ -8,6 +8,7 @@ use rand::rngs::OsRng;
use x25519_dalek::PublicKey; use x25519_dalek::PublicKey;
use x25519_dalek::StaticSecret; use x25519_dalek::StaticSecret;
use super::macs;
use super::messages::{CookieReply, Initiation, Response}; use super::messages::{CookieReply, Initiation, Response};
use super::messages::{TYPE_COOKIEREPLY, TYPE_INITIATION, TYPE_RESPONSE}; use super::messages::{TYPE_COOKIEREPLY, TYPE_INITIATION, TYPE_RESPONSE};
use super::noise; use super::noise;
@@ -17,6 +18,7 @@ use super::types::*;
pub struct Device<T> { pub struct Device<T> {
pub sk: StaticSecret, // static secret key pub sk: StaticSecret, // static secret key
pub pk: PublicKey, // static public key pub pk: PublicKey, // static public key
macs: macs::Validator, // validator for the mac fields
pk_map: HashMap<[u8; 32], Peer<T>>, // public key -> peer state pk_map: HashMap<[u8; 32], Peer<T>>, // public key -> peer state
id_map: RwLock<HashMap<u32, [u8; 32]>>, // receiver ids -> public key id_map: RwLock<HashMap<u32, [u8; 32]>>, // receiver ids -> public key
} }
@@ -34,9 +36,11 @@ where
/// ///
/// * `sk` - x25519 scalar representing the local private key /// * `sk` - x25519 scalar representing the local private key
pub fn new(sk: StaticSecret) -> Device<T> { pub fn new(sk: StaticSecret) -> Device<T> {
let pk = PublicKey::from(&sk);
Device { Device {
pk: PublicKey::from(&sk), pk,
sk: sk, sk,
macs: macs::Validator::new(pk),
pk_map: HashMap::new(), pk_map: HashMap::new(),
id_map: RwLock::new(HashMap::new()), id_map: RwLock::new(HashMap::new()),
} }
@@ -167,7 +171,9 @@ where
// add macs to initation // 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()) Ok(msg.as_bytes().to_owned())
} }
@@ -184,8 +190,10 @@ where
Some(&TYPE_INITIATION) => { Some(&TYPE_INITIATION) => {
let msg = Initiation::parse(msg)?; 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 // consume the initiation
let (peer, st) = noise::consume_initiation(self, &msg.noise)?; let (peer, st) = noise::consume_initiation(self, &msg.noise)?;
@@ -203,9 +211,11 @@ where
})?; })?;
// add macs to response // 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(( Ok((
peer.identifier, peer.identifier,
Some(resp.as_bytes().to_owned()), Some(resp.as_bytes().to_owned()),
@@ -215,7 +225,8 @@ where
Some(&TYPE_RESPONSE) => { Some(&TYPE_RESPONSE) => {
let msg = Response::parse(msg)?; 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) noise::consume_response(self, &msg.noise)
} }

View File

@@ -1,10 +1,11 @@
use std::time::Instant; use std::time::{Duration, Instant};
use blake2::Blake2s; use blake2::Blake2s;
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
use x25519_dalek::PublicKey; use x25519_dalek::PublicKey;
use super::messages::{CookieReply, MacsFooter}; use super::messages::{CookieReply, MacsFooter};
use super::types::HandshakeError;
const LABEL_MAC1: &[u8] = b"mac1----"; const LABEL_MAC1: &[u8] = b"mac1----";
const LABEL_COOKIE: &[u8] = b"cookie--"; const LABEL_COOKIE: &[u8] = b"cookie--";
@@ -12,6 +13,8 @@ const LABEL_COOKIE: &[u8] = b"cookie--";
const SIZE_COOKIE: usize = 16; const SIZE_COOKIE: usize = 16;
const SIZE_MAC: usize = 16; // blake2s-mac128 const SIZE_MAC: usize = 16; // blake2s-mac128
const SECS_COOKIE_UPDATE: u64 = 120;
macro_rules! HASH { macro_rules! HASH {
( $($input:expr),* ) => {{ ( $($input:expr),* ) => {{
use blake2::Digest; use blake2::Digest;
@@ -38,39 +41,93 @@ macro_rules! MAC {
}}; }};
} }
struct Cookie {
value: [u8; 16],
birth: Instant,
}
pub struct Generator { pub struct Generator {
mac1_key: [u8; 32], mac1_key: [u8; 32],
cookie_value: [u8; 16], last_mac1: Option<[u8; 16]>,
cookie_birth: Option<Instant>, // when was the cookie set? cookie: Option<Cookie>,
} }
impl Generator { 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 { pub fn new(pk: PublicKey) -> Generator {
Generator { Generator {
mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(),
cookie_value: [0u8; SIZE_COOKIE], last_mac1: None,
cookie_birth: None, cookie: None,
} }
} }
fn mac1(&self, msg: &[u8]) -> [u8; SIZE_MAC] { /// Process a CookieReply message
MAC!(&self.mac1_key, msg) ///
/// # 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<HandshakeError> {
self.cookie = Some(Cookie {
birth: Instant::now(),
value: reply.f_cookie,
});
None
} }
fn mac2(&self, msg: &[u8]) -> [u8; SIZE_MAC] { /// Generate both mac fields for an inner message
MAC!(&self.cookie_value, msg) ///
} /// # Arguments
///
pub fn set_cookie(&mut self, cookie: &[u8; SIZE_COOKIE]) { /// - inner: A byteslice representing the inner message to be covered
self.cookie_birth = Some(Instant::now()); /// - macs: The destination mac footer for the resulting macs
self.cookie_value = *cookie; pub fn generate(&mut self, inner: &[u8], macs: &mut MacsFooter) {
} macs.f_mac1 = MAC!(&self.mac1_key, inner);
macs.f_mac2 = match &self.cookie {
pub fn generate(&self, inner: &[u8], macs : &mut MacsFooter) { 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 struct Validator {} pub struct Validator {
mac1_key: [u8; 32],
}
impl Validator {} 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(())
}
}
}

View File

@@ -26,7 +26,7 @@ pub struct Peer<T> {
timestamp: Mutex<Option<timestamp::TAI64N>>, timestamp: Mutex<Option<timestamp::TAI64N>>,
// state related to DoS mitigation fields // state related to DoS mitigation fields
pub(crate) macs: macs::Generator, pub(crate) macs: Mutex<macs::Generator>,
// constant state // constant state
pub(crate) pk: PublicKey, // public key of peer pub(crate) pk: PublicKey, // public key of peer
@@ -73,7 +73,7 @@ where
ss: SharedSecret, // precomputed DH(static, static) ss: SharedSecret, // precomputed DH(static, static)
) -> Self { ) -> Self {
Self { Self {
macs: macs::Generator::new(pk), macs: Mutex::new(macs::Generator::new(pk)),
identifier: identifier, identifier: identifier,
state: Mutex::new(State::Reset), state: Mutex::new(State::Reset),
timestamp: Mutex::new(None), timestamp: Mutex::new(None),

View File

@@ -42,6 +42,7 @@ pub enum HandshakeError {
InvalidMessageFormat, InvalidMessageFormat,
OldTimestamp, OldTimestamp,
InvalidState, InvalidState,
InvalidMac1,
} }
impl fmt::Display for HandshakeError { impl fmt::Display for HandshakeError {
@@ -55,6 +56,7 @@ impl fmt::Display for HandshakeError {
HandshakeError::InvalidMessageFormat => write!(f, "Invalid handshake message format"), HandshakeError::InvalidMessageFormat => write!(f, "Invalid handshake message format"),
HandshakeError::OldTimestamp => write!(f, "Timestamp is less/equal to the newest"), HandshakeError::OldTimestamp => write!(f, "Timestamp is less/equal to the newest"),
HandshakeError::InvalidState => write!(f, "Message does not apply to handshake state"), HandshakeError::InvalidState => write!(f, "Message does not apply to handshake state"),
HandshakeError::InvalidMac1 => write!(f, "Message has invalid mac1 field"),
} }
} }
} }