Move to nested handshake message structure
Having the nested structure:
Handshake Message:
Noise part (zerocopy message)
MAC footer part (zerocopy message)
Greatly simplifies processing the MAC fields,
since the MAC footer covers the noise part, which can
be accessed as bytes using AsBytes.
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -70,7 +70,7 @@ dependencies = [
|
|||||||
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -298,7 +298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.0.0"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -367,12 +367,14 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"x25519-dalek 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"x25519-dalek 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zerocopy 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zerocopy 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -445,7 +447,7 @@ dependencies = [
|
|||||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||||
"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55"
|
"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55"
|
||||||
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||||
"checksum subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "702662512f3ddeb74a64ce2fbbf3707ee1b6bb663d28bb054e0779bbc720d926"
|
"checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829"
|
||||||
"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e"
|
"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e"
|
||||||
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
|
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
|
||||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ rust-crypto = "^0.2"
|
|||||||
generic-array = "0.12.3"
|
generic-array = "0.12.3"
|
||||||
zerocopy = "0.2.7"
|
zerocopy = "0.2.7"
|
||||||
byteorder = "1.3.1"
|
byteorder = "1.3.1"
|
||||||
|
digest = "0.8.0"
|
||||||
|
|
||||||
[dependencies.x25519-dalek]
|
[dependencies.x25519-dalek]
|
||||||
version = "^0.5"
|
version = "^0.5"
|
||||||
|
|
||||||
|
[dependencies.subtle]
|
||||||
|
version = "2.1"
|
||||||
|
features = ["nightly"]
|
||||||
@@ -7,7 +7,8 @@ use rand::rngs::OsRng;
|
|||||||
use x25519_dalek::PublicKey;
|
use x25519_dalek::PublicKey;
|
||||||
use x25519_dalek::StaticSecret;
|
use x25519_dalek::StaticSecret;
|
||||||
|
|
||||||
use super::messages;
|
use super::messages::{CookieReply, Initiation, Response};
|
||||||
|
use super::messages::{TYPE_COOKIEREPLY, TYPE_INITIATION, TYPE_RESPONSE};
|
||||||
use super::noise;
|
use super::noise;
|
||||||
use super::peer::Peer;
|
use super::peer::Peer;
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
@@ -170,20 +171,40 @@ where
|
|||||||
/// * `msg` - Byte slice containing the message (untrusted input)
|
/// * `msg` - Byte slice containing the message (untrusted input)
|
||||||
pub fn process(&self, msg: &[u8]) -> Result<Output<T>, HandshakeError> {
|
pub fn process(&self, msg: &[u8]) -> Result<Output<T>, HandshakeError> {
|
||||||
match msg.get(0) {
|
match msg.get(0) {
|
||||||
Some(&messages::TYPE_INITIATION) => {
|
Some(&TYPE_INITIATION) => {
|
||||||
|
let msg = Initiation::parse(msg)?;
|
||||||
|
|
||||||
|
// check mac footer and ratelimiter
|
||||||
|
|
||||||
// consume the initiation
|
// consume the initiation
|
||||||
let (peer, st) = noise::consume_initiation(self, msg)?;
|
let (peer, st) = noise::consume_initiation(self, &msg.noise)?;
|
||||||
|
|
||||||
// allocate new index for response
|
// allocate new index for response
|
||||||
let sender = self.allocate(peer);
|
let sender = self.allocate(peer);
|
||||||
|
|
||||||
// create response (release id on error)
|
// create response (release id on error), TODO: take slice
|
||||||
noise::create_response(peer, sender, st).map_err(|e| {
|
let mut resp = Response::default();
|
||||||
|
noise::create_response(peer, sender, st, &mut resp.noise).map_err(|e| {
|
||||||
self.release(sender);
|
self.release(sender);
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(&messages::TYPE_RESPONSE) => noise::consume_response(self, msg),
|
Some(&TYPE_RESPONSE) => {
|
||||||
|
let msg = Response::parse(msg)?;
|
||||||
|
|
||||||
|
// check mac footer and ratelimiter
|
||||||
|
|
||||||
|
noise::consume_response(self, &msg.noise)
|
||||||
|
}
|
||||||
|
Some(&TYPE_COOKIEREPLY) => {
|
||||||
|
let msg = CookieReply::parse(msg)?;
|
||||||
|
|
||||||
|
// validate cookie reply
|
||||||
|
|
||||||
|
// update cookie generator for peer
|
||||||
|
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
_ => Err(HandshakeError::InvalidMessageFormat),
|
_ => Err(HandshakeError::InvalidMessageFormat),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,9 +256,9 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::super::messages::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use hex;
|
use hex;
|
||||||
use messages::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handshake() {
|
fn handshake() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use blake2::Blake2s;
|
|||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
use x25519_dalek::PublicKey;
|
use x25519_dalek::PublicKey;
|
||||||
|
|
||||||
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified};
|
use super::messages::{CookieReply, MacsFooter};
|
||||||
|
|
||||||
const LABEL_MAC1: &[u8] = b"mac1----";
|
const LABEL_MAC1: &[u8] = b"mac1----";
|
||||||
const LABEL_COOKIE: &[u8] = b"cookie--";
|
const LABEL_COOKIE: &[u8] = b"cookie--";
|
||||||
@@ -38,22 +38,6 @@ macro_rules! MAC {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
|
||||||
pub struct MacsFooter {
|
|
||||||
pub f_mac1: [u8; SIZE_MAC],
|
|
||||||
pub f_mac2: [u8; SIZE_MAC],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MacsFooter {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
f_mac1: [0u8; SIZE_MAC],
|
|
||||||
f_mac2: [0u8; SIZE_MAC],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Generator {
|
struct Generator {
|
||||||
mac1_key: [u8; 32],
|
mac1_key: [u8; 32],
|
||||||
cookie_value: [u8; 16],
|
cookie_value: [u8; 16],
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use byteorder::LittleEndian;
|
|||||||
use zerocopy::byteorder::U32;
|
use zerocopy::byteorder::U32;
|
||||||
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified};
|
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified};
|
||||||
|
|
||||||
use super::macs::MacsFooter;
|
|
||||||
use super::timestamp;
|
use super::timestamp;
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
|
|
||||||
|
const SIZE_MAC: usize = 16;
|
||||||
const SIZE_TAG: usize = 16; // poly1305 tag
|
const SIZE_TAG: usize = 16; // poly1305 tag
|
||||||
const SIZE_NONCE: usize = 16; // xchacha20 nonce
|
const SIZE_NONCE: usize = 16; // xchacha20 nonce
|
||||||
const SIZE_COOKIE: usize = 16; //
|
const SIZE_COOKIE: usize = 16; //
|
||||||
@@ -21,28 +21,20 @@ pub const TYPE_INITIATION: u8 = 1;
|
|||||||
pub const TYPE_RESPONSE: u8 = 2;
|
pub const TYPE_RESPONSE: u8 = 2;
|
||||||
pub const TYPE_COOKIEREPLY: u8 = 3;
|
pub const TYPE_COOKIEREPLY: u8 = 3;
|
||||||
|
|
||||||
#[repr(C)]
|
/* Handshake messsages */
|
||||||
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
|
||||||
pub struct Initiation {
|
|
||||||
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_macs: MacsFooter,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
f_type: U32<LittleEndian>,
|
pub noise: NoiseResponse, // inner message covered by macs
|
||||||
pub f_sender: U32<LittleEndian>,
|
pub macs: MacsFooter,
|
||||||
pub f_receiver: U32<LittleEndian>,
|
}
|
||||||
pub f_ephemeral: [u8; SIZE_X25519_POINT],
|
|
||||||
pub f_empty_tag: [u8; SIZE_TAG],
|
#[repr(C)]
|
||||||
pub f_macs: MacsFooter,
|
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
||||||
|
pub struct Initiation {
|
||||||
|
pub noise: NoiseInitiation, // inner message covered by macs
|
||||||
|
pub macs: MacsFooter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -55,8 +47,121 @@ pub struct CookieReply {
|
|||||||
pub f_cookie_tag: [u8; SIZE_TAG],
|
pub f_cookie_tag: [u8; SIZE_TAG],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inner sub-messages */
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
||||||
|
pub struct MacsFooter {
|
||||||
|
pub f_mac1: [u8; SIZE_MAC],
|
||||||
|
pub f_mac2: [u8; SIZE_MAC],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
||||||
|
pub struct NoiseInitiation {
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
||||||
|
pub struct NoiseResponse {
|
||||||
|
f_type: U32<LittleEndian>,
|
||||||
|
pub f_sender: U32<LittleEndian>,
|
||||||
|
pub f_receiver: U32<LittleEndian>,
|
||||||
|
pub f_ephemeral: [u8; SIZE_X25519_POINT],
|
||||||
|
pub f_empty_tag: [u8; SIZE_TAG]
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero copy parsing of handshake messages */
|
||||||
|
|
||||||
|
impl Initiation {
|
||||||
|
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
||||||
|
let msg: LayoutVerified<B, Self> =
|
||||||
|
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
||||||
|
|
||||||
|
if msg.noise.f_type.get() != (TYPE_INITIATION as u32) {
|
||||||
|
return Err(HandshakeError::InvalidMessageFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
||||||
|
let msg: LayoutVerified<B, Self> =
|
||||||
|
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
||||||
|
|
||||||
|
if msg.noise.f_type.get() != (TYPE_RESPONSE as u32) {
|
||||||
|
return Err(HandshakeError::InvalidMessageFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CookieReply {
|
||||||
|
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
||||||
|
let msg: LayoutVerified<B, Self> =
|
||||||
|
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
||||||
|
|
||||||
|
if msg.f_type.get() != (TYPE_COOKIEREPLY as u32) {
|
||||||
|
return Err(HandshakeError::InvalidMessageFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default values */
|
||||||
|
|
||||||
|
impl Default for Response {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
noise: Default::default(),
|
||||||
|
macs: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Initiation {
|
impl Default for Initiation {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
noise: Default::default(),
|
||||||
|
macs: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CookieReply {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
f_type: <U32<LittleEndian>>::new(TYPE_COOKIEREPLY as u32),
|
||||||
|
f_receiver: <U32<LittleEndian>>::ZERO,
|
||||||
|
f_nonce: [0u8; SIZE_NONCE],
|
||||||
|
f_cookie: [0u8; SIZE_COOKIE],
|
||||||
|
f_cookie_tag: [0u8; SIZE_TAG],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MacsFooter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
f_mac1: [0u8; SIZE_MAC],
|
||||||
|
f_mac2: [0u8; SIZE_MAC],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NoiseInitiation {
|
||||||
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32),
|
f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32),
|
||||||
|
|
||||||
@@ -65,30 +170,58 @@ impl Default for Initiation {
|
|||||||
f_static: [0u8; SIZE_X25519_POINT],
|
f_static: [0u8; SIZE_X25519_POINT],
|
||||||
f_static_tag: [0u8; SIZE_TAG],
|
f_static_tag: [0u8; SIZE_TAG],
|
||||||
f_timestamp: timestamp::ZERO,
|
f_timestamp: timestamp::ZERO,
|
||||||
f_timestamp_tag: [0u8; SIZE_TAG],
|
f_timestamp_tag: [0u8; SIZE_TAG]
|
||||||
f_macs: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Initiation {
|
impl Default for NoiseResponse {
|
||||||
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
fn default() -> Self {
|
||||||
let msg: LayoutVerified<B, Self> =
|
Self {
|
||||||
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
f_type: <U32<LittleEndian>>::new(TYPE_RESPONSE as u32),
|
||||||
|
f_sender: <U32<LittleEndian>>::ZERO,
|
||||||
if msg.f_type.get() != (TYPE_INITIATION as u32) {
|
f_receiver: <U32<LittleEndian>>::ZERO,
|
||||||
return Err(HandshakeError::InvalidMessageFormat);
|
f_ephemeral: [0u8; SIZE_X25519_POINT],
|
||||||
|
f_empty_tag: [0u8; SIZE_TAG],
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(msg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Debug formatting (for testing purposes) */
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl fmt::Debug for Initiation {
|
impl fmt::Debug for Initiation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Initiation {{ {:?} || {:?} }}", self.noise, self.macs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl fmt::Debug for Response {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Response {{ {:?} || {:?} }}", self.noise, self.macs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl fmt::Debug for CookieReply {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f,
|
write!(f,
|
||||||
"MessageInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}",
|
"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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl fmt::Debug for NoiseInitiation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f,
|
||||||
|
"NoiseInitiation {{ type = {}, sender = {}, ephemeral = {}, static = {}|{}, timestamp = {}|{} }}",
|
||||||
self.f_type.get(),
|
self.f_type.get(),
|
||||||
self.f_sender.get(),
|
self.f_sender.get(),
|
||||||
hex::encode(self.f_ephemeral),
|
hex::encode(self.f_ephemeral),
|
||||||
@@ -101,52 +234,10 @@ impl fmt::Debug for Initiation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl PartialEq for Initiation {
|
impl fmt::Debug for NoiseResponse {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.f_type.get() == other.f_type.get()
|
|
||||||
&& self.f_sender.get() == other.f_sender.get()
|
|
||||||
&& self.f_ephemeral[..] == other.f_ephemeral[..]
|
|
||||||
&& self.f_static[..] == other.f_static[..]
|
|
||||||
&& self.f_static_tag[..] == other.f_static_tag[..]
|
|
||||||
&& self.f_timestamp[..] == other.f_timestamp
|
|
||||||
&& self.f_timestamp_tag[..] == other.f_timestamp_tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Eq for Initiation {}
|
|
||||||
|
|
||||||
impl Response {
|
|
||||||
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
|
||||||
let msg: LayoutVerified<B, Self> =
|
|
||||||
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
|
||||||
|
|
||||||
if msg.f_type.get() != (TYPE_RESPONSE as u32) {
|
|
||||||
return Err(HandshakeError::InvalidMessageFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Response {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
f_type: <U32<LittleEndian>>::new(TYPE_RESPONSE as u32),
|
|
||||||
f_sender: <U32<LittleEndian>>::ZERO,
|
|
||||||
f_receiver: <U32<LittleEndian>>::ZERO,
|
|
||||||
f_ephemeral: [0u8; SIZE_X25519_POINT],
|
|
||||||
f_empty_tag: [0u8; SIZE_TAG],
|
|
||||||
f_macs: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl fmt::Debug for Response {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f,
|
write!(f,
|
||||||
"MessageResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}",
|
"NoiseResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}",
|
||||||
self.f_type,
|
self.f_type,
|
||||||
self.f_sender,
|
self.f_sender,
|
||||||
self.f_receiver,
|
self.f_receiver,
|
||||||
@@ -157,16 +248,47 @@ impl fmt::Debug for Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl PartialEq for Response {
|
impl fmt::Debug for MacsFooter {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.f_type == other.f_type
|
write!(f,
|
||||||
&& self.f_sender == other.f_sender
|
"Macs {{ mac1 = {}, mac2 = {} }}",
|
||||||
&& self.f_receiver == other.f_receiver
|
hex::encode(self.f_mac1),
|
||||||
&& self.f_ephemeral == other.f_ephemeral
|
hex::encode(self.f_mac2)
|
||||||
&& self.f_empty_tag == other.f_empty_tag
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Equality (for testing purposes) */
|
||||||
|
|
||||||
|
macro_rules! eq_as_bytes {
|
||||||
|
($type:path) => {
|
||||||
|
impl PartialEq for $type {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.as_bytes() == other.as_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for $type {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
eq_as_bytes!(Initiation);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
eq_as_bytes!(Response);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
eq_as_bytes!(CookieReply);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
eq_as_bytes!(MacsFooter);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
eq_as_bytes!(NoiseInitiation);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
eq_as_bytes!(NoiseResponse);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -175,22 +297,22 @@ mod tests {
|
|||||||
fn message_response_identity() {
|
fn message_response_identity() {
|
||||||
let mut msg: Response = Default::default();
|
let mut msg: Response = Default::default();
|
||||||
|
|
||||||
msg.f_sender.set(146252);
|
msg.noise.f_sender.set(146252);
|
||||||
msg.f_receiver.set(554442);
|
msg.noise.f_receiver.set(554442);
|
||||||
msg.f_ephemeral = [
|
msg.noise.f_ephemeral = [
|
||||||
0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c,
|
0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c,
|
||||||
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
|
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
|
||||||
0x13, 0x56, 0x52, 0x1f,
|
0x13, 0x56, 0x52, 0x1f,
|
||||||
];
|
];
|
||||||
msg.f_empty_tag = [
|
msg.noise.f_empty_tag = [
|
||||||
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
|
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
|
||||||
0x2f, 0xde,
|
0x2f, 0xde,
|
||||||
];
|
];
|
||||||
msg.f_macs.f_mac1 = [
|
msg.macs.f_mac1 = [
|
||||||
0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54,
|
0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54,
|
||||||
0x69, 0x29,
|
0x69, 0x29,
|
||||||
];
|
];
|
||||||
msg.f_macs.f_mac2 = [
|
msg.macs.f_mac2 = [
|
||||||
0x4f, 0xd2, 0x1b, 0xfe, 0x77, 0xe6, 0x2e, 0xc9, 0x07, 0xe2, 0x87, 0x17, 0xbb, 0xe5,
|
0x4f, 0xd2, 0x1b, 0xfe, 0x77, 0xe6, 0x2e, 0xc9, 0x07, 0xe2, 0x87, 0x17, 0xbb, 0xe5,
|
||||||
0xdf, 0xbb,
|
0xdf, 0xbb,
|
||||||
];
|
];
|
||||||
@@ -204,33 +326,33 @@ mod tests {
|
|||||||
fn message_initiate_identity() {
|
fn message_initiate_identity() {
|
||||||
let mut msg: Initiation = Default::default();
|
let mut msg: Initiation = Default::default();
|
||||||
|
|
||||||
msg.f_sender.set(575757);
|
msg.noise.f_sender.set(575757);
|
||||||
msg.f_ephemeral = [
|
msg.noise.f_ephemeral = [
|
||||||
0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c,
|
0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c,
|
||||||
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
|
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
|
||||||
0x13, 0x56, 0x52, 0x1f,
|
0x13, 0x56, 0x52, 0x1f,
|
||||||
];
|
];
|
||||||
msg.f_static = [
|
msg.noise.f_static = [
|
||||||
0xdc, 0x33, 0x90, 0x15, 0x8f, 0x82, 0x3e, 0x06, 0x44, 0xa0, 0xde, 0x4c, 0x15, 0x6c,
|
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,
|
0x5d, 0xa4, 0x65, 0x99, 0xf6, 0x6c, 0xa1, 0x14, 0x77, 0xf9, 0xeb, 0x6a, 0xec, 0xc3,
|
||||||
0x3c, 0xda, 0x47, 0xe1,
|
0x3c, 0xda, 0x47, 0xe1,
|
||||||
];
|
];
|
||||||
msg.f_static_tag = [
|
msg.noise.f_static_tag = [
|
||||||
0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83,
|
0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83,
|
||||||
0x6b, 0x42,
|
0x6b, 0x42,
|
||||||
];
|
];
|
||||||
msg.f_timestamp = [
|
msg.noise.f_timestamp = [
|
||||||
0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42,
|
0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42,
|
||||||
];
|
];
|
||||||
msg.f_timestamp_tag = [
|
msg.noise.f_timestamp_tag = [
|
||||||
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
|
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
|
||||||
0x2f, 0xde,
|
0x2f, 0xde,
|
||||||
];
|
];
|
||||||
msg.f_macs.f_mac1 = [
|
msg.macs.f_mac1 = [
|
||||||
0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54,
|
0xf2, 0xad, 0x40, 0xb5, 0xf7, 0xde, 0x77, 0x35, 0x89, 0x19, 0xb7, 0x5c, 0xf9, 0x54,
|
||||||
0x69, 0x29,
|
0x69, 0x29,
|
||||||
];
|
];
|
||||||
msg.f_macs.f_mac2 = [
|
msg.macs.f_mac2 = [
|
||||||
0x4f, 0xd2, 0x1b, 0xfe, 0x77, 0xe6, 0x2e, 0xc9, 0x07, 0xe2, 0x87, 0x17, 0xbb, 0xe5,
|
0x4f, 0xd2, 0x1b, 0xfe, 0x77, 0xe6, 0x2e, 0xc9, 0x07, 0xe2, 0x87, 0x17, 0xbb, 0xe5,
|
||||||
0xdf, 0xbb,
|
0xdf, 0xbb,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use generic_array::GenericArray;
|
|||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
use super::device::Device;
|
use super::device::Device;
|
||||||
use super::messages::{Initiation, Response};
|
use super::messages::{NoiseInitiation, NoiseResponse};
|
||||||
use super::peer::{Peer, State};
|
use super::peer::{Peer, State};
|
||||||
use super::timestamp;
|
use super::timestamp;
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
@@ -142,7 +142,7 @@ pub fn create_initiation<T: Copy>(
|
|||||||
sender: u32,
|
sender: u32,
|
||||||
) -> Result<Vec<u8>, HandshakeError> {
|
) -> Result<Vec<u8>, HandshakeError> {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
let mut msg: Initiation = Default::default();
|
let mut msg: NoiseInitiation = Default::default();
|
||||||
|
|
||||||
// initialize state
|
// initialize state
|
||||||
|
|
||||||
@@ -221,12 +221,8 @@ pub fn create_initiation<T: Copy>(
|
|||||||
|
|
||||||
pub fn consume_initiation<'a, T: Copy>(
|
pub fn consume_initiation<'a, T: Copy>(
|
||||||
device: &'a Device<T>,
|
device: &'a Device<T>,
|
||||||
msg: &[u8],
|
msg: &NoiseInitiation,
|
||||||
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
|
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
|
||||||
// parse message
|
|
||||||
|
|
||||||
let msg = Initiation::parse(msg)?;
|
|
||||||
|
|
||||||
// initialize state
|
// initialize state
|
||||||
|
|
||||||
let ck = INITIAL_CK;
|
let ck = INITIAL_CK;
|
||||||
@@ -295,12 +291,11 @@ pub fn consume_initiation<'a, T: Copy>(
|
|||||||
|
|
||||||
pub fn create_response<T: Copy>(
|
pub fn create_response<T: Copy>(
|
||||||
peer: &Peer<T>,
|
peer: &Peer<T>,
|
||||||
sender: u32, // sending identifier
|
sender: u32, // sending identifier
|
||||||
state: TemporaryState, // state from "consume_initiation"
|
state: TemporaryState, // state from "consume_initiation"
|
||||||
|
msg: &mut NoiseResponse, // resulting response
|
||||||
) -> Result<Output<T>, HandshakeError> {
|
) -> Result<Output<T>, HandshakeError> {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
let mut msg: Response = Default::default();
|
|
||||||
|
|
||||||
let (receiver, eph_r_pk, hs, ck) = state;
|
let (receiver, eph_r_pk, hs, ck) = state;
|
||||||
|
|
||||||
msg.f_sender.set(sender);
|
msg.f_sender.set(sender);
|
||||||
@@ -380,12 +375,8 @@ pub fn create_response<T: Copy>(
|
|||||||
|
|
||||||
pub fn consume_response<T: Copy>(
|
pub fn consume_response<T: Copy>(
|
||||||
device: &Device<T>,
|
device: &Device<T>,
|
||||||
msg: &[u8],
|
msg: &NoiseResponse,
|
||||||
) -> Result<Output<T>, HandshakeError> {
|
) -> Result<Output<T>, HandshakeError> {
|
||||||
// parse message
|
|
||||||
|
|
||||||
let msg = Response::parse(msg)?;
|
|
||||||
|
|
||||||
// retrieve peer and associated state
|
// retrieve peer and associated state
|
||||||
|
|
||||||
let peer = device.lookup_id(msg.f_receiver.get())?;
|
let peer = device.lookup_id(msg.f_receiver.get())?;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
mod noise;
|
mod handshake;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
use noise::Device;
|
use handshake::Device;
|
||||||
use types::KeyPair;
|
use types::KeyPair;
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|||||||
@@ -1,315 +0,0 @@
|
|||||||
use spin::RwLock;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use rand::rngs::OsRng;
|
|
||||||
|
|
||||||
use x25519_dalek::PublicKey;
|
|
||||||
use x25519_dalek::StaticSecret;
|
|
||||||
|
|
||||||
use super::messages;
|
|
||||||
use super::noise;
|
|
||||||
use super::peer::Peer;
|
|
||||||
use super::types::*;
|
|
||||||
|
|
||||||
pub struct Device<T> {
|
|
||||||
pub sk: StaticSecret, // static secret key
|
|
||||||
pub pk: PublicKey, // static public key
|
|
||||||
pk_map: HashMap<[u8; 32], Peer<T>>, // public key -> peer state
|
|
||||||
id_map: RwLock<HashMap<u32, [u8; 32]>>, // receiver ids -> public key
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A mutable reference to the device needs to be held during configuration.
|
|
||||||
* Wrapping the device in a RwLock enables peer config after "configuration time"
|
|
||||||
*/
|
|
||||||
impl<T> Device<T>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
/// Initialize a new handshake state machine
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `sk` - x25519 scalar representing the local private key
|
|
||||||
pub fn new(sk: StaticSecret) -> Device<T> {
|
|
||||||
Device {
|
|
||||||
pk: PublicKey::from(&sk),
|
|
||||||
sk: sk,
|
|
||||||
pk_map: HashMap::new(),
|
|
||||||
id_map: RwLock::new(HashMap::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a new public key to the state machine
|
|
||||||
/// To remove public keys, you must create a new machine instance
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `pk` - The public key to add
|
|
||||||
/// * `identifier` - Associated identifier which can be used to distinguish the peers
|
|
||||||
pub fn add(&mut self, pk: PublicKey, identifier: T) -> Result<(), ConfigError> {
|
|
||||||
// check that the pk is not added twice
|
|
||||||
|
|
||||||
if let Some(_) = self.pk_map.get(pk.as_bytes()) {
|
|
||||||
return Err(ConfigError::new("Duplicate public key"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// check that the pk is not that of the device
|
|
||||||
|
|
||||||
if *self.pk.as_bytes() == *pk.as_bytes() {
|
|
||||||
return Err(ConfigError::new(
|
|
||||||
"Public key corresponds to secret key of interface",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// map : pk -> new index
|
|
||||||
|
|
||||||
self.pk_map.insert(
|
|
||||||
*pk.as_bytes(),
|
|
||||||
Peer::new(identifier, pk, self.sk.diffie_hellman(&pk)),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a peer by public key
|
|
||||||
/// To remove public keys, you must create a new machine instance
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `pk` - The public key of the peer to remove
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The call might fail if the public key is not found
|
|
||||||
pub fn remove(&mut self, pk: PublicKey) -> Result<(), ConfigError> {
|
|
||||||
// take write-lock on receive id table
|
|
||||||
let mut id_map = self.id_map.write();
|
|
||||||
|
|
||||||
// remove the peer
|
|
||||||
self.pk_map
|
|
||||||
.remove(pk.as_bytes())
|
|
||||||
.ok_or(ConfigError::new("Public key not in device"))?;
|
|
||||||
|
|
||||||
// pruge the id map (linear scan)
|
|
||||||
id_map.retain(|_, v| v != pk.as_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a psk to the peer
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `pk` - The public key of the peer
|
|
||||||
/// * `psk` - The psk to set / unset
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The call might fail if the public key is not found
|
|
||||||
pub fn set_psk(&mut self, pk: PublicKey, psk: Option<Psk>) -> Result<(), ConfigError> {
|
|
||||||
match self.pk_map.get_mut(pk.as_bytes()) {
|
|
||||||
Some(mut peer) => {
|
|
||||||
peer.psk = match psk {
|
|
||||||
Some(v) => v,
|
|
||||||
None => [0u8; 32],
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => Err(ConfigError::new("No such public key")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the psk for the peer
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `pk` - The public key of the peer
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A 32 byte array holding the PSK
|
|
||||||
///
|
|
||||||
/// The call might fail if the public key is not found
|
|
||||||
pub fn get_psk(&self, pk: PublicKey) -> Result<Psk, ConfigError> {
|
|
||||||
match self.pk_map.get(pk.as_bytes()) {
|
|
||||||
Some(peer) => Ok(peer.psk),
|
|
||||||
_ => Err(ConfigError::new("No such public key")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Release an id back to the pool
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `id` - The (sender) id to release
|
|
||||||
pub fn release(&self, id: u32) {
|
|
||||||
let mut m = self.id_map.write();
|
|
||||||
debug_assert!(m.contains_key(&id), "Releasing id not allocated");
|
|
||||||
m.remove(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin a new handshake
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `pk` - Public key of peer to initiate handshake for
|
|
||||||
pub fn begin(&self, pk: &PublicKey) -> Result<Vec<u8>, HandshakeError> {
|
|
||||||
match self.pk_map.get(pk.as_bytes()) {
|
|
||||||
None => Err(HandshakeError::UnknownPublicKey),
|
|
||||||
Some(peer) => {
|
|
||||||
let sender = self.allocate(peer);
|
|
||||||
noise::create_initiation(self, peer, sender)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process a handshake message.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `msg` - Byte slice containing the message (untrusted input)
|
|
||||||
pub fn process(&self, msg: &[u8]) -> Result<Output<T>, HandshakeError> {
|
|
||||||
match msg.get(0) {
|
|
||||||
Some(&messages::TYPE_INITIATION) => {
|
|
||||||
// consume the initiation
|
|
||||||
let (peer, st) = noise::consume_initiation(self, msg)?;
|
|
||||||
|
|
||||||
// allocate new index for response
|
|
||||||
let sender = self.allocate(peer);
|
|
||||||
|
|
||||||
// create response (release id on error)
|
|
||||||
noise::create_response(peer, sender, st).map_err(|e| {
|
|
||||||
self.release(sender);
|
|
||||||
e
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some(&messages::TYPE_RESPONSE) => noise::consume_response(self, msg),
|
|
||||||
_ => Err(HandshakeError::InvalidMessageFormat),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function
|
|
||||||
//
|
|
||||||
// Return the peer associated with the public key
|
|
||||||
pub(crate) fn lookup_pk(&self, pk: &PublicKey) -> Result<&Peer<T>, HandshakeError> {
|
|
||||||
self.pk_map
|
|
||||||
.get(pk.as_bytes())
|
|
||||||
.ok_or(HandshakeError::UnknownPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function
|
|
||||||
//
|
|
||||||
// Return the peer currently associated with the receiver identifier
|
|
||||||
pub(crate) fn lookup_id(&self, id: u32) -> Result<&Peer<T>, HandshakeError> {
|
|
||||||
let im = self.id_map.read();
|
|
||||||
let pk = im.get(&id).ok_or(HandshakeError::UnknownReceiverId)?;
|
|
||||||
match self.pk_map.get(pk) {
|
|
||||||
Some(peer) => Ok(peer),
|
|
||||||
_ => unreachable!(), // if the id-lookup succeeded, the peer should exist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function
|
|
||||||
//
|
|
||||||
// Allocated a new receiver identifier for the peer
|
|
||||||
fn allocate(&self, peer: &Peer<T>) -> u32 {
|
|
||||||
let mut rng = OsRng::new().unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let id = rng.gen();
|
|
||||||
|
|
||||||
// check membership with read lock
|
|
||||||
if self.id_map.read().contains_key(&id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// take write lock and add index
|
|
||||||
let mut m = self.id_map.write();
|
|
||||||
if !m.contains_key(&id) {
|
|
||||||
m.insert(id, *peer.pk.as_bytes());
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use hex;
|
|
||||||
use messages::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn handshake() {
|
|
||||||
// generate new keypairs
|
|
||||||
|
|
||||||
let mut rng = OsRng::new().unwrap();
|
|
||||||
|
|
||||||
let sk1 = StaticSecret::new(&mut rng);
|
|
||||||
let pk1 = PublicKey::from(&sk1);
|
|
||||||
|
|
||||||
let sk2 = StaticSecret::new(&mut rng);
|
|
||||||
let pk2 = PublicKey::from(&sk2);
|
|
||||||
|
|
||||||
// pick random psk
|
|
||||||
|
|
||||||
let mut psk = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut psk[..]);
|
|
||||||
|
|
||||||
// intialize devices on both ends
|
|
||||||
|
|
||||||
let mut dev1 = Device::new(sk1);
|
|
||||||
let mut dev2 = Device::new(sk2);
|
|
||||||
|
|
||||||
dev1.add(pk2, 1337).unwrap();
|
|
||||||
dev2.add(pk1, 2600).unwrap();
|
|
||||||
|
|
||||||
dev1.set_psk(pk2, Some(psk)).unwrap();
|
|
||||||
dev2.set_psk(pk1, Some(psk)).unwrap();
|
|
||||||
|
|
||||||
// do a few handshakes
|
|
||||||
|
|
||||||
for i in 0..10 {
|
|
||||||
println!("handshake : {}", i);
|
|
||||||
|
|
||||||
// create initiation
|
|
||||||
|
|
||||||
let msg1 = dev1.begin(&pk2).unwrap();
|
|
||||||
|
|
||||||
println!("msg1 = {}", hex::encode(&msg1[..]));
|
|
||||||
println!("msg1 = {:?}", Initiation::parse(&msg1[..]).unwrap());
|
|
||||||
|
|
||||||
// process initiation and create response
|
|
||||||
|
|
||||||
let (_, msg2, ks_r) = dev2.process(&msg1).unwrap();
|
|
||||||
|
|
||||||
let ks_r = ks_r.unwrap();
|
|
||||||
let msg2 = msg2.unwrap();
|
|
||||||
|
|
||||||
println!("msg2 = {}", hex::encode(&msg2[..]));
|
|
||||||
println!("msg2 = {:?}", Response::parse(&msg2[..]).unwrap());
|
|
||||||
|
|
||||||
assert!(!ks_r.confirmed, "Responders key-pair is confirmed");
|
|
||||||
|
|
||||||
// process response and obtain confirmed key-pair
|
|
||||||
|
|
||||||
let (_, msg3, ks_i) = dev1.process(&msg2).unwrap();
|
|
||||||
let ks_i = ks_i.unwrap();
|
|
||||||
|
|
||||||
assert!(msg3.is_none(), "Returned message after response");
|
|
||||||
assert!(ks_i.confirmed, "Initiators key-pair is not confirmed");
|
|
||||||
|
|
||||||
assert_eq!(ks_i.send, ks_r.recv, "KeyI.send != KeyR.recv");
|
|
||||||
assert_eq!(ks_i.recv, ks_r.send, "KeyI.recv != KeyR.send");
|
|
||||||
|
|
||||||
dev1.release(ks_i.send.id);
|
|
||||||
dev2.release(ks_r.send.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(dev1.get_psk(pk2).unwrap(), psk);
|
|
||||||
assert_eq!(dev2.get_psk(pk1).unwrap(), psk);
|
|
||||||
|
|
||||||
dev1.remove(pk2).unwrap();
|
|
||||||
dev2.remove(pk1).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
use hex;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use byteorder::LittleEndian;
|
|
||||||
use zerocopy::byteorder::U32;
|
|
||||||
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified};
|
|
||||||
|
|
||||||
use super::types::*;
|
|
||||||
|
|
||||||
const SIZE_TAG: usize = 16;
|
|
||||||
const SIZE_X25519_POINT: usize = 32;
|
|
||||||
const SIZE_TIMESTAMP: usize = 12;
|
|
||||||
|
|
||||||
pub const TYPE_INITIATION: u8 = 1;
|
|
||||||
pub const TYPE_RESPONSE: u8 = 2;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
|
||||||
pub struct Initiation {
|
|
||||||
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: [u8; SIZE_TIMESTAMP],
|
|
||||||
pub f_timestamp_tag: [u8; SIZE_TAG],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Initiation {
|
|
||||||
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: [0u8; SIZE_TIMESTAMP],
|
|
||||||
f_timestamp_tag: [0u8; SIZE_TAG],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Initiation {
|
|
||||||
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
|
||||||
let msg: LayoutVerified<B, Self> =
|
|
||||||
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
|
||||||
|
|
||||||
if msg.f_type.get() != (TYPE_INITIATION as u32) {
|
|
||||||
return Err(HandshakeError::InvalidMessageFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl fmt::Debug for Initiation {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f,
|
|
||||||
"MessageInitiation {{ 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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl PartialEq for Initiation {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.f_type.get() == other.f_type.get()
|
|
||||||
&& self.f_sender.get() == other.f_sender.get()
|
|
||||||
&& self.f_ephemeral[..] == other.f_ephemeral[..]
|
|
||||||
&& self.f_static[..] == other.f_static[..]
|
|
||||||
&& self.f_static_tag[..] == other.f_static_tag[..]
|
|
||||||
&& self.f_timestamp[..] == other.f_timestamp
|
|
||||||
&& self.f_timestamp_tag[..] == other.f_timestamp_tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Eq for Initiation {}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, FromBytes, AsBytes)]
|
|
||||||
pub struct Response {
|
|
||||||
f_type: U32<LittleEndian>,
|
|
||||||
pub f_sender: U32<LittleEndian>,
|
|
||||||
pub f_receiver: U32<LittleEndian>,
|
|
||||||
pub f_ephemeral: [u8; SIZE_X25519_POINT],
|
|
||||||
pub f_empty_tag: [u8; SIZE_TAG],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
|
||||||
pub fn parse<B: ByteSlice>(bytes: B) -> Result<LayoutVerified<B, Self>, HandshakeError> {
|
|
||||||
let msg: LayoutVerified<B, Self> =
|
|
||||||
LayoutVerified::new(bytes).ok_or(HandshakeError::InvalidMessageFormat)?;
|
|
||||||
|
|
||||||
if msg.f_type.get() != (TYPE_RESPONSE as u32) {
|
|
||||||
return Err(HandshakeError::InvalidMessageFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Response {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
f_type: <U32<LittleEndian>>::new(TYPE_RESPONSE as u32),
|
|
||||||
f_sender: <U32<LittleEndian>>::ZERO,
|
|
||||||
f_receiver: <U32<LittleEndian>>::ZERO,
|
|
||||||
f_ephemeral: [0u8; SIZE_X25519_POINT],
|
|
||||||
f_empty_tag: [0u8; SIZE_TAG],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl fmt::Debug for Response {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f,
|
|
||||||
"MessageResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}",
|
|
||||||
self.f_type,
|
|
||||||
self.f_sender,
|
|
||||||
self.f_receiver,
|
|
||||||
hex::encode(self.f_ephemeral),
|
|
||||||
hex::encode(self.f_empty_tag)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl PartialEq for Response {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.f_type == other.f_type
|
|
||||||
&& self.f_sender == other.f_sender
|
|
||||||
&& self.f_receiver == other.f_receiver
|
|
||||||
&& self.f_ephemeral == other.f_ephemeral
|
|
||||||
&& self.f_empty_tag == other.f_empty_tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn message_response_identity() {
|
|
||||||
let mut msg: Response = Default::default();
|
|
||||||
|
|
||||||
msg.f_sender.set(146252);
|
|
||||||
msg.f_receiver.set(554442);
|
|
||||||
msg.f_ephemeral = [
|
|
||||||
0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c,
|
|
||||||
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
|
|
||||||
0x13, 0x56, 0x52, 0x1f,
|
|
||||||
];
|
|
||||||
msg.f_empty_tag = [
|
|
||||||
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
|
|
||||||
0x2f, 0xde,
|
|
||||||
];
|
|
||||||
|
|
||||||
let buf: Vec<u8> = msg.as_bytes().to_vec();
|
|
||||||
let msg_p = Response::parse(&buf[..]).unwrap();
|
|
||||||
assert_eq!(msg, *msg_p.into_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn message_initiate_identity() {
|
|
||||||
let mut msg: Initiation = Default::default();
|
|
||||||
|
|
||||||
msg.f_sender.set(575757);
|
|
||||||
msg.f_ephemeral = [
|
|
||||||
0xc1, 0x66, 0x0a, 0x0c, 0xdc, 0x0f, 0x6c, 0x51, 0x0f, 0xc2, 0xcc, 0x51, 0x52, 0x0c,
|
|
||||||
0xde, 0x1e, 0xf7, 0xf1, 0xca, 0x90, 0x86, 0x72, 0xad, 0x67, 0xea, 0x89, 0x45, 0x44,
|
|
||||||
0x13, 0x56, 0x52, 0x1f,
|
|
||||||
];
|
|
||||||
msg.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.f_static_tag = [
|
|
||||||
0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83,
|
|
||||||
0x6b, 0x42,
|
|
||||||
];
|
|
||||||
msg.f_timestamp = [
|
|
||||||
0x4f, 0x1c, 0x60, 0xec, 0x0e, 0xf6, 0x36, 0xf0, 0x78, 0x28, 0x57, 0x42,
|
|
||||||
];
|
|
||||||
msg.f_timestamp_tag = [
|
|
||||||
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
|
|
||||||
0x2f, 0xde,
|
|
||||||
];
|
|
||||||
|
|
||||||
let buf: Vec<u8> = msg.as_bytes().to_vec();
|
|
||||||
let msg_p = Initiation::parse(&buf[..]).unwrap();
|
|
||||||
assert_eq!(msg, *msg_p.into_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/* Implementation of the:
|
|
||||||
*
|
|
||||||
* Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s
|
|
||||||
*
|
|
||||||
* Protocol pattern, see: http://www.noiseprotocol.org/noise.html.
|
|
||||||
* For documentation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
mod device;
|
|
||||||
mod messages;
|
|
||||||
mod noise;
|
|
||||||
mod peer;
|
|
||||||
mod timestamp;
|
|
||||||
mod types;
|
|
||||||
|
|
||||||
// publicly exposed interface
|
|
||||||
|
|
||||||
pub use device::Device;
|
|
||||||
@@ -1,458 +0,0 @@
|
|||||||
// DH
|
|
||||||
use x25519_dalek::PublicKey;
|
|
||||||
use x25519_dalek::StaticSecret;
|
|
||||||
|
|
||||||
// HASH & MAC
|
|
||||||
use blake2::Blake2s;
|
|
||||||
use hmac::Hmac;
|
|
||||||
|
|
||||||
// AEAD
|
|
||||||
use crypto::aead::{AeadDecryptor, AeadEncryptor};
|
|
||||||
use crypto::chacha20poly1305::ChaCha20Poly1305;
|
|
||||||
|
|
||||||
use rand::rngs::OsRng;
|
|
||||||
|
|
||||||
use generic_array::typenum::*;
|
|
||||||
use generic_array::GenericArray;
|
|
||||||
|
|
||||||
use zerocopy::AsBytes;
|
|
||||||
|
|
||||||
use super::device::Device;
|
|
||||||
use super::messages::{Initiation, Response};
|
|
||||||
use super::peer::{Peer, State};
|
|
||||||
use super::timestamp;
|
|
||||||
use super::types::*;
|
|
||||||
|
|
||||||
use crate::types::{Key, KeyPair};
|
|
||||||
|
|
||||||
// HMAC hasher (generic construction)
|
|
||||||
|
|
||||||
type HMACBlake2s = Hmac<Blake2s>;
|
|
||||||
|
|
||||||
// convenient alias to pass state temporarily into device.rs and back
|
|
||||||
|
|
||||||
type TemporaryState = (u32, PublicKey, GenericArray<u8, U32>, GenericArray<u8, U32>);
|
|
||||||
|
|
||||||
const SIZE_CK: usize = 32;
|
|
||||||
const SIZE_HS: usize = 32;
|
|
||||||
const SIZE_NONCE: usize = 8;
|
|
||||||
|
|
||||||
// C := Hash(Construction)
|
|
||||||
const INITIAL_CK: [u8; SIZE_CK] = [
|
|
||||||
0x60, 0xe2, 0x6d, 0xae, 0xf3, 0x27, 0xef, 0xc0, 0x2e, 0xc3, 0x35, 0xe2, 0xa0, 0x25, 0xd2, 0xd0,
|
|
||||||
0x16, 0xeb, 0x42, 0x06, 0xf8, 0x72, 0x77, 0xf5, 0x2d, 0x38, 0xd1, 0x98, 0x8b, 0x78, 0xcd, 0x36,
|
|
||||||
];
|
|
||||||
|
|
||||||
// H := Hash(C || Identifier)
|
|
||||||
const INITIAL_HS: [u8; SIZE_HS] = [
|
|
||||||
0x22, 0x11, 0xb3, 0x61, 0x08, 0x1a, 0xc5, 0x66, 0x69, 0x12, 0x43, 0xdb, 0x45, 0x8a, 0xd5, 0x32,
|
|
||||||
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];
|
|
||||||
|
|
||||||
macro_rules! HASH {
|
|
||||||
( $($input:expr),* ) => {{
|
|
||||||
use blake2::Digest;
|
|
||||||
let mut hsh = Blake2s::new();
|
|
||||||
$(
|
|
||||||
hsh.input($input);
|
|
||||||
)*
|
|
||||||
hsh.result()
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! HMAC {
|
|
||||||
($key:expr, $($input:expr),*) => {{
|
|
||||||
use hmac::Mac;
|
|
||||||
let mut mac = HMACBlake2s::new_varkey($key).unwrap();
|
|
||||||
$(
|
|
||||||
mac.input($input);
|
|
||||||
)*
|
|
||||||
mac.result().code()
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! KDF1 {
|
|
||||||
($ck:expr, $input:expr) => {{
|
|
||||||
let t0 = HMAC!($ck, $input);
|
|
||||||
let t1 = HMAC!(&t0, &[0x1]);
|
|
||||||
t1
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! KDF2 {
|
|
||||||
($ck:expr, $input:expr) => {{
|
|
||||||
let t0 = HMAC!($ck, $input);
|
|
||||||
let t1 = HMAC!(&t0, &[0x1]);
|
|
||||||
let t2 = HMAC!(&t0, &t1, &[0x2]);
|
|
||||||
(t1, t2)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! KDF3 {
|
|
||||||
($ck:expr, $input:expr) => {{
|
|
||||||
let t0 = HMAC!($ck, $input);
|
|
||||||
let t1 = HMAC!(&t0, &[0x1]);
|
|
||||||
let t2 = HMAC!(&t0, &t1, &[0x2]);
|
|
||||||
let t3 = HMAC!(&t0, &t2, &[0x3]);
|
|
||||||
(t1, t2, t3)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const IDENTIFIER: &[u8] = b"WireGuard v1 zx2c4 Jason@zx2c4.com";
|
|
||||||
const CONSTRUCTION: &[u8] = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn precomputed_chain_key() {
|
|
||||||
assert_eq!(INITIAL_CK[..], HASH!(CONSTRUCTION)[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn precomputed_hash() {
|
|
||||||
assert_eq!(INITIAL_HS[..], HASH!(INITIAL_CK, IDENTIFIER)[..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_initiation<T: Copy>(
|
|
||||||
device: &Device<T>,
|
|
||||||
peer: &Peer<T>,
|
|
||||||
sender: u32,
|
|
||||||
) -> Result<Vec<u8>, HandshakeError> {
|
|
||||||
let mut rng = OsRng::new().unwrap();
|
|
||||||
let mut msg: Initiation = Default::default();
|
|
||||||
|
|
||||||
// initialize state
|
|
||||||
|
|
||||||
let ck = INITIAL_CK;
|
|
||||||
let hs = INITIAL_HS;
|
|
||||||
let hs = HASH!(&hs, peer.pk.as_bytes());
|
|
||||||
|
|
||||||
msg.f_sender.set(sender);
|
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
|
||||||
|
|
||||||
let eph_sk = StaticSecret::new(&mut rng);
|
|
||||||
let eph_pk = PublicKey::from(&eph_sk);
|
|
||||||
|
|
||||||
// C := Kdf(C, E_pub)
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_pk.as_bytes());
|
|
||||||
|
|
||||||
// msg.ephemeral := E_pub
|
|
||||||
|
|
||||||
msg.f_ephemeral = *eph_pk.as_bytes();
|
|
||||||
|
|
||||||
// H := HASH(H, msg.ephemeral)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, msg.f_ephemeral);
|
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
|
|
||||||
|
|
||||||
let (ck, key) = KDF2!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
|
|
||||||
|
|
||||||
// msg.static := Aead(k, 0, S_pub, H)
|
|
||||||
|
|
||||||
SEAL!(
|
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
device.pk.as_bytes(), // pt
|
|
||||||
&mut msg.f_static, // ct
|
|
||||||
&mut msg.f_static_tag // tag
|
|
||||||
);
|
|
||||||
|
|
||||||
// H := Hash(H || msg.static)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_static, &msg.f_static_tag);
|
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
|
|
||||||
|
|
||||||
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
|
|
||||||
|
|
||||||
// msg.timestamp := Aead(k, 0, Timestamp(), H)
|
|
||||||
|
|
||||||
SEAL!(
|
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
×tamp::now(), // pt
|
|
||||||
&mut msg.f_timestamp, // ct
|
|
||||||
&mut msg.f_timestamp_tag // tag
|
|
||||||
);
|
|
||||||
|
|
||||||
// H := Hash(H || msg.timestamp)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_timestamp, &msg.f_timestamp_tag);
|
|
||||||
|
|
||||||
// update state of peer
|
|
||||||
|
|
||||||
peer.set_state(State::InitiationSent {
|
|
||||||
hs,
|
|
||||||
ck,
|
|
||||||
eph_sk,
|
|
||||||
sender,
|
|
||||||
});
|
|
||||||
|
|
||||||
// return message as vector
|
|
||||||
|
|
||||||
Ok(msg.as_bytes().to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn consume_initiation<'a, T: Copy>(
|
|
||||||
device: &'a Device<T>,
|
|
||||||
msg: &[u8],
|
|
||||||
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
|
|
||||||
// parse message
|
|
||||||
|
|
||||||
let msg = Initiation::parse(msg)?;
|
|
||||||
|
|
||||||
// initialize state
|
|
||||||
|
|
||||||
let ck = INITIAL_CK;
|
|
||||||
let hs = INITIAL_HS;
|
|
||||||
let hs = HASH!(&hs, device.pk.as_bytes());
|
|
||||||
|
|
||||||
// C := Kdf(C, E_pub)
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, &msg.f_ephemeral);
|
|
||||||
|
|
||||||
// H := HASH(H, msg.ephemeral)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_ephemeral);
|
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
|
|
||||||
|
|
||||||
let eph_r_pk = PublicKey::from(msg.f_ephemeral);
|
|
||||||
let (ck, key) = KDF2!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
|
|
||||||
|
|
||||||
// msg.static := Aead(k, 0, S_pub, H)
|
|
||||||
|
|
||||||
let mut pk = [0u8; 32];
|
|
||||||
|
|
||||||
OPEN!(
|
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
&mut pk, // pt
|
|
||||||
&msg.f_static, // ct
|
|
||||||
&msg.f_static_tag // 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);
|
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
|
|
||||||
|
|
||||||
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
|
|
||||||
|
|
||||||
// msg.timestamp := Aead(k, 0, Timestamp(), H)
|
|
||||||
|
|
||||||
let mut ts = timestamp::zero();
|
|
||||||
|
|
||||||
OPEN!(
|
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
&mut ts, // pt
|
|
||||||
&msg.f_timestamp, // ct
|
|
||||||
&msg.f_timestamp_tag // tag
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// check and update timestamp
|
|
||||||
|
|
||||||
peer.check_timestamp(device, &ts)?;
|
|
||||||
|
|
||||||
// H := Hash(H || msg.timestamp)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_timestamp, &msg.f_timestamp_tag);
|
|
||||||
|
|
||||||
// return state (to create response)
|
|
||||||
|
|
||||||
Ok((peer, (msg.f_sender.get(), eph_r_pk, hs, ck)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_response<T: Copy>(
|
|
||||||
peer: &Peer<T>,
|
|
||||||
sender: u32, // sending identifier
|
|
||||||
state: TemporaryState, // state from "consume_initiation"
|
|
||||||
) -> Result<Output<T>, HandshakeError> {
|
|
||||||
let mut rng = OsRng::new().unwrap();
|
|
||||||
let mut msg: Response = Default::default();
|
|
||||||
|
|
||||||
let (receiver, eph_r_pk, hs, ck) = state;
|
|
||||||
|
|
||||||
msg.f_sender.set(sender);
|
|
||||||
msg.f_receiver.set(receiver);
|
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
|
||||||
|
|
||||||
let eph_sk = StaticSecret::new(&mut rng);
|
|
||||||
let eph_pk = PublicKey::from(&eph_sk);
|
|
||||||
|
|
||||||
// C := Kdf1(C, E_pub)
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_pk.as_bytes());
|
|
||||||
|
|
||||||
// msg.ephemeral := E_pub
|
|
||||||
|
|
||||||
msg.f_ephemeral = *eph_pk.as_bytes();
|
|
||||||
|
|
||||||
// H := Hash(H || msg.ephemeral)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_ephemeral);
|
|
||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, E_pub))
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
|
|
||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, S_pub))
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
|
|
||||||
|
|
||||||
// (C, tau, k) := Kdf3(C, Q)
|
|
||||||
|
|
||||||
let (ck, tau, key) = KDF3!(&ck, &peer.psk);
|
|
||||||
|
|
||||||
// H := Hash(H || tau)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, tau);
|
|
||||||
|
|
||||||
// msg.empty := Aead(k, 0, [], H)
|
|
||||||
|
|
||||||
SEAL!(
|
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
&[], // pt
|
|
||||||
&mut [], // ct
|
|
||||||
&mut msg.f_empty_tag // tag
|
|
||||||
);
|
|
||||||
|
|
||||||
/* not strictly needed
|
|
||||||
* // H := Hash(H || msg.empty)
|
|
||||||
* let hs = HASH!(&hs, &msg.f_empty_tag);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// derive key-pair
|
|
||||||
// (verbose code, due to GenericArray -> [u8; 32] conversion)
|
|
||||||
|
|
||||||
let (key_recv, key_send) = KDF2!(&ck, &[]);
|
|
||||||
|
|
||||||
// return response and unconfirmed key-pair
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
peer.identifier,
|
|
||||||
Some(msg.as_bytes().to_vec()),
|
|
||||||
Some(KeyPair {
|
|
||||||
confirmed: false,
|
|
||||||
send: Key {
|
|
||||||
id: sender,
|
|
||||||
key: key_send.into(),
|
|
||||||
},
|
|
||||||
recv: Key {
|
|
||||||
id: receiver,
|
|
||||||
key: key_recv.into(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn consume_response<T: Copy>(
|
|
||||||
device: &Device<T>,
|
|
||||||
msg: &[u8],
|
|
||||||
) -> Result<Output<T>, HandshakeError> {
|
|
||||||
// parse message
|
|
||||||
|
|
||||||
let msg = Response::parse(msg)?;
|
|
||||||
|
|
||||||
// retrieve peer and associated state
|
|
||||||
|
|
||||||
let peer = device.lookup_id(msg.f_receiver.get())?;
|
|
||||||
let (hs, ck, sender, eph_sk) = match peer.get_state() {
|
|
||||||
State::Reset => Err(HandshakeError::InvalidState),
|
|
||||||
State::InitiationSent {
|
|
||||||
hs,
|
|
||||||
ck,
|
|
||||||
sender,
|
|
||||||
eph_sk,
|
|
||||||
} => Ok((hs, ck, sender, eph_sk)),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
// C := Kdf1(C, E_pub)
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, &msg.f_ephemeral);
|
|
||||||
|
|
||||||
// H := Hash(H || msg.ephemeral)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_ephemeral);
|
|
||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, E_pub))
|
|
||||||
|
|
||||||
let eph_r_pk = PublicKey::from(msg.f_ephemeral);
|
|
||||||
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
|
|
||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, S_pub))
|
|
||||||
|
|
||||||
let ck = KDF1!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
|
|
||||||
|
|
||||||
// (C, tau, k) := Kdf3(C, Q)
|
|
||||||
|
|
||||||
let (ck, tau, key) = KDF3!(&ck, &peer.psk);
|
|
||||||
|
|
||||||
// H := Hash(H || tau)
|
|
||||||
|
|
||||||
let hs = HASH!(&hs, tau);
|
|
||||||
|
|
||||||
// msg.empty := Aead(k, 0, [], H)
|
|
||||||
|
|
||||||
OPEN!(
|
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
&mut [], // pt
|
|
||||||
&[], // ct
|
|
||||||
&msg.f_empty_tag // tag
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// derive key-pair
|
|
||||||
|
|
||||||
let (key_send, key_recv) = KDF2!(&ck, &[]);
|
|
||||||
|
|
||||||
// return confirmed key-pair
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
peer.identifier,
|
|
||||||
None,
|
|
||||||
Some(KeyPair {
|
|
||||||
confirmed: true,
|
|
||||||
send: Key {
|
|
||||||
id: sender,
|
|
||||||
key: key_send.into(),
|
|
||||||
},
|
|
||||||
recv: Key {
|
|
||||||
id: msg.f_sender.get(),
|
|
||||||
key: key_recv.into(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
use generic_array::typenum::U32;
|
|
||||||
use generic_array::GenericArray;
|
|
||||||
|
|
||||||
use x25519_dalek::PublicKey;
|
|
||||||
use x25519_dalek::SharedSecret;
|
|
||||||
use x25519_dalek::StaticSecret;
|
|
||||||
|
|
||||||
use super::device::Device;
|
|
||||||
use super::timestamp;
|
|
||||||
use super::types::*;
|
|
||||||
|
|
||||||
/* Represents the recomputation and state of a peer.
|
|
||||||
*
|
|
||||||
* This type is only for internal use and not exposed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub struct Peer<T> {
|
|
||||||
// external identifier
|
|
||||||
pub(crate) identifier: T,
|
|
||||||
|
|
||||||
// mutable state
|
|
||||||
state: Mutex<State>,
|
|
||||||
timestamp: Mutex<Option<timestamp::TAI64N>>,
|
|
||||||
|
|
||||||
// constant state
|
|
||||||
pub(crate) pk: PublicKey, // public key of peer
|
|
||||||
pub(crate) ss: SharedSecret, // precomputed DH(static, static)
|
|
||||||
pub(crate) psk: Psk, // psk of peer
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum State {
|
|
||||||
Reset,
|
|
||||||
InitiationSent {
|
|
||||||
sender: u32, // assigned sender id
|
|
||||||
eph_sk: StaticSecret,
|
|
||||||
hs: GenericArray<u8, U32>,
|
|
||||||
ck: GenericArray<u8, U32>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for State {
|
|
||||||
fn clone(&self) -> State {
|
|
||||||
match self {
|
|
||||||
State::Reset => State::Reset,
|
|
||||||
State::InitiationSent {
|
|
||||||
sender,
|
|
||||||
eph_sk,
|
|
||||||
hs,
|
|
||||||
ck,
|
|
||||||
} => State::InitiationSent {
|
|
||||||
sender: *sender,
|
|
||||||
eph_sk: StaticSecret::from(eph_sk.to_bytes()),
|
|
||||||
hs: *hs,
|
|
||||||
ck: *ck,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Peer<T>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
pub fn new(
|
|
||||||
identifier: T, // external identifier
|
|
||||||
pk: PublicKey, // public key of peer
|
|
||||||
ss: SharedSecret, // precomputed DH(static, static)
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
identifier: identifier,
|
|
||||||
state: Mutex::new(State::Reset),
|
|
||||||
timestamp: Mutex::new(None),
|
|
||||||
pk: pk,
|
|
||||||
ss: ss,
|
|
||||||
psk: [0u8; 32],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the state of the peer
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
pub fn get_state(&self) -> State {
|
|
||||||
self.state.lock().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the state of the peer unconditionally
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
pub fn set_state(&self, state_new: State) {
|
|
||||||
*self.state.lock() = state_new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the mutable state of the peer conditioned on the timestamp being newer
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * st_new - The updated state of the peer
|
|
||||||
/// * ts_new - The associated timestamp
|
|
||||||
pub fn check_timestamp(
|
|
||||||
&self,
|
|
||||||
device: &Device<T>,
|
|
||||||
timestamp_new: ×tamp::TAI64N,
|
|
||||||
) -> Result<(), HandshakeError> {
|
|
||||||
let mut state = self.state.lock();
|
|
||||||
let mut timestamp = self.timestamp.lock();
|
|
||||||
|
|
||||||
let update = match *timestamp {
|
|
||||||
None => true,
|
|
||||||
Some(timestamp_old) => {
|
|
||||||
if timestamp::compare(×tamp_old, ×tamp_new) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if update {
|
|
||||||
// release existing identifier
|
|
||||||
match *state {
|
|
||||||
State::InitiationSent { sender, .. } => device.release(sender),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset state and update timestamp
|
|
||||||
*state = State::Reset;
|
|
||||||
*timestamp = Some(*timestamp_new);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(HandshakeError::OldTimestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
const TAI64_EPOCH: u64 = 0x4000000000000000;
|
|
||||||
|
|
||||||
pub type TAI64N = [u8; 12];
|
|
||||||
|
|
||||||
pub fn zero() -> TAI64N {
|
|
||||||
[0u8; 12]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn now() -> TAI64N {
|
|
||||||
// get system time as duration
|
|
||||||
let sysnow = SystemTime::now();
|
|
||||||
let delta = sysnow.duration_since(UNIX_EPOCH).unwrap();
|
|
||||||
|
|
||||||
// convert to tai64n
|
|
||||||
let tai64_secs = delta.as_secs() + TAI64_EPOCH;
|
|
||||||
let tai64_nano = delta.subsec_nanos();
|
|
||||||
|
|
||||||
// serialize
|
|
||||||
let mut res = [0u8; 12];
|
|
||||||
res[..8].copy_from_slice(&tai64_secs.to_be_bytes()[..]);
|
|
||||||
res[8..].copy_from_slice(&tai64_nano.to_be_bytes()[..]);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compare(old: &TAI64N, new: &TAI64N) -> bool {
|
|
||||||
for i in 0..12 {
|
|
||||||
if new[i] > old[i] {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::types::KeyPair;
|
|
||||||
|
|
||||||
/* Internal types for the noise IKpsk2 implementation */
|
|
||||||
|
|
||||||
// config error
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ConfigError(String);
|
|
||||||
|
|
||||||
impl ConfigError {
|
|
||||||
pub fn new(s: &str) -> Self {
|
|
||||||
ConfigError(s.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ConfigError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "ConfigError({})", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ConfigError {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handshake error
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum HandshakeError {
|
|
||||||
DecryptionFailure,
|
|
||||||
UnknownPublicKey,
|
|
||||||
UnknownReceiverId,
|
|
||||||
InvalidMessageFormat,
|
|
||||||
OldTimestamp,
|
|
||||||
InvalidState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for HandshakeError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
HandshakeError::DecryptionFailure => write!(f, "Failed to AEAD:OPEN"),
|
|
||||||
HandshakeError::UnknownPublicKey => write!(f, "Unknown public key"),
|
|
||||||
HandshakeError::UnknownReceiverId => {
|
|
||||||
write!(f, "Receiver id not allocated to any handshake")
|
|
||||||
}
|
|
||||||
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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for HandshakeError {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
"Generic Handshake Error"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Output<T> = (
|
|
||||||
T, // external identifier associated with peer
|
|
||||||
// (e.g. a reference or vector index)
|
|
||||||
Option<Vec<u8>>, // message to send
|
|
||||||
Option<KeyPair>, // resulting key-pair of successful handshake
|
|
||||||
);
|
|
||||||
|
|
||||||
// preshared key
|
|
||||||
|
|
||||||
pub type Psk = [u8; 32];
|
|
||||||
Reference in New Issue
Block a user