Successfully validate mac1 field
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
/// 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)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user