aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMathias Hall-Andersen <mathias@hall-andersen.dk>2019-07-30 15:28:11 +0200
committerMathias Hall-Andersen <mathias@hall-andersen.dk>2019-07-30 15:28:11 +0200
commit1cfd5aea1aba4b01905414351df13e8f5d4dfb1c (patch)
tree1ddc2d7a08a486676e9cf045f31a1fe0d5714bc5 /src
parentBegin work on MAC field processing (diff)
downloadwireguard-rs-1cfd5aea1aba4b01905414351df13e8f5d4dfb1c.tar.xz
wireguard-rs-1cfd5aea1aba4b01905414351df13e8f5d4dfb1c.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/handshake/device.rs35
-rw-r--r--src/handshake/macs.rs18
-rw-r--r--src/handshake/messages.rs290
-rw-r--r--src/handshake/noise.rs23
-rw-r--r--src/main.rs4
-rw-r--r--src/noise/device.rs315
-rw-r--r--src/noise/messages.rs208
-rw-r--r--src/noise/mod.rs18
-rw-r--r--src/noise/noise.rs458
-rw-r--r--src/noise/peer.rs136
-rw-r--r--src/noise/timestamp.rs34
-rw-r--r--src/noise/types.rs81
12 files changed, 244 insertions, 1376 deletions
diff --git a/src/handshake/device.rs b/src/handshake/device.rs
index 04e00f9..b28613a 100644
--- a/src/handshake/device.rs
+++ b/src/handshake/device.rs
@@ -7,7 +7,8 @@ use rand::rngs::OsRng;
use x25519_dalek::PublicKey;
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::peer::Peer;
use super::types::*;
@@ -170,20 +171,40 @@ where
/// * `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) => {
+ Some(&TYPE_INITIATION) => {
+ let msg = Initiation::parse(msg)?;
+
+ // check mac footer and ratelimiter
+
// 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
let sender = self.allocate(peer);
- // create response (release id on error)
- noise::create_response(peer, sender, st).map_err(|e| {
+ // create response (release id on error), TODO: take slice
+ let mut resp = Response::default();
+ noise::create_response(peer, sender, st, &mut resp.noise).map_err(|e| {
self.release(sender);
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),
}
}
@@ -235,9 +256,9 @@ where
#[cfg(test)]
mod tests {
+ use super::super::messages::*;
use super::*;
use hex;
- use messages::*;
#[test]
fn handshake() {
diff --git a/src/handshake/macs.rs b/src/handshake/macs.rs
index c8b97e3..05546a7 100644
--- a/src/handshake/macs.rs
+++ b/src/handshake/macs.rs
@@ -4,7 +4,7 @@ use blake2::Blake2s;
use subtle::ConstantTimeEq;
use x25519_dalek::PublicKey;
-use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified};
+use super::messages::{CookieReply, MacsFooter};
const LABEL_MAC1: &[u8] = b"mac1----";
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 {
mac1_key: [u8; 32],
cookie_value: [u8; 16],
diff --git a/src/handshake/messages.rs b/src/handshake/messages.rs
index 8fa08b3..a2ff19a 100644
--- a/src/handshake/messages.rs
+++ b/src/handshake/messages.rs
@@ -8,10 +8,10 @@ use byteorder::LittleEndian;
use zerocopy::byteorder::U32;
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified};
-use super::macs::MacsFooter;
use super::timestamp;
use super::types::*;
+const SIZE_MAC: usize = 16;
const SIZE_TAG: usize = 16; // poly1305 tag
const SIZE_NONCE: usize = 16; // xchacha20 nonce
const SIZE_COOKIE: usize = 16; //
@@ -21,28 +21,20 @@ pub const TYPE_INITIATION: u8 = 1;
pub const TYPE_RESPONSE: u8 = 2;
pub const TYPE_COOKIEREPLY: u8 = 3;
+/* Handshake messsages */
+
#[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: timestamp::TAI64N,
- pub f_timestamp_tag: [u8; SIZE_TAG],
- pub f_macs: MacsFooter,
+pub struct Response {
+ pub noise: NoiseResponse, // inner message covered by macs
+ pub macs: MacsFooter,
}
#[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],
- pub f_macs: MacsFooter,
+pub struct Initiation {
+ pub noise: NoiseInitiation, // inner message covered by macs
+ pub macs: MacsFooter,
}
#[repr(C)]
@@ -55,28 +47,45 @@ pub struct CookieReply {
pub f_cookie_tag: [u8; SIZE_TAG],
}
-impl Default for Initiation {
- fn default() -> Self {
- Self {
- f_type: <U32<LittleEndian>>::new(TYPE_INITIATION as u32),
+/* Inner sub-messages */
- 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_macs: Default::default(),
- }
- }
+#[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.f_type.get() != (TYPE_INITIATION as u32) {
+ if msg.noise.f_type.get() != (TYPE_INITIATION as u32) {
return Err(HandshakeError::InvalidMessageFormat);
}
@@ -84,44 +93,25 @@ impl Initiation {
}
}
-#[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)
- )
- }
-}
+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)?;
-#[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
+ if msg.noise.f_type.get() != (TYPE_RESPONSE as u32) {
+ return Err(HandshakeError::InvalidMessageFormat);
+ }
+
+ Ok(msg)
}
}
-#[cfg(test)]
-impl Eq for Initiation {}
-
-impl Response {
+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_RESPONSE as u32) {
+ if msg.f_type.get() != (TYPE_COOKIEREPLY as u32) {
return Err(HandshakeError::InvalidMessageFormat);
}
@@ -129,24 +119,125 @@ impl Response {
}
}
+/* Default values */
+
impl Default for Response {
fn default() -> Self {
Self {
+ noise: Default::default(),
+ macs: Default::default(),
+ }
+ }
+}
+
+impl Default for Initiation {
+ 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 {
+ 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]
+ }
+ }
+}
+
+impl Default for NoiseResponse {
+ 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(),
}
}
}
+/* Debug formatting (for testing purposes) */
+
+#[cfg(test)]
+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 {
write!(f,
- "MessageResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}",
+ "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_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 fmt::Debug for NoiseResponse {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f,
+ "NoiseResponse {{ type = {}, sender = {}, receiver = {}, ephemeral = {}, empty = |{} }}",
self.f_type,
self.f_sender,
self.f_receiver,
@@ -157,17 +248,48 @@ impl fmt::Debug for Response {
}
#[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
+impl fmt::Debug for MacsFooter {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f,
+ "Macs {{ mac1 = {}, mac2 = {} }}",
+ hex::encode(self.f_mac1),
+ hex::encode(self.f_mac2)
+ )
+ }
+}
+
+/* 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)]
mod tests {
use super::*;
@@ -175,22 +297,22 @@ mod tests {
fn message_response_identity() {
let mut msg: Response = Default::default();
- msg.f_sender.set(146252);
- msg.f_receiver.set(554442);
- msg.f_ephemeral = [
+ msg.noise.f_sender.set(146252);
+ msg.noise.f_receiver.set(554442);
+ msg.noise.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 = [
+ msg.noise.f_empty_tag = [
0x60, 0x0e, 0x1e, 0x95, 0x41, 0x6b, 0x52, 0x05, 0xa2, 0x09, 0xe1, 0xbf, 0x40, 0x05,
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,
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,
0xdf, 0xbb,
];
@@ -204,33 +326,33 @@ mod tests {
fn message_initiate_identity() {
let mut msg: Initiation = Default::default();
- msg.f_sender.set(575757);
- msg.f_ephemeral = [
+ msg.noise.f_sender.set(575757);
+ msg.noise.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 = [
+ 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.f_static_tag = [
+ msg.noise.f_static_tag = [
0x45, 0xac, 0x8d, 0x43, 0xea, 0x1b, 0x2f, 0x02, 0x45, 0x5d, 0x86, 0x37, 0xee, 0x83,
0x6b, 0x42,
];
- msg.f_timestamp = [
+ msg.noise.f_timestamp = [
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,
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,
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,
0xdf, 0xbb,
];
diff --git a/src/handshake/noise.rs b/src/handshake/noise.rs
index a03cae2..72e0f47 100644
--- a/src/handshake/noise.rs
+++ b/src/handshake/noise.rs
@@ -18,7 +18,7 @@ use generic_array::GenericArray;
use zerocopy::AsBytes;
use super::device::Device;
-use super::messages::{Initiation, Response};
+use super::messages::{NoiseInitiation, NoiseResponse};
use super::peer::{Peer, State};
use super::timestamp;
use super::types::*;
@@ -142,7 +142,7 @@ pub fn create_initiation<T: Copy>(
sender: u32,
) -> Result<Vec<u8>, HandshakeError> {
let mut rng = OsRng::new().unwrap();
- let mut msg: Initiation = Default::default();
+ let mut msg: NoiseInitiation = Default::default();
// initialize state
@@ -221,12 +221,8 @@ pub fn create_initiation<T: Copy>(
pub fn consume_initiation<'a, T: Copy>(
device: &'a Device<T>,
- msg: &[u8],
+ msg: &NoiseInitiation,
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
- // parse message
-
- let msg = Initiation::parse(msg)?;
-
// initialize state
let ck = INITIAL_CK;
@@ -295,12 +291,11 @@ pub fn consume_initiation<'a, T: Copy>(
pub fn create_response<T: Copy>(
peer: &Peer<T>,
- sender: u32, // sending identifier
- state: TemporaryState, // state from "consume_initiation"
+ sender: u32, // sending identifier
+ state: TemporaryState, // state from "consume_initiation"
+ msg: &mut NoiseResponse, // resulting response
) -> 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);
@@ -380,12 +375,8 @@ pub fn create_response<T: Copy>(
pub fn consume_response<T: Copy>(
device: &Device<T>,
- msg: &[u8],
+ msg: &NoiseResponse,
) -> 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())?;
diff --git a/src/main.rs b/src/main.rs
index b2995e7..f408d49 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,7 @@
-mod noise;
+mod handshake;
mod types;
-use noise::Device;
+use handshake::Device;
use types::KeyPair;
fn main() {}
diff --git a/src/noise/device.rs b/src/noise/device.rs
deleted file mode 100644
index 04e00f9..0000000
--- a/src/noise/device.rs
+++ /dev/null
@@ -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();
- }
-}
diff --git a/src/noise/messages.rs b/src/noise/messages.rs
deleted file mode 100644
index dca49b9..0000000
--- a/src/noise/messages.rs
+++ /dev/null
@@ -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());
- }
-}
diff --git a/src/noise/mod.rs b/src/noise/mod.rs
deleted file mode 100644
index d48b5e0..0000000
--- a/src/noise/mod.rs
+++ /dev/null
@@ -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;
diff --git a/src/noise/noise.rs b/src/noise/noise.rs
deleted file mode 100644
index 980f1db..0000000
--- a/src/noise/noise.rs
+++ /dev/null
@@ -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
- &timestamp::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(),
- },
- }),
- ))
-}
diff --git a/src/noise/peer.rs b/src/noise/peer.rs
deleted file mode 100644
index 5b01d75..0000000
--- a/src/noise/peer.rs
+++ /dev/null
@@ -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: &timestamp::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(&timestamp_old, &timestamp_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)
- }
- }
-}
diff --git a/src/noise/timestamp.rs b/src/noise/timestamp.rs
deleted file mode 100644
index 0996f8b..0000000
--- a/src/noise/timestamp.rs
+++ /dev/null
@@ -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;
-}
diff --git a/src/noise/types.rs b/src/noise/types.rs
deleted file mode 100644
index 0d9a5d3..0000000
--- a/src/noise/types.rs
+++ /dev/null
@@ -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];