hightower-wireguard

Crates.iohightower-wireguard
lib.rshightower-wireguard
version0.2.1
created_at2025-10-01 18:14:54.202323+00
updated_at2025-10-11 22:32:50.365077+00
descriptionA WireGuard Noise_IK handshake implementation
homepage
repositoryhttps://github.com/chrishayen/hightower-wireguard
max_upload_size
id1863171
size225,242
Chris Hayen (chrishayen)

documentation

README

hightower-wireguard

A Rust implementation of the WireGuard cryptographic handshake protocol (Noise_IK).

What is this?

This crate implements the core cryptographic handshake protocol used by WireGuard. It provides the building blocks for establishing secure sessions between peers using the Noise_IK pattern with pre-shared key support.

What's Implemented

  • Noise_IK Handshake Protocol: Full implementation of the WireGuard handshake
    • Handshake initiation (Message Type 1)
    • Handshake response (Message Type 2)
    • Session key derivation using HKDF
    • Pre-shared key (PSK) support for post-quantum security
  • Cryptographic Primitives:
    • X25519 Diffie-Hellman key exchange
    • ChaCha20-Poly1305 AEAD encryption
    • BLAKE2s hashing
    • HKDF key derivation
  • Protocol State Management:
    • Initiator state machine
    • Responder state machine
    • Session key management
    • Peer configuration
    • Replay protection (anti-replay window)
    • Automatic session rekeying (REKEY_AFTER_TIME)
    • Keep-alive packet support
    • Endpoint roaming for mobile devices

What's NOT Implemented

This is not a complete VPN implementation. The following components are not included:

  • TUN/TAP Interface: No virtual network interface creation or management
  • Routing: No IP routing, forwarding, or allowed-IPs enforcement
  • Cookie Mechanism: No DoS protection via cookies (Message Type 3)

This library is suitable for:

  • Learning WireGuard's cryptographic protocol
  • Building custom WireGuard-based protocols
  • Testing and experimentation
  • Integration into larger VPN implementations

Installation

Add to your Cargo.toml:

[dependencies]
# For handshake protocol only
hightower-wireguard = "0.2.1"

# For transport layer (UDP Connection and Stream)
hightower-wireguard = { version = "0.2.1", features = ["transport"] }

Usage

use hightower_wireguard::protocol::{WireGuardProtocol, PeerInfo};
use hightower_wireguard::crypto::dh_generate;

// Generate keys for both peers
let (alice_private, alice_public) = dh_generate();
let (bob_private, bob_public) = dh_generate();

// Create protocol instances
let mut alice = WireGuardProtocol::new(Some(alice_private));
let mut bob = WireGuardProtocol::new(Some(bob_private));

// Configure peers
alice.add_peer(PeerInfo {
    public_key: bob_public,
    preshared_key: None,
    endpoint: None,
    allowed_ips: Vec::new(),
    persistent_keepalive: None,
});

bob.add_peer(PeerInfo {
    public_key: alice_public,
    preshared_key: None,
    endpoint: None,
    allowed_ips: Vec::new(),
    persistent_keepalive: None,
});

// Perform handshake
let initiation = alice.initiate_handshake(&bob_public)?;
let response = bob.process_initiation(&initiation)?;
let peer_key = alice.process_response(&response)?;

// Now both sides have active sessions with transport keys
let alice_session = alice.get_session(response.sender).unwrap();
let bob_session = bob.get_session(response.sender).unwrap();

// Use session keys for encryption/decryption
// alice_session.keys.send_key and alice_session.keys.recv_key

Pre-Shared Key Support

let psk = [42u8; 32]; // Your pre-shared key

alice.add_peer(PeerInfo {
    public_key: bob_public,
    preshared_key: Some(psk),
    // ... other fields
});

bob.add_peer(PeerInfo {
    public_key: alice_public,
    preshared_key: Some(psk),
    // ... other fields
});

// Handshake will now use PSK for additional security

Message Serialization

Messages can be serialized to and from wire format for network transmission:

use hightower_wireguard::messages::HandshakeInitiation;

// After creating a handshake initiation message
let initiation = alice.initiate_handshake(&bob_public)?;

// Serialize to wire format (148 bytes)
let bytes = initiation.to_bytes()?;

// Send over network...
// let socket.send_to(&bytes, peer_addr)?;

// On the receiving side, deserialize from bytes
let received = HandshakeInitiation::from_bytes(&bytes)?;
let response = bob.process_initiation(&received)?;

// Serialize the response (92 bytes)
let response_bytes = response.to_bytes()?;

The following message types support serialization:

  • HandshakeInitiation: 148 bytes
  • HandshakeResponse: 92 bytes
  • TransportData: 16 bytes + variable packet length

Transport Layer

With the transport feature enabled, you can use the high-level UDP-based transport layer:

use hightower_wireguard::crypto::dh_generate;
use hightower_wireguard::connection::Connection;

#[tokio::main]
async fn main() {
    // Generate keys
    let (alice_private, alice_public) = dh_generate();
    let (bob_private, bob_public) = dh_generate();

    // Create connections
    let alice = Connection::new("127.0.0.1:0".parse().unwrap(), alice_private)
        .await
        .unwrap();
    let bob = Connection::new("127.0.0.1:0".parse().unwrap(), bob_private)
        .await
        .unwrap();

    let alice_addr = alice.local_addr();
    let bob_addr = bob.local_addr();

    // Add peers
    alice.add_peer(bob_public, Some(bob_addr)).await.unwrap();
    bob.add_peer(alice_public, Some(alice_addr)).await.unwrap();

    // Bob listens for incoming connections
    let mut bob_incoming = bob.listen().await.unwrap();

    // Alice connects to Bob
    let mut alice_stream = alice.connect(bob_addr, bob_public).await.unwrap();

    // Bob accepts the connection
    let mut bob_stream = bob_incoming.recv().await.unwrap();

    // Send and receive data
    alice_stream.send(b"Hello from Alice!").await.unwrap();

    let received = bob_stream.recv().await.unwrap();
    println!("Bob received: {}", String::from_utf8_lossy(&received));
}

Keep-Alive Configuration

Configure persistent keep-alive to maintain NAT mappings and detect dead peers:

// Add peer with keep-alive packets sent every 25 seconds
alice.add_peer_with_keepalive(
    bob_public,
    Some(bob_addr),
    Some(25)  // send keep-alive every 25 seconds
).await.unwrap();

The transport layer provides:

  • Connection: UDP socket management, packet routing, and session management
  • Stream: Bidirectional encrypted communication channel to a peer
  • Automatic handshake handling
  • Message encryption/decryption using session keys
  • Automatic keep-alive packets (configurable per-peer)
  • Automatic session rekeying after 120 seconds (initiator only)
  • Endpoint roaming support for mobile devices
  • Session cleanup and timeout handling

Testing

Run the test suite:

# Test handshake protocol only
cargo test

# Test with transport layer
cargo test --features transport

References

Commit count: 0

cargo fmt