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::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<T> {
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<T>>, // public key -> peer state
id_map: RwLock<HashMap<u32, [u8; 32]>>, // receiver ids -> public key
}
@@ -34,9 +36,11 @@ where
///
/// * `sk` - x25519 scalar representing the local private key
pub fn new(sk: StaticSecret) -> Device<T> {
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)
}

View File

@@ -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<Instant>, // when was the cookie set?
last_mac1: Option<[u8; 16]>,
cookie: Option<Cookie>,
}
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<HandshakeError> {
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)
}
pub fn set_cookie(&mut self, cookie: &[u8; SIZE_COOKIE]) {
self.cookie_birth = Some(Instant::now());
self.cookie_value = *cookie;
}
pub fn generate(&self, inner: &[u8], macs : &mut MacsFooter) {
/// 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 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>>,
// state related to DoS mitigation fields
pub(crate) macs: macs::Generator,
pub(crate) macs: Mutex<macs::Generator>,
// 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),

View File

@@ -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"),
}
}
}