Remove rust-crypto, move to libsodium bindings

This commit is contained in:
Mathias Hall-Andersen
2019-08-02 17:18:37 +02:00
parent d6e40f9ea6
commit 2bdcda067c
5 changed files with 294 additions and 64 deletions

View File

@@ -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<Cookie>,
}
@@ -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<Secret>,
}
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<T>(&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<T>(
&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)]

View File

@@ -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<T: Copy>(
) -> Result<KeyPair, HandshakeError> {
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);

View File

@@ -1,7 +1,12 @@
mod handshake;
mod types;
use sodiumoxide;
use handshake::Device;
use types::KeyPair;
fn main() {}
fn main() {
// choose optimal crypto implementations for platform
sodiumoxide::init().unwrap();
}