[noise] refactor, actually use rfc6479 in data deencapsulation, check for handshake chain relavency
This commit is contained in:
parent
e1093cab1b
commit
45bcd98f08
|
@ -10,6 +10,7 @@
|
|||
|
||||
// This is an annoyance
|
||||
#![allow(clippy::must_use_candidate)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
pub use cidr;
|
||||
|
||||
|
|
|
@ -53,14 +53,18 @@ pub fn deencapsulate(state: &mut HandshakeState, packet: &[u8]) -> Result<Vec<u8
|
|||
return Err(NoiseError::PacketParseError(NoisePacketParseError::WrongIndex(index_me as usize, usize::from_le_bytes(packet[4..8].try_into().unwrap()))))
|
||||
}
|
||||
let counter = u64::from_le_bytes(packet[8..16].try_into().unwrap());
|
||||
if state.n_recv == counter {
|
||||
|
||||
if !state.bitfield.check_replay_window(counter) {
|
||||
// this packet is not okay! replayed or too old
|
||||
return Err(NoiseError::PacketReplayed)
|
||||
}
|
||||
// TODO: Check if this counter is okay, sliding windows and stuff. Don't feel like implementing it fully right now
|
||||
|
||||
let packet = match qcrypto_aead_decrypt(&state.t_recv, counter, &packet[16..], &[]) {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Err(NoiseError::ChaCha20Error(e))
|
||||
};
|
||||
|
||||
state.bitfield.update_replay_window(counter);
|
||||
state.n_recv = counter;
|
||||
|
||||
Ok(unpad_packet(&packet))
|
||||
|
|
|
@ -13,6 +13,10 @@ pub enum NoiseError {
|
|||
ChaCha20Error(chacha20poly1305::Error),
|
||||
/// Represents that the packet had a missing or incorrect cookie MAC.
|
||||
PacketUnauthenticated,
|
||||
/// Represents that the packet had the wrong i_i value
|
||||
UnrelatedHandshakePacket,
|
||||
/// Represents that the packet has been replayed and should be dropped
|
||||
PacketReplayed
|
||||
}
|
||||
impl Error for NoiseError {}
|
||||
impl Display for NoiseError {
|
||||
|
@ -20,7 +24,9 @@ impl Display for NoiseError {
|
|||
match &self {
|
||||
Self::PacketParseError(err) => write!(f, "{}", err),
|
||||
Self::ChaCha20Error(error) => write!(f, "Encryption error: {}", error),
|
||||
Self::PacketUnauthenticated => write!(f, "Unauthenticated packet")
|
||||
Self::PacketUnauthenticated => write!(f, "Unauthenticated packet"),
|
||||
Self::UnrelatedHandshakePacket => write!(f, "Unrelated handshake packet"),
|
||||
Self::PacketReplayed => write!(f, "Packet was replayed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,48 @@ use crate::qcrypto::hashes::{qcrypto_hash_twice, qcrypto_mac};
|
|||
use crate::qcrypto::hkdf::qcrypto_hkdf;
|
||||
use crate::qcrypto::pki::{qcrypto_dh_generate_longterm, qcrypto_dh_longterm};
|
||||
|
||||
struct HandshakeInitiatorRaw {
|
||||
sender: [u8; 4],
|
||||
ephemeral: [u8; 32],
|
||||
static_pub: [u8; 32 + 16],
|
||||
timestamp: [u8; 12 + 16],
|
||||
mac1: [u8; 16],
|
||||
mac2: [u8; 16]
|
||||
}
|
||||
impl HandshakeInitiatorRaw {
|
||||
fn to_bytes(&self, session: &HandshakeState) -> [u8; 148] {
|
||||
let mut output = [0u8; 148];
|
||||
|
||||
output[0] = 1u8;
|
||||
output[4..8].copy_from_slice(&self.sender);
|
||||
output[8..40].copy_from_slice(&self.ephemeral);
|
||||
output[40..88].copy_from_slice(&self.static_pub);
|
||||
output[88..116].copy_from_slice(&self.timestamp);
|
||||
|
||||
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &output[..116]);
|
||||
|
||||
output[116..132].copy_from_slice(&mac1);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)] // Only used for type conversions in known safe ways
|
||||
fn from_bytes(bytes: [u8; 148]) -> Self {
|
||||
Self {
|
||||
sender: bytes[4..8].try_into().unwrap(),
|
||||
ephemeral: bytes[8..40].try_into().unwrap(),
|
||||
static_pub: bytes[40..88].try_into().unwrap(),
|
||||
timestamp: bytes[88..116].try_into().unwrap(),
|
||||
mac1: bytes[116..132].try_into().unwrap(),
|
||||
mac2: bytes[132..148].try_into().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a handshake initiator packet and encrypt it using the given session state, starting a new handshake state
|
||||
/// # Errors
|
||||
/// This function will error if encryption was unsuccessful
|
||||
|
@ -18,7 +60,7 @@ use crate::qcrypto::pki::{qcrypto_dh_generate_longterm, qcrypto_dh_longterm};
|
|||
/// While containing unwraps, this function will never panic.
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[allow(clippy::unwrap_used)] // Safe because it is only used for type conversions in known safe ways
|
||||
pub fn handshake_init_to(session: &mut HandshakeState) -> Result<[u8; 148], NoiseError> {
|
||||
pub fn generate_handshake_init(session: &mut HandshakeState) -> Result<[u8; 148], NoiseError> {
|
||||
session.we_are_initiator = true;
|
||||
session.s_pub_i = PublicKey::from(session.s_priv_me);
|
||||
session.s_pub_r = session.s_pub_them;
|
||||
|
@ -75,49 +117,6 @@ pub fn handshake_init_to(session: &mut HandshakeState) -> Result<[u8; 148], Nois
|
|||
Ok(msg.to_bytes(session))
|
||||
}
|
||||
|
||||
|
||||
struct HandshakeInitiatorRaw {
|
||||
sender: [u8; 4],
|
||||
ephemeral: [u8; 32],
|
||||
static_pub: [u8; 32 + 16],
|
||||
timestamp: [u8; 12 + 16],
|
||||
mac1: [u8; 16],
|
||||
mac2: [u8; 16]
|
||||
}
|
||||
impl HandshakeInitiatorRaw {
|
||||
fn to_bytes(&self, session: &HandshakeState) -> [u8; 148] {
|
||||
let mut output = [0u8; 148];
|
||||
|
||||
output[0] = 1u8;
|
||||
output[4..8].copy_from_slice(&self.sender);
|
||||
output[8..40].copy_from_slice(&self.ephemeral);
|
||||
output[40..88].copy_from_slice(&self.static_pub);
|
||||
output[88..116].copy_from_slice(&self.timestamp);
|
||||
|
||||
let mac1: [u8; 16] = qcrypto_mac(&qcrypto_hash_twice(LABEL_MAC1.as_bytes(), session.s_pub_i.as_bytes()), &output[..116]);
|
||||
|
||||
output[116..132].copy_from_slice(&mac1);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)] // Only used for type conversions in known safe ways
|
||||
fn from_bytes(bytes: [u8; 148]) -> Self {
|
||||
Self {
|
||||
sender: bytes[4..8].try_into().unwrap(),
|
||||
ephemeral: bytes[8..40].try_into().unwrap(),
|
||||
static_pub: bytes[40..88].try_into().unwrap(),
|
||||
timestamp: bytes[88..116].try_into().unwrap(),
|
||||
mac1: bytes[116..132].try_into().unwrap(),
|
||||
mac2: bytes[132..148].try_into().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a handshake initiator packet and encrypt it using the given session state, updating the session state with decrypted and authenticated values
|
||||
/// # Errors
|
||||
/// This function will error if decryption was unsuccessful
|
||||
|
@ -125,7 +124,7 @@ impl HandshakeInitiatorRaw {
|
|||
/// While containing unwraps, this function will never panic.
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[allow(clippy::unwrap_used)] // Only used for type conversions in known safe ways
|
||||
pub fn handshake_init_from(session: &mut HandshakeState, packet: [u8; 148]) -> Result<(), NoiseError> {
|
||||
pub fn parse_handshake_init(session: &mut HandshakeState, packet: [u8; 148]) -> Result<(), NoiseError> {
|
||||
let s_pub_i = session.s_pub_them;
|
||||
let s_pub_r = PublicKey::from(session.s_priv_me);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::fmt::{Debug, Formatter};
|
|||
use rand::rngs::OsRng;
|
||||
use tai64::Tai64N;
|
||||
use x25519_dalek::{PublicKey, StaticSecret};
|
||||
use crate::noise::rfc6479::ShiftWindow;
|
||||
|
||||
use crate::qcrypto::hkdf::qcrypto_hkdf;
|
||||
use crate::qcrypto::timestamp;
|
||||
|
@ -63,7 +64,9 @@ pub struct HandshakeState<'a> {
|
|||
pub n_send: u64,
|
||||
pub n_recv: u64,
|
||||
|
||||
pub we_are_initiator: bool
|
||||
pub we_are_initiator: bool,
|
||||
|
||||
pub bitfield: ShiftWindow
|
||||
}
|
||||
impl<'a> HandshakeState<'a> {
|
||||
/// Determines if the state variables of this `HandshakeState` are the same as another
|
||||
|
@ -114,6 +117,7 @@ impl<'a> HandshakeState<'a> {
|
|||
n_send: 0,
|
||||
n_recv: 0,
|
||||
we_are_initiator: false,
|
||||
bitfield: ShiftWindow::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,55 @@ use crate::qcrypto::hkdf::qcrypto_hkdf;
|
|||
use crate::qcrypto::LABEL_MAC1;
|
||||
use crate::qcrypto::pki::{qcrypto_dh_generate_longterm, qcrypto_dh_longterm};
|
||||
|
||||
struct HandshakeResponseRaw {
|
||||
sender: [u8; 4],
|
||||
receiver: [u8; 4],
|
||||
ephemeral: [u8; 32],
|
||||
empty: [u8; 16],
|
||||
mac1: [u8; 16],
|
||||
mac2: [u8; 16]
|
||||
}
|
||||
impl HandshakeResponseRaw {
|
||||
fn to_bytes(&self, session: &mut HandshakeState) -> [u8; 92] {
|
||||
let mut output_array = [0u8; 92];
|
||||
|
||||
output_array[0] = 2u8;
|
||||
output_array[4..8].copy_from_slice(&self.sender);
|
||||
output_array[8..12].copy_from_slice(&self.receiver);
|
||||
output_array[12..44].copy_from_slice(&self.ephemeral);
|
||||
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[..60]);
|
||||
|
||||
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[..76]) } else { [0u8; 16] };
|
||||
|
||||
output_array[76..92].copy_from_slice(&mac2);
|
||||
|
||||
output_array
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)] // Only used for type conversions in known safe ways
|
||||
fn from_bytes(packet: [u8; 92]) -> Self {
|
||||
Self {
|
||||
sender: packet[4..8].try_into().unwrap(),
|
||||
receiver: packet[8..12].try_into().unwrap(),
|
||||
ephemeral: packet[12..44].try_into().unwrap(),
|
||||
empty: packet[44..60].try_into().unwrap(),
|
||||
mac1: packet[60..76].try_into().unwrap(),
|
||||
mac2: packet[76..92].try_into().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[allow(clippy::unwrap_used)] // Used for known safe type conversions only
|
||||
pub fn handshake_response_to(session: &mut HandshakeState) -> Result<[u8; 92], NoiseError> {
|
||||
pub fn generate_handshake_response(session: &mut HandshakeState) -> Result<[u8; 92], NoiseError> {
|
||||
let eph_keypair = qcrypto_dh_generate_longterm();
|
||||
|
||||
let mut msg = HandshakeResponseRaw {
|
||||
|
@ -60,7 +102,7 @@ pub fn handshake_response_to(session: &mut HandshakeState) -> Result<[u8; 92], N
|
|||
/// 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> {
|
||||
pub fn parse_handshake_response(session: &mut HandshakeState, packet: [u8; 92]) -> Result<(), NoiseError> {
|
||||
let msg = HandshakeResponseRaw::from_bytes(packet);
|
||||
|
||||
let e_pub_r = PublicKey::from(msg.ephemeral);
|
||||
|
@ -99,51 +141,13 @@ pub fn handshake_response_from(session: &mut HandshakeState, packet: [u8; 92]) -
|
|||
return Err(NoiseError::PacketUnauthenticated)
|
||||
}
|
||||
|
||||
if msg.receiver != session.i_i.to_le_bytes() {
|
||||
return Err(NoiseError::UnrelatedHandshakePacket)
|
||||
}
|
||||
|
||||
session.e_pub_r = e_pub_r;
|
||||
session.ck = ck;
|
||||
session.h = h;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct HandshakeResponseRaw {
|
||||
sender: [u8; 4],
|
||||
receiver: [u8; 4],
|
||||
ephemeral: [u8; 32],
|
||||
empty: [u8; 16],
|
||||
mac1: [u8; 16],
|
||||
mac2: [u8; 16]
|
||||
}
|
||||
impl HandshakeResponseRaw {
|
||||
fn to_bytes(&self, session: &mut HandshakeState) -> [u8; 92] {
|
||||
let mut output_array = [0u8; 92];
|
||||
|
||||
output_array[0] = 2u8;
|
||||
output_array[4..8].copy_from_slice(&self.sender);
|
||||
output_array[8..12].copy_from_slice(&self.receiver);
|
||||
output_array[12..44].copy_from_slice(&self.ephemeral);
|
||||
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[..60]);
|
||||
|
||||
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[..76]) } else { [0u8; 16] };
|
||||
|
||||
output_array[76..92].copy_from_slice(&mac2);
|
||||
|
||||
output_array
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)] // Only used for type conversions in known safe ways
|
||||
fn from_bytes(packet: [u8; 92]) -> Self {
|
||||
Self {
|
||||
sender: packet[4..8].try_into().unwrap(),
|
||||
receiver: packet[8..12].try_into().unwrap(),
|
||||
ephemeral: packet[12..44].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,8 +1,8 @@
|
|||
#![allow(clippy::unwrap_used)] // this is a test harness, we want to panic
|
||||
|
||||
use crate::noise::handshake::HandshakeState;
|
||||
use crate::noise::handshake::initiator::{handshake_init_from, handshake_init_to};
|
||||
use crate::noise::handshake::response::{handshake_response_from, handshake_response_to};
|
||||
use crate::noise::handshake::initiator::{parse_handshake_init, generate_handshake_init};
|
||||
use crate::noise::handshake::response::{parse_handshake_response, generate_handshake_response};
|
||||
use crate::qcrypto::pki::qcrypto_dh_generate_longterm;
|
||||
|
||||
#[test]
|
||||
|
@ -13,8 +13,8 @@ fn noise_halfhandshake_test() {
|
|||
let mut alice_session = HandshakeState::new(&alice_keypair.0, bob_keypair.1, None);
|
||||
let mut bob_session = HandshakeState::new(&bob_keypair.0, alice_keypair.1, None);
|
||||
|
||||
let handshake_init = handshake_init_to(&mut alice_session).unwrap();
|
||||
handshake_init_from(&mut bob_session, handshake_init).unwrap();
|
||||
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
||||
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
||||
|
||||
println!("{:?}", alice_session);
|
||||
println!("{:?}", bob_session);
|
||||
|
@ -30,11 +30,11 @@ fn noise_nocookie_handshake_test() {
|
|||
let mut alice_session = HandshakeState::new(&alice_keypair.0, bob_keypair.1, None);
|
||||
let mut bob_session = HandshakeState::new(&bob_keypair.0, alice_keypair.1, None);
|
||||
|
||||
let handshake_init = handshake_init_to(&mut alice_session).unwrap();
|
||||
handshake_init_from(&mut bob_session, handshake_init).unwrap();
|
||||
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
||||
parse_handshake_init(&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();
|
||||
let handshake_response = generate_handshake_response(&mut bob_session).unwrap();
|
||||
parse_handshake_response(&mut alice_session, handshake_response).unwrap();
|
||||
|
||||
println!("{:?}", alice_session);
|
||||
println!("{:?}", bob_session);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use crate::noise::data::{deencapsulate, encapsulate};
|
||||
use crate::noise::handshake::HandshakeState;
|
||||
use crate::noise::handshake::initiator::{handshake_init_from, handshake_init_to};
|
||||
use crate::noise::handshake::response::{handshake_response_from, handshake_response_to};
|
||||
use crate::noise::handshake::initiator::{parse_handshake_init, generate_handshake_init};
|
||||
use crate::noise::handshake::response::{parse_handshake_response, generate_handshake_response};
|
||||
use crate::qcrypto::pki::qcrypto_dh_generate_longterm;
|
||||
|
||||
#[test]
|
||||
|
@ -15,11 +15,11 @@ pub fn noise_transport_test() {
|
|||
let mut alice_session = HandshakeState::new(&alice_keypair.0, bob_keypair.1, None);
|
||||
let mut bob_session = HandshakeState::new(&bob_keypair.0, alice_keypair.1, None);
|
||||
|
||||
let handshake_init = handshake_init_to(&mut alice_session).unwrap();
|
||||
handshake_init_from(&mut bob_session, handshake_init).unwrap();
|
||||
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
||||
parse_handshake_init(&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();
|
||||
let handshake_response = generate_handshake_response(&mut bob_session).unwrap();
|
||||
parse_handshake_response(&mut alice_session, handshake_response).unwrap();
|
||||
|
||||
alice_session.derive_transport();
|
||||
bob_session.derive_transport();
|
||||
|
|
Loading…
Reference in New Issue