Better management of key material
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1241,6 +1241,7 @@ dependencies = [
|
|||||||
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chacha20poly1305 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chacha20poly1305 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ hjul = "0.1.2"
|
|||||||
ring = "0.16.7"
|
ring = "0.16.7"
|
||||||
chacha20poly1305 = "^0.1"
|
chacha20poly1305 = "^0.1"
|
||||||
aead = "^0.1.1"
|
aead = "^0.1.1"
|
||||||
|
clear_on_drop = "0.2.3"
|
||||||
|
|
||||||
[dependencies.x25519-dalek]
|
[dependencies.x25519-dalek]
|
||||||
version = "^0.5"
|
version = "^0.5"
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ use rand::{CryptoRng, RngCore};
|
|||||||
use generic_array::typenum::*;
|
use generic_array::typenum::*;
|
||||||
use generic_array::*;
|
use generic_array::*;
|
||||||
|
|
||||||
|
use clear_on_drop::clear_stack_on_return;
|
||||||
|
|
||||||
use super::device::Device;
|
use super::device::Device;
|
||||||
use super::messages::{NoiseInitiation, NoiseResponse};
|
use super::messages::{NoiseInitiation, NoiseResponse};
|
||||||
use super::messages::{TYPE_INITIATION, TYPE_RESPONSE};
|
use super::messages::{TYPE_INITIATION, TYPE_RESPONSE};
|
||||||
@@ -39,6 +41,9 @@ const SIZE_HS: usize = 32;
|
|||||||
const SIZE_NONCE: usize = 8;
|
const SIZE_NONCE: usize = 8;
|
||||||
const SIZE_TAG: usize = 16;
|
const SIZE_TAG: usize = 16;
|
||||||
|
|
||||||
|
// number of pages to clear after sensitive call
|
||||||
|
const CLEAR_PAGES: usize = 1;
|
||||||
|
|
||||||
// C := Hash(Construction)
|
// C := Hash(Construction)
|
||||||
const INITIAL_CK: [u8; SIZE_CK] = [
|
const INITIAL_CK: [u8; SIZE_CK] = [
|
||||||
0x60, 0xe2, 0x6d, 0xae, 0xf3, 0x27, 0xef, 0xc0, 0x2e, 0xc3, 0x35, 0xe2, 0xa0, 0x25, 0xd2, 0xd0,
|
0x60, 0xe2, 0x6d, 0xae, 0xf3, 0x27, 0xef, 0xc0, 0x2e, 0xc3, 0x35, 0xe2, 0xa0, 0x25, 0xd2, 0xd0,
|
||||||
@@ -103,21 +108,21 @@ macro_rules! KDF3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! SEAL {
|
macro_rules! SEAL {
|
||||||
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {{
|
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {
|
||||||
let ct = ChaCha20Poly1305::new(*GenericArray::from_slice($key))
|
ChaCha20Poly1305::new(*GenericArray::from_slice($key))
|
||||||
.encrypt(&ZERO_NONCE.into(), Payload { msg: $pt, aad: $ad })
|
.encrypt(&ZERO_NONCE.into(), Payload { msg: $pt, aad: $ad })
|
||||||
.unwrap();
|
.map(|ct| $ct.copy_from_slice(&ct))
|
||||||
$ct.copy_from_slice(&ct);
|
.unwrap()
|
||||||
}};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! OPEN {
|
macro_rules! OPEN {
|
||||||
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {{
|
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {
|
||||||
ChaCha20Poly1305::new(*GenericArray::from_slice($key))
|
ChaCha20Poly1305::new(*GenericArray::from_slice($key))
|
||||||
.decrypt(&ZERO_NONCE.into(), Payload { msg: $ct, aad: $ad })
|
.decrypt(&ZERO_NONCE.into(), Payload { msg: $ct, aad: $ad })
|
||||||
.map_err(|_| HandshakeError::DecryptionFailure)
|
.map_err(|_| HandshakeError::DecryptionFailure)
|
||||||
.map(|pt| $pt.copy_from_slice(&pt))
|
.map(|pt| $pt.copy_from_slice(&pt))
|
||||||
}};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -211,144 +216,151 @@ pub fn create_initiation<T: Copy, R: RngCore + CryptoRng>(
|
|||||||
sender: u32,
|
sender: u32,
|
||||||
msg: &mut NoiseInitiation,
|
msg: &mut NoiseInitiation,
|
||||||
) -> Result<(), HandshakeError> {
|
) -> Result<(), HandshakeError> {
|
||||||
// initialize state
|
clear_stack_on_return(CLEAR_PAGES, || {
|
||||||
|
// initialize state
|
||||||
|
|
||||||
let ck = INITIAL_CK;
|
let ck = INITIAL_CK;
|
||||||
let hs = INITIAL_HS;
|
let hs = INITIAL_HS;
|
||||||
let hs = HASH!(&hs, peer.pk.as_bytes());
|
let hs = HASH!(&hs, peer.pk.as_bytes());
|
||||||
|
|
||||||
msg.f_type.set(TYPE_INITIATION as u32);
|
msg.f_type.set(TYPE_INITIATION as u32);
|
||||||
msg.f_sender.set(sender);
|
msg.f_sender.set(sender);
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
// (E_priv, E_pub) := DH-Generate()
|
||||||
|
|
||||||
let eph_sk = StaticSecret::new(rng);
|
let eph_sk = StaticSecret::new(rng);
|
||||||
let eph_pk = PublicKey::from(&eph_sk);
|
let eph_pk = PublicKey::from(&eph_sk);
|
||||||
|
|
||||||
// C := Kdf(C, E_pub)
|
// C := Kdf(C, E_pub)
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_pk.as_bytes());
|
let ck = KDF1!(&ck, eph_pk.as_bytes());
|
||||||
|
|
||||||
// msg.ephemeral := E_pub
|
// msg.ephemeral := E_pub
|
||||||
|
|
||||||
msg.f_ephemeral = *eph_pk.as_bytes();
|
msg.f_ephemeral = *eph_pk.as_bytes();
|
||||||
|
|
||||||
// H := HASH(H, msg.ephemeral)
|
// H := HASH(H, msg.ephemeral)
|
||||||
|
|
||||||
let hs = HASH!(&hs, msg.f_ephemeral);
|
let hs = HASH!(&hs, msg.f_ephemeral);
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
|
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
|
||||||
|
|
||||||
let (ck, key) = KDF2!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
|
let (ck, key) = KDF2!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
|
||||||
|
|
||||||
// msg.static := Aead(k, 0, S_pub, H)
|
// msg.static := Aead(k, 0, S_pub, H)
|
||||||
|
|
||||||
SEAL!(
|
SEAL!(
|
||||||
&key,
|
&key,
|
||||||
&hs, // ad
|
&hs, // ad
|
||||||
device.pk.as_bytes(), // pt
|
device.pk.as_bytes(), // pt
|
||||||
&mut msg.f_static // ct || tag
|
&mut msg.f_static // ct || tag
|
||||||
);
|
);
|
||||||
|
|
||||||
// H := Hash(H || msg.static)
|
// H := Hash(H || msg.static)
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_static[..]);
|
let hs = HASH!(&hs, &msg.f_static[..]);
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
|
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
|
||||||
|
|
||||||
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
|
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
|
||||||
|
|
||||||
// msg.timestamp := Aead(k, 0, Timestamp(), H)
|
// msg.timestamp := Aead(k, 0, Timestamp(), H)
|
||||||
|
|
||||||
SEAL!(
|
SEAL!(
|
||||||
&key,
|
&key,
|
||||||
&hs, // ad
|
&hs, // ad
|
||||||
×tamp::now(), // pt
|
×tamp::now(), // pt
|
||||||
&mut msg.f_timestamp // ct || tag
|
&mut msg.f_timestamp // ct || tag
|
||||||
);
|
);
|
||||||
|
|
||||||
// H := Hash(H || msg.timestamp)
|
// H := Hash(H || msg.timestamp)
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_timestamp);
|
let hs = HASH!(&hs, &msg.f_timestamp);
|
||||||
|
|
||||||
// update state of peer
|
// update state of peer
|
||||||
|
|
||||||
peer.set_state(State::InitiationSent {
|
peer.set_state(State::InitiationSent {
|
||||||
hs,
|
hs,
|
||||||
ck,
|
ck,
|
||||||
eph_sk,
|
eph_sk,
|
||||||
sender,
|
sender,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_initiation<'a, T: Copy>(
|
pub fn consume_initiation<'a, T: Copy>(
|
||||||
device: &'a Device<T>,
|
device: &'a Device<T>,
|
||||||
msg: &NoiseInitiation,
|
msg: &NoiseInitiation,
|
||||||
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
|
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
|
||||||
// initialize state
|
clear_stack_on_return(CLEAR_PAGES, || {
|
||||||
|
// initialize state
|
||||||
|
|
||||||
let ck = INITIAL_CK;
|
let ck = INITIAL_CK;
|
||||||
let hs = INITIAL_HS;
|
let hs = INITIAL_HS;
|
||||||
let hs = HASH!(&hs, device.pk.as_bytes());
|
let hs = HASH!(&hs, device.pk.as_bytes());
|
||||||
|
|
||||||
// C := Kdf(C, E_pub)
|
// C := Kdf(C, E_pub)
|
||||||
|
|
||||||
let ck = KDF1!(&ck, &msg.f_ephemeral);
|
let ck = KDF1!(&ck, &msg.f_ephemeral);
|
||||||
|
|
||||||
// H := HASH(H, msg.ephemeral)
|
// H := HASH(H, msg.ephemeral)
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_ephemeral);
|
let hs = HASH!(&hs, &msg.f_ephemeral);
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
|
// (C, k) := Kdf2(C, DH(E_priv, S_pub))
|
||||||
|
|
||||||
let eph_r_pk = PublicKey::from(msg.f_ephemeral);
|
let eph_r_pk = PublicKey::from(msg.f_ephemeral);
|
||||||
let (ck, key) = KDF2!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
|
let (ck, key) = KDF2!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
|
||||||
|
|
||||||
// msg.static := Aead(k, 0, S_pub, H)
|
// msg.static := Aead(k, 0, S_pub, H)
|
||||||
|
|
||||||
let mut pk = [0u8; 32];
|
let mut pk = [0u8; 32];
|
||||||
|
|
||||||
OPEN!(
|
OPEN!(
|
||||||
&key,
|
&key,
|
||||||
&hs, // ad
|
&hs, // ad
|
||||||
&mut pk, // pt
|
&mut pk, // pt
|
||||||
&msg.f_static // ct || tag
|
&msg.f_static // ct || tag
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let peer = device.lookup_pk(&PublicKey::from(pk))?;
|
let peer = device.lookup_pk(&PublicKey::from(pk))?;
|
||||||
|
|
||||||
// H := Hash(H || msg.static)
|
// H := Hash(H || msg.static)
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_static[..]);
|
let hs = HASH!(&hs, &msg.f_static[..]);
|
||||||
|
|
||||||
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
|
// (C, k) := Kdf2(C, DH(S_priv, S_pub))
|
||||||
|
|
||||||
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
|
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
|
||||||
|
|
||||||
// msg.timestamp := Aead(k, 0, Timestamp(), H)
|
// msg.timestamp := Aead(k, 0, Timestamp(), H)
|
||||||
|
|
||||||
let mut ts = timestamp::ZERO;
|
let mut ts = timestamp::ZERO;
|
||||||
|
|
||||||
OPEN!(
|
OPEN!(
|
||||||
&key,
|
&key,
|
||||||
&hs, // ad
|
&hs, // ad
|
||||||
&mut ts, // pt
|
&mut ts, // pt
|
||||||
&msg.f_timestamp // ct || tag
|
&msg.f_timestamp // ct || tag
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// check and update timestamp
|
// check and update timestamp
|
||||||
|
|
||||||
peer.check_replay_flood(device, &ts)?;
|
peer.check_replay_flood(device, &ts)?;
|
||||||
|
|
||||||
// H := Hash(H || msg.timestamp)
|
// H := Hash(H || msg.timestamp)
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_timestamp);
|
let hs = HASH!(&hs, &msg.f_timestamp);
|
||||||
|
|
||||||
// return state (to create response)
|
// clear initiation state
|
||||||
|
*peer.state.lock() = State::Reset;
|
||||||
|
|
||||||
Ok((peer, (msg.f_sender.get(), eph_r_pk, hs, ck)))
|
// return state (to create response)
|
||||||
|
|
||||||
|
Ok((peer, (msg.f_sender.get(), eph_r_pk, hs, ck)))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_response<T: Copy, R: RngCore + CryptoRng>(
|
pub fn create_response<T: Copy, R: RngCore + CryptoRng>(
|
||||||
@@ -358,79 +370,77 @@ pub fn create_response<T: Copy, R: RngCore + CryptoRng>(
|
|||||||
state: TemporaryState, // state from "consume_initiation"
|
state: TemporaryState, // state from "consume_initiation"
|
||||||
msg: &mut NoiseResponse, // resulting response
|
msg: &mut NoiseResponse, // resulting response
|
||||||
) -> Result<KeyPair, HandshakeError> {
|
) -> Result<KeyPair, HandshakeError> {
|
||||||
// unpack state
|
clear_stack_on_return(CLEAR_PAGES, || {
|
||||||
|
// unpack state
|
||||||
|
|
||||||
let (receiver, eph_r_pk, hs, ck) = state;
|
let (receiver, eph_r_pk, hs, ck) = state;
|
||||||
|
|
||||||
msg.f_type.set(TYPE_RESPONSE as u32);
|
msg.f_type.set(TYPE_RESPONSE as u32);
|
||||||
msg.f_sender.set(sender);
|
msg.f_sender.set(sender);
|
||||||
msg.f_receiver.set(receiver);
|
msg.f_receiver.set(receiver);
|
||||||
|
|
||||||
// (E_priv, E_pub) := DH-Generate()
|
// (E_priv, E_pub) := DH-Generate()
|
||||||
|
|
||||||
let eph_sk = StaticSecret::new(rng);
|
let eph_sk = StaticSecret::new(rng);
|
||||||
let eph_pk = PublicKey::from(&eph_sk);
|
let eph_pk = PublicKey::from(&eph_sk);
|
||||||
|
|
||||||
// C := Kdf1(C, E_pub)
|
// C := Kdf1(C, E_pub)
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_pk.as_bytes());
|
let ck = KDF1!(&ck, eph_pk.as_bytes());
|
||||||
|
|
||||||
// msg.ephemeral := E_pub
|
// msg.ephemeral := E_pub
|
||||||
|
|
||||||
msg.f_ephemeral = *eph_pk.as_bytes();
|
msg.f_ephemeral = *eph_pk.as_bytes();
|
||||||
|
|
||||||
// H := Hash(H || msg.ephemeral)
|
// H := Hash(H || msg.ephemeral)
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_ephemeral);
|
let hs = HASH!(&hs, &msg.f_ephemeral);
|
||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, E_pub))
|
// C := Kdf1(C, DH(E_priv, E_pub))
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
|
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
|
||||||
|
|
||||||
// C := Kdf1(C, DH(E_priv, S_pub))
|
// C := Kdf1(C, DH(E_priv, S_pub))
|
||||||
|
|
||||||
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
|
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
|
||||||
|
|
||||||
// (C, tau, k) := Kdf3(C, Q)
|
// (C, tau, k) := Kdf3(C, Q)
|
||||||
|
|
||||||
let (ck, tau, key) = KDF3!(&ck, &peer.psk);
|
let (ck, tau, key) = KDF3!(&ck, &peer.psk);
|
||||||
|
|
||||||
// H := Hash(H || tau)
|
// H := Hash(H || tau)
|
||||||
|
|
||||||
let hs = HASH!(&hs, tau);
|
let hs = HASH!(&hs, tau);
|
||||||
|
|
||||||
// msg.empty := Aead(k, 0, [], H)
|
// msg.empty := Aead(k, 0, [], H)
|
||||||
|
|
||||||
SEAL!(
|
SEAL!(
|
||||||
&key,
|
&key,
|
||||||
&hs, // ad
|
&hs, // ad
|
||||||
&[], // pt
|
&[], // pt
|
||||||
&mut msg.f_empty // \epsilon || tag
|
&mut msg.f_empty // \epsilon || tag
|
||||||
);
|
);
|
||||||
|
|
||||||
/* not strictly needed
|
// Not strictly needed
|
||||||
* // H := Hash(H || msg.empty)
|
// let hs = HASH!(&hs, &msg.f_empty_tag);
|
||||||
* let hs = HASH!(&hs, &msg.f_empty_tag);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// derive key-pair
|
// derive key-pair
|
||||||
// (verbose code, due to GenericArray -> [u8; 32] conversion)
|
let (key_recv, key_send) = KDF2!(&ck, &[]);
|
||||||
|
|
||||||
let (key_recv, key_send) = KDF2!(&ck, &[]);
|
// return unconfirmed key-pair
|
||||||
|
|
||||||
// return unconfirmed key-pair
|
Ok(KeyPair {
|
||||||
|
birth: Instant::now(),
|
||||||
Ok(KeyPair {
|
initiator: false,
|
||||||
birth: Instant::now(),
|
send: Key {
|
||||||
initiator: false,
|
id: sender,
|
||||||
send: Key {
|
key: key_send.into(),
|
||||||
id: sender,
|
},
|
||||||
key: key_send.into(),
|
recv: Key {
|
||||||
},
|
id: receiver,
|
||||||
recv: Key {
|
key: key_recv.into(),
|
||||||
id: receiver,
|
},
|
||||||
key: key_recv.into(),
|
})
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,73 +448,78 @@ pub fn consume_response<T: Copy>(
|
|||||||
device: &Device<T>,
|
device: &Device<T>,
|
||||||
msg: &NoiseResponse,
|
msg: &NoiseResponse,
|
||||||
) -> Result<Output<T>, HandshakeError> {
|
) -> Result<Output<T>, HandshakeError> {
|
||||||
// retrieve peer and associated state
|
clear_stack_on_return(CLEAR_PAGES, || {
|
||||||
|
// retrieve peer and copy associated state
|
||||||
|
let peer = device.lookup_id(msg.f_receiver.get())?;
|
||||||
|
let (hs, ck, sender, eph_sk) = match *peer.state.lock() {
|
||||||
|
State::InitiationSent {
|
||||||
|
hs,
|
||||||
|
ck,
|
||||||
|
sender,
|
||||||
|
ref eph_sk,
|
||||||
|
} => Ok((hs, ck, sender, StaticSecret::from(eph_sk.to_bytes()))),
|
||||||
|
_ => Err(HandshakeError::InvalidState),
|
||||||
|
}?;
|
||||||
|
|
||||||
let peer = device.lookup_id(msg.f_receiver.get())?;
|
// C := Kdf1(C, E_pub)
|
||||||
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);
|
||||||
|
|
||||||
let ck = KDF1!(&ck, &msg.f_ephemeral);
|
// H := Hash(H || msg.ephemeral)
|
||||||
|
|
||||||
// H := Hash(H || msg.ephemeral)
|
let hs = HASH!(&hs, &msg.f_ephemeral);
|
||||||
|
|
||||||
let hs = HASH!(&hs, &msg.f_ephemeral);
|
// C := Kdf1(C, DH(E_priv, E_pub))
|
||||||
|
|
||||||
// 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());
|
||||||
|
|
||||||
let eph_r_pk = PublicKey::from(msg.f_ephemeral);
|
// C := Kdf1(C, DH(E_priv, S_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, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
|
||||||
|
|
||||||
let ck = KDF1!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
|
// (C, tau, k) := Kdf3(C, Q)
|
||||||
|
|
||||||
// (C, tau, k) := Kdf3(C, Q)
|
let (ck, tau, key) = KDF3!(&ck, &peer.psk);
|
||||||
|
|
||||||
let (ck, tau, key) = KDF3!(&ck, &peer.psk);
|
// H := Hash(H || tau)
|
||||||
|
|
||||||
// H := Hash(H || tau)
|
let hs = HASH!(&hs, tau);
|
||||||
|
|
||||||
let hs = HASH!(&hs, tau);
|
// msg.empty := Aead(k, 0, [], H)
|
||||||
|
|
||||||
// msg.empty := Aead(k, 0, [], H)
|
OPEN!(
|
||||||
|
&key,
|
||||||
|
&hs, // ad
|
||||||
|
&mut [], // pt
|
||||||
|
&msg.f_empty // \epsilon || tag
|
||||||
|
)?;
|
||||||
|
|
||||||
OPEN!(
|
// derive key-pair
|
||||||
&key,
|
|
||||||
&hs, // ad
|
|
||||||
&mut [], // pt
|
|
||||||
&msg.f_empty // \epsilon || tag
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// derive key-pair
|
let (key_send, key_recv) = KDF2!(&ck, &[]);
|
||||||
|
|
||||||
let (key_send, key_recv) = KDF2!(&ck, &[]);
|
// null the state
|
||||||
|
|
||||||
// return confirmed key-pair
|
*peer.state.lock() = State::Reset;
|
||||||
|
|
||||||
Ok((
|
// return confirmed key-pair
|
||||||
Some(peer.identifier), // proves overship of the public key (e.g. for updating the endpoint)
|
|
||||||
None, // no response message
|
Ok((
|
||||||
Some(KeyPair {
|
Some(peer.identifier), // proves ownership of the public key (e.g. for updating the endpoint)
|
||||||
birth: Instant::now(),
|
None, // no response message
|
||||||
initiator: true,
|
Some(KeyPair {
|
||||||
send: Key {
|
birth: Instant::now(),
|
||||||
id: sender,
|
initiator: true,
|
||||||
key: key_send.into(),
|
send: Key {
|
||||||
},
|
id: sender,
|
||||||
recv: Key {
|
key: key_send.into(),
|
||||||
id: msg.f_sender.get(),
|
},
|
||||||
key: key_recv.into(),
|
recv: Key {
|
||||||
},
|
id: msg.f_sender.get(),
|
||||||
}),
|
key: key_recv.into(),
|
||||||
))
|
},
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use x25519_dalek::PublicKey;
|
|||||||
use x25519_dalek::SharedSecret;
|
use x25519_dalek::SharedSecret;
|
||||||
use x25519_dalek::StaticSecret;
|
use x25519_dalek::StaticSecret;
|
||||||
|
|
||||||
|
use clear_on_drop::clear::Clear;
|
||||||
|
|
||||||
use super::device::Device;
|
use super::device::Device;
|
||||||
use super::macs;
|
use super::macs;
|
||||||
use super::timestamp;
|
use super::timestamp;
|
||||||
@@ -27,9 +29,9 @@ pub struct Peer<T> {
|
|||||||
pub(crate) identifier: T,
|
pub(crate) identifier: T,
|
||||||
|
|
||||||
// mutable state
|
// mutable state
|
||||||
state: Mutex<State>,
|
pub(crate) state: Mutex<State>,
|
||||||
timestamp: Mutex<Option<timestamp::TAI64N>>,
|
pub(crate) timestamp: Mutex<Option<timestamp::TAI64N>>,
|
||||||
last_initiation_consumption: Mutex<Option<Instant>>,
|
pub(crate) last_initiation_consumption: Mutex<Option<Instant>>,
|
||||||
|
|
||||||
// state related to DoS mitigation fields
|
// state related to DoS mitigation fields
|
||||||
pub(crate) macs: Mutex<macs::Generator>,
|
pub(crate) macs: Mutex<macs::Generator>,
|
||||||
@@ -50,21 +52,15 @@ pub enum State {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for State {
|
impl Drop for State {
|
||||||
fn clone(&self) -> State {
|
fn drop(&mut self) {
|
||||||
match self {
|
match self {
|
||||||
State::Reset => State::Reset,
|
State::InitiationSent{hs, ck, ..} => {
|
||||||
State::InitiationSent {
|
// eph_sk already cleared by dalek-x25519
|
||||||
sender,
|
hs.clear();
|
||||||
eph_sk,
|
ck.clear();
|
||||||
hs,
|
|
||||||
ck,
|
|
||||||
} => State::InitiationSent {
|
|
||||||
sender: *sender,
|
|
||||||
eph_sk: StaticSecret::from(eph_sk.to_bytes()),
|
|
||||||
hs: *hs,
|
|
||||||
ck: *ck,
|
|
||||||
},
|
},
|
||||||
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,13 +86,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the state of the peer
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
pub fn get_state(&self) -> State {
|
|
||||||
self.state.lock().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the state of the peer unconditionally
|
/// Set the state of the peer unconditionally
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
/* The generic implementation (not supporting "sticky-sockets"),
|
pub trait Endpoint: Into<SocketAddr> + From<SocketAddr> {}
|
||||||
* is to simply use SocketAddr directly as the endpoint.
|
|
||||||
*/
|
|
||||||
pub trait Endpoint: Into<SocketAddr> {}
|
|
||||||
|
|
||||||
impl<T> Endpoint for T where T: Into<SocketAddr> {}
|
impl<T> Endpoint for T where T: Into<SocketAddr> + From<SocketAddr> {}
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
|
use clear_on_drop::clear::Clear;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
/* This file holds types passed between components.
|
/* This file holds types passed between components.
|
||||||
* Whenever a type cannot be held local to a single module.
|
* Whenever a type cannot be held local to a single module.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
pub key: [u8; 32],
|
pub key: [u8; 32],
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zero key on drop
|
||||||
|
impl Drop for Key {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.key.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl PartialEq for Key {
|
impl PartialEq for Key {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
@@ -17,7 +25,7 @@ impl PartialEq for Key {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct KeyPair {
|
pub struct KeyPair {
|
||||||
pub birth: Instant, // when was the key-pair created
|
pub birth: Instant, // when was the key-pair created
|
||||||
pub initiator: bool, // has the key-pair been confirmed?
|
pub initiator: bool, // has the key-pair been confirmed?
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::error;
|
use std::error;
|
||||||
|
|
||||||
pub trait Tun: Send + Sync {
|
pub trait Tun: Send + Sync + 'static {
|
||||||
type Error: error::Error;
|
type Error: error::Error;
|
||||||
|
|
||||||
/// Returns the MTU of the device
|
/// Returns the MTU of the device
|
||||||
@@ -22,13 +22,13 @@ pub trait Tun: Send + Sync {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - dst: Destination buffer (enough space for MTU bytes + header)
|
/// - buf: Destination buffer (enough space for MTU bytes + header)
|
||||||
/// - offset: Offset for the beginning of the IP packet
|
/// - offset: Offset for the beginning of the IP packet
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// The size of the IP packet (ignoring the header) or an std::error::Error instance:
|
/// The size of the IP packet (ignoring the header) or an std::error::Error instance:
|
||||||
fn read(&self, dst: &mut [u8], offset: usize) -> Result<usize, Self::Error>;
|
fn read(&self, buf: &mut [u8], offset: usize) -> Result<usize, Self::Error>;
|
||||||
|
|
||||||
/// Writes an IP packet to the tunnel device
|
/// Writes an IP packet to the tunnel device
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user