UDP-WG Implementation
Loading...
Searching...
No Matches
wireguard Namespace Reference

This namespace includes the WireGuard implementation,. More...

Classes

struct  config
 A WireGuard Configuration. More...
 
class  CookiePacket
 
class  InitPacket
 The initial packet sent from initiator to responder. More...
 
class  Packet
 For both security and ease of use, we want to use crypto::string as much as possible. This makes it easy to run all our cryptographic functions on the handshake and transport, but it raises an issue in that these objects are not contiguous values in memory (Or, they are, but casting it into a character array isn't going to get you the bytes). If you tried to just cast the string and send it across the wire, you'd get garbage. Instead, we need to Serialize and Expand from a collection of crypto::strings making up a packet (Whether that be the handshake packets or transport packets), so that we can work with crypto::string, but be able to serialize it down when we need to send it, and construct it back from bytes when receiving it. To unify this functionality, the Packet object contains a list of crypto::string's in a vector and will serialize the contents of it, and construct a itself from bytes. Other objects derive from this class, and specify the size of the vector and its elements, and values to access specific parts. More...
 
class  ResponsePacket
 The packet sent by the responder to the initiator during the handshake. More...
 
class  Rm
 A controlled wrapper for the secret random value used for the WireGuard cookies. More...
 
class  TransportPacket
 A WireGuard packet for sending Transport Messages. More...
 

Typedefs

typedef struct wireguard::config config
 A WireGuard Configuration.
 

Functions

void Handshake1 (crypto::keypair &ephemeral, const crypto::string &remote_pub, config &con, InitPacket &msg, const bool &init, crypto::string &C, crypto::string &H)
 The first half of the Handshake process.
 
void Handshake2 (const crypto::keypair &init_ephemeral, const crypto::string &remote_pub, config &con, ResponsePacket &msg, const bool &init, crypto::string &C, crypto::string &H)
 Complete the Handshake.
 
template<typename Q >
config Handshake (const crypto::string &remote_pub, const connection &peer, const bool &init, Q &in, Q &out, config conf={}, const bool &cookie=false)
 Perform a WireGuard Handshake.
 
void test ()
 Test the WireGuard Cryptographic Functions.
 

Variables

auto pair = crypto::DH_GENERATE()
 
const crypto::string EPSILON = 32
 
const crypto::string CONSTRUCTION = ("Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s", 32)
 
const crypto::string IDENTIFIER = ("WireGuard v1 zx2c4 Jason@zx2c4.com", 34)
 
const crypto::string LABEL_MAC1 = ("mac1----", 8)
 
const crypto::string LABEL_COOKIE = ("cookie--", 8)
 
const uint64_t REKEY_MSGS = 1152921504606846976
 
const uint64_t RJECT_MSGS = 1.844674407370954e19
 
const uint64_t REKEY_TIME = 120
 
const uint64_t RJECT_TIME = 180
 
const uint64_t REKEY_TOUT = 5
 
const uint64_t KEEPALIVE = 10
 
Rm cookie_random
 

Detailed Description

This namespace includes the WireGuard implementation,.

Remarks
This implementation was created in reference to the WireGuard Whitepaper (https://www.wireguard.com/papers/wireguard.pdf) herein referred to as the "Reference"

Typedef Documentation

◆ config

typedef struct wireguard::config wireguard::config

A WireGuard Configuration.

Remarks
For every WireGuard connection, both the server and client create a config object that contains all the information needed to talk; the server supplies this to the newly spawned WG Thread, whereas the client saves it within wireguard_server value in the Main Thread. This includes everything from timestamps to indicate a rekey, identities, send/recv, the source, and cookies.

Function Documentation

◆ Handshake()

template<typename Q >
config wireguard::Handshake ( const crypto::string & remote_pub,
const connection & peer,
const bool & init,
Q & in,
Q & out,
config conf = {},
const bool & cookie = false )

Perform a WireGuard Handshake.

Template Parameters
Thequeue ;)
Parameters
remote_pubThe remote's public key
peerThe connection to the peer.
initWhether we are the initiator.
inThe in queue
outThe out queue.
confThe wireguard configuration to built.
cookieWhether we are sending a cookie.
Remarks
This function completes the entire Handshake Process for a WireGuard connection. The flow of logic is somewhat complicated: Both initiator and responder enter this function, where the responder immediately waits for the client to provide an InitPacket. The client stepping into Handshake1, dutifully produces that Packet, adds some information to the configuration, and gets the working values of H and C. It then steps back to this function, where it sends the packet across, and then waits. The responder then receives this packet, and uses the populated InitPacket runs through Handshake1, reaching the same state as the initiator. It then steps into Handshake2, completing its part of the Handshake, producing Transport Keys that have been derived from both Static and Ephemeral of both peers, and sends the ResponsePacket across. The client then wakes up, and uses this ResponsePacket to run through Handshake2, ending with the same Transport Keys, and a completed configuration.
An aside for the template: in C++, if you have a circular dependencies and you don't want there to be one, you can hand wave it away with templates. The queue is defined in network.h, and for a while this function lived in that header, but it didn't make much sense: it'S a WireGuard handshake. So, what do we do? We make the function a template, and then each instance of the Handshake called (Spoilers, it's only called with Q = network::queue), is created by the compiler. So, we can stick Handshake here, and don't even need to explicitly provide Q in the function calls because C++ is smart enough to deduce it based on the function arguments.

◆ Handshake1()

void wireguard::Handshake1 ( crypto::keypair & ephemeral,
const crypto::string & remote_pub,
config & con,
InitPacket & msg,
const bool & init,
crypto::string & C,
crypto::string & H )

The first half of the Handshake process.

Parameters
ephemeralThe ephemeral keypairs of the peer.
remote_pubThe peer's public key.
conThe configuration that we populate for subsequent communication.
msgThe InitPacket. The initiator builds this Packet, and then sends it to the responder, who uses the pre-populated Packet for this function.
initWhether this is the initiator.
CThe chaining key value.
HThe hash result value.
Exceptions
std::runtime_errorIf the handshake fails.
Remarks
This part of the Handshake builds the InitPacket, or the information the initiator of the exchange sends over to the responder. Both run this function, but their behavior is different. The initiator runs through this function and populates an empty InitPacket msg, computing its Ephemeral Keys, and then returning the completed InitPacket, and the values it got for C and H. All this time, the responder is just sitting in wait for the initiator. Once the initiator finishes, they return from this function back to Handshake, where it will then send the InitPacket across the wire. The responsder then runs through this function (init = false), and not only performs checks to ensure that the peer is the person they're expecting, but extracts the relevant information. The way this function works such that both peers will return with the same C and H value. All this time, the initiator has been sitting, and the responder then heads into Handshake2, which is where the responder generates its own Ephemeral Keys, constructs the ResponsePacket, and finally sends it back to the initiator. The initiator then brings this packet into Handshake2, generating the required details and verifying, and the result is a set of Transport Keys, one for receiving, one for sending, that has been generated through a combination of both the peers Static and Ephemeral Keys.
This is based on the Noise Framework: https://noiseprotocol.org/noise.pdf
This may look a little daunting (I'm sure the wall of comments probably isn't helping), but Jason, the creator of WireGuard has a really nice presentation that he has given at several conventions: https://www.wireguard.com/talks/eindhoven2018-slides.pdf It helps explain more of the protocol.

◆ Handshake2()

void wireguard::Handshake2 ( const crypto::keypair & init_ephemeral,
const crypto::string & remote_pub,
config & con,
ResponsePacket & msg,
const bool & init,
crypto::string & C,
crypto::string & H )

Complete the Handshake.

Parameters
init_ephemeralThe ephemeral keys the initiator created. If this is the responder, we only know the public component.
remote_pubThe other peer's public key.
conThe WireGuard configuration we're building.
msgThe packet to send back. If we are the initiator, this is already populated. If we're the responder, we build this before returning, and then sending it to the initiator.
initWhether we are the initiator.
CThe chaining key value.
HThe hash result value.
Remarks
This function complete the Handshake we started in Handshake1, and does largely the same thing as the prior one; now, the Responder generates their Ephemeral Keys, and ties them into the chaining key and hash result, ensuring that both peer's have their Static and Ephemeral Keys tied into the eventual Transport Keys. One important difference is that the Responder's Packet is smaller than the Initiators. Why? It's to avoid amplification attacks. The Responder cannot be flooded with small InitPackets, and send a barrage of massive packets to whatever unassuming target you've pointed the server at.

◆ test()

void wireguard::test ( )

Test the WireGuard Cryptographic Functions.

Remarks
If the system has outdated OpenSSL, or the pre-compiled version doesn't cooperate well, we should detect that as soon as possible to prevent confusing bugs down the line.
This is probably excessive; OpenSSL has really good backward compatibility, and any version of glibc within the last decade will easily be able to handle our pre-compiled program.