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