[noise] handshake response - can now complete full wireguard handshake without cookies
This commit is contained in:
parent
d57423b0c8
commit
3b95577a8d
|
@ -7,7 +7,7 @@ use crate::noise::handshake::{HANDSHAKE_INITIATOR_CHAIN_KEY, HANDSHAKE_INITIATOR
|
||||||
use crate::qcrypto::aead::{qcrypto_aead, qcrypto_aead_decrypt};
|
use crate::qcrypto::aead::{qcrypto_aead, qcrypto_aead_decrypt};
|
||||||
use crate::qcrypto::hashes::{qcrypto_hash_twice, qcrypto_mac};
|
use crate::qcrypto::hashes::{qcrypto_hash_twice, qcrypto_mac};
|
||||||
use crate::qcrypto::hkdf::qcrypto_hkdf;
|
use crate::qcrypto::hkdf::qcrypto_hkdf;
|
||||||
use crate::qcrypto::pki::{qcrypto_dh_ephemeral, qcrypto_dh_generate_ephemeral, qcrypto_dh_longterm};
|
use crate::qcrypto::pki::{qcrypto_dh_ephemeral, qcrypto_dh_generate_ephemeral, qcrypto_dh_generate_longterm, qcrypto_dh_longterm};
|
||||||
use crate::qcrypto::{LABEL_MAC1, timestamp};
|
use crate::qcrypto::{LABEL_MAC1, timestamp};
|
||||||
|
|
||||||
/// Generate a handshake initiator packet and encrypt it using the given session state, starting a new handshake state
|
/// Generate a handshake initiator packet and encrypt it using the given session state, starting a new handshake state
|
||||||
|
@ -35,16 +35,17 @@ pub fn handshake_init_to(session: &mut HandshakeState) -> Result<[u8; 148], Nois
|
||||||
session.h = HANDSHAKE_INITIATOR_CHAIN_KEY_HASH;
|
session.h = HANDSHAKE_INITIATOR_CHAIN_KEY_HASH;
|
||||||
session.h = qcrypto_hash_twice(&session.h, session.s_pub_r.as_bytes());
|
session.h = qcrypto_hash_twice(&session.h, session.s_pub_r.as_bytes());
|
||||||
|
|
||||||
let eph_keypair = qcrypto_dh_generate_ephemeral();
|
let eph_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
|
||||||
session.ck = qcrypto_hkdf::<1>(&session.ck, eph_keypair.1.as_bytes())[0];
|
session.ck = qcrypto_hkdf::<1>(&session.ck, eph_keypair.1.as_bytes())[0];
|
||||||
session.e_pub_i = eph_keypair.1;
|
session.e_pub_i = eph_keypair.1;
|
||||||
|
session.e_priv_me = eph_keypair.0;
|
||||||
|
|
||||||
msg.ephemeral = eph_keypair.1.to_bytes();
|
msg.ephemeral = eph_keypair.1.to_bytes();
|
||||||
|
|
||||||
session.h = qcrypto_hash_twice(&session.h, &msg.ephemeral);
|
session.h = qcrypto_hash_twice(&session.h, &msg.ephemeral);
|
||||||
|
|
||||||
let ci_k_pair = qcrypto_hkdf::<2>(&session.ck, qcrypto_dh_ephemeral(eph_keypair.0, &session.s_pub_r).as_bytes());
|
let ci_k_pair = qcrypto_hkdf::<2>(&session.ck, qcrypto_dh_longterm(&session.e_priv_me, &session.s_pub_r).as_bytes());
|
||||||
session.ck = ci_k_pair[0];
|
session.ck = ci_k_pair[0];
|
||||||
let k = ci_k_pair[1];
|
let k = ci_k_pair[1];
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ impl HandshakeInitiatorRaw {
|
||||||
|
|
||||||
output[116..132].copy_from_slice(&mac1);
|
output[116..132].copy_from_slice(&mac1);
|
||||||
|
|
||||||
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &packet[..132]) } else { [0u8; 16] };
|
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &output[..132]) } else { [0u8; 16] };
|
||||||
|
|
||||||
output[132..148].copy_from_slice(&mac2);
|
output[132..148].copy_from_slice(&mac2);
|
||||||
|
|
||||||
|
@ -168,7 +169,6 @@ pub fn handshake_init_from(session: &mut HandshakeState, packet: [u8; 148]) -> R
|
||||||
// we need to check mac1 and mac2
|
// we need to check mac1 and mac2
|
||||||
|
|
||||||
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &packet[..116]);
|
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &packet[..116]);
|
||||||
|
|
||||||
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &packet[..132]) } else { [0u8; 16] };
|
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &packet[..132]) } else { [0u8; 16] };
|
||||||
|
|
||||||
if mac1 != msg.mac1 || mac2 != msg.mac2 {
|
if mac1 != msg.mac1 || mac2 != msg.mac2 {
|
||||||
|
|
|
@ -37,17 +37,20 @@ pub struct HandshakeState {
|
||||||
pub ck: [u8; 32],
|
pub ck: [u8; 32],
|
||||||
|
|
||||||
pub e_pub_i: PublicKey,
|
pub e_pub_i: PublicKey,
|
||||||
|
pub e_pub_r: PublicKey,
|
||||||
|
|
||||||
pub s_pub_i: PublicKey,
|
pub s_pub_i: PublicKey,
|
||||||
pub s_pub_r: PublicKey,
|
pub s_pub_r: PublicKey,
|
||||||
|
|
||||||
pub e_priv_me: EphemeralSecret,
|
pub e_priv_me: StaticSecret,
|
||||||
pub s_priv_me: StaticSecret,
|
pub s_priv_me: StaticSecret,
|
||||||
pub s_pub_them: PublicKey,
|
pub s_pub_them: PublicKey,
|
||||||
|
|
||||||
pub i_i: u32,
|
pub i_i: u32,
|
||||||
pub i_r: u32,
|
pub i_r: u32,
|
||||||
|
|
||||||
|
pub q: [u8; 32],
|
||||||
|
|
||||||
pub cookies: Vec<Cookie>
|
pub cookies: Vec<Cookie>
|
||||||
}
|
}
|
||||||
impl HandshakeState {
|
impl HandshakeState {
|
||||||
|
@ -65,8 +68,6 @@ impl Debug for HandshakeState {
|
||||||
.field("e_pub_i", &self.e_pub_i)
|
.field("e_pub_i", &self.e_pub_i)
|
||||||
.field("s_pub_i", &self.s_pub_i)
|
.field("s_pub_i", &self.s_pub_i)
|
||||||
.field("s_pub_r", &self.s_pub_r)
|
.field("s_pub_r", &self.s_pub_r)
|
||||||
.field("e_priv_me", &"<redacted>")
|
|
||||||
.field("s_priv_me", &"<redacted>")
|
|
||||||
.field("s_pub_them", &self.s_pub_them)
|
.field("s_pub_them", &self.s_pub_them)
|
||||||
.field("i_i", &self.i_i)
|
.field("i_i", &self.i_i)
|
||||||
.field("i_r", &self.i_r)
|
.field("i_r", &self.i_r)
|
||||||
|
|
|
@ -1,14 +1,116 @@
|
||||||
//! `Noise_IKpsk2` handshake response packet
|
//! `Noise_IKpsk2` handshake response packet
|
||||||
|
|
||||||
|
use x25519_dalek::PublicKey;
|
||||||
|
use crate::noise::error::NoiseError;
|
||||||
use crate::noise::handshake::{HandshakeState, needs_cookie};
|
use crate::noise::handshake::{HandshakeState, needs_cookie};
|
||||||
|
use crate::qcrypto::aead::{qcrypto_aead, qcrypto_aead_decrypt};
|
||||||
use crate::qcrypto::hashes::{qcrypto_hash_twice, qcrypto_mac};
|
use crate::qcrypto::hashes::{qcrypto_hash_twice, qcrypto_mac};
|
||||||
|
use crate::qcrypto::hkdf::qcrypto_hkdf;
|
||||||
use crate::qcrypto::LABEL_MAC1;
|
use crate::qcrypto::LABEL_MAC1;
|
||||||
|
use crate::qcrypto::pki::{qcrypto_dh_ephemeral, qcrypto_dh_generate_ephemeral, qcrypto_dh_generate_longterm, qcrypto_dh_longterm};
|
||||||
|
|
||||||
|
/// Creates a handshake response packet using the current active handshake session.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will error if an encryption step is unsuccessful
|
||||||
|
/// # Panics
|
||||||
|
/// This function, while containing unwraps, will never panic.
|
||||||
|
pub fn handshake_response_to(session: &mut HandshakeState) -> Result<[u8; 92], NoiseError> {
|
||||||
|
let eph_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
|
||||||
|
let mut msg = HandshakeResponseRaw {
|
||||||
|
sender: [0u8; 4],
|
||||||
|
receiver: [0u8; 4],
|
||||||
|
ephemeral: [0u8; 32],
|
||||||
|
empty: [0u8; 16],
|
||||||
|
mac1: [0u8; 16],
|
||||||
|
mac2: [0u8; 16]
|
||||||
|
};
|
||||||
|
|
||||||
|
session.ck = qcrypto_hkdf::<1>(&session.ck, eph_keypair.1.as_bytes())[0];
|
||||||
|
|
||||||
|
msg.ephemeral = eph_keypair.1.to_bytes();
|
||||||
|
|
||||||
|
session.h = qcrypto_hash_twice(&session.h, &msg.ephemeral);
|
||||||
|
|
||||||
|
session.ck = qcrypto_hkdf::<1>(&session.ck, qcrypto_dh_longterm(&eph_keypair.0, &session.e_pub_i).as_bytes())[0];
|
||||||
|
session.ck = qcrypto_hkdf::<1>(&session.ck, qcrypto_dh_longterm(&eph_keypair.0, &session.s_pub_i).as_bytes())[0];
|
||||||
|
|
||||||
|
let cr_t_k = qcrypto_hkdf::<3>(&session.ck, &session.q);
|
||||||
|
session.ck = cr_t_k[0];
|
||||||
|
let t = cr_t_k[1];
|
||||||
|
let k = cr_t_k[2];
|
||||||
|
|
||||||
|
session.h = qcrypto_hash_twice(&session.h, &t);
|
||||||
|
|
||||||
|
println!("{:?} {:?} {:?}", k, 0, session.h);
|
||||||
|
msg.empty = match qcrypto_aead(&k, 0, &[], &session.h) {
|
||||||
|
Ok(s) => s.try_into().unwrap(),
|
||||||
|
Err(e) => return Err(NoiseError::ChaCha20Error(e))
|
||||||
|
};
|
||||||
|
|
||||||
|
session.h = qcrypto_hash_twice(&session.h, &msg.empty);
|
||||||
|
|
||||||
|
Ok(msg.to_bytes(session))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypts a handshake response packet using the current active handshake session.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will error if a decryption step is unsuccessful
|
||||||
|
/// # Panics
|
||||||
|
/// This function, while containing unwraps, will never panic.
|
||||||
|
pub fn handshake_response_from(session: &mut HandshakeState, packet: [u8; 92]) -> Result<(), NoiseError> {
|
||||||
|
let mut msg = HandshakeResponseRaw::from_bytes(packet);
|
||||||
|
|
||||||
|
let e_pub_r = PublicKey::from(msg.ephemeral);
|
||||||
|
|
||||||
|
let mut ck = qcrypto_hkdf::<1>(&session.ck, e_pub_r.as_bytes())[0];
|
||||||
|
let mut h = qcrypto_hash_twice(&session.h, &msg.ephemeral);
|
||||||
|
|
||||||
|
ck = qcrypto_hkdf::<1>(&ck, qcrypto_dh_longterm(&session.e_priv_me, &e_pub_r).as_bytes())[0];
|
||||||
|
ck = qcrypto_hkdf::<1>(&ck, qcrypto_dh_longterm(&session.s_priv_me, &e_pub_r).as_bytes())[0];
|
||||||
|
|
||||||
|
let cr_t_k = qcrypto_hkdf::<3>(&ck, &session.q);
|
||||||
|
ck = cr_t_k[0];
|
||||||
|
let t = cr_t_k[1];
|
||||||
|
let k = cr_t_k[2];
|
||||||
|
|
||||||
|
h = qcrypto_hash_twice(&h, &t);
|
||||||
|
|
||||||
|
println!("here");
|
||||||
|
println!("{:?} {:?} {:?}", k, 0, h);
|
||||||
|
|
||||||
|
let empty = match qcrypto_aead_decrypt(&k, 0, &msg.empty, &h) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return Err(NoiseError::ChaCha20Error(e))
|
||||||
|
};
|
||||||
|
|
||||||
|
if !empty.is_empty() {
|
||||||
|
return Err(NoiseError::PacketUnauthenticated)
|
||||||
|
}
|
||||||
|
|
||||||
|
h = qcrypto_hash_twice(&h, &msg.empty);
|
||||||
|
|
||||||
|
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &packet[..60]);
|
||||||
|
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &packet[..76]) } else { [0u8; 16] };
|
||||||
|
|
||||||
|
if mac1 != msg.mac1 || mac2 != msg.mac2 {
|
||||||
|
return Err(NoiseError::PacketUnauthenticated)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.e_pub_r = e_pub_r;
|
||||||
|
session.ck = ck;
|
||||||
|
session.h = h;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
struct HandshakeResponseRaw {
|
struct HandshakeResponseRaw {
|
||||||
sender: [u8; 4],
|
sender: [u8; 4],
|
||||||
receiver: [u8; 4],
|
receiver: [u8; 4],
|
||||||
ephemeral: [u8; 32],
|
ephemeral: [u8; 32],
|
||||||
empty: [u8; 0 + 16],
|
empty: [u8; 0 + 16],
|
||||||
|
mac1: [u8; 16],
|
||||||
|
mac2: [u8; 16]
|
||||||
}
|
}
|
||||||
impl HandshakeResponseRaw {
|
impl HandshakeResponseRaw {
|
||||||
fn to_bytes(&self, session: &mut HandshakeState) -> [u8; 92] {
|
fn to_bytes(&self, session: &mut HandshakeState) -> [u8; 92] {
|
||||||
|
@ -20,13 +122,13 @@ impl HandshakeResponseRaw {
|
||||||
output_array[12..44].copy_from_slice(&self.ephemeral);
|
output_array[12..44].copy_from_slice(&self.ephemeral);
|
||||||
output_array[44..60].copy_from_slice(&self.empty);
|
output_array[44..60].copy_from_slice(&self.empty);
|
||||||
|
|
||||||
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &output_array[..116]);
|
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &output_array[..60]);
|
||||||
|
|
||||||
output_array[116..132].copy_from_slice(&mac1);
|
output_array[60..76].copy_from_slice(&mac1);
|
||||||
|
|
||||||
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &output_array[..132]) } else { [0u8; 16] };
|
let mac2 = if needs_cookie(session) { qcrypto_mac(&session.cookies[session.cookies.len() - 1].cookie, &output_array[..76]) } else { [0u8; 16] };
|
||||||
|
|
||||||
output_array[132..148].copy_from_slice(&mac2);
|
output_array[76..92].copy_from_slice(&mac2);
|
||||||
|
|
||||||
output_array
|
output_array
|
||||||
}
|
}
|
||||||
|
@ -37,6 +139,8 @@ impl HandshakeResponseRaw {
|
||||||
receiver: packet[8..12].try_into().unwrap(),
|
receiver: packet[8..12].try_into().unwrap(),
|
||||||
ephemeral: packet[12..44].try_into().unwrap(),
|
ephemeral: packet[12..44].try_into().unwrap(),
|
||||||
empty: packet[44..60].try_into().unwrap(),
|
empty: packet[44..60].try_into().unwrap(),
|
||||||
|
mac1: packet[60..76].try_into().unwrap(),
|
||||||
|
mac2: packet[76..92].try_into().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret};
|
||||||
use crate::noise::handshake::HandshakeState;
|
use crate::noise::handshake::HandshakeState;
|
||||||
use crate::noise::handshake::initiator::{handshake_init_from, handshake_init_to};
|
use crate::noise::handshake::initiator::{handshake_init_from, handshake_init_to};
|
||||||
|
use crate::noise::handshake::response::{handshake_response_from, handshake_response_to};
|
||||||
use crate::qcrypto::pki::qcrypto_dh_generate_longterm;
|
use crate::qcrypto::pki::qcrypto_dh_generate_longterm;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -13,26 +14,30 @@ fn noise_halfhandshake_test() {
|
||||||
h: [0u8; 32],
|
h: [0u8; 32],
|
||||||
ck: [0u8; 32],
|
ck: [0u8; 32],
|
||||||
e_pub_i: PublicKey::from([0u8; 32]),
|
e_pub_i: PublicKey::from([0u8; 32]),
|
||||||
|
e_pub_r: PublicKey::from([0u8; 32]),
|
||||||
s_pub_i: PublicKey::from([0u8; 32]),
|
s_pub_i: PublicKey::from([0u8; 32]),
|
||||||
s_pub_r: PublicKey::from([0u8; 32]),
|
s_pub_r: PublicKey::from([0u8; 32]),
|
||||||
e_priv_me: EphemeralSecret::new(OsRng),
|
e_priv_me: StaticSecret::new(OsRng),
|
||||||
s_priv_me: alice_keypair.0,
|
s_priv_me: alice_keypair.0,
|
||||||
s_pub_them: bob_keypair.1,
|
s_pub_them: bob_keypair.1,
|
||||||
i_i: 0,
|
i_i: 0,
|
||||||
i_r: 0,
|
i_r: 0,
|
||||||
|
q: [0u8; 32],
|
||||||
cookies: vec![],
|
cookies: vec![],
|
||||||
};
|
};
|
||||||
let mut bob_session = HandshakeState {
|
let mut bob_session = HandshakeState {
|
||||||
h: [0u8; 32],
|
h: [0u8; 32],
|
||||||
ck: [0u8; 32],
|
ck: [0u8; 32],
|
||||||
e_pub_i: PublicKey::from([0u8; 32]),
|
e_pub_i: PublicKey::from([0u8; 32]),
|
||||||
|
e_pub_r: PublicKey::from([0u8; 32]),
|
||||||
s_pub_i: PublicKey::from([0u8; 32]),
|
s_pub_i: PublicKey::from([0u8; 32]),
|
||||||
s_pub_r: PublicKey::from([0u8; 32]),
|
s_pub_r: PublicKey::from([0u8; 32]),
|
||||||
e_priv_me: EphemeralSecret::new(OsRng),
|
e_priv_me: StaticSecret::new(OsRng),
|
||||||
s_priv_me: bob_keypair.0,
|
s_priv_me: bob_keypair.0,
|
||||||
s_pub_them: alice_keypair.1,
|
s_pub_them: alice_keypair.1,
|
||||||
i_i: 0,
|
i_i: 0,
|
||||||
i_r: 0,
|
i_r: 0,
|
||||||
|
q: [0u8; 32],
|
||||||
cookies: vec![],
|
cookies: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,5 +47,53 @@ fn noise_halfhandshake_test() {
|
||||||
println!("{:?}", alice_session);
|
println!("{:?}", alice_session);
|
||||||
println!("{:?}", bob_session);
|
println!("{:?}", bob_session);
|
||||||
|
|
||||||
|
assert!(alice_session.is_eq(&bob_session));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn noise_nocookie_handshake_test() {
|
||||||
|
let alice_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
let bob_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
|
||||||
|
let mut alice_session = HandshakeState {
|
||||||
|
h: [0u8; 32],
|
||||||
|
ck: [0u8; 32],
|
||||||
|
e_pub_i: PublicKey::from([0u8; 32]),
|
||||||
|
e_pub_r: PublicKey::from([0u8; 32]),
|
||||||
|
s_pub_i: PublicKey::from([0u8; 32]),
|
||||||
|
s_pub_r: PublicKey::from([0u8; 32]),
|
||||||
|
e_priv_me: StaticSecret::new(OsRng),
|
||||||
|
s_priv_me: alice_keypair.0,
|
||||||
|
s_pub_them: bob_keypair.1,
|
||||||
|
i_i: 0,
|
||||||
|
i_r: 0,
|
||||||
|
q: [0u8; 32],
|
||||||
|
cookies: vec![],
|
||||||
|
};
|
||||||
|
let mut bob_session = HandshakeState {
|
||||||
|
h: [0u8; 32],
|
||||||
|
ck: [0u8; 32],
|
||||||
|
e_pub_i: PublicKey::from([0u8; 32]),
|
||||||
|
e_pub_r: PublicKey::from([0u8; 32]),
|
||||||
|
s_pub_i: PublicKey::from([0u8; 32]),
|
||||||
|
s_pub_r: PublicKey::from([0u8; 32]),
|
||||||
|
e_priv_me: StaticSecret::new(OsRng),
|
||||||
|
s_priv_me: bob_keypair.0,
|
||||||
|
s_pub_them: alice_keypair.1,
|
||||||
|
i_i: 0,
|
||||||
|
i_r: 0,
|
||||||
|
q: [0u8; 32],
|
||||||
|
cookies: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let handshake_init = handshake_init_to(&mut alice_session).unwrap();
|
||||||
|
handshake_init_from(&mut bob_session, handshake_init).unwrap();
|
||||||
|
|
||||||
|
let handshake_response = handshake_response_to(&mut bob_session).unwrap();
|
||||||
|
handshake_response_from(&mut alice_session, handshake_response).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", alice_session);
|
||||||
|
println!("{:?}", bob_session);
|
||||||
|
|
||||||
assert!(alice_session.is_eq(&bob_session));
|
assert!(alice_session.is_eq(&bob_session));
|
||||||
}
|
}
|
Loading…
Reference in New Issue