Note: This is a personal project, and as such, I will most likely not provide any support at all for this outside of personal use cases
Userland WireGuard library for Nintendo Switch.
| Algorithm | Implementation | Reference |
|---|---|---|
| ChaCha20 | ARM NEON (src/wg_chacha20_neon.c) |
RFC 8439 |
| Poly1305 | ARM NEON (src/wg_poly1305_neon.c) |
RFC 8439 |
| BLAKE2s | ARM NEON (src/blake2s_neon.c) |
RFC 7693 |
| X25519 ECDH | Monocypher | RFC 7748 |
Monocypher provides X25519 and is used as a reference for verifying NEON correctness in tests.
| Component | Reference |
|---|---|
| WireGuard | WireGuard Whitepaper |
| Noise IKpsk2 | Noise Protocol Framework |
typedef enum {
WG_OK = 0,
WG_ERR_INVALID_CONFIG = -1,
WG_ERR_SOCKET = -2,
WG_ERR_HANDSHAKE = -3,
WG_ERR_TIMEOUT = -4,
WG_ERR_DECRYPT = -5,
WG_ERR_NOT_CONNECTED = -6,
WG_ERR_BUFFER_TOO_SMALL = -7,
WG_ERR_THREAD = -8,
WG_ERR_ALREADY_RUNNING = -9,
} WgError;#define WG_KEY_LEN 32
#define WG_MAX_ENDPOINT 256
typedef struct {
uint8_t private_key[WG_KEY_LEN];
struct in_addr tunnel_ip;
uint8_t peer_public_key[WG_KEY_LEN];
char endpoint_host[WG_MAX_ENDPOINT];
uint16_t endpoint_port;
uint16_t keepalive_interval;
uint8_t preshared_key[WG_KEY_LEN];
int has_preshared_key;
} WgConfig;WgTunnel* wg_init(const WgConfig* config);
int wg_connect(WgTunnel* tun);
int wg_start(WgTunnel* tun);
void wg_stop(WgTunnel* tun);
void wg_close(WgTunnel* tun);/* Zero-copy recv callback. `data` lives inside `slot`.
* Return 0 -> slot is auto-released on return (simple synchronous consumers).
* Return !0 -> callback takes ownership; must later call
* wg_recv_slot_release(tun, slot) exactly once. */
typedef int (*WgRecvCallback)(void* user, WgRecvSlot* slot, const void* data, size_t len);
int wg_send(WgTunnel* tun, const void* data, size_t len);
int wg_recv(WgTunnel* tun, void* buf, size_t len, int timeout_ms);
void wg_set_recv_callback(WgTunnel* tun, WgRecvCallback cb, void* user);
void wg_recv_slot_release(WgTunnel* tun, WgRecvSlot* slot);int wg_rekey(WgTunnel* tun);
uint32_t wg_get_session_index(WgTunnel* tun);
int wg_get_ip(WgTunnel* tun, struct in_addr* addr);void wg_generate_keypair(uint8_t private_key[WG_KEY_LEN], uint8_t public_key[WG_KEY_LEN]);
int wg_key_from_base64(uint8_t key[WG_KEY_LEN], const char* base64);
int wg_key_to_base64(char* base64, size_t len, const uint8_t key[WG_KEY_LEN]);void wg_set_log_callback(void (*func)(const char* msg));For apps that expect a normal UDP socket:
typedef struct WgRelay WgRelay;
WgRelay* wg_relay_create(WgTunnel* tun, uint16_t local_port);
int wg_relay_start(WgRelay* relay);
void wg_relay_stop(WgRelay* relay);
void wg_relay_destroy(WgRelay* relay);
uint16_t wg_relay_get_port(WgRelay* relay);For apps that need full TCP/UDP connectivity over the tunnel, the lwip-relay/ module provides a relay using the lwIP TCP/IP stack.
namespace wgnx {
enum class LogLevel {
Debug,
Info,
Error
};
struct LwipRelayConfig {
std::function<void(LogLevel level, const char* message)> log_callback;
std::function<void()> thread_affinity_callback;
bool debug_logging = false;
};
class LwipRelay {
public:
explicit LwipRelay(WgTunnel* tunnel, const LwipRelayConfig& config = {});
~LwipRelay();
bool start(const std::string& tunnelIp, const std::string& targetIp);
void stop();
uint16_t startTcpRelay(uint16_t targetPort, uint16_t localPort);
uint16_t startUdpRelay(uint16_t targetPort, uint16_t localPort);
void tick();
bool isRunning() const;
};
}LwipRelay::start() installs its own zero-copy recv callback on the tunnel; no
manual wg_set_recv_callback wiring is required. The relay must be stopped (or
destroyed, which stops it) before wg_close(tun).
#include <wg_lwip_relay.hpp>
wgnx::LwipRelayConfig config;
config.log_callback = [](wgnx::LogLevel level, const char* msg) {
printf("[WG] %s\n", msg);
};
config.debug_logging = false;
wgnx::LwipRelay relay(tunnel, config);
relay.start("10.0.0.2", "10.0.0.1");
relay.startTcpRelay(9295, 9295);
relay.startUdpRelay(9296, 9296);
relay.stop();wg-tester/ provides a borealis app that tests crypto primitives, threading, and a small integration rekey test against the publicly available wireguard demo server.
./dev.sh --build-only
./dev.sh <SWITCH_IP>