[noise][stack] fixup initiator to increase security and allow it to initialize HandshakeStates even when we dont know the other parties public key prior to initialization
This commit is contained in:
parent
2f98b78f85
commit
78fb32b253
|
@ -1,2 +1,37 @@
|
|||
//! A high-level implementation of the `WireGuard` protocol; does everything the kernel module does except for interfacing with the networking interfaces.
|
||||
//! Use the appropriate glue layer in `quicktap::drivers` to provide the tun/tap interface, and the mechanism in `quicktap::drivers::udp_listener` for working with the `UdpSocket`.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use x25519_dalek::PublicKey;
|
||||
use crate::noise::handshake::HandshakeState;
|
||||
|
||||
#[derive(Default)]
|
||||
/// A high-level struct for the configuration of a `WireGuard` device. This is the equivalent of `[Interface]` in wg-quick.
|
||||
pub struct QTInterface<'a> {
|
||||
peers: HashMap<PublicKey, QTPeer<'a>>
|
||||
}
|
||||
impl<'a> QTInterface<'a> {
|
||||
/// Create a new, blank `QTInterface` with no configuration
|
||||
pub fn new() -> Self {
|
||||
QTInterface {
|
||||
peers: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A high-level struct for the current state with another peer. This is the equivalent of `[Peer]` in wg-quick, with additional tunneling information added.
|
||||
pub struct QTPeer<'a> {
|
||||
handshake: Option<HandshakeState<'a>>,
|
||||
handshake_in_flight: Option<HandshakeState<'a>>,
|
||||
public_key: PublicKey
|
||||
}
|
||||
impl<'a> QTPeer<'a> {
|
||||
/// Create a new, blank `QTPeer` with no configuration
|
||||
pub fn new(key: PublicKey) -> Self {
|
||||
QTPeer {
|
||||
handshake: None,
|
||||
handshake_in_flight: None,
|
||||
public_key: key,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,9 @@
|
|||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::UdpSocket;
|
||||
use crate::device::QTInterface;
|
||||
use crate::drivers::error::DriverError;
|
||||
use crate::stack::entry::PacketEntryStage;
|
||||
use crate::stack::entry_udp::UDPPacketEntryStage;
|
||||
use crate::stack::PacketProcessingStage;
|
||||
|
||||
/// The maximum amount of bytes that can be taken up by a Handshake Initiation packet.
|
||||
|
@ -29,7 +30,7 @@ pub enum SocketRoundResult {
|
|||
/// If you're writing a frontend, this is probably not what you want. Use `qtap_socket_rounds_until_empty` instead.
|
||||
/// # Errors
|
||||
/// This function will error if a socket read fails or the processing of the packet fails. See `PacketEntryStage::process_packet()` for more details on packet processing.
|
||||
pub fn qtap_socket_round(socket: &mut UdpSocket) -> Result<(), SocketRoundResult> {
|
||||
pub fn qtap_socket_round(socket: &mut UdpSocket, interface: &QTInterface) -> Result<(), SocketRoundResult> {
|
||||
let mut packet_id = [0u8; 1];
|
||||
match socket.peek(&mut packet_id) {
|
||||
Ok(_) => (),
|
||||
|
@ -54,7 +55,7 @@ pub fn qtap_socket_round(socket: &mut UdpSocket) -> Result<(), SocketRoundResult
|
|||
|
||||
let packet = packet_buf[..read_bytes].to_vec();
|
||||
|
||||
match PacketEntryStage::process_packet(packet, &from) {
|
||||
match UDPPacketEntryStage::process_packet(packet, &from, interface) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(SocketRoundResult::GenericAnyError(e))
|
||||
}
|
||||
|
@ -65,9 +66,9 @@ pub fn qtap_socket_round(socket: &mut UdpSocket) -> Result<(), SocketRoundResult
|
|||
/// If you only want to run one update, use `qtap_socket_round` instead.
|
||||
/// # Errors
|
||||
/// See `qtap_socket_round`
|
||||
pub fn qtap_socket_rounds_until_empty(socket: &mut UdpSocket) -> Result<(), Box<dyn Error>> {
|
||||
pub fn qtap_socket_rounds_until_empty(socket: &mut UdpSocket, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
||||
loop {
|
||||
match qtap_socket_round(socket) {
|
||||
match qtap_socket_round(socket, interface) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
match e {
|
||||
|
|
|
@ -117,7 +117,7 @@ pub fn generate_handshake_init(session: &mut HandshakeState) -> Result<[u8; 148]
|
|||
Ok(msg.to_bytes(session))
|
||||
}
|
||||
|
||||
/// Parse a handshake initiator packet and encrypt it using the given session state, updating the session state with decrypted and authenticated values
|
||||
/// Parse a handshake initiator packet and decrypt it using the given session state, updating the session state with decrypted and authenticated values
|
||||
/// # Errors
|
||||
/// This function will error if decryption was unsuccessful
|
||||
/// # Panics
|
||||
|
@ -125,7 +125,6 @@ pub fn generate_handshake_init(session: &mut HandshakeState) -> Result<[u8; 148]
|
|||
#[allow(clippy::module_name_repetitions)]
|
||||
#[allow(clippy::unwrap_used)] // Only used for type conversions in known safe ways
|
||||
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);
|
||||
|
||||
let msg = HandshakeInitiatorRaw::from_bytes(packet);
|
||||
|
@ -151,14 +150,14 @@ pub fn parse_handshake_init(session: &mut HandshakeState, packet: [u8; 148]) ->
|
|||
// This unwrap is safe because the output length is a known constant with these inputs
|
||||
|
||||
|
||||
session.s_pub_i = PublicKey::from(<Vec<u8> as TryInto<[u8; 32]>>::try_into(match qcrypto_aead_decrypt(&k, 0, &msg.static_pub, &h) {
|
||||
let s_pub_i = PublicKey::from(<Vec<u8> as TryInto<[u8; 32]>>::try_into(match qcrypto_aead_decrypt(&k, 0, &msg.static_pub, &h) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(NoiseError::ChaCha20Error(e))
|
||||
}).unwrap());
|
||||
|
||||
let h = qcrypto_hash_twice(&h, &msg.static_pub);
|
||||
|
||||
let ci_k_pair = qcrypto_hkdf::<2>(&ck, qcrypto_dh_longterm(session.s_priv_me, &session.s_pub_i).as_bytes());
|
||||
let ci_k_pair = qcrypto_hkdf::<2>(&ck, qcrypto_dh_longterm(session.s_priv_me, &s_pub_i).as_bytes());
|
||||
let ck = ci_k_pair[0];
|
||||
let k = ci_k_pair[1];
|
||||
|
||||
|
@ -172,7 +171,7 @@ pub fn parse_handshake_init(session: &mut HandshakeState, packet: [u8; 148]) ->
|
|||
|
||||
// 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(), 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] };
|
||||
|
||||
if mac1 != msg.mac1 || mac2 != msg.mac2 {
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
//! The ENTRY stage of the packet processing stack. Provides a `PacketEntryStage` which implements `PacketProcessingStage`.
|
||||
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use crate::stack::{PacketProcessingStage};
|
||||
|
||||
/// The ENTRY stage of the packet processing stack
|
||||
pub struct PacketEntryStage {}
|
||||
|
||||
impl PacketProcessingStage for PacketEntryStage {
|
||||
fn process_packet(pkt: Vec<u8>, from: &SocketAddr) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//! The ENTRY stage of the packet processing stack. Provides a `PacketEntryStage` which implements `PacketProcessingStage`.
|
||||
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use crate::device::QTInterface;
|
||||
use crate::stack::{PacketProcessingStage};
|
||||
|
||||
/// The ENTRY stage of the packet processing stack for UDP packets
|
||||
pub struct UDPPacketEntryStage {}
|
||||
|
||||
impl PacketProcessingStage for UDPPacketEntryStage {
|
||||
fn process_packet(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
|
@ -26,8 +26,9 @@
|
|||
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use crate::device::QTInterface;
|
||||
|
||||
pub mod entry;
|
||||
pub mod entry_udp;
|
||||
|
||||
/// The methods and functions that need to be implemented by a stage in the packet processor
|
||||
pub trait PacketProcessingStage {
|
||||
|
@ -35,5 +36,5 @@ pub trait PacketProcessingStage {
|
|||
/// For example, if you are the CRYPT layer, and are processing a tun packet, assume that the ENTRY and ROUTING stack stages have already been completed and you are being given the result of that processing.
|
||||
/// # Errors
|
||||
/// This function will error if an error occurs
|
||||
fn process_packet(pkt: Vec<u8>, from: &SocketAddr) -> Result<(), Box<dyn Error>> where Self: Sized;
|
||||
fn process_packet(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> where Self: Sized;
|
||||
}
|
Loading…
Reference in New Issue