[stack][ENTRY_UDP] process handshake initialization packets
This commit is contained in:
parent
b07895b043
commit
4e73da252d
|
@ -15,6 +15,7 @@ rand = "0.8.5"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
chacha20poly1305 = "0.10.1"
|
chacha20poly1305 = "0.10.1"
|
||||||
build-info = "0.0.29"
|
build-info = "0.0.29"
|
||||||
|
log = "0.4.17"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
tun = "0.5.4"
|
tun = "0.5.4"
|
||||||
|
|
|
@ -2,28 +2,36 @@
|
||||||
//! 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 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 std::collections::HashMap;
|
||||||
use x25519_dalek::PublicKey;
|
use x25519_dalek::{PublicKey, StaticSecret};
|
||||||
use crate::noise::handshake::HandshakeState;
|
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.
|
/// A high-level struct for the configuration of a `WireGuard` device. This is the equivalent of `[Interface]` in wg-quick.
|
||||||
pub struct QTInterface<'a> {
|
pub struct QTInterface<'a> {
|
||||||
peers: HashMap<PublicKey, QTPeer<'a>>
|
/// A list of `QTPeer`s that this interface manages
|
||||||
|
pub peers: HashMap<PublicKey, QTPeer<'a>>,
|
||||||
|
/// The private key of the interface
|
||||||
|
pub private_key: StaticSecret
|
||||||
}
|
}
|
||||||
impl<'a> QTInterface<'a> {
|
impl<'a> QTInterface<'a> {
|
||||||
/// Create a new, blank `QTInterface` with no configuration
|
/// Create a new, blank `QTInterface` with no configuration
|
||||||
pub fn new() -> Self {
|
pub fn new(private_key: StaticSecret) -> Self {
|
||||||
QTInterface {
|
QTInterface {
|
||||||
peers: HashMap::default(),
|
peers: HashMap::default(),
|
||||||
|
private_key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
/// 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> {
|
pub struct QTPeer<'a> {
|
||||||
handshake: Option<HandshakeState<'a>>,
|
/// The currently active WireGuard handshake session with this peer, if any
|
||||||
handshake_in_flight: Option<HandshakeState<'a>>,
|
pub handshake: Option<HandshakeState<'a>>,
|
||||||
public_key: PublicKey
|
/// The in-flight WireGuard handshake with this peer, if any
|
||||||
|
pub handshake_in_flight: Option<HandshakeState<'a>>,
|
||||||
|
/// The public key of this peer
|
||||||
|
pub public_key: PublicKey,
|
||||||
|
/// The optional pre-shared key with this peer. Set to all zero if there is none.
|
||||||
|
pub q: [u8; 32]
|
||||||
}
|
}
|
||||||
impl<'a> QTPeer<'a> {
|
impl<'a> QTPeer<'a> {
|
||||||
/// Create a new, blank `QTPeer` with no configuration
|
/// Create a new, blank `QTPeer` with no configuration
|
||||||
|
@ -32,6 +40,7 @@ impl<'a> QTPeer<'a> {
|
||||||
handshake: None,
|
handshake: None,
|
||||||
handshake_in_flight: None,
|
handshake_in_flight: None,
|
||||||
public_key: key,
|
public_key: key,
|
||||||
|
q: [0u8; 32]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,13 +8,16 @@ use std::fmt::{Display, Formatter};
|
||||||
pub enum DriverError {
|
pub enum DriverError {
|
||||||
/// An invalid packet type has been received on the interface, and quicktap does not know how to process it
|
/// An invalid packet type has been received on the interface, and quicktap does not know how to process it
|
||||||
/// This is raised in the PREENTRY stage of packet processing
|
/// This is raised in the PREENTRY stage of packet processing
|
||||||
InvalidPacketTypeRecvOnInterface
|
InvalidPacketTypeRecvOnInterface,
|
||||||
|
/// A correctly authenticated handshake packet was received, but we do not recognize the peer
|
||||||
|
UnrecognizedValidHandshake
|
||||||
}
|
}
|
||||||
impl Error for DriverError {}
|
impl Error for DriverError {}
|
||||||
impl Display for DriverError {
|
impl Display for DriverError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::InvalidPacketTypeRecvOnInterface => write!(f, "Invalid packet type received on interface")
|
Self::InvalidPacketTypeRecvOnInterface => write!(f, "Invalid packet type received on interface"),
|
||||||
|
Self::UnrecognizedValidHandshake => write!(f, "Handshake packet valid, but from unknown peer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,10 +27,9 @@ pub enum SocketRoundResult {
|
||||||
|
|
||||||
/// Run a socket update round on the (nonblocking!) UDP socket provided. Checks for any packets that may be present and send them off to get processed.
|
/// Run a socket update round on the (nonblocking!) UDP socket provided. Checks for any packets that may be present and send them off to get processed.
|
||||||
/// **Warning!** This only runs *one* update round - therefore only one packet will be processed if any are present.
|
/// **Warning!** This only runs *one* update round - therefore only one packet will be processed if any are present.
|
||||||
/// If you're writing a frontend, this is probably not what you want. Use `qtap_socket_rounds_until_empty` instead.
|
|
||||||
/// # Errors
|
/// # 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.
|
/// 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, interface: &QTInterface) -> Result<(), SocketRoundResult> {
|
pub fn qtap_socket_round<'a>(socket: &mut UdpSocket, interface: &'a mut QTInterface<'a>) -> Result<(), SocketRoundResult> {
|
||||||
let mut packet_id = [0u8; 1];
|
let mut packet_id = [0u8; 1];
|
||||||
match socket.peek(&mut packet_id) {
|
match socket.peek(&mut packet_id) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
|
@ -59,25 +58,4 @@ pub fn qtap_socket_round(socket: &mut UdpSocket, interface: &QTInterface) -> Res
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) => Err(SocketRoundResult::GenericAnyError(e))
|
Err(e) => Err(SocketRoundResult::GenericAnyError(e))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Run several socket update rounds on the (nonblocking!) UDP socket provided. Will continually call `qtap_socket_round` in a loop until `io::WouldBlock` is returned.
|
|
||||||
/// **Warning!** This runs as many socket rounds are needed to clear the UDP datagram queue. If there is a long queue, this function will take a very long time.
|
|
||||||
/// 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, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
|
||||||
loop {
|
|
||||||
match qtap_socket_round(socket, interface) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => {
|
|
||||||
match e {
|
|
||||||
SocketRoundResult::IoError(e) if matches!(e.kind(), io::ErrorKind::WouldBlock) => break,
|
|
||||||
SocketRoundResult::GenericAnyError(e) => return Err(e),
|
|
||||||
SocketRoundResult::IoError(e) => return Err(e.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
|
@ -27,6 +27,22 @@ pub const HANDSHAKE_INITIATOR_CHAIN_KEY_HASH: [u8; 32] = [
|
||||||
147, 232, 183, 14, 225, 156, 101, 186, 7, 158, 243,
|
147, 232, 183, 14, 225, 156, 101, 186, 7, 158, 243,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// Represents what mode the handshake state machine currently is in.
|
||||||
|
pub enum HandshakeMode {
|
||||||
|
/// No state is currently present; an init has not been sent or received.
|
||||||
|
Ready,
|
||||||
|
/// We have received an init from the other peer, and need to send the ack packet.
|
||||||
|
InitReceived,
|
||||||
|
/// We have sent an init to the other peer, and are waiting for their ack packet
|
||||||
|
InitSent,
|
||||||
|
/// We have received the ack packet from the other peer, and are ready to derive data keys and begin transport.
|
||||||
|
AckReceived,
|
||||||
|
/// We have sent the ack packet to the other peer, and are ready to derive data keys and begin transport.
|
||||||
|
AckSent,
|
||||||
|
/// We have derived data keys and are ready to send data.
|
||||||
|
Data
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a cookie we got from the other peer
|
/// Represents a cookie we got from the other peer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cookie {
|
pub struct Cookie {
|
||||||
|
@ -66,7 +82,9 @@ pub struct HandshakeState<'a> {
|
||||||
|
|
||||||
pub we_are_initiator: bool,
|
pub we_are_initiator: bool,
|
||||||
|
|
||||||
pub bitfield: ShiftWindow
|
pub bitfield: ShiftWindow,
|
||||||
|
|
||||||
|
pub mode: HandshakeMode
|
||||||
}
|
}
|
||||||
impl<'a> HandshakeState<'a> {
|
impl<'a> HandshakeState<'a> {
|
||||||
/// Determines if the state variables of this `HandshakeState` are the same as another
|
/// Determines if the state variables of this `HandshakeState` are the same as another
|
||||||
|
@ -97,7 +115,7 @@ impl<'a> HandshakeState<'a> {
|
||||||
|
|
||||||
/// Create a new handshake state representing a brand-new handshake.
|
/// Create a new handshake state representing a brand-new handshake.
|
||||||
/// This function initializes the important values with their appropriate initialization vectors, and zeroes out all other values.
|
/// This function initializes the important values with their appropriate initialization vectors, and zeroes out all other values.
|
||||||
pub fn new(private_key: &'a StaticSecret, other_pubkey: PublicKey, pre_shared_key: Option<[u8; 32]>) -> Self {
|
pub fn new(private_key: &'a StaticSecret) -> Self {
|
||||||
Self {
|
Self {
|
||||||
h: [0u8; 32],
|
h: [0u8; 32],
|
||||||
ck: [0u8; 32],
|
ck: [0u8; 32],
|
||||||
|
@ -107,17 +125,18 @@ impl<'a> HandshakeState<'a> {
|
||||||
s_pub_r: PublicKey::from([0u8; 32]),
|
s_pub_r: PublicKey::from([0u8; 32]),
|
||||||
e_priv_me: StaticSecret::new(OsRng),
|
e_priv_me: StaticSecret::new(OsRng),
|
||||||
s_priv_me: private_key,
|
s_priv_me: private_key,
|
||||||
s_pub_them: other_pubkey,
|
s_pub_them: PublicKey::from([0u8; 32]),
|
||||||
i_i: 0,
|
i_i: 0,
|
||||||
i_r: 0,
|
i_r: 0,
|
||||||
q: pre_shared_key.unwrap_or([0u8; 32]),
|
q: [0u8; 32],
|
||||||
cookies: vec![],
|
cookies: vec![],
|
||||||
t_send: [0u8; 32],
|
t_send: [0u8; 32],
|
||||||
t_recv: [0u8; 32],
|
t_recv: [0u8; 32],
|
||||||
n_send: 1,
|
n_send: 1,
|
||||||
n_recv: 1,
|
n_recv: 1,
|
||||||
we_are_initiator: false,
|
we_are_initiator: false,
|
||||||
bitfield: ShiftWindow::new()
|
bitfield: ShiftWindow::new(),
|
||||||
|
mode: HandshakeMode::Ready
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ fn noise_halfhandshake_test() {
|
||||||
let alice_keypair = qcrypto_dh_generate_longterm();
|
let alice_keypair = qcrypto_dh_generate_longterm();
|
||||||
let bob_keypair = qcrypto_dh_generate_longterm();
|
let bob_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
|
||||||
let mut alice_session = HandshakeState::new(&alice_keypair.0, bob_keypair.1, None);
|
let mut alice_session = HandshakeState::new(&alice_keypair.0);
|
||||||
let mut bob_session = HandshakeState::new(&bob_keypair.0, alice_keypair.1, None);
|
alice_session.s_pub_them = bob_keypair.1;
|
||||||
|
let mut bob_session = HandshakeState::new(&bob_keypair.0);
|
||||||
|
bob_session.s_pub_them = alice_keypair.1;
|
||||||
|
|
||||||
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
||||||
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
||||||
|
@ -27,8 +29,10 @@ fn noise_nocookie_handshake_test() {
|
||||||
let alice_keypair = qcrypto_dh_generate_longterm();
|
let alice_keypair = qcrypto_dh_generate_longterm();
|
||||||
let bob_keypair = qcrypto_dh_generate_longterm();
|
let bob_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
|
||||||
let mut alice_session = HandshakeState::new(&alice_keypair.0, bob_keypair.1, None);
|
let mut alice_session = HandshakeState::new(&alice_keypair.0);
|
||||||
let mut bob_session = HandshakeState::new(&bob_keypair.0, alice_keypair.1, None);
|
alice_session.s_pub_them = bob_keypair.1;
|
||||||
|
let mut bob_session = HandshakeState::new(&bob_keypair.0);
|
||||||
|
bob_session.s_pub_them = alice_keypair.1;
|
||||||
|
|
||||||
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
||||||
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
||||||
|
|
|
@ -12,8 +12,10 @@ pub fn noise_transport_test() {
|
||||||
let alice_keypair = qcrypto_dh_generate_longterm();
|
let alice_keypair = qcrypto_dh_generate_longterm();
|
||||||
let bob_keypair = qcrypto_dh_generate_longterm();
|
let bob_keypair = qcrypto_dh_generate_longterm();
|
||||||
|
|
||||||
let mut alice_session = HandshakeState::new(&alice_keypair.0, bob_keypair.1, None);
|
let mut alice_session = HandshakeState::new(&alice_keypair.0);
|
||||||
let mut bob_session = HandshakeState::new(&bob_keypair.0, alice_keypair.1, None);
|
alice_session.s_pub_them = bob_keypair.1;
|
||||||
|
let mut bob_session = HandshakeState::new(&bob_keypair.0);
|
||||||
|
bob_session.s_pub_them = alice_keypair.1;
|
||||||
|
|
||||||
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
let handshake_init = generate_handshake_init(&mut alice_session).unwrap();
|
||||||
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
parse_handshake_init(&mut bob_session, handshake_init).unwrap();
|
||||||
|
|
|
@ -2,38 +2,59 @@
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use log::{warn};
|
||||||
use crate::device::QTInterface;
|
use crate::device::QTInterface;
|
||||||
use crate::drivers::error::DriverError;
|
use crate::drivers::error::DriverError;
|
||||||
|
use crate::noise::handshake::{HandshakeMode, HandshakeState};
|
||||||
|
use crate::noise::handshake::initiator::parse_handshake_init;
|
||||||
use crate::stack::{PacketProcessingStage};
|
use crate::stack::{PacketProcessingStage};
|
||||||
|
|
||||||
/// The ENTRY stage of the packet processing stack for UDP packets
|
/// The ENTRY stage of the packet processing stack for UDP packets
|
||||||
pub struct UDPPacketEntryStage {}
|
pub struct UDPPacketEntryStage {}
|
||||||
|
|
||||||
impl PacketProcessingStage for UDPPacketEntryStage {
|
impl PacketProcessingStage for UDPPacketEntryStage {
|
||||||
fn process_packet(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
#[allow(clippy::unwrap_used)] // the arrays are fixed size, with known size values
|
||||||
|
fn process_packet<'a>(pkt: Vec<u8>, from: &SocketAddr, interface: &'a mut QTInterface<'a>) -> Result<(), Box<dyn Error>> {
|
||||||
// packet types are determined by the first byte of the packet
|
// packet types are determined by the first byte of the packet
|
||||||
// if it is not a known packet type, return an error and drop the packet
|
// if it is not a known packet type, return an error and drop the packet
|
||||||
match pkt.first().ok_or(DriverError::InvalidPacketTypeRecvOnInterface)? {
|
match pkt.first().ok_or(DriverError::InvalidPacketTypeRecvOnInterface)? {
|
||||||
1 => Self::handle_new_handshake(pkt, from, interface),
|
1 => Self::handle_new_handshake(pkt.try_into().unwrap(), from, interface),
|
||||||
2 => Self::handle_handshake_response(pkt, from, interface),
|
2 => Self::handle_handshake_response(pkt.try_into().unwrap(), from, interface),
|
||||||
3 => Self::handle_handshake_cookie_reply(pkt, from, interface),
|
3 => Self::handle_handshake_cookie_reply(pkt.try_into().unwrap(), from, interface),
|
||||||
4 => Self::handle_data_packet(pkt, from, interface),
|
4 => Self::handle_data_packet(pkt.try_into().unwrap(), from, interface),
|
||||||
_ => Err(DriverError::InvalidPacketTypeRecvOnInterface.into())
|
_ => Err(DriverError::InvalidPacketTypeRecvOnInterface.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UDPPacketEntryStage {
|
impl UDPPacketEntryStage {
|
||||||
fn handle_new_handshake(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
fn handle_new_handshake<'a>(pkt: [u8; 148], from: &SocketAddr, interface: &'a mut QTInterface<'a>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut new_state = HandshakeState::new(&interface.private_key);
|
||||||
|
parse_handshake_init(&mut new_state, pkt)?; // we have initialized the handshake
|
||||||
|
// now we need to update keying values, and possibly reject the handshake if the peer is unrecognized
|
||||||
|
if !interface.peers.contains_key(&new_state.s_pub_them) {
|
||||||
|
// this peer is unrecognized. ignore it and drop the packet
|
||||||
|
warn!("[stack/ENTRY_UDP] stack received handshake initialization packet from unrecognized peer {:?}", new_state.s_pub_them);
|
||||||
|
return Err(DriverError::UnrecognizedValidHandshake.into())
|
||||||
|
}
|
||||||
|
let peer = interface.peers.get_mut(&new_state.s_pub_them).ok_or(DriverError::UnrecognizedValidHandshake)?;
|
||||||
|
// update keying
|
||||||
|
new_state.q = peer.q;
|
||||||
|
|
||||||
|
// update state machine
|
||||||
|
new_state.mode = HandshakeMode::InitReceived;
|
||||||
|
|
||||||
|
peer.handshake_in_flight = Some(new_state);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn handle_handshake_response(pkt: Vec<u8>, from: &SocketAddr, interface: &mut QTInterface) -> Result<(), Box<dyn Error>> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
fn handle_handshake_response(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
fn handle_handshake_cookie_reply(pkt: Vec<u8>, from: &SocketAddr, interface: &mut QTInterface) -> Result<(), Box<dyn Error>> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
fn handle_handshake_cookie_reply(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
fn handle_data_packet(pkt: Vec<u8>, from: &SocketAddr, interface: &mut QTInterface) -> Result<(), Box<dyn Error>> {
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
fn handle_data_packet(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> {
|
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -36,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.
|
/// 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
|
/// # Errors
|
||||||
/// This function will error if an error occurs
|
/// This function will error if an error occurs
|
||||||
fn process_packet(pkt: Vec<u8>, from: &SocketAddr, interface: &QTInterface) -> Result<(), Box<dyn Error>> where Self: Sized;
|
fn process_packet<'a>(pkt: Vec<u8>, from: &SocketAddr, interface: &'a mut QTInterface<'a>) -> Result<(), Box<dyn Error>> where Self: Sized;
|
||||||
}
|
}
|
Loading…
Reference in New Issue