Move to RustCrypto AEAD crate for handshake

This commit is contained in:
Mathias Hall-Andersen
2019-08-30 19:46:00 +02:00
parent 6785aa4cb5
commit 0520b28ac2
6 changed files with 163 additions and 271 deletions

View File

@@ -1,14 +1,20 @@
use generic_array::GenericArray;
use lazy_static::lazy_static;
use rand::{CryptoRng, RngCore};
use spin::RwLock;
use std::time::{Duration, Instant};
use blake2::Blake2s;
use sodiumoxide::crypto::aead::xchacha20poly1305_ietf;
use subtle::ConstantTimeEq;
// types to coalesce into bytes
use std::net::SocketAddr;
use x25519_dalek::PublicKey;
use std::net::SocketAddr;
// AEAD
use aead::{Aead, NewAead, Payload};
use chacha20poly1305::XChaCha20Poly1305;
// MAC
use blake2::Blake2s;
use subtle::ConstantTimeEq;
use super::messages::{CookieReply, MacsFooter, TYPE_COOKIE_REPLY};
use super::types::HandshakeError;
@@ -19,6 +25,7 @@ const LABEL_COOKIE: &[u8] = b"cookie--";
const SIZE_COOKIE: usize = 16;
const SIZE_SECRET: usize = 32;
const SIZE_MAC: usize = 16; // blake2s-mac128
const SIZE_TAG: usize = 16; // xchacha20poly1305 tag
lazy_static! {
pub static ref COOKIE_UPDATE_INTERVAL: Duration = Duration::new(120, 0);
@@ -51,41 +58,28 @@ 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(), xchacha20poly1305_ietf::TAGBYTES);
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());
($key:expr, $nonce:expr, $ad:expr, $pt:expr, $ct:expr) => {{
let ct = XChaCha20Poly1305::new(*GenericArray::from_slice($key))
.encrypt(
GenericArray::from_slice($nonce),
Payload { msg: $pt, aad: $ad },
)
.unwrap();
debug_assert_eq!(ct.len(), $pt.len() + SIZE_TAG);
$ct.copy_from_slice(&ct);
}};
}
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)
($key:expr, $nonce:expr, $ad:expr, $pt:expr, $ct:expr) => {{
debug_assert_eq!($ct.len(), $pt.len() + SIZE_TAG);
XChaCha20Poly1305::new(*GenericArray::from_slice($key))
.decrypt(
GenericArray::from_slice($nonce),
Payload { msg: $ct, aad: $ad },
)
.map_err(|_| HandshakeError::DecryptionFailure)
.map(|pt| $pt.copy_from_slice(&pt))
}};
}
@@ -151,12 +145,11 @@ impl Generator {
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_key, // key
&reply.f_nonce, // nonce
&mac1, // ad
&mut tau, // pt
&reply.f_cookie // ct || tag
)?;
self.cookie = Some(Cookie {
birth: Instant::now(),
@@ -260,8 +253,7 @@ impl Validator {
&msg.f_nonce, // nonce
&macs.f_mac1, // ad
&self.get_set_tau(rng, &src), // pt
&mut msg.f_cookie, // ct
&mut msg.f_cookie_tag // tagf
&mut msg.f_cookie // ct || tag
);
}

View File

@@ -16,6 +16,7 @@ const SIZE_TAG: usize = 16; // poly1305 tag
const SIZE_XNONCE: usize = 24; // xchacha20 nonce
const SIZE_COOKIE: usize = 16; //
const SIZE_X25519_POINT: usize = 32; // x25519 public key
const SIZE_TIMESTAMP: usize = 12;
pub const TYPE_INITIATION: u8 = 1;
pub const TYPE_RESPONSE: u8 = 2;
@@ -43,8 +44,7 @@ pub struct CookieReply {
pub f_type: U32<LittleEndian>,
pub f_receiver: U32<LittleEndian>,
pub f_nonce: [u8; SIZE_XNONCE],
pub f_cookie: [u8; SIZE_COOKIE],
pub f_cookie_tag: [u8; SIZE_TAG],
pub f_cookie: [u8; SIZE_COOKIE + SIZE_TAG],
}
/* Inner sub-messages */
@@ -62,10 +62,8 @@ pub struct NoiseInitiation {
pub f_type: U32<LittleEndian>,
pub f_sender: U32<LittleEndian>,
pub f_ephemeral: [u8; SIZE_X25519_POINT],
pub f_static: [u8; SIZE_X25519_POINT],
pub f_static_tag: [u8; SIZE_TAG],
pub f_timestamp: timestamp::TAI64N,
pub f_timestamp_tag: [u8; SIZE_TAG],
pub f_static: [u8; SIZE_X25519_POINT + SIZE_TAG],
pub f_timestamp: [u8; SIZE_TIMESTAMP + SIZE_TAG],
}
#[repr(packed)]
@@ -75,7 +73,7 @@ pub struct NoiseResponse {
pub f_sender: U32<LittleEndian>,
pub f_receiver: U32<LittleEndian>,
pub f_ephemeral: [u8; SIZE_X25519_POINT],
pub f_empty_tag: [u8; SIZE_TAG],
pub f_empty: [u8; SIZE_TAG],
}
/* Zero copy parsing of handshake messages */
@@ -145,8 +143,7 @@ impl Default for CookieReply {
f_type: <U32<LittleEndian>>::new(TYPE_COOKIE_REPLY as u32),
f_receiver: <U32<LittleEndian>>::ZERO,
f_nonce: [0u8; SIZE_XNONCE],
f_cookie: [0u8; SIZE_COOKIE],
f_cookie_tag: [0u8; SIZE_TAG],
f_cookie: [0u8; SIZE_COOKIE + SIZE_TAG],
}
}
}
@@ -164,13 +161,10 @@ impl Default for NoiseInitiation {
fn default() -> Self {
Self {
f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32),
f_sender: <U32<LittleEndian>>::ZERO,
f_ephemeral: [0u8; SIZE_X25519_POINT],
f_static: [0u8; SIZE_X25519_POINT],
f_static_tag: [0u8; SIZE_TAG],
f_timestamp: timestamp::ZERO,
f_timestamp_tag: [0u8; SIZE_TAG],
f_static: [0u8; SIZE_X25519_POINT + SIZE_TAG],
f_timestamp: [0u8; SIZE_TIMESTAMP + SIZE_TAG],
}
}
}
@@ -182,7 +176,7 @@ impl Default for NoiseResponse {
f_sender: <U32<LittleEndian>>::ZERO,
f_receiver: <U32<LittleEndian>>::ZERO,
f_ephemeral: [0u8; SIZE_X25519_POINT],
f_empty_tag: [0u8; SIZE_TAG],
f_empty: [0u8; SIZE_TAG],
}
}
}
@@ -208,12 +202,11 @@ impl fmt::Debug for CookieReply {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CookieReply {{ type = {}, receiver = {}, nonce = {}, cookie = {}|{} }}",
"CookieReply {{ type = {}, receiver = {}, nonce = {}, cookie = {} }}",
self.f_type,
self.f_receiver,
hex::encode(self.f_nonce),
hex::encode(self.f_cookie),
hex::encode(self.f_cookie_tag)
hex::encode(&self.f_nonce[..]),
hex::encode(&self.f_cookie[..]),
)
}
}
@@ -222,14 +215,12 @@ impl fmt::Debug for CookieReply {
impl fmt::Debug for NoiseInitiation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,
"NoiseInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}",
"NoiseInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}, timestamp = {} }}",
self.f_type.get(),
self.f_sender.get(),
hex::encode(self.f_ephemeral),
hex::encode(self.f_static),
hex::encode(self.f_static_tag),
hex::encode(self.f_timestamp),
hex::encode(self.f_timestamp_tag)
hex::encode(&self.f_ephemeral[..]),
hex::encode(&self.f_static[..]),
hex::encode(&self.f_timestamp[..]),
)
}
}
@@ -242,8 +233,8 @@ impl fmt::Debug for NoiseResponse {
self.f_type,
self.f_sender,
self.f_receiver,
hex::encode(self.f_ephemeral),
hex::encode(self.f_empty_tag)
hex::encode(&self.f_ephemeral[..]),
hex::encode(&self.f_empty[..])
)
}
}
@@ -254,8 +245,8 @@ impl fmt::Debug for MacsFooter {
write!(
f,
"Macs {{ mac1 = {}, mac2 = {} }}",
hex::encode(self.f_mac1),
hex::encode(self.f_mac2)
hex::encode(&self.f_mac1[..]),
hex::encode(&self.f_mac2[..])
)
}
}
@@ -306,7 +297,7 @@ mod tests {
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
0x13, 0x56, 0x52, 0x1f,
];
msg.noise.f_empty_tag = [
msg.noise.f_empty = [
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
0x2f, 0xde,
];
@@ -337,18 +328,12 @@ mod tests {
msg.noise.f_static = [
0xdc, 0x33, 0x90, 0x15, 0x8f, 0x82, 0x3e, 0x06, 0x44, 0xa0, 0xde, 0x4c, 0x15, 0x6c,
0x5d, 0xa4, 0x65, 0x99, 0xf6, 0x6c, 0xa1, 0x14, 0x77, 0xf9, 0xeb, 0x6a, 0xec, 0xc3,
0x3c, 0xda, 0x47, 0xe1,
];
msg.noise.f_static_tag = [
0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83,
0x6b, 0x42,
0x3c, 0xda, 0x47, 0xe1, 0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d,
0x86, 0x37, 0xee, 0x83, 0x6b, 0x42,
];
msg.noise.f_timestamp = [
0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42,
];
msg.noise.f_timestamp_tag = [
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
0x2f, 0xde,
0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42, 0x60, 0x0e,
0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05, 0x2f, 0xde,
];
msg.macs.f_mac1 = [
0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54,

View File

@@ -6,13 +6,14 @@ use x25519_dalek::StaticSecret;
use blake2::Blake2s;
use hmac::Hmac;
// AEAD (from libsodium)
use sodiumoxide::crypto::aead::chacha20poly1305;
// AEAD
use aead::{Aead, NewAead, Payload};
use chacha20poly1305::ChaCha20Poly1305;
use rand::{CryptoRng, RngCore};
use generic_array::typenum::*;
use generic_array::GenericArray;
use generic_array::*;
use super::device::Device;
use super::messages::{NoiseInitiation, NoiseResponse};
@@ -36,6 +37,7 @@ type TemporaryState = (u32, PublicKey, GenericArray<u8, U32>, GenericArray<u8, U
const SIZE_CK: usize = 32;
const SIZE_HS: usize = 32;
const SIZE_NONCE: usize = 8;
const SIZE_TAG: usize = 16;
// C := Hash(Construction)
const INITIAL_CK: [u8; SIZE_CK] = [
@@ -49,7 +51,7 @@ const INITIAL_HS: [u8; SIZE_HS] = [
0x2d, 0x9c, 0x6c, 0x66, 0x22, 0x93, 0xe8, 0xb7, 0x0e, 0xe1, 0x9c, 0x65, 0xba, 0x07, 0x9e, 0xf3,
];
const ZERO_NONCE: [u8; SIZE_NONCE] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
const ZERO_NONCE: [u8; 12] = [0u8; 12];
macro_rules! HASH {
( $($input:expr),* ) => {{
@@ -101,52 +103,20 @@ macro_rules! KDF3 {
}
macro_rules! SEAL {
($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());
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {{
let ct = ChaCha20Poly1305::new(*GenericArray::from_slice($key))
.encrypt(&ZERO_NONCE.into(), Payload { msg: $pt, aad: $ad })
.unwrap();
$ct.copy_from_slice(&ct);
}};
}
macro_rules! OPEN {
($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)
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {{
ChaCha20Poly1305::new(*GenericArray::from_slice($key))
.decrypt(&ZERO_NONCE.into(), Payload { msg: $ct, aad: $ad })
.map_err(|_| HandshakeError::DecryptionFailure)
.map(|pt| $pt.copy_from_slice(&pt))
}};
}
@@ -275,15 +245,14 @@ pub fn create_initiation<T: Copy, R: RngCore + CryptoRng>(
SEAL!(
&key,
&hs, // ad
device.pk.as_bytes(), // pt
&mut msg.f_static, // ct
&mut msg.f_static_tag // tag
&hs, // ad
device.pk.as_bytes(), // pt
&mut msg.f_static // ct || tag
);
// H := Hash(H || msg.static)
let hs = HASH!(&hs, &msg.f_static, &msg.f_static_tag);
let hs = HASH!(&hs, &msg.f_static[..]);
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
@@ -293,15 +262,14 @@ pub fn create_initiation<T: Copy, R: RngCore + CryptoRng>(
SEAL!(
&key,
&hs, // ad
&timestamp::now(), // pt
&mut msg.f_timestamp, // ct
&mut msg.f_timestamp_tag // tag
&hs, // ad
&timestamp::now(), // pt
&mut msg.f_timestamp // ct || tag
);
// H := Hash(H || msg.timestamp)
let hs = HASH!(&hs, &msg.f_timestamp, &msg.f_timestamp_tag);
let hs = HASH!(&hs, &msg.f_timestamp);
// update state of peer
@@ -344,17 +312,16 @@ pub fn consume_initiation<'a, T: Copy>(
OPEN!(
&key,
&hs, // ad
&mut pk, // pt
&msg.f_static, // ct
&msg.f_static_tag // tag
&hs, // ad
&mut pk, // pt
&msg.f_static // ct || tag
)?;
let peer = device.lookup_pk(&PublicKey::from(pk))?;
// H := Hash(H || msg.static)
let hs = HASH!(&hs, &msg.f_static, &msg.f_static_tag);
let hs = HASH!(&hs, &msg.f_static[..]);
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
@@ -366,10 +333,9 @@ pub fn consume_initiation<'a, T: Copy>(
OPEN!(
&key,
&hs, // ad
&mut ts, // pt
&msg.f_timestamp, // ct
&msg.f_timestamp_tag // tag
&hs, // ad
&mut ts, // pt
&msg.f_timestamp // ct || tag
)?;
// check and update timestamp
@@ -378,7 +344,7 @@ pub fn consume_initiation<'a, T: Copy>(
// H := Hash(H || msg.timestamp)
let hs = HASH!(&hs, &msg.f_timestamp, &msg.f_timestamp_tag);
let hs = HASH!(&hs, &msg.f_timestamp);
// return state (to create response)
@@ -437,10 +403,9 @@ pub fn create_response<T: Copy, R: RngCore + CryptoRng>(
SEAL!(
&key,
&hs, // ad
&[], // pt
&mut [], // ct
&mut msg.f_empty_tag // tag
&hs, // ad
&[], // pt
&mut msg.f_empty // \epsilon || tag
);
/* not strictly needed
@@ -515,10 +480,9 @@ pub fn consume_response<T: Copy>(
OPEN!(
&key,
&hs, // ad
&mut [], // pt
&[], // ct
&msg.f_empty_tag // tag
&hs, // ad
&mut [], // pt
&msg.f_empty // \epsilon || tag
)?;
// derive key-pair

View File

@@ -13,7 +13,6 @@ use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use sodiumoxide;
use types::{Bind, KeyPair};
struct Test {}
@@ -71,8 +70,6 @@ struct PeerTimer {
fn main() {
let runner = Runner::new(Duration::from_millis(100), 1000, 1024);
// choose optimal crypto implementations for platform
sodiumoxide::init().unwrap();
{
let router = router::Device::new(
4,