From 2bdcda067cdea198cda90a5d5b8aef2b8b7ba4ce Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Fri, 2 Aug 2019 17:18:37 +0200 Subject: Remove rust-crypto, move to libsodium bindings --- src/handshake/macs.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++++++- src/handshake/noise.rs | 59 ++++++++++++++++----- 2 files changed, 178 insertions(+), 16 deletions(-) (limited to 'src/handshake') diff --git a/src/handshake/macs.rs b/src/handshake/macs.rs index 99c83ac..36ae1b8 100644 --- a/src/handshake/macs.rs +++ b/src/handshake/macs.rs @@ -1,9 +1,17 @@ use std::time::{Duration, Instant}; +use rand::rngs::OsRng; +use rand::CryptoRng; +use rand::RngCore; + +use spin::Mutex; + use blake2::Blake2s; use subtle::ConstantTimeEq; use x25519_dalek::PublicKey; +use sodiumoxide::crypto::aead::xchacha20poly1305_ietf; + use super::messages::{CookieReply, MacsFooter}; use super::types::HandshakeError; @@ -11,6 +19,7 @@ const LABEL_MAC1: &[u8] = b"mac1----"; const LABEL_COOKIE: &[u8] = b"cookie--"; const SIZE_COOKIE: usize = 16; +const SIZE_SECRET: usize = 32; const SIZE_MAC: usize = 16; // blake2s-mac128 const SECS_COOKIE_UPDATE: u64 = 120; @@ -41,6 +50,45 @@ macro_rules! MAC { }}; } +macro_rules! XSEAL { + ($key:expr, $nonce:expr, $ad:expr, $pt:expr, $ct:expr, $tag:expr) => {{ + let s_key = xchacha20poly1305_ietf::Key::from_slice($key).unwrap(); + let s_nonce = xchacha20poly1305_ietf::Nonce::from_slice($nonce).unwrap(); + + debug_assert_eq!($tag.len(), 16); + debug_assert_eq!($pt.len(), $ct.len()); + + $ct.copy_from_slice($pt); + let tag = xchacha20poly1305_ietf::seal_detached( + $ct, + if $ad.len() == 0 { None } else { Some($ad) }, + &s_nonce, + &s_key, + ); + $tag.copy_from_slice(tag.as_ref()); + }}; +} + +macro_rules! XOPEN { + ($key:expr, $nonce:expr, $ad:expr, $pt:expr, $ct:expr, $tag:expr) => {{ + let s_key = xchacha20poly1305_ietf::Key::from_slice($key).unwrap(); + let s_nonce = xchacha20poly1305_ietf::Nonce::from_slice($nonce).unwrap(); + let s_tag = xchacha20poly1305_ietf::Tag::from_slice($tag).unwrap(); + + debug_assert_eq!($pt.len(), $ct.len()); + + $pt.copy_from_slice($ct); + xchacha20poly1305_ietf::open_detached( + $pt, + if $ad.len() == 0 { None } else { Some($ad) }, + &s_tag, + &s_nonce, + &s_key, + ) + .map_err(|_| HandshakeError::DecryptionFailure) + }}; +} + struct Cookie { value: [u8; 16], birth: Instant, @@ -48,6 +96,7 @@ struct Cookie { pub struct Generator { mac1_key: [u8; 32], + cookie_key: [u8; 32], // xchacha20poly key for opening cookie response last_mac1: Option<[u8; 16]>, cookie: Option, } @@ -65,6 +114,7 @@ impl Generator { pub fn new(pk: PublicKey) -> Generator { Generator { mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), + cookie_key: HASH!(LABEL_COOKIE, pk.as_bytes()).into(), last_mac1: None, cookie: None, } @@ -81,10 +131,19 @@ impl Generator { /// Can fail if the cookie reply fails to validate /// (either indicating that it is outdated or malformed) pub fn process(&mut self, reply: &CookieReply) -> Result<(), HandshakeError> { - unimplemented!("do the checks and decryption"); + let mac1 = self.last_mac1.ok_or(HandshakeError::InvalidState)?; + let mut tau = [0u8; SIZE_COOKIE]; + XOPEN!( + &self.cookie_key, // key + &reply.f_nonce, // nonce + &mac1, // ad + &mut tau, // pt + &reply.f_cookie, // ct + &reply.f_cookie_tag // tag + )?; self.cookie = Some(Cookie { birth: Instant::now(), - value: reply.f_cookie, + value: tau, }); Ok(()) } @@ -112,17 +171,68 @@ impl Generator { } } +struct Secret { + value: [u8; 32], + birth: Instant, +} + pub struct Validator { mac1_key: [u8; 32], + cookie_key: [u8; 32], // xchacha20poly key for sealing cookie response + secret: Mutex, } impl Validator { pub fn new(pk: PublicKey) -> Validator { Validator { mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), + cookie_key: HASH!(LABEL_COOKIE, pk.as_bytes()).into(), + secret: Mutex::new(Secret { + value: [0u8; SIZE_SECRET], + birth: Instant::now() - Duration::from_secs(2 * SECS_COOKIE_UPDATE), + }), } } + fn get_tau(&self, rng: &mut T, addr: &[u8]) -> [u8; SIZE_COOKIE] + where + T: RngCore + CryptoRng, + { + let mut secret = self.secret.lock(); + + // check if current value is still valid + if secret.birth.elapsed() < Duration::from_secs(SECS_COOKIE_UPDATE) { + return MAC!(&secret.value, addr); + }; + + // generate new value + rng.fill_bytes(&mut secret.value); + secret.birth = Instant::now(); + MAC!(&secret.value, addr) + } + + fn create_cookie_reply( + &mut self, + rng: &mut T, + receiver: u32, // receiver id of incoming message + src: &[u8], // source address of incoming message + macs: &MacsFooter, // footer of incoming message + msg: &mut CookieReply, // resulting cookie reply + ) where + T: RngCore + CryptoRng, + { + msg.f_receiver.set(receiver); + rng.fill_bytes(&mut msg.f_nonce); + XSEAL!( + &self.cookie_key, // key + &msg.f_nonce, // nonce + &macs.f_mac1, // ad + &self.get_tau(rng, src), // pt + &mut msg.f_cookie, // ct + &mut msg.f_cookie_tag // tag + ); + } + /// Check the mac1 field against the inner message /// /// # Arguments @@ -137,6 +247,27 @@ impl Validator { Ok(()) } } + + /// Check the mac2 field against the inner message + /// + /// # Arguments + /// + /// - inner: The inner message covered by the mac1 field + /// - src: Source address + /// - macs: The mac footer + pub fn check_mac2( + &self, + inner: &[u8], + src: &[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(()) + } + } } #[cfg(test)] diff --git a/src/handshake/noise.rs b/src/handshake/noise.rs index b0d492c..cf5238c 100644 --- a/src/handshake/noise.rs +++ b/src/handshake/noise.rs @@ -6,9 +6,8 @@ use x25519_dalek::StaticSecret; use blake2::Blake2s; use hmac::Hmac; -// AEAD -use crypto::aead::{AeadDecryptor, AeadEncryptor}; -use crypto::chacha20poly1305::ChaCha20Poly1305; +// AEAD (from libsodium) +use sodiumoxide::crypto::aead::chacha20poly1305; use rand::rngs::OsRng; @@ -99,20 +98,52 @@ macro_rules! KDF3 { } macro_rules! SEAL { - ($key:expr, $aead:expr, $pt:expr, $ct:expr, $tag:expr) => {{ - let mut aead = ChaCha20Poly1305::new($key, &ZERO_NONCE, $aead); - aead.encrypt($pt, $ct, $tag); + ($key:expr, $ad:expr, $pt:expr, $ct:expr, $tag:expr) => {{ + // create annoying nonce and key objects + let s_nonce = chacha20poly1305::Nonce::from_slice(&ZERO_NONCE).unwrap(); + let s_key = chacha20poly1305::Key::from_slice($key).unwrap(); + + // type annontate the ct and pt arguments + let pt: &[u8] = $pt; + let ct: &mut [u8] = $ct; + + // basic sanity checks + debug_assert_eq!(pt.len(), ct.len()); + debug_assert_eq!($tag.len(), chacha20poly1305::TAGBYTES); + + // encrypt + ct.copy_from_slice(pt); + let tag = chacha20poly1305::seal_detached( + ct, + if $ad.len() == 0 { None } else { Some($ad) }, + &s_nonce, + &s_key, + ); + $tag.copy_from_slice(tag.as_ref()); }}; } macro_rules! OPEN { - ($key:expr, $aead:expr, $pt:expr, $ct:expr, $tag:expr) => {{ - let mut aead = ChaCha20Poly1305::new($key, &ZERO_NONCE, $aead); - if !aead.decrypt($ct, $pt, $tag) { - Err(HandshakeError::DecryptionFailure) - } else { - Ok(()) - } + ($key:expr, $ad:expr, $pt:expr, $ct:expr, $tag:expr) => {{ + // create annoying nonce and key objects + let s_nonce = chacha20poly1305::Nonce::from_slice(&ZERO_NONCE).unwrap(); + let s_key = chacha20poly1305::Key::from_slice($key).unwrap(); + let s_tag = chacha20poly1305::Tag::from_slice($tag).unwrap(); + + // type annontate the ct and pt arguments + let pt: &mut [u8] = $pt; + let ct: &[u8] = $ct; + + // decrypt + pt.copy_from_slice(ct); + chacha20poly1305::open_detached( + pt, + if $ad.len() == 0 { None } else { Some($ad) }, + &s_tag, + &s_nonce, + &s_key, + ) + .map_err(|_| HandshakeError::DecryptionFailure) }}; } @@ -293,7 +324,7 @@ pub fn create_response( ) -> Result { let mut rng = OsRng::new().unwrap(); let (receiver, eph_r_pk, hs, ck) = state; - + let mut rng = OsRng::new().unwrap(); msg.f_sender.set(sender); msg.f_receiver.set(receiver); -- cgit v1.2.3-59-g8ed1b