diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..4343ea95a0a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# GPL-2.0, not used on FreeBSD: +Makefile +completion +fuzz/ +ipc-uapi-windows.h +ipc-windows.h +netlink.h +uapi/ +wg-quick/ +wincompat/ + +# License OK, but not needed for FreeBSD +ipc-linux.h +ipc-openbsd.h +man/wg-quick.8 +systemd/ diff --git a/config.c b/config.c new file mode 100644 index 000000000000..81ccb479c367 --- /dev/null +++ b/config.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "containers.h" +#include "ipc.h" +#include "encoding.h" +#include "ctype.h" + +#define COMMENT_CHAR '#' + +static const char *get_value(const char *line, const char *key) +{ + size_t linelen = strlen(line); + size_t keylen = strlen(key); + + if (keylen >= linelen) + return NULL; + + if (strncasecmp(line, key, keylen)) + return NULL; + + return line + keylen; +} + +static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value) +{ + int ret; + struct addrinfo *resolved; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, + .ai_flags = AI_PASSIVE + }; + + if (!strlen(value)) { + fprintf(stderr, "Unable to parse empty port\n"); + return false; + } + + ret = getaddrinfo(NULL, value, &hints, &resolved); + if (ret) { + fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value); + return false; + } + + ret = -1; + if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) { + *port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port); + ret = 0; + } else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) { + *port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port); + ret = 0; + } else + fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value); + + freeaddrinfo(resolved); + if (!ret) + *flags |= WGDEVICE_HAS_LISTEN_PORT; + return ret == 0; +} + +static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value) +{ + unsigned long ret; + char *end; + int base = 10; + + if (!strcasecmp(value, "off")) { + *fwmark = 0; + *flags |= WGDEVICE_HAS_FWMARK; + return true; + } + + if (!char_is_digit(value[0])) + goto err; + + if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') + base = 16; + + ret = strtoul(value, &end, base); + if (*end || ret > UINT32_MAX) + goto err; + + *fwmark = ret; + *flags |= WGDEVICE_HAS_FWMARK; + return true; +err: + fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value); + return false; +} + +static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value) +{ + if (!key_from_base64(key, value)) { + fprintf(stderr, "Key is not the correct length or format: `%s'\n", value); + memset(key, 0, WG_KEY_LEN); + return false; + } + return true; +} + +static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path) +{ + FILE *f; + int c; + char dst[WG_KEY_LEN_BASE64]; + bool ret = false; + + f = fopen(path, "r"); + if (!f) { + perror("fopen"); + return false; + } + + if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) { + /* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */ + if (!ferror(f) && feof(f) && !ftell(f)) { + memset(key, 0, WG_KEY_LEN); + ret = true; + goto out; + } + + fprintf(stderr, "Invalid length key in key file\n"); + goto out; + } + dst[WG_KEY_LEN_BASE64 - 1] = '\0'; + + while ((c = getc(f)) != EOF) { + if (!char_is_space(c)) { + fprintf(stderr, "Found trailing character in key file: `%c'\n", c); + goto out; + } + } + if (ferror(f) && errno) { + perror("getc"); + goto out; + } + ret = parse_key(key, dst); + +out: + fclose(f); + return ret; +} + +static inline bool parse_ip(struct wgallowedip *allowedip, const char *value) +{ + allowedip->family = AF_UNSPEC; + if (strchr(value, ':')) { + if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1) + allowedip->family = AF_INET6; + } else { + if (inet_pton(AF_INET, value, &allowedip->ip4) == 1) + allowedip->family = AF_INET; + } + if (allowedip->family == AF_UNSPEC) { + fprintf(stderr, "Unable to parse IP address: `%s'\n", value); + return false; + } + return true; +} + +static inline int parse_dns_retries(void) +{ + unsigned long ret; + char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end; + + if (!retries) + return 15; + if (!strcmp(retries, "infinity")) + return -1; + + ret = strtoul(retries, &end, 10); + if (*end || ret > INT_MAX) { + fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries); + exit(1); + } + return (int)ret; +} + +static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value) +{ + char *mutable = strdup(value); + char *begin, *end; + int ret, retries = parse_dns_retries(); + struct addrinfo *resolved; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP + }; + if (!mutable) { + perror("strdup"); + return false; + } + if (!strlen(value)) { + free(mutable); + fprintf(stderr, "Unable to parse empty endpoint\n"); + return false; + } + if (mutable[0] == '[') { + begin = &mutable[1]; + end = strchr(mutable, ']'); + if (!end) { + free(mutable); + fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value); + return false; + } + *end++ = '\0'; + if (*end++ != ':' || !*end) { + free(mutable); + fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value); + return false; + } + } else { + begin = mutable; + end = strrchr(mutable, ':'); + if (!end || !*(end + 1)) { + free(mutable); + fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value); + return false; + } + *end++ = '\0'; + } + + #define min(a, b) ((a) < (b) ? (a) : (b)) + for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) { + ret = getaddrinfo(begin, end, &hints, &resolved); + if (!ret) + break; + /* The set of return codes that are "permanent failures". All other possibilities are potentially transient. + * + * This is according to https://sourceware.org/glibc/wiki/NameResolver which states: + * "From the perspective of the application that calls getaddrinfo() it perhaps + * doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all + * permanent failure codes and the causes are all permanent failures in the + * sense that there is no point in retrying later." + * + * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional. + */ + if (ret == EAI_NONAME || ret == EAI_FAIL || + #ifdef EAI_NODATA + ret == EAI_NODATA || + #endif + (retries >= 0 && !retries--)) { + free(mutable); + fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value); + return false; + } + fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0); + usleep(timeout); + } + + if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) || + (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) + memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen); + else { + freeaddrinfo(resolved); + free(mutable); + fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value); + return false; + } + freeaddrinfo(resolved); + free(mutable); + return true; +} + +static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value) +{ + unsigned long ret; + char *end; + + if (!strcasecmp(value, "off")) { + *interval = 0; + *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; + return true; + } + + if (!char_is_digit(value[0])) + goto err; + + ret = strtoul(value, &end, 10); + if (*end || ret > 65535) + goto err; + + *interval = (uint16_t)ret; + *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; + return true; +err: + fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value); + return false; +} + +static bool validate_netmask(struct wgallowedip *allowedip) +{ + uint32_t *ip; + int last; + + switch (allowedip->family) { + case AF_INET: + last = 0; + ip = (uint32_t *)&allowedip->ip4; + break; + case AF_INET6: + last = 3; + ip = (uint32_t *)&allowedip->ip6; + break; + default: + return true; /* We don't know how to validate it, so say 'okay'. */ + } + + for (int i = last; i >= 0; --i) { + uint32_t mask = ~0; + + if (allowedip->cidr >= 32 * (i + 1)) + break; + if (allowedip->cidr > 32 * i) + mask >>= (allowedip->cidr - 32 * i); + if (ntohl(ip[i]) & mask) + return false; + } + + return true; +} + +static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value) +{ + struct wgallowedip *allowedip = *last_allowedip, *new_allowedip; + char *mask, *mutable = strdup(value), *sep, *saved_entry; + + if (!mutable) { + perror("strdup"); + return false; + } + peer->flags |= WGPEER_REPLACE_ALLOWEDIPS; + if (!strlen(value)) { + free(mutable); + return true; + } + sep = mutable; + while ((mask = strsep(&sep, ","))) { + unsigned long cidr; + char *end, *ip; + + saved_entry = strdup(mask); + ip = strsep(&mask, "/"); + + new_allowedip = calloc(1, sizeof(*new_allowedip)); + if (!new_allowedip) { + perror("calloc"); + free(saved_entry); + free(mutable); + return false; + } + + if (!parse_ip(new_allowedip, ip)) { + free(new_allowedip); + free(saved_entry); + free(mutable); + return false; + } + + if (mask) { + if (!char_is_digit(mask[0])) + goto err; + cidr = strtoul(mask, &end, 10); + if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6)) + goto err; + } else if (new_allowedip->family == AF_INET) + cidr = 32; + else if (new_allowedip->family == AF_INET6) + cidr = 128; + else + goto err; + new_allowedip->cidr = cidr; + + if (!validate_netmask(new_allowedip)) + fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask); + + if (allowedip) + allowedip->next_allowedip = new_allowedip; + else + peer->first_allowedip = new_allowedip; + allowedip = new_allowedip; + free(saved_entry); + } + free(mutable); + *last_allowedip = allowedip; + return true; + +err: + free(new_allowedip); + free(mutable); + fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry); + free(saved_entry); + return false; +} + +static bool process_line(struct config_ctx *ctx, const char *line) +{ + const char *value; + bool ret = true; + + if (!strcasecmp(line, "[Interface]")) { + ctx->is_peer_section = false; + ctx->is_device_section = true; + return true; + } + if (!strcasecmp(line, "[Peer]")) { + struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer)); + + if (!new_peer) { + perror("calloc"); + return false; + } + ctx->last_allowedip = NULL; + if (ctx->last_peer) + ctx->last_peer->next_peer = new_peer; + else + ctx->device->first_peer = new_peer; + ctx->last_peer = new_peer; + ctx->is_peer_section = true; + ctx->is_device_section = false; + ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS; + return true; + } + +#define key_match(key) (value = get_value(line, key "=")) + + if (ctx->is_device_section) { + if (key_match("ListenPort")) + ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value); + else if (key_match("FwMark")) + ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value); + else if (key_match("PrivateKey")) { + ret = parse_key(ctx->device->private_key, value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY; + } else + goto error; + } else if (ctx->is_peer_section) { + if (key_match("Endpoint")) + ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value); + else if (key_match("PublicKey")) { + ret = parse_key(ctx->last_peer->public_key, value); + if (ret) + ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY; + } else if (key_match("AllowedIPs")) + ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value); + else if (key_match("PersistentKeepalive")) + ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value); + else if (key_match("PresharedKey")) { + ret = parse_key(ctx->last_peer->preshared_key, value); + if (ret) + ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY; + } else + goto error; + } else + goto error; + return ret; + +#undef key_match + +error: + fprintf(stderr, "Line unrecognized: `%s'\n", line); + return false; +} + +bool config_read_line(struct config_ctx *ctx, const char *input) +{ + size_t len, cleaned_len = 0; + char *line, *comment; + bool ret = true; + + /* This is what strchrnul is for, but that isn't portable. */ + comment = strchr(input, COMMENT_CHAR); + if (comment) + len = comment - input; + else + len = strlen(input); + + line = calloc(len + 1, sizeof(char)); + if (!line) { + perror("calloc"); + ret = false; + goto out; + } + + for (size_t i = 0; i < len; ++i) { + if (!char_is_space(input[i])) + line[cleaned_len++] = input[i]; + } + if (!cleaned_len) + goto out; + ret = process_line(ctx, line); +out: + free(line); + if (!ret) + free_wgdevice(ctx->device); + return ret; +} + +bool config_read_init(struct config_ctx *ctx, bool append) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->device = calloc(1, sizeof(*ctx->device)); + if (!ctx->device) { + perror("calloc"); + return false; + } + if (!append) + ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT; + return true; +} + +struct wgdevice *config_read_finish(struct config_ctx *ctx) +{ + struct wgpeer *peer; + + for_each_wgpeer(ctx->device, peer) { + if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) { + fprintf(stderr, "A peer is missing a public key\n"); + goto err; + } + } + return ctx->device; +err: + free_wgdevice(ctx->device); + return NULL; +} + +static char *strip_spaces(const char *in) +{ + char *out; + size_t t, l, i; + + t = strlen(in); + out = calloc(t + 1, sizeof(char)); + if (!out) { + perror("calloc"); + return NULL; + } + for (i = 0, l = 0; i < t; ++i) { + if (!char_is_space(in[i])) + out[l++] = in[i]; + } + return out; +} + +struct wgdevice *config_read_cmd(const char *argv[], int argc) +{ + struct wgdevice *device = calloc(1, sizeof(*device)); + struct wgpeer *peer = NULL; + struct wgallowedip *allowedip = NULL; + + if (!device) { + perror("calloc"); + return false; + } + while (argc > 0) { + if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) { + if (!parse_port(&device->listen_port, &device->flags, argv[1])) + goto error; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) { + if (!parse_fwmark(&device->fwmark, &device->flags, argv[1])) + goto error; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) { + if (!parse_keyfile(device->private_key, argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_PRIVATE_KEY; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "peer") && argc >= 2) { + struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); + + allowedip = NULL; + if (!new_peer) { + perror("calloc"); + goto error; + } + if (peer) + peer->next_peer = new_peer; + else + device->first_peer = new_peer; + peer = new_peer; + if (!parse_key(peer->public_key, argv[1])) + goto error; + peer->flags |= WGPEER_HAS_PUBLIC_KEY; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) { + peer->flags |= WGPEER_REMOVE_ME; + argv += 1; + argc -= 1; + } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) { + if (!parse_endpoint(&peer->endpoint.addr, argv[1])) + goto error; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) { + char *line = strip_spaces(argv[1]); + + if (!line) + goto error; + if (!parse_allowedips(peer, &allowedip, line)) { + free(line); + goto error; + } + free(line); + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) { + if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1])) + goto error; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) { + if (!parse_keyfile(peer->preshared_key, argv[1])) + goto error; + peer->flags |= WGPEER_HAS_PRESHARED_KEY; + argv += 2; + argc -= 2; + } else { + fprintf(stderr, "Invalid argument: %s\n", argv[0]); + goto error; + } + } + return device; +error: + free_wgdevice(device); + return false; +} diff --git a/config.h b/config.h new file mode 100644 index 000000000000..443cf2147446 --- /dev/null +++ b/config.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include + +struct wgdevice; +struct wgpeer; +struct wgallowedip; + +struct config_ctx { + struct wgdevice *device; + struct wgpeer *last_peer; + struct wgallowedip *last_allowedip; + bool is_peer_section, is_device_section; +}; + +struct wgdevice *config_read_cmd(const char *argv[], int argc); +bool config_read_init(struct config_ctx *ctx, bool append); +bool config_read_line(struct config_ctx *ctx, const char *line); +struct wgdevice *config_read_finish(struct config_ctx *ctx); + +#endif diff --git a/containers.h b/containers.h new file mode 100644 index 000000000000..a82e8ddee46a --- /dev/null +++ b/containers.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef CONTAINERS_H +#define CONTAINERS_H + +#include +#include +#include +#include +#include +#include +#if defined(__linux__) +#include +#elif defined(__OpenBSD__) +#include +#endif + +#ifndef WG_KEY_LEN +#define WG_KEY_LEN 32 +#endif + +/* Cross platform __kernel_timespec */ +struct timespec64 { + int64_t tv_sec; + int64_t tv_nsec; +}; + +struct wgallowedip { + uint16_t family; + union { + struct in_addr ip4; + struct in6_addr ip6; + }; + uint8_t cidr; + struct wgallowedip *next_allowedip; +}; + +enum { + WGPEER_REMOVE_ME = 1U << 0, + WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, + WGPEER_HAS_PUBLIC_KEY = 1U << 2, + WGPEER_HAS_PRESHARED_KEY = 1U << 3, + WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4 +}; + +struct wgpeer { + uint32_t flags; + + uint8_t public_key[WG_KEY_LEN]; + uint8_t preshared_key[WG_KEY_LEN]; + + union { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } endpoint; + + struct timespec64 last_handshake_time; + uint64_t rx_bytes, tx_bytes; + uint16_t persistent_keepalive_interval; + + struct wgallowedip *first_allowedip, *last_allowedip; + struct wgpeer *next_peer; +}; + +enum { + WGDEVICE_REPLACE_PEERS = 1U << 0, + WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, + WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, + WGDEVICE_HAS_LISTEN_PORT = 1U << 3, + WGDEVICE_HAS_FWMARK = 1U << 4 +}; + +struct wgdevice { + char name[IFNAMSIZ]; + uint32_t ifindex; + + uint32_t flags; + + uint8_t public_key[WG_KEY_LEN]; + uint8_t private_key[WG_KEY_LEN]; + + uint32_t fwmark; + uint16_t listen_port; + + struct wgpeer *first_peer, *last_peer; +}; + +#define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer) +#define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip) + +static inline void free_wgdevice(struct wgdevice *dev) +{ + if (!dev) + return; + for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) { + for (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL) + free(allowedip); + free(peer); + } + free(dev); +} + +#endif diff --git a/ctype.h b/ctype.h new file mode 100644 index 000000000000..7c9942c29265 --- /dev/null +++ b/ctype.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + * + * Specialized constant-time ctype.h reimplementations that aren't locale-specific. + */ + +#ifndef CTYPE_H +#define CTYPE_H + +#include + +static inline bool char_is_space(int c) +{ + unsigned char d = c - 9; + return (0x80001FU >> (d & 31)) & (1U >> (d >> 5)); +} + +static inline bool char_is_digit(int c) +{ + return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1); +} + +static inline bool char_is_alpha(int c) +{ + return (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1); +} + +#endif diff --git a/curve25519-fiat32.h b/curve25519-fiat32.h new file mode 100644 index 000000000000..66f3309c8d88 --- /dev/null +++ b/curve25519-fiat32.h @@ -0,0 +1,860 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2016 The fiat-crypto Authors. + * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. + * + * This is a machine-generated formally verified implementation of Curve25519 + * ECDH from: . Though originally + * machine generated, it has been tweaked to be suitable for use in the kernel. + * It is optimized for 32-bit machines and machines that cannot work efficiently + * with 128-bit integer types. + */ + +/* fe means field element. Here the field is \Z/(2^255-19). An element t, + * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 + * t[3]+2^102 t[4]+...+2^230 t[9]. + * fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc. + * Multiplication and carrying produce fe from fe_loose. + */ +typedef struct fe { u32 v[10]; } fe; + +/* fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc + * Addition and subtraction produce fe_loose from (fe, fe). + */ +typedef struct fe_loose { u32 v[10]; } fe_loose; + +static __always_inline void fe_frombytes_impl(u32 h[10], const u8 *s) +{ + /* Ignores top bit of s. */ + u32 a0 = get_unaligned_le32(s); + u32 a1 = get_unaligned_le32(s+4); + u32 a2 = get_unaligned_le32(s+8); + u32 a3 = get_unaligned_le32(s+12); + u32 a4 = get_unaligned_le32(s+16); + u32 a5 = get_unaligned_le32(s+20); + u32 a6 = get_unaligned_le32(s+24); + u32 a7 = get_unaligned_le32(s+28); + h[0] = a0&((1<<26)-1); /* 26 used, 32-26 left. 26 */ + h[1] = (a0>>26) | ((a1&((1<<19)-1))<< 6); /* (32-26) + 19 = 6+19 = 25 */ + h[2] = (a1>>19) | ((a2&((1<<13)-1))<<13); /* (32-19) + 13 = 13+13 = 26 */ + h[3] = (a2>>13) | ((a3&((1<< 6)-1))<<19); /* (32-13) + 6 = 19+ 6 = 25 */ + h[4] = (a3>> 6); /* (32- 6) = 26 */ + h[5] = a4&((1<<25)-1); /* 25 */ + h[6] = (a4>>25) | ((a5&((1<<19)-1))<< 7); /* (32-25) + 19 = 7+19 = 26 */ + h[7] = (a5>>19) | ((a6&((1<<12)-1))<<13); /* (32-19) + 12 = 13+12 = 25 */ + h[8] = (a6>>12) | ((a7&((1<< 6)-1))<<20); /* (32-12) + 6 = 20+ 6 = 26 */ + h[9] = (a7>> 6)&((1<<25)-1); /* 25 */ +} + +static __always_inline void fe_frombytes(fe *h, const u8 *s) +{ + fe_frombytes_impl(h->v, s); +} + +static __always_inline u8 /*bool*/ +addcarryx_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low) +{ + /* This function extracts 25 bits of result and 1 bit of carry + * (26 total), so a 32-bit intermediate is sufficient. + */ + u32 x = a + b + c; + *low = x & ((1 << 25) - 1); + return (x >> 25) & 1; +} + +static __always_inline u8 /*bool*/ +addcarryx_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low) +{ + /* This function extracts 26 bits of result and 1 bit of carry + * (27 total), so a 32-bit intermediate is sufficient. + */ + u32 x = a + b + c; + *low = x & ((1 << 26) - 1); + return (x >> 26) & 1; +} + +static __always_inline u8 /*bool*/ +subborrow_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low) +{ + /* This function extracts 25 bits of result and 1 bit of borrow + * (26 total), so a 32-bit intermediate is sufficient. + */ + u32 x = a - b - c; + *low = x & ((1 << 25) - 1); + return x >> 31; +} + +static __always_inline u8 /*bool*/ +subborrow_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low) +{ + /* This function extracts 26 bits of result and 1 bit of borrow + *(27 total), so a 32-bit intermediate is sufficient. + */ + u32 x = a - b - c; + *low = x & ((1 << 26) - 1); + return x >> 31; +} + +static __always_inline u32 cmovznz32(u32 t, u32 z, u32 nz) +{ + t = -!!t; /* all set if nonzero, 0 if 0 */ + return (t&nz) | ((~t)&z); +} + +static __always_inline void fe_freeze(u32 out[10], const u32 in1[10]) +{ + { const u32 x17 = in1[9]; + { const u32 x18 = in1[8]; + { const u32 x16 = in1[7]; + { const u32 x14 = in1[6]; + { const u32 x12 = in1[5]; + { const u32 x10 = in1[4]; + { const u32 x8 = in1[3]; + { const u32 x6 = in1[2]; + { const u32 x4 = in1[1]; + { const u32 x2 = in1[0]; + { u32 x20; u8/*bool*/ x21 = subborrow_u26(0x0, x2, 0x3ffffed, &x20); + { u32 x23; u8/*bool*/ x24 = subborrow_u25(x21, x4, 0x1ffffff, &x23); + { u32 x26; u8/*bool*/ x27 = subborrow_u26(x24, x6, 0x3ffffff, &x26); + { u32 x29; u8/*bool*/ x30 = subborrow_u25(x27, x8, 0x1ffffff, &x29); + { u32 x32; u8/*bool*/ x33 = subborrow_u26(x30, x10, 0x3ffffff, &x32); + { u32 x35; u8/*bool*/ x36 = subborrow_u25(x33, x12, 0x1ffffff, &x35); + { u32 x38; u8/*bool*/ x39 = subborrow_u26(x36, x14, 0x3ffffff, &x38); + { u32 x41; u8/*bool*/ x42 = subborrow_u25(x39, x16, 0x1ffffff, &x41); + { u32 x44; u8/*bool*/ x45 = subborrow_u26(x42, x18, 0x3ffffff, &x44); + { u32 x47; u8/*bool*/ x48 = subborrow_u25(x45, x17, 0x1ffffff, &x47); + { u32 x49 = cmovznz32(x48, 0x0, 0xffffffff); + { u32 x50 = (x49 & 0x3ffffed); + { u32 x52; u8/*bool*/ x53 = addcarryx_u26(0x0, x20, x50, &x52); + { u32 x54 = (x49 & 0x1ffffff); + { u32 x56; u8/*bool*/ x57 = addcarryx_u25(x53, x23, x54, &x56); + { u32 x58 = (x49 & 0x3ffffff); + { u32 x60; u8/*bool*/ x61 = addcarryx_u26(x57, x26, x58, &x60); + { u32 x62 = (x49 & 0x1ffffff); + { u32 x64; u8/*bool*/ x65 = addcarryx_u25(x61, x29, x62, &x64); + { u32 x66 = (x49 & 0x3ffffff); + { u32 x68; u8/*bool*/ x69 = addcarryx_u26(x65, x32, x66, &x68); + { u32 x70 = (x49 & 0x1ffffff); + { u32 x72; u8/*bool*/ x73 = addcarryx_u25(x69, x35, x70, &x72); + { u32 x74 = (x49 & 0x3ffffff); + { u32 x76; u8/*bool*/ x77 = addcarryx_u26(x73, x38, x74, &x76); + { u32 x78 = (x49 & 0x1ffffff); + { u32 x80; u8/*bool*/ x81 = addcarryx_u25(x77, x41, x78, &x80); + { u32 x82 = (x49 & 0x3ffffff); + { u32 x84; u8/*bool*/ x85 = addcarryx_u26(x81, x44, x82, &x84); + { u32 x86 = (x49 & 0x1ffffff); + { u32 x88; addcarryx_u25(x85, x47, x86, &x88); + out[0] = x52; + out[1] = x56; + out[2] = x60; + out[3] = x64; + out[4] = x68; + out[5] = x72; + out[6] = x76; + out[7] = x80; + out[8] = x84; + out[9] = x88; + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +} + +static __always_inline void fe_tobytes(u8 s[32], const fe *f) +{ + u32 h[10]; + fe_freeze(h, f->v); + s[0] = h[0] >> 0; + s[1] = h[0] >> 8; + s[2] = h[0] >> 16; + s[3] = (h[0] >> 24) | (h[1] << 2); + s[4] = h[1] >> 6; + s[5] = h[1] >> 14; + s[6] = (h[1] >> 22) | (h[2] << 3); + s[7] = h[2] >> 5; + s[8] = h[2] >> 13; + s[9] = (h[2] >> 21) | (h[3] << 5); + s[10] = h[3] >> 3; + s[11] = h[3] >> 11; + s[12] = (h[3] >> 19) | (h[4] << 6); + s[13] = h[4] >> 2; + s[14] = h[4] >> 10; + s[15] = h[4] >> 18; + s[16] = h[5] >> 0; + s[17] = h[5] >> 8; + s[18] = h[5] >> 16; + s[19] = (h[5] >> 24) | (h[6] << 1); + s[20] = h[6] >> 7; + s[21] = h[6] >> 15; + s[22] = (h[6] >> 23) | (h[7] << 3); + s[23] = h[7] >> 5; + s[24] = h[7] >> 13; + s[25] = (h[7] >> 21) | (h[8] << 4); + s[26] = h[8] >> 4; + s[27] = h[8] >> 12; + s[28] = (h[8] >> 20) | (h[9] << 6); + s[29] = h[9] >> 2; + s[30] = h[9] >> 10; + s[31] = h[9] >> 18; +} + +/* h = f */ +static __always_inline void fe_copy(fe *h, const fe *f) +{ + memmove(h, f, sizeof(u32) * 10); +} + +static __always_inline void fe_copy_lt(fe_loose *h, const fe *f) +{ + memmove(h, f, sizeof(u32) * 10); +} + +/* h = 0 */ +static __always_inline void fe_0(fe *h) +{ + memset(h, 0, sizeof(u32) * 10); +} + +/* h = 1 */ +static __always_inline void fe_1(fe *h) +{ + memset(h, 0, sizeof(u32) * 10); + h->v[0] = 1; +} + +static void fe_add_impl(u32 out[10], const u32 in1[10], const u32 in2[10]) +{ + { const u32 x20 = in1[9]; + { const u32 x21 = in1[8]; + { const u32 x19 = in1[7]; + { const u32 x17 = in1[6]; + { const u32 x15 = in1[5]; + { const u32 x13 = in1[4]; + { const u32 x11 = in1[3]; + { const u32 x9 = in1[2]; + { const u32 x7 = in1[1]; + { const u32 x5 = in1[0]; + { const u32 x38 = in2[9]; + { const u32 x39 = in2[8]; + { const u32 x37 = in2[7]; + { const u32 x35 = in2[6]; + { const u32 x33 = in2[5]; + { const u32 x31 = in2[4]; + { const u32 x29 = in2[3]; + { const u32 x27 = in2[2]; + { const u32 x25 = in2[1]; + { const u32 x23 = in2[0]; + out[0] = (x5 + x23); + out[1] = (x7 + x25); + out[2] = (x9 + x27); + out[3] = (x11 + x29); + out[4] = (x13 + x31); + out[5] = (x15 + x33); + out[6] = (x17 + x35); + out[7] = (x19 + x37); + out[8] = (x21 + x39); + out[9] = (x20 + x38); + }}}}}}}}}}}}}}}}}}}} +} + +/* h = f + g + * Can overlap h with f or g. + */ +static __always_inline void fe_add(fe_loose *h, const fe *f, const fe *g) +{ + fe_add_impl(h->v, f->v, g->v); +} + +static void fe_sub_impl(u32 out[10], const u32 in1[10], const u32 in2[10]) +{ + { const u32 x20 = in1[9]; + { const u32 x21 = in1[8]; + { const u32 x19 = in1[7]; + { const u32 x17 = in1[6]; + { const u32 x15 = in1[5]; + { const u32 x13 = in1[4]; + { const u32 x11 = in1[3]; + { const u32 x9 = in1[2]; + { const u32 x7 = in1[1]; + { const u32 x5 = in1[0]; + { const u32 x38 = in2[9]; + { const u32 x39 = in2[8]; + { const u32 x37 = in2[7]; + { const u32 x35 = in2[6]; + { const u32 x33 = in2[5]; + { const u32 x31 = in2[4]; + { const u32 x29 = in2[3]; + { const u32 x27 = in2[2]; + { const u32 x25 = in2[1]; + { const u32 x23 = in2[0]; + out[0] = ((0x7ffffda + x5) - x23); + out[1] = ((0x3fffffe + x7) - x25); + out[2] = ((0x7fffffe + x9) - x27); + out[3] = ((0x3fffffe + x11) - x29); + out[4] = ((0x7fffffe + x13) - x31); + out[5] = ((0x3fffffe + x15) - x33); + out[6] = ((0x7fffffe + x17) - x35); + out[7] = ((0x3fffffe + x19) - x37); + out[8] = ((0x7fffffe + x21) - x39); + out[9] = ((0x3fffffe + x20) - x38); + }}}}}}}}}}}}}}}}}}}} +} + +/* h = f - g + * Can overlap h with f or g. + */ +static __always_inline void fe_sub(fe_loose *h, const fe *f, const fe *g) +{ + fe_sub_impl(h->v, f->v, g->v); +} + +static void fe_mul_impl(u32 out[10], const u32 in1[10], const u32 in2[10]) +{ + { const u32 x20 = in1[9]; + { const u32 x21 = in1[8]; + { const u32 x19 = in1[7]; + { const u32 x17 = in1[6]; + { const u32 x15 = in1[5]; + { const u32 x13 = in1[4]; + { const u32 x11 = in1[3]; + { const u32 x9 = in1[2]; + { const u32 x7 = in1[1]; + { const u32 x5 = in1[0]; + { const u32 x38 = in2[9]; + { const u32 x39 = in2[8]; + { const u32 x37 = in2[7]; + { const u32 x35 = in2[6]; + { const u32 x33 = in2[5]; + { const u32 x31 = in2[4]; + { const u32 x29 = in2[3]; + { const u32 x27 = in2[2]; + { const u32 x25 = in2[1]; + { const u32 x23 = in2[0]; + { u64 x40 = ((u64)x23 * x5); + { u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5)); + { u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5)); + { u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5)); + { u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5)); + { u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5)); + { u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5)); + { u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5)); + { u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5)); + { u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5)); + { u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9)); + { u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9)); + { u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13)); + { u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13)); + { u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17)); + { u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17)); + { u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19)))); + { u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21)); + { u64 x58 = ((u64)(0x2 * x38) * x20); + { u64 x59 = (x48 + (x58 << 0x4)); + { u64 x60 = (x59 + (x58 << 0x1)); + { u64 x61 = (x60 + x58); + { u64 x62 = (x47 + (x57 << 0x4)); + { u64 x63 = (x62 + (x57 << 0x1)); + { u64 x64 = (x63 + x57); + { u64 x65 = (x46 + (x56 << 0x4)); + { u64 x66 = (x65 + (x56 << 0x1)); + { u64 x67 = (x66 + x56); + { u64 x68 = (x45 + (x55 << 0x4)); + { u64 x69 = (x68 + (x55 << 0x1)); + { u64 x70 = (x69 + x55); + { u64 x71 = (x44 + (x54 << 0x4)); + { u64 x72 = (x71 + (x54 << 0x1)); + { u64 x73 = (x72 + x54); + { u64 x74 = (x43 + (x53 << 0x4)); + { u64 x75 = (x74 + (x53 << 0x1)); + { u64 x76 = (x75 + x53); + { u64 x77 = (x42 + (x52 << 0x4)); + { u64 x78 = (x77 + (x52 << 0x1)); + { u64 x79 = (x78 + x52); + { u64 x80 = (x41 + (x51 << 0x4)); + { u64 x81 = (x80 + (x51 << 0x1)); + { u64 x82 = (x81 + x51); + { u64 x83 = (x40 + (x50 << 0x4)); + { u64 x84 = (x83 + (x50 << 0x1)); + { u64 x85 = (x84 + x50); + { u64 x86 = (x85 >> 0x1a); + { u32 x87 = ((u32)x85 & 0x3ffffff); + { u64 x88 = (x86 + x82); + { u64 x89 = (x88 >> 0x19); + { u32 x90 = ((u32)x88 & 0x1ffffff); + { u64 x91 = (x89 + x79); + { u64 x92 = (x91 >> 0x1a); + { u32 x93 = ((u32)x91 & 0x3ffffff); + { u64 x94 = (x92 + x76); + { u64 x95 = (x94 >> 0x19); + { u32 x96 = ((u32)x94 & 0x1ffffff); + { u64 x97 = (x95 + x73); + { u64 x98 = (x97 >> 0x1a); + { u32 x99 = ((u32)x97 & 0x3ffffff); + { u64 x100 = (x98 + x70); + { u64 x101 = (x100 >> 0x19); + { u32 x102 = ((u32)x100 & 0x1ffffff); + { u64 x103 = (x101 + x67); + { u64 x104 = (x103 >> 0x1a); + { u32 x105 = ((u32)x103 & 0x3ffffff); + { u64 x106 = (x104 + x64); + { u64 x107 = (x106 >> 0x19); + { u32 x108 = ((u32)x106 & 0x1ffffff); + { u64 x109 = (x107 + x61); + { u64 x110 = (x109 >> 0x1a); + { u32 x111 = ((u32)x109 & 0x3ffffff); + { u64 x112 = (x110 + x49); + { u64 x113 = (x112 >> 0x19); + { u32 x114 = ((u32)x112 & 0x1ffffff); + { u64 x115 = (x87 + (0x13 * x113)); + { u32 x116 = (u32) (x115 >> 0x1a); + { u32 x117 = ((u32)x115 & 0x3ffffff); + { u32 x118 = (x116 + x90); + { u32 x119 = (x118 >> 0x19); + { u32 x120 = (x118 & 0x1ffffff); + out[0] = x117; + out[1] = x120; + out[2] = (x119 + x93); + out[3] = x96; + out[4] = x99; + out[5] = x102; + out[6] = x105; + out[7] = x108; + out[8] = x111; + out[9] = x114; + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +} + +static __always_inline void fe_mul_ttt(fe *h, const fe *f, const fe *g) +{ + fe_mul_impl(h->v, f->v, g->v); +} + +static __always_inline void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g) +{ + fe_mul_impl(h->v, f->v, g->v); +} + +static __always_inline void +fe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g) +{ + fe_mul_impl(h->v, f->v, g->v); +} + +static void fe_sqr_impl(u32 out[10], const u32 in1[10]) +{ + { const u32 x17 = in1[9]; + { const u32 x18 = in1[8]; + { const u32 x16 = in1[7]; + { const u32 x14 = in1[6]; + { const u32 x12 = in1[5]; + { const u32 x10 = in1[4]; + { const u32 x8 = in1[3]; + { const u32 x6 = in1[2]; + { const u32 x4 = in1[1]; + { const u32 x2 = in1[0]; + { u64 x19 = ((u64)x2 * x2); + { u64 x20 = ((u64)(0x2 * x2) * x4); + { u64 x21 = (0x2 * (((u64)x4 * x4) + ((u64)x2 * x6))); + { u64 x22 = (0x2 * (((u64)x4 * x6) + ((u64)x2 * x8))); + { u64 x23 = ((((u64)x6 * x6) + ((u64)(0x4 * x4) * x8)) + ((u64)(0x2 * x2) * x10)); + { u64 x24 = (0x2 * ((((u64)x6 * x8) + ((u64)x4 * x10)) + ((u64)x2 * x12))); + { u64 x25 = (0x2 * (((((u64)x8 * x8) + ((u64)x6 * x10)) + ((u64)x2 * x14)) + ((u64)(0x2 * x4) * x12))); + { u64 x26 = (0x2 * (((((u64)x8 * x10) + ((u64)x6 * x12)) + ((u64)x4 * x14)) + ((u64)x2 * x16))); + { u64 x27 = (((u64)x10 * x10) + (0x2 * ((((u64)x6 * x14) + ((u64)x2 * x18)) + (0x2 * (((u64)x4 * x16) + ((u64)x8 * x12)))))); + { u64 x28 = (0x2 * ((((((u64)x10 * x12) + ((u64)x8 * x14)) + ((u64)x6 * x16)) + ((u64)x4 * x18)) + ((u64)x2 * x17))); + { u64 x29 = (0x2 * (((((u64)x12 * x12) + ((u64)x10 * x14)) + ((u64)x6 * x18)) + (0x2 * (((u64)x8 * x16) + ((u64)x4 * x17))))); + { u64 x30 = (0x2 * (((((u64)x12 * x14) + ((u64)x10 * x16)) + ((u64)x8 * x18)) + ((u64)x6 * x17))); + { u64 x31 = (((u64)x14 * x14) + (0x2 * (((u64)x10 * x18) + (0x2 * (((u64)x12 * x16) + ((u64)x8 * x17)))))); + { u64 x32 = (0x2 * ((((u64)x14 * x16) + ((u64)x12 * x18)) + ((u64)x10 * x17))); + { u64 x33 = (0x2 * ((((u64)x16 * x16) + ((u64)x14 * x18)) + ((u64)(0x2 * x12) * x17))); + { u64 x34 = (0x2 * (((u64)x16 * x18) + ((u64)x14 * x17))); + { u64 x35 = (((u64)x18 * x18) + ((u64)(0x4 * x16) * x17)); + { u64 x36 = ((u64)(0x2 * x18) * x17); + { u64 x37 = ((u64)(0x2 * x17) * x17); + { u64 x38 = (x27 + (x37 << 0x4)); + { u64 x39 = (x38 + (x37 << 0x1)); + { u64 x40 = (x39 + x37); + { u64 x41 = (x26 + (x36 << 0x4)); + { u64 x42 = (x41 + (x36 << 0x1)); + { u64 x43 = (x42 + x36); + { u64 x44 = (x25 + (x35 << 0x4)); + { u64 x45 = (x44 + (x35 << 0x1)); + { u64 x46 = (x45 + x35); + { u64 x47 = (x24 + (x34 << 0x4)); + { u64 x48 = (x47 + (x34 << 0x1)); + { u64 x49 = (x48 + x34); + { u64 x50 = (x23 + (x33 << 0x4)); + { u64 x51 = (x50 + (x33 << 0x1)); + { u64 x52 = (x51 + x33); + { u64 x53 = (x22 + (x32 << 0x4)); + { u64 x54 = (x53 + (x32 << 0x1)); + { u64 x55 = (x54 + x32); + { u64 x56 = (x21 + (x31 << 0x4)); + { u64 x57 = (x56 + (x31 << 0x1)); + { u64 x58 = (x57 + x31); + { u64 x59 = (x20 + (x30 << 0x4)); + { u64 x60 = (x59 + (x30 << 0x1)); + { u64 x61 = (x60 + x30); + { u64 x62 = (x19 + (x29 << 0x4)); + { u64 x63 = (x62 + (x29 << 0x1)); + { u64 x64 = (x63 + x29); + { u64 x65 = (x64 >> 0x1a); + { u32 x66 = ((u32)x64 & 0x3ffffff); + { u64 x67 = (x65 + x61); + { u64 x68 = (x67 >> 0x19); + { u32 x69 = ((u32)x67 & 0x1ffffff); + { u64 x70 = (x68 + x58); + { u64 x71 = (x70 >> 0x1a); + { u32 x72 = ((u32)x70 & 0x3ffffff); + { u64 x73 = (x71 + x55); + { u64 x74 = (x73 >> 0x19); + { u32 x75 = ((u32)x73 & 0x1ffffff); + { u64 x76 = (x74 + x52); + { u64 x77 = (x76 >> 0x1a); + { u32 x78 = ((u32)x76 & 0x3ffffff); + { u64 x79 = (x77 + x49); + { u64 x80 = (x79 >> 0x19); + { u32 x81 = ((u32)x79 & 0x1ffffff); + { u64 x82 = (x80 + x46); + { u64 x83 = (x82 >> 0x1a); + { u32 x84 = ((u32)x82 & 0x3ffffff); + { u64 x85 = (x83 + x43); + { u64 x86 = (x85 >> 0x19); + { u32 x87 = ((u32)x85 & 0x1ffffff); + { u64 x88 = (x86 + x40); + { u64 x89 = (x88 >> 0x1a); + { u32 x90 = ((u32)x88 & 0x3ffffff); + { u64 x91 = (x89 + x28); + { u64 x92 = (x91 >> 0x19); + { u32 x93 = ((u32)x91 & 0x1ffffff); + { u64 x94 = (x66 + (0x13 * x92)); + { u32 x95 = (u32) (x94 >> 0x1a); + { u32 x96 = ((u32)x94 & 0x3ffffff); + { u32 x97 = (x95 + x69); + { u32 x98 = (x97 >> 0x19); + { u32 x99 = (x97 & 0x1ffffff); + out[0] = x96; + out[1] = x99; + out[2] = (x98 + x72); + out[3] = x75; + out[4] = x78; + out[5] = x81; + out[6] = x84; + out[7] = x87; + out[8] = x90; + out[9] = x93; + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +} + +static __always_inline void fe_sq_tl(fe *h, const fe_loose *f) +{ + fe_sqr_impl(h->v, f->v); +} + +static __always_inline void fe_sq_tt(fe *h, const fe *f) +{ + fe_sqr_impl(h->v, f->v); +} + +static __always_inline void fe_loose_invert(fe *out, const fe_loose *z) +{ + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq_tl(&t0, z); + fe_sq_tt(&t1, &t0); + for (i = 1; i < 2; ++i) + fe_sq_tt(&t1, &t1); + fe_mul_tlt(&t1, z, &t1); + fe_mul_ttt(&t0, &t0, &t1); + fe_sq_tt(&t2, &t0); + fe_mul_ttt(&t1, &t1, &t2); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 5; ++i) + fe_sq_tt(&t2, &t2); + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 10; ++i) + fe_sq_tt(&t2, &t2); + fe_mul_ttt(&t2, &t2, &t1); + fe_sq_tt(&t3, &t2); + for (i = 1; i < 20; ++i) + fe_sq_tt(&t3, &t3); + fe_mul_ttt(&t2, &t3, &t2); + fe_sq_tt(&t2, &t2); + for (i = 1; i < 10; ++i) + fe_sq_tt(&t2, &t2); + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 50; ++i) + fe_sq_tt(&t2, &t2); + fe_mul_ttt(&t2, &t2, &t1); + fe_sq_tt(&t3, &t2); + for (i = 1; i < 100; ++i) + fe_sq_tt(&t3, &t3); + fe_mul_ttt(&t2, &t3, &t2); + fe_sq_tt(&t2, &t2); + for (i = 1; i < 50; ++i) + fe_sq_tt(&t2, &t2); + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t1, &t1); + for (i = 1; i < 5; ++i) + fe_sq_tt(&t1, &t1); + fe_mul_ttt(out, &t1, &t0); +} + +static __always_inline void fe_invert(fe *out, const fe *z) +{ + fe_loose l; + fe_copy_lt(&l, z); + fe_loose_invert(out, &l); +} + +/* Replace (f,g) with (g,f) if b == 1; + * replace (f,g) with (f,g) if b == 0. + * + * Preconditions: b in {0,1} + */ +static __always_inline void fe_cswap(fe *f, fe *g, unsigned int b) +{ + unsigned i; + b = 0 - b; + for (i = 0; i < 10; i++) { + u32 x = f->v[i] ^ g->v[i]; + x &= b; + f->v[i] ^= x; + g->v[i] ^= x; + } +} + +/* NOTE: based on fiat-crypto fe_mul, edited for in2=121666, 0, 0.*/ +static __always_inline void fe_mul_121666_impl(u32 out[10], const u32 in1[10]) +{ + { const u32 x20 = in1[9]; + { const u32 x21 = in1[8]; + { const u32 x19 = in1[7]; + { const u32 x17 = in1[6]; + { const u32 x15 = in1[5]; + { const u32 x13 = in1[4]; + { const u32 x11 = in1[3]; + { const u32 x9 = in1[2]; + { const u32 x7 = in1[1]; + { const u32 x5 = in1[0]; + { const u32 x38 = 0; + { const u32 x39 = 0; + { const u32 x37 = 0; + { const u32 x35 = 0; + { const u32 x33 = 0; + { const u32 x31 = 0; + { const u32 x29 = 0; + { const u32 x27 = 0; + { const u32 x25 = 0; + { const u32 x23 = 121666; + { u64 x40 = ((u64)x23 * x5); + { u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5)); + { u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5)); + { u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5)); + { u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5)); + { u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5)); + { u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5)); + { u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5)); + { u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5)); + { u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5)); + { u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9)); + { u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9)); + { u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13)); + { u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13)); + { u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17)); + { u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17)); + { u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19)))); + { u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21)); + { u64 x58 = ((u64)(0x2 * x38) * x20); + { u64 x59 = (x48 + (x58 << 0x4)); + { u64 x60 = (x59 + (x58 << 0x1)); + { u64 x61 = (x60 + x58); + { u64 x62 = (x47 + (x57 << 0x4)); + { u64 x63 = (x62 + (x57 << 0x1)); + { u64 x64 = (x63 + x57); + { u64 x65 = (x46 + (x56 << 0x4)); + { u64 x66 = (x65 + (x56 << 0x1)); + { u64 x67 = (x66 + x56); + { u64 x68 = (x45 + (x55 << 0x4)); + { u64 x69 = (x68 + (x55 << 0x1)); + { u64 x70 = (x69 + x55); + { u64 x71 = (x44 + (x54 << 0x4)); + { u64 x72 = (x71 + (x54 << 0x1)); + { u64 x73 = (x72 + x54); + { u64 x74 = (x43 + (x53 << 0x4)); + { u64 x75 = (x74 + (x53 << 0x1)); + { u64 x76 = (x75 + x53); + { u64 x77 = (x42 + (x52 << 0x4)); + { u64 x78 = (x77 + (x52 << 0x1)); + { u64 x79 = (x78 + x52); + { u64 x80 = (x41 + (x51 << 0x4)); + { u64 x81 = (x80 + (x51 << 0x1)); + { u64 x82 = (x81 + x51); + { u64 x83 = (x40 + (x50 << 0x4)); + { u64 x84 = (x83 + (x50 << 0x1)); + { u64 x85 = (x84 + x50); + { u64 x86 = (x85 >> 0x1a); + { u32 x87 = ((u32)x85 & 0x3ffffff); + { u64 x88 = (x86 + x82); + { u64 x89 = (x88 >> 0x19); + { u32 x90 = ((u32)x88 & 0x1ffffff); + { u64 x91 = (x89 + x79); + { u64 x92 = (x91 >> 0x1a); + { u32 x93 = ((u32)x91 & 0x3ffffff); + { u64 x94 = (x92 + x76); + { u64 x95 = (x94 >> 0x19); + { u32 x96 = ((u32)x94 & 0x1ffffff); + { u64 x97 = (x95 + x73); + { u64 x98 = (x97 >> 0x1a); + { u32 x99 = ((u32)x97 & 0x3ffffff); + { u64 x100 = (x98 + x70); + { u64 x101 = (x100 >> 0x19); + { u32 x102 = ((u32)x100 & 0x1ffffff); + { u64 x103 = (x101 + x67); + { u64 x104 = (x103 >> 0x1a); + { u32 x105 = ((u32)x103 & 0x3ffffff); + { u64 x106 = (x104 + x64); + { u64 x107 = (x106 >> 0x19); + { u32 x108 = ((u32)x106 & 0x1ffffff); + { u64 x109 = (x107 + x61); + { u64 x110 = (x109 >> 0x1a); + { u32 x111 = ((u32)x109 & 0x3ffffff); + { u64 x112 = (x110 + x49); + { u64 x113 = (x112 >> 0x19); + { u32 x114 = ((u32)x112 & 0x1ffffff); + { u64 x115 = (x87 + (0x13 * x113)); + { u32 x116 = (u32) (x115 >> 0x1a); + { u32 x117 = ((u32)x115 & 0x3ffffff); + { u32 x118 = (x116 + x90); + { u32 x119 = (x118 >> 0x19); + { u32 x120 = (x118 & 0x1ffffff); + out[0] = x117; + out[1] = x120; + out[2] = (x119 + x93); + out[3] = x96; + out[4] = x99; + out[5] = x102; + out[6] = x105; + out[7] = x108; + out[8] = x111; + out[9] = x114; + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +} + +static __always_inline void fe_mul121666(fe *h, const fe_loose *f) +{ + fe_mul_121666_impl(h->v, f->v); +} + +static void curve25519_generic(u8 out[CURVE25519_KEY_SIZE], + const u8 scalar[CURVE25519_KEY_SIZE], + const u8 point[CURVE25519_KEY_SIZE]) +{ + fe x1, x2, z2, x3, z3; + fe_loose x2l, z2l, x3l; + unsigned swap = 0; + int pos; + u8 e[32]; + + memcpy(e, scalar, 32); + curve25519_clamp_secret(e); + + /* The following implementation was transcribed to Coq and proven to + * correspond to unary scalar multiplication in affine coordinates given + * that x1 != 0 is the x coordinate of some point on the curve. It was + * also checked in Coq that doing a ladderstep with x1 = x3 = 0 gives + * z2' = z3' = 0, and z2 = z3 = 0 gives z2' = z3' = 0. The statement was + * quantified over the underlying field, so it applies to Curve25519 + * itself and the quadratic twist of Curve25519. It was not proven in + * Coq that prime-field arithmetic correctly simulates extension-field + * arithmetic on prime-field values. The decoding of the byte array + * representation of e was not considered. + * + * Specification of Montgomery curves in affine coordinates: + * + * + * Proof that these form a group that is isomorphic to a Weierstrass + * curve: + * + * + * Coq transcription and correctness proof of the loop + * (where scalarbits=255): + * + * + * preconditions: 0 <= e < 2^255 (not necessarily e < order), + * fe_invert(0) = 0 + */ + fe_frombytes(&x1, point); + fe_1(&x2); + fe_0(&z2); + fe_copy(&x3, &x1); + fe_1(&z3); + + for (pos = 254; pos >= 0; --pos) { + fe tmp0, tmp1; + fe_loose tmp0l, tmp1l; + /* loop invariant as of right before the test, for the case + * where x1 != 0: + * pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3 + * is nonzero + * let r := e >> (pos+1) in the following equalities of + * projective points: + * to_xz (r*P) === if swap then (x3, z3) else (x2, z2) + * to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3) + * x1 is the nonzero x coordinate of the nonzero + * point (r*P-(r+1)*P) + */ + unsigned b = 1 & (e[pos / 8] >> (pos & 7)); + swap ^= b; + fe_cswap(&x2, &x3, swap); + fe_cswap(&z2, &z3, swap); + swap = b; + /* Coq transcription of ladderstep formula (called from + * transcribed loop): + * + * + * x1 != 0 + * x1 = 0 + */ + fe_sub(&tmp0l, &x3, &z3); + fe_sub(&tmp1l, &x2, &z2); + fe_add(&x2l, &x2, &z2); + fe_add(&z2l, &x3, &z3); + fe_mul_tll(&z3, &tmp0l, &x2l); + fe_mul_tll(&z2, &z2l, &tmp1l); + fe_sq_tl(&tmp0, &tmp1l); + fe_sq_tl(&tmp1, &x2l); + fe_add(&x3l, &z3, &z2); + fe_sub(&z2l, &z3, &z2); + fe_mul_ttt(&x2, &tmp1, &tmp0); + fe_sub(&tmp1l, &tmp1, &tmp0); + fe_sq_tl(&z2, &z2l); + fe_mul121666(&z3, &tmp1l); + fe_sq_tl(&x3, &x3l); + fe_add(&tmp0l, &tmp0, &z3); + fe_mul_ttt(&z3, &x1, &z2); + fe_mul_tll(&z2, &tmp1l, &tmp0l); + } + /* here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3) + * else (x2, z2) + */ + fe_cswap(&x2, &x3, swap); + fe_cswap(&z2, &z3, swap); + + fe_invert(&z2, &z2); + fe_mul_ttt(&x2, &x2, &z2); + fe_tobytes(out, &x2); + + memzero_explicit(&x1, sizeof(x1)); + memzero_explicit(&x2, sizeof(x2)); + memzero_explicit(&z2, sizeof(z2)); + memzero_explicit(&x3, sizeof(x3)); + memzero_explicit(&z3, sizeof(z3)); + memzero_explicit(&x2l, sizeof(x2l)); + memzero_explicit(&z2l, sizeof(z2l)); + memzero_explicit(&x3l, sizeof(x3l)); + memzero_explicit(&e, sizeof(e)); +} diff --git a/curve25519-hacl64.h b/curve25519-hacl64.h new file mode 100644 index 000000000000..1fba1f5949f0 --- /dev/null +++ b/curve25519-hacl64.h @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2016-2017 INRIA and Microsoft Corporation. + * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. + * + * This is a machine-generated formally verified implementation of Curve25519 + * ECDH from: . Though originally machine + * generated, it has been tweaked to be suitable for use in the kernel. It is + * optimized for 64-bit machines that can efficiently work with 128-bit + * integer types. + */ + +typedef __uint128_t u128; + +static __always_inline u64 u64_eq_mask(u64 a, u64 b) +{ + u64 x = a ^ b; + u64 minus_x = ~x + (u64)1U; + u64 x_or_minus_x = x | minus_x; + u64 xnx = x_or_minus_x >> (u32)63U; + u64 c = xnx - (u64)1U; + return c; +} + +static __always_inline u64 u64_gte_mask(u64 a, u64 b) +{ + u64 x = a; + u64 y = b; + u64 x_xor_y = x ^ y; + u64 x_sub_y = x - y; + u64 x_sub_y_xor_y = x_sub_y ^ y; + u64 q = x_xor_y | x_sub_y_xor_y; + u64 x_xor_q = x ^ q; + u64 x_xor_q_ = x_xor_q >> (u32)63U; + u64 c = x_xor_q_ - (u64)1U; + return c; +} + +static __always_inline void modulo_carry_top(u64 *b) +{ + u64 b4 = b[4]; + u64 b0 = b[0]; + u64 b4_ = b4 & 0x7ffffffffffffLLU; + u64 b0_ = b0 + 19 * (b4 >> 51); + b[4] = b4_; + b[0] = b0_; +} + +static __always_inline void fproduct_copy_from_wide_(u64 *output, u128 *input) +{ + { + u128 xi = input[0]; + output[0] = ((u64)(xi)); + } + { + u128 xi = input[1]; + output[1] = ((u64)(xi)); + } + { + u128 xi = input[2]; + output[2] = ((u64)(xi)); + } + { + u128 xi = input[3]; + output[3] = ((u64)(xi)); + } + { + u128 xi = input[4]; + output[4] = ((u64)(xi)); + } +} + +static __always_inline void +fproduct_sum_scalar_multiplication_(u128 *output, u64 *input, u64 s) +{ + output[0] += (u128)input[0] * s; + output[1] += (u128)input[1] * s; + output[2] += (u128)input[2] * s; + output[3] += (u128)input[3] * s; + output[4] += (u128)input[4] * s; +} + +static __always_inline void fproduct_carry_wide_(u128 *tmp) +{ + { + u32 ctr = 0; + u128 tctr = tmp[ctr]; + u128 tctrp1 = tmp[ctr + 1]; + u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU; + u128 c = ((tctr) >> (51)); + tmp[ctr] = ((u128)(r0)); + tmp[ctr + 1] = ((tctrp1) + (c)); + } + { + u32 ctr = 1; + u128 tctr = tmp[ctr]; + u128 tctrp1 = tmp[ctr + 1]; + u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU; + u128 c = ((tctr) >> (51)); + tmp[ctr] = ((u128)(r0)); + tmp[ctr + 1] = ((tctrp1) + (c)); + } + + { + u32 ctr = 2; + u128 tctr = tmp[ctr]; + u128 tctrp1 = tmp[ctr + 1]; + u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU; + u128 c = ((tctr) >> (51)); + tmp[ctr] = ((u128)(r0)); + tmp[ctr + 1] = ((tctrp1) + (c)); + } + { + u32 ctr = 3; + u128 tctr = tmp[ctr]; + u128 tctrp1 = tmp[ctr + 1]; + u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU; + u128 c = ((tctr) >> (51)); + tmp[ctr] = ((u128)(r0)); + tmp[ctr + 1] = ((tctrp1) + (c)); + } +} + +static __always_inline void fmul_shift_reduce(u64 *output) +{ + u64 tmp = output[4]; + u64 b0; + { + u32 ctr = 5 - 0 - 1; + u64 z = output[ctr - 1]; + output[ctr] = z; + } + { + u32 ctr = 5 - 1 - 1; + u64 z = output[ctr - 1]; + output[ctr] = z; + } + { + u32 ctr = 5 - 2 - 1; + u64 z = output[ctr - 1]; + output[ctr] = z; + } + { + u32 ctr = 5 - 3 - 1; + u64 z = output[ctr - 1]; + output[ctr] = z; + } + output[0] = tmp; + b0 = output[0]; + output[0] = 19 * b0; +} + +static __always_inline void fmul_mul_shift_reduce_(u128 *output, u64 *input, + u64 *input21) +{ + u32 i; + u64 input2i; + { + u64 input2i = input21[0]; + fproduct_sum_scalar_multiplication_(output, input, input2i); + fmul_shift_reduce(input); + } + { + u64 input2i = input21[1]; + fproduct_sum_scalar_multiplication_(output, input, input2i); + fmul_shift_reduce(input); + } + { + u64 input2i = input21[2]; + fproduct_sum_scalar_multiplication_(output, input, input2i); + fmul_shift_reduce(input); + } + { + u64 input2i = input21[3]; + fproduct_sum_scalar_multiplication_(output, input, input2i); + fmul_shift_reduce(input); + } + i = 4; + input2i = input21[i]; + fproduct_sum_scalar_multiplication_(output, input, input2i); +} + +static __always_inline void fmul_fmul(u64 *output, u64 *input, u64 *input21) +{ + u64 tmp[5] = { input[0], input[1], input[2], input[3], input[4] }; + { + u128 b4; + u128 b0; + u128 b4_; + u128 b0_; + u64 i0; + u64 i1; + u64 i0_; + u64 i1_; + u128 t[5] = { 0 }; + fmul_mul_shift_reduce_(t, tmp, input21); + fproduct_carry_wide_(t); + b4 = t[4]; + b0 = t[0]; + b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU)))); + b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51)))))))); + t[4] = b4_; + t[0] = b0_; + fproduct_copy_from_wide_(output, t); + i0 = output[0]; + i1 = output[1]; + i0_ = i0 & 0x7ffffffffffffLLU; + i1_ = i1 + (i0 >> 51); + output[0] = i0_; + output[1] = i1_; + } +} + +static __always_inline void fsquare_fsquare__(u128 *tmp, u64 *output) +{ + u64 r0 = output[0]; + u64 r1 = output[1]; + u64 r2 = output[2]; + u64 r3 = output[3]; + u64 r4 = output[4]; + u64 d0 = r0 * 2; + u64 d1 = r1 * 2; + u64 d2 = r2 * 2 * 19; + u64 d419 = r4 * 19; + u64 d4 = d419 * 2; + u128 s0 = ((((((u128)(r0) * (r0))) + (((u128)(d4) * (r1))))) + + (((u128)(d2) * (r3)))); + u128 s1 = ((((((u128)(d0) * (r1))) + (((u128)(d4) * (r2))))) + + (((u128)(r3 * 19) * (r3)))); + u128 s2 = ((((((u128)(d0) * (r2))) + (((u128)(r1) * (r1))))) + + (((u128)(d4) * (r3)))); + u128 s3 = ((((((u128)(d0) * (r3))) + (((u128)(d1) * (r2))))) + + (((u128)(r4) * (d419)))); + u128 s4 = ((((((u128)(d0) * (r4))) + (((u128)(d1) * (r3))))) + + (((u128)(r2) * (r2)))); + tmp[0] = s0; + tmp[1] = s1; + tmp[2] = s2; + tmp[3] = s3; + tmp[4] = s4; +} + +static __always_inline void fsquare_fsquare_(u128 *tmp, u64 *output) +{ + u128 b4; + u128 b0; + u128 b4_; + u128 b0_; + u64 i0; + u64 i1; + u64 i0_; + u64 i1_; + fsquare_fsquare__(tmp, output); + fproduct_carry_wide_(tmp); + b4 = tmp[4]; + b0 = tmp[0]; + b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU)))); + b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51)))))))); + tmp[4] = b4_; + tmp[0] = b0_; + fproduct_copy_from_wide_(output, tmp); + i0 = output[0]; + i1 = output[1]; + i0_ = i0 & 0x7ffffffffffffLLU; + i1_ = i1 + (i0 >> 51); + output[0] = i0_; + output[1] = i1_; +} + +static __always_inline void fsquare_fsquare_times_(u64 *output, u128 *tmp, + u32 count1) +{ + u32 i; + fsquare_fsquare_(tmp, output); + for (i = 1; i < count1; ++i) + fsquare_fsquare_(tmp, output); +} + +static __always_inline void fsquare_fsquare_times(u64 *output, u64 *input, + u32 count1) +{ + u128 t[5]; + memcpy(output, input, 5 * sizeof(*input)); + fsquare_fsquare_times_(output, t, count1); +} + +static __always_inline void fsquare_fsquare_times_inplace(u64 *output, + u32 count1) +{ + u128 t[5]; + fsquare_fsquare_times_(output, t, count1); +} + +static __always_inline void crecip_crecip(u64 *out, u64 *z) +{ + u64 buf[20] = { 0 }; + u64 *a0 = buf; + u64 *t00 = buf + 5; + u64 *b0 = buf + 10; + u64 *t01; + u64 *b1; + u64 *c0; + u64 *a; + u64 *t0; + u64 *b; + u64 *c; + fsquare_fsquare_times(a0, z, 1); + fsquare_fsquare_times(t00, a0, 2); + fmul_fmul(b0, t00, z); + fmul_fmul(a0, b0, a0); + fsquare_fsquare_times(t00, a0, 1); + fmul_fmul(b0, t00, b0); + fsquare_fsquare_times(t00, b0, 5); + t01 = buf + 5; + b1 = buf + 10; + c0 = buf + 15; + fmul_fmul(b1, t01, b1); + fsquare_fsquare_times(t01, b1, 10); + fmul_fmul(c0, t01, b1); + fsquare_fsquare_times(t01, c0, 20); + fmul_fmul(t01, t01, c0); + fsquare_fsquare_times_inplace(t01, 10); + fmul_fmul(b1, t01, b1); + fsquare_fsquare_times(t01, b1, 50); + a = buf; + t0 = buf + 5; + b = buf + 10; + c = buf + 15; + fmul_fmul(c, t0, b); + fsquare_fsquare_times(t0, c, 100); + fmul_fmul(t0, t0, c); + fsquare_fsquare_times_inplace(t0, 50); + fmul_fmul(t0, t0, b); + fsquare_fsquare_times_inplace(t0, 5); + fmul_fmul(out, t0, a); +} + +static __always_inline void fsum(u64 *a, u64 *b) +{ + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + a[3] += b[3]; + a[4] += b[4]; +} + +static __always_inline void fdifference(u64 *a, u64 *b) +{ + u64 tmp[5] = { 0 }; + u64 b0; + u64 b1; + u64 b2; + u64 b3; + u64 b4; + memcpy(tmp, b, 5 * sizeof(*b)); + b0 = tmp[0]; + b1 = tmp[1]; + b2 = tmp[2]; + b3 = tmp[3]; + b4 = tmp[4]; + tmp[0] = b0 + 0x3fffffffffff68LLU; + tmp[1] = b1 + 0x3ffffffffffff8LLU; + tmp[2] = b2 + 0x3ffffffffffff8LLU; + tmp[3] = b3 + 0x3ffffffffffff8LLU; + tmp[4] = b4 + 0x3ffffffffffff8LLU; + { + u64 xi = a[0]; + u64 yi = tmp[0]; + a[0] = yi - xi; + } + { + u64 xi = a[1]; + u64 yi = tmp[1]; + a[1] = yi - xi; + } + { + u64 xi = a[2]; + u64 yi = tmp[2]; + a[2] = yi - xi; + } + { + u64 xi = a[3]; + u64 yi = tmp[3]; + a[3] = yi - xi; + } + { + u64 xi = a[4]; + u64 yi = tmp[4]; + a[4] = yi - xi; + } +} + +static __always_inline void fscalar(u64 *output, u64 *b, u64 s) +{ + u128 tmp[5]; + u128 b4; + u128 b0; + u128 b4_; + u128 b0_; + { + u64 xi = b[0]; + tmp[0] = ((u128)(xi) * (s)); + } + { + u64 xi = b[1]; + tmp[1] = ((u128)(xi) * (s)); + } + { + u64 xi = b[2]; + tmp[2] = ((u128)(xi) * (s)); + } + { + u64 xi = b[3]; + tmp[3] = ((u128)(xi) * (s)); + } + { + u64 xi = b[4]; + tmp[4] = ((u128)(xi) * (s)); + } + fproduct_carry_wide_(tmp); + b4 = tmp[4]; + b0 = tmp[0]; + b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU)))); + b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51)))))))); + tmp[4] = b4_; + tmp[0] = b0_; + fproduct_copy_from_wide_(output, tmp); +} + +static __always_inline void fmul(u64 *output, u64 *a, u64 *b) +{ + fmul_fmul(output, a, b); +} + +static __always_inline void crecip(u64 *output, u64 *input) +{ + crecip_crecip(output, input); +} + +static __always_inline void point_swap_conditional_step(u64 *a, u64 *b, + u64 swap1, u32 ctr) +{ + u32 i = ctr - 1; + u64 ai = a[i]; + u64 bi = b[i]; + u64 x = swap1 & (ai ^ bi); + u64 ai1 = ai ^ x; + u64 bi1 = bi ^ x; + a[i] = ai1; + b[i] = bi1; +} + +static __always_inline void point_swap_conditional5(u64 *a, u64 *b, u64 swap1) +{ + point_swap_conditional_step(a, b, swap1, 5); + point_swap_conditional_step(a, b, swap1, 4); + point_swap_conditional_step(a, b, swap1, 3); + point_swap_conditional_step(a, b, swap1, 2); + point_swap_conditional_step(a, b, swap1, 1); +} + +static __always_inline void point_swap_conditional(u64 *a, u64 *b, u64 iswap) +{ + u64 swap1 = 0 - iswap; + point_swap_conditional5(a, b, swap1); + point_swap_conditional5(a + 5, b + 5, swap1); +} + +static __always_inline void point_copy(u64 *output, u64 *input) +{ + memcpy(output, input, 5 * sizeof(*input)); + memcpy(output + 5, input + 5, 5 * sizeof(*input)); +} + +static __always_inline void addanddouble_fmonty(u64 *pp, u64 *ppq, u64 *p, + u64 *pq, u64 *qmqp) +{ + u64 *qx = qmqp; + u64 *x2 = pp; + u64 *z2 = pp + 5; + u64 *x3 = ppq; + u64 *z3 = ppq + 5; + u64 *x = p; + u64 *z = p + 5; + u64 *xprime = pq; + u64 *zprime = pq + 5; + u64 buf[40] = { 0 }; + u64 *origx = buf; + u64 *origxprime0 = buf + 5; + u64 *xxprime0; + u64 *zzprime0; + u64 *origxprime; + xxprime0 = buf + 25; + zzprime0 = buf + 30; + memcpy(origx, x, 5 * sizeof(*x)); + fsum(x, z); + fdifference(z, origx); + memcpy(origxprime0, xprime, 5 * sizeof(*xprime)); + fsum(xprime, zprime); + fdifference(zprime, origxprime0); + fmul(xxprime0, xprime, z); + fmul(zzprime0, x, zprime); + origxprime = buf + 5; + { + u64 *xx0; + u64 *zz0; + u64 *xxprime; + u64 *zzprime; + u64 *zzzprime; + xx0 = buf + 15; + zz0 = buf + 20; + xxprime = buf + 25; + zzprime = buf + 30; + zzzprime = buf + 35; + memcpy(origxprime, xxprime, 5 * sizeof(*xxprime)); + fsum(xxprime, zzprime); + fdifference(zzprime, origxprime); + fsquare_fsquare_times(x3, xxprime, 1); + fsquare_fsquare_times(zzzprime, zzprime, 1); + fmul(z3, zzzprime, qx); + fsquare_fsquare_times(xx0, x, 1); + fsquare_fsquare_times(zz0, z, 1); + { + u64 *zzz; + u64 *xx; + u64 *zz; + u64 scalar; + zzz = buf + 10; + xx = buf + 15; + zz = buf + 20; + fmul(x2, xx, zz); + fdifference(zz, xx); + scalar = 121665; + fscalar(zzz, zz, scalar); + fsum(zzz, xx); + fmul(z2, zzz, zz); + } + } +} + +static __always_inline void +ladder_smallloop_cmult_small_loop_step(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2, + u64 *q, u8 byt) +{ + u64 bit0 = (u64)(byt >> 7); + u64 bit; + point_swap_conditional(nq, nqpq, bit0); + addanddouble_fmonty(nq2, nqpq2, nq, nqpq, q); + bit = (u64)(byt >> 7); + point_swap_conditional(nq2, nqpq2, bit); +} + +static __always_inline void +ladder_smallloop_cmult_small_loop_double_step(u64 *nq, u64 *nqpq, u64 *nq2, + u64 *nqpq2, u64 *q, u8 byt) +{ + u8 byt1; + ladder_smallloop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt); + byt1 = byt << 1; + ladder_smallloop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1); +} + +static __always_inline void +ladder_smallloop_cmult_small_loop(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2, + u64 *q, u8 byt, u32 i) +{ + while (i--) { + ladder_smallloop_cmult_small_loop_double_step(nq, nqpq, nq2, + nqpq2, q, byt); + byt <<= 2; + } +} + +static __always_inline void ladder_bigloop_cmult_big_loop(u8 *n1, u64 *nq, + u64 *nqpq, u64 *nq2, + u64 *nqpq2, u64 *q, + u32 i) +{ + while (i--) { + u8 byte = n1[i]; + ladder_smallloop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, + byte, 4); + } +} + +static void ladder_cmult(u64 *result, u8 *n1, u64 *q) +{ + u64 point_buf[40] = { 0 }; + u64 *nq = point_buf; + u64 *nqpq = point_buf + 10; + u64 *nq2 = point_buf + 20; + u64 *nqpq2 = point_buf + 30; + point_copy(nqpq, q); + nq[0] = 1; + ladder_bigloop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, 32); + point_copy(result, nq); +} + +static __always_inline void format_fexpand(u64 *output, const u8 *input) +{ + const u8 *x00 = input + 6; + const u8 *x01 = input + 12; + const u8 *x02 = input + 19; + const u8 *x0 = input + 24; + u64 i0, i1, i2, i3, i4, output0, output1, output2, output3, output4; + i0 = get_unaligned_le64(input); + i1 = get_unaligned_le64(x00); + i2 = get_unaligned_le64(x01); + i3 = get_unaligned_le64(x02); + i4 = get_unaligned_le64(x0); + output0 = i0 & 0x7ffffffffffffLLU; + output1 = i1 >> 3 & 0x7ffffffffffffLLU; + output2 = i2 >> 6 & 0x7ffffffffffffLLU; + output3 = i3 >> 1 & 0x7ffffffffffffLLU; + output4 = i4 >> 12 & 0x7ffffffffffffLLU; + output[0] = output0; + output[1] = output1; + output[2] = output2; + output[3] = output3; + output[4] = output4; +} + +static __always_inline void format_fcontract_first_carry_pass(u64 *input) +{ + u64 t0 = input[0]; + u64 t1 = input[1]; + u64 t2 = input[2]; + u64 t3 = input[3]; + u64 t4 = input[4]; + u64 t1_ = t1 + (t0 >> 51); + u64 t0_ = t0 & 0x7ffffffffffffLLU; + u64 t2_ = t2 + (t1_ >> 51); + u64 t1__ = t1_ & 0x7ffffffffffffLLU; + u64 t3_ = t3 + (t2_ >> 51); + u64 t2__ = t2_ & 0x7ffffffffffffLLU; + u64 t4_ = t4 + (t3_ >> 51); + u64 t3__ = t3_ & 0x7ffffffffffffLLU; + input[0] = t0_; + input[1] = t1__; + input[2] = t2__; + input[3] = t3__; + input[4] = t4_; +} + +static __always_inline void format_fcontract_first_carry_full(u64 *input) +{ + format_fcontract_first_carry_pass(input); + modulo_carry_top(input); +} + +static __always_inline void format_fcontract_second_carry_pass(u64 *input) +{ + u64 t0 = input[0]; + u64 t1 = input[1]; + u64 t2 = input[2]; + u64 t3 = input[3]; + u64 t4 = input[4]; + u64 t1_ = t1 + (t0 >> 51); + u64 t0_ = t0 & 0x7ffffffffffffLLU; + u64 t2_ = t2 + (t1_ >> 51); + u64 t1__ = t1_ & 0x7ffffffffffffLLU; + u64 t3_ = t3 + (t2_ >> 51); + u64 t2__ = t2_ & 0x7ffffffffffffLLU; + u64 t4_ = t4 + (t3_ >> 51); + u64 t3__ = t3_ & 0x7ffffffffffffLLU; + input[0] = t0_; + input[1] = t1__; + input[2] = t2__; + input[3] = t3__; + input[4] = t4_; +} + +static __always_inline void format_fcontract_second_carry_full(u64 *input) +{ + u64 i0; + u64 i1; + u64 i0_; + u64 i1_; + format_fcontract_second_carry_pass(input); + modulo_carry_top(input); + i0 = input[0]; + i1 = input[1]; + i0_ = i0 & 0x7ffffffffffffLLU; + i1_ = i1 + (i0 >> 51); + input[0] = i0_; + input[1] = i1_; +} + +static __always_inline void format_fcontract_trim(u64 *input) +{ + u64 a0 = input[0]; + u64 a1 = input[1]; + u64 a2 = input[2]; + u64 a3 = input[3]; + u64 a4 = input[4]; + u64 mask0 = u64_gte_mask(a0, 0x7ffffffffffedLLU); + u64 mask1 = u64_eq_mask(a1, 0x7ffffffffffffLLU); + u64 mask2 = u64_eq_mask(a2, 0x7ffffffffffffLLU); + u64 mask3 = u64_eq_mask(a3, 0x7ffffffffffffLLU); + u64 mask4 = u64_eq_mask(a4, 0x7ffffffffffffLLU); + u64 mask = (((mask0 & mask1) & mask2) & mask3) & mask4; + u64 a0_ = a0 - (0x7ffffffffffedLLU & mask); + u64 a1_ = a1 - (0x7ffffffffffffLLU & mask); + u64 a2_ = a2 - (0x7ffffffffffffLLU & mask); + u64 a3_ = a3 - (0x7ffffffffffffLLU & mask); + u64 a4_ = a4 - (0x7ffffffffffffLLU & mask); + input[0] = a0_; + input[1] = a1_; + input[2] = a2_; + input[3] = a3_; + input[4] = a4_; +} + +static __always_inline void format_fcontract_store(u8 *output, u64 *input) +{ + u64 t0 = input[0]; + u64 t1 = input[1]; + u64 t2 = input[2]; + u64 t3 = input[3]; + u64 t4 = input[4]; + u64 o0 = t1 << 51 | t0; + u64 o1 = t2 << 38 | t1 >> 13; + u64 o2 = t3 << 25 | t2 >> 26; + u64 o3 = t4 << 12 | t3 >> 39; + u8 *b0 = output; + u8 *b1 = output + 8; + u8 *b2 = output + 16; + u8 *b3 = output + 24; + put_unaligned_le64(o0, b0); + put_unaligned_le64(o1, b1); + put_unaligned_le64(o2, b2); + put_unaligned_le64(o3, b3); +} + +static __always_inline void format_fcontract(u8 *output, u64 *input) +{ + format_fcontract_first_carry_full(input); + format_fcontract_second_carry_full(input); + format_fcontract_trim(input); + format_fcontract_store(output, input); +} + +static __always_inline void format_scalar_of_point(u8 *scalar, u64 *point) +{ + u64 *x = point; + u64 *z = point + 5; + u64 buf[10] __aligned(32) = { 0 }; + u64 *zmone = buf; + u64 *sc = buf + 5; + crecip(zmone, z); + fmul(sc, x, zmone); + format_fcontract(scalar, sc); +} + +static void curve25519_generic(u8 mypublic[CURVE25519_KEY_SIZE], + const u8 secret[CURVE25519_KEY_SIZE], + const u8 basepoint[CURVE25519_KEY_SIZE]) +{ + u64 buf0[10] __aligned(32) = { 0 }; + u64 *x0 = buf0; + u64 *z = buf0 + 5; + u64 *q; + format_fexpand(x0, basepoint); + z[0] = 1; + q = buf0; + { + u8 e[32] __aligned(32) = { 0 }; + u8 *scalar; + memcpy(e, secret, 32); + curve25519_clamp_secret(e); + scalar = e; + { + u64 buf[15] = { 0 }; + u64 *nq = buf; + u64 *x = nq; + x[0] = 1; + ladder_cmult(nq, scalar, q); + format_scalar_of_point(mypublic, nq); + memzero_explicit(buf, sizeof(buf)); + } + memzero_explicit(e, sizeof(e)); + } + memzero_explicit(buf0, sizeof(buf0)); +} diff --git a/curve25519.c b/curve25519.c new file mode 100644 index 000000000000..7121d1eed124 --- /dev/null +++ b/curve25519.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include "curve25519.h" + +#include +#include + +#ifndef __BYTE_ORDER__ +#include +#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN) +#error "Unable to determine endianness." +#endif +#define __BYTE_ORDER__ BYTE_ORDER +#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN +#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN +#endif + +#ifdef __linux__ +#include +typedef __u64 u64; +typedef __u32 u32; +typedef __u8 u8; +typedef __s64 s64; +#else +typedef uint64_t u64, __le64; +typedef uint32_t u32, __le32; +typedef uint8_t u8; +typedef int64_t s64; +#endif +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define le64_to_cpup(a) __builtin_bswap64(*(a)) +#define le32_to_cpup(a) __builtin_bswap32(*(a)) +#define cpu_to_le64(a) __builtin_bswap64(a) +#else +#define le64_to_cpup(a) (*(a)) +#define le32_to_cpup(a) (*(a)) +#define cpu_to_le64(a) (a) +#endif +#ifndef __unused +#define __unused __attribute__((unused)) +#endif +#ifndef __always_inline +#define __always_inline __inline __attribute__((__always_inline__)) +#endif +#ifndef noinline +#define noinline __attribute__((noinline)) +#endif +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif +#ifndef __force +#define __force +#endif + +static __always_inline __unused __le32 get_unaligned_le32(const u8 *a) +{ + __le32 l; + __builtin_memcpy(&l, a, sizeof(l)); + return le32_to_cpup(&l); +} +static __always_inline __unused __le64 get_unaligned_le64(const u8 *a) +{ + __le64 l; + __builtin_memcpy(&l, a, sizeof(l)); + return le64_to_cpup(&l); +} +static __always_inline __unused void put_unaligned_le64(u64 s, u8 *d) +{ + __le64 l = cpu_to_le64(s); + __builtin_memcpy(d, &l, sizeof(l)); +} + +static noinline void memzero_explicit(void *s, size_t count) +{ + memset(s, 0, count); + asm volatile("": :"r"(s) : "memory"); +} + +#ifdef __SIZEOF_INT128__ +#include "curve25519-hacl64.h" +#else +#include "curve25519-fiat32.h" +#endif + +void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]) +{ + static const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 }; + + curve25519(pub, secret, basepoint); +} + +void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]) +{ + curve25519_generic(mypublic, secret, basepoint); +} diff --git a/curve25519.h b/curve25519.h new file mode 100644 index 000000000000..b05432fb1cd3 --- /dev/null +++ b/curve25519.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef CURVE25519_H +#define CURVE25519_H + +#include +#include + +enum curve25519_lengths { + CURVE25519_KEY_SIZE = 32 +}; + +void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]); +void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]); +static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE]) +{ + secret[0] &= 248; + secret[31] = (secret[31] & 127) | 64; +} + +#endif diff --git a/encoding.c b/encoding.c new file mode 100644 index 000000000000..9b2cda51e8c0 --- /dev/null +++ b/encoding.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + * + * This is a specialized constant-time base64/hex implementation that resists side-channel attacks. + */ + +#include +#include "encoding.h" + +static inline void encode_base64(char dest[static 4], const uint8_t src[static 3]) +{ + const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 }; + + for (unsigned int i = 0; i < 4; ++i) + dest[i] = input[i] + 'A' + + (((25 - input[i]) >> 8) & 6) + - (((51 - input[i]) >> 8) & 75) + - (((61 - input[i]) >> 8) & 15) + + (((62 - input[i]) >> 8) & 3); + +} + +void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]) +{ + unsigned int i; + + for (i = 0; i < WG_KEY_LEN / 3; ++i) + encode_base64(&base64[i * 4], &key[i * 3]); + encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 }); + base64[WG_KEY_LEN_BASE64 - 2] = '='; + base64[WG_KEY_LEN_BASE64 - 1] = '\0'; +} + +static inline int decode_base64(const char src[static 4]) +{ + int val = 0; + + for (unsigned int i = 0; i < 4; ++i) + val |= (-1 + + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64)) + + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70)) + + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) + + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) + + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64) + ) << (18 - 6 * i); + return val; +} + +bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64) +{ + unsigned int i; + volatile uint8_t ret = 0; + int val; + + if (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=') + return false; + + for (i = 0; i < WG_KEY_LEN / 3; ++i) { + val = decode_base64(&base64[i * 4]); + ret |= (uint32_t)val >> 31; + key[i * 3 + 0] = (val >> 16) & 0xff; + key[i * 3 + 1] = (val >> 8) & 0xff; + key[i * 3 + 2] = val & 0xff; + } + val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' }); + ret |= ((uint32_t)val >> 31) | (val & 0xff); + key[i * 3 + 0] = (val >> 16) & 0xff; + key[i * 3 + 1] = (val >> 8) & 0xff; + + return 1 & ((ret - 1) >> 8); +} + +void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]) +{ + unsigned int i; + + for (i = 0; i < WG_KEY_LEN; ++i) { + hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U); + hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U); + } + hex[i * 2] = '\0'; +} + +bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex) +{ + uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val; + volatile uint8_t ret = 0; + + if (strlen(hex) != WG_KEY_LEN_HEX - 1) + return false; + + for (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) { + c = (uint8_t)hex[i]; + c_num = c ^ 48U; + c_num0 = (c_num - 10U) >> 8; + c_alpha = (c & ~32U) - 55U; + c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; + ret |= ((c_num0 | c_alpha0) - 1) >> 8; + c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); + c_acc = c_val * 16U; + + c = (uint8_t)hex[i + 1]; + c_num = c ^ 48U; + c_num0 = (c_num - 10U) >> 8; + c_alpha = (c & ~32U) - 55U; + c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; + ret |= ((c_num0 | c_alpha0) - 1) >> 8; + c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); + key[i / 2] = c_acc | c_val; + } + + return 1 & ((ret - 1) >> 8); +} + +bool key_is_zero(const uint8_t key[static WG_KEY_LEN]) +{ + volatile uint8_t acc = 0; + + for (unsigned int i = 0; i < WG_KEY_LEN; ++i) { + acc |= key[i]; + asm volatile("" : "=r"(acc) : "0"(acc)); + } + return 1 & ((acc - 1) >> 8); +} diff --git a/encoding.h b/encoding.h new file mode 100644 index 000000000000..3cabe9cddba4 --- /dev/null +++ b/encoding.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef ENCODING_H +#define ENCODING_H + +#include +#include +#include "containers.h" + +#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1) +#define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1) + +void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]); +bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64); + +void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]); +bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex); + +bool key_is_zero(const uint8_t key[static WG_KEY_LEN]); + +#endif diff --git a/genkey.c b/genkey.c new file mode 100644 index 000000000000..0201b2826d86 --- /dev/null +++ b/genkey.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#ifdef __APPLE__ +#include +#ifndef MAC_OS_X_VERSION_10_12 +#define MAC_OS_X_VERSION_10_12 101200 +#endif +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 +#include +#endif +#endif + +#include "curve25519.h" +#include "encoding.h" +#include "subcommands.h" + +#ifndef _WIN32 +static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) +{ + ssize_t ret = 0; + size_t i; + int fd; + + if (len > 256) { + errno = EOVERFLOW; + return false; + } + +#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) + if (!getentropy(out, len)) + return true; +#endif + +#if defined(__NR_getrandom) && defined(__linux__) + if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len) + return true; +#endif + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + return false; + for (errno = 0, i = 0; i < len; i += ret, ret = 0) { + ret = read(fd, out + i, len - i); + if (ret <= 0) { + ret = errno ? -errno : -EIO; + break; + } + } + close(fd); + errno = -ret; + return i == len; +} +#else +#include +static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) +{ + return RtlGenRandom(out, len); +} +#endif + +int genkey_main(int argc, const char *argv[]) +{ + uint8_t key[WG_KEY_LEN]; + char base64[WG_KEY_LEN_BASE64]; + struct stat stat; + + if (argc != 1) { + fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]); + return 1; + } + + if (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO) + fputs("Warning: writing to world accessible file.\nConsider setting the umask to 077 and trying again.\n", stderr); + + if (!get_random_bytes(key, WG_KEY_LEN)) { + perror("getrandom"); + return 1; + } + if (!strcmp(argv[0], "genkey")) + curve25519_clamp_secret(key); + + key_to_base64(base64, key); + puts(base64); + return 0; +} diff --git a/ipc-freebsd.h b/ipc-freebsd.h new file mode 100644 index 000000000000..b5be15b82140 --- /dev/null +++ b/ipc-freebsd.h @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2015-2021 Jason A. Donenfeld . All Rights Reserved. + * + */ + +#include +#include +#include + +#define IPC_SUPPORTS_KERNEL_INTERFACE + +static int get_dgram_socket(void) +{ + static int sock = -1; + if (sock < 0) + sock = socket(AF_INET, SOCK_DGRAM, 0); + return sock; +} + +static int kernel_get_wireguard_interfaces(struct string_list *list) +{ + struct ifgroupreq ifgr = { .ifgr_name = "wg" }; + struct ifg_req *ifg; + int s = get_dgram_socket(), ret = 0; + + if (s < 0) + return -errno; + + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) + return errno == ENOENT ? 0 : -errno; + + ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len); + if (!ifgr.ifgr_groups) + return -errno; + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) { + ret = -errno; + goto out; + } + + for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) { + if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0) + goto out; + ifgr.ifgr_len -= sizeof(struct ifg_req); + } + +out: + free(ifgr.ifgr_groups); + return ret; +} + +static int kernel_get_device(struct wgdevice **device, const char *ifname) +{ + struct wg_data_io wgd = { 0 }; + nvlist_t *nvl_device = NULL; + const nvlist_t *const *nvl_peers; + struct wgdevice *dev = NULL; + size_t size, peer_count, i; + uint64_t number; + const void *binary; + int ret = 0, s; + + *device = NULL; + s = get_dgram_socket(); + if (s < 0) + goto err; + + strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name)); + if (ioctl(s, SIOCGWG, &wgd) < 0) + goto err; + + wgd.wgd_data = malloc(wgd.wgd_size); + if (!wgd.wgd_data) + goto err; + if (ioctl(s, SIOCGWG, &wgd) < 0) + goto err; + + dev = calloc(1, sizeof(*dev)); + if (!dev) + goto err; + strlcpy(dev->name, ifname, sizeof(dev->name)); + nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0); + if (!nvl_device) + goto err; + + if (nvlist_exists_number(nvl_device, "listen-port")) { + number = nvlist_get_number(nvl_device, "listen-port"); + if (number <= UINT16_MAX) { + dev->listen_port = number; + dev->flags |= WGDEVICE_HAS_LISTEN_PORT; + } + } + if (nvlist_exists_number(nvl_device, "user-cookie")) { + number = nvlist_get_number(nvl_device, "user-cookie"); + if (number <= UINT32_MAX) { + dev->fwmark = number; + dev->flags |= WGDEVICE_HAS_FWMARK; + } + } + if (nvlist_exists_binary(nvl_device, "public-key")) { + binary = nvlist_get_binary(nvl_device, "public-key", &size); + if (binary && size == sizeof(dev->public_key)) { + memcpy(dev->public_key, binary, sizeof(dev->public_key)); + dev->flags |= WGDEVICE_HAS_PUBLIC_KEY; + } + } + if (nvlist_exists_binary(nvl_device, "private-key")) { + binary = nvlist_get_binary(nvl_device, "private-key", &size); + if (binary && size == sizeof(dev->private_key)) { + memcpy(dev->private_key, binary, sizeof(dev->private_key)); + dev->flags |= WGDEVICE_HAS_PRIVATE_KEY; + } + } + if (!nvlist_exists_nvlist_array(nvl_device, "peers")) + goto skip_peers; + nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count); + if (!nvl_peers) + goto skip_peers; + for (i = 0; i < peer_count; ++i) { + struct wgpeer *peer; + struct wgallowedip *aip; + const nvlist_t *const *nvl_aips; + size_t aip_count, j; + + peer = calloc(1, sizeof(*peer)); + if (!peer) + goto err_peer; + if (nvlist_exists_binary(nvl_peers[i], "public-key")) { + binary = nvlist_get_binary(nvl_peers[i], "public-key", &size); + if (binary && size == sizeof(peer->public_key)) { + memcpy(peer->public_key, binary, sizeof(peer->public_key)); + peer->flags |= WGPEER_HAS_PUBLIC_KEY; + } + } + if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) { + binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size); + if (binary && size == sizeof(peer->preshared_key)) { + memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key)); + if (!key_is_zero(peer->preshared_key)) + peer->flags |= WGPEER_HAS_PRESHARED_KEY; + } + } + if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) { + number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval"); + if (number <= UINT16_MAX) { + peer->persistent_keepalive_interval = number; + peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; + } + } + if (nvlist_exists_binary(nvl_peers[i], "endpoint")) { + const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size); + if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) && + (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6)) + memcpy(&peer->endpoint.addr, endpoint, size); + } + if (nvlist_exists_number(nvl_peers[i], "rx-bytes")) + peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes"); + if (nvlist_exists_number(nvl_peers[i], "tx-bytes")) + peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes"); + if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) { + binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size); + if (binary && size == sizeof(peer->last_handshake_time)) + memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time)); + } + + if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips")) + goto skip_allowed_ips; + nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count); + if (!aip_count || !nvl_aips) + goto skip_allowed_ips; + for (j = 0; j < aip_count; ++j) { + aip = calloc(1, sizeof(*aip)); + if (!aip) + goto err_allowed_ips; + if (!nvlist_exists_number(nvl_aips[j], "cidr")) + continue; + number = nvlist_get_number(nvl_aips[j], "cidr"); + if (nvlist_exists_binary(nvl_aips[j], "ipv4")) { + binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size); + if (!binary || number > 32) { + ret = EINVAL; + goto err_allowed_ips; + } + aip->family = AF_INET; + aip->cidr = number; + memcpy(&aip->ip4, binary, sizeof(aip->ip4)); + } else if (nvlist_exists_binary(nvl_aips[j], "ipv6")) { + binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size); + if (!binary || number > 128) { + ret = EINVAL; + goto err_allowed_ips; + } + aip->family = AF_INET6; + aip->cidr = number; + memcpy(&aip->ip6, binary, sizeof(aip->ip6)); + } else + continue; + + if (!peer->first_allowedip) + peer->first_allowedip = aip; + else + peer->last_allowedip->next_allowedip = aip; + peer->last_allowedip = aip; + continue; + + err_allowed_ips: + if (!ret) + ret = -errno; + free(aip); + goto err_peer; + } + skip_allowed_ips: + if (!dev->first_peer) + dev->first_peer = peer; + else + dev->last_peer->next_peer = peer; + dev->last_peer = peer; + continue; + + err_peer: + if (!ret) + ret = -errno; + free(peer); + goto err; + } + +skip_peers: + free(wgd.wgd_data); + nvlist_destroy(nvl_device); + *device = dev; + return 0; + +err: + if (!ret) + ret = -errno; + free(wgd.wgd_data); + nvlist_destroy(nvl_device); + free(dev); + return ret; +} + + +static int kernel_set_device(struct wgdevice *dev) +{ + struct wg_data_io wgd = { 0 }; + nvlist_t *nvl_device = NULL, **nvl_peers = NULL; + size_t peer_count = 0, i = 0; + struct wgpeer *peer; + int ret = 0, s; + + strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name)); + + nvl_device = nvlist_create(0); + if (!nvl_device) + goto err; + + for_each_wgpeer(dev, peer) + ++peer_count; + if (peer_count) { + nvl_peers = calloc(peer_count, sizeof(*nvl_peers)); + if (!nvl_peers) + goto err; + } + if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) + nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key)); + if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) + nvlist_add_number(nvl_device, "listen-port", dev->listen_port); + if (dev->flags & WGDEVICE_HAS_FWMARK) + nvlist_add_number(nvl_device, "user-cookie", dev->fwmark); + if (dev->flags & WGDEVICE_REPLACE_PEERS) + nvlist_add_bool(nvl_device, "replace-peers", true); + + for_each_wgpeer(dev, peer) { + size_t aip_count = 0, j = 0; + nvlist_t **nvl_aips = NULL; + struct wgallowedip *aip; + + nvl_peers[i] = nvlist_create(0); + if (!nvl_peers[i]) + goto err_peer; + for_each_wgallowedip(peer, aip) + ++aip_count; + if (aip_count) { + nvl_aips = calloc(aip_count, sizeof(*nvl_aips)); + if (!nvl_aips) + goto err_peer; + } + nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key)); + if (peer->flags & WGPEER_HAS_PRESHARED_KEY) + nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key)); + if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) + nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval); + if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) + nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len); + if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) + nvlist_add_bool(nvl_peers[i], "replace-allowedips", true); + if (peer->flags & WGPEER_REMOVE_ME) + nvlist_add_bool(nvl_peers[i], "remove", true); + for_each_wgallowedip(peer, aip) { + nvl_aips[j] = nvlist_create(0); + if (!nvl_aips[j]) + goto err_peer; + nvlist_add_number(nvl_aips[j], "cidr", aip->cidr); + if (aip->family == AF_INET) + nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4)); + else if (aip->family == AF_INET6) + nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6)); + ++j; + } + if (j) { + nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j); + for (j = 0; j < aip_count; ++j) + nvlist_destroy(nvl_aips[j]); + free(nvl_aips); + } + ++i; + continue; + + err_peer: + ret = -errno; + for (j = 0; j < aip_count && nvl_aips; ++j) + nvlist_destroy(nvl_aips[j]); + free(nvl_aips); + nvlist_destroy(nvl_peers[i]); + goto err; + } + if (i) { + nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i); + for (i = 0; i < peer_count; ++i) + nvlist_destroy(nvl_peers[i]); + free(nvl_peers); + } + wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size); + nvlist_destroy(nvl_device); + if (!wgd.wgd_data) + goto err; + s = get_dgram_socket(); + if (s < 0) + return -errno; + return ioctl(s, SIOCSWG, &wgd); + +err: + if (!ret) + ret = -errno; + for (i = 0; i < peer_count && nvl_peers; ++i) + nvlist_destroy(nvl_peers[i]); + free(nvl_peers); + nvlist_destroy(nvl_device); + return ret; +} diff --git a/ipc-uapi-unix.h b/ipc-uapi-unix.h new file mode 100644 index 000000000000..aaf60ca69af9 --- /dev/null +++ b/ipc-uapi-unix.h @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCK_PATH RUNSTATEDIR "/wireguard/" +#define SOCK_SUFFIX ".sock" + +static FILE *userspace_interface_file(const char *iface) +{ + struct stat sbuf; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + int fd = -1, ret; + FILE *f = NULL; + + errno = EINVAL; + if (strchr(iface, '/')) + goto out; + ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface); + if (ret < 0) + goto out; + ret = stat(addr.sun_path, &sbuf); + if (ret < 0) + goto out; + errno = EBADF; + if (!S_ISSOCK(sbuf.st_mode)) + goto out; + + ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ret < 0) + goto out; + + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */ + unlink(addr.sun_path); + goto out; + } + f = fdopen(fd, "r+"); + if (f) + errno = 0; +out: + ret = -errno; + if (ret) { + if (fd >= 0) + close(fd); + errno = -ret; + return NULL; + } + return f; +} + +static bool userspace_has_wireguard_interface(const char *iface) +{ + struct stat sbuf; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + int fd, ret; + + if (strchr(iface, '/')) + return false; + if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0) + return false; + if (stat(addr.sun_path, &sbuf) < 0) + return false; + if (!S_ISSOCK(sbuf.st_mode)) + return false; + ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ret < 0) + return false; + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */ + close(fd); + unlink(addr.sun_path); + return false; + } + close(fd); + return true; +} + +static int userspace_get_wireguard_interfaces(struct string_list *list) +{ + DIR *dir; + struct dirent *ent; + size_t len; + char *end; + int ret = 0; + + dir = opendir(SOCK_PATH); + if (!dir) + return errno == ENOENT ? 0 : -errno; + while ((ent = readdir(dir))) { + len = strlen(ent->d_name); + if (len <= strlen(SOCK_SUFFIX)) + continue; + end = &ent->d_name[len - strlen(SOCK_SUFFIX)]; + if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX))) + continue; + *end = '\0'; + if (!userspace_has_wireguard_interface(ent->d_name)) + continue; + ret = string_list_add(list, ent->d_name); + if (ret < 0) + goto out; + } +out: + closedir(dir); + return ret; +} diff --git a/ipc-uapi.h b/ipc-uapi.h new file mode 100644 index 000000000000..f582916ecc9f --- /dev/null +++ b/ipc-uapi.h @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "containers.h" +#include "curve25519.h" +#include "encoding.h" +#include "ctype.h" + +#ifdef _WIN32 +#include "ipc-uapi-windows.h" +#else +#include "ipc-uapi-unix.h" +#endif + +static int userspace_set_device(struct wgdevice *dev) +{ + char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1]; + struct wgpeer *peer; + struct wgallowedip *allowedip; + FILE *f; + int ret, set_errno = -EPROTO; + socklen_t addr_len; + size_t line_buffer_len = 0, line_len; + char *key = NULL, *value; + + f = userspace_interface_file(dev->name); + if (!f) + return -errno; + fprintf(f, "set=1\n"); + + if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) { + key_to_hex(hex, dev->private_key); + fprintf(f, "private_key=%s\n", hex); + } + if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) + fprintf(f, "listen_port=%u\n", dev->listen_port); + if (dev->flags & WGDEVICE_HAS_FWMARK) + fprintf(f, "fwmark=%u\n", dev->fwmark); + if (dev->flags & WGDEVICE_REPLACE_PEERS) + fprintf(f, "replace_peers=true\n"); + + for_each_wgpeer(dev, peer) { + key_to_hex(hex, peer->public_key); + fprintf(f, "public_key=%s\n", hex); + if (peer->flags & WGPEER_REMOVE_ME) { + fprintf(f, "remove=true\n"); + continue; + } + if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { + key_to_hex(hex, peer->preshared_key); + fprintf(f, "preshared_key=%s\n", hex); + } + if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) { + addr_len = 0; + if (peer->endpoint.addr.sa_family == AF_INET) + addr_len = sizeof(struct sockaddr_in); + else if (peer->endpoint.addr.sa_family == AF_INET6) + addr_len = sizeof(struct sockaddr_in6); + if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) { + if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':')) + fprintf(f, "endpoint=[%s]:%s\n", host, service); + else + fprintf(f, "endpoint=%s:%s\n", host, service); + } + } + if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) + fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval); + if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) + fprintf(f, "replace_allowed_ips=true\n"); + for_each_wgallowedip(peer, allowedip) { + if (allowedip->family == AF_INET) { + if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN)) + continue; + } else if (allowedip->family == AF_INET6) { + if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN)) + continue; + } else + continue; + fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr); + } + } + fprintf(f, "\n"); + fflush(f); + + while (getline(&key, &line_buffer_len, f) > 0) { + line_len = strlen(key); + ret = set_errno; + if (line_len == 1 && key[0] == '\n') + goto out; + value = strchr(key, '='); + if (!value || line_len == 0 || key[line_len - 1] != '\n') + break; + *value++ = key[--line_len] = '\0'; + + if (!strcmp(key, "errno")) { + long long num; + char *end; + if (value[0] != '-' && !char_is_digit(value[0])) + break; + num = strtoll(value, &end, 10); + if (*end || num > INT_MAX || num < INT_MIN) + break; + set_errno = num; + } + } + ret = errno ? -errno : -EPROTO; +out: + free(key); + fclose(f); + errno = -ret; + return ret; +} + +#define NUM(max) ({ \ + unsigned long long num; \ + char *end; \ + if (!char_is_digit(value[0])) \ + break; \ + num = strtoull(value, &end, 10); \ + if (*end || num > max) \ + break; \ + num; \ +}) + +static int userspace_get_device(struct wgdevice **out, const char *iface) +{ + struct wgdevice *dev; + struct wgpeer *peer = NULL; + struct wgallowedip *allowedip = NULL; + size_t line_buffer_len = 0, line_len; + char *key = NULL, *value; + FILE *f; + int ret = -EPROTO; + + *out = dev = calloc(1, sizeof(*dev)); + if (!dev) + return -errno; + + f = userspace_interface_file(iface); + if (!f) { + ret = -errno; + free(dev); + *out = NULL; + return ret; + } + + fprintf(f, "get=1\n\n"); + fflush(f); + + strncpy(dev->name, iface, IFNAMSIZ - 1); + dev->name[IFNAMSIZ - 1] = '\0'; + + while (getline(&key, &line_buffer_len, f) > 0) { + line_len = strlen(key); + if (line_len == 1 && key[0] == '\n') + goto err; + value = strchr(key, '='); + if (!value || line_len == 0 || key[line_len - 1] != '\n') + break; + *value++ = key[--line_len] = '\0'; + + if (!peer && !strcmp(key, "private_key")) { + if (!key_from_hex(dev->private_key, value)) + break; + curve25519_generate_public(dev->public_key, dev->private_key); + dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY; + } else if (!peer && !strcmp(key, "listen_port")) { + dev->listen_port = NUM(0xffffU); + dev->flags |= WGDEVICE_HAS_LISTEN_PORT; + } else if (!peer && !strcmp(key, "fwmark")) { + dev->fwmark = NUM(0xffffffffU); + dev->flags |= WGDEVICE_HAS_FWMARK; + } else if (!strcmp(key, "public_key")) { + struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); + + if (!new_peer) { + ret = -ENOMEM; + goto err; + } + allowedip = NULL; + if (peer) + peer->next_peer = new_peer; + else + dev->first_peer = new_peer; + peer = new_peer; + if (!key_from_hex(peer->public_key, value)) + break; + peer->flags |= WGPEER_HAS_PUBLIC_KEY; + } else if (peer && !strcmp(key, "preshared_key")) { + if (!key_from_hex(peer->preshared_key, value)) + break; + if (!key_is_zero(peer->preshared_key)) + peer->flags |= WGPEER_HAS_PRESHARED_KEY; + } else if (peer && !strcmp(key, "endpoint")) { + char *begin, *end; + struct addrinfo *resolved; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP + }; + if (!strlen(value)) + break; + if (value[0] == '[') { + begin = &value[1]; + end = strchr(value, ']'); + if (!end) + break; + *end++ = '\0'; + if (*end++ != ':' || !*end) + break; + } else { + begin = value; + end = strrchr(value, ':'); + if (!end || !*(end + 1)) + break; + *end++ = '\0'; + } + if (getaddrinfo(begin, end, &hints, &resolved) != 0) { + ret = ENETUNREACH; + goto err; + } + if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) || + (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) + memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen); + else { + freeaddrinfo(resolved); + break; + } + freeaddrinfo(resolved); + } else if (peer && !strcmp(key, "persistent_keepalive_interval")) { + peer->persistent_keepalive_interval = NUM(0xffffU); + peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; + } else if (peer && !strcmp(key, "allowed_ip")) { + struct wgallowedip *new_allowedip; + char *end, *mask = value, *ip = strsep(&mask, "/"); + + if (!mask || !char_is_digit(mask[0])) + break; + new_allowedip = calloc(1, sizeof(*new_allowedip)); + if (!new_allowedip) { + ret = -ENOMEM; + goto err; + } + if (allowedip) + allowedip->next_allowedip = new_allowedip; + else + peer->first_allowedip = new_allowedip; + allowedip = new_allowedip; + allowedip->family = AF_UNSPEC; + if (strchr(ip, ':')) { + if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1) + allowedip->family = AF_INET6; + } else { + if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1) + allowedip->family = AF_INET; + } + allowedip->cidr = strtoul(mask, &end, 10); + if (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32)) + break; + } else if (peer && !strcmp(key, "last_handshake_time_sec")) + peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL); + else if (peer && !strcmp(key, "last_handshake_time_nsec")) + peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL); + else if (peer && !strcmp(key, "rx_bytes")) + peer->rx_bytes = NUM(0xffffffffffffffffULL); + else if (peer && !strcmp(key, "tx_bytes")) + peer->tx_bytes = NUM(0xffffffffffffffffULL); + else if (!strcmp(key, "errno")) + ret = -NUM(0x7fffffffU); + } + ret = -EPROTO; +err: + free(key); + if (ret) { + free_wgdevice(dev); + *out = NULL; + } + fclose(f); + errno = -ret; + return ret; + +} +#undef NUM diff --git a/ipc.c b/ipc.c new file mode 100644 index 000000000000..1155bd569913 --- /dev/null +++ b/ipc.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include "containers.h" +#include "ipc.h" + +struct string_list { + char *buffer; + size_t len; + size_t cap; +}; + +static int string_list_add(struct string_list *list, const char *str) +{ + size_t len = strlen(str) + 1; + + if (len == 1) + return 0; + + if (len >= list->cap - list->len) { + char *new_buffer; + size_t new_cap = list->cap * 2; + + if (new_cap < list->len + len + 1) + new_cap = list->len + len + 1; + new_buffer = realloc(list->buffer, new_cap); + if (!new_buffer) + return -errno; + list->buffer = new_buffer; + list->cap = new_cap; + } + memcpy(list->buffer + list->len, str, len); + list->len += len; + list->buffer[list->len] = '\0'; + return 0; +} + +#include "ipc-uapi.h" +#if defined(__linux__) +#include "ipc-linux.h" +#elif defined(__OpenBSD__) +#include "ipc-openbsd.h" +#elif defined(__FreeBSD__) +#include "ipc-freebsd.h" +#elif defined(_WIN32) +#include "ipc-windows.h" +#endif + +/* first\0second\0third\0forth\0last\0\0 */ +char *ipc_list_devices(void) +{ + struct string_list list = { 0 }; + int ret; + +#ifdef IPC_SUPPORTS_KERNEL_INTERFACE + ret = kernel_get_wireguard_interfaces(&list); + if (ret < 0) + goto cleanup; +#endif + ret = userspace_get_wireguard_interfaces(&list); + if (ret < 0) + goto cleanup; + +cleanup: + errno = -ret; + if (errno) { + free(list.buffer); + return NULL; + } + return list.buffer ?: strdup("\0"); +} + +int ipc_get_device(struct wgdevice **dev, const char *iface) +{ +#ifdef IPC_SUPPORTS_KERNEL_INTERFACE + if (userspace_has_wireguard_interface(iface)) + return userspace_get_device(dev, iface); + return kernel_get_device(dev, iface); +#else + return userspace_get_device(dev, iface); +#endif +} + +int ipc_set_device(struct wgdevice *dev) +{ +#ifdef IPC_SUPPORTS_KERNEL_INTERFACE + if (userspace_has_wireguard_interface(dev->name)) + return userspace_set_device(dev); + return kernel_set_device(dev); +#else + return userspace_set_device(dev); +#endif +} diff --git a/ipc.h b/ipc.h new file mode 100644 index 000000000000..bc0fd60bcb3a --- /dev/null +++ b/ipc.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef IPC_H +#define IPC_H + +#include + +struct wgdevice; + +int ipc_set_device(struct wgdevice *dev); +int ipc_get_device(struct wgdevice **dev, const char *interface); +char *ipc_list_devices(void); + +#endif diff --git a/man/wg.8 b/man/wg.8 new file mode 100644 index 000000000000..79845391ec02 --- /dev/null +++ b/man/wg.8 @@ -0,0 +1,258 @@ +.TH WG 8 "2015 August 13" ZX2C4 "WireGuard" + +.SH NAME +wg - set and retrieve configuration of WireGuard interfaces + +.SH SYNOPSIS +.B wg +[ +.I COMMAND +] [ +.I OPTIONS +]... [ +.I ARGS +]... + +.SH DESCRIPTION + +.B wg +is the configuration utility for getting and setting the configuration of +WireGuard tunnel interfaces. The interfaces themselves can be added and removed +using +.BR ip-link (8) +and their IP addresses and routing tables can be set using +.BR ip-address (8) +and +.BR ip-route (8). +The +.B wg +utility provides a series of sub-commands for changing WireGuard-specific +aspects of WireGuard interfaces. + +If no COMMAND is specified, COMMAND defaults to +.BR show . +Sub-commands that take an INTERFACE must be passed a WireGuard interface. + +.SH COMMANDS + +.TP +\fBshow\fP { \fI\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP] +Shows current WireGuard configuration and runtime information of specified \fI\fP. +If no \fI\fP is specified, \fI\fP defaults to \fIall\fP. +If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces, +one per line, and quits. If no options are given after the interface +specification, then prints a list of all attributes in a visually pleasing way +meant for the terminal. Otherwise, prints specified information grouped by +newlines and tabs, meant to be used in scripts. For this script-friendly display, +if \fIall\fP is specified, then the first field for all categories of information +is the interface name. If \fPdump\fP is specified, then several lines are printed; +the first contains in order separated by tab: private-key, public-key, listen-port, +fwmark. Subsequent lines are printed for each peer and contain in order separated +by tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake, +transfer-rx, transfer-tx, persistent-keepalive. +.TP +\fBshowconf\fP \fI\fP +Shows the current configuration of \fI\fP in the format described +by \fICONFIGURATION FILE FORMAT\fP below. +.TP +\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... +Sets configuration values for the specified \fI\fP. Multiple +\fIpeer\fPs may be specified, and if the \fIremove\fP argument is given +for a peer, that peer is removed, not configured. If \fIlisten-port\fP +is not specified, or set to 0, the port will be chosen randomly when the +interface comes up. Both \fIprivate-key\fP and \fIpreshared-key\fP must +be files, because command line arguments are not considered private on +most systems but if you are using +.BR bash (1), +you may safely pass in a string by specifying as \fIprivate-key\fP or +\fIpreshared-key\fP the expression: <(echo PRIVATEKEYSTRING). If +\fI/dev/null\fP or another empty file is specified as the filename for +either \fIprivate-key\fP or \fIpreshared-key\fP, the key is removed from +the device. The use of \fIpreshared-key\fP is optional, and may be omitted; +it adds an additional layer of symmetric-key cryptography to be mixed into +the already existing public-key cryptography, for post-quantum resistance. +If \fIallowed-ips\fP is specified, but the value is the empty string, all +allowed ips are removed from the peer. The use of \fIpersistent-keepalive\fP +is optional and is by default off; setting it to 0 or "off" disables it. +Otherwise it represents, in seconds, between 1 and 65535 inclusive, how often +to send an authenticated empty packet to the peer, for the purpose of keeping +a stateful firewall or NAT mapping valid persistently. For example, if the +interface very rarely sends traffic, but it might at anytime receive traffic +from a peer, and it is behind NAT, the interface might benefit from having a +persistent keepalive interval of 25 seconds; however, most users will not need +this. The use of \fIfwmark\fP is optional and is by default off; setting it to +0 or "off" disables it. Otherwise it is a 32-bit fwmark for outgoing packets +and may be specified in hexadecimal by prepending "0x". +.TP +\fBsetconf\fP \fI\fP \fI\fP +Sets the current configuration of \fI\fP to the contents of +\fI\fP, which must be in the format described +by \fICONFIGURATION FILE FORMAT\fP below. +.TP +\fBaddconf\fP \fI\fP \fI\fP +Appends the contents of \fI\fP, which must +be in the format described by \fICONFIGURATION FILE FORMAT\fP below, +to the current configuration of \fI\fP. +.TP +\fBsyncconf\fP \fI\fP \fI\fP +Like \fBsetconf\fP, but reads back the existing configuration first +and only makes changes that are explicitly different between the configuration +file and the interface. This is much less efficient than \fBsetconf\fP, +but has the benefit of not disrupting current peer sessions. The contents of +\fI\fP must be in the format described by +\fICONFIGURATION FILE FORMAT\fP below. +.TP +\fBgenkey\fP +Generates a random \fIprivate\fP key in base64 and prints it to +standard output. +.TP +\fBgenpsk\fP +Generates a random \fIpreshared\fP key in base64 and prints it to +standard output. +.TP +\fBpubkey\fP +Calculates a \fIpublic\fP key and prints it in base64 to standard +output from a corresponding \fIprivate\fP key (generated with +\fIgenkey\fP) given in base64 on standard input. + +A private key and a corresponding public key may be generated at once by calling: +.br + $ umask 077 +.br + $ wg genkey | tee private.key | wg pubkey > public.key +.TP +\fBhelp\fP +Shows usage message. + +.SH CONFIGURATION FILE FORMAT +The configuration file format is based on \fIINI\fP. There are two top level sections +-- \fIInterface\fP and \fIPeer\fP. Multiple \fIPeer\fP sections may be specified, but +only one \fIInterface\fP section may be specified. + +.P +The \fIInterface\fP section may contain the following fields: +.IP \(bu +PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. Required. +.IP \(bu +ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen +randomly. +.IP \(bu +FwMark \(em a 32-bit fwmark for outgoing packets. If set to 0 or "off", this +option is disabled. May be specified in hexadecimal by prepending "0x". Optional. +.P +The \fIPeer\fP sections may contain the following fields: +.IP \(bu +PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a +private key, and usually transmitted out of band to the author of the +configuration file. Required. +.IP \(bu +PresharedKey \(em a base64 preshared key generated by \fIwg genpsk\fP. Optional, +and may be omitted. This option adds an additional layer of symmetric-key +cryptography to be mixed into the already existing public-key cryptography, +for post-quantum resistance. +.IP \(bu +AllowedIPs \(em a comma-separated list of IP (v4 or v6) addresses with +CIDR masks from which incoming traffic for this peer is allowed and to +which outgoing traffic for this peer is directed. The catch-all +\fI0.0.0.0/0\fP may be specified for matching all IPv4 addresses, and +\fI::/0\fP may be specified for matching all IPv6 addresses. May be specified +multiple times. +.IP \(bu +Endpoint \(em an endpoint IP or hostname, followed by a colon, and then a +port number. This endpoint will be updated automatically to the most recent +source IP address and port of correctly authenticated packets from the peer. +Optional. +.IP \(bu +PersistentKeepalive \(em a seconds interval, between 1 and 65535 inclusive, of +how often to send an authenticated empty packet to the peer for the purpose of keeping a +stateful firewall or NAT mapping valid persistently. For example, if the interface +very rarely sends traffic, but it might at anytime receive traffic from a peer, +and it is behind NAT, the interface might benefit from having a persistent keepalive +interval of 25 seconds. If set to 0 or "off", this option is disabled. By default or +when unspecified, this option is off. Most users will not need this. Optional. + +.SH CONFIGURATION FILE FORMAT EXAMPLE +This example may be used as a model for writing configuration files, following an +INI-like syntax. Characters after and including a '#' are considered comments and +are thus ignored. + + [Interface] +.br + PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk= +.br + ListenPort = 51820 +.br + +.br + [Peer] +.br + PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= +.br + Endpoint = 192.95.5.67:1234 +.br + AllowedIPs = 10.192.122.3/32, 10.192.124.1/24 +.br + +.br + [Peer] +.br + PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0= +.br + Endpoint = [2607:5300:60:6b0::c05f:543]:2468 +.br + AllowedIPs = 10.192.122.4/32, 192.168.0.0/16 +.br + +.br + [Peer] +.br + PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA= +.br + Endpoint = test.wireguard.com:18981 +.br + AllowedIPs = 10.10.10.230/32 + +.SH DEBUGGING INFORMATION +Sometimes it is useful to have information on the current runtime state of a tunnel. When using the Linux kernel module on a kernel that supports dynamic debugging, debugging information can be written into +.BR dmesg (1) +by running as root: + +\fB # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\fP + +On OpenBSD and FreeBSD, debugging information can be written into +.BR dmesg (1) +on a per-interface basis by using +.BR ifconfig (1): + +\fB # ifconfig wg0 debug + +On userspace implementations, it is customary to set the \fILOG_LEVEL\fP environment variable to \fIverbose\fP. + +.SH ENVIRONMENT VARIABLES +.TP +.I WG_COLOR_MODE +If set to \fIalways\fP, always print ANSI colorized output. If set to \fInever\fP, never print ANSI colorized output. If set to \fIauto\fP, something invalid, or unset, then print ANSI colorized output only when writing to a TTY. +.TP +.I WG_HIDE_KEYS +If set to \fInever\fP, then the pretty-printing \fBshow\fP sub-command will show private and preshared keys in the output. If set to \fIalways\fP, something invalid, or unset, then private and preshared keys will be printed as "(hidden)". +.TP +.I WG_ENDPOINT_RESOLUTION_RETRIES +If set to an integer or to \fIinfinity\fP, DNS resolution for each peer's endpoint will be retried that many times for non-permanent errors, with an increasing delay between retries. If unset, the default is 15 retries. + +.SH SEE ALSO +.BR wg-quick (8), +.BR ip (8), +.BR ip-link (8), +.BR ip-address (8), +.BR ip-route (8). + +.SH AUTHOR +.B wg +was written by +.MT Jason@zx2c4.com +Jason A. Donenfeld +.ME . +For updates and more information, a project page is available on the +.UR https://\:www.wireguard.com/ +World Wide Web +.UE . diff --git a/pubkey.c b/pubkey.c new file mode 100644 index 000000000000..f191592f518c --- /dev/null +++ b/pubkey.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include + +#include "curve25519.h" +#include "encoding.h" +#include "subcommands.h" +#include "ctype.h" + +int pubkey_main(int argc, const char *argv[]) +{ + uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t)))); + char base64[WG_KEY_LEN_BASE64]; + int trailing_char; + + if (argc != 1) { + fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]); + return 1; + } + + if (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) { + errno = EINVAL; + fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME); + return 1; + } + base64[WG_KEY_LEN_BASE64 - 1] = '\0'; + + for (;;) { + trailing_char = getc(stdin); + if (!trailing_char || char_is_space(trailing_char)) + continue; + if (trailing_char == EOF) + break; + fprintf(stderr, "%s: Trailing characters found after key\n", PROG_NAME); + return 1; + } + + if (!key_from_base64(key, base64)) { + fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME); + return 1; + } + curve25519_generate_public(key, key); + key_to_base64(base64, key); + puts(base64); + return 0; +} diff --git a/set.c b/set.c new file mode 100644 index 000000000000..75560fd8cf62 --- /dev/null +++ b/set.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include + +#include "containers.h" +#include "config.h" +#include "ipc.h" +#include "subcommands.h" + +int set_main(int argc, const char *argv[]) +{ + struct wgdevice *device = NULL; + int ret = 1; + + if (argc < 3) { + fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); + return 1; + } + + device = config_read_cmd(argv + 2, argc - 2); + if (!device) + goto cleanup; + strncpy(device->name, argv[1], IFNAMSIZ - 1); + device->name[IFNAMSIZ - 1] = '\0'; + + if (ipc_set_device(device) != 0) { + perror("Unable to modify interface"); + goto cleanup; + } + + ret = 0; + +cleanup: + free_wgdevice(device); + return ret; +} diff --git a/setconf.c b/setconf.c new file mode 100644 index 000000000000..1c5b13876ff6 --- /dev/null +++ b/setconf.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include + +#include "containers.h" +#include "config.h" +#include "ipc.h" +#include "subcommands.h" + +struct pubkey_origin { + uint8_t *pubkey; + bool from_file; +}; + +static int pubkey_cmp(const void *first, const void *second) +{ + const struct pubkey_origin *a = first, *b = second; + int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN); + if (ret) + return ret; + return a->from_file - b->from_file; +} + +static bool sync_conf(struct wgdevice *file) +{ + struct wgdevice *runtime; + struct wgpeer *peer; + struct pubkey_origin *pubkeys; + size_t peer_count = 0, i = 0; + + if (!file->first_peer) + return true; + + for_each_wgpeer(file, peer) + ++peer_count; + + if (ipc_get_device(&runtime, file->name) != 0) { + perror("Unable to retrieve current interface configuration"); + return false; + } + + if (!runtime->first_peer) { + free_wgdevice(runtime); + return true; + } + + file->flags &= ~WGDEVICE_REPLACE_PEERS; + + for_each_wgpeer(runtime, peer) + ++peer_count; + + pubkeys = calloc(peer_count, sizeof(*pubkeys)); + if (!pubkeys) { + free_wgdevice(runtime); + perror("Public key allocation"); + return false; + } + + for_each_wgpeer(file, peer) { + pubkeys[i].pubkey = peer->public_key; + pubkeys[i].from_file = true; + ++i; + } + for_each_wgpeer(runtime, peer) { + pubkeys[i].pubkey = peer->public_key; + pubkeys[i].from_file = false; + ++i; + } + qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp); + + for (i = 0; i < peer_count; ++i) { + if (pubkeys[i].from_file) + continue; + if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) { + peer = calloc(1, sizeof(struct wgpeer)); + if (!peer) { + free_wgdevice(runtime); + free(pubkeys); + perror("Peer allocation"); + return false; + } + peer->flags = WGPEER_REMOVE_ME; + memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN); + peer->next_peer = file->first_peer; + file->first_peer = peer; + if (!file->last_peer) + file->last_peer = peer; + } + } + free_wgdevice(runtime); + free(pubkeys); + return true; +} + +int setconf_main(int argc, const char *argv[]) +{ + struct wgdevice *device = NULL; + struct config_ctx ctx; + FILE *config_input = NULL; + char *config_buffer = NULL; + size_t config_buffer_len = 0; + int ret = 1; + + if (argc != 3) { + fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); + return 1; + } + + config_input = fopen(argv[2], "r"); + if (!config_input) { + perror("fopen"); + return 1; + } + if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) { + fclose(config_input); + return 1; + } + while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) { + if (!config_read_line(&ctx, config_buffer)) { + fprintf(stderr, "Configuration parsing error\n"); + goto cleanup; + } + } + device = config_read_finish(&ctx); + if (!device) { + fprintf(stderr, "Invalid configuration\n"); + goto cleanup; + } + strncpy(device->name, argv[1], IFNAMSIZ - 1); + device->name[IFNAMSIZ - 1] = '\0'; + + if (!strcmp(argv[0], "syncconf")) { + if (!sync_conf(device)) + goto cleanup; + } + + if (ipc_set_device(device) != 0) { + perror("Unable to modify interface"); + goto cleanup; + } + + ret = 0; + +cleanup: + if (config_input) + fclose(config_input); + free(config_buffer); + free_wgdevice(device); + return ret; +} diff --git a/show.c b/show.c new file mode 100644 index 000000000000..a61a06ef0646 --- /dev/null +++ b/show.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "containers.h" +#include "ipc.h" +#include "terminal.h" +#include "encoding.h" +#include "subcommands.h" + +static int peer_cmp(const void *first, const void *second) +{ + time_t diff; + const struct wgpeer *a = *(const void **)first, *b = *(const void **)second; + + if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec)) + return 1; + if (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_nsec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_nsec)) + return -1; + diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec; + if (!diff) + diff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec; + if (diff < 0) + return 1; + if (diff > 0) + return -1; + return 0; +} + +/* This, hilariously, is not the right way to sort a linked list... */ +static void sort_peers(struct wgdevice *device) +{ + size_t peer_count = 0, i = 0; + struct wgpeer *peer, **peers; + + for_each_wgpeer(device, peer) + ++peer_count; + if (!peer_count) + return; + peers = calloc(peer_count, sizeof(*peers)); + if (!peers) + return; + for_each_wgpeer(device, peer) + peers[i++] = peer; + qsort(peers, peer_count, sizeof(*peers), peer_cmp); + device->first_peer = peers[0]; + for (i = 1; i < peer_count; ++i) { + peers[i - 1]->next_peer = peers[i]; + } + peers[peer_count - 1]->next_peer = NULL; + free(peers); +} + +static char *key(const uint8_t key[static WG_KEY_LEN]) +{ + static char base64[WG_KEY_LEN_BASE64]; + + key_to_base64(base64, key); + return base64; +} + +static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it) +{ + if (!have_it) + return "(none)"; + return key(maybe_key); +} + +static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN]) +{ + const char *var = getenv("WG_HIDE_KEYS"); + + if (var && !strcmp(var, "never")) + return key(masked_key); + return "(hidden)"; +} + +static char *ip(const struct wgallowedip *ip) +{ + static char buf[INET6_ADDRSTRLEN + 1]; + + memset(buf, 0, INET6_ADDRSTRLEN + 1); + if (ip->family == AF_INET) + inet_ntop(AF_INET, &ip->ip4, buf, INET6_ADDRSTRLEN); + else if (ip->family == AF_INET6) + inet_ntop(AF_INET6, &ip->ip6, buf, INET6_ADDRSTRLEN); + return buf; +} + +static char *endpoint(const struct sockaddr *addr) +{ + char host[4096 + 1]; + char service[512 + 1]; + static char buf[sizeof(host) + sizeof(service) + 4]; + int ret; + socklen_t addr_len = 0; + + memset(buf, 0, sizeof(buf)); + if (addr->sa_family == AF_INET) + addr_len = sizeof(struct sockaddr_in); + else if (addr->sa_family == AF_INET6) + addr_len = sizeof(struct sockaddr_in6); + + ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); + if (ret) { + strncpy(buf, gai_strerror(ret), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + } else + snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service); + return buf; +} + +static size_t pretty_time(char *buf, const size_t len, unsigned long long left) +{ + size_t offset = 0; + unsigned long long years, days, hours, minutes, seconds; + + years = left / (365 * 24 * 60 * 60); + left = left % (365 * 24 * 60 * 60); + days = left / (24 * 60 * 60); + left = left % (24 * 60 * 60); + hours = left / (60 * 60); + left = left % (60 * 60); + minutes = left / 60; + seconds = left % 60; + + if (years) + offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s"); + if (days) + offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s"); + if (hours) + offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s"); + if (minutes) + offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s"); + if (seconds) + offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s"); + + return offset; +} + +static char *ago(const struct timespec64 *t) +{ + static char buf[1024]; + size_t offset; + time_t now = time(NULL); + + if (now == t->tv_sec) + strncpy(buf, "Now", sizeof(buf) - 1); + else if (now < t->tv_sec) + strncpy(buf, "(" TERMINAL_FG_RED "System clock wound backward; connection problems may ensue." TERMINAL_RESET ")", sizeof(buf) - 1); + else { + offset = pretty_time(buf, sizeof(buf), now - t->tv_sec); + strncpy(buf + offset, " ago", sizeof(buf) - offset - 1); + } + buf[sizeof(buf) - 1] = '\0'; + + return buf; +} + +static char *every(uint16_t seconds) +{ + static char buf[1024] = "every "; + + pretty_time(buf + strlen("every "), sizeof(buf) - strlen("every ") - 1, seconds); + return buf; +} + +static char *bytes(uint64_t b) +{ + static char buf[1024]; + + if (b < 1024ULL) + snprintf(buf, sizeof(buf), "%u " TERMINAL_FG_CYAN "B" TERMINAL_RESET, (unsigned int)b); + else if (b < 1024ULL * 1024ULL) + snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "KiB" TERMINAL_RESET, (double)b / 1024); + else if (b < 1024ULL * 1024ULL * 1024ULL) + snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "MiB" TERMINAL_RESET, (double)b / (1024 * 1024)); + else if (b < 1024ULL * 1024ULL * 1024ULL * 1024ULL) + snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "GiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024)); + else + snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "TiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024); + + return buf; +} + +static const char *COMMAND_NAME; +static void show_usage(void) +{ + fprintf(stderr, "Usage: %s %s { | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME); +} + +static void pretty_print(struct wgdevice *device) +{ + struct wgpeer *peer; + struct wgallowedip *allowedip; + + terminal_printf(TERMINAL_RESET); + terminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD "interface" TERMINAL_RESET ": " TERMINAL_FG_GREEN "%s" TERMINAL_RESET "\n", device->name); + if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) + terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key)); + if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) + terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", masked_key(device->private_key)); + if (device->listen_port) + terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port); + if (device->fwmark) + terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark); + if (device->first_peer) { + sort_peers(device); + terminal_printf("\n"); + } + for_each_wgpeer(device, peer) { + terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key)); + if (peer->flags & WGPEER_HAS_PRESHARED_KEY) + terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key)); + if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) + terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint.addr)); + terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": "); + if (peer->first_allowedip) { + for_each_wgallowedip(peer, allowedip) + terminal_printf("%s" TERMINAL_FG_CYAN "/" TERMINAL_RESET "%u%s", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ", " : "\n"); + } else + terminal_printf("(none)\n"); + if (peer->last_handshake_time.tv_sec) + terminal_printf(" " TERMINAL_BOLD "latest handshake" TERMINAL_RESET ": %s\n", ago(&peer->last_handshake_time)); + if (peer->rx_bytes || peer->tx_bytes) { + terminal_printf(" " TERMINAL_BOLD "transfer" TERMINAL_RESET ": "); + terminal_printf("%s received, ", bytes(peer->rx_bytes)); + terminal_printf("%s sent\n", bytes(peer->tx_bytes)); + } + if (peer->persistent_keepalive_interval) + terminal_printf(" " TERMINAL_BOLD "persistent keepalive" TERMINAL_RESET ": %s\n", every(peer->persistent_keepalive_interval)); + if (peer->next_peer) + terminal_printf("\n"); + } +} + +static void dump_print(struct wgdevice *device, bool with_interface) +{ + struct wgpeer *peer; + struct wgallowedip *allowedip; + + if (with_interface) + printf("%s\t", device->name); + printf("%s\t", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY)); + printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY)); + printf("%u\t", device->listen_port); + if (device->fwmark) + printf("0x%x\n", device->fwmark); + else + printf("off\n"); + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\t", key(peer->public_key)); + printf("%s\t", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY)); + if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) + printf("%s\t", endpoint(&peer->endpoint.addr)); + else + printf("(none)\t"); + if (peer->first_allowedip) { + for_each_wgallowedip(peer, allowedip) + printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\t'); + } else + printf("(none)\t"); + printf("%llu\t", (unsigned long long)peer->last_handshake_time.tv_sec); + printf("%" PRIu64 "\t%" PRIu64 "\t", (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes); + if (peer->persistent_keepalive_interval) + printf("%u\n", peer->persistent_keepalive_interval); + else + printf("off\n"); + } +} + +static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface) +{ + struct wgpeer *peer; + struct wgallowedip *allowedip; + + if (!strcmp(param, "public-key")) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\n", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY)); + } else if (!strcmp(param, "private-key")) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\n", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY)); + } else if (!strcmp(param, "listen-port")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->listen_port); + } else if (!strcmp(param, "fwmark")) { + if (with_interface) + printf("%s\t", device->name); + if (device->fwmark) + printf("0x%x\n", device->fwmark); + else + printf("off\n"); + } else if (!strcmp(param, "endpoints")) { + if (with_interface) + printf("%s\t", device->name); + for_each_wgpeer(device, peer) { + printf("%s\t", key(peer->public_key)); + if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) + printf("%s\n", endpoint(&peer->endpoint.addr)); + else + printf("(none)\n"); + } + } else if (!strcmp(param, "allowed-ips")) { + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\t", key(peer->public_key)); + if (peer->first_allowedip) { + for_each_wgallowedip(peer, allowedip) + printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ' ' : '\n'); + } else + printf("(none)\n"); + } + } else if (!strcmp(param, "latest-handshakes")) { + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\t%llu\n", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec); + } + } else if (!strcmp(param, "transfer")) { + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\t%" PRIu64 "\t%" PRIu64 "\n", key(peer->public_key), (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes); + } + } else if (!strcmp(param, "persistent-keepalive")) { + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + if (peer->persistent_keepalive_interval) + printf("%s\t%u\n", key(peer->public_key), peer->persistent_keepalive_interval); + else + printf("%s\toff\n", key(peer->public_key)); + } + } else if (!strcmp(param, "preshared-keys")) { + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\t", key(peer->public_key)); + printf("%s\n", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY)); + } + } else if (!strcmp(param, "peers")) { + for_each_wgpeer(device, peer) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\n", key(peer->public_key)); + } + } else if (!strcmp(param, "dump")) + dump_print(device, with_interface); + else { + fprintf(stderr, "Invalid parameter: `%s'\n", param); + show_usage(); + return false; + } + return true; +} + +int show_main(int argc, const char *argv[]) +{ + int ret = 0; + + COMMAND_NAME = argv[0]; + + if (argc > 3) { + show_usage(); + return 1; + } + + if (argc == 1 || !strcmp(argv[1], "all")) { + char *interfaces = ipc_list_devices(), *interface; + + if (!interfaces) { + perror("Unable to list interfaces"); + return 1; + } + ret = !!*interfaces; + interface = interfaces; + for (size_t len = 0; (len = strlen(interface)); interface += len + 1) { + struct wgdevice *device = NULL; + + if (ipc_get_device(&device, interface) < 0) { + fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno)); + continue; + } + if (argc == 3) { + if (!ugly_print(device, argv[2], true)) { + ret = 1; + free_wgdevice(device); + break; + } + } else { + pretty_print(device); + if (strlen(interface + len + 1)) + printf("\n"); + } + free_wgdevice(device); + ret = 0; + } + free(interfaces); + } else if (!strcmp(argv[1], "interfaces")) { + char *interfaces, *interface; + + if (argc > 2) { + show_usage(); + return 1; + } + interfaces = ipc_list_devices(); + if (!interfaces) { + perror("Unable to list interfaces"); + return 1; + } + interface = interfaces; + for (size_t len = 0; (len = strlen(interface)); interface += len + 1) + printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n'); + free(interfaces); + } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) + show_usage(); + else { + struct wgdevice *device = NULL; + + if (ipc_get_device(&device, argv[1]) < 0) { + perror("Unable to access interface"); + return 1; + } + if (argc == 3) { + if (!ugly_print(device, argv[2], false)) + ret = 1; + } else + pretty_print(device); + free_wgdevice(device); + } + return ret; +} diff --git a/showconf.c b/showconf.c new file mode 100644 index 000000000000..62070dc27af2 --- /dev/null +++ b/showconf.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "containers.h" +#include "encoding.h" +#include "ipc.h" +#include "subcommands.h" + +int showconf_main(int argc, const char *argv[]) +{ + char base64[WG_KEY_LEN_BASE64]; + char ip[INET6_ADDRSTRLEN]; + struct wgdevice *device = NULL; + struct wgpeer *peer; + struct wgallowedip *allowedip; + int ret = 1; + + if (argc != 2) { + fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); + return 1; + } + + if (ipc_get_device(&device, argv[1])) { + perror("Unable to access interface"); + goto cleanup; + } + + printf("[Interface]\n"); + if (device->listen_port) + printf("ListenPort = %u\n", device->listen_port); + if (device->fwmark) + printf("FwMark = 0x%x\n", device->fwmark); + if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) { + key_to_base64(base64, device->private_key); + printf("PrivateKey = %s\n", base64); + } + printf("\n"); + for_each_wgpeer(device, peer) { + key_to_base64(base64, peer->public_key); + printf("[Peer]\nPublicKey = %s\n", base64); + if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { + key_to_base64(base64, peer->preshared_key); + printf("PresharedKey = %s\n", base64); + } + if (peer->first_allowedip) + printf("AllowedIPs = "); + for_each_wgallowedip(peer, allowedip) { + if (allowedip->family == AF_INET) { + if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN)) + continue; + } else if (allowedip->family == AF_INET6) { + if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN)) + continue; + } else + continue; + printf("%s/%d", ip, allowedip->cidr); + if (allowedip->next_allowedip) + printf(", "); + } + if (peer->first_allowedip) + printf("\n"); + + if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) { + char host[4096 + 1]; + char service[512 + 1]; + socklen_t addr_len = 0; + + if (peer->endpoint.addr.sa_family == AF_INET) + addr_len = sizeof(struct sockaddr_in); + else if (peer->endpoint.addr.sa_family == AF_INET6) + addr_len = sizeof(struct sockaddr_in6); + if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) { + if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':')) + printf("Endpoint = [%s]:%s\n", host, service); + else + printf("Endpoint = %s:%s\n", host, service); + } + } + + if (peer->persistent_keepalive_interval) + printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval); + + if (peer->next_peer) + printf("\n"); + } + ret = 0; + +cleanup: + free_wgdevice(device); + return ret; +} diff --git a/subcommands.h b/subcommands.h new file mode 100644 index 000000000000..4308b5b6eb4f --- /dev/null +++ b/subcommands.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef SUBCOMMANDS_H +#define SUBCOMMANDS_H + +extern const char *PROG_NAME; +int show_main(int argc, const char *argv[]); +int showconf_main(int argc, const char *argv[]); +int set_main(int argc, const char *argv[]); +int setconf_main(int argc, const char *argv[]); +int genkey_main(int argc, const char *argv[]); +int pubkey_main(int argc, const char *argv[]); + +#endif diff --git a/terminal.c b/terminal.c new file mode 100644 index 000000000000..d3e6611d2e5a --- /dev/null +++ b/terminal.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ctype.h" +#include "terminal.h" + +static bool color_mode(void) +{ + static int mode = -1; + const char *var; + + if (mode != -1) + return mode; + var = getenv("WG_COLOR_MODE"); + if (var && !strcmp(var, "always")) + mode = true; + else if (var && !strcmp(var, "never")) + mode = false; + else + mode = isatty(fileno(stdout)); + return mode; +} + +static void filter_ansi(const char *fmt, va_list args) +{ + char *str = NULL; + size_t len, i, j; + + if (color_mode()) { + vfprintf(stdout, fmt, args); + return; + } + + len = vasprintf(&str, fmt, args); + + if (len >= 2) { + for (i = 0; i < len - 2; ++i) { + if (str[i] == '\x1b' && str[i + 1] == '[') { + str[i] = str[i + 1] = '\0'; + for (j = i + 2; j < len; ++j) { + if (char_is_alpha(str[j])) + break; + str[j] = '\0'; + } + str[j] = '\0'; + } + } + } + for (i = 0; i < len; i = j) { + fputs(&str[i], stdout); + for (j = i + strlen(&str[i]); j < len; ++j) { + if (str[j] != '\0') + break; + } + } + + free(str); +} + +void terminal_printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + filter_ansi(fmt, args); + va_end(args); +} diff --git a/terminal.h b/terminal.h new file mode 100644 index 000000000000..50b16868cc89 --- /dev/null +++ b/terminal.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef TERMINAL_H +#define TERMINAL_H + +#define TERMINAL_FG_BLACK "\x1b[30m" +#define TERMINAL_FG_RED "\x1b[31m" +#define TERMINAL_FG_GREEN "\x1b[32m" +#define TERMINAL_FG_YELLOW "\x1b[33m" +#define TERMINAL_FG_BLUE "\x1b[34m" +#define TERMINAL_FG_MAGENTA "\x1b[35m" +#define TERMINAL_FG_CYAN "\x1b[36m" +#define TERMINAL_FG_WHITE "\x1b[37m" +#define TERMINAL_FG_DEFAULT "\x1b[39m" + +#define TERMINAL_BG_BLACK "\x1b[40m" +#define TERMINAL_BG_RED "\x1b[41m" +#define TERMINAL_BG_GREEN "\x1b[42m" +#define TERMINAL_BG_YELLOW "\x1b[43m" +#define TERMINAL_BG_BLUE "\x1b[44m" +#define TERMINAL_BG_MAGENTA "\x1b[45m" +#define TERMINAL_BG_CYAN "\x1b[46m" +#define TERMINAL_BG_WHITE "\x1b[47m" +#define TERMINAL_BG_DEFAULT "\x1b[49m" + +#define TERMINAL_BOLD "\x1b[1m" +#define TERMINAL_NO_BOLD "\x1b[22m" +#define TERMINAL_UNDERLINE "\x1b[4m" +#define TERMINAL_NO_UNDERLINE "\x1b[24m" + +#define TERMINAL_RESET "\x1b[0m" + +#define TERMINAL_SAVE_CURSOR "\x1b[s" +#define TERMINAL_RESTORE_CURSOR "\x1b[u" +#define TERMINAL_UP_CURSOR(l) "\x1b[" #l "A" +#define TERMINAL_DOWN_CURSOR(l) "\x1b[" #l "B" +#define TERMINAL_RIGHT_CURSOR(c) "\x1b[" #c "C" +#define TERMINAL_LEFT_CURSOR(c) "\x1b[" #c "D" +#define TERMINAL_CLEAR_DOWN "\x1b[0J" +#define TERMINAL_CLEAR_UP "\x1b[1J" +#define TERMINAL_CLEAR_RIGHT "\x1b[0K" +#define TERMINAL_CLEAR_LEFT "\x1b[1K" +#define TERMINAL_CLEAR_LINE "\x1b[2K" +#define TERMINAL_CLEAR_ALL "\x1b[2J" + +void terminal_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); + +#endif diff --git a/version.h b/version.h new file mode 100644 index 000000000000..c3ca131aadf4 --- /dev/null +++ b/version.h @@ -0,0 +1,3 @@ +#ifndef WIREGUARD_TOOLS_VERSION +#define WIREGUARD_TOOLS_VERSION "1.0.20210914" +#endif diff --git a/wg.c b/wg.c new file mode 100644 index 000000000000..648097061448 --- /dev/null +++ b/wg.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#include +#include +#include + +#include "subcommands.h" +#include "version.h" + +const char *PROG_NAME; + +static const struct { + const char *subcommand; + int (*function)(int, const char**); + const char *description; +} subcommands[] = { + { "show", show_main, "Shows the current configuration and device information" }, + { "showconf", showconf_main, "Shows the current configuration of a given WireGuard interface, for use with `setconf'" }, + { "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" }, + { "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" }, + { "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" }, + { "syncconf", setconf_main, "Synchronizes a configuration file to a WireGuard interface" }, + { "genkey", genkey_main, "Generates a new private key and writes it to stdout" }, + { "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" }, + { "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" } +}; + +static void show_usage(FILE *file) +{ + fprintf(file, "Usage: %s []\n\n", PROG_NAME); + fprintf(file, "Available subcommands:\n"); + for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) + fprintf(file, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description); + fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n"); +} + +int main(int argc, const char *argv[]) +{ + PROG_NAME = argv[0]; + + if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "version"))) { + printf("wireguard-tools v%s - https://git.zx2c4.com/wireguard-tools/\n", WIREGUARD_TOOLS_VERSION); + return 0; + } + if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) { + show_usage(stdout); + return 0; + } + + if (argc == 1) { + static const char *new_argv[] = { "show", NULL }; + return show_main(1, new_argv); + } + + for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) { + if (!strcmp(argv[1], subcommands[i].subcommand)) + return subcommands[i].function(argc - 1, argv + 1); + } + + fprintf(stderr, "Invalid subcommand: `%s'\n", argv[1]); + show_usage(stderr); + return 1; +}