From f187d6dfbf633665ba6740fe22742aec60ce02a2 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Mon, 15 Mar 2021 21:38:23 -0500 Subject: [PATCH] base: remove if_wg(4) and associated utilities, manpage After length decisions, we've decided that the if_wg(4) driver and related work is not yet ready to live in the tree. This driver has larger security implications than many, and thus will be held to more scrutiny than other drivers. Please also see the related message sent to the freebsd-hackers@ and freebsd-arch@ lists by Kyle Evans on 2021/03/16, with the subject line "Removing WireGuard Support From Base" for additional context. --- etc/mtree/BSD.include.dist | 2 - include/Makefile | 9 +- sbin/ifconfig/Makefile | 1 - sbin/ifconfig/ifwg.c | 731 ------- share/man/man4/Makefile | 2 - share/man/man4/wg.4 | 259 --- sys/dev/if_wg/crypto.c | 1705 --------------- sys/dev/if_wg/crypto.h | 114 - sys/dev/if_wg/if_wg.c | 3462 ------------------------------- sys/dev/if_wg/if_wg.h | 36 - sys/dev/if_wg/support.h | 56 - sys/dev/if_wg/wg_cookie.c | 438 ---- sys/dev/if_wg/wg_cookie.h | 125 -- sys/dev/if_wg/wg_noise.c | 963 --------- sys/dev/if_wg/wg_noise.h | 191 -- sys/kern/kern_jail.c | 1 - sys/kern/uipc_socket.c | 11 - sys/kern/uipc_syscalls.c | 4 +- sys/modules/Makefile | 1 - sys/modules/if_wg/Makefile | 12 - sys/net/if_types.h | 1 - sys/netinet6/nd6.c | 4 +- sys/sys/priv.h | 1 - sys/sys/socketvar.h | 1 - tests/sys/netinet/Makefile | 10 +- tests/sys/netinet/if_wg_test.sh | 188 -- 26 files changed, 8 insertions(+), 8320 deletions(-) delete mode 100644 sbin/ifconfig/ifwg.c delete mode 100644 share/man/man4/wg.4 delete mode 100644 sys/dev/if_wg/crypto.c delete mode 100644 sys/dev/if_wg/crypto.h delete mode 100644 sys/dev/if_wg/if_wg.c delete mode 100644 sys/dev/if_wg/if_wg.h delete mode 100644 sys/dev/if_wg/support.h delete mode 100644 sys/dev/if_wg/wg_cookie.c delete mode 100644 sys/dev/if_wg/wg_cookie.h delete mode 100644 sys/dev/if_wg/wg_noise.c delete mode 100644 sys/dev/if_wg/wg_noise.h delete mode 100644 sys/modules/if_wg/Makefile delete mode 100644 tests/sys/netinet/if_wg_test.sh diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 0f85798815d5..e7784cbb0a47 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -64,8 +64,6 @@ .. iicbus .. - if_wg - .. io .. mfi diff --git a/include/Makefile b/include/Makefile index d47879e11c93..eebc7f0e121f 100644 --- a/include/Makefile +++ b/include/Makefile @@ -43,7 +43,7 @@ LDIRS= geom net net80211 netgraph netinet netinet6 \ LSUBDIRS= dev/acpica dev/agp dev/an dev/ciss dev/filemon dev/firewire \ dev/hwpmc dev/hyperv \ - dev/ic dev/iicbus dev/if_wg dev/io dev/mfi dev/mmc dev/nvme \ + dev/ic dev/iicbus dev/io dev/mfi dev/mmc dev/nvme \ dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/pwm \ dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd \ fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/nullfs \ @@ -220,10 +220,6 @@ NVPAIRDIR= ${INCLUDEDIR}/sys MLX5= mlx5io.h MLX5DIR= ${INCLUDEDIR}/dev/mlx5 -.PATH: ${SRCTOP}/sys/dev/if_wg -WG= if_wg.h -WGDIR= ${INCLUDEDIR}/dev/if_wg - INCSGROUPS= INCS \ ACPICA \ AGP \ @@ -241,8 +237,7 @@ INCSGROUPS= INCS \ PCI \ RPC \ TEKEN \ - VERIEXEC \ - WG + VERIEXEC .if ${MK_AUDIT} != "no" INCSGROUPS+= BSM diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index 61cb8ab933fd..b178dc0c7e6a 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -35,7 +35,6 @@ SRCS+= ifvxlan.c # VXLAN support SRCS+= ifgre.c # GRE keys etc SRCS+= ifgif.c # GIF reversed header workaround SRCS+= ifipsec.c # IPsec VTI -SRCS+= ifwg.c # Wireguard SRCS+= sfp.c # SFP/SFP+ information LIBADD+= ifconfig m util diff --git a/sbin/ifconfig/ifwg.c b/sbin/ifconfig/ifwg.c deleted file mode 100644 index a102f392cf80..000000000000 --- a/sbin/ifconfig/ifwg.c +++ /dev/null @@ -1,731 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2020 Rubicon Communications, LLC (Netgate) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#ifndef RESCUE -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* NB: for offsetof */ -#include -#include -#include - -#include "ifconfig.h" - -static void wgfinish(int s, void *arg); - -static bool wgfinish_registered; - -static int allowed_ips_count; -static int allowed_ips_max; -static nvlist_t **allowed_ips, *nvl_peer; - -#define ALLOWEDIPS_START 16 -#define WG_KEY_SIZE_BASE64 ((((WG_KEY_SIZE) + 2) / 3) * 4 + 1) -#define WG_KEY_SIZE_HEX (WG_KEY_SIZE * 2 + 1) -#define WG_MAX_STRLEN 64 - -struct allowedip { - union { - struct in_addr ip4; - struct in6_addr ip6; - }; -}; - -static void -register_wgfinish(void) -{ - - if (wgfinish_registered) - return; - callback_register(wgfinish, NULL); - wgfinish_registered = true; -} - -static nvlist_t * -nvl_device(void) -{ - static nvlist_t *_nvl_device; - - if (_nvl_device == NULL) - _nvl_device = nvlist_create(0); - register_wgfinish(); - return (_nvl_device); -} - -static bool -key_from_base64(uint8_t key[static WG_KEY_SIZE], const char *base64) -{ - - if (strlen(base64) != WG_KEY_SIZE_BASE64 - 1) { - warnx("bad key len - need %d got %zu\n", WG_KEY_SIZE_BASE64 - 1, strlen(base64)); - return false; - } - if (base64[WG_KEY_SIZE_BASE64 - 2] != '=') { - warnx("bad key terminator, expected '=' got '%c'", base64[WG_KEY_SIZE_BASE64 - 2]); - return false; - } - return (b64_pton(base64, key, WG_KEY_SIZE)); -} - -static void -parse_endpoint(const char *endpoint_) -{ - int err; - char *base, *endpoint, *port, *colon, *tmp; - struct addrinfo hints, *res; - - endpoint = base = strdup(endpoint_); - colon = rindex(endpoint, ':'); - if (colon == NULL) - errx(1, "bad endpoint format %s - no port delimiter found", endpoint); - *colon = '\0'; - port = colon + 1; - - /* [::]:<> */ - if (endpoint[0] == '[') { - endpoint++; - tmp = index(endpoint, ']'); - if (tmp == NULL) - errx(1, "bad endpoint format %s - '[' found with no matching ']'", endpoint); - *tmp = '\0'; - } - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - err = getaddrinfo(endpoint, port, &hints, &res); - if (err) - errx(1, "%s", gai_strerror(err)); - nvlist_add_binary(nvl_peer, "endpoint", res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - free(base); -} - -static void -in_len2mask(struct in_addr *mask, u_int len) -{ - u_int i; - u_char *p; - - p = (u_char *)mask; - memset(mask, 0, sizeof(*mask)); - for (i = 0; i < len / NBBY; i++) - p[i] = 0xff; - if (len % NBBY) - p[i] = (0xff00 >> (len % NBBY)) & 0xff; -} - -static u_int -in_mask2len(struct in_addr *mask) -{ - u_int x, y; - u_char *p; - - p = (u_char *)mask; - for (x = 0; x < sizeof(*mask); x++) { - if (p[x] != 0xff) - break; - } - y = 0; - if (x < sizeof(*mask)) { - for (y = 0; y < NBBY; y++) { - if ((p[x] & (0x80 >> y)) == 0) - break; - } - } - return x * NBBY + y; -} - -static void -in6_prefixlen2mask(struct in6_addr *maskp, int len) -{ - static const u_char maskarray[NBBY] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; - int bytelen, bitlen, i; - - /* sanity check */ - if (len < 0 || len > 128) { - errx(1, "in6_prefixlen2mask: invalid prefix length(%d)\n", - len); - return; - } - - memset(maskp, 0, sizeof(*maskp)); - bytelen = len / NBBY; - bitlen = len % NBBY; - for (i = 0; i < bytelen; i++) - maskp->s6_addr[i] = 0xff; - if (bitlen) - maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; -} - -static int -in6_mask2len(struct in6_addr *mask, u_char *lim0) -{ - int x = 0, y; - u_char *lim = lim0, *p; - - /* ignore the scope_id part */ - if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) - lim = (u_char *)mask + sizeof(*mask); - for (p = (u_char *)mask; p < lim; x++, p++) { - if (*p != 0xff) - break; - } - y = 0; - if (p < lim) { - for (y = 0; y < NBBY; y++) { - if ((*p & (0x80 >> y)) == 0) - break; - } - } - - /* - * when the limit pointer is given, do a stricter check on the - * remaining bits. - */ - if (p < lim) { - if (y != 0 && (*p & (0x00ff >> y)) != 0) - return -1; - for (p = p + 1; p < lim; p++) - if (*p != 0) - return -1; - } - - return x * NBBY + y; -} - -static bool -parse_ip(struct allowedip *aip, uint16_t *family, const char *value) -{ - struct addrinfo hints, *res; - int err; - bool ret; - - ret = true; - bzero(aip, sizeof(*aip)); - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_NUMERICHOST; - err = getaddrinfo(value, NULL, &hints, &res); - if (err) - errx(1, "%s", gai_strerror(err)); - - *family = res->ai_family; - if (res->ai_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; - - aip->ip4 = sin->sin_addr; - } else if (res->ai_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; - - aip->ip6 = sin6->sin6_addr; - } else { - ret = false; - } - - freeaddrinfo(res); - return (ret); -} - -static void -sa_ntop(const struct sockaddr *sa, char *buf, int *port) -{ - const struct sockaddr_in *sin; - const struct sockaddr_in6 *sin6; - int err; - - err = getnameinfo(sa, sa->sa_len, buf, INET6_ADDRSTRLEN, NULL, - 0, NI_NUMERICHOST); - - if (sa->sa_family == AF_INET) { - sin = (const struct sockaddr_in *)sa; - if (port) - *port = sin->sin_port; - } else if (sa->sa_family == AF_INET6) { - sin6 = (const struct sockaddr_in6 *)sa; - if (port) - *port = sin6->sin6_port; - } - - if (err) - errx(1, "%s", gai_strerror(err)); -} - -static void -dump_peer(const nvlist_t *nvl_peer_cfg) -{ - const void *key; - const struct sockaddr *endpoint; - char outbuf[WG_MAX_STRLEN]; - char addr_buf[INET6_ADDRSTRLEN]; - size_t aip_count, size; - int port; - uint16_t persistent_keepalive; - const nvlist_t * const *nvl_aips; - - printf("[Peer]\n"); - if (nvlist_exists_binary(nvl_peer_cfg, "public-key")) { - key = nvlist_get_binary(nvl_peer_cfg, "public-key", &size); - b64_ntop((const uint8_t *)key, size, outbuf, WG_MAX_STRLEN); - printf("PublicKey = %s\n", outbuf); - } - if (nvlist_exists_binary(nvl_peer_cfg, "preshared-key")) { - key = nvlist_get_binary(nvl_peer_cfg, "preshared-key", &size); - b64_ntop((const uint8_t *)key, size, outbuf, WG_MAX_STRLEN); - printf("PresharedKey = %s\n", outbuf); - } - if (nvlist_exists_binary(nvl_peer_cfg, "endpoint")) { - endpoint = nvlist_get_binary(nvl_peer_cfg, "endpoint", &size); - sa_ntop(endpoint, addr_buf, &port); - printf("Endpoint = %s:%d\n", addr_buf, ntohs(port)); - } - if (nvlist_exists_number(nvl_peer_cfg, - "persistent-keepalive-interval")) { - persistent_keepalive = nvlist_get_number(nvl_peer_cfg, - "persistent-keepalive-interval"); - printf("PersistentKeepalive = %d\n", persistent_keepalive); - } - if (!nvlist_exists_nvlist_array(nvl_peer_cfg, "allowed-ips")) - return; - - nvl_aips = nvlist_get_nvlist_array(nvl_peer_cfg, "allowed-ips", &aip_count); - if (nvl_aips == NULL || aip_count == 0) - return; - - printf("AllowedIPs = "); - for (size_t i = 0; i < aip_count; i++) { - uint8_t cidr; - struct sockaddr_storage ss; - sa_family_t family; - - if (!nvlist_exists_number(nvl_aips[i], "cidr")) - continue; - cidr = nvlist_get_number(nvl_aips[i], "cidr"); - if (nvlist_exists_binary(nvl_aips[i], "ipv4")) { - struct sockaddr_in *sin = (struct sockaddr_in *)&ss; - const struct in_addr *ip4; - - ip4 = nvlist_get_binary(nvl_aips[i], "ipv4", &size); - if (ip4 == NULL || cidr > 32) - continue; - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_addr = *ip4; - } else if (nvlist_exists_binary(nvl_aips[i], "ipv6")) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; - const struct in6_addr *ip6; - - ip6 = nvlist_get_binary(nvl_aips[i], "ipv6", &size); - if (ip6 == NULL || cidr > 128) - continue; - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_addr = *ip6; - } else { - continue; - } - - family = ss.ss_family; - getnameinfo((struct sockaddr *)&ss, ss.ss_len, addr_buf, - INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); - printf("%s/%d", addr_buf, cidr); - if (i < aip_count - 1) - printf(", "); - } - printf("\n"); -} - -static int -get_nvl_out_size(int sock, u_long op, size_t *size) -{ - struct wg_data_io wgd; - int err; - - memset(&wgd, 0, sizeof(wgd)); - - strlcpy(wgd.wgd_name, name, sizeof(wgd.wgd_name)); - wgd.wgd_size = 0; - wgd.wgd_data = NULL; - - err = ioctl(sock, op, &wgd); - if (err) - return (err); - *size = wgd.wgd_size; - return (0); -} - -static int -do_cmd(int sock, u_long op, void *arg, size_t argsize, int set) -{ - struct wg_data_io wgd; - - memset(&wgd, 0, sizeof(wgd)); - - strlcpy(wgd.wgd_name, name, sizeof(wgd.wgd_name)); - wgd.wgd_size = argsize; - wgd.wgd_data = arg; - - return (ioctl(sock, op, &wgd)); -} - -static -DECL_CMD_FUNC(peerlist, val, d) -{ - size_t size, peercount; - void *packed; - const nvlist_t *nvl; - const nvlist_t *const *nvl_peerlist; - - if (get_nvl_out_size(s, SIOCGWG, &size)) - errx(1, "can't get peer list size"); - if ((packed = malloc(size)) == NULL) - errx(1, "malloc failed for peer list"); - if (do_cmd(s, SIOCGWG, packed, size, 0)) - errx(1, "failed to obtain peer list"); - - nvl = nvlist_unpack(packed, size, 0); - if (!nvlist_exists_nvlist_array(nvl, "peers")) - return; - nvl_peerlist = nvlist_get_nvlist_array(nvl, "peers", &peercount); - - for (int i = 0; i < peercount; i++, nvl_peerlist++) { - dump_peer(*nvl_peerlist); - } -} - -static void -wgfinish(int s, void *arg) -{ - void *packed; - size_t size; - static nvlist_t *nvl_dev; - - nvl_dev = nvl_device(); - if (nvl_peer != NULL) { - if (!nvlist_exists_binary(nvl_peer, "public-key")) - errx(1, "must specify a public-key for adding peer"); - if (allowed_ips_count != 0) { - nvlist_add_nvlist_array(nvl_peer, "allowed-ips", - (const nvlist_t * const *)allowed_ips, - allowed_ips_count); - for (size_t i = 0; i < allowed_ips_count; i++) { - nvlist_destroy(allowed_ips[i]); - } - - free(allowed_ips); - } - - nvlist_add_nvlist_array(nvl_dev, "peers", - (const nvlist_t * const *)&nvl_peer, 1); - } - - packed = nvlist_pack(nvl_dev, &size); - - if (do_cmd(s, SIOCSWG, packed, size, true)) - errx(1, "failed to configure"); -} - -static -DECL_CMD_FUNC(peerstart, val, d) -{ - - if (nvl_peer != NULL) - errx(1, "cannot both add and remove a peer"); - register_wgfinish(); - nvl_peer = nvlist_create(0); - allowed_ips = calloc(ALLOWEDIPS_START, sizeof(*allowed_ips)); - allowed_ips_max = ALLOWEDIPS_START; - if (allowed_ips == NULL) - errx(1, "failed to allocate array for allowedips"); -} - -static -DECL_CMD_FUNC(peerdel, val, d) -{ - - if (nvl_peer != NULL) - errx(1, "cannot both add and remove a peer"); - register_wgfinish(); - nvl_peer = nvlist_create(0); - nvlist_add_bool(nvl_peer, "remove", true); -} - -static -DECL_CMD_FUNC(setwglistenport, val, d) -{ - struct addrinfo hints, *res; - const struct sockaddr_in *sin; - const struct sockaddr_in6 *sin6; - - u_long ul; - int err; - - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_NUMERICHOST; - err = getaddrinfo(NULL, val, &hints, &res); - if (err) - errx(1, "%s", gai_strerror(err)); - - if (res->ai_family == AF_INET) { - sin = (struct sockaddr_in *)res->ai_addr; - ul = sin->sin_port; - } else if (res->ai_family == AF_INET6) { - sin6 = (struct sockaddr_in6 *)res->ai_addr; - ul = sin6->sin6_port; - } else { - errx(1, "unknown family"); - } - ul = ntohs((u_short)ul); - nvlist_add_number(nvl_device(), "listen-port", ul); -} - -static -DECL_CMD_FUNC(setwgprivkey, val, d) -{ - uint8_t key[WG_KEY_SIZE]; - - if (!key_from_base64(key, val)) - errx(1, "invalid key %s", val); - nvlist_add_binary(nvl_device(), "private-key", key, WG_KEY_SIZE); -} - -static -DECL_CMD_FUNC(setwgpubkey, val, d) -{ - uint8_t key[WG_KEY_SIZE]; - - if (nvl_peer == NULL) - errx(1, "setting public key only valid when adding peer"); - - if (!key_from_base64(key, val)) - errx(1, "invalid key %s", val); - nvlist_add_binary(nvl_peer, "public-key", key, WG_KEY_SIZE); -} - -static -DECL_CMD_FUNC(setwgpresharedkey, val, d) -{ - uint8_t key[WG_KEY_SIZE]; - - if (nvl_peer == NULL) - errx(1, "setting preshared-key only valid when adding peer"); - - if (!key_from_base64(key, val)) - errx(1, "invalid key %s", val); - nvlist_add_binary(nvl_peer, "preshared-key", key, WG_KEY_SIZE); -} - - -static -DECL_CMD_FUNC(setwgpersistentkeepalive, val, d) -{ - unsigned long persistent_keepalive; - char *endp; - - if (nvl_peer == NULL) - errx(1, "setting persistent keepalive only valid when adding peer"); - - errno = 0; - persistent_keepalive = strtoul(val, &endp, 0); - if (errno != 0 || *endp != '\0') - errx(1, "persistent-keepalive must be numeric (seconds)"); - if (persistent_keepalive > USHRT_MAX) - errx(1, "persistent-keepalive '%lu' too large", - persistent_keepalive); - nvlist_add_number(nvl_peer, "persistent-keepalive-interval", - persistent_keepalive); -} - -static -DECL_CMD_FUNC(setallowedips, val, d) -{ - char *base, *allowedip, *mask; - u_long ul; - char *endp; - struct allowedip aip; - nvlist_t *nvl_aip; - uint16_t family; - - if (nvl_peer == NULL) - errx(1, "setting allowed ip only valid when adding peer"); - if (allowed_ips_count == allowed_ips_max) { - allowed_ips_max *= 2; - allowed_ips = reallocarray(allowed_ips, allowed_ips_max, - sizeof(*allowed_ips)); - if (allowed_ips == NULL) - errx(1, "failed to grow allowed ip array"); - } - - allowed_ips[allowed_ips_count] = nvl_aip = nvlist_create(0); - if (nvl_aip == NULL) - errx(1, "failed to create new allowedip nvlist"); - - base = allowedip = strdup(val); - mask = index(allowedip, '/'); - if (mask == NULL) - errx(1, "mask separator not found in allowedip %s", val); - *mask = '\0'; - mask++; - - parse_ip(&aip, &family, allowedip); - ul = strtoul(mask, &endp, 0); - if (*endp != '\0') - errx(1, "invalid value for allowedip mask"); - - nvlist_add_number(nvl_aip, "cidr", ul); - if (family == AF_INET) { - nvlist_add_binary(nvl_aip, "ipv4", &aip.ip4, sizeof(aip.ip4)); - } else if (family == AF_INET6) { - nvlist_add_binary(nvl_aip, "ipv6", &aip.ip6, sizeof(aip.ip6)); - } else { - /* Shouldn't happen */ - nvlist_destroy(nvl_aip); - goto out; - } - - allowed_ips_count++; - -out: - free(base); -} - -static -DECL_CMD_FUNC(setendpoint, val, d) -{ - if (nvl_peer == NULL) - errx(1, "setting endpoint only valid when adding peer"); - parse_endpoint(val); -} - -static void -wireguard_status(int s) -{ - size_t size; - void *packed; - nvlist_t *nvl; - char buf[WG_KEY_SIZE_BASE64]; - const void *key; - uint16_t listen_port; - - if (get_nvl_out_size(s, SIOCGWG, &size)) - return; - if ((packed = malloc(size)) == NULL) - return; - if (do_cmd(s, SIOCGWG, packed, size, 0)) - return; - nvl = nvlist_unpack(packed, size, 0); - if (nvlist_exists_number(nvl, "listen-port")) { - listen_port = nvlist_get_number(nvl, "listen-port"); - printf("\tlisten-port: %d\n", listen_port); - } - if (nvlist_exists_binary(nvl, "private-key")) { - key = nvlist_get_binary(nvl, "private-key", &size); - b64_ntop((const uint8_t *)key, size, buf, WG_MAX_STRLEN); - printf("\tprivate-key: %s\n", buf); - } - if (nvlist_exists_binary(nvl, "public-key")) { - key = nvlist_get_binary(nvl, "public-key", &size); - b64_ntop((const uint8_t *)key, size, buf, WG_MAX_STRLEN); - printf("\tpublic-key: %s\n", buf); - } -} - -static struct cmd wireguard_cmds[] = { - DEF_CMD_ARG("listen-port", setwglistenport), - DEF_CMD_ARG("private-key", setwgprivkey), - /* XXX peer-list is deprecated. */ - DEF_CMD("peer-list", 0, peerlist), - DEF_CMD("peers", 0, peerlist), - DEF_CMD("peer", 0, peerstart), - DEF_CMD("-peer", 0, peerdel), - DEF_CMD_ARG("preshared-key", setwgpresharedkey), - DEF_CMD_ARG("public-key", setwgpubkey), - DEF_CMD_ARG("persistent-keepalive", setwgpersistentkeepalive), - DEF_CMD_ARG("allowed-ips", setallowedips), - DEF_CMD_ARG("endpoint", setendpoint), -}; - -static struct afswtch af_wireguard = { - .af_name = "af_wireguard", - .af_af = AF_UNSPEC, - .af_other_status = wireguard_status, -}; - -static void -wg_create(int s, struct ifreq *ifr) -{ - - setproctitle("ifconfig %s create ...\n", name); - - ifr->ifr_data = NULL; - if (ioctl(s, SIOCIFCREATE, ifr) < 0) - err(1, "SIOCIFCREATE"); -} - -static __constructor void -wireguard_ctor(void) -{ - int i; - - for (i = 0; i < nitems(wireguard_cmds); i++) - cmd_register(&wireguard_cmds[i]); - af_register(&af_wireguard); - clone_setdefcallback_prefix("wg", wg_create); -} - -#endif diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 54fd89fe7590..c29bb80c7a58 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -585,7 +585,6 @@ MAN= aac.4 \ vtnet.4 \ watchdog.4 \ ${_wbwd.4} \ - wg.4 \ witness.4 \ wlan.4 \ wlan_acl.4 \ @@ -768,7 +767,6 @@ MLINKS+=vr.4 if_vr.4 MLINKS+=vte.4 if_vte.4 MLINKS+=vtnet.4 if_vtnet.4 MLINKS+=watchdog.4 SW_WATCHDOG.4 -MLINKS+=wg.4 if_wg.4 MLINKS+=${_wpi.4} ${_if_wpi.4} MLINKS+=xl.4 if_xl.4 diff --git a/share/man/man4/wg.4 b/share/man/man4/wg.4 deleted file mode 100644 index 29215bd438ff..000000000000 --- a/share/man/man4/wg.4 +++ /dev/null @@ -1,259 +0,0 @@ -.\" Copyright (c) 2020 Gordon Bergling -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd March 12, 2021 -.Dt WG 4 -.Os -.Sh NAME -.Nm wg -.Nd "WireGuard - pseudo-device" -.Sh SYNOPSIS -To load the driver as a module at boot time, place the following line in -.Xr loader.conf 5 : -.Bd -literal -offset indent -if_wg_load="YES" -.Ed -.Sh DESCRIPTION -The -.Nm -driver provides Virtual Private Network (VPN) interfaces for the secure -exchange of layer 3 traffic with other WireGuard peers using the WireGuard -protocol. -.Pp -A -.Nm -interface recognises one or more peers, establishes a secure tunnel with -each on demand, and tracks each peer's UDP endpoint for exchanging encrypted -traffic with. -.Pp -The interfaces can be created at runtime using the -.Ic ifconfig Cm wg Ns Ar N Cm create -command. -The interface itself can be configured with -.Xr ifconfig 8 . -.Pp -The following parameters are available: -.Bl -tag -width indent -.It Cm listen-port -The listing port of the -.Nm -interface. -.It Cm public-key -The public key of the -.Nm -interface. -.It Cm private-key -The private key of the -.Nm -interface. -.It Cm preshared-key -Defines a pre-shared key for the -.Nm -interface. -.It Cm allowed-ips -A list of allowed IP addresses. -.It Cm endpoint -The IP address of the WiredGuard to connect to. -.It Cm peers -A list of peering IP addresses to connect to. -.It Cm persistent-keepalive-interval -Interval, in seconds, at which to send persistent keepalive packets. -.El -.Pp -The -.Nm -interfaces support the following -.Xr ioctl 2 Ns s : -.Bl -tag -width Ds -offset indent -.It Dv SIOCSWG Fa "struct wg_device_io *" -Set the device configuration. -.It Dv SIOCGWG Fa "struct wg_device_io *" -Get the device configuration. -.El -.Pp -The following glossary provides a brief overview of WireGuard -terminology: -.Bl -tag -width indent -offset 3n -.It Peer -Peers exchange IPv4 or IPv6 traffic over secure tunnels. -Each -.Nm -interface may be configured to recognise one or more peers. -.It Key -Each peer uses its private key and corresponding public key to -identify itself to others. -A peer configures a -.Nm -interface with its own private key and with the public keys of its peers. -.It Pre-shared key -In addition to the public keys, each peer pair may be configured with a -unique pre-shared symmetric key. -This is used in their handshake to guard against future compromise of the -peers' encrypted tunnel if a quantum-computational attack on their -Diffie-Hellman exchange becomes feasible. -It is optional, but recommended. -.It Allowed IPs -A single -.Nm -interface may maintain concurrent tunnels connecting diverse networks. -The interface therefore implements rudimentary routing and reverse-path -filtering functions for its tunneled traffic. -These functions reference a set of allowed IP ranges configured against -each peer. -.Pp -The interface will route outbound tunneled traffic to the peer configured -with the most specific matching allowed IP address range, or drop it -if no such match exists. -.Pp -The interface will accept tunneled traffic only from the peer -configured with the most specific matching allowed IP address range -for the incoming traffic, or drop it if no such match exists. -That is, tunneled traffic routed to a given peer cannot return through -another peer of the same -.Nm -interface. -This ensures that peers cannot spoof another's traffic. -.It Handshake -Two peers handshake to mutually authenticate each other and to -establish a shared series of secret ephemeral encryption keys. -Any peer may initiate a handshake. -Handshakes occur only when there is traffic to send, and recur every -two minutes during transfers. -.It Connectionless -Due to the handshake behavior, there is no connected or disconnected -state. -.El -.Ss Keys -Private keys for WireGuard can be generated from any sufficiently -secure random source. -The Curve25519 keys and the pre-shared keys are both 32 bytes -long and are commonly encoded in base64 for ease of use. -.Pp -Keys can be generated with -.Xr openssl 1 -as follows: -.Pp -.Dl $ openssl rand -base64 32 -.Pp -Although a valid Curve25519 key must have 5 bits set to -specific values, this is done by the interface and so it -will accept any random 32-byte base64 string. -.Pp -When an interface has a private key set with -.Nm public-key , -the corresponding -public key is shown in the status output of the interface: -.Bd -literal -offset indent -# ifconfig wg0 | grep public-key - public-key: 7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw= -.Ed -.Sh EXAMPLES -Create a -.Nm -interface and set random private key. -.Bd -literal -offset indent -# ifconfig wg0 create listen-port 54321 private-key `openssl rand -base64 32` -.Ed -.Pp -Retrieve the associated public key from a -.Nm -interface. -.Bd -literal -offset indent -$ ifconfig wg0 | awk '/public-key/ { print $2 }'` -.Ed -.Pp -Connect to a specific endpoint using its public-key and set the allowed IP address -.Bd -literal -offset indent -# ifconfig wg0 peer public-key '7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw=' endpoint 10.0.1.100:54321 allowed-ips 192.168.2.100/32 -.Ed -.Pp -Remove a peer -.Bd -literal -offset indent -# ifconfig wg0 -peer public-key '7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw=' -.Ed -.Sh DIAGNOSTICS -The -.Nm -interface supports runtime debugging, which can be enabled with: -.Pp -.D1 Ic ifconfig Cm wg Ns Ar N Cm debug -.Pp -Some common error messages include: -.Bl -diag -.It "Handshake for peer X did not complete after 5 seconds, retrying" -Peer X did not reply to our initiation packet, for example because: -.Bl -bullet -.It -The peer does not have the local interface configured as a peer. -Peers must be able to mutually authenticate each other. -.It -The peer endpoint IP address is incorrectly configured. -.It -There are firewall rules preventing communication between hosts. -.El -.It "Invalid handshake initiation" -The incoming handshake packet could not be processed. -This is likely due to the local interface not containing -the correct public key for the peer. -.It "Invalid initiation MAC" -The incoming handshake initiation packet had an invalid MAC. -This is likely because the initiation sender has the wrong public key -for the handshake receiver. -.It "Packet has unallowed src IP from peer X" -After decryption, an incoming data packet has a source IP address that -is not assigned to the allowed IPs of Peer X. -.El -.Sh SEE ALSO -.Xr inet 4 , -.Xr ip 4 , -.Xr netintro 4 , -.Xr ipf 5 , -.Xr pf.conf 5 , -.Xr ifconfig 8 , -.Xr ipfw 8 -.Rs -.%T WireGuard whitepaper -.%U https://www.wireguard.com/papers/wireguard.pdf -.Re -.Sh HISTORY -The -.Nm -device driver first appeared in -.Fx 13.0 . -.Sh AUTHORS -The -.Nm -device driver written by -.An Jason A. Donenfeld Aq Mt Jason@zx2c4.com , -.An Matt Dunwoodie Aq Mt ncon@nconroy.net , -and -.An Kyle Evans Aq Mt kevans@FreeBSD.org . -.Pp -This manual page was written by -.An Gordon Bergling Aq Mt gbe@FreeBSD.org -and is based on the -.Ox -manual page written by -.An David Gwynne Aq Mt dlg@openbsd.org . diff --git a/sys/dev/if_wg/crypto.c b/sys/dev/if_wg/crypto.c deleted file mode 100644 index f28585429272..000000000000 --- a/sys/dev/if_wg/crypto.c +++ /dev/null @@ -1,1705 +0,0 @@ -/* - * Copyright (C) 2015-2021 Jason A. Donenfeld . All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include - -#include "crypto.h" - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif -#ifndef noinline -#define noinline __attribute__((noinline)) -#endif -#ifndef __aligned -#define __aligned(x) __attribute__((aligned(x))) -#endif -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) -#endif - -#define le32_to_cpup(a) le32toh(*(a)) -#define le64_to_cpup(a) le64toh(*(a)) -#define cpu_to_le32(a) htole32(a) -#define cpu_to_le64(a) htole64(a) - -static inline uint32_t get_unaligned_le32(const uint8_t *a) -{ - uint32_t l; - __builtin_memcpy(&l, a, sizeof(l)); - return le32_to_cpup(&l); -} -static inline uint64_t get_unaligned_le64(const uint8_t *a) -{ - uint64_t l; - __builtin_memcpy(&l, a, sizeof(l)); - return le64_to_cpup(&l); -} -static inline void put_unaligned_le32(uint32_t s, uint8_t *d) -{ - uint32_t l = cpu_to_le32(s); - __builtin_memcpy(d, &l, sizeof(l)); -} -static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words) -{ - while (words--) { - *buf = cpu_to_le32(*buf); - ++buf; - } -} -static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words) -{ - while (words--) { - *buf = le32_to_cpup(buf); - ++buf; - } -} - -static inline uint32_t rol32(uint32_t word, unsigned int shift) -{ - return (word << (shift & 31)) | (word >> ((-shift) & 31)); -} -static inline uint32_t ror32(uint32_t word, unsigned int shift) -{ - return (word >> (shift & 31)) | (word << ((-shift) & 31)); -} - -static void xor_cpy(uint8_t *dst, const uint8_t *src1, const uint8_t *src2, - size_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) - dst[i] = src1[i] ^ src2[i]; -} - -#define QUARTER_ROUND(x, a, b, c, d) ( \ - x[a] += x[b], \ - x[d] = rol32((x[d] ^ x[a]), 16), \ - x[c] += x[d], \ - x[b] = rol32((x[b] ^ x[c]), 12), \ - x[a] += x[b], \ - x[d] = rol32((x[d] ^ x[a]), 8), \ - x[c] += x[d], \ - x[b] = rol32((x[b] ^ x[c]), 7) \ -) - -#define C(i, j) (i * 4 + j) - -#define DOUBLE_ROUND(x) ( \ - /* Column Round */ \ - QUARTER_ROUND(x, C(0, 0), C(1, 0), C(2, 0), C(3, 0)), \ - QUARTER_ROUND(x, C(0, 1), C(1, 1), C(2, 1), C(3, 1)), \ - QUARTER_ROUND(x, C(0, 2), C(1, 2), C(2, 2), C(3, 2)), \ - QUARTER_ROUND(x, C(0, 3), C(1, 3), C(2, 3), C(3, 3)), \ - /* Diagonal Round */ \ - QUARTER_ROUND(x, C(0, 0), C(1, 1), C(2, 2), C(3, 3)), \ - QUARTER_ROUND(x, C(0, 1), C(1, 2), C(2, 3), C(3, 0)), \ - QUARTER_ROUND(x, C(0, 2), C(1, 3), C(2, 0), C(3, 1)), \ - QUARTER_ROUND(x, C(0, 3), C(1, 0), C(2, 1), C(3, 2)) \ -) - -#define TWENTY_ROUNDS(x) ( \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x), \ - DOUBLE_ROUND(x) \ -) - -enum chacha20_lengths { - CHACHA20_NONCE_SIZE = 16, - CHACHA20_KEY_SIZE = 32, - CHACHA20_KEY_WORDS = CHACHA20_KEY_SIZE / sizeof(uint32_t), - CHACHA20_BLOCK_SIZE = 64, - CHACHA20_BLOCK_WORDS = CHACHA20_BLOCK_SIZE / sizeof(uint32_t), - HCHACHA20_NONCE_SIZE = CHACHA20_NONCE_SIZE, - HCHACHA20_KEY_SIZE = CHACHA20_KEY_SIZE -}; - -enum chacha20_constants { /* expand 32-byte k */ - CHACHA20_CONSTANT_EXPA = 0x61707865U, - CHACHA20_CONSTANT_ND_3 = 0x3320646eU, - CHACHA20_CONSTANT_2_BY = 0x79622d32U, - CHACHA20_CONSTANT_TE_K = 0x6b206574U -}; - -struct chacha20_ctx { - union { - uint32_t state[16]; - struct { - uint32_t constant[4]; - uint32_t key[8]; - uint32_t counter[4]; - }; - }; -}; - -static void chacha20_init(struct chacha20_ctx *ctx, - const uint8_t key[CHACHA20_KEY_SIZE], - const uint64_t nonce) -{ - ctx->constant[0] = CHACHA20_CONSTANT_EXPA; - ctx->constant[1] = CHACHA20_CONSTANT_ND_3; - ctx->constant[2] = CHACHA20_CONSTANT_2_BY; - ctx->constant[3] = CHACHA20_CONSTANT_TE_K; - ctx->key[0] = get_unaligned_le32(key + 0); - ctx->key[1] = get_unaligned_le32(key + 4); - ctx->key[2] = get_unaligned_le32(key + 8); - ctx->key[3] = get_unaligned_le32(key + 12); - ctx->key[4] = get_unaligned_le32(key + 16); - ctx->key[5] = get_unaligned_le32(key + 20); - ctx->key[6] = get_unaligned_le32(key + 24); - ctx->key[7] = get_unaligned_le32(key + 28); - ctx->counter[0] = 0; - ctx->counter[1] = 0; - ctx->counter[2] = nonce & 0xffffffffU; - ctx->counter[3] = nonce >> 32; -} - -static void chacha20_block(struct chacha20_ctx *ctx, uint32_t *stream) -{ - uint32_t x[CHACHA20_BLOCK_WORDS]; - int i; - - for (i = 0; i < ARRAY_SIZE(x); ++i) - x[i] = ctx->state[i]; - - TWENTY_ROUNDS(x); - - for (i = 0; i < ARRAY_SIZE(x); ++i) - stream[i] = cpu_to_le32(x[i] + ctx->state[i]); - - ctx->counter[0] += 1; -} - -static void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, - uint32_t len) -{ - uint32_t buf[CHACHA20_BLOCK_WORDS]; - - while (len >= CHACHA20_BLOCK_SIZE) { - chacha20_block(ctx, buf); - xor_cpy(out, in, (uint8_t *)buf, CHACHA20_BLOCK_SIZE); - len -= CHACHA20_BLOCK_SIZE; - out += CHACHA20_BLOCK_SIZE; - in += CHACHA20_BLOCK_SIZE; - } - if (len) { - chacha20_block(ctx, buf); - xor_cpy(out, in, (uint8_t *)buf, len); - } -} - -static void hchacha20(uint32_t derived_key[CHACHA20_KEY_WORDS], - const uint8_t nonce[HCHACHA20_NONCE_SIZE], - const uint8_t key[HCHACHA20_KEY_SIZE]) -{ - uint32_t x[] = { CHACHA20_CONSTANT_EXPA, - CHACHA20_CONSTANT_ND_3, - CHACHA20_CONSTANT_2_BY, - CHACHA20_CONSTANT_TE_K, - get_unaligned_le32(key + 0), - get_unaligned_le32(key + 4), - get_unaligned_le32(key + 8), - get_unaligned_le32(key + 12), - get_unaligned_le32(key + 16), - get_unaligned_le32(key + 20), - get_unaligned_le32(key + 24), - get_unaligned_le32(key + 28), - get_unaligned_le32(nonce + 0), - get_unaligned_le32(nonce + 4), - get_unaligned_le32(nonce + 8), - get_unaligned_le32(nonce + 12) - }; - - TWENTY_ROUNDS(x); - - memcpy(derived_key + 0, x + 0, sizeof(uint32_t) * 4); - memcpy(derived_key + 4, x + 12, sizeof(uint32_t) * 4); -} - -enum poly1305_lengths { - POLY1305_BLOCK_SIZE = 16, - POLY1305_KEY_SIZE = 32, - POLY1305_MAC_SIZE = 16 -}; - -struct poly1305_internal { - uint32_t h[5]; - uint32_t r[5]; - uint32_t s[4]; -}; - -struct poly1305_ctx { - struct poly1305_internal state; - uint32_t nonce[4]; - uint8_t data[POLY1305_BLOCK_SIZE]; - size_t num; -}; - -static void poly1305_init_core(struct poly1305_internal *st, - const uint8_t key[16]) -{ - /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ - st->r[0] = (get_unaligned_le32(&key[0])) & 0x3ffffff; - st->r[1] = (get_unaligned_le32(&key[3]) >> 2) & 0x3ffff03; - st->r[2] = (get_unaligned_le32(&key[6]) >> 4) & 0x3ffc0ff; - st->r[3] = (get_unaligned_le32(&key[9]) >> 6) & 0x3f03fff; - st->r[4] = (get_unaligned_le32(&key[12]) >> 8) & 0x00fffff; - - /* s = 5*r */ - st->s[0] = st->r[1] * 5; - st->s[1] = st->r[2] * 5; - st->s[2] = st->r[3] * 5; - st->s[3] = st->r[4] * 5; - - /* h = 0 */ - st->h[0] = 0; - st->h[1] = 0; - st->h[2] = 0; - st->h[3] = 0; - st->h[4] = 0; -} - -static void poly1305_blocks_core(struct poly1305_internal *st, - const uint8_t *input, size_t len, - const uint32_t padbit) -{ - const uint32_t hibit = padbit << 24; - uint32_t r0, r1, r2, r3, r4; - uint32_t s1, s2, s3, s4; - uint32_t h0, h1, h2, h3, h4; - uint64_t d0, d1, d2, d3, d4; - uint32_t c; - - r0 = st->r[0]; - r1 = st->r[1]; - r2 = st->r[2]; - r3 = st->r[3]; - r4 = st->r[4]; - - s1 = st->s[0]; - s2 = st->s[1]; - s3 = st->s[2]; - s4 = st->s[3]; - - h0 = st->h[0]; - h1 = st->h[1]; - h2 = st->h[2]; - h3 = st->h[3]; - h4 = st->h[4]; - - while (len >= POLY1305_BLOCK_SIZE) { - /* h += m[i] */ - h0 += (get_unaligned_le32(&input[0])) & 0x3ffffff; - h1 += (get_unaligned_le32(&input[3]) >> 2) & 0x3ffffff; - h2 += (get_unaligned_le32(&input[6]) >> 4) & 0x3ffffff; - h3 += (get_unaligned_le32(&input[9]) >> 6) & 0x3ffffff; - h4 += (get_unaligned_le32(&input[12]) >> 8) | hibit; - - /* h *= r */ - d0 = ((uint64_t)h0 * r0) + ((uint64_t)h1 * s4) + - ((uint64_t)h2 * s3) + ((uint64_t)h3 * s2) + - ((uint64_t)h4 * s1); - d1 = ((uint64_t)h0 * r1) + ((uint64_t)h1 * r0) + - ((uint64_t)h2 * s4) + ((uint64_t)h3 * s3) + - ((uint64_t)h4 * s2); - d2 = ((uint64_t)h0 * r2) + ((uint64_t)h1 * r1) + - ((uint64_t)h2 * r0) + ((uint64_t)h3 * s4) + - ((uint64_t)h4 * s3); - d3 = ((uint64_t)h0 * r3) + ((uint64_t)h1 * r2) + - ((uint64_t)h2 * r1) + ((uint64_t)h3 * r0) + - ((uint64_t)h4 * s4); - d4 = ((uint64_t)h0 * r4) + ((uint64_t)h1 * r3) + - ((uint64_t)h2 * r2) + ((uint64_t)h3 * r1) + - ((uint64_t)h4 * r0); - - /* (partial) h %= p */ - c = (uint32_t)(d0 >> 26); - h0 = (uint32_t)d0 & 0x3ffffff; - d1 += c; - c = (uint32_t)(d1 >> 26); - h1 = (uint32_t)d1 & 0x3ffffff; - d2 += c; - c = (uint32_t)(d2 >> 26); - h2 = (uint32_t)d2 & 0x3ffffff; - d3 += c; - c = (uint32_t)(d3 >> 26); - h3 = (uint32_t)d3 & 0x3ffffff; - d4 += c; - c = (uint32_t)(d4 >> 26); - h4 = (uint32_t)d4 & 0x3ffffff; - h0 += c * 5; - c = (h0 >> 26); - h0 = h0 & 0x3ffffff; - h1 += c; - - input += POLY1305_BLOCK_SIZE; - len -= POLY1305_BLOCK_SIZE; - } - - st->h[0] = h0; - st->h[1] = h1; - st->h[2] = h2; - st->h[3] = h3; - st->h[4] = h4; -} - -static void poly1305_emit_core(struct poly1305_internal *st, uint8_t mac[16], - const uint32_t nonce[4]) -{ - uint32_t h0, h1, h2, h3, h4, c; - uint32_t g0, g1, g2, g3, g4; - uint64_t f; - uint32_t mask; - - /* fully carry h */ - h0 = st->h[0]; - h1 = st->h[1]; - h2 = st->h[2]; - h3 = st->h[3]; - h4 = st->h[4]; - - c = h1 >> 26; - h1 = h1 & 0x3ffffff; - h2 += c; - c = h2 >> 26; - h2 = h2 & 0x3ffffff; - h3 += c; - c = h3 >> 26; - h3 = h3 & 0x3ffffff; - h4 += c; - c = h4 >> 26; - h4 = h4 & 0x3ffffff; - h0 += c * 5; - c = h0 >> 26; - h0 = h0 & 0x3ffffff; - h1 += c; - - /* compute h + -p */ - g0 = h0 + 5; - c = g0 >> 26; - g0 &= 0x3ffffff; - g1 = h1 + c; - c = g1 >> 26; - g1 &= 0x3ffffff; - g2 = h2 + c; - c = g2 >> 26; - g2 &= 0x3ffffff; - g3 = h3 + c; - c = g3 >> 26; - g3 &= 0x3ffffff; - g4 = h4 + c - (1UL << 26); - - /* select h if h < p, or h + -p if h >= p */ - mask = (g4 >> ((sizeof(uint32_t) * 8) - 1)) - 1; - g0 &= mask; - g1 &= mask; - g2 &= mask; - g3 &= mask; - g4 &= mask; - mask = ~mask; - - h0 = (h0 & mask) | g0; - h1 = (h1 & mask) | g1; - h2 = (h2 & mask) | g2; - h3 = (h3 & mask) | g3; - h4 = (h4 & mask) | g4; - - /* h = h % (2^128) */ - h0 = ((h0) | (h1 << 26)) & 0xffffffff; - h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; - h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; - h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; - - /* mac = (h + nonce) % (2^128) */ - f = (uint64_t)h0 + nonce[0]; - h0 = (uint32_t)f; - f = (uint64_t)h1 + nonce[1] + (f >> 32); - h1 = (uint32_t)f; - f = (uint64_t)h2 + nonce[2] + (f >> 32); - h2 = (uint32_t)f; - f = (uint64_t)h3 + nonce[3] + (f >> 32); - h3 = (uint32_t)f; - - put_unaligned_le32(h0, &mac[0]); - put_unaligned_le32(h1, &mac[4]); - put_unaligned_le32(h2, &mac[8]); - put_unaligned_le32(h3, &mac[12]); -} - -static void poly1305_init(struct poly1305_ctx *ctx, - const uint8_t key[POLY1305_KEY_SIZE]) -{ - ctx->nonce[0] = get_unaligned_le32(&key[16]); - ctx->nonce[1] = get_unaligned_le32(&key[20]); - ctx->nonce[2] = get_unaligned_le32(&key[24]); - ctx->nonce[3] = get_unaligned_le32(&key[28]); - - poly1305_init_core(&ctx->state, key); - - ctx->num = 0; -} - -static void poly1305_update(struct poly1305_ctx *ctx, const uint8_t *input, - size_t len) -{ - const size_t num = ctx->num; - size_t rem; - - if (num) { - rem = POLY1305_BLOCK_SIZE - num; - if (len < rem) { - memcpy(ctx->data + num, input, len); - ctx->num = num + len; - return; - } - memcpy(ctx->data + num, input, rem); - poly1305_blocks_core(&ctx->state, ctx->data, - POLY1305_BLOCK_SIZE, 1); - input += rem; - len -= rem; - } - - rem = len % POLY1305_BLOCK_SIZE; - len -= rem; - - if (len >= POLY1305_BLOCK_SIZE) { - poly1305_blocks_core(&ctx->state, input, len, 1); - input += len; - } - - if (rem) - memcpy(ctx->data, input, rem); - - ctx->num = rem; -} - -static void poly1305_final(struct poly1305_ctx *ctx, - uint8_t mac[POLY1305_MAC_SIZE]) -{ - size_t num = ctx->num; - - if (num) { - ctx->data[num++] = 1; - while (num < POLY1305_BLOCK_SIZE) - ctx->data[num++] = 0; - poly1305_blocks_core(&ctx->state, ctx->data, - POLY1305_BLOCK_SIZE, 0); - } - - poly1305_emit_core(&ctx->state, mac, ctx->nonce); - - explicit_bzero(ctx, sizeof(*ctx)); -} - - -static const uint8_t pad0[16] = { 0 }; - -void -chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, const size_t src_len, - const uint8_t *ad, const size_t ad_len, - const uint64_t nonce, - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) -{ - struct poly1305_ctx poly1305_state; - struct chacha20_ctx chacha20_state; - union { - uint8_t block0[POLY1305_KEY_SIZE]; - uint64_t lens[2]; - } b = { { 0 } }; - - chacha20_init(&chacha20_state, key, nonce); - chacha20(&chacha20_state, b.block0, b.block0, sizeof(b.block0)); - poly1305_init(&poly1305_state, b.block0); - - poly1305_update(&poly1305_state, ad, ad_len); - poly1305_update(&poly1305_state, pad0, (0x10 - ad_len) & 0xf); - - chacha20(&chacha20_state, dst, src, src_len); - - poly1305_update(&poly1305_state, dst, src_len); - poly1305_update(&poly1305_state, pad0, (0x10 - src_len) & 0xf); - - b.lens[0] = cpu_to_le64(ad_len); - b.lens[1] = cpu_to_le64(src_len); - poly1305_update(&poly1305_state, (uint8_t *)b.lens, sizeof(b.lens)); - - poly1305_final(&poly1305_state, dst + src_len); - - explicit_bzero(&chacha20_state, sizeof(chacha20_state)); - explicit_bzero(&b, sizeof(b)); -} - -bool -chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len, - const uint8_t *ad, const size_t ad_len, - const uint64_t nonce, - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) -{ - struct poly1305_ctx poly1305_state; - struct chacha20_ctx chacha20_state; - bool ret; - size_t dst_len; - union { - uint8_t block0[POLY1305_KEY_SIZE]; - uint8_t mac[POLY1305_MAC_SIZE]; - uint64_t lens[2]; - } b = { { 0 } }; - - if (src_len < POLY1305_MAC_SIZE) - return false; - - chacha20_init(&chacha20_state, key, nonce); - chacha20(&chacha20_state, b.block0, b.block0, sizeof(b.block0)); - poly1305_init(&poly1305_state, b.block0); - - poly1305_update(&poly1305_state, ad, ad_len); - poly1305_update(&poly1305_state, pad0, (0x10 - ad_len) & 0xf); - - dst_len = src_len - POLY1305_MAC_SIZE; - poly1305_update(&poly1305_state, src, dst_len); - poly1305_update(&poly1305_state, pad0, (0x10 - dst_len) & 0xf); - - b.lens[0] = cpu_to_le64(ad_len); - b.lens[1] = cpu_to_le64(dst_len); - poly1305_update(&poly1305_state, (uint8_t *)b.lens, sizeof(b.lens)); - - poly1305_final(&poly1305_state, b.mac); - - ret = timingsafe_bcmp(b.mac, src + dst_len, POLY1305_MAC_SIZE) == 0; - if (ret) - chacha20(&chacha20_state, dst, src, dst_len); - - explicit_bzero(&chacha20_state, sizeof(chacha20_state)); - explicit_bzero(&b, sizeof(b)); - - return ret; -} - -void -xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, - const size_t src_len, const uint8_t *ad, - const size_t ad_len, - const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) -{ - uint32_t derived_key[CHACHA20_KEY_WORDS]; - - hchacha20(derived_key, nonce, key); - cpu_to_le32_array(derived_key, ARRAY_SIZE(derived_key)); - chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len, - get_unaligned_le64(nonce + 16), - (uint8_t *)derived_key); - explicit_bzero(derived_key, CHACHA20POLY1305_KEY_SIZE); -} - -bool -xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, - const size_t src_len, const uint8_t *ad, - const size_t ad_len, - const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) -{ - bool ret; - uint32_t derived_key[CHACHA20_KEY_WORDS]; - - hchacha20(derived_key, nonce, key); - cpu_to_le32_array(derived_key, ARRAY_SIZE(derived_key)); - ret = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len, - get_unaligned_le64(nonce + 16), - (uint8_t *)derived_key); - explicit_bzero(derived_key, CHACHA20POLY1305_KEY_SIZE); - return ret; -} - - -static const uint32_t blake2s_iv[8] = { - 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, - 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL -}; - -static const uint8_t blake2s_sigma[10][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, -}; - -static inline void blake2s_set_lastblock(struct blake2s_state *state) -{ - state->f[0] = -1; -} - -static inline void blake2s_increment_counter(struct blake2s_state *state, - const uint32_t inc) -{ - state->t[0] += inc; - state->t[1] += (state->t[0] < inc); -} - -static inline void blake2s_init_param(struct blake2s_state *state, - const uint32_t param) -{ - int i; - - memset(state, 0, sizeof(*state)); - for (i = 0; i < 8; ++i) - state->h[i] = blake2s_iv[i]; - state->h[0] ^= param; -} - -void blake2s_init(struct blake2s_state *state, const size_t outlen) -{ - blake2s_init_param(state, 0x01010000 | outlen); - state->outlen = outlen; -} - -void blake2s_init_key(struct blake2s_state *state, const size_t outlen, - const uint8_t *key, const size_t keylen) -{ - uint8_t block[BLAKE2S_BLOCK_SIZE] = { 0 }; - - blake2s_init_param(state, 0x01010000 | keylen << 8 | outlen); - state->outlen = outlen; - memcpy(block, key, keylen); - blake2s_update(state, block, BLAKE2S_BLOCK_SIZE); - explicit_bzero(block, BLAKE2S_BLOCK_SIZE); -} - -static inline void blake2s_compress(struct blake2s_state *state, - const uint8_t *block, size_t nblocks, - const uint32_t inc) -{ - uint32_t m[16]; - uint32_t v[16]; - int i; - - while (nblocks > 0) { - blake2s_increment_counter(state, inc); - memcpy(m, block, BLAKE2S_BLOCK_SIZE); - le32_to_cpu_array(m, ARRAY_SIZE(m)); - memcpy(v, state->h, 32); - v[ 8] = blake2s_iv[0]; - v[ 9] = blake2s_iv[1]; - v[10] = blake2s_iv[2]; - v[11] = blake2s_iv[3]; - v[12] = blake2s_iv[4] ^ state->t[0]; - v[13] = blake2s_iv[5] ^ state->t[1]; - v[14] = blake2s_iv[6] ^ state->f[0]; - v[15] = blake2s_iv[7] ^ state->f[1]; - -#define G(r, i, a, b, c, d) do { \ - a += b + m[blake2s_sigma[r][2 * i + 0]]; \ - d = ror32(d ^ a, 16); \ - c += d; \ - b = ror32(b ^ c, 12); \ - a += b + m[blake2s_sigma[r][2 * i + 1]]; \ - d = ror32(d ^ a, 8); \ - c += d; \ - b = ror32(b ^ c, 7); \ -} while (0) - -#define ROUND(r) do { \ - G(r, 0, v[0], v[ 4], v[ 8], v[12]); \ - G(r, 1, v[1], v[ 5], v[ 9], v[13]); \ - G(r, 2, v[2], v[ 6], v[10], v[14]); \ - G(r, 3, v[3], v[ 7], v[11], v[15]); \ - G(r, 4, v[0], v[ 5], v[10], v[15]); \ - G(r, 5, v[1], v[ 6], v[11], v[12]); \ - G(r, 6, v[2], v[ 7], v[ 8], v[13]); \ - G(r, 7, v[3], v[ 4], v[ 9], v[14]); \ -} while (0) - ROUND(0); - ROUND(1); - ROUND(2); - ROUND(3); - ROUND(4); - ROUND(5); - ROUND(6); - ROUND(7); - ROUND(8); - ROUND(9); - -#undef G -#undef ROUND - - for (i = 0; i < 8; ++i) - state->h[i] ^= v[i] ^ v[i + 8]; - - block += BLAKE2S_BLOCK_SIZE; - --nblocks; - } -} - -void blake2s_update(struct blake2s_state *state, const uint8_t *in, size_t inlen) -{ - const size_t fill = BLAKE2S_BLOCK_SIZE - state->buflen; - - if (!inlen) - return; - if (inlen > fill) { - memcpy(state->buf + state->buflen, in, fill); - blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_SIZE); - state->buflen = 0; - in += fill; - inlen -= fill; - } - if (inlen > BLAKE2S_BLOCK_SIZE) { - const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_SIZE); - /* Hash one less (full) block than strictly possible */ - blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_SIZE); - in += BLAKE2S_BLOCK_SIZE * (nblocks - 1); - inlen -= BLAKE2S_BLOCK_SIZE * (nblocks - 1); - } - memcpy(state->buf + state->buflen, in, inlen); - state->buflen += inlen; -} - -void blake2s_final(struct blake2s_state *state, uint8_t *out) -{ - blake2s_set_lastblock(state); - memset(state->buf + state->buflen, 0, - BLAKE2S_BLOCK_SIZE - state->buflen); /* Padding */ - blake2s_compress(state, state->buf, 1, state->buflen); - cpu_to_le32_array(state->h, ARRAY_SIZE(state->h)); - memcpy(out, state->h, state->outlen); - explicit_bzero(state, sizeof(*state)); -} - -void blake2s(uint8_t *out, const uint8_t *in, const uint8_t *key, - const size_t outlen, const size_t inlen, const size_t keylen) -{ - struct blake2s_state state; - - if (keylen) - blake2s_init_key(&state, outlen, key, keylen); - else - blake2s_init(&state, outlen); - - blake2s_update(&state, in, inlen); - blake2s_final(&state, out); -} - -void blake2s_hmac(uint8_t *out, const uint8_t *in, const uint8_t *key, const size_t outlen, - const size_t inlen, const size_t keylen) -{ - struct blake2s_state state; - uint8_t x_key[BLAKE2S_BLOCK_SIZE] __aligned(sizeof(uint32_t)) = { 0 }; - uint8_t i_hash[BLAKE2S_HASH_SIZE] __aligned(sizeof(uint32_t)); - int i; - - if (keylen > BLAKE2S_BLOCK_SIZE) { - blake2s_init(&state, BLAKE2S_HASH_SIZE); - blake2s_update(&state, key, keylen); - blake2s_final(&state, x_key); - } else - memcpy(x_key, key, keylen); - - for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i) - x_key[i] ^= 0x36; - - blake2s_init(&state, BLAKE2S_HASH_SIZE); - blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE); - blake2s_update(&state, in, inlen); - blake2s_final(&state, i_hash); - - for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i) - x_key[i] ^= 0x5c ^ 0x36; - - blake2s_init(&state, BLAKE2S_HASH_SIZE); - blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE); - blake2s_update(&state, i_hash, BLAKE2S_HASH_SIZE); - blake2s_final(&state, i_hash); - - memcpy(out, i_hash, outlen); - explicit_bzero(x_key, BLAKE2S_BLOCK_SIZE); - explicit_bzero(i_hash, BLAKE2S_HASH_SIZE); -} - - -/* Below here is fiat's implementation of x25519. - * - * Copyright (C) 2015-2016 The fiat-crypto Authors. - * Copyright (C) 2018-2021 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 { uint32_t 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 { uint32_t v[10]; } fe_loose; - -static inline void fe_frombytes_impl(uint32_t h[10], const uint8_t *s) -{ - /* Ignores top bit of s. */ - uint32_t a0 = get_unaligned_le32(s); - uint32_t a1 = get_unaligned_le32(s+4); - uint32_t a2 = get_unaligned_le32(s+8); - uint32_t a3 = get_unaligned_le32(s+12); - uint32_t a4 = get_unaligned_le32(s+16); - uint32_t a5 = get_unaligned_le32(s+20); - uint32_t a6 = get_unaligned_le32(s+24); - uint32_t 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 inline void fe_frombytes(fe *h, const uint8_t *s) -{ - fe_frombytes_impl(h->v, s); -} - -static inline uint8_t /*bool*/ -addcarryx_u25(uint8_t /*bool*/ c, uint32_t a, uint32_t b, uint32_t *low) -{ - /* This function extracts 25 bits of result and 1 bit of carry - * (26 total), so a 32-bit intermediate is sufficient. - */ - uint32_t x = a + b + c; - *low = x & ((1 << 25) - 1); - return (x >> 25) & 1; -} - -static inline uint8_t /*bool*/ -addcarryx_u26(uint8_t /*bool*/ c, uint32_t a, uint32_t b, uint32_t *low) -{ - /* This function extracts 26 bits of result and 1 bit of carry - * (27 total), so a 32-bit intermediate is sufficient. - */ - uint32_t x = a + b + c; - *low = x & ((1 << 26) - 1); - return (x >> 26) & 1; -} - -static inline uint8_t /*bool*/ -subborrow_u25(uint8_t /*bool*/ c, uint32_t a, uint32_t b, uint32_t *low) -{ - /* This function extracts 25 bits of result and 1 bit of borrow - * (26 total), so a 32-bit intermediate is sufficient. - */ - uint32_t x = a - b - c; - *low = x & ((1 << 25) - 1); - return x >> 31; -} - -static inline uint8_t /*bool*/ -subborrow_u26(uint8_t /*bool*/ c, uint32_t a, uint32_t b, uint32_t *low) -{ - /* This function extracts 26 bits of result and 1 bit of borrow - *(27 total), so a 32-bit intermediate is sufficient. - */ - uint32_t x = a - b - c; - *low = x & ((1 << 26) - 1); - return x >> 31; -} - -static inline uint32_t cmovznz32(uint32_t t, uint32_t z, uint32_t nz) -{ - t = -!!t; /* all set if nonzero, 0 if 0 */ - return (t&nz) | ((~t)&z); -} - -static inline void fe_freeze(uint32_t out[10], const uint32_t in1[10]) -{ - const uint32_t x17 = in1[9]; - const uint32_t x18 = in1[8]; - const uint32_t x16 = in1[7]; - const uint32_t x14 = in1[6]; - const uint32_t x12 = in1[5]; - const uint32_t x10 = in1[4]; - const uint32_t x8 = in1[3]; - const uint32_t x6 = in1[2]; - const uint32_t x4 = in1[1]; - const uint32_t x2 = in1[0]; - uint32_t x20; uint8_t/*bool*/ x21 = subborrow_u26(0x0, x2, 0x3ffffed, &x20); - uint32_t x23; uint8_t/*bool*/ x24 = subborrow_u25(x21, x4, 0x1ffffff, &x23); - uint32_t x26; uint8_t/*bool*/ x27 = subborrow_u26(x24, x6, 0x3ffffff, &x26); - uint32_t x29; uint8_t/*bool*/ x30 = subborrow_u25(x27, x8, 0x1ffffff, &x29); - uint32_t x32; uint8_t/*bool*/ x33 = subborrow_u26(x30, x10, 0x3ffffff, &x32); - uint32_t x35; uint8_t/*bool*/ x36 = subborrow_u25(x33, x12, 0x1ffffff, &x35); - uint32_t x38; uint8_t/*bool*/ x39 = subborrow_u26(x36, x14, 0x3ffffff, &x38); - uint32_t x41; uint8_t/*bool*/ x42 = subborrow_u25(x39, x16, 0x1ffffff, &x41); - uint32_t x44; uint8_t/*bool*/ x45 = subborrow_u26(x42, x18, 0x3ffffff, &x44); - uint32_t x47; uint8_t/*bool*/ x48 = subborrow_u25(x45, x17, 0x1ffffff, &x47); - uint32_t x49 = cmovznz32(x48, 0x0, 0xffffffff); - uint32_t x50 = (x49 & 0x3ffffed); - uint32_t x52; uint8_t/*bool*/ x53 = addcarryx_u26(0x0, x20, x50, &x52); - uint32_t x54 = (x49 & 0x1ffffff); - uint32_t x56; uint8_t/*bool*/ x57 = addcarryx_u25(x53, x23, x54, &x56); - uint32_t x58 = (x49 & 0x3ffffff); - uint32_t x60; uint8_t/*bool*/ x61 = addcarryx_u26(x57, x26, x58, &x60); - uint32_t x62 = (x49 & 0x1ffffff); - uint32_t x64; uint8_t/*bool*/ x65 = addcarryx_u25(x61, x29, x62, &x64); - uint32_t x66 = (x49 & 0x3ffffff); - uint32_t x68; uint8_t/*bool*/ x69 = addcarryx_u26(x65, x32, x66, &x68); - uint32_t x70 = (x49 & 0x1ffffff); - uint32_t x72; uint8_t/*bool*/ x73 = addcarryx_u25(x69, x35, x70, &x72); - uint32_t x74 = (x49 & 0x3ffffff); - uint32_t x76; uint8_t/*bool*/ x77 = addcarryx_u26(x73, x38, x74, &x76); - uint32_t x78 = (x49 & 0x1ffffff); - uint32_t x80; uint8_t/*bool*/ x81 = addcarryx_u25(x77, x41, x78, &x80); - uint32_t x82 = (x49 & 0x3ffffff); - uint32_t x84; uint8_t/*bool*/ x85 = addcarryx_u26(x81, x44, x82, &x84); - uint32_t x86 = (x49 & 0x1ffffff); - uint32_t 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 inline void fe_tobytes(uint8_t s[32], const fe *f) -{ - uint32_t 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 inline void fe_copy(fe *h, const fe *f) -{ - memmove(h, f, sizeof(uint32_t) * 10); -} - -static inline void fe_copy_lt(fe_loose *h, const fe *f) -{ - memmove(h, f, sizeof(uint32_t) * 10); -} - -/* h = 0 */ -static inline void fe_0(fe *h) -{ - memset(h, 0, sizeof(uint32_t) * 10); -} - -/* h = 1 */ -static inline void fe_1(fe *h) -{ - memset(h, 0, sizeof(uint32_t) * 10); - h->v[0] = 1; -} - -static void fe_add_impl(uint32_t out[10], const uint32_t in1[10], const uint32_t in2[10]) -{ - const uint32_t x20 = in1[9]; - const uint32_t x21 = in1[8]; - const uint32_t x19 = in1[7]; - const uint32_t x17 = in1[6]; - const uint32_t x15 = in1[5]; - const uint32_t x13 = in1[4]; - const uint32_t x11 = in1[3]; - const uint32_t x9 = in1[2]; - const uint32_t x7 = in1[1]; - const uint32_t x5 = in1[0]; - const uint32_t x38 = in2[9]; - const uint32_t x39 = in2[8]; - const uint32_t x37 = in2[7]; - const uint32_t x35 = in2[6]; - const uint32_t x33 = in2[5]; - const uint32_t x31 = in2[4]; - const uint32_t x29 = in2[3]; - const uint32_t x27 = in2[2]; - const uint32_t x25 = in2[1]; - const uint32_t 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 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(uint32_t out[10], const uint32_t in1[10], const uint32_t in2[10]) -{ - const uint32_t x20 = in1[9]; - const uint32_t x21 = in1[8]; - const uint32_t x19 = in1[7]; - const uint32_t x17 = in1[6]; - const uint32_t x15 = in1[5]; - const uint32_t x13 = in1[4]; - const uint32_t x11 = in1[3]; - const uint32_t x9 = in1[2]; - const uint32_t x7 = in1[1]; - const uint32_t x5 = in1[0]; - const uint32_t x38 = in2[9]; - const uint32_t x39 = in2[8]; - const uint32_t x37 = in2[7]; - const uint32_t x35 = in2[6]; - const uint32_t x33 = in2[5]; - const uint32_t x31 = in2[4]; - const uint32_t x29 = in2[3]; - const uint32_t x27 = in2[2]; - const uint32_t x25 = in2[1]; - const uint32_t 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 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(uint32_t out[10], const uint32_t in1[10], const uint32_t in2[10]) -{ - const uint32_t x20 = in1[9]; - const uint32_t x21 = in1[8]; - const uint32_t x19 = in1[7]; - const uint32_t x17 = in1[6]; - const uint32_t x15 = in1[5]; - const uint32_t x13 = in1[4]; - const uint32_t x11 = in1[3]; - const uint32_t x9 = in1[2]; - const uint32_t x7 = in1[1]; - const uint32_t x5 = in1[0]; - const uint32_t x38 = in2[9]; - const uint32_t x39 = in2[8]; - const uint32_t x37 = in2[7]; - const uint32_t x35 = in2[6]; - const uint32_t x33 = in2[5]; - const uint32_t x31 = in2[4]; - const uint32_t x29 = in2[3]; - const uint32_t x27 = in2[2]; - const uint32_t x25 = in2[1]; - const uint32_t x23 = in2[0]; - uint64_t x40 = ((uint64_t)x23 * x5); - uint64_t x41 = (((uint64_t)x23 * x7) + ((uint64_t)x25 * x5)); - uint64_t x42 = ((((uint64_t)(0x2 * x25) * x7) + ((uint64_t)x23 * x9)) + ((uint64_t)x27 * x5)); - uint64_t x43 = (((((uint64_t)x25 * x9) + ((uint64_t)x27 * x7)) + ((uint64_t)x23 * x11)) + ((uint64_t)x29 * x5)); - uint64_t x44 = (((((uint64_t)x27 * x9) + (0x2 * (((uint64_t)x25 * x11) + ((uint64_t)x29 * x7)))) + ((uint64_t)x23 * x13)) + ((uint64_t)x31 * x5)); - uint64_t x45 = (((((((uint64_t)x27 * x11) + ((uint64_t)x29 * x9)) + ((uint64_t)x25 * x13)) + ((uint64_t)x31 * x7)) + ((uint64_t)x23 * x15)) + ((uint64_t)x33 * x5)); - uint64_t x46 = (((((0x2 * ((((uint64_t)x29 * x11) + ((uint64_t)x25 * x15)) + ((uint64_t)x33 * x7))) + ((uint64_t)x27 * x13)) + ((uint64_t)x31 * x9)) + ((uint64_t)x23 * x17)) + ((uint64_t)x35 * x5)); - uint64_t x47 = (((((((((uint64_t)x29 * x13) + ((uint64_t)x31 * x11)) + ((uint64_t)x27 * x15)) + ((uint64_t)x33 * x9)) + ((uint64_t)x25 * x17)) + ((uint64_t)x35 * x7)) + ((uint64_t)x23 * x19)) + ((uint64_t)x37 * x5)); - uint64_t x48 = (((((((uint64_t)x31 * x13) + (0x2 * (((((uint64_t)x29 * x15) + ((uint64_t)x33 * x11)) + ((uint64_t)x25 * x19)) + ((uint64_t)x37 * x7)))) + ((uint64_t)x27 * x17)) + ((uint64_t)x35 * x9)) + ((uint64_t)x23 * x21)) + ((uint64_t)x39 * x5)); - uint64_t x49 = (((((((((((uint64_t)x31 * x15) + ((uint64_t)x33 * x13)) + ((uint64_t)x29 * x17)) + ((uint64_t)x35 * x11)) + ((uint64_t)x27 * x19)) + ((uint64_t)x37 * x9)) + ((uint64_t)x25 * x21)) + ((uint64_t)x39 * x7)) + ((uint64_t)x23 * x20)) + ((uint64_t)x38 * x5)); - uint64_t x50 = (((((0x2 * ((((((uint64_t)x33 * x15) + ((uint64_t)x29 * x19)) + ((uint64_t)x37 * x11)) + ((uint64_t)x25 * x20)) + ((uint64_t)x38 * x7))) + ((uint64_t)x31 * x17)) + ((uint64_t)x35 * x13)) + ((uint64_t)x27 * x21)) + ((uint64_t)x39 * x9)); - uint64_t x51 = (((((((((uint64_t)x33 * x17) + ((uint64_t)x35 * x15)) + ((uint64_t)x31 * x19)) + ((uint64_t)x37 * x13)) + ((uint64_t)x29 * x21)) + ((uint64_t)x39 * x11)) + ((uint64_t)x27 * x20)) + ((uint64_t)x38 * x9)); - uint64_t x52 = (((((uint64_t)x35 * x17) + (0x2 * (((((uint64_t)x33 * x19) + ((uint64_t)x37 * x15)) + ((uint64_t)x29 * x20)) + ((uint64_t)x38 * x11)))) + ((uint64_t)x31 * x21)) + ((uint64_t)x39 * x13)); - uint64_t x53 = (((((((uint64_t)x35 * x19) + ((uint64_t)x37 * x17)) + ((uint64_t)x33 * x21)) + ((uint64_t)x39 * x15)) + ((uint64_t)x31 * x20)) + ((uint64_t)x38 * x13)); - uint64_t x54 = (((0x2 * ((((uint64_t)x37 * x19) + ((uint64_t)x33 * x20)) + ((uint64_t)x38 * x15))) + ((uint64_t)x35 * x21)) + ((uint64_t)x39 * x17)); - uint64_t x55 = (((((uint64_t)x37 * x21) + ((uint64_t)x39 * x19)) + ((uint64_t)x35 * x20)) + ((uint64_t)x38 * x17)); - uint64_t x56 = (((uint64_t)x39 * x21) + (0x2 * (((uint64_t)x37 * x20) + ((uint64_t)x38 * x19)))); - uint64_t x57 = (((uint64_t)x39 * x20) + ((uint64_t)x38 * x21)); - uint64_t x58 = ((uint64_t)(0x2 * x38) * x20); - uint64_t x59 = (x48 + (x58 << 0x4)); - uint64_t x60 = (x59 + (x58 << 0x1)); - uint64_t x61 = (x60 + x58); - uint64_t x62 = (x47 + (x57 << 0x4)); - uint64_t x63 = (x62 + (x57 << 0x1)); - uint64_t x64 = (x63 + x57); - uint64_t x65 = (x46 + (x56 << 0x4)); - uint64_t x66 = (x65 + (x56 << 0x1)); - uint64_t x67 = (x66 + x56); - uint64_t x68 = (x45 + (x55 << 0x4)); - uint64_t x69 = (x68 + (x55 << 0x1)); - uint64_t x70 = (x69 + x55); - uint64_t x71 = (x44 + (x54 << 0x4)); - uint64_t x72 = (x71 + (x54 << 0x1)); - uint64_t x73 = (x72 + x54); - uint64_t x74 = (x43 + (x53 << 0x4)); - uint64_t x75 = (x74 + (x53 << 0x1)); - uint64_t x76 = (x75 + x53); - uint64_t x77 = (x42 + (x52 << 0x4)); - uint64_t x78 = (x77 + (x52 << 0x1)); - uint64_t x79 = (x78 + x52); - uint64_t x80 = (x41 + (x51 << 0x4)); - uint64_t x81 = (x80 + (x51 << 0x1)); - uint64_t x82 = (x81 + x51); - uint64_t x83 = (x40 + (x50 << 0x4)); - uint64_t x84 = (x83 + (x50 << 0x1)); - uint64_t x85 = (x84 + x50); - uint64_t x86 = (x85 >> 0x1a); - uint32_t x87 = ((uint32_t)x85 & 0x3ffffff); - uint64_t x88 = (x86 + x82); - uint64_t x89 = (x88 >> 0x19); - uint32_t x90 = ((uint32_t)x88 & 0x1ffffff); - uint64_t x91 = (x89 + x79); - uint64_t x92 = (x91 >> 0x1a); - uint32_t x93 = ((uint32_t)x91 & 0x3ffffff); - uint64_t x94 = (x92 + x76); - uint64_t x95 = (x94 >> 0x19); - uint32_t x96 = ((uint32_t)x94 & 0x1ffffff); - uint64_t x97 = (x95 + x73); - uint64_t x98 = (x97 >> 0x1a); - uint32_t x99 = ((uint32_t)x97 & 0x3ffffff); - uint64_t x100 = (x98 + x70); - uint64_t x101 = (x100 >> 0x19); - uint32_t x102 = ((uint32_t)x100 & 0x1ffffff); - uint64_t x103 = (x101 + x67); - uint64_t x104 = (x103 >> 0x1a); - uint32_t x105 = ((uint32_t)x103 & 0x3ffffff); - uint64_t x106 = (x104 + x64); - uint64_t x107 = (x106 >> 0x19); - uint32_t x108 = ((uint32_t)x106 & 0x1ffffff); - uint64_t x109 = (x107 + x61); - uint64_t x110 = (x109 >> 0x1a); - uint32_t x111 = ((uint32_t)x109 & 0x3ffffff); - uint64_t x112 = (x110 + x49); - uint64_t x113 = (x112 >> 0x19); - uint32_t x114 = ((uint32_t)x112 & 0x1ffffff); - uint64_t x115 = (x87 + (0x13 * x113)); - uint32_t x116 = (uint32_t) (x115 >> 0x1a); - uint32_t x117 = ((uint32_t)x115 & 0x3ffffff); - uint32_t x118 = (x116 + x90); - uint32_t x119 = (x118 >> 0x19); - uint32_t 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 inline void fe_mul_ttt(fe *h, const fe *f, const fe *g) -{ - fe_mul_impl(h->v, f->v, g->v); -} - -static inline void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g) -{ - fe_mul_impl(h->v, f->v, g->v); -} - -static 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(uint32_t out[10], const uint32_t in1[10]) -{ - const uint32_t x17 = in1[9]; - const uint32_t x18 = in1[8]; - const uint32_t x16 = in1[7]; - const uint32_t x14 = in1[6]; - const uint32_t x12 = in1[5]; - const uint32_t x10 = in1[4]; - const uint32_t x8 = in1[3]; - const uint32_t x6 = in1[2]; - const uint32_t x4 = in1[1]; - const uint32_t x2 = in1[0]; - uint64_t x19 = ((uint64_t)x2 * x2); - uint64_t x20 = ((uint64_t)(0x2 * x2) * x4); - uint64_t x21 = (0x2 * (((uint64_t)x4 * x4) + ((uint64_t)x2 * x6))); - uint64_t x22 = (0x2 * (((uint64_t)x4 * x6) + ((uint64_t)x2 * x8))); - uint64_t x23 = ((((uint64_t)x6 * x6) + ((uint64_t)(0x4 * x4) * x8)) + ((uint64_t)(0x2 * x2) * x10)); - uint64_t x24 = (0x2 * ((((uint64_t)x6 * x8) + ((uint64_t)x4 * x10)) + ((uint64_t)x2 * x12))); - uint64_t x25 = (0x2 * (((((uint64_t)x8 * x8) + ((uint64_t)x6 * x10)) + ((uint64_t)x2 * x14)) + ((uint64_t)(0x2 * x4) * x12))); - uint64_t x26 = (0x2 * (((((uint64_t)x8 * x10) + ((uint64_t)x6 * x12)) + ((uint64_t)x4 * x14)) + ((uint64_t)x2 * x16))); - uint64_t x27 = (((uint64_t)x10 * x10) + (0x2 * ((((uint64_t)x6 * x14) + ((uint64_t)x2 * x18)) + (0x2 * (((uint64_t)x4 * x16) + ((uint64_t)x8 * x12)))))); - uint64_t x28 = (0x2 * ((((((uint64_t)x10 * x12) + ((uint64_t)x8 * x14)) + ((uint64_t)x6 * x16)) + ((uint64_t)x4 * x18)) + ((uint64_t)x2 * x17))); - uint64_t x29 = (0x2 * (((((uint64_t)x12 * x12) + ((uint64_t)x10 * x14)) + ((uint64_t)x6 * x18)) + (0x2 * (((uint64_t)x8 * x16) + ((uint64_t)x4 * x17))))); - uint64_t x30 = (0x2 * (((((uint64_t)x12 * x14) + ((uint64_t)x10 * x16)) + ((uint64_t)x8 * x18)) + ((uint64_t)x6 * x17))); - uint64_t x31 = (((uint64_t)x14 * x14) + (0x2 * (((uint64_t)x10 * x18) + (0x2 * (((uint64_t)x12 * x16) + ((uint64_t)x8 * x17)))))); - uint64_t x32 = (0x2 * ((((uint64_t)x14 * x16) + ((uint64_t)x12 * x18)) + ((uint64_t)x10 * x17))); - uint64_t x33 = (0x2 * ((((uint64_t)x16 * x16) + ((uint64_t)x14 * x18)) + ((uint64_t)(0x2 * x12) * x17))); - uint64_t x34 = (0x2 * (((uint64_t)x16 * x18) + ((uint64_t)x14 * x17))); - uint64_t x35 = (((uint64_t)x18 * x18) + ((uint64_t)(0x4 * x16) * x17)); - uint64_t x36 = ((uint64_t)(0x2 * x18) * x17); - uint64_t x37 = ((uint64_t)(0x2 * x17) * x17); - uint64_t x38 = (x27 + (x37 << 0x4)); - uint64_t x39 = (x38 + (x37 << 0x1)); - uint64_t x40 = (x39 + x37); - uint64_t x41 = (x26 + (x36 << 0x4)); - uint64_t x42 = (x41 + (x36 << 0x1)); - uint64_t x43 = (x42 + x36); - uint64_t x44 = (x25 + (x35 << 0x4)); - uint64_t x45 = (x44 + (x35 << 0x1)); - uint64_t x46 = (x45 + x35); - uint64_t x47 = (x24 + (x34 << 0x4)); - uint64_t x48 = (x47 + (x34 << 0x1)); - uint64_t x49 = (x48 + x34); - uint64_t x50 = (x23 + (x33 << 0x4)); - uint64_t x51 = (x50 + (x33 << 0x1)); - uint64_t x52 = (x51 + x33); - uint64_t x53 = (x22 + (x32 << 0x4)); - uint64_t x54 = (x53 + (x32 << 0x1)); - uint64_t x55 = (x54 + x32); - uint64_t x56 = (x21 + (x31 << 0x4)); - uint64_t x57 = (x56 + (x31 << 0x1)); - uint64_t x58 = (x57 + x31); - uint64_t x59 = (x20 + (x30 << 0x4)); - uint64_t x60 = (x59 + (x30 << 0x1)); - uint64_t x61 = (x60 + x30); - uint64_t x62 = (x19 + (x29 << 0x4)); - uint64_t x63 = (x62 + (x29 << 0x1)); - uint64_t x64 = (x63 + x29); - uint64_t x65 = (x64 >> 0x1a); - uint32_t x66 = ((uint32_t)x64 & 0x3ffffff); - uint64_t x67 = (x65 + x61); - uint64_t x68 = (x67 >> 0x19); - uint32_t x69 = ((uint32_t)x67 & 0x1ffffff); - uint64_t x70 = (x68 + x58); - uint64_t x71 = (x70 >> 0x1a); - uint32_t x72 = ((uint32_t)x70 & 0x3ffffff); - uint64_t x73 = (x71 + x55); - uint64_t x74 = (x73 >> 0x19); - uint32_t x75 = ((uint32_t)x73 & 0x1ffffff); - uint64_t x76 = (x74 + x52); - uint64_t x77 = (x76 >> 0x1a); - uint32_t x78 = ((uint32_t)x76 & 0x3ffffff); - uint64_t x79 = (x77 + x49); - uint64_t x80 = (x79 >> 0x19); - uint32_t x81 = ((uint32_t)x79 & 0x1ffffff); - uint64_t x82 = (x80 + x46); - uint64_t x83 = (x82 >> 0x1a); - uint32_t x84 = ((uint32_t)x82 & 0x3ffffff); - uint64_t x85 = (x83 + x43); - uint64_t x86 = (x85 >> 0x19); - uint32_t x87 = ((uint32_t)x85 & 0x1ffffff); - uint64_t x88 = (x86 + x40); - uint64_t x89 = (x88 >> 0x1a); - uint32_t x90 = ((uint32_t)x88 & 0x3ffffff); - uint64_t x91 = (x89 + x28); - uint64_t x92 = (x91 >> 0x19); - uint32_t x93 = ((uint32_t)x91 & 0x1ffffff); - uint64_t x94 = (x66 + (0x13 * x92)); - uint32_t x95 = (uint32_t) (x94 >> 0x1a); - uint32_t x96 = ((uint32_t)x94 & 0x3ffffff); - uint32_t x97 = (x95 + x69); - uint32_t x98 = (x97 >> 0x19); - uint32_t 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 inline void fe_sq_tl(fe *h, const fe_loose *f) -{ - fe_sqr_impl(h->v, f->v); -} - -static inline void fe_sq_tt(fe *h, const fe *f) -{ - fe_sqr_impl(h->v, f->v); -} - -static 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 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 inline void fe_cswap(fe *f, fe *g, unsigned int b) -{ - unsigned i; - b = 0 - b; - for (i = 0; i < 10; i++) { - uint32_t 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 inline void fe_mul_121666_impl(uint32_t out[10], const uint32_t in1[10]) -{ - const uint32_t x20 = in1[9]; - const uint32_t x21 = in1[8]; - const uint32_t x19 = in1[7]; - const uint32_t x17 = in1[6]; - const uint32_t x15 = in1[5]; - const uint32_t x13 = in1[4]; - const uint32_t x11 = in1[3]; - const uint32_t x9 = in1[2]; - const uint32_t x7 = in1[1]; - const uint32_t x5 = in1[0]; - const uint32_t x38 = 0; - const uint32_t x39 = 0; - const uint32_t x37 = 0; - const uint32_t x35 = 0; - const uint32_t x33 = 0; - const uint32_t x31 = 0; - const uint32_t x29 = 0; - const uint32_t x27 = 0; - const uint32_t x25 = 0; - const uint32_t x23 = 121666; - uint64_t x40 = ((uint64_t)x23 * x5); - uint64_t x41 = (((uint64_t)x23 * x7) + ((uint64_t)x25 * x5)); - uint64_t x42 = ((((uint64_t)(0x2 * x25) * x7) + ((uint64_t)x23 * x9)) + ((uint64_t)x27 * x5)); - uint64_t x43 = (((((uint64_t)x25 * x9) + ((uint64_t)x27 * x7)) + ((uint64_t)x23 * x11)) + ((uint64_t)x29 * x5)); - uint64_t x44 = (((((uint64_t)x27 * x9) + (0x2 * (((uint64_t)x25 * x11) + ((uint64_t)x29 * x7)))) + ((uint64_t)x23 * x13)) + ((uint64_t)x31 * x5)); - uint64_t x45 = (((((((uint64_t)x27 * x11) + ((uint64_t)x29 * x9)) + ((uint64_t)x25 * x13)) + ((uint64_t)x31 * x7)) + ((uint64_t)x23 * x15)) + ((uint64_t)x33 * x5)); - uint64_t x46 = (((((0x2 * ((((uint64_t)x29 * x11) + ((uint64_t)x25 * x15)) + ((uint64_t)x33 * x7))) + ((uint64_t)x27 * x13)) + ((uint64_t)x31 * x9)) + ((uint64_t)x23 * x17)) + ((uint64_t)x35 * x5)); - uint64_t x47 = (((((((((uint64_t)x29 * x13) + ((uint64_t)x31 * x11)) + ((uint64_t)x27 * x15)) + ((uint64_t)x33 * x9)) + ((uint64_t)x25 * x17)) + ((uint64_t)x35 * x7)) + ((uint64_t)x23 * x19)) + ((uint64_t)x37 * x5)); - uint64_t x48 = (((((((uint64_t)x31 * x13) + (0x2 * (((((uint64_t)x29 * x15) + ((uint64_t)x33 * x11)) + ((uint64_t)x25 * x19)) + ((uint64_t)x37 * x7)))) + ((uint64_t)x27 * x17)) + ((uint64_t)x35 * x9)) + ((uint64_t)x23 * x21)) + ((uint64_t)x39 * x5)); - uint64_t x49 = (((((((((((uint64_t)x31 * x15) + ((uint64_t)x33 * x13)) + ((uint64_t)x29 * x17)) + ((uint64_t)x35 * x11)) + ((uint64_t)x27 * x19)) + ((uint64_t)x37 * x9)) + ((uint64_t)x25 * x21)) + ((uint64_t)x39 * x7)) + ((uint64_t)x23 * x20)) + ((uint64_t)x38 * x5)); - uint64_t x50 = (((((0x2 * ((((((uint64_t)x33 * x15) + ((uint64_t)x29 * x19)) + ((uint64_t)x37 * x11)) + ((uint64_t)x25 * x20)) + ((uint64_t)x38 * x7))) + ((uint64_t)x31 * x17)) + ((uint64_t)x35 * x13)) + ((uint64_t)x27 * x21)) + ((uint64_t)x39 * x9)); - uint64_t x51 = (((((((((uint64_t)x33 * x17) + ((uint64_t)x35 * x15)) + ((uint64_t)x31 * x19)) + ((uint64_t)x37 * x13)) + ((uint64_t)x29 * x21)) + ((uint64_t)x39 * x11)) + ((uint64_t)x27 * x20)) + ((uint64_t)x38 * x9)); - uint64_t x52 = (((((uint64_t)x35 * x17) + (0x2 * (((((uint64_t)x33 * x19) + ((uint64_t)x37 * x15)) + ((uint64_t)x29 * x20)) + ((uint64_t)x38 * x11)))) + ((uint64_t)x31 * x21)) + ((uint64_t)x39 * x13)); - uint64_t x53 = (((((((uint64_t)x35 * x19) + ((uint64_t)x37 * x17)) + ((uint64_t)x33 * x21)) + ((uint64_t)x39 * x15)) + ((uint64_t)x31 * x20)) + ((uint64_t)x38 * x13)); - uint64_t x54 = (((0x2 * ((((uint64_t)x37 * x19) + ((uint64_t)x33 * x20)) + ((uint64_t)x38 * x15))) + ((uint64_t)x35 * x21)) + ((uint64_t)x39 * x17)); - uint64_t x55 = (((((uint64_t)x37 * x21) + ((uint64_t)x39 * x19)) + ((uint64_t)x35 * x20)) + ((uint64_t)x38 * x17)); - uint64_t x56 = (((uint64_t)x39 * x21) + (0x2 * (((uint64_t)x37 * x20) + ((uint64_t)x38 * x19)))); - uint64_t x57 = (((uint64_t)x39 * x20) + ((uint64_t)x38 * x21)); - uint64_t x58 = ((uint64_t)(0x2 * x38) * x20); - uint64_t x59 = (x48 + (x58 << 0x4)); - uint64_t x60 = (x59 + (x58 << 0x1)); - uint64_t x61 = (x60 + x58); - uint64_t x62 = (x47 + (x57 << 0x4)); - uint64_t x63 = (x62 + (x57 << 0x1)); - uint64_t x64 = (x63 + x57); - uint64_t x65 = (x46 + (x56 << 0x4)); - uint64_t x66 = (x65 + (x56 << 0x1)); - uint64_t x67 = (x66 + x56); - uint64_t x68 = (x45 + (x55 << 0x4)); - uint64_t x69 = (x68 + (x55 << 0x1)); - uint64_t x70 = (x69 + x55); - uint64_t x71 = (x44 + (x54 << 0x4)); - uint64_t x72 = (x71 + (x54 << 0x1)); - uint64_t x73 = (x72 + x54); - uint64_t x74 = (x43 + (x53 << 0x4)); - uint64_t x75 = (x74 + (x53 << 0x1)); - uint64_t x76 = (x75 + x53); - uint64_t x77 = (x42 + (x52 << 0x4)); - uint64_t x78 = (x77 + (x52 << 0x1)); - uint64_t x79 = (x78 + x52); - uint64_t x80 = (x41 + (x51 << 0x4)); - uint64_t x81 = (x80 + (x51 << 0x1)); - uint64_t x82 = (x81 + x51); - uint64_t x83 = (x40 + (x50 << 0x4)); - uint64_t x84 = (x83 + (x50 << 0x1)); - uint64_t x85 = (x84 + x50); - uint64_t x86 = (x85 >> 0x1a); - uint32_t x87 = ((uint32_t)x85 & 0x3ffffff); - uint64_t x88 = (x86 + x82); - uint64_t x89 = (x88 >> 0x19); - uint32_t x90 = ((uint32_t)x88 & 0x1ffffff); - uint64_t x91 = (x89 + x79); - uint64_t x92 = (x91 >> 0x1a); - uint32_t x93 = ((uint32_t)x91 & 0x3ffffff); - uint64_t x94 = (x92 + x76); - uint64_t x95 = (x94 >> 0x19); - uint32_t x96 = ((uint32_t)x94 & 0x1ffffff); - uint64_t x97 = (x95 + x73); - uint64_t x98 = (x97 >> 0x1a); - uint32_t x99 = ((uint32_t)x97 & 0x3ffffff); - uint64_t x100 = (x98 + x70); - uint64_t x101 = (x100 >> 0x19); - uint32_t x102 = ((uint32_t)x100 & 0x1ffffff); - uint64_t x103 = (x101 + x67); - uint64_t x104 = (x103 >> 0x1a); - uint32_t x105 = ((uint32_t)x103 & 0x3ffffff); - uint64_t x106 = (x104 + x64); - uint64_t x107 = (x106 >> 0x19); - uint32_t x108 = ((uint32_t)x106 & 0x1ffffff); - uint64_t x109 = (x107 + x61); - uint64_t x110 = (x109 >> 0x1a); - uint32_t x111 = ((uint32_t)x109 & 0x3ffffff); - uint64_t x112 = (x110 + x49); - uint64_t x113 = (x112 >> 0x19); - uint32_t x114 = ((uint32_t)x112 & 0x1ffffff); - uint64_t x115 = (x87 + (0x13 * x113)); - uint32_t x116 = (uint32_t) (x115 >> 0x1a); - uint32_t x117 = ((uint32_t)x115 & 0x3ffffff); - uint32_t x118 = (x116 + x90); - uint32_t x119 = (x118 >> 0x19); - uint32_t 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 inline void fe_mul121666(fe *h, const fe_loose *f) -{ - fe_mul_121666_impl(h->v, f->v); -} - -static const uint8_t curve25519_null_point[CURVE25519_KEY_SIZE]; - -bool curve25519(uint8_t out[CURVE25519_KEY_SIZE], - const uint8_t scalar[CURVE25519_KEY_SIZE], - const uint8_t point[CURVE25519_KEY_SIZE]) -{ - fe x1, x2, z2, x3, z3; - fe_loose x2l, z2l, x3l; - unsigned swap = 0; - int pos; - uint8_t 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); - - explicit_bzero(&x1, sizeof(x1)); - explicit_bzero(&x2, sizeof(x2)); - explicit_bzero(&z2, sizeof(z2)); - explicit_bzero(&x3, sizeof(x3)); - explicit_bzero(&z3, sizeof(z3)); - explicit_bzero(&x2l, sizeof(x2l)); - explicit_bzero(&z2l, sizeof(z2l)); - explicit_bzero(&x3l, sizeof(x3l)); - explicit_bzero(&e, sizeof(e)); - - return timingsafe_bcmp(out, curve25519_null_point, CURVE25519_KEY_SIZE) != 0; -} diff --git a/sys/dev/if_wg/crypto.h b/sys/dev/if_wg/crypto.h deleted file mode 100644 index 6e045c2fe0bf..000000000000 --- a/sys/dev/if_wg/crypto.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2015-2021 Jason A. Donenfeld . All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _WG_CRYPTO -#define _WG_CRYPTO - -#include - -enum chacha20poly1305_lengths { - XCHACHA20POLY1305_NONCE_SIZE = 24, - CHACHA20POLY1305_KEY_SIZE = 32, - CHACHA20POLY1305_AUTHTAG_SIZE = 16 -}; - -void -chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, const size_t src_len, - const uint8_t *ad, const size_t ad_len, - const uint64_t nonce, - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]); - -bool -chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len, - const uint8_t *ad, const size_t ad_len, - const uint64_t nonce, - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]); - -void -xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, - const size_t src_len, const uint8_t *ad, - const size_t ad_len, - const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]); - -bool -xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, - const size_t src_len, const uint8_t *ad, - const size_t ad_len, - const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], - const uint8_t key[CHACHA20POLY1305_KEY_SIZE]); - - -enum blake2s_lengths { - BLAKE2S_BLOCK_SIZE = 64, - BLAKE2S_HASH_SIZE = 32, - BLAKE2S_KEY_SIZE = 32 -}; - -struct blake2s_state { - uint32_t h[8]; - uint32_t t[2]; - uint32_t f[2]; - uint8_t buf[BLAKE2S_BLOCK_SIZE]; - unsigned int buflen; - unsigned int outlen; -}; - -void blake2s_init(struct blake2s_state *state, const size_t outlen); - -void blake2s_init_key(struct blake2s_state *state, const size_t outlen, - const uint8_t *key, const size_t keylen); - -void blake2s_update(struct blake2s_state *state, const uint8_t *in, size_t inlen); - -void blake2s_final(struct blake2s_state *state, uint8_t *out); - -void blake2s(uint8_t *out, const uint8_t *in, const uint8_t *key, - const size_t outlen, const size_t inlen, const size_t keylen); - -void blake2s_hmac(uint8_t *out, const uint8_t *in, const uint8_t *key, - const size_t outlen, const size_t inlen, const size_t keylen); - -enum curve25519_lengths { - CURVE25519_KEY_SIZE = 32 -}; - -bool curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], - const uint8_t secret[static CURVE25519_KEY_SIZE], - const uint8_t basepoint[static CURVE25519_KEY_SIZE]); - -static inline bool -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] = { 9 }; - - return curve25519(pub, secret, basepoint); -} - -static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE]) -{ - secret[0] &= 248; - secret[31] = (secret[31] & 127) | 64; -} - -static inline void curve25519_generate_secret(uint8_t secret[CURVE25519_KEY_SIZE]) -{ - arc4random_buf(secret, CURVE25519_KEY_SIZE); - curve25519_clamp_secret(secret); -} - -#endif diff --git a/sys/dev/if_wg/if_wg.c b/sys/dev/if_wg/if_wg.c deleted file mode 100644 index 8c11cc58a3bb..000000000000 --- a/sys/dev/if_wg/if_wg.c +++ /dev/null @@ -1,3462 +0,0 @@ -/* - * Copyright (C) 2015-2021 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2019-2021 Matt Dunwoodie - * Copyright (c) 2019-2020 Rubicon Communications, LLC (Netgate) - * Copyright (c) 2021 Kyle Evans - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* TODO audit imports */ -#include "opt_inet.h" -#include "opt_inet6.h" - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "support.h" -#include "wg_noise.h" -#include "wg_cookie.h" -#include "if_wg.h" - -/* It'd be nice to use IF_MAXMTU, but that means more complicated mbuf allocations, - * so instead just do the biggest mbuf we can easily allocate minus the usual maximum - * IPv6 overhead of 80 bytes. If somebody wants bigger frames, we can revisit this. */ -#define MAX_MTU (MJUM16BYTES - 80) - -#define DEFAULT_MTU 1420 - -#define MAX_STAGED_PKT 128 -#define MAX_QUEUED_PKT 1024 -#define MAX_QUEUED_PKT_MASK (MAX_QUEUED_PKT - 1) - -#define MAX_QUEUED_HANDSHAKES 4096 - -#define HASHTABLE_PEER_SIZE (1 << 11) -#define HASHTABLE_INDEX_SIZE (1 << 13) -#define MAX_PEERS_PER_IFACE (1 << 20) - -#define REKEY_TIMEOUT 5 -#define REKEY_TIMEOUT_JITTER 334 /* 1/3 sec, round for arc4random_uniform */ -#define KEEPALIVE_TIMEOUT 10 -#define MAX_TIMER_HANDSHAKES (90 / REKEY_TIMEOUT) -#define NEW_HANDSHAKE_TIMEOUT (REKEY_TIMEOUT + KEEPALIVE_TIMEOUT) -#define UNDERLOAD_TIMEOUT 1 - -#define DPRINTF(sc, ...) if (wireguard_debug) if_printf(sc->sc_ifp, ##__VA_ARGS__) - -/* First byte indicating packet type on the wire */ -#define WG_PKT_INITIATION htole32(1) -#define WG_PKT_RESPONSE htole32(2) -#define WG_PKT_COOKIE htole32(3) -#define WG_PKT_DATA htole32(4) - -#define WG_PKT_WITH_PADDING(n) (((n) + (16-1)) & (~(16-1))) -#define WG_KEY_SIZE 32 - -struct wg_pkt_initiation { - uint32_t t; - uint32_t s_idx; - uint8_t ue[NOISE_PUBLIC_KEY_LEN]; - uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN]; - uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]; - struct cookie_macs m; -}; - -struct wg_pkt_response { - uint32_t t; - uint32_t s_idx; - uint32_t r_idx; - uint8_t ue[NOISE_PUBLIC_KEY_LEN]; - uint8_t en[0 + NOISE_AUTHTAG_LEN]; - struct cookie_macs m; -}; - -struct wg_pkt_cookie { - uint32_t t; - uint32_t r_idx; - uint8_t nonce[COOKIE_NONCE_SIZE]; - uint8_t ec[COOKIE_ENCRYPTED_SIZE]; -}; - -struct wg_pkt_data { - uint32_t t; - uint32_t r_idx; - uint8_t nonce[sizeof(uint64_t)]; - uint8_t buf[]; -}; - -struct wg_endpoint { - union { - struct sockaddr r_sa; - struct sockaddr_in r_sin; -#ifdef INET6 - struct sockaddr_in6 r_sin6; -#endif - } e_remote; - union { - struct in_addr l_in; -#ifdef INET6 - struct in6_pktinfo l_pktinfo6; -#define l_in6 l_pktinfo6.ipi6_addr -#endif - } e_local; -}; - -struct wg_tag { - struct m_tag t_tag; - struct wg_endpoint t_endpoint; - struct wg_peer *t_peer; - struct mbuf *t_mbuf; - int t_done; - int t_mtu; -}; - -struct wg_index { - LIST_ENTRY(wg_index) i_entry; - SLIST_ENTRY(wg_index) i_unused_entry; - uint32_t i_key; - struct noise_remote *i_value; -}; - -struct wg_timers { - /* t_lock is for blocking wg_timers_event_* when setting t_disabled. */ - struct rwlock t_lock; - - int t_disabled; - int t_need_another_keepalive; - uint16_t t_persistent_keepalive_interval; - struct callout t_new_handshake; - struct callout t_send_keepalive; - struct callout t_retry_handshake; - struct callout t_zero_key_material; - struct callout t_persistent_keepalive; - - struct mtx t_handshake_mtx; - struct timespec t_handshake_last_sent; - struct timespec t_handshake_complete; - volatile int t_handshake_retries; -}; - -struct wg_aip { - struct radix_node r_nodes[2]; - CK_LIST_ENTRY(wg_aip) r_entry; - struct sockaddr_storage r_addr; - struct sockaddr_storage r_mask; - struct wg_peer *r_peer; -}; - -struct wg_queue { - struct mtx q_mtx; - struct mbufq q; -}; - -struct wg_peer { - CK_LIST_ENTRY(wg_peer) p_hash_entry; - CK_LIST_ENTRY(wg_peer) p_entry; - uint64_t p_id; - struct wg_softc *p_sc; - - struct noise_remote p_remote; - struct cookie_maker p_cookie; - struct wg_timers p_timers; - - struct rwlock p_endpoint_lock; - struct wg_endpoint p_endpoint; - - SLIST_HEAD(,wg_index) p_unused_index; - struct wg_index p_index[3]; - - struct wg_queue p_stage_queue; - struct wg_queue p_encap_queue; - struct wg_queue p_decap_queue; - - struct grouptask p_clear_secrets; - struct grouptask p_send_initiation; - struct grouptask p_send_keepalive; - struct grouptask p_send; - struct grouptask p_recv; - - counter_u64_t p_tx_bytes; - counter_u64_t p_rx_bytes; - - CK_LIST_HEAD(, wg_aip) p_aips; - struct mtx p_lock; - struct epoch_context p_ctx; -}; - -enum route_direction { - /* TODO OpenBSD doesn't use IN/OUT, instead passes the address buffer - * directly to route_lookup. */ - IN, - OUT, -}; - -struct wg_aip_table { - size_t t_count; - struct radix_node_head *t_ip; - struct radix_node_head *t_ip6; -}; - -struct wg_allowedip { - uint16_t family; - union { - struct in_addr ip4; - struct in6_addr ip6; - }; - uint8_t cidr; -}; - -struct wg_hashtable { - struct mtx h_mtx; - SIPHASH_KEY h_secret; - CK_LIST_HEAD(, wg_peer) h_peers_list; - CK_LIST_HEAD(, wg_peer) *h_peers; - u_long h_peers_mask; - size_t h_num_peers; -}; - -struct wg_socket { - struct mtx so_mtx; - struct socket *so_so4; - struct socket *so_so6; - uint32_t so_user_cookie; - in_port_t so_port; -}; - -struct wg_softc { - LIST_ENTRY(wg_softc) sc_entry; - struct ifnet *sc_ifp; - int sc_flags; - - struct ucred *sc_ucred; - struct wg_socket sc_socket; - struct wg_hashtable sc_hashtable; - struct wg_aip_table sc_aips; - - struct mbufq sc_handshake_queue; - struct grouptask sc_handshake; - - struct noise_local sc_local; - struct cookie_checker sc_cookie; - - struct buf_ring *sc_encap_ring; - struct buf_ring *sc_decap_ring; - - struct grouptask *sc_encrypt; - struct grouptask *sc_decrypt; - - struct rwlock sc_index_lock; - LIST_HEAD(,wg_index) *sc_index; - u_long sc_index_mask; - - struct sx sc_lock; - volatile u_int sc_peer_count; -}; - -#define WGF_DYING 0x0001 - -/* TODO the following defines are freebsd specific, we should see what is - * necessary and cleanup from there (i suspect a lot can be junked). */ - -#ifndef ENOKEY -#define ENOKEY ENOTCAPABLE -#endif - -#if __FreeBSD_version > 1300000 -typedef void timeout_t (void *); -#endif - -#define GROUPTASK_DRAIN(gtask) \ - gtaskqueue_drain((gtask)->gt_taskqueue, &(gtask)->gt_task) - -#define MTAG_WIREGUARD 0xBEAD -#define M_ENQUEUED M_PROTO1 - -static int clone_count; -static uma_zone_t ratelimit_zone; -static int wireguard_debug; -static volatile unsigned long peer_counter = 0; -static const char wgname[] = "wg"; -static unsigned wg_osd_jail_slot; - -static struct sx wg_sx; -SX_SYSINIT(wg_sx, &wg_sx, "wg_sx"); - -static LIST_HEAD(, wg_softc) wg_list = LIST_HEAD_INITIALIZER(wg_list); - -SYSCTL_NODE(_net, OID_AUTO, wg, CTLFLAG_RW, 0, "WireGuard"); -SYSCTL_INT(_net_wg, OID_AUTO, debug, CTLFLAG_RWTUN, &wireguard_debug, 0, - "enable debug logging"); - -TASKQGROUP_DECLARE(if_io_tqg); - -MALLOC_DEFINE(M_WG, "WG", "wireguard"); -VNET_DEFINE_STATIC(struct if_clone *, wg_cloner); - - -#define V_wg_cloner VNET(wg_cloner) -#define WG_CAPS IFCAP_LINKSTATE -#define ph_family PH_loc.eight[5] - -struct wg_timespec64 { - uint64_t tv_sec; - uint64_t tv_nsec; -}; - -struct wg_peer_export { - struct sockaddr_storage endpoint; - struct timespec last_handshake; - uint8_t public_key[WG_KEY_SIZE]; - uint8_t preshared_key[NOISE_SYMMETRIC_KEY_LEN]; - size_t endpoint_sz; - struct wg_allowedip *aip; - uint64_t rx_bytes; - uint64_t tx_bytes; - int aip_count; - uint16_t persistent_keepalive; -}; - -static struct wg_tag *wg_tag_get(struct mbuf *); -static struct wg_endpoint *wg_mbuf_endpoint_get(struct mbuf *); -static int wg_socket_init(struct wg_softc *, in_port_t); -static int wg_socket_bind(struct socket *, struct socket *, in_port_t *); -static void wg_socket_set(struct wg_softc *, struct socket *, struct socket *); -static void wg_socket_uninit(struct wg_softc *); -static void wg_socket_set_cookie(struct wg_softc *, uint32_t); -static int wg_send(struct wg_softc *, struct wg_endpoint *, struct mbuf *); -static void wg_timers_event_data_sent(struct wg_timers *); -static void wg_timers_event_data_received(struct wg_timers *); -static void wg_timers_event_any_authenticated_packet_sent(struct wg_timers *); -static void wg_timers_event_any_authenticated_packet_received(struct wg_timers *); -static void wg_timers_event_any_authenticated_packet_traversal(struct wg_timers *); -static void wg_timers_event_handshake_initiated(struct wg_timers *); -static void wg_timers_event_handshake_responded(struct wg_timers *); -static void wg_timers_event_handshake_complete(struct wg_timers *); -static void wg_timers_event_session_derived(struct wg_timers *); -static void wg_timers_event_want_initiation(struct wg_timers *); -static void wg_timers_event_reset_handshake_last_sent(struct wg_timers *); -static void wg_timers_run_send_initiation(struct wg_timers *, int); -static void wg_timers_run_retry_handshake(struct wg_timers *); -static void wg_timers_run_send_keepalive(struct wg_timers *); -static void wg_timers_run_new_handshake(struct wg_timers *); -static void wg_timers_run_zero_key_material(struct wg_timers *); -static void wg_timers_run_persistent_keepalive(struct wg_timers *); -static void wg_timers_init(struct wg_timers *); -static void wg_timers_enable(struct wg_timers *); -static void wg_timers_disable(struct wg_timers *); -static void wg_timers_set_persistent_keepalive(struct wg_timers *, uint16_t); -static void wg_timers_get_last_handshake(struct wg_timers *, struct timespec *); -static int wg_timers_expired_handshake_last_sent(struct wg_timers *); -static int wg_timers_check_handshake_last_sent(struct wg_timers *); -static void wg_queue_init(struct wg_queue *, const char *); -static void wg_queue_deinit(struct wg_queue *); -static void wg_queue_purge(struct wg_queue *); -static struct mbuf *wg_queue_dequeue(struct wg_queue *, struct wg_tag **); -static int wg_queue_len(struct wg_queue *); -static int wg_queue_in(struct wg_peer *, struct mbuf *); -static void wg_queue_out(struct wg_peer *); -static void wg_queue_stage(struct wg_peer *, struct mbuf *); -static int wg_aip_init(struct wg_aip_table *); -static void wg_aip_destroy(struct wg_aip_table *); -static void wg_aip_populate_aip4(struct wg_aip *, const struct in_addr *, uint8_t); -static void wg_aip_populate_aip6(struct wg_aip *, const struct in6_addr *, uint8_t); -static int wg_aip_add(struct wg_aip_table *, struct wg_peer *, const struct wg_allowedip *); -static int wg_peer_remove(struct radix_node *, void *); -static void wg_peer_remove_all(struct wg_softc *); -static int wg_aip_delete(struct wg_aip_table *, struct wg_peer *); -static struct wg_peer *wg_aip_lookup(struct wg_aip_table *, struct mbuf *, enum route_direction); -static void wg_hashtable_init(struct wg_hashtable *); -static void wg_hashtable_destroy(struct wg_hashtable *); -static void wg_hashtable_peer_insert(struct wg_hashtable *, struct wg_peer *); -static struct wg_peer *wg_peer_lookup(struct wg_softc *, const uint8_t [32]); -static void wg_hashtable_peer_remove(struct wg_hashtable *, struct wg_peer *); -static int wg_cookie_validate_packet(struct cookie_checker *, struct mbuf *, int); -static struct wg_peer *wg_peer_alloc(struct wg_softc *); -static void wg_peer_free_deferred(epoch_context_t); -static void wg_peer_destroy(struct wg_peer *); -static void wg_peer_send_buf(struct wg_peer *, uint8_t *, size_t); -static void wg_send_initiation(struct wg_peer *); -static void wg_send_response(struct wg_peer *); -static void wg_send_cookie(struct wg_softc *, struct cookie_macs *, uint32_t, struct mbuf *); -static void wg_peer_set_endpoint_from_tag(struct wg_peer *, struct wg_tag *); -static void wg_peer_clear_src(struct wg_peer *); -static void wg_peer_get_endpoint(struct wg_peer *, struct wg_endpoint *); -static void wg_deliver_out(struct wg_peer *); -static void wg_deliver_in(struct wg_peer *); -static void wg_send_buf(struct wg_softc *, struct wg_endpoint *, uint8_t *, size_t); -static void wg_send_keepalive(struct wg_peer *); -static void wg_handshake(struct wg_softc *, struct mbuf *); -static void wg_encap(struct wg_softc *, struct mbuf *); -static void wg_decap(struct wg_softc *, struct mbuf *); -static void wg_softc_handshake_receive(struct wg_softc *); -static void wg_softc_decrypt(struct wg_softc *); -static void wg_softc_encrypt(struct wg_softc *); -static struct noise_remote *wg_remote_get(struct wg_softc *, uint8_t [NOISE_PUBLIC_KEY_LEN]); -static uint32_t wg_index_set(struct wg_softc *, struct noise_remote *); -static struct noise_remote *wg_index_get(struct wg_softc *, uint32_t); -static void wg_index_drop(struct wg_softc *, uint32_t); -static int wg_update_endpoint_addrs(struct wg_endpoint *, const struct sockaddr *, struct ifnet *); -static void wg_input(struct mbuf *, int, struct inpcb *, const struct sockaddr *, void *); -static void wg_encrypt_dispatch(struct wg_softc *); -static void wg_decrypt_dispatch(struct wg_softc *); -static void crypto_taskq_setup(struct wg_softc *); -static void crypto_taskq_destroy(struct wg_softc *); -static int wg_clone_create(struct if_clone *, int, caddr_t); -static void wg_qflush(struct ifnet *); -static int wg_transmit(struct ifnet *, struct mbuf *); -static int wg_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); -static void wg_clone_destroy(struct ifnet *); -static int wg_peer_to_export(struct wg_peer *, struct wg_peer_export *); -static bool wgc_privileged(struct wg_softc *); -static int wgc_get(struct wg_softc *, struct wg_data_io *); -static int wgc_set(struct wg_softc *, struct wg_data_io *); -static int wg_up(struct wg_softc *); -static void wg_down(struct wg_softc *); -static void wg_reassign(struct ifnet *, struct vnet *, char *unused); -static void wg_init(void *); -static int wg_ioctl(struct ifnet *, u_long, caddr_t); -static void vnet_wg_init(const void *); -static void vnet_wg_uninit(const void *); -static void wg_module_init(void); -static void wg_module_deinit(void); - -/* TODO Peer */ -static struct wg_peer * -wg_peer_alloc(struct wg_softc *sc) -{ - struct wg_peer *peer; - - sx_assert(&sc->sc_lock, SX_XLOCKED); - - peer = malloc(sizeof(*peer), M_WG, M_WAITOK|M_ZERO); - peer->p_sc = sc; - peer->p_id = peer_counter++; - CK_LIST_INIT(&peer->p_aips); - - rw_init(&peer->p_endpoint_lock, "wg_peer_endpoint"); - wg_queue_init(&peer->p_stage_queue, "stageq"); - wg_queue_init(&peer->p_encap_queue, "txq"); - wg_queue_init(&peer->p_decap_queue, "rxq"); - - GROUPTASK_INIT(&peer->p_send_initiation, 0, (gtask_fn_t *)wg_send_initiation, peer); - taskqgroup_attach(qgroup_if_io_tqg, &peer->p_send_initiation, peer, NULL, NULL, "wg initiation"); - GROUPTASK_INIT(&peer->p_send_keepalive, 0, (gtask_fn_t *)wg_send_keepalive, peer); - taskqgroup_attach(qgroup_if_io_tqg, &peer->p_send_keepalive, peer, NULL, NULL, "wg keepalive"); - GROUPTASK_INIT(&peer->p_clear_secrets, 0, (gtask_fn_t *)noise_remote_clear, &peer->p_remote); - taskqgroup_attach(qgroup_if_io_tqg, &peer->p_clear_secrets, - &peer->p_remote, NULL, NULL, "wg clear secrets"); - - GROUPTASK_INIT(&peer->p_send, 0, (gtask_fn_t *)wg_deliver_out, peer); - taskqgroup_attach(qgroup_if_io_tqg, &peer->p_send, peer, NULL, NULL, "wg send"); - GROUPTASK_INIT(&peer->p_recv, 0, (gtask_fn_t *)wg_deliver_in, peer); - taskqgroup_attach(qgroup_if_io_tqg, &peer->p_recv, peer, NULL, NULL, "wg recv"); - - wg_timers_init(&peer->p_timers); - - peer->p_tx_bytes = counter_u64_alloc(M_WAITOK); - peer->p_rx_bytes = counter_u64_alloc(M_WAITOK); - - SLIST_INIT(&peer->p_unused_index); - SLIST_INSERT_HEAD(&peer->p_unused_index, &peer->p_index[0], - i_unused_entry); - SLIST_INSERT_HEAD(&peer->p_unused_index, &peer->p_index[1], - i_unused_entry); - SLIST_INSERT_HEAD(&peer->p_unused_index, &peer->p_index[2], - i_unused_entry); - - return (peer); -} - -#define WG_HASHTABLE_PEER_FOREACH(peer, i, ht) \ - for (i = 0; i < HASHTABLE_PEER_SIZE; i++) \ - LIST_FOREACH(peer, &(ht)->h_peers[i], p_hash_entry) -#define WG_HASHTABLE_PEER_FOREACH_SAFE(peer, i, ht, tpeer) \ - for (i = 0; i < HASHTABLE_PEER_SIZE; i++) \ - CK_LIST_FOREACH_SAFE(peer, &(ht)->h_peers[i], p_hash_entry, tpeer) -static void -wg_hashtable_init(struct wg_hashtable *ht) -{ - mtx_init(&ht->h_mtx, "hash lock", NULL, MTX_DEF); - arc4random_buf(&ht->h_secret, sizeof(ht->h_secret)); - ht->h_num_peers = 0; - ht->h_peers = hashinit(HASHTABLE_PEER_SIZE, M_DEVBUF, - &ht->h_peers_mask); -} - -static void -wg_hashtable_destroy(struct wg_hashtable *ht) -{ - MPASS(ht->h_num_peers == 0); - mtx_destroy(&ht->h_mtx); - hashdestroy(ht->h_peers, M_DEVBUF, ht->h_peers_mask); -} - -static void -wg_hashtable_peer_insert(struct wg_hashtable *ht, struct wg_peer *peer) -{ - uint64_t key; - - key = siphash24(&ht->h_secret, peer->p_remote.r_public, - sizeof(peer->p_remote.r_public)); - - mtx_lock(&ht->h_mtx); - ht->h_num_peers++; - CK_LIST_INSERT_HEAD(&ht->h_peers[key & ht->h_peers_mask], peer, p_hash_entry); - CK_LIST_INSERT_HEAD(&ht->h_peers_list, peer, p_entry); - mtx_unlock(&ht->h_mtx); -} - -static struct wg_peer * -wg_peer_lookup(struct wg_softc *sc, - const uint8_t pubkey[WG_KEY_SIZE]) -{ - struct wg_hashtable *ht = &sc->sc_hashtable; - uint64_t key; - struct wg_peer *i = NULL; - - key = siphash24(&ht->h_secret, pubkey, WG_KEY_SIZE); - - mtx_lock(&ht->h_mtx); - CK_LIST_FOREACH(i, &ht->h_peers[key & ht->h_peers_mask], p_hash_entry) { - if (timingsafe_bcmp(i->p_remote.r_public, pubkey, - WG_KEY_SIZE) == 0) - break; - } - mtx_unlock(&ht->h_mtx); - - return i; -} - -static void -wg_hashtable_peer_remove(struct wg_hashtable *ht, struct wg_peer *peer) -{ - mtx_lock(&ht->h_mtx); - ht->h_num_peers--; - CK_LIST_REMOVE(peer, p_hash_entry); - CK_LIST_REMOVE(peer, p_entry); - mtx_unlock(&ht->h_mtx); -} - -static void -wg_peer_free_deferred(epoch_context_t ctx) -{ - struct wg_peer *peer = __containerof(ctx, struct wg_peer, p_ctx); - counter_u64_free(peer->p_tx_bytes); - counter_u64_free(peer->p_rx_bytes); - rw_destroy(&peer->p_timers.t_lock); - rw_destroy(&peer->p_endpoint_lock); - free(peer, M_WG); -} - -static void -wg_peer_destroy(struct wg_peer *peer) -{ - /* Callers should already have called: - * wg_hashtable_peer_remove(&sc->sc_hashtable, peer); - */ - wg_aip_delete(&peer->p_sc->sc_aips, peer); - MPASS(CK_LIST_EMPTY(&peer->p_aips)); - - /* We disable all timers, so we can't call the following tasks. */ - wg_timers_disable(&peer->p_timers); - - /* Ensure the tasks have finished running */ - GROUPTASK_DRAIN(&peer->p_clear_secrets); - GROUPTASK_DRAIN(&peer->p_send_initiation); - GROUPTASK_DRAIN(&peer->p_send_keepalive); - GROUPTASK_DRAIN(&peer->p_recv); - GROUPTASK_DRAIN(&peer->p_send); - - taskqgroup_detach(qgroup_if_io_tqg, &peer->p_clear_secrets); - taskqgroup_detach(qgroup_if_io_tqg, &peer->p_send_initiation); - taskqgroup_detach(qgroup_if_io_tqg, &peer->p_send_keepalive); - taskqgroup_detach(qgroup_if_io_tqg, &peer->p_recv); - taskqgroup_detach(qgroup_if_io_tqg, &peer->p_send); - - wg_queue_deinit(&peer->p_decap_queue); - wg_queue_deinit(&peer->p_encap_queue); - wg_queue_deinit(&peer->p_stage_queue); - - /* Final cleanup */ - --peer->p_sc->sc_peer_count; - noise_remote_clear(&peer->p_remote); - DPRINTF(peer->p_sc, "Peer %llu destroyed\n", (unsigned long long)peer->p_id); - NET_EPOCH_CALL(wg_peer_free_deferred, &peer->p_ctx); -} - -static void -wg_peer_set_endpoint_from_tag(struct wg_peer *peer, struct wg_tag *t) -{ - struct wg_endpoint *e = &t->t_endpoint; - - MPASS(e->e_remote.r_sa.sa_family != 0); - if (memcmp(e, &peer->p_endpoint, sizeof(*e)) == 0) - return; - - peer->p_endpoint = *e; -} - -static void -wg_peer_clear_src(struct wg_peer *peer) -{ - rw_rlock(&peer->p_endpoint_lock); - bzero(&peer->p_endpoint.e_local, sizeof(peer->p_endpoint.e_local)); - rw_runlock(&peer->p_endpoint_lock); -} - -static void -wg_peer_get_endpoint(struct wg_peer *p, struct wg_endpoint *e) -{ - memcpy(e, &p->p_endpoint, sizeof(*e)); -} - -/* Allowed IP */ -static int -wg_aip_init(struct wg_aip_table *tbl) -{ - int rc; - - tbl->t_count = 0; - rc = rn_inithead((void **)&tbl->t_ip, - offsetof(struct sockaddr_in, sin_addr) * NBBY); - - if (rc == 0) - return (ENOMEM); - RADIX_NODE_HEAD_LOCK_INIT(tbl->t_ip); -#ifdef INET6 - rc = rn_inithead((void **)&tbl->t_ip6, - offsetof(struct sockaddr_in6, sin6_addr) * NBBY); - if (rc == 0) { - free(tbl->t_ip, M_RTABLE); - return (ENOMEM); - } - RADIX_NODE_HEAD_LOCK_INIT(tbl->t_ip6); -#endif - return (0); -} - -static void -wg_aip_destroy(struct wg_aip_table *tbl) -{ - RADIX_NODE_HEAD_DESTROY(tbl->t_ip); - free(tbl->t_ip, M_RTABLE); -#ifdef INET6 - RADIX_NODE_HEAD_DESTROY(tbl->t_ip6); - free(tbl->t_ip6, M_RTABLE); -#endif -} - -static void -wg_aip_populate_aip4(struct wg_aip *aip, const struct in_addr *addr, - uint8_t mask) -{ - struct sockaddr_in *raddr, *rmask; - uint8_t *p; - unsigned int i; - - raddr = (struct sockaddr_in *)&aip->r_addr; - rmask = (struct sockaddr_in *)&aip->r_mask; - - raddr->sin_len = sizeof(*raddr); - raddr->sin_family = AF_INET; - raddr->sin_addr = *addr; - - rmask->sin_len = sizeof(*rmask); - p = (uint8_t *)&rmask->sin_addr.s_addr; - for (i = 0; i < mask / NBBY; i++) - p[i] = 0xff; - if ((mask % NBBY) != 0) - p[i] = (0xff00 >> (mask % NBBY)) & 0xff; - raddr->sin_addr.s_addr &= rmask->sin_addr.s_addr; -} - -static void -wg_aip_populate_aip6(struct wg_aip *aip, const struct in6_addr *addr, - uint8_t mask) -{ - struct sockaddr_in6 *raddr, *rmask; - - raddr = (struct sockaddr_in6 *)&aip->r_addr; - rmask = (struct sockaddr_in6 *)&aip->r_mask; - - raddr->sin6_len = sizeof(*raddr); - raddr->sin6_family = AF_INET6; - raddr->sin6_addr = *addr; - - rmask->sin6_len = sizeof(*rmask); - in6_prefixlen2mask(&rmask->sin6_addr, mask); - for (int i = 0; i < 4; ++i) - raddr->sin6_addr.__u6_addr.__u6_addr32[i] &= rmask->sin6_addr.__u6_addr.__u6_addr32[i]; -} - -/* wg_aip_take assumes that the caller guarantees the allowed-ip exists. */ -static void -wg_aip_take(struct radix_node_head *root, struct wg_peer *peer, - struct wg_aip *route) -{ - struct radix_node *node; - struct wg_peer *ppeer; - - RADIX_NODE_HEAD_LOCK_ASSERT(root); - - node = root->rnh_lookup(&route->r_addr, &route->r_mask, - &root->rh); - MPASS(node != NULL); - - route = (struct wg_aip *)node; - ppeer = route->r_peer; - if (ppeer != peer) { - route->r_peer = peer; - - CK_LIST_REMOVE(route, r_entry); - CK_LIST_INSERT_HEAD(&peer->p_aips, route, r_entry); - } -} - -static int -wg_aip_add(struct wg_aip_table *tbl, struct wg_peer *peer, - const struct wg_allowedip *aip) -{ - struct radix_node *node; - struct radix_node_head *root; - struct wg_aip *route; - sa_family_t family; - bool needfree = false; - - family = aip->family; - if (family != AF_INET && family != AF_INET6) { - return (EINVAL); - } - - route = malloc(sizeof(*route), M_WG, M_WAITOK|M_ZERO); - switch (family) { - case AF_INET: - root = tbl->t_ip; - - wg_aip_populate_aip4(route, &aip->ip4, aip->cidr); - break; - case AF_INET6: - root = tbl->t_ip6; - - wg_aip_populate_aip6(route, &aip->ip6, aip->cidr); - break; - } - - route->r_peer = peer; - - RADIX_NODE_HEAD_LOCK(root); - node = root->rnh_addaddr(&route->r_addr, &route->r_mask, &root->rh, - route->r_nodes); - if (node == route->r_nodes) { - tbl->t_count++; - CK_LIST_INSERT_HEAD(&peer->p_aips, route, r_entry); - } else { - needfree = true; - wg_aip_take(root, peer, route); - } - RADIX_NODE_HEAD_UNLOCK(root); - if (needfree) { - free(route, M_WG); - } - return (0); -} - -static struct wg_peer * -wg_aip_lookup(struct wg_aip_table *tbl, struct mbuf *m, - enum route_direction dir) -{ - RADIX_NODE_HEAD_RLOCK_TRACKER; - struct ip *iphdr; - struct ip6_hdr *ip6hdr; - struct radix_node_head *root; - struct radix_node *node; - struct wg_peer *peer = NULL; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - void *addr; - int version; - - NET_EPOCH_ASSERT(); - iphdr = mtod(m, struct ip *); - version = iphdr->ip_v; - - if (__predict_false(dir != IN && dir != OUT)) - return NULL; - - if (version == 4) { - root = tbl->t_ip; - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(struct sockaddr_in); - if (dir == IN) - sin.sin_addr = iphdr->ip_src; - else - sin.sin_addr = iphdr->ip_dst; - addr = &sin; - } else if (version == 6) { - ip6hdr = mtod(m, struct ip6_hdr *); - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_len = sizeof(struct sockaddr_in6); - - root = tbl->t_ip6; - if (dir == IN) - addr = &ip6hdr->ip6_src; - else - addr = &ip6hdr->ip6_dst; - memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr)); - addr = &sin6; - } else { - return (NULL); - } - RADIX_NODE_HEAD_RLOCK(root); - if ((node = root->rnh_matchaddr(addr, &root->rh)) != NULL) { - peer = ((struct wg_aip *) node)->r_peer; - } - RADIX_NODE_HEAD_RUNLOCK(root); - return (peer); -} - -struct peer_del_arg { - struct radix_node_head * pda_head; - struct wg_peer *pda_peer; - struct wg_aip_table *pda_tbl; -}; - -static int -wg_peer_remove(struct radix_node *rn, void *arg) -{ - struct peer_del_arg *pda = arg; - struct wg_peer *peer = pda->pda_peer; - struct radix_node_head * rnh = pda->pda_head; - struct wg_aip_table *tbl = pda->pda_tbl; - struct wg_aip *route = (struct wg_aip *)rn; - struct radix_node *x; - - if (route->r_peer != peer) - return (0); - x = (struct radix_node *)rnh->rnh_deladdr(&route->r_addr, - &route->r_mask, &rnh->rh); - if (x != NULL) { - tbl->t_count--; - CK_LIST_REMOVE(route, r_entry); - free(route, M_WG); - } - return (0); -} - -static void -wg_peer_remove_all(struct wg_softc *sc) -{ - struct wg_peer *peer, *tpeer; - - sx_assert(&sc->sc_lock, SX_XLOCKED); - - CK_LIST_FOREACH_SAFE(peer, &sc->sc_hashtable.h_peers_list, - p_entry, tpeer) { - wg_hashtable_peer_remove(&sc->sc_hashtable, peer); - wg_peer_destroy(peer); - } -} - -static int -wg_aip_delete(struct wg_aip_table *tbl, struct wg_peer *peer) -{ - struct peer_del_arg pda; - - pda.pda_peer = peer; - pda.pda_tbl = tbl; - RADIX_NODE_HEAD_LOCK(tbl->t_ip); - pda.pda_head = tbl->t_ip; - rn_walktree(&tbl->t_ip->rh, wg_peer_remove, &pda); - RADIX_NODE_HEAD_UNLOCK(tbl->t_ip); - - RADIX_NODE_HEAD_LOCK(tbl->t_ip6); - pda.pda_head = tbl->t_ip6; - rn_walktree(&tbl->t_ip6->rh, wg_peer_remove, &pda); - RADIX_NODE_HEAD_UNLOCK(tbl->t_ip6); - return (0); -} - -static int -wg_socket_init(struct wg_softc *sc, in_port_t port) -{ - struct thread *td; - struct ucred *cred; - struct socket *so4, *so6; - int rc; - - sx_assert(&sc->sc_lock, SX_XLOCKED); - - so4 = so6 = NULL; - td = curthread; - if ((cred = sc->sc_ucred) == NULL) - return (EBUSY); - - /* - * For socket creation, we use the creds of the thread that created the - * tunnel rather than the current thread to maintain the semantics that - * WireGuard has on Linux with network namespaces -- that the sockets - * are created in their home vnet so that they can be configured and - * functionally attached to a foreign vnet as the jail's only interface - * to the network. - */ - rc = socreate(AF_INET, &so4, SOCK_DGRAM, IPPROTO_UDP, cred, td); - if (rc != 0) - goto out; - - rc = udp_set_kernel_tunneling(so4, wg_input, NULL, sc); - /* - * udp_set_kernel_tunneling can only fail if there is already a tunneling function set. - * This should never happen with a new socket. - */ - MPASS(rc == 0); - - rc = socreate(AF_INET6, &so6, SOCK_DGRAM, IPPROTO_UDP, cred, td); - if (rc != 0) - goto out; - rc = udp_set_kernel_tunneling(so6, wg_input, NULL, sc); - MPASS(rc == 0); - - so4->so_user_cookie = so6->so_user_cookie = sc->sc_socket.so_user_cookie; - - rc = wg_socket_bind(so4, so6, &port); - if (rc == 0) { - sc->sc_socket.so_port = port; - wg_socket_set(sc, so4, so6); - } -out: - if (rc != 0) { - if (so4 != NULL) - soclose(so4); - if (so6 != NULL) - soclose(so6); - } - return (rc); -} - -static void wg_socket_set_cookie(struct wg_softc *sc, uint32_t user_cookie) -{ - struct wg_socket *so = &sc->sc_socket; - - sx_assert(&sc->sc_lock, SX_XLOCKED); - - so->so_user_cookie = user_cookie; - if (so->so_so4) - so->so_so4->so_user_cookie = user_cookie; - if (so->so_so6) - so->so_so6->so_user_cookie = user_cookie; -} - -static void -wg_socket_uninit(struct wg_softc *sc) -{ - wg_socket_set(sc, NULL, NULL); -} - -static void -wg_socket_set(struct wg_softc *sc, struct socket *new_so4, struct socket *new_so6) -{ - struct wg_socket *so = &sc->sc_socket; - struct socket *so4, *so6; - - sx_assert(&sc->sc_lock, SX_XLOCKED); - - so4 = atomic_load_ptr(&so->so_so4); - so6 = atomic_load_ptr(&so->so_so6); - atomic_store_ptr(&so->so_so4, new_so4); - atomic_store_ptr(&so->so_so6, new_so6); - - if (!so4 && !so6) - return; - NET_EPOCH_WAIT(); - if (so4) - soclose(so4); - if (so6) - soclose(so6); -} - -union wg_sockaddr { - struct sockaddr sa; - struct sockaddr_in in4; - struct sockaddr_in6 in6; -}; - -static int -wg_socket_bind(struct socket *so4, struct socket *so6, in_port_t *requested_port) -{ - int rc; - struct thread *td; - union wg_sockaddr laddr; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - in_port_t port = *requested_port; - - td = curthread; - bzero(&laddr, sizeof(laddr)); - sin = &laddr.in4; - sin->sin_len = sizeof(laddr.in4); - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - sin->sin_addr = (struct in_addr) { 0 }; - - if ((rc = sobind(so4, &laddr.sa, td)) != 0) - return (rc); - - if (port == 0) { - rc = sogetsockaddr(so4, (struct sockaddr **)&sin); - if (rc != 0) - return (rc); - port = ntohs(sin->sin_port); - free(sin, M_SONAME); - } - - sin6 = &laddr.in6; - sin6->sin6_len = sizeof(laddr.in6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(port); - sin6->sin6_addr = (struct in6_addr) { .s6_addr = { 0 } }; - rc = sobind(so6, &laddr.sa, td); - if (rc != 0) - return (rc); - *requested_port = port; - return (0); -} - -static int -wg_send(struct wg_softc *sc, struct wg_endpoint *e, struct mbuf *m) -{ - struct epoch_tracker et; - struct sockaddr *sa; - struct wg_socket *so = &sc->sc_socket; - struct socket *so4, *so6; - struct mbuf *control = NULL; - int ret = 0; - size_t len = m->m_pkthdr.len; - - /* Get local control address before locking */ - if (e->e_remote.r_sa.sa_family == AF_INET) { - if (e->e_local.l_in.s_addr != INADDR_ANY) - control = sbcreatecontrol((caddr_t)&e->e_local.l_in, - sizeof(struct in_addr), IP_SENDSRCADDR, - IPPROTO_IP); -#ifdef INET6 - } else if (e->e_remote.r_sa.sa_family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED(&e->e_local.l_in6)) - control = sbcreatecontrol((caddr_t)&e->e_local.l_pktinfo6, - sizeof(struct in6_pktinfo), IPV6_PKTINFO, - IPPROTO_IPV6); -#endif - } else { - m_freem(m); - return (EAFNOSUPPORT); - } - - /* Get remote address */ - sa = &e->e_remote.r_sa; - - NET_EPOCH_ENTER(et); - so4 = atomic_load_ptr(&so->so_so4); - so6 = atomic_load_ptr(&so->so_so6); - if (e->e_remote.r_sa.sa_family == AF_INET && so4 != NULL) - ret = sosend(so4, sa, NULL, m, control, 0, curthread); - else if (e->e_remote.r_sa.sa_family == AF_INET6 && so6 != NULL) - ret = sosend(so6, sa, NULL, m, control, 0, curthread); - else { - ret = ENOTCONN; - m_freem(control); - m_freem(m); - } - NET_EPOCH_EXIT(et); - if (ret == 0) { - if_inc_counter(sc->sc_ifp, IFCOUNTER_OPACKETS, 1); - if_inc_counter(sc->sc_ifp, IFCOUNTER_OBYTES, len); - } - return (ret); -} - -static void -wg_send_buf(struct wg_softc *sc, struct wg_endpoint *e, uint8_t *buf, - size_t len) -{ - struct mbuf *m; - int ret = 0; - -retry: - m = m_gethdr(M_WAITOK, MT_DATA); - m->m_len = 0; - m_copyback(m, 0, len, buf); - - if (ret == 0) { - ret = wg_send(sc, e, m); - /* Retry if we couldn't bind to e->e_local */ - if (ret == EADDRNOTAVAIL) { - bzero(&e->e_local, sizeof(e->e_local)); - goto retry; - } - } else { - ret = wg_send(sc, e, m); - } - if (ret) - DPRINTF(sc, "Unable to send packet: %d\n", ret); -} - -/* TODO Tag */ -static struct wg_tag * -wg_tag_get(struct mbuf *m) -{ - struct m_tag *tag; - - tag = m_tag_find(m, MTAG_WIREGUARD, NULL); - if (tag == NULL) { - tag = m_tag_get(MTAG_WIREGUARD, sizeof(struct wg_tag), M_NOWAIT|M_ZERO); - m_tag_prepend(m, tag); - MPASS(!SLIST_EMPTY(&m->m_pkthdr.tags)); - MPASS(m_tag_locate(m, MTAG_ABI_COMPAT, MTAG_WIREGUARD, NULL) == tag); - } - return (struct wg_tag *)tag; -} - -static struct wg_endpoint * -wg_mbuf_endpoint_get(struct mbuf *m) -{ - struct wg_tag *hdr; - - if ((hdr = wg_tag_get(m)) == NULL) - return (NULL); - - return (&hdr->t_endpoint); -} - -/* Timers */ -static void -wg_timers_init(struct wg_timers *t) -{ - bzero(t, sizeof(*t)); - - t->t_disabled = 1; - rw_init(&t->t_lock, "wg peer timers"); - callout_init(&t->t_retry_handshake, true); - callout_init(&t->t_send_keepalive, true); - callout_init(&t->t_new_handshake, true); - callout_init(&t->t_zero_key_material, true); - callout_init(&t->t_persistent_keepalive, true); -} - -static void -wg_timers_enable(struct wg_timers *t) -{ - rw_wlock(&t->t_lock); - t->t_disabled = 0; - rw_wunlock(&t->t_lock); - wg_timers_run_persistent_keepalive(t); -} - -static void -wg_timers_disable(struct wg_timers *t) -{ - rw_wlock(&t->t_lock); - t->t_disabled = 1; - t->t_need_another_keepalive = 0; - rw_wunlock(&t->t_lock); - - callout_stop(&t->t_retry_handshake); - callout_stop(&t->t_send_keepalive); - callout_stop(&t->t_new_handshake); - callout_stop(&t->t_zero_key_material); - callout_stop(&t->t_persistent_keepalive); -} - -static void -wg_timers_set_persistent_keepalive(struct wg_timers *t, uint16_t interval) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled) { - t->t_persistent_keepalive_interval = interval; - wg_timers_run_persistent_keepalive(t); - } - rw_runlock(&t->t_lock); -} - -static void -wg_timers_get_last_handshake(struct wg_timers *t, struct timespec *time) -{ - rw_rlock(&t->t_lock); - time->tv_sec = t->t_handshake_complete.tv_sec; - time->tv_nsec = t->t_handshake_complete.tv_nsec; - rw_runlock(&t->t_lock); -} - -static int -wg_timers_expired_handshake_last_sent(struct wg_timers *t) -{ - struct timespec uptime; - struct timespec expire = { .tv_sec = REKEY_TIMEOUT, .tv_nsec = 0 }; - - getnanouptime(&uptime); - timespecadd(&t->t_handshake_last_sent, &expire, &expire); - return timespeccmp(&uptime, &expire, >) ? ETIMEDOUT : 0; -} - -static int -wg_timers_check_handshake_last_sent(struct wg_timers *t) -{ - int ret; - - rw_wlock(&t->t_lock); - if ((ret = wg_timers_expired_handshake_last_sent(t)) == ETIMEDOUT) - getnanouptime(&t->t_handshake_last_sent); - rw_wunlock(&t->t_lock); - return (ret); -} - -/* Should be called after an authenticated data packet is sent. */ -static void -wg_timers_event_data_sent(struct wg_timers *t) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled && !callout_pending(&t->t_new_handshake)) - callout_reset(&t->t_new_handshake, MSEC_2_TICKS( - NEW_HANDSHAKE_TIMEOUT * 1000 + - arc4random_uniform(REKEY_TIMEOUT_JITTER)), - (timeout_t *)wg_timers_run_new_handshake, t); - rw_runlock(&t->t_lock); -} - -/* Should be called after an authenticated data packet is received. */ -static void -wg_timers_event_data_received(struct wg_timers *t) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled) { - if (!callout_pending(&t->t_send_keepalive)) { - callout_reset(&t->t_send_keepalive, - MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000), - (timeout_t *)wg_timers_run_send_keepalive, t); - } else { - t->t_need_another_keepalive = 1; - } - } - rw_runlock(&t->t_lock); -} - -/* - * Should be called after any type of authenticated packet is sent, whether - * keepalive, data, or handshake. - */ -static void -wg_timers_event_any_authenticated_packet_sent(struct wg_timers *t) -{ - callout_stop(&t->t_send_keepalive); -} - -/* - * Should be called after any type of authenticated packet is received, whether - * keepalive, data, or handshake. - */ -static void -wg_timers_event_any_authenticated_packet_received(struct wg_timers *t) -{ - callout_stop(&t->t_new_handshake); -} - -/* - * Should be called before a packet with authentication, whether - * keepalive, data, or handshake is sent, or after one is received. - */ -static void -wg_timers_event_any_authenticated_packet_traversal(struct wg_timers *t) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled && t->t_persistent_keepalive_interval > 0) - callout_reset(&t->t_persistent_keepalive, - MSEC_2_TICKS(t->t_persistent_keepalive_interval * 1000), - (timeout_t *)wg_timers_run_persistent_keepalive, t); - rw_runlock(&t->t_lock); -} - -/* Should be called after a handshake initiation message is sent. */ -static void -wg_timers_event_handshake_initiated(struct wg_timers *t) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled) - callout_reset(&t->t_retry_handshake, MSEC_2_TICKS( - REKEY_TIMEOUT * 1000 + - arc4random_uniform(REKEY_TIMEOUT_JITTER)), - (timeout_t *)wg_timers_run_retry_handshake, t); - rw_runlock(&t->t_lock); -} - -static void -wg_timers_event_handshake_responded(struct wg_timers *t) -{ - rw_wlock(&t->t_lock); - getnanouptime(&t->t_handshake_last_sent); - rw_wunlock(&t->t_lock); -} - -/* - * Should be called after a handshake response message is received and processed - * or when getting key confirmation via the first data message. - */ -static void -wg_timers_event_handshake_complete(struct wg_timers *t) -{ - rw_wlock(&t->t_lock); - if (!t->t_disabled) { - callout_stop(&t->t_retry_handshake); - t->t_handshake_retries = 0; - getnanotime(&t->t_handshake_complete); - wg_timers_run_send_keepalive(t); - } - rw_wunlock(&t->t_lock); -} - -/* - * Should be called after an ephemeral key is created, which is before sending a - * handshake response or after receiving a handshake response. - */ -static void -wg_timers_event_session_derived(struct wg_timers *t) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled) { - callout_reset(&t->t_zero_key_material, - MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000), - (timeout_t *)wg_timers_run_zero_key_material, t); - } - rw_runlock(&t->t_lock); -} - -static void -wg_timers_event_want_initiation(struct wg_timers *t) -{ - rw_rlock(&t->t_lock); - if (!t->t_disabled) - wg_timers_run_send_initiation(t, 0); - rw_runlock(&t->t_lock); -} - -static void -wg_timers_event_reset_handshake_last_sent(struct wg_timers *t) -{ - rw_wlock(&t->t_lock); - t->t_handshake_last_sent.tv_sec -= (REKEY_TIMEOUT + 1); - rw_wunlock(&t->t_lock); -} - -static void -wg_timers_run_send_initiation(struct wg_timers *t, int is_retry) -{ - struct wg_peer *peer = __containerof(t, struct wg_peer, p_timers); - if (!is_retry) - t->t_handshake_retries = 0; - if (wg_timers_expired_handshake_last_sent(t) == ETIMEDOUT) - GROUPTASK_ENQUEUE(&peer->p_send_initiation); -} - -static void -wg_timers_run_retry_handshake(struct wg_timers *t) -{ - struct wg_peer *peer = __containerof(t, struct wg_peer, p_timers); - - rw_wlock(&t->t_lock); - if (t->t_handshake_retries <= MAX_TIMER_HANDSHAKES) { - t->t_handshake_retries++; - rw_wunlock(&t->t_lock); - - DPRINTF(peer->p_sc, "Handshake for peer %llu did not complete " - "after %d seconds, retrying (try %d)\n", - (unsigned long long)peer->p_id, - REKEY_TIMEOUT, t->t_handshake_retries + 1); - wg_peer_clear_src(peer); - wg_timers_run_send_initiation(t, 1); - } else { - rw_wunlock(&t->t_lock); - - DPRINTF(peer->p_sc, "Handshake for peer %llu did not complete " - "after %d retries, giving up\n", - (unsigned long long) peer->p_id, MAX_TIMER_HANDSHAKES + 2); - - callout_stop(&t->t_send_keepalive); - wg_queue_purge(&peer->p_stage_queue); - if (!callout_pending(&t->t_zero_key_material)) - callout_reset(&t->t_zero_key_material, - MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000), - (timeout_t *)wg_timers_run_zero_key_material, t); - } -} - -static void -wg_timers_run_send_keepalive(struct wg_timers *t) -{ - struct wg_peer *peer = __containerof(t, struct wg_peer, p_timers); - - GROUPTASK_ENQUEUE(&peer->p_send_keepalive); - if (t->t_need_another_keepalive) { - t->t_need_another_keepalive = 0; - callout_reset(&t->t_send_keepalive, - MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000), - (timeout_t *)wg_timers_run_send_keepalive, t); - } -} - -static void -wg_timers_run_new_handshake(struct wg_timers *t) -{ - struct wg_peer *peer = __containerof(t, struct wg_peer, p_timers); - - DPRINTF(peer->p_sc, "Retrying handshake with peer %llu because we " - "stopped hearing back after %d seconds\n", - (unsigned long long)peer->p_id, NEW_HANDSHAKE_TIMEOUT); - wg_peer_clear_src(peer); - - wg_timers_run_send_initiation(t, 0); -} - -static void -wg_timers_run_zero_key_material(struct wg_timers *t) -{ - struct wg_peer *peer = __containerof(t, struct wg_peer, p_timers); - - DPRINTF(peer->p_sc, "Zeroing out all keys for peer %llu, since we " - "haven't received a new one in %d seconds\n", - (unsigned long long)peer->p_id, REJECT_AFTER_TIME * 3); - GROUPTASK_ENQUEUE(&peer->p_clear_secrets); -} - -static void -wg_timers_run_persistent_keepalive(struct wg_timers *t) -{ - struct wg_peer *peer = __containerof(t, struct wg_peer, p_timers); - - if (t->t_persistent_keepalive_interval != 0) - GROUPTASK_ENQUEUE(&peer->p_send_keepalive); -} - -/* TODO Handshake */ -static void -wg_peer_send_buf(struct wg_peer *peer, uint8_t *buf, size_t len) -{ - struct wg_endpoint endpoint; - - counter_u64_add(peer->p_tx_bytes, len); - wg_timers_event_any_authenticated_packet_traversal(&peer->p_timers); - wg_timers_event_any_authenticated_packet_sent(&peer->p_timers); - wg_peer_get_endpoint(peer, &endpoint); - wg_send_buf(peer->p_sc, &endpoint, buf, len); -} - -static void -wg_send_initiation(struct wg_peer *peer) -{ - struct wg_pkt_initiation pkt; - struct epoch_tracker et; - - if (wg_timers_check_handshake_last_sent(&peer->p_timers) != ETIMEDOUT) - return; - DPRINTF(peer->p_sc, "Sending handshake initiation to peer %llu\n", - (unsigned long long)peer->p_id); - - NET_EPOCH_ENTER(et); - if (noise_create_initiation(&peer->p_remote, &pkt.s_idx, pkt.ue, - pkt.es, pkt.ets) != 0) - goto out; - pkt.t = WG_PKT_INITIATION; - cookie_maker_mac(&peer->p_cookie, &pkt.m, &pkt, - sizeof(pkt)-sizeof(pkt.m)); - wg_peer_send_buf(peer, (uint8_t *)&pkt, sizeof(pkt)); - wg_timers_event_handshake_initiated(&peer->p_timers); -out: - NET_EPOCH_EXIT(et); -} - -static void -wg_send_response(struct wg_peer *peer) -{ - struct wg_pkt_response pkt; - struct epoch_tracker et; - - NET_EPOCH_ENTER(et); - - DPRINTF(peer->p_sc, "Sending handshake response to peer %llu\n", - (unsigned long long)peer->p_id); - - if (noise_create_response(&peer->p_remote, &pkt.s_idx, &pkt.r_idx, - pkt.ue, pkt.en) != 0) - goto out; - if (noise_remote_begin_session(&peer->p_remote) != 0) - goto out; - - wg_timers_event_session_derived(&peer->p_timers); - pkt.t = WG_PKT_RESPONSE; - cookie_maker_mac(&peer->p_cookie, &pkt.m, &pkt, - sizeof(pkt)-sizeof(pkt.m)); - wg_timers_event_handshake_responded(&peer->p_timers); - wg_peer_send_buf(peer, (uint8_t*)&pkt, sizeof(pkt)); -out: - NET_EPOCH_EXIT(et); -} - -static void -wg_send_cookie(struct wg_softc *sc, struct cookie_macs *cm, uint32_t idx, - struct mbuf *m) -{ - struct wg_pkt_cookie pkt; - struct wg_endpoint *e; - - DPRINTF(sc, "Sending cookie response for denied handshake message\n"); - - pkt.t = WG_PKT_COOKIE; - pkt.r_idx = idx; - - e = wg_mbuf_endpoint_get(m); - cookie_checker_create_payload(&sc->sc_cookie, cm, pkt.nonce, - pkt.ec, &e->e_remote.r_sa); - wg_send_buf(sc, e, (uint8_t *)&pkt, sizeof(pkt)); -} - -static void -wg_send_keepalive(struct wg_peer *peer) -{ - struct mbuf *m = NULL; - struct wg_tag *t; - struct epoch_tracker et; - - if (wg_queue_len(&peer->p_stage_queue) != 0) { - NET_EPOCH_ENTER(et); - goto send; - } - if ((m = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) - return; - if ((t = wg_tag_get(m)) == NULL) { - m_freem(m); - return; - } - t->t_peer = peer; - t->t_mbuf = NULL; - t->t_done = 0; - t->t_mtu = 0; /* MTU == 0 OK for keepalive */ - - NET_EPOCH_ENTER(et); - wg_queue_stage(peer, m); -send: - wg_queue_out(peer); - NET_EPOCH_EXIT(et); -} - -static int -wg_cookie_validate_packet(struct cookie_checker *checker, struct mbuf *m, - int under_load) -{ - struct wg_pkt_initiation *init; - struct wg_pkt_response *resp; - struct cookie_macs *macs; - struct wg_endpoint *e; - int type, size; - void *data; - - type = *mtod(m, uint32_t *); - data = m->m_data; - e = wg_mbuf_endpoint_get(m); - if (type == WG_PKT_INITIATION) { - init = mtod(m, struct wg_pkt_initiation *); - macs = &init->m; - size = sizeof(*init) - sizeof(*macs); - } else if (type == WG_PKT_RESPONSE) { - resp = mtod(m, struct wg_pkt_response *); - macs = &resp->m; - size = sizeof(*resp) - sizeof(*macs); - } else - return 0; - - return (cookie_checker_validate_macs(checker, macs, data, size, - under_load, &e->e_remote.r_sa)); -} - - -static void -wg_handshake(struct wg_softc *sc, struct mbuf *m) -{ - struct wg_pkt_initiation *init; - struct wg_pkt_response *resp; - struct noise_remote *remote; - struct wg_pkt_cookie *cook; - struct wg_peer *peer; - struct wg_tag *t; - - /* This is global, so that our load calculation applies to the whole - * system. We don't care about races with it at all. - */ - static struct timeval wg_last_underload; - static const struct timeval underload_interval = { UNDERLOAD_TIMEOUT, 0 }; - bool packet_needs_cookie = false; - int underload, res; - - underload = mbufq_len(&sc->sc_handshake_queue) >= - MAX_QUEUED_HANDSHAKES / 8; - if (underload) - getmicrouptime(&wg_last_underload); - else if (wg_last_underload.tv_sec != 0) { - if (!ratecheck(&wg_last_underload, &underload_interval)) - underload = 1; - else - bzero(&wg_last_underload, sizeof(wg_last_underload)); - } - - res = wg_cookie_validate_packet(&sc->sc_cookie, m, underload); - - if (res && res != EAGAIN) { - printf("validate_packet got %d\n", res); - goto free; - } - if (res == EINVAL) { - DPRINTF(sc, "Invalid initiation MAC\n"); - goto free; - } else if (res == ECONNREFUSED) { - DPRINTF(sc, "Handshake ratelimited\n"); - goto free; - } else if (res == EAGAIN) { - packet_needs_cookie = true; - } else if (res != 0) { - DPRINTF(sc, "Unexpected handshake ratelimit response: %d\n", res); - goto free; - } - - t = wg_tag_get(m); - switch (*mtod(m, uint32_t *)) { - case WG_PKT_INITIATION: - init = mtod(m, struct wg_pkt_initiation *); - - if (packet_needs_cookie) { - wg_send_cookie(sc, &init->m, init->s_idx, m); - goto free; - } - if (noise_consume_initiation(&sc->sc_local, &remote, - init->s_idx, init->ue, init->es, init->ets) != 0) { - DPRINTF(sc, "Invalid handshake initiation"); - goto free; - } - - peer = __containerof(remote, struct wg_peer, p_remote); - DPRINTF(sc, "Receiving handshake initiation from peer %llu\n", - (unsigned long long)peer->p_id); - counter_u64_add(peer->p_rx_bytes, sizeof(*init)); - if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1); - if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, sizeof(*init)); - wg_peer_set_endpoint_from_tag(peer, t); - wg_send_response(peer); - break; - case WG_PKT_RESPONSE: - resp = mtod(m, struct wg_pkt_response *); - - if (packet_needs_cookie) { - wg_send_cookie(sc, &resp->m, resp->s_idx, m); - goto free; - } - - if ((remote = wg_index_get(sc, resp->r_idx)) == NULL) { - DPRINTF(sc, "Unknown handshake response\n"); - goto free; - } - peer = __containerof(remote, struct wg_peer, p_remote); - if (noise_consume_response(remote, resp->s_idx, resp->r_idx, - resp->ue, resp->en) != 0) { - DPRINTF(sc, "Invalid handshake response\n"); - goto free; - } - - DPRINTF(sc, "Receiving handshake response from peer %llu\n", - (unsigned long long)peer->p_id); - counter_u64_add(peer->p_rx_bytes, sizeof(*resp)); - if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1); - if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, sizeof(*resp)); - wg_peer_set_endpoint_from_tag(peer, t); - if (noise_remote_begin_session(&peer->p_remote) == 0) { - wg_timers_event_session_derived(&peer->p_timers); - wg_timers_event_handshake_complete(&peer->p_timers); - } - break; - case WG_PKT_COOKIE: - cook = mtod(m, struct wg_pkt_cookie *); - - if ((remote = wg_index_get(sc, cook->r_idx)) == NULL) { - DPRINTF(sc, "Unknown cookie index\n"); - goto free; - } - - peer = __containerof(remote, struct wg_peer, p_remote); - - if (cookie_maker_consume_payload(&peer->p_cookie, - cook->nonce, cook->ec) != 0) { - DPRINTF(sc, "Could not decrypt cookie response\n"); - goto free; - } - - DPRINTF(sc, "Receiving cookie response\n"); - goto free; - default: - goto free; - } - MPASS(peer != NULL); - wg_timers_event_any_authenticated_packet_received(&peer->p_timers); - wg_timers_event_any_authenticated_packet_traversal(&peer->p_timers); - -free: - m_freem(m); -} - -static void -wg_softc_handshake_receive(struct wg_softc *sc) -{ - struct mbuf *m; - - while ((m = mbufq_dequeue(&sc->sc_handshake_queue)) != NULL) - wg_handshake(sc, m); -} - -/* TODO Encrypt */ -static void -wg_encap(struct wg_softc *sc, struct mbuf *m) -{ - struct wg_pkt_data *data; - size_t padding_len, plaintext_len, out_len; - struct mbuf *mc; - struct wg_peer *peer; - struct wg_tag *t; - uint64_t nonce; - int res, allocation_order; - - NET_EPOCH_ASSERT(); - t = wg_tag_get(m); - peer = t->t_peer; - - plaintext_len = MIN(WG_PKT_WITH_PADDING(m->m_pkthdr.len), t->t_mtu); - padding_len = plaintext_len - m->m_pkthdr.len; - out_len = sizeof(struct wg_pkt_data) + plaintext_len + NOISE_AUTHTAG_LEN; - - if (out_len <= MCLBYTES) - allocation_order = MCLBYTES; - else if (out_len <= MJUMPAGESIZE) - allocation_order = MJUMPAGESIZE; - else if (out_len <= MJUM9BYTES) - allocation_order = MJUM9BYTES; - else if (out_len <= MJUM16BYTES) - allocation_order = MJUM16BYTES; - else - goto error; - - if ((mc = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, allocation_order)) == NULL) - goto error; - - data = mtod(mc, struct wg_pkt_data *); - m_copydata(m, 0, m->m_pkthdr.len, data->buf); - bzero(data->buf + m->m_pkthdr.len, padding_len); - - data->t = WG_PKT_DATA; - - res = noise_remote_encrypt(&peer->p_remote, &data->r_idx, &nonce, - data->buf, plaintext_len); - nonce = htole64(nonce); /* Wire format is little endian. */ - memcpy(data->nonce, &nonce, sizeof(data->nonce)); - - if (__predict_false(res)) { - if (res == EINVAL) { - wg_timers_event_want_initiation(&peer->p_timers); - m_freem(mc); - goto error; - } else if (res == ESTALE) { - wg_timers_event_want_initiation(&peer->p_timers); - } else { - m_freem(mc); - goto error; - } - } - - /* A packet with length 0 is a keepalive packet */ - if (m->m_pkthdr.len == 0) - DPRINTF(sc, "Sending keepalive packet to peer %llu\n", - (unsigned long long)peer->p_id); - /* - * Set the correct output value here since it will be copied - * when we move the pkthdr in send. - */ - mc->m_len = mc->m_pkthdr.len = out_len; - mc->m_flags &= ~(M_MCAST | M_BCAST); - - t->t_mbuf = mc; - error: - /* XXX membar ? */ - t->t_done = 1; - GROUPTASK_ENQUEUE(&peer->p_send); -} - -static void -wg_decap(struct wg_softc *sc, struct mbuf *m) -{ - struct wg_pkt_data *data; - struct wg_peer *peer, *routed_peer; - struct wg_tag *t; - size_t plaintext_len; - uint8_t version; - uint64_t nonce; - int res; - - NET_EPOCH_ASSERT(); - data = mtod(m, struct wg_pkt_data *); - plaintext_len = m->m_pkthdr.len - sizeof(struct wg_pkt_data); - - t = wg_tag_get(m); - peer = t->t_peer; - - memcpy(&nonce, data->nonce, sizeof(nonce)); - nonce = le64toh(nonce); /* Wire format is little endian. */ - - res = noise_remote_decrypt(&peer->p_remote, data->r_idx, nonce, - data->buf, plaintext_len); - - if (__predict_false(res)) { - if (res == EINVAL) { - goto error; - } else if (res == ECONNRESET) { - wg_timers_event_handshake_complete(&peer->p_timers); - } else if (res == ESTALE) { - wg_timers_event_want_initiation(&peer->p_timers); - } else { - panic("unexpected response: %d\n", res); - } - } - wg_peer_set_endpoint_from_tag(peer, t); - - /* Remove the data header, and crypto mac tail from the packet */ - m_adj(m, sizeof(struct wg_pkt_data)); - m_adj(m, -NOISE_AUTHTAG_LEN); - - /* A packet with length 0 is a keepalive packet */ - if (m->m_pkthdr.len == 0) { - DPRINTF(peer->p_sc, "Receiving keepalive packet from peer " - "%llu\n", (unsigned long long)peer->p_id); - goto done; - } - - version = mtod(m, struct ip *)->ip_v; - if (!((version == 4 && m->m_pkthdr.len >= sizeof(struct ip)) || - (version == 6 && m->m_pkthdr.len >= sizeof(struct ip6_hdr)))) { - DPRINTF(peer->p_sc, "Packet is neither ipv4 nor ipv6 from peer " - "%llu\n", (unsigned long long)peer->p_id); - goto error; - } - - routed_peer = wg_aip_lookup(&peer->p_sc->sc_aips, m, IN); - if (routed_peer != peer) { - DPRINTF(peer->p_sc, "Packet has unallowed src IP from peer " - "%llu\n", (unsigned long long)peer->p_id); - goto error; - } - -done: - t->t_mbuf = m; -error: - t->t_done = 1; - GROUPTASK_ENQUEUE(&peer->p_recv); -} - -static void -wg_softc_decrypt(struct wg_softc *sc) -{ - struct epoch_tracker et; - struct mbuf *m; - - NET_EPOCH_ENTER(et); - while ((m = buf_ring_dequeue_mc(sc->sc_decap_ring)) != NULL) - wg_decap(sc, m); - NET_EPOCH_EXIT(et); -} - -static void -wg_softc_encrypt(struct wg_softc *sc) -{ - struct mbuf *m; - struct epoch_tracker et; - - NET_EPOCH_ENTER(et); - while ((m = buf_ring_dequeue_mc(sc->sc_encap_ring)) != NULL) - wg_encap(sc, m); - NET_EPOCH_EXIT(et); -} - -static void -wg_encrypt_dispatch(struct wg_softc *sc) -{ - for (int i = 0; i < mp_ncpus; i++) { - if (sc->sc_encrypt[i].gt_task.ta_flags & TASK_ENQUEUED) - continue; - GROUPTASK_ENQUEUE(&sc->sc_encrypt[i]); - } -} - -static void -wg_decrypt_dispatch(struct wg_softc *sc) -{ - for (int i = 0; i < mp_ncpus; i++) { - if (sc->sc_decrypt[i].gt_task.ta_flags & TASK_ENQUEUED) - continue; - GROUPTASK_ENQUEUE(&sc->sc_decrypt[i]); - } -} - -static void -wg_deliver_out(struct wg_peer *peer) -{ - struct epoch_tracker et; - struct wg_tag *t; - struct mbuf *m; - struct wg_endpoint endpoint; - size_t len; - int ret; - - NET_EPOCH_ENTER(et); - if (peer->p_sc->sc_ifp->if_link_state == LINK_STATE_DOWN) - goto done; - - wg_peer_get_endpoint(peer, &endpoint); - - while ((m = wg_queue_dequeue(&peer->p_encap_queue, &t)) != NULL) { - /* t_mbuf will contain the encrypted packet */ - if (t->t_mbuf == NULL) { - if_inc_counter(peer->p_sc->sc_ifp, IFCOUNTER_OERRORS, 1); - m_freem(m); - continue; - } - len = t->t_mbuf->m_pkthdr.len; - ret = wg_send(peer->p_sc, &endpoint, t->t_mbuf); - - if (ret == 0) { - wg_timers_event_any_authenticated_packet_traversal( - &peer->p_timers); - wg_timers_event_any_authenticated_packet_sent( - &peer->p_timers); - - if (m->m_pkthdr.len != 0) - wg_timers_event_data_sent(&peer->p_timers); - counter_u64_add(peer->p_tx_bytes, len); - } else if (ret == EADDRNOTAVAIL) { - wg_peer_clear_src(peer); - wg_peer_get_endpoint(peer, &endpoint); - } - m_freem(m); - } -done: - NET_EPOCH_EXIT(et); -} - -static void -wg_deliver_in(struct wg_peer *peer) -{ - struct mbuf *m; - struct ifnet *ifp; - struct wg_softc *sc; - struct epoch_tracker et; - struct wg_tag *t; - uint32_t af; - int version; - - NET_EPOCH_ENTER(et); - sc = peer->p_sc; - ifp = sc->sc_ifp; - - while ((m = wg_queue_dequeue(&peer->p_decap_queue, &t)) != NULL) { - /* t_mbuf will contain the encrypted packet */ - if (t->t_mbuf == NULL) { - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - m_freem(m); - continue; - } - MPASS(m == t->t_mbuf); - - wg_timers_event_any_authenticated_packet_received( - &peer->p_timers); - wg_timers_event_any_authenticated_packet_traversal( - &peer->p_timers); - - counter_u64_add(peer->p_rx_bytes, m->m_pkthdr.len + sizeof(struct wg_pkt_data) + NOISE_AUTHTAG_LEN); - if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1); - if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len + sizeof(struct wg_pkt_data) + NOISE_AUTHTAG_LEN); - - if (m->m_pkthdr.len == 0) { - m_freem(m); - continue; - } - - m->m_flags &= ~(M_MCAST | M_BCAST); - m->m_pkthdr.rcvif = ifp; - version = mtod(m, struct ip *)->ip_v; - if (version == IPVERSION) { - af = AF_INET; - BPF_MTAP2(ifp, &af, sizeof(af), m); - CURVNET_SET(ifp->if_vnet); - ip_input(m); - CURVNET_RESTORE(); - } else if (version == 6) { - af = AF_INET6; - BPF_MTAP2(ifp, &af, sizeof(af), m); - CURVNET_SET(ifp->if_vnet); - ip6_input(m); - CURVNET_RESTORE(); - } else - m_freem(m); - - wg_timers_event_data_received(&peer->p_timers); - } - NET_EPOCH_EXIT(et); -} - -static int -wg_queue_in(struct wg_peer *peer, struct mbuf *m) -{ - struct buf_ring *parallel = peer->p_sc->sc_decap_ring; - struct wg_queue *serial = &peer->p_decap_queue; - struct wg_tag *t; - int rc; - - MPASS(wg_tag_get(m) != NULL); - - mtx_lock(&serial->q_mtx); - if ((rc = mbufq_enqueue(&serial->q, m)) == ENOBUFS) { - m_freem(m); - if_inc_counter(peer->p_sc->sc_ifp, IFCOUNTER_OQDROPS, 1); - } else { - m->m_flags |= M_ENQUEUED; - rc = buf_ring_enqueue(parallel, m); - if (rc == ENOBUFS) { - t = wg_tag_get(m); - t->t_done = 1; - } - } - mtx_unlock(&serial->q_mtx); - return (rc); -} - -static void -wg_queue_stage(struct wg_peer *peer, struct mbuf *m) -{ - struct wg_queue *q = &peer->p_stage_queue; - mtx_lock(&q->q_mtx); - STAILQ_INSERT_TAIL(&q->q.mq_head, m, m_stailqpkt); - q->q.mq_len++; - while (mbufq_full(&q->q)) { - m = mbufq_dequeue(&q->q); - if (m) { - m_freem(m); - if_inc_counter(peer->p_sc->sc_ifp, IFCOUNTER_OQDROPS, 1); - } - } - mtx_unlock(&q->q_mtx); -} - -static void -wg_queue_out(struct wg_peer *peer) -{ - struct buf_ring *parallel = peer->p_sc->sc_encap_ring; - struct wg_queue *serial = &peer->p_encap_queue; - struct wg_tag *t; - struct mbufq staged; - struct mbuf *m; - - if (noise_remote_ready(&peer->p_remote) != 0) { - if (wg_queue_len(&peer->p_stage_queue)) - wg_timers_event_want_initiation(&peer->p_timers); - return; - } - - /* We first "steal" the staged queue to a local queue, so that we can do these - * remaining operations without having to hold the staged queue mutex. */ - STAILQ_INIT(&staged.mq_head); - mtx_lock(&peer->p_stage_queue.q_mtx); - STAILQ_SWAP(&staged.mq_head, &peer->p_stage_queue.q.mq_head, mbuf); - staged.mq_len = peer->p_stage_queue.q.mq_len; - peer->p_stage_queue.q.mq_len = 0; - staged.mq_maxlen = peer->p_stage_queue.q.mq_maxlen; - mtx_unlock(&peer->p_stage_queue.q_mtx); - - while ((m = mbufq_dequeue(&staged)) != NULL) { - if ((t = wg_tag_get(m)) == NULL) { - m_freem(m); - continue; - } - t->t_peer = peer; - mtx_lock(&serial->q_mtx); - if (mbufq_enqueue(&serial->q, m) != 0) { - m_freem(m); - if_inc_counter(peer->p_sc->sc_ifp, IFCOUNTER_OQDROPS, 1); - } else { - m->m_flags |= M_ENQUEUED; - if (buf_ring_enqueue(parallel, m)) { - t = wg_tag_get(m); - t->t_done = 1; - } - } - mtx_unlock(&serial->q_mtx); - } - wg_encrypt_dispatch(peer->p_sc); -} - -static struct mbuf * -wg_queue_dequeue(struct wg_queue *q, struct wg_tag **t) -{ - struct mbuf *m_, *m; - - m = NULL; - mtx_lock(&q->q_mtx); - m_ = mbufq_first(&q->q); - if (m_ != NULL && (*t = wg_tag_get(m_))->t_done) { - m = mbufq_dequeue(&q->q); - m->m_flags &= ~M_ENQUEUED; - } - mtx_unlock(&q->q_mtx); - return (m); -} - -static int -wg_queue_len(struct wg_queue *q) -{ - /* This access races. We might consider adding locking here. */ - return (mbufq_len(&q->q)); -} - -static void -wg_queue_init(struct wg_queue *q, const char *name) -{ - mtx_init(&q->q_mtx, name, NULL, MTX_DEF); - mbufq_init(&q->q, MAX_QUEUED_PKT); -} - -static void -wg_queue_deinit(struct wg_queue *q) -{ - wg_queue_purge(q); - mtx_destroy(&q->q_mtx); -} - -static void -wg_queue_purge(struct wg_queue *q) -{ - mtx_lock(&q->q_mtx); - mbufq_drain(&q->q); - mtx_unlock(&q->q_mtx); -} - -/* TODO Indexes */ -static struct noise_remote * -wg_remote_get(struct wg_softc *sc, uint8_t public[NOISE_PUBLIC_KEY_LEN]) -{ - struct wg_peer *peer; - - if ((peer = wg_peer_lookup(sc, public)) == NULL) - return (NULL); - return (&peer->p_remote); -} - -static uint32_t -wg_index_set(struct wg_softc *sc, struct noise_remote *remote) -{ - struct wg_index *index, *iter; - struct wg_peer *peer; - uint32_t key; - - /* We can modify this without a lock as wg_index_set, wg_index_drop are - * guaranteed to be serialised (per remote). */ - peer = __containerof(remote, struct wg_peer, p_remote); - index = SLIST_FIRST(&peer->p_unused_index); - MPASS(index != NULL); - SLIST_REMOVE_HEAD(&peer->p_unused_index, i_unused_entry); - - index->i_value = remote; - - rw_wlock(&sc->sc_index_lock); -assign_id: - key = index->i_key = arc4random(); - key &= sc->sc_index_mask; - LIST_FOREACH(iter, &sc->sc_index[key], i_entry) - if (iter->i_key == index->i_key) - goto assign_id; - - LIST_INSERT_HEAD(&sc->sc_index[key], index, i_entry); - - rw_wunlock(&sc->sc_index_lock); - - /* Likewise, no need to lock for index here. */ - return index->i_key; -} - -static struct noise_remote * -wg_index_get(struct wg_softc *sc, uint32_t key0) -{ - struct wg_index *iter; - struct noise_remote *remote = NULL; - uint32_t key = key0 & sc->sc_index_mask; - - rw_enter_read(&sc->sc_index_lock); - LIST_FOREACH(iter, &sc->sc_index[key], i_entry) - if (iter->i_key == key0) { - remote = iter->i_value; - break; - } - rw_exit_read(&sc->sc_index_lock); - return remote; -} - -static void -wg_index_drop(struct wg_softc *sc, uint32_t key0) -{ - struct wg_index *iter; - struct wg_peer *peer = NULL; - uint32_t key = key0 & sc->sc_index_mask; - - rw_enter_write(&sc->sc_index_lock); - LIST_FOREACH(iter, &sc->sc_index[key], i_entry) - if (iter->i_key == key0) { - LIST_REMOVE(iter, i_entry); - break; - } - rw_exit_write(&sc->sc_index_lock); - - if (iter == NULL) - return; - - /* We expect a peer */ - peer = __containerof(iter->i_value, struct wg_peer, p_remote); - MPASS(peer != NULL); - SLIST_INSERT_HEAD(&peer->p_unused_index, iter, i_unused_entry); -} - -static int -wg_update_endpoint_addrs(struct wg_endpoint *e, const struct sockaddr *srcsa, - struct ifnet *rcvif) -{ - const struct sockaddr_in *sa4; -#ifdef INET6 - const struct sockaddr_in6 *sa6; -#endif - int ret = 0; - - /* - * UDP passes a 2-element sockaddr array: first element is the - * source addr/port, second the destination addr/port. - */ - if (srcsa->sa_family == AF_INET) { - sa4 = (const struct sockaddr_in *)srcsa; - e->e_remote.r_sin = sa4[0]; - e->e_local.l_in = sa4[1].sin_addr; -#ifdef INET6 - } else if (srcsa->sa_family == AF_INET6) { - sa6 = (const struct sockaddr_in6 *)srcsa; - e->e_remote.r_sin6 = sa6[0]; - e->e_local.l_in6 = sa6[1].sin6_addr; -#endif - } else { - ret = EAFNOSUPPORT; - } - - return (ret); -} - -static void -wg_input(struct mbuf *m0, int offset, struct inpcb *inpcb, - const struct sockaddr *srcsa, void *_sc) -{ - struct wg_pkt_data *pkt_data; - struct wg_endpoint *e; - struct wg_softc *sc = _sc; - struct mbuf *m; - int pktlen, pkttype; - struct noise_remote *remote; - struct wg_tag *t; - void *data; - - /* Caller provided us with srcsa, no need for this header. */ - m_adj(m0, offset + sizeof(struct udphdr)); - - /* - * Ensure mbuf has at least enough contiguous data to peel off our - * headers at the beginning. - */ - if ((m = m_defrag(m0, M_NOWAIT)) == NULL) { - m_freem(m0); - return; - } - data = mtod(m, void *); - pkttype = *(uint32_t*)data; - t = wg_tag_get(m); - if (t == NULL) { - goto free; - } - e = wg_mbuf_endpoint_get(m); - - if (wg_update_endpoint_addrs(e, srcsa, m->m_pkthdr.rcvif)) { - goto free; - } - - pktlen = m->m_pkthdr.len; - - if ((pktlen == sizeof(struct wg_pkt_initiation) && - pkttype == WG_PKT_INITIATION) || - (pktlen == sizeof(struct wg_pkt_response) && - pkttype == WG_PKT_RESPONSE) || - (pktlen == sizeof(struct wg_pkt_cookie) && - pkttype == WG_PKT_COOKIE)) { - if (mbufq_enqueue(&sc->sc_handshake_queue, m) == 0) { - GROUPTASK_ENQUEUE(&sc->sc_handshake); - } else { - DPRINTF(sc, "Dropping handshake packet\n"); - m_freem(m); - } - } else if (pktlen >= sizeof(struct wg_pkt_data) + NOISE_AUTHTAG_LEN - && pkttype == WG_PKT_DATA) { - - pkt_data = data; - remote = wg_index_get(sc, pkt_data->r_idx); - if (remote == NULL) { - if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); - m_freem(m); - } else if (buf_ring_count(sc->sc_decap_ring) > MAX_QUEUED_PKT) { - if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1); - m_freem(m); - } else { - t->t_peer = __containerof(remote, struct wg_peer, - p_remote); - t->t_mbuf = NULL; - t->t_done = 0; - - wg_queue_in(t->t_peer, m); - wg_decrypt_dispatch(sc); - } - } else { -free: - m_freem(m); - } -} - -static int -wg_transmit(struct ifnet *ifp, struct mbuf *m) -{ - struct wg_softc *sc; - sa_family_t family; - struct epoch_tracker et; - struct wg_peer *peer; - struct wg_tag *t; - uint32_t af; - int rc; - - /* - * Work around lifetime issue in the ipv6 mld code. - */ - if (__predict_false(ifp->if_flags & IFF_DYING)) - return (ENXIO); - - rc = 0; - sc = ifp->if_softc; - if ((t = wg_tag_get(m)) == NULL) { - rc = ENOBUFS; - goto early_out; - } - af = m->m_pkthdr.ph_family; - BPF_MTAP2(ifp, &af, sizeof(af), m); - - NET_EPOCH_ENTER(et); - peer = wg_aip_lookup(&sc->sc_aips, m, OUT); - if (__predict_false(peer == NULL)) { - rc = ENOKEY; - goto err; - } - - family = peer->p_endpoint.e_remote.r_sa.sa_family; - if (__predict_false(family != AF_INET && family != AF_INET6)) { - DPRINTF(sc, "No valid endpoint has been configured or " - "discovered for peer %llu\n", (unsigned long long)peer->p_id); - - rc = EHOSTUNREACH; - goto err; - } - t->t_peer = peer; - t->t_mbuf = NULL; - t->t_done = 0; - t->t_mtu = ifp->if_mtu; - - wg_queue_stage(peer, m); - wg_queue_out(peer); - NET_EPOCH_EXIT(et); - return (rc); -err: - NET_EPOCH_EXIT(et); -early_out: - if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); - /* TODO: send ICMP unreachable */ - m_free(m); - return (rc); -} - -static int -wg_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, struct route *rt) -{ - m->m_pkthdr.ph_family = sa->sa_family; - return (wg_transmit(ifp, m)); -} - -static int -wg_peer_add(struct wg_softc *sc, const nvlist_t *nvl) -{ - uint8_t public[WG_KEY_SIZE]; - const void *pub_key; - const struct sockaddr *endpoint; - int err; - size_t size; - struct wg_peer *peer = NULL; - bool need_insert = false; - - sx_assert(&sc->sc_lock, SX_XLOCKED); - - if (!nvlist_exists_binary(nvl, "public-key")) { - return (EINVAL); - } - pub_key = nvlist_get_binary(nvl, "public-key", &size); - if (size != WG_KEY_SIZE) { - return (EINVAL); - } - if (noise_local_keys(&sc->sc_local, public, NULL) == 0 && - bcmp(public, pub_key, WG_KEY_SIZE) == 0) { - return (0); // Silently ignored; not actually a failure. - } - peer = wg_peer_lookup(sc, pub_key); - if (nvlist_exists_bool(nvl, "remove") && - nvlist_get_bool(nvl, "remove")) { - if (peer != NULL) { - wg_hashtable_peer_remove(&sc->sc_hashtable, peer); - wg_peer_destroy(peer); - } - return (0); - } - if (nvlist_exists_bool(nvl, "replace-allowedips") && - nvlist_get_bool(nvl, "replace-allowedips") && - peer != NULL) { - - wg_aip_delete(&peer->p_sc->sc_aips, peer); - } - if (peer == NULL) { - if (sc->sc_peer_count >= MAX_PEERS_PER_IFACE) - return (E2BIG); - sc->sc_peer_count++; - - need_insert = true; - peer = wg_peer_alloc(sc); - MPASS(peer != NULL); - noise_remote_init(&peer->p_remote, pub_key, &sc->sc_local); - cookie_maker_init(&peer->p_cookie, pub_key); - } - if (nvlist_exists_binary(nvl, "endpoint")) { - endpoint = nvlist_get_binary(nvl, "endpoint", &size); - if (size > sizeof(peer->p_endpoint.e_remote)) { - err = EINVAL; - goto out; - } - memcpy(&peer->p_endpoint.e_remote, endpoint, size); - } - if (nvlist_exists_binary(nvl, "preshared-key")) { - const void *key; - - key = nvlist_get_binary(nvl, "preshared-key", &size); - if (size != WG_KEY_SIZE) { - err = EINVAL; - goto out; - } - noise_remote_set_psk(&peer->p_remote, key); - } - if (nvlist_exists_number(nvl, "persistent-keepalive-interval")) { - uint64_t pki = nvlist_get_number(nvl, "persistent-keepalive-interval"); - if (pki > UINT16_MAX) { - err = EINVAL; - goto out; - } - wg_timers_set_persistent_keepalive(&peer->p_timers, pki); - } - if (nvlist_exists_nvlist_array(nvl, "allowed-ips")) { - const void *binary; - uint64_t cidr; - const nvlist_t * const * aipl; - struct wg_allowedip aip; - size_t allowedip_count; - - aipl = nvlist_get_nvlist_array(nvl, "allowed-ips", - &allowedip_count); - for (size_t idx = 0; idx < allowedip_count; idx++) { - if (!nvlist_exists_number(aipl[idx], "cidr")) - continue; - cidr = nvlist_get_number(aipl[idx], "cidr"); - if (nvlist_exists_binary(aipl[idx], "ipv4")) { - binary = nvlist_get_binary(aipl[idx], "ipv4", &size); - if (binary == NULL || cidr > 32 || size != sizeof(aip.ip4)) { - err = EINVAL; - goto out; - } - aip.family = AF_INET; - memcpy(&aip.ip4, binary, sizeof(aip.ip4)); - } else if (nvlist_exists_binary(aipl[idx], "ipv6")) { - binary = nvlist_get_binary(aipl[idx], "ipv6", &size); - if (binary == NULL || cidr > 128 || size != sizeof(aip.ip6)) { - err = EINVAL; - goto out; - } - aip.family = AF_INET6; - memcpy(&aip.ip6, binary, sizeof(aip.ip6)); - } else { - continue; - } - aip.cidr = cidr; - - if ((err = wg_aip_add(&sc->sc_aips, peer, &aip)) != 0) { - goto out; - } - } - } - if (need_insert) { - wg_hashtable_peer_insert(&sc->sc_hashtable, peer); - if (sc->sc_ifp->if_link_state == LINK_STATE_UP) - wg_timers_enable(&peer->p_timers); - } - return (0); - -out: - if (need_insert) /* If we fail, only destroy if it was new. */ - wg_peer_destroy(peer); - return (err); -} - -static int -wgc_set(struct wg_softc *sc, struct wg_data_io *wgd) -{ - uint8_t public[WG_KEY_SIZE], private[WG_KEY_SIZE]; - struct ifnet *ifp; - void *nvlpacked; - nvlist_t *nvl; - ssize_t size; - int err; - - ifp = sc->sc_ifp; - if (wgd->wgd_size == 0 || wgd->wgd_data == NULL) - return (EFAULT); - - sx_xlock(&sc->sc_lock); - - nvlpacked = malloc(wgd->wgd_size, M_TEMP, M_WAITOK); - err = copyin(wgd->wgd_data, nvlpacked, wgd->wgd_size); - if (err) - goto out; - nvl = nvlist_unpack(nvlpacked, wgd->wgd_size, 0); - if (nvl == NULL) { - err = EBADMSG; - goto out; - } - if (nvlist_exists_bool(nvl, "replace-peers") && - nvlist_get_bool(nvl, "replace-peers")) - wg_peer_remove_all(sc); - if (nvlist_exists_number(nvl, "listen-port")) { - uint64_t new_port = nvlist_get_number(nvl, "listen-port"); - if (new_port > UINT16_MAX) { - err = EINVAL; - goto out; - } - if (new_port != sc->sc_socket.so_port) { - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { - if ((err = wg_socket_init(sc, new_port)) != 0) - goto out; - } else - sc->sc_socket.so_port = new_port; - } - } - if (nvlist_exists_binary(nvl, "private-key")) { - const void *key = nvlist_get_binary(nvl, "private-key", &size); - if (size != WG_KEY_SIZE) { - err = EINVAL; - goto out; - } - - if (noise_local_keys(&sc->sc_local, NULL, private) != 0 || - timingsafe_bcmp(private, key, WG_KEY_SIZE) != 0) { - struct noise_local *local; - struct wg_peer *peer; - struct wg_hashtable *ht = &sc->sc_hashtable; - bool has_identity; - - if (curve25519_generate_public(public, key)) { - /* Peer conflict: remove conflicting peer. */ - if ((peer = wg_peer_lookup(sc, public)) != - NULL) { - wg_hashtable_peer_remove(ht, peer); - wg_peer_destroy(peer); - } - } - - /* - * Set the private key and invalidate all existing - * handshakes. - */ - local = &sc->sc_local; - noise_local_lock_identity(local); - /* Note: we might be removing the private key. */ - has_identity = noise_local_set_private(local, key) == 0; - mtx_lock(&ht->h_mtx); - CK_LIST_FOREACH(peer, &ht->h_peers_list, p_entry) { - noise_remote_precompute(&peer->p_remote); - wg_timers_event_reset_handshake_last_sent( - &peer->p_timers); - noise_remote_expire_current(&peer->p_remote); - } - mtx_unlock(&ht->h_mtx); - cookie_checker_update(&sc->sc_cookie, - has_identity ? public : NULL); - noise_local_unlock_identity(local); - } - } - if (nvlist_exists_number(nvl, "user-cookie")) { - uint64_t user_cookie = nvlist_get_number(nvl, "user-cookie"); - if (user_cookie > UINT32_MAX) { - err = EINVAL; - goto out; - } - wg_socket_set_cookie(sc, user_cookie); - } - if (nvlist_exists_nvlist_array(nvl, "peers")) { - size_t peercount; - const nvlist_t * const*nvl_peers; - - nvl_peers = nvlist_get_nvlist_array(nvl, "peers", &peercount); - for (int i = 0; i < peercount; i++) { - err = wg_peer_add(sc, nvl_peers[i]); - if (err != 0) - goto out; - } - } - - nvlist_destroy(nvl); -out: - free(nvlpacked, M_TEMP); - sx_xunlock(&sc->sc_lock); - return (err); -} - -static unsigned int -in_mask2len(struct in_addr *mask) -{ - unsigned int x, y; - uint8_t *p; - - p = (uint8_t *)mask; - for (x = 0; x < sizeof(*mask); x++) { - if (p[x] != 0xff) - break; - } - y = 0; - if (x < sizeof(*mask)) { - for (y = 0; y < NBBY; y++) { - if ((p[x] & (0x80 >> y)) == 0) - break; - } - } - return x * NBBY + y; -} - -static int -wg_peer_to_export(struct wg_peer *peer, struct wg_peer_export *exp) -{ - struct wg_endpoint *ep; - struct wg_aip *rt; - struct noise_remote *remote; - int i; - - /* Non-sleepable context. */ - NET_EPOCH_ASSERT(); - - bzero(&exp->endpoint, sizeof(exp->endpoint)); - remote = &peer->p_remote; - ep = &peer->p_endpoint; - if (ep->e_remote.r_sa.sa_family != 0) { - exp->endpoint_sz = (ep->e_remote.r_sa.sa_family == AF_INET) ? - sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - - memcpy(&exp->endpoint, &ep->e_remote, exp->endpoint_sz); - } - - /* We always export it. */ - (void)noise_remote_keys(remote, exp->public_key, exp->preshared_key); - exp->persistent_keepalive = - peer->p_timers.t_persistent_keepalive_interval; - wg_timers_get_last_handshake(&peer->p_timers, &exp->last_handshake); - exp->rx_bytes = counter_u64_fetch(peer->p_rx_bytes); - exp->tx_bytes = counter_u64_fetch(peer->p_tx_bytes); - - exp->aip_count = 0; - CK_LIST_FOREACH(rt, &peer->p_aips, r_entry) { - exp->aip_count++; - } - - /* Early success; no allowed-ips to copy out. */ - if (exp->aip_count == 0) - return (0); - - exp->aip = malloc(exp->aip_count * sizeof(*exp->aip), M_TEMP, M_NOWAIT); - if (exp->aip == NULL) - return (ENOMEM); - - i = 0; - CK_LIST_FOREACH(rt, &peer->p_aips, r_entry) { - exp->aip[i].family = rt->r_addr.ss_family; - if (exp->aip[i].family == AF_INET) { - struct sockaddr_in *sin = - (struct sockaddr_in *)&rt->r_addr; - - exp->aip[i].ip4 = sin->sin_addr; - - sin = (struct sockaddr_in *)&rt->r_mask; - exp->aip[i].cidr = in_mask2len(&sin->sin_addr); - } else if (exp->aip[i].family == AF_INET6) { - struct sockaddr_in6 *sin6 = - (struct sockaddr_in6 *)&rt->r_addr; - - exp->aip[i].ip6 = sin6->sin6_addr; - - sin6 = (struct sockaddr_in6 *)&rt->r_mask; - exp->aip[i].cidr = in6_mask2len(&sin6->sin6_addr, NULL); - } - i++; - if (i == exp->aip_count) - break; - } - - /* Again, AllowedIPs might have shrank; update it. */ - exp->aip_count = i; - - return (0); -} - -static nvlist_t * -wg_peer_export_to_nvl(struct wg_softc *sc, struct wg_peer_export *exp) -{ - struct wg_timespec64 ts64; - nvlist_t *nvl, **nvl_aips; - size_t i; - uint16_t family; - - nvl_aips = NULL; - if ((nvl = nvlist_create(0)) == NULL) - return (NULL); - - nvlist_add_binary(nvl, "public-key", exp->public_key, - sizeof(exp->public_key)); - if (wgc_privileged(sc)) - nvlist_add_binary(nvl, "preshared-key", exp->preshared_key, - sizeof(exp->preshared_key)); - if (exp->endpoint_sz != 0) - nvlist_add_binary(nvl, "endpoint", &exp->endpoint, - exp->endpoint_sz); - - if (exp->aip_count != 0) { - nvl_aips = mallocarray(exp->aip_count, sizeof(*nvl_aips), - M_WG, M_WAITOK | M_ZERO); - } - - for (i = 0; i < exp->aip_count; i++) { - nvl_aips[i] = nvlist_create(0); - if (nvl_aips[i] == NULL) - goto err; - family = exp->aip[i].family; - nvlist_add_number(nvl_aips[i], "cidr", exp->aip[i].cidr); - if (family == AF_INET) - nvlist_add_binary(nvl_aips[i], "ipv4", - &exp->aip[i].ip4, sizeof(exp->aip[i].ip4)); - else if (family == AF_INET6) - nvlist_add_binary(nvl_aips[i], "ipv6", - &exp->aip[i].ip6, sizeof(exp->aip[i].ip6)); - } - - if (i != 0) { - nvlist_add_nvlist_array(nvl, "allowed-ips", - (const nvlist_t *const *)nvl_aips, i); - } - - for (i = 0; i < exp->aip_count; ++i) - nvlist_destroy(nvl_aips[i]); - - free(nvl_aips, M_WG); - nvl_aips = NULL; - - ts64.tv_sec = exp->last_handshake.tv_sec; - ts64.tv_nsec = exp->last_handshake.tv_nsec; - nvlist_add_binary(nvl, "last-handshake-time", &ts64, sizeof(ts64)); - - if (exp->persistent_keepalive != 0) - nvlist_add_number(nvl, "persistent-keepalive-interval", - exp->persistent_keepalive); - - if (exp->rx_bytes != 0) - nvlist_add_number(nvl, "rx-bytes", exp->rx_bytes); - if (exp->tx_bytes != 0) - nvlist_add_number(nvl, "tx-bytes", exp->tx_bytes); - - return (nvl); -err: - for (i = 0; i < exp->aip_count && nvl_aips[i] != NULL; i++) { - nvlist_destroy(nvl_aips[i]); - } - - free(nvl_aips, M_WG); - nvlist_destroy(nvl); - return (NULL); -} - -static int -wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, int *peer_countp) -{ - struct wg_peer *peer; - int err, i, peer_count; - nvlist_t *nvl, **nvl_array; - struct epoch_tracker et; - struct wg_peer_export *wpe; - - nvl = NULL; - nvl_array = NULL; - if (nvl_arrayp) - *nvl_arrayp = NULL; - if (nvlp) - *nvlp = NULL; - if (peer_countp) - *peer_countp = 0; - peer_count = sc->sc_hashtable.h_num_peers; - if (peer_count == 0) { - return (ENOENT); - } - - if (nvlp && (nvl = nvlist_create(0)) == NULL) - return (ENOMEM); - - err = i = 0; - nvl_array = malloc(peer_count*sizeof(void*), M_TEMP, M_WAITOK | M_ZERO); - wpe = malloc(peer_count*sizeof(*wpe), M_TEMP, M_WAITOK | M_ZERO); - - NET_EPOCH_ENTER(et); - CK_LIST_FOREACH(peer, &sc->sc_hashtable.h_peers_list, p_entry) { - if ((err = wg_peer_to_export(peer, &wpe[i])) != 0) { - break; - } - - i++; - if (i == peer_count) - break; - } - NET_EPOCH_EXIT(et); - - if (err != 0) - goto out; - - /* Update the peer count, in case we found fewer entries. */ - *peer_countp = peer_count = i; - if (peer_count == 0) { - err = ENOENT; - goto out; - } - - for (i = 0; i < peer_count; i++) { - int idx; - - /* - * Peers are added to the list in reverse order, effectively, - * because it's simpler/quicker to add at the head every time. - * - * Export them in reverse order. No worries if we fail mid-way - * through, the cleanup below will DTRT. - */ - idx = peer_count - i - 1; - nvl_array[idx] = wg_peer_export_to_nvl(sc, &wpe[i]); - if (nvl_array[idx] == NULL) { - break; - } - } - - if (i < peer_count) { - /* Error! */ - *peer_countp = 0; - err = ENOMEM; - } else if (nvl) { - nvlist_add_nvlist_array(nvl, "peers", - (const nvlist_t * const *)nvl_array, peer_count); - if ((err = nvlist_error(nvl))) { - goto out; - } - *nvlp = nvl; - } - *nvl_arrayp = nvl_array; - out: - if (err != 0) { - /* Note that nvl_array is populated in reverse order. */ - for (i = 0; i < peer_count; i++) { - nvlist_destroy(nvl_array[i]); - } - - free(nvl_array, M_TEMP); - if (nvl != NULL) - nvlist_destroy(nvl); - } - - for (i = 0; i < peer_count; i++) - free(wpe[i].aip, M_TEMP); - free(wpe, M_TEMP); - return (err); -} - -static int -wgc_get(struct wg_softc *sc, struct wg_data_io *wgd) -{ - nvlist_t *nvl, **nvl_array; - void *packed; - size_t size; - int peer_count, err; - - nvl = nvlist_create(0); - if (nvl == NULL) - return (ENOMEM); - - sx_slock(&sc->sc_lock); - - err = 0; - packed = NULL; - if (sc->sc_socket.so_port != 0) - nvlist_add_number(nvl, "listen-port", sc->sc_socket.so_port); - if (sc->sc_socket.so_user_cookie != 0) - nvlist_add_number(nvl, "user-cookie", sc->sc_socket.so_user_cookie); - if (sc->sc_local.l_has_identity) { - nvlist_add_binary(nvl, "public-key", sc->sc_local.l_public, WG_KEY_SIZE); - if (wgc_privileged(sc)) - nvlist_add_binary(nvl, "private-key", sc->sc_local.l_private, WG_KEY_SIZE); - } - if (sc->sc_hashtable.h_num_peers > 0) { - err = wg_marshal_peers(sc, NULL, &nvl_array, &peer_count); - if (err) - goto out_nvl; - nvlist_add_nvlist_array(nvl, "peers", - (const nvlist_t * const *)nvl_array, peer_count); - } - packed = nvlist_pack(nvl, &size); - if (packed == NULL) { - err = ENOMEM; - goto out_nvl; - } - if (wgd->wgd_size == 0) { - wgd->wgd_size = size; - goto out_packed; - } - if (wgd->wgd_size < size) { - err = ENOSPC; - goto out_packed; - } - if (wgd->wgd_data == NULL) { - err = EFAULT; - goto out_packed; - } - err = copyout(packed, wgd->wgd_data, size); - wgd->wgd_size = size; - -out_packed: - free(packed, M_NVLIST); -out_nvl: - nvlist_destroy(nvl); - sx_sunlock(&sc->sc_lock); - return (err); -} - -static int -wg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct wg_data_io *wgd = (struct wg_data_io *)data; - struct ifreq *ifr = (struct ifreq *)data; - struct wg_softc *sc = ifp->if_softc; - int ret = 0; - - switch (cmd) { - case SIOCSWG: - ret = priv_check(curthread, PRIV_NET_WG); - if (ret == 0) - ret = wgc_set(sc, wgd); - break; - case SIOCGWG: - ret = wgc_get(sc, wgd); - break; - /* Interface IOCTLs */ - case SIOCSIFADDR: - /* - * This differs from *BSD norms, but is more uniform with how - * WireGuard behaves elsewhere. - */ - break; - case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) != 0) - ret = wg_up(sc); - else - wg_down(sc); - break; - case SIOCSIFMTU: - if (ifr->ifr_mtu <= 0 || ifr->ifr_mtu > MAX_MTU) - ret = EINVAL; - else - ifp->if_mtu = ifr->ifr_mtu; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - break; - default: - ret = ENOTTY; - } - - return ret; -} - -static int -wg_up(struct wg_softc *sc) -{ - struct wg_hashtable *ht = &sc->sc_hashtable; - struct ifnet *ifp = sc->sc_ifp; - struct wg_peer *peer; - int rc = EBUSY; - - sx_xlock(&sc->sc_lock); - /* Jail's being removed, no more wg_up(). */ - if ((sc->sc_flags & WGF_DYING) != 0) - goto out; - - /* Silent success if we're already running. */ - rc = 0; - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - goto out; - ifp->if_drv_flags |= IFF_DRV_RUNNING; - - rc = wg_socket_init(sc, sc->sc_socket.so_port); - if (rc == 0) { - mtx_lock(&ht->h_mtx); - CK_LIST_FOREACH(peer, &ht->h_peers_list, p_entry) { - wg_timers_enable(&peer->p_timers); - wg_queue_out(peer); - } - mtx_unlock(&ht->h_mtx); - - if_link_state_change(sc->sc_ifp, LINK_STATE_UP); - } else { - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - } -out: - sx_xunlock(&sc->sc_lock); - return (rc); -} - -static void -wg_down(struct wg_softc *sc) -{ - struct wg_hashtable *ht = &sc->sc_hashtable; - struct ifnet *ifp = sc->sc_ifp; - struct wg_peer *peer; - - sx_xlock(&sc->sc_lock); - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - sx_xunlock(&sc->sc_lock); - return; - } - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - - mtx_lock(&ht->h_mtx); - CK_LIST_FOREACH(peer, &ht->h_peers_list, p_entry) { - wg_queue_purge(&peer->p_stage_queue); - wg_timers_disable(&peer->p_timers); - } - mtx_unlock(&ht->h_mtx); - - mbufq_drain(&sc->sc_handshake_queue); - - mtx_lock(&ht->h_mtx); - CK_LIST_FOREACH(peer, &ht->h_peers_list, p_entry) { - noise_remote_clear(&peer->p_remote); - wg_timers_event_reset_handshake_last_sent(&peer->p_timers); - } - mtx_unlock(&ht->h_mtx); - - if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN); - wg_socket_uninit(sc); - - sx_xunlock(&sc->sc_lock); -} - -static void -crypto_taskq_setup(struct wg_softc *sc) -{ - - sc->sc_encrypt = malloc(sizeof(struct grouptask)*mp_ncpus, M_WG, M_WAITOK); - sc->sc_decrypt = malloc(sizeof(struct grouptask)*mp_ncpus, M_WG, M_WAITOK); - - for (int i = 0; i < mp_ncpus; i++) { - GROUPTASK_INIT(&sc->sc_encrypt[i], 0, - (gtask_fn_t *)wg_softc_encrypt, sc); - taskqgroup_attach_cpu(qgroup_if_io_tqg, &sc->sc_encrypt[i], sc, i, NULL, NULL, "wg encrypt"); - GROUPTASK_INIT(&sc->sc_decrypt[i], 0, - (gtask_fn_t *)wg_softc_decrypt, sc); - taskqgroup_attach_cpu(qgroup_if_io_tqg, &sc->sc_decrypt[i], sc, i, NULL, NULL, "wg decrypt"); - } -} - -static void -crypto_taskq_destroy(struct wg_softc *sc) -{ - for (int i = 0; i < mp_ncpus; i++) { - taskqgroup_detach(qgroup_if_io_tqg, &sc->sc_encrypt[i]); - taskqgroup_detach(qgroup_if_io_tqg, &sc->sc_decrypt[i]); - } - free(sc->sc_encrypt, M_WG); - free(sc->sc_decrypt, M_WG); -} - -static int -wg_clone_create(struct if_clone *ifc, int unit, caddr_t params) -{ - struct wg_softc *sc; - struct ifnet *ifp; - struct noise_upcall noise_upcall; - - sc = malloc(sizeof(*sc), M_WG, M_WAITOK | M_ZERO); - sc->sc_ucred = crhold(curthread->td_ucred); - ifp = sc->sc_ifp = if_alloc(IFT_WIREGUARD); - ifp->if_softc = sc; - if_initname(ifp, wgname, unit); - - noise_upcall.u_arg = sc; - noise_upcall.u_remote_get = - (struct noise_remote *(*)(void *, uint8_t *))wg_remote_get; - noise_upcall.u_index_set = - (uint32_t (*)(void *, struct noise_remote *))wg_index_set; - noise_upcall.u_index_drop = - (void (*)(void *, uint32_t))wg_index_drop; - noise_local_init(&sc->sc_local, &noise_upcall); - cookie_checker_init(&sc->sc_cookie, ratelimit_zone); - - sc->sc_socket.so_port = 0; - - atomic_add_int(&clone_count, 1); - ifp->if_capabilities = ifp->if_capenable = WG_CAPS; - - mbufq_init(&sc->sc_handshake_queue, MAX_QUEUED_HANDSHAKES); - sx_init(&sc->sc_lock, "wg softc lock"); - rw_init(&sc->sc_index_lock, "wg index lock"); - sc->sc_peer_count = 0; - sc->sc_encap_ring = buf_ring_alloc(MAX_QUEUED_PKT, M_WG, M_WAITOK, NULL); - sc->sc_decap_ring = buf_ring_alloc(MAX_QUEUED_PKT, M_WG, M_WAITOK, NULL); - GROUPTASK_INIT(&sc->sc_handshake, 0, - (gtask_fn_t *)wg_softc_handshake_receive, sc); - taskqgroup_attach(qgroup_if_io_tqg, &sc->sc_handshake, sc, NULL, NULL, "wg tx initiation"); - crypto_taskq_setup(sc); - - wg_hashtable_init(&sc->sc_hashtable); - sc->sc_index = hashinit(HASHTABLE_INDEX_SIZE, M_DEVBUF, &sc->sc_index_mask); - wg_aip_init(&sc->sc_aips); - - if_setmtu(ifp, ETHERMTU - 80); - ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_NOARP; - ifp->if_init = wg_init; - ifp->if_reassign = wg_reassign; - ifp->if_qflush = wg_qflush; - ifp->if_transmit = wg_transmit; - ifp->if_output = wg_output; - ifp->if_ioctl = wg_ioctl; - - if_attach(ifp); - bpfattach(ifp, DLT_NULL, sizeof(uint32_t)); - - sx_xlock(&wg_sx); - LIST_INSERT_HEAD(&wg_list, sc, sc_entry); - sx_xunlock(&wg_sx); - - return 0; -} - -static void -wg_clone_destroy(struct ifnet *ifp) -{ - struct wg_softc *sc = ifp->if_softc; - struct ucred *cred; - - sx_xlock(&wg_sx); - sx_xlock(&sc->sc_lock); - sc->sc_flags |= WGF_DYING; - cred = sc->sc_ucred; - sc->sc_ucred = NULL; - sx_xunlock(&sc->sc_lock); - LIST_REMOVE(sc, sc_entry); - sx_xunlock(&wg_sx); - - if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN); - - sx_xlock(&sc->sc_lock); - wg_socket_uninit(sc); - sx_xunlock(&sc->sc_lock); - - /* - * No guarantees that all traffic have passed until the epoch has - * elapsed with the socket closed. - */ - NET_EPOCH_WAIT(); - - taskqgroup_drain_all(qgroup_if_io_tqg); - sx_xlock(&sc->sc_lock); - wg_peer_remove_all(sc); - epoch_drain_callbacks(net_epoch_preempt); - sx_xunlock(&sc->sc_lock); - sx_destroy(&sc->sc_lock); - rw_destroy(&sc->sc_index_lock); - taskqgroup_detach(qgroup_if_io_tqg, &sc->sc_handshake); - crypto_taskq_destroy(sc); - buf_ring_free(sc->sc_encap_ring, M_WG); - buf_ring_free(sc->sc_decap_ring, M_WG); - - wg_aip_destroy(&sc->sc_aips); - wg_hashtable_destroy(&sc->sc_hashtable); - - if (cred != NULL) - crfree(cred); - if_detach(sc->sc_ifp); - if_free(sc->sc_ifp); - /* Ensure any local/private keys are cleaned up */ - explicit_bzero(sc, sizeof(*sc)); - free(sc, M_WG); - - atomic_add_int(&clone_count, -1); -} - -static void -wg_qflush(struct ifnet *ifp __unused) -{ -} - -/* - * Privileged information (private-key, preshared-key) are only exported for - * root and jailed root by default. - */ -static bool -wgc_privileged(struct wg_softc *sc) -{ - struct thread *td; - - td = curthread; - return (priv_check(td, PRIV_NET_WG) == 0); -} - -static void -wg_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused, - char *unused __unused) -{ - struct wg_softc *sc; - - sc = ifp->if_softc; - wg_down(sc); -} - -static void -wg_init(void *xsc) -{ - struct wg_softc *sc; - - sc = xsc; - wg_up(sc); -} - -static void -vnet_wg_init(const void *unused __unused) -{ - - V_wg_cloner = if_clone_simple(wgname, wg_clone_create, wg_clone_destroy, - 0); -} -VNET_SYSINIT(vnet_wg_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, - vnet_wg_init, NULL); - -static void -vnet_wg_uninit(const void *unused __unused) -{ - - if_clone_detach(V_wg_cloner); -} -VNET_SYSUNINIT(vnet_wg_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, - vnet_wg_uninit, NULL); - -static int -wg_prison_remove(void *obj, void *data __unused) -{ - const struct prison *pr = obj; - struct wg_softc *sc; - struct ucred *cred; - bool dying; - - /* - * Do a pass through all if_wg interfaces and release creds on any from - * the jail that are supposed to be going away. This will, in turn, let - * the jail die so that we don't end up with Schrödinger's jail. - */ - sx_slock(&wg_sx); - LIST_FOREACH(sc, &wg_list, sc_entry) { - cred = NULL; - - sx_xlock(&sc->sc_lock); - dying = (sc->sc_flags & WGF_DYING) != 0; - if (!dying && sc->sc_ucred != NULL && - sc->sc_ucred->cr_prison == pr) { - /* Home jail is going away. */ - cred = sc->sc_ucred; - sc->sc_ucred = NULL; - - sc->sc_flags |= WGF_DYING; - } - - /* - * If this is our foreign vnet going away, we'll also down the - * link and kill the socket because traffic needs to stop. Any - * address will be revoked in the rehoming process. - */ - if (cred != NULL || (!dying && - sc->sc_ifp->if_vnet == pr->pr_vnet)) { - if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN); - /* Have to kill the sockets, as they also hold refs. */ - wg_socket_uninit(sc); - } - - sx_xunlock(&sc->sc_lock); - - if (cred != NULL) { - CURVNET_SET(sc->sc_ifp->if_vnet); - if_purgeaddrs(sc->sc_ifp); - CURVNET_RESTORE(); - crfree(cred); - } - } - sx_sunlock(&wg_sx); - - return (0); -} - -static void -wg_module_init(void) -{ - osd_method_t methods[PR_MAXMETHOD] = { - [PR_METHOD_REMOVE] = wg_prison_remove, - }; - - ratelimit_zone = uma_zcreate("wg ratelimit", sizeof(struct ratelimit), - NULL, NULL, NULL, NULL, 0, 0); - wg_osd_jail_slot = osd_jail_register(NULL, methods); -} - -static void -wg_module_deinit(void) -{ - - uma_zdestroy(ratelimit_zone); - osd_jail_deregister(wg_osd_jail_slot); - - MPASS(LIST_EMPTY(&wg_list)); -} - -static int -wg_module_event_handler(module_t mod, int what, void *arg) -{ - - switch (what) { - case MOD_LOAD: - wg_module_init(); - break; - case MOD_UNLOAD: - if (atomic_load_int(&clone_count) == 0) - wg_module_deinit(); - else - return (EBUSY); - break; - default: - return (EOPNOTSUPP); - } - return (0); -} - -static moduledata_t wg_moduledata = { - "wg", - wg_module_event_handler, - NULL -}; - -DECLARE_MODULE(wg, wg_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); -MODULE_VERSION(wg, 1); -MODULE_DEPEND(wg, crypto, 1, 1, 1); diff --git a/sys/dev/if_wg/if_wg.h b/sys/dev/if_wg/if_wg.h deleted file mode 100644 index 2a100456d406..000000000000 --- a/sys/dev/if_wg/if_wg.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2019 Matt Dunwoodie - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * $FreeBSD$ - */ - -#ifndef __IF_WG_H__ -#define __IF_WG_H__ - -#include -#include - -struct wg_data_io { - char wgd_name[IFNAMSIZ]; - void *wgd_data; - size_t wgd_size; -}; - -#define WG_KEY_SIZE 32 - -#define SIOCSWG _IOWR('i', 210, struct wg_data_io) -#define SIOCGWG _IOWR('i', 211, struct wg_data_io) - -#endif /* __IF_WG_H__ */ diff --git a/sys/dev/if_wg/support.h b/sys/dev/if_wg/support.h deleted file mode 100644 index 412806b465af..000000000000 --- a/sys/dev/if_wg/support.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2021 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2021 Matt Dunwoodie - */ - -#ifndef _WG_SUPPORT -#define _WG_SUPPORT - -#include -#include -#include -#include -#include -#include -#include -#include - -/* TODO the following is openbsd compat defines to allow us to copy the wg_* - * files from openbsd (almost) verbatim. this will greatly increase maintenance - * across the platforms. it should be moved to it's own file. the only thing - * we're missing from this is struct pool (freebsd: uma_zone_t), which isn't a - * show stopper, but is something worth considering in the future. - * - md */ - -#define rw_assert_wrlock(x) rw_assert(x, RA_WLOCKED) -#define rw_enter_write rw_wlock -#define rw_exit_write rw_wunlock -#define rw_enter_read rw_rlock -#define rw_exit_read rw_runlock -#define rw_exit rw_unlock - -#define RW_DOWNGRADE 1 -#define rw_enter(x, y) do { \ - CTASSERT(y == RW_DOWNGRADE); \ - rw_downgrade(x); \ -} while (0) - -MALLOC_DECLARE(M_WG); - -#include -typedef struct { - uint64_t k0; - uint64_t k1; -} SIPHASH_KEY; - -static inline uint64_t -siphash24(const SIPHASH_KEY *key, const void *src, size_t len) -{ - SIPHASH_CTX ctx; - - return (SipHashX(&ctx, 2, 4, (const uint8_t *)key, src, len)); -} -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -#endif diff --git a/sys/dev/if_wg/wg_cookie.c b/sys/dev/if_wg/wg_cookie.c deleted file mode 100644 index c56b2fb2e75e..000000000000 --- a/sys/dev/if_wg/wg_cookie.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2019-2020 Matt Dunwoodie - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include /* Because systm doesn't include M_NOWAIT, M_DEVBUF */ -#include - -#include "support.h" -#include "wg_cookie.h" - -static void cookie_precompute_key(uint8_t *, - const uint8_t[COOKIE_INPUT_SIZE], const char *); -static void cookie_macs_mac1(struct cookie_macs *, const void *, size_t, - const uint8_t[COOKIE_KEY_SIZE]); -static void cookie_macs_mac2(struct cookie_macs *, const void *, size_t, - const uint8_t[COOKIE_COOKIE_SIZE]); -static int cookie_timer_expired(struct timespec *, time_t, long); -static void cookie_checker_make_cookie(struct cookie_checker *, - uint8_t[COOKIE_COOKIE_SIZE], struct sockaddr *); -static int ratelimit_init(struct ratelimit *, uma_zone_t); -static void ratelimit_deinit(struct ratelimit *); -static void ratelimit_gc(struct ratelimit *, int); -static int ratelimit_allow(struct ratelimit *, struct sockaddr *); - -/* Public Functions */ -void -cookie_maker_init(struct cookie_maker *cp, const uint8_t key[COOKIE_INPUT_SIZE]) -{ - bzero(cp, sizeof(*cp)); - cookie_precompute_key(cp->cp_mac1_key, key, COOKIE_MAC1_KEY_LABEL); - cookie_precompute_key(cp->cp_cookie_key, key, COOKIE_COOKIE_KEY_LABEL); - rw_init(&cp->cp_lock, "cookie_maker"); -} - -int -cookie_checker_init(struct cookie_checker *cc, uma_zone_t zone) -{ - int res; - bzero(cc, sizeof(*cc)); - - rw_init(&cc->cc_key_lock, "cookie_checker_key"); - rw_init(&cc->cc_secret_lock, "cookie_checker_secret"); - - if ((res = ratelimit_init(&cc->cc_ratelimit_v4, zone)) != 0) - return res; -#ifdef INET6 - if ((res = ratelimit_init(&cc->cc_ratelimit_v6, zone)) != 0) { - ratelimit_deinit(&cc->cc_ratelimit_v4); - return res; - } -#endif - return 0; -} - -void -cookie_checker_update(struct cookie_checker *cc, - const uint8_t key[COOKIE_INPUT_SIZE]) -{ - rw_enter_write(&cc->cc_key_lock); - if (key) { - cookie_precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL); - cookie_precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL); - } else { - bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key)); - bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key)); - } - rw_exit_write(&cc->cc_key_lock); -} - -void -cookie_checker_deinit(struct cookie_checker *cc) -{ - ratelimit_deinit(&cc->cc_ratelimit_v4); -#ifdef INET6 - ratelimit_deinit(&cc->cc_ratelimit_v6); -#endif -} - -void -cookie_checker_create_payload(struct cookie_checker *cc, - struct cookie_macs *cm, uint8_t nonce[COOKIE_NONCE_SIZE], - uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa) -{ - uint8_t cookie[COOKIE_COOKIE_SIZE]; - - cookie_checker_make_cookie(cc, cookie, sa); - arc4random_buf(nonce, COOKIE_NONCE_SIZE); - - rw_enter_read(&cc->cc_key_lock); - xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE, - cm->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key); - rw_exit_read(&cc->cc_key_lock); - - explicit_bzero(cookie, sizeof(cookie)); -} - -int -cookie_maker_consume_payload(struct cookie_maker *cp, - uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE]) -{ - int ret = 0; - uint8_t cookie[COOKIE_COOKIE_SIZE]; - - rw_enter_write(&cp->cp_lock); - - if (cp->cp_mac1_valid == 0) { - ret = ETIMEDOUT; - goto error; - } - - if (xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE, - cp->cp_mac1_last, COOKIE_MAC_SIZE, nonce, cp->cp_cookie_key) == 0) { - ret = EINVAL; - goto error; - } - - memcpy(cp->cp_cookie, cookie, COOKIE_COOKIE_SIZE); - getnanouptime(&cp->cp_birthdate); - cp->cp_mac1_valid = 0; - -error: - rw_exit_write(&cp->cp_lock); - return ret; -} - -void -cookie_maker_mac(struct cookie_maker *cp, struct cookie_macs *cm, void *buf, - size_t len) -{ - rw_enter_read(&cp->cp_lock); - - cookie_macs_mac1(cm, buf, len, cp->cp_mac1_key); - - memcpy(cp->cp_mac1_last, cm->mac1, COOKIE_MAC_SIZE); - cp->cp_mac1_valid = 1; - - if (!cookie_timer_expired(&cp->cp_birthdate, - COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) - cookie_macs_mac2(cm, buf, len, cp->cp_cookie); - else - bzero(cm->mac2, COOKIE_MAC_SIZE); - - rw_exit_read(&cp->cp_lock); -} - -int -cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *cm, - void *buf, size_t len, int busy, struct sockaddr *sa) -{ - struct cookie_macs our_cm; - uint8_t cookie[COOKIE_COOKIE_SIZE]; - - /* Validate incoming MACs */ - rw_enter_read(&cc->cc_key_lock); - cookie_macs_mac1(&our_cm, buf, len, cc->cc_mac1_key); - rw_exit_read(&cc->cc_key_lock); - - /* If mac1 is invald, we want to drop the packet */ - if (timingsafe_bcmp(our_cm.mac1, cm->mac1, COOKIE_MAC_SIZE) != 0) - return EINVAL; - - if (busy != 0) { - cookie_checker_make_cookie(cc, cookie, sa); - cookie_macs_mac2(&our_cm, buf, len, cookie); - - /* If the mac2 is invalid, we want to send a cookie response */ - if (timingsafe_bcmp(our_cm.mac2, cm->mac2, COOKIE_MAC_SIZE) != 0) - return EAGAIN; - - /* If the mac2 is valid, we may want rate limit the peer. - * ratelimit_allow will return either 0 or ECONNREFUSED, - * implying there is no ratelimiting, or we should ratelimit - * (refuse) respectively. */ - if (sa->sa_family == AF_INET) - return ratelimit_allow(&cc->cc_ratelimit_v4, sa); -#ifdef INET6 - else if (sa->sa_family == AF_INET6) - return ratelimit_allow(&cc->cc_ratelimit_v6, sa); -#endif - else - return EAFNOSUPPORT; - } - return 0; -} - -/* Private functions */ -static void -cookie_precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE], - const char *label) -{ - struct blake2s_state blake; - - blake2s_init(&blake, COOKIE_KEY_SIZE); - blake2s_update(&blake, label, strlen(label)); - blake2s_update(&blake, input, COOKIE_INPUT_SIZE); - /* TODO we shouldn't need to provide outlen to _final. we can align - * this with openbsd after fixing the blake library. */ - blake2s_final(&blake, key); -} - -static void -cookie_macs_mac1(struct cookie_macs *cm, const void *buf, size_t len, - const uint8_t key[COOKIE_KEY_SIZE]) -{ - struct blake2s_state state; - blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE); - blake2s_update(&state, buf, len); - blake2s_final(&state, cm->mac1); -} - -static void -cookie_macs_mac2(struct cookie_macs *cm, const void *buf, size_t len, - const uint8_t key[COOKIE_COOKIE_SIZE]) -{ - struct blake2s_state state; - blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE); - blake2s_update(&state, buf, len); - blake2s_update(&state, cm->mac1, COOKIE_MAC_SIZE); - blake2s_final(&state, cm->mac2); -} - -static int -cookie_timer_expired(struct timespec *birthdate, time_t sec, long nsec) -{ - struct timespec uptime; - struct timespec expire = { .tv_sec = sec, .tv_nsec = nsec }; - - if (birthdate->tv_sec == 0 && birthdate->tv_nsec == 0) - return ETIMEDOUT; - - getnanouptime(&uptime); - timespecadd(birthdate, &expire, &expire); - return timespeccmp(&uptime, &expire, >) ? ETIMEDOUT : 0; -} - -static void -cookie_checker_make_cookie(struct cookie_checker *cc, - uint8_t cookie[COOKIE_COOKIE_SIZE], struct sockaddr *sa) -{ - struct blake2s_state state; - - rw_enter_write(&cc->cc_secret_lock); - if (cookie_timer_expired(&cc->cc_secret_birthdate, - COOKIE_SECRET_MAX_AGE, 0)) { - arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE); - getnanouptime(&cc->cc_secret_birthdate); - } - blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret, - COOKIE_SECRET_SIZE); - rw_exit_write(&cc->cc_secret_lock); - - if (sa->sa_family == AF_INET) { - blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr, - sizeof(struct in_addr)); - blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port, - sizeof(in_port_t)); - blake2s_final(&state, cookie); -#ifdef INET6 - } else if (sa->sa_family == AF_INET6) { - blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr, - sizeof(struct in6_addr)); - blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port, - sizeof(in_port_t)); - blake2s_final(&state, cookie); -#endif - } else { - arc4random_buf(cookie, COOKIE_COOKIE_SIZE); - } -} - -static int -ratelimit_init(struct ratelimit *rl, uma_zone_t zone) -{ - rw_init(&rl->rl_lock, "ratelimit_lock"); - arc4random_buf(&rl->rl_secret, sizeof(rl->rl_secret)); - rl->rl_table = hashinit_flags(RATELIMIT_SIZE, M_DEVBUF, - &rl->rl_table_mask, M_NOWAIT); - rl->rl_zone = zone; - rl->rl_table_num = 0; - return rl->rl_table == NULL ? ENOBUFS : 0; -} - -static void -ratelimit_deinit(struct ratelimit *rl) -{ - rw_enter_write(&rl->rl_lock); - ratelimit_gc(rl, 1); - hashdestroy(rl->rl_table, M_DEVBUF, rl->rl_table_mask); - rw_exit_write(&rl->rl_lock); -} - -static void -ratelimit_gc(struct ratelimit *rl, int force) -{ - size_t i; - struct ratelimit_entry *r, *tr; - struct timespec expiry; - - rw_assert_wrlock(&rl->rl_lock); - - if (force) { - for (i = 0; i < RATELIMIT_SIZE; i++) { - LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) { - rl->rl_table_num--; - LIST_REMOVE(r, r_entry); - uma_zfree(rl->rl_zone, r); - } - } - return; - } - - if ((cookie_timer_expired(&rl->rl_last_gc, ELEMENT_TIMEOUT, 0) && - rl->rl_table_num > 0)) { - getnanouptime(&rl->rl_last_gc); - getnanouptime(&expiry); - expiry.tv_sec -= ELEMENT_TIMEOUT; - - for (i = 0; i < RATELIMIT_SIZE; i++) { - LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) { - if (timespeccmp(&r->r_last_time, &expiry, <)) { - rl->rl_table_num--; - LIST_REMOVE(r, r_entry); - uma_zfree(rl->rl_zone, r); - } - } - } - } -} - -static int -ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa) -{ - uint64_t key, tokens; - struct timespec diff; - struct ratelimit_entry *r; - int ret = ECONNREFUSED; - - if (sa->sa_family == AF_INET) - /* TODO siphash24 is the FreeBSD siphash, OK? */ - key = siphash24(&rl->rl_secret, &satosin(sa)->sin_addr, - IPV4_MASK_SIZE); -#ifdef INET6 - else if (sa->sa_family == AF_INET6) - key = siphash24(&rl->rl_secret, &satosin6(sa)->sin6_addr, - IPV6_MASK_SIZE); -#endif - else - return ret; - - rw_enter_write(&rl->rl_lock); - - LIST_FOREACH(r, &rl->rl_table[key & rl->rl_table_mask], r_entry) { - if (r->r_af != sa->sa_family) - continue; - - if (r->r_af == AF_INET && bcmp(&r->r_in, - &satosin(sa)->sin_addr, IPV4_MASK_SIZE) != 0) - continue; - -#ifdef INET6 - if (r->r_af == AF_INET6 && bcmp(&r->r_in6, - &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE) != 0) - continue; -#endif - - /* If we get to here, we've found an entry for the endpoint. - * We apply standard token bucket, by calculating the time - * lapsed since our last_time, adding that, ensuring that we - * cap the tokens at TOKEN_MAX. If the endpoint has no tokens - * left (that is tokens <= INITIATION_COST) then we block the - * request, otherwise we subtract the INITITIATION_COST and - * return OK. */ - diff = r->r_last_time; - getnanouptime(&r->r_last_time); - timespecsub(&r->r_last_time, &diff, &diff); - - tokens = r->r_tokens + diff.tv_sec * NSEC_PER_SEC + diff.tv_nsec; - - if (tokens > TOKEN_MAX) - tokens = TOKEN_MAX; - - if (tokens >= INITIATION_COST) { - r->r_tokens = tokens - INITIATION_COST; - goto ok; - } else { - r->r_tokens = tokens; - goto error; - } - } - - /* If we get to here, we didn't have an entry for the endpoint. */ - ratelimit_gc(rl, 0); - - /* Hard limit on number of entries */ - if (rl->rl_table_num >= RATELIMIT_SIZE_MAX) - goto error; - - /* Goto error if out of memory */ - if ((r = uma_zalloc(rl->rl_zone, M_NOWAIT)) == NULL) - goto error; - - rl->rl_table_num++; - - /* Insert entry into the hashtable and ensure it's initialised */ - LIST_INSERT_HEAD(&rl->rl_table[key & rl->rl_table_mask], r, r_entry); - r->r_af = sa->sa_family; - if (r->r_af == AF_INET) - memcpy(&r->r_in, &satosin(sa)->sin_addr, IPV4_MASK_SIZE); -#ifdef INET6 - else if (r->r_af == AF_INET6) - memcpy(&r->r_in6, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE); -#endif - - getnanouptime(&r->r_last_time); - r->r_tokens = TOKEN_MAX - INITIATION_COST; -ok: - ret = 0; -error: - rw_exit_write(&rl->rl_lock); - return ret; -} diff --git a/sys/dev/if_wg/wg_cookie.h b/sys/dev/if_wg/wg_cookie.h deleted file mode 100644 index 699f3ebf40c1..000000000000 --- a/sys/dev/if_wg/wg_cookie.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2019-2020 Matt Dunwoodie - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef __COOKIE_H__ -#define __COOKIE_H__ - -#include -#include -#include -#include - -#include - -#include "crypto.h" - -#define COOKIE_MAC_SIZE 16 -#define COOKIE_KEY_SIZE 32 -#define COOKIE_NONCE_SIZE XCHACHA20POLY1305_NONCE_SIZE -#define COOKIE_COOKIE_SIZE 16 -#define COOKIE_SECRET_SIZE 32 -#define COOKIE_INPUT_SIZE 32 -#define COOKIE_ENCRYPTED_SIZE (COOKIE_COOKIE_SIZE + COOKIE_MAC_SIZE) - -#define COOKIE_MAC1_KEY_LABEL "mac1----" -#define COOKIE_COOKIE_KEY_LABEL "cookie--" -#define COOKIE_SECRET_MAX_AGE 120 -#define COOKIE_SECRET_LATENCY 5 - -/* Constants for initiation rate limiting */ -#define RATELIMIT_SIZE (1 << 13) -#define RATELIMIT_SIZE_MAX (RATELIMIT_SIZE * 8) -#define NSEC_PER_SEC 1000000000LL -#define INITIATIONS_PER_SECOND 20 -#define INITIATIONS_BURSTABLE 5 -#define INITIATION_COST (NSEC_PER_SEC / INITIATIONS_PER_SECOND) -#define TOKEN_MAX (INITIATION_COST * INITIATIONS_BURSTABLE) -#define ELEMENT_TIMEOUT 1 -#define IPV4_MASK_SIZE 4 /* Use all 4 bytes of IPv4 address */ -#define IPV6_MASK_SIZE 8 /* Use top 8 bytes (/64) of IPv6 address */ - -struct cookie_macs { - uint8_t mac1[COOKIE_MAC_SIZE]; - uint8_t mac2[COOKIE_MAC_SIZE]; -}; - -struct ratelimit_entry { - LIST_ENTRY(ratelimit_entry) r_entry; - sa_family_t r_af; - union { - struct in_addr r_in; -#ifdef INET6 - struct in6_addr r_in6; -#endif - }; - struct timespec r_last_time; /* nanouptime */ - uint64_t r_tokens; -}; - -struct ratelimit { - SIPHASH_KEY rl_secret; - uma_zone_t rl_zone; - - struct rwlock rl_lock; - LIST_HEAD(, ratelimit_entry) *rl_table; - u_long rl_table_mask; - size_t rl_table_num; - struct timespec rl_last_gc; /* nanouptime */ -}; - -struct cookie_maker { - uint8_t cp_mac1_key[COOKIE_KEY_SIZE]; - uint8_t cp_cookie_key[COOKIE_KEY_SIZE]; - - struct rwlock cp_lock; - uint8_t cp_cookie[COOKIE_COOKIE_SIZE]; - struct timespec cp_birthdate; /* nanouptime */ - int cp_mac1_valid; - uint8_t cp_mac1_last[COOKIE_MAC_SIZE]; -}; - -struct cookie_checker { - struct ratelimit cc_ratelimit_v4; -#ifdef INET6 - struct ratelimit cc_ratelimit_v6; -#endif - - struct rwlock cc_key_lock; - uint8_t cc_mac1_key[COOKIE_KEY_SIZE]; - uint8_t cc_cookie_key[COOKIE_KEY_SIZE]; - - struct rwlock cc_secret_lock; - struct timespec cc_secret_birthdate; /* nanouptime */ - uint8_t cc_secret[COOKIE_SECRET_SIZE]; -}; - -void cookie_maker_init(struct cookie_maker *, const uint8_t[COOKIE_INPUT_SIZE]); -int cookie_checker_init(struct cookie_checker *, uma_zone_t); -void cookie_checker_update(struct cookie_checker *, - const uint8_t[COOKIE_INPUT_SIZE]); -void cookie_checker_deinit(struct cookie_checker *); -void cookie_checker_create_payload(struct cookie_checker *, - struct cookie_macs *cm, uint8_t[COOKIE_NONCE_SIZE], - uint8_t [COOKIE_ENCRYPTED_SIZE], struct sockaddr *); -int cookie_maker_consume_payload(struct cookie_maker *, - uint8_t[COOKIE_NONCE_SIZE], uint8_t[COOKIE_ENCRYPTED_SIZE]); -void cookie_maker_mac(struct cookie_maker *, struct cookie_macs *, - void *, size_t); -int cookie_checker_validate_macs(struct cookie_checker *, - struct cookie_macs *, void *, size_t, int, struct sockaddr *); - -#endif /* __COOKIE_H__ */ diff --git a/sys/dev/if_wg/wg_noise.c b/sys/dev/if_wg/wg_noise.c deleted file mode 100644 index ae527eb99bde..000000000000 --- a/sys/dev/if_wg/wg_noise.c +++ /dev/null @@ -1,963 +0,0 @@ -/* - * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2019-2020 Matt Dunwoodie - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include - -#include "support.h" -#include "wg_noise.h" - -/* Private functions */ -static struct noise_keypair * - noise_remote_keypair_allocate(struct noise_remote *); -static void - noise_remote_keypair_free(struct noise_remote *, - struct noise_keypair *); -static uint32_t noise_remote_handshake_index_get(struct noise_remote *); -static void noise_remote_handshake_index_drop(struct noise_remote *); - -static uint64_t noise_counter_send(struct noise_counter *); -static int noise_counter_recv(struct noise_counter *, uint64_t); - -static void noise_kdf(uint8_t *, uint8_t *, uint8_t *, const uint8_t *, - size_t, size_t, size_t, size_t, - const uint8_t [NOISE_HASH_LEN]); -static int noise_mix_dh( - uint8_t [NOISE_HASH_LEN], - uint8_t [NOISE_SYMMETRIC_KEY_LEN], - const uint8_t [NOISE_PUBLIC_KEY_LEN], - const uint8_t [NOISE_PUBLIC_KEY_LEN]); -static int noise_mix_ss( - uint8_t ck[NOISE_HASH_LEN], - uint8_t key[NOISE_SYMMETRIC_KEY_LEN], - const uint8_t ss[NOISE_PUBLIC_KEY_LEN]); -static void noise_mix_hash( - uint8_t [NOISE_HASH_LEN], - const uint8_t *, - size_t); -static void noise_mix_psk( - uint8_t [NOISE_HASH_LEN], - uint8_t [NOISE_HASH_LEN], - uint8_t [NOISE_SYMMETRIC_KEY_LEN], - const uint8_t [NOISE_SYMMETRIC_KEY_LEN]); -static void noise_param_init( - uint8_t [NOISE_HASH_LEN], - uint8_t [NOISE_HASH_LEN], - const uint8_t [NOISE_PUBLIC_KEY_LEN]); - -static void noise_msg_encrypt(uint8_t *, const uint8_t *, size_t, - uint8_t [NOISE_SYMMETRIC_KEY_LEN], - uint8_t [NOISE_HASH_LEN]); -static int noise_msg_decrypt(uint8_t *, const uint8_t *, size_t, - uint8_t [NOISE_SYMMETRIC_KEY_LEN], - uint8_t [NOISE_HASH_LEN]); -static void noise_msg_ephemeral( - uint8_t [NOISE_HASH_LEN], - uint8_t [NOISE_HASH_LEN], - const uint8_t src[NOISE_PUBLIC_KEY_LEN]); - -static void noise_tai64n_now(uint8_t [NOISE_TIMESTAMP_LEN]); -static int noise_timer_expired(struct timespec *, time_t, long); - -/* Set/Get noise parameters */ -void -noise_local_init(struct noise_local *l, struct noise_upcall *upcall) -{ - bzero(l, sizeof(*l)); - rw_init(&l->l_identity_lock, "noise_local_identity"); - l->l_upcall = *upcall; -} - -void -noise_local_lock_identity(struct noise_local *l) -{ - rw_enter_write(&l->l_identity_lock); -} - -void -noise_local_unlock_identity(struct noise_local *l) -{ - rw_exit_write(&l->l_identity_lock); -} - -int -noise_local_set_private(struct noise_local *l, - const uint8_t private[NOISE_PUBLIC_KEY_LEN]) -{ - rw_assert_wrlock(&l->l_identity_lock); - - memcpy(l->l_private, private, NOISE_PUBLIC_KEY_LEN); - curve25519_clamp_secret(l->l_private); - l->l_has_identity = curve25519_generate_public(l->l_public, private); - - return l->l_has_identity ? 0 : ENXIO; -} - -int -noise_local_keys(struct noise_local *l, uint8_t public[NOISE_PUBLIC_KEY_LEN], - uint8_t private[NOISE_PUBLIC_KEY_LEN]) -{ - int ret = 0; - rw_enter_read(&l->l_identity_lock); - if (l->l_has_identity) { - if (public != NULL) - memcpy(public, l->l_public, NOISE_PUBLIC_KEY_LEN); - if (private != NULL) - memcpy(private, l->l_private, NOISE_PUBLIC_KEY_LEN); - } else { - ret = ENXIO; - } - rw_exit_read(&l->l_identity_lock); - return ret; -} - -void -noise_remote_init(struct noise_remote *r, - const uint8_t public[NOISE_PUBLIC_KEY_LEN], struct noise_local *l) -{ - bzero(r, sizeof(*r)); - memcpy(r->r_public, public, NOISE_PUBLIC_KEY_LEN); - rw_init(&r->r_handshake_lock, "noise_handshake"); - rw_init(&r->r_keypair_lock, "noise_keypair"); - - SLIST_INSERT_HEAD(&r->r_unused_keypairs, &r->r_keypair[0], kp_entry); - SLIST_INSERT_HEAD(&r->r_unused_keypairs, &r->r_keypair[1], kp_entry); - SLIST_INSERT_HEAD(&r->r_unused_keypairs, &r->r_keypair[2], kp_entry); - - KASSERT(l != NULL, ("must provide local")); - r->r_local = l; - - rw_enter_write(&l->l_identity_lock); - noise_remote_precompute(r); - rw_exit_write(&l->l_identity_lock); -} - -int -noise_remote_set_psk(struct noise_remote *r, - const uint8_t psk[NOISE_SYMMETRIC_KEY_LEN]) -{ - int same; - rw_enter_write(&r->r_handshake_lock); - same = !timingsafe_bcmp(r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN); - if (!same) { - memcpy(r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN); - } - rw_exit_write(&r->r_handshake_lock); - return same ? EEXIST : 0; -} - -int -noise_remote_keys(struct noise_remote *r, uint8_t public[NOISE_PUBLIC_KEY_LEN], - uint8_t psk[NOISE_SYMMETRIC_KEY_LEN]) -{ - static uint8_t null_psk[NOISE_SYMMETRIC_KEY_LEN]; - int ret; - - if (public != NULL) - memcpy(public, r->r_public, NOISE_PUBLIC_KEY_LEN); - - rw_enter_read(&r->r_handshake_lock); - if (psk != NULL) - memcpy(psk, r->r_psk, NOISE_SYMMETRIC_KEY_LEN); - ret = timingsafe_bcmp(r->r_psk, null_psk, NOISE_SYMMETRIC_KEY_LEN); - rw_exit_read(&r->r_handshake_lock); - - /* If r_psk != null_psk return 0, else ENOENT (no psk) */ - return ret ? 0 : ENOENT; -} - -void -noise_remote_precompute(struct noise_remote *r) -{ - struct noise_local *l = r->r_local; - rw_assert_wrlock(&l->l_identity_lock); - if (!l->l_has_identity) - bzero(r->r_ss, NOISE_PUBLIC_KEY_LEN); - else if (!curve25519(r->r_ss, l->l_private, r->r_public)) - bzero(r->r_ss, NOISE_PUBLIC_KEY_LEN); - - rw_enter_write(&r->r_handshake_lock); - noise_remote_handshake_index_drop(r); - explicit_bzero(&r->r_handshake, sizeof(r->r_handshake)); - rw_exit_write(&r->r_handshake_lock); -} - -/* Handshake functions */ -int -noise_create_initiation(struct noise_remote *r, uint32_t *s_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], - uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN], - uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]) -{ - struct noise_handshake *hs = &r->r_handshake; - struct noise_local *l = r->r_local; - uint8_t key[NOISE_SYMMETRIC_KEY_LEN]; - int ret = EINVAL; - - rw_enter_read(&l->l_identity_lock); - rw_enter_write(&r->r_handshake_lock); - if (!l->l_has_identity) - goto error; - noise_param_init(hs->hs_ck, hs->hs_hash, r->r_public); - - /* e */ - curve25519_generate_secret(hs->hs_e); - if (curve25519_generate_public(ue, hs->hs_e) == 0) - goto error; - noise_msg_ephemeral(hs->hs_ck, hs->hs_hash, ue); - - /* es */ - if (noise_mix_dh(hs->hs_ck, key, hs->hs_e, r->r_public) != 0) - goto error; - - /* s */ - noise_msg_encrypt(es, l->l_public, - NOISE_PUBLIC_KEY_LEN, key, hs->hs_hash); - - /* ss */ - if (noise_mix_ss(hs->hs_ck, key, r->r_ss) != 0) - goto error; - - /* {t} */ - noise_tai64n_now(ets); - noise_msg_encrypt(ets, ets, - NOISE_TIMESTAMP_LEN, key, hs->hs_hash); - - noise_remote_handshake_index_drop(r); - hs->hs_state = CREATED_INITIATION; - hs->hs_local_index = noise_remote_handshake_index_get(r); - *s_idx = hs->hs_local_index; - ret = 0; -error: - rw_exit_write(&r->r_handshake_lock); - rw_exit_read(&l->l_identity_lock); - explicit_bzero(key, NOISE_SYMMETRIC_KEY_LEN); - return ret; -} - -int -noise_consume_initiation(struct noise_local *l, struct noise_remote **rp, - uint32_t s_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], - uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN], - uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]) -{ - struct noise_remote *r; - struct noise_handshake hs; - uint8_t key[NOISE_SYMMETRIC_KEY_LEN]; - uint8_t r_public[NOISE_PUBLIC_KEY_LEN]; - uint8_t timestamp[NOISE_TIMESTAMP_LEN]; - int ret = EINVAL; - - rw_enter_read(&l->l_identity_lock); - if (!l->l_has_identity) - goto error; - noise_param_init(hs.hs_ck, hs.hs_hash, l->l_public); - - /* e */ - noise_msg_ephemeral(hs.hs_ck, hs.hs_hash, ue); - - /* es */ - if (noise_mix_dh(hs.hs_ck, key, l->l_private, ue) != 0) - goto error; - - /* s */ - if (noise_msg_decrypt(r_public, es, - NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN, key, hs.hs_hash) != 0) - goto error; - - /* Lookup the remote we received from */ - if ((r = l->l_upcall.u_remote_get(l->l_upcall.u_arg, r_public)) == NULL) - goto error; - - /* ss */ - if (noise_mix_ss(hs.hs_ck, key, r->r_ss) != 0) - goto error; - - /* {t} */ - if (noise_msg_decrypt(timestamp, ets, - NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN, key, hs.hs_hash) != 0) - goto error; - - hs.hs_state = CONSUMED_INITIATION; - hs.hs_local_index = 0; - hs.hs_remote_index = s_idx; - memcpy(hs.hs_e, ue, NOISE_PUBLIC_KEY_LEN); - - /* We have successfully computed the same results, now we ensure that - * this is not an initiation replay, or a flood attack */ - rw_enter_write(&r->r_handshake_lock); - - /* Replay */ - if (memcmp(timestamp, r->r_timestamp, NOISE_TIMESTAMP_LEN) > 0) - memcpy(r->r_timestamp, timestamp, NOISE_TIMESTAMP_LEN); - else - goto error_set; - /* Flood attack */ - if (noise_timer_expired(&r->r_last_init, 0, REJECT_INTERVAL)) - getnanouptime(&r->r_last_init); - else - goto error_set; - - /* Ok, we're happy to accept this initiation now */ - noise_remote_handshake_index_drop(r); - r->r_handshake = hs; - *rp = r; - ret = 0; -error_set: - rw_exit_write(&r->r_handshake_lock); -error: - rw_exit_read(&l->l_identity_lock); - explicit_bzero(key, NOISE_SYMMETRIC_KEY_LEN); - explicit_bzero(&hs, sizeof(hs)); - return ret; -} - -int -noise_create_response(struct noise_remote *r, uint32_t *s_idx, uint32_t *r_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], uint8_t en[0 + NOISE_AUTHTAG_LEN]) -{ - struct noise_handshake *hs = &r->r_handshake; - uint8_t key[NOISE_SYMMETRIC_KEY_LEN]; - uint8_t e[NOISE_PUBLIC_KEY_LEN]; - int ret = EINVAL; - - rw_enter_read(&r->r_local->l_identity_lock); - rw_enter_write(&r->r_handshake_lock); - - if (hs->hs_state != CONSUMED_INITIATION) - goto error; - - /* e */ - curve25519_generate_secret(e); - if (curve25519_generate_public(ue, e) == 0) - goto error; - noise_msg_ephemeral(hs->hs_ck, hs->hs_hash, ue); - - /* ee */ - if (noise_mix_dh(hs->hs_ck, NULL, e, hs->hs_e) != 0) - goto error; - - /* se */ - if (noise_mix_dh(hs->hs_ck, NULL, e, r->r_public) != 0) - goto error; - - /* psk */ - noise_mix_psk(hs->hs_ck, hs->hs_hash, key, r->r_psk); - - /* {} */ - noise_msg_encrypt(en, NULL, 0, key, hs->hs_hash); - - hs->hs_state = CREATED_RESPONSE; - hs->hs_local_index = noise_remote_handshake_index_get(r); - *r_idx = hs->hs_remote_index; - *s_idx = hs->hs_local_index; - ret = 0; -error: - rw_exit_write(&r->r_handshake_lock); - rw_exit_read(&r->r_local->l_identity_lock); - explicit_bzero(key, NOISE_SYMMETRIC_KEY_LEN); - explicit_bzero(e, NOISE_PUBLIC_KEY_LEN); - return ret; -} - -int -noise_consume_response(struct noise_remote *r, uint32_t s_idx, uint32_t r_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], uint8_t en[0 + NOISE_AUTHTAG_LEN]) -{ - struct noise_local *l = r->r_local; - struct noise_handshake hs; - uint8_t key[NOISE_SYMMETRIC_KEY_LEN]; - uint8_t preshared_key[NOISE_PUBLIC_KEY_LEN]; - int ret = EINVAL; - - rw_enter_read(&l->l_identity_lock); - if (!l->l_has_identity) - goto error; - - rw_enter_read(&r->r_handshake_lock); - hs = r->r_handshake; - memcpy(preshared_key, r->r_psk, NOISE_SYMMETRIC_KEY_LEN); - rw_exit_read(&r->r_handshake_lock); - - if (hs.hs_state != CREATED_INITIATION || - hs.hs_local_index != r_idx) - goto error; - - /* e */ - noise_msg_ephemeral(hs.hs_ck, hs.hs_hash, ue); - - /* ee */ - if (noise_mix_dh(hs.hs_ck, NULL, hs.hs_e, ue) != 0) - goto error; - - /* se */ - if (noise_mix_dh(hs.hs_ck, NULL, l->l_private, ue) != 0) - goto error; - - /* psk */ - noise_mix_psk(hs.hs_ck, hs.hs_hash, key, preshared_key); - - /* {} */ - if (noise_msg_decrypt(NULL, en, - 0 + NOISE_AUTHTAG_LEN, key, hs.hs_hash) != 0) - goto error; - - hs.hs_remote_index = s_idx; - - rw_enter_write(&r->r_handshake_lock); - if (r->r_handshake.hs_state == hs.hs_state && - r->r_handshake.hs_local_index == hs.hs_local_index) { - r->r_handshake = hs; - r->r_handshake.hs_state = CONSUMED_RESPONSE; - ret = 0; - } - rw_exit_write(&r->r_handshake_lock); -error: - rw_exit_read(&l->l_identity_lock); - explicit_bzero(&hs, sizeof(hs)); - explicit_bzero(key, NOISE_SYMMETRIC_KEY_LEN); - return ret; -} - -int -noise_remote_begin_session(struct noise_remote *r) -{ - struct noise_handshake *hs = &r->r_handshake; - struct noise_keypair kp, *next, *current, *previous; - - rw_enter_write(&r->r_handshake_lock); - - /* We now derive the keypair from the handshake */ - if (hs->hs_state == CONSUMED_RESPONSE) { - kp.kp_is_initiator = 1; - noise_kdf(kp.kp_send, kp.kp_recv, NULL, NULL, - NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0, - hs->hs_ck); - } else if (hs->hs_state == CREATED_RESPONSE) { - kp.kp_is_initiator = 0; - noise_kdf(kp.kp_recv, kp.kp_send, NULL, NULL, - NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0, - hs->hs_ck); - } else { - rw_exit_write(&r->r_handshake_lock); - return EINVAL; - } - - kp.kp_valid = 1; - kp.kp_local_index = hs->hs_local_index; - kp.kp_remote_index = hs->hs_remote_index; - getnanouptime(&kp.kp_birthdate); - bzero(&kp.kp_ctr, sizeof(kp.kp_ctr)); - rw_init(&kp.kp_ctr.c_lock, "noise_counter"); - - /* Now we need to add_new_keypair */ - rw_enter_write(&r->r_keypair_lock); - next = r->r_next; - current = r->r_current; - previous = r->r_previous; - - if (kp.kp_is_initiator) { - if (next != NULL) { - r->r_next = NULL; - r->r_previous = next; - noise_remote_keypair_free(r, current); - } else { - r->r_previous = current; - } - - noise_remote_keypair_free(r, previous); - - r->r_current = noise_remote_keypair_allocate(r); - *r->r_current = kp; - } else { - noise_remote_keypair_free(r, next); - r->r_previous = NULL; - noise_remote_keypair_free(r, previous); - - r->r_next = noise_remote_keypair_allocate(r); - *r->r_next = kp; - } - rw_exit_write(&r->r_keypair_lock); - - explicit_bzero(&r->r_handshake, sizeof(r->r_handshake)); - rw_exit_write(&r->r_handshake_lock); - - explicit_bzero(&kp, sizeof(kp)); - return 0; -} - -void -noise_remote_clear(struct noise_remote *r) -{ - rw_enter_write(&r->r_handshake_lock); - noise_remote_handshake_index_drop(r); - explicit_bzero(&r->r_handshake, sizeof(r->r_handshake)); - rw_exit_write(&r->r_handshake_lock); - - rw_enter_write(&r->r_keypair_lock); - noise_remote_keypair_free(r, r->r_next); - noise_remote_keypair_free(r, r->r_current); - noise_remote_keypair_free(r, r->r_previous); - r->r_next = NULL; - r->r_current = NULL; - r->r_previous = NULL; - rw_exit_write(&r->r_keypair_lock); -} - -void -noise_remote_expire_current(struct noise_remote *r) -{ - rw_enter_write(&r->r_keypair_lock); - if (r->r_next != NULL) - r->r_next->kp_valid = 0; - if (r->r_current != NULL) - r->r_current->kp_valid = 0; - rw_exit_write(&r->r_keypair_lock); -} - -int -noise_remote_ready(struct noise_remote *r) -{ - struct noise_keypair *kp; - int ret; - - rw_enter_read(&r->r_keypair_lock); - /* kp_ctr isn't locked here, we're happy to accept a racy read. */ - if ((kp = r->r_current) == NULL || - !kp->kp_valid || - noise_timer_expired(&kp->kp_birthdate, REJECT_AFTER_TIME, 0) || - kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES || - kp->kp_ctr.c_send >= REJECT_AFTER_MESSAGES) - ret = EINVAL; - else - ret = 0; - rw_exit_read(&r->r_keypair_lock); - return ret; -} - -int -noise_remote_encrypt(struct noise_remote *r, uint32_t *r_idx, uint64_t *nonce, - uint8_t *buf, size_t buflen) -{ - struct noise_keypair *kp; - int ret = EINVAL; - - rw_enter_read(&r->r_keypair_lock); - if ((kp = r->r_current) == NULL) - goto error; - - /* We confirm that our values are within our tolerances. We want: - * - a valid keypair - * - our keypair to be less than REJECT_AFTER_TIME seconds old - * - our receive counter to be less than REJECT_AFTER_MESSAGES - * - our send counter to be less than REJECT_AFTER_MESSAGES - * - * kp_ctr isn't locked here, we're happy to accept a racy read. */ - if (!kp->kp_valid || - noise_timer_expired(&kp->kp_birthdate, REJECT_AFTER_TIME, 0) || - kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES || - ((*nonce = noise_counter_send(&kp->kp_ctr)) > REJECT_AFTER_MESSAGES)) - goto error; - - /* We encrypt into the same buffer, so the caller must ensure that buf - * has NOISE_AUTHTAG_LEN bytes to store the MAC. The nonce and index - * are passed back out to the caller through the provided data pointer. */ - *r_idx = kp->kp_remote_index; - chacha20poly1305_encrypt(buf, buf, buflen, - NULL, 0, *nonce, kp->kp_send); - - /* If our values are still within tolerances, but we are approaching - * the tolerances, we notify the caller with ESTALE that they should - * establish a new keypair. The current keypair can continue to be used - * until the tolerances are hit. We notify if: - * - our send counter is valid and not less than REKEY_AFTER_MESSAGES - * - we're the initiator and our keypair is older than - * REKEY_AFTER_TIME seconds */ - ret = ESTALE; - if ((kp->kp_valid && *nonce >= REKEY_AFTER_MESSAGES) || - (kp->kp_is_initiator && - noise_timer_expired(&kp->kp_birthdate, REKEY_AFTER_TIME, 0))) - goto error; - - ret = 0; -error: - rw_exit_read(&r->r_keypair_lock); - return ret; -} - -int -noise_remote_decrypt(struct noise_remote *r, uint32_t r_idx, uint64_t nonce, - uint8_t *buf, size_t buflen) -{ - struct noise_keypair *kp; - int ret = EINVAL; - - /* We retrieve the keypair corresponding to the provided index. We - * attempt the current keypair first as that is most likely. We also - * want to make sure that the keypair is valid as it would be - * catastrophic to decrypt against a zero'ed keypair. */ - rw_enter_read(&r->r_keypair_lock); - - if (r->r_current != NULL && r->r_current->kp_local_index == r_idx) { - kp = r->r_current; - } else if (r->r_previous != NULL && r->r_previous->kp_local_index == r_idx) { - kp = r->r_previous; - } else if (r->r_next != NULL && r->r_next->kp_local_index == r_idx) { - kp = r->r_next; - } else { - goto error; - } - - /* We confirm that our values are within our tolerances. These values - * are the same as the encrypt routine. - * - * kp_ctr isn't locked here, we're happy to accept a racy read. */ - if (noise_timer_expired(&kp->kp_birthdate, REJECT_AFTER_TIME, 0) || - kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES) - goto error; - - /* Decrypt, then validate the counter. We don't want to validate the - * counter before decrypting as we do not know the message is authentic - * prior to decryption. */ - if (chacha20poly1305_decrypt(buf, buf, buflen, - NULL, 0, nonce, kp->kp_recv) == 0) - goto error; - - if (noise_counter_recv(&kp->kp_ctr, nonce) != 0) - goto error; - - /* If we've received the handshake confirming data packet then move the - * next keypair into current. If we do slide the next keypair in, then - * we skip the REKEY_AFTER_TIME_RECV check. This is safe to do as a - * data packet can't confirm a session that we are an INITIATOR of. */ - if (kp == r->r_next) { - rw_exit_read(&r->r_keypair_lock); - rw_enter_write(&r->r_keypair_lock); - if (kp == r->r_next && kp->kp_local_index == r_idx) { - noise_remote_keypair_free(r, r->r_previous); - r->r_previous = r->r_current; - r->r_current = r->r_next; - r->r_next = NULL; - - ret = ECONNRESET; - goto error; - } - rw_enter(&r->r_keypair_lock, RW_DOWNGRADE); - } - - /* Similar to when we encrypt, we want to notify the caller when we - * are approaching our tolerances. We notify if: - * - we're the initiator and the current keypair is older than - * REKEY_AFTER_TIME_RECV seconds. */ - ret = ESTALE; - kp = r->r_current; - if (kp != NULL && - kp->kp_valid && - kp->kp_is_initiator && - noise_timer_expired(&kp->kp_birthdate, REKEY_AFTER_TIME_RECV, 0)) - goto error; - - ret = 0; - -error: - rw_exit(&r->r_keypair_lock); - return ret; -} - -/* Private functions - these should not be called outside this file under any - * circumstances. */ -static struct noise_keypair * -noise_remote_keypair_allocate(struct noise_remote *r) -{ - struct noise_keypair *kp; - kp = SLIST_FIRST(&r->r_unused_keypairs); - SLIST_REMOVE_HEAD(&r->r_unused_keypairs, kp_entry); - return kp; -} - -static void -noise_remote_keypair_free(struct noise_remote *r, struct noise_keypair *kp) -{ - struct noise_upcall *u = &r->r_local->l_upcall; - if (kp != NULL) { - SLIST_INSERT_HEAD(&r->r_unused_keypairs, kp, kp_entry); - u->u_index_drop(u->u_arg, kp->kp_local_index); - bzero(kp->kp_send, sizeof(kp->kp_send)); - bzero(kp->kp_recv, sizeof(kp->kp_recv)); - } -} - -static uint32_t -noise_remote_handshake_index_get(struct noise_remote *r) -{ - struct noise_upcall *u = &r->r_local->l_upcall; - return u->u_index_set(u->u_arg, r); -} - -static void -noise_remote_handshake_index_drop(struct noise_remote *r) -{ - struct noise_handshake *hs = &r->r_handshake; - struct noise_upcall *u = &r->r_local->l_upcall; - rw_assert_wrlock(&r->r_handshake_lock); - if (hs->hs_state != HS_ZEROED) - u->u_index_drop(u->u_arg, hs->hs_local_index); -} - -static uint64_t -noise_counter_send(struct noise_counter *ctr) -{ - uint64_t ret; - rw_enter_write(&ctr->c_lock); - ret = ctr->c_send++; - rw_exit_write(&ctr->c_lock); - return ret; -} - -static int -noise_counter_recv(struct noise_counter *ctr, uint64_t recv) -{ - uint64_t i, top, index_recv, index_ctr; - unsigned long bit; - int ret = EEXIST; - - rw_enter_write(&ctr->c_lock); - - /* Check that the recv counter is valid */ - if (ctr->c_recv >= REJECT_AFTER_MESSAGES || - recv >= REJECT_AFTER_MESSAGES) - goto error; - - /* If the packet is out of the window, invalid */ - if (recv + COUNTER_WINDOW_SIZE < ctr->c_recv) - goto error; - - /* If the new counter is ahead of the current counter, we'll need to - * zero out the bitmap that has previously been used */ - index_recv = recv / COUNTER_BITS; - index_ctr = ctr->c_recv / COUNTER_BITS; - - if (recv > ctr->c_recv) { - top = MIN(index_recv - index_ctr, COUNTER_NUM); - for (i = 1; i <= top; i++) - ctr->c_backtrack[ - (i + index_ctr) & (COUNTER_NUM - 1)] = 0; - ctr->c_recv = recv; - } - - index_recv %= COUNTER_NUM; - bit = 1ul << (recv % COUNTER_BITS); - - if (ctr->c_backtrack[index_recv] & bit) - goto error; - - ctr->c_backtrack[index_recv] |= bit; - - ret = 0; -error: - rw_exit_write(&ctr->c_lock); - return ret; -} - -static void -noise_kdf(uint8_t *a, uint8_t *b, uint8_t *c, const uint8_t *x, - size_t a_len, size_t b_len, size_t c_len, size_t x_len, - const uint8_t ck[NOISE_HASH_LEN]) -{ - uint8_t out[BLAKE2S_HASH_SIZE + 1]; - uint8_t sec[BLAKE2S_HASH_SIZE]; - -#ifdef DIAGNOSTIC - MPASS(a_len <= BLAKE2S_HASH_SIZE && b_len <= BLAKE2S_HASH_SIZE && - c_len <= BLAKE2S_HASH_SIZE); - MPASS(!(b || b_len || c || c_len) || (a && a_len)); - MPASS(!(c || c_len) || (b && b_len)); -#endif - - /* Extract entropy from "x" into sec */ - blake2s_hmac(sec, x, ck, BLAKE2S_HASH_SIZE, x_len, NOISE_HASH_LEN); - - if (a == NULL || a_len == 0) - goto out; - - /* Expand first key: key = sec, data = 0x1 */ - out[0] = 1; - blake2s_hmac(out, out, sec, BLAKE2S_HASH_SIZE, 1, BLAKE2S_HASH_SIZE); - memcpy(a, out, a_len); - - if (b == NULL || b_len == 0) - goto out; - - /* Expand second key: key = sec, data = "a" || 0x2 */ - out[BLAKE2S_HASH_SIZE] = 2; - blake2s_hmac(out, out, sec, BLAKE2S_HASH_SIZE, BLAKE2S_HASH_SIZE + 1, - BLAKE2S_HASH_SIZE); - memcpy(b, out, b_len); - - if (c == NULL || c_len == 0) - goto out; - - /* Expand third key: key = sec, data = "b" || 0x3 */ - out[BLAKE2S_HASH_SIZE] = 3; - blake2s_hmac(out, out, sec, BLAKE2S_HASH_SIZE, BLAKE2S_HASH_SIZE + 1, - BLAKE2S_HASH_SIZE); - memcpy(c, out, c_len); - -out: - /* Clear sensitive data from stack */ - explicit_bzero(sec, BLAKE2S_HASH_SIZE); - explicit_bzero(out, BLAKE2S_HASH_SIZE + 1); -} - -static int -noise_mix_dh(uint8_t ck[NOISE_HASH_LEN], uint8_t key[NOISE_SYMMETRIC_KEY_LEN], - const uint8_t private[NOISE_PUBLIC_KEY_LEN], - const uint8_t public[NOISE_PUBLIC_KEY_LEN]) -{ - uint8_t dh[NOISE_PUBLIC_KEY_LEN]; - - if (!curve25519(dh, private, public)) - return EINVAL; - noise_kdf(ck, key, NULL, dh, - NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, ck); - explicit_bzero(dh, NOISE_PUBLIC_KEY_LEN); - return 0; -} - -static int -noise_mix_ss(uint8_t ck[NOISE_HASH_LEN], uint8_t key[NOISE_SYMMETRIC_KEY_LEN], - const uint8_t ss[NOISE_PUBLIC_KEY_LEN]) -{ - static uint8_t null_point[NOISE_PUBLIC_KEY_LEN]; - if (timingsafe_bcmp(ss, null_point, NOISE_PUBLIC_KEY_LEN) == 0) - return ENOENT; - noise_kdf(ck, key, NULL, ss, - NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, ck); - return 0; -} - -static void -noise_mix_hash(uint8_t hash[NOISE_HASH_LEN], const uint8_t *src, - size_t src_len) -{ - struct blake2s_state blake; - - blake2s_init(&blake, NOISE_HASH_LEN); - blake2s_update(&blake, hash, NOISE_HASH_LEN); - blake2s_update(&blake, src, src_len); - blake2s_final(&blake, hash); -} - -static void -noise_mix_psk(uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN], - uint8_t key[NOISE_SYMMETRIC_KEY_LEN], - const uint8_t psk[NOISE_SYMMETRIC_KEY_LEN]) -{ - uint8_t tmp[NOISE_HASH_LEN]; - - noise_kdf(ck, tmp, key, psk, - NOISE_HASH_LEN, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, - NOISE_SYMMETRIC_KEY_LEN, ck); - noise_mix_hash(hash, tmp, NOISE_HASH_LEN); - explicit_bzero(tmp, NOISE_HASH_LEN); -} - -static void -noise_param_init(uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN], - const uint8_t s[NOISE_PUBLIC_KEY_LEN]) -{ - struct blake2s_state blake; - - blake2s(ck, (uint8_t *)NOISE_HANDSHAKE_NAME, NULL, - NOISE_HASH_LEN, strlen(NOISE_HANDSHAKE_NAME), 0); - blake2s_init(&blake, NOISE_HASH_LEN); - blake2s_update(&blake, ck, NOISE_HASH_LEN); - blake2s_update(&blake, (uint8_t *)NOISE_IDENTIFIER_NAME, - strlen(NOISE_IDENTIFIER_NAME)); - blake2s_final(&blake, hash); - - noise_mix_hash(hash, s, NOISE_PUBLIC_KEY_LEN); -} - -static void -noise_msg_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, - uint8_t key[NOISE_SYMMETRIC_KEY_LEN], uint8_t hash[NOISE_HASH_LEN]) -{ - /* Nonce always zero for Noise_IK */ - chacha20poly1305_encrypt(dst, src, src_len, - hash, NOISE_HASH_LEN, 0, key); - noise_mix_hash(hash, dst, src_len + NOISE_AUTHTAG_LEN); -} - -static int -noise_msg_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, - uint8_t key[NOISE_SYMMETRIC_KEY_LEN], uint8_t hash[NOISE_HASH_LEN]) -{ - /* Nonce always zero for Noise_IK */ - if (!chacha20poly1305_decrypt(dst, src, src_len, - hash, NOISE_HASH_LEN, 0, key)) - return EINVAL; - noise_mix_hash(hash, src, src_len); - return 0; -} - -static void -noise_msg_ephemeral(uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN], - const uint8_t src[NOISE_PUBLIC_KEY_LEN]) -{ - noise_mix_hash(hash, src, NOISE_PUBLIC_KEY_LEN); - noise_kdf(ck, NULL, NULL, src, NOISE_HASH_LEN, 0, 0, - NOISE_PUBLIC_KEY_LEN, ck); -} - -static void -noise_tai64n_now(uint8_t output[NOISE_TIMESTAMP_LEN]) -{ - struct timespec time; - uint64_t sec; - uint32_t nsec; - - getnanotime(&time); - - /* Round down the nsec counter to limit precise timing leak. */ - time.tv_nsec &= REJECT_INTERVAL_MASK; - - /* https://cr.yp.to/libtai/tai64.html */ - sec = htobe64(0x400000000000000aULL + time.tv_sec); - nsec = htobe32(time.tv_nsec); - - /* memcpy to output buffer, assuming output could be unaligned. */ - memcpy(output, &sec, sizeof(sec)); - memcpy(output + sizeof(sec), &nsec, sizeof(nsec)); -} - -static int -noise_timer_expired(struct timespec *birthdate, time_t sec, long nsec) -{ - struct timespec uptime; - struct timespec expire = { .tv_sec = sec, .tv_nsec = nsec }; - - /* We don't really worry about a zeroed birthdate, to avoid the extra - * check on every encrypt/decrypt. This does mean that r_last_init - * check may fail if getnanouptime is < REJECT_INTERVAL from 0. */ - - getnanouptime(&uptime); - timespecadd(birthdate, &expire, &expire); - return timespeccmp(&uptime, &expire, >) ? ETIMEDOUT : 0; -} diff --git a/sys/dev/if_wg/wg_noise.h b/sys/dev/if_wg/wg_noise.h deleted file mode 100644 index 95617ae9bfef..000000000000 --- a/sys/dev/if_wg/wg_noise.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2019-2020 Matt Dunwoodie - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef __NOISE_H__ -#define __NOISE_H__ - -#include -#include -#include - -#include "crypto.h" - -#define NOISE_PUBLIC_KEY_LEN CURVE25519_KEY_SIZE -#define NOISE_SYMMETRIC_KEY_LEN CHACHA20POLY1305_KEY_SIZE -#define NOISE_TIMESTAMP_LEN (sizeof(uint64_t) + sizeof(uint32_t)) -#define NOISE_AUTHTAG_LEN CHACHA20POLY1305_AUTHTAG_SIZE -#define NOISE_HASH_LEN BLAKE2S_HASH_SIZE - -/* Protocol string constants */ -#define NOISE_HANDSHAKE_NAME "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s" -#define NOISE_IDENTIFIER_NAME "WireGuard v1 zx2c4 Jason@zx2c4.com" - -/* Constants for the counter */ -#define COUNTER_BITS_TOTAL 8192 -#define COUNTER_BITS (sizeof(unsigned long) * 8) -#define COUNTER_NUM (COUNTER_BITS_TOTAL / COUNTER_BITS) -#define COUNTER_WINDOW_SIZE (COUNTER_BITS_TOTAL - COUNTER_BITS) - -/* Constants for the keypair */ -#define REKEY_AFTER_MESSAGES (1ull << 60) -#define REJECT_AFTER_MESSAGES (UINT64_MAX - COUNTER_WINDOW_SIZE - 1) -#define REKEY_AFTER_TIME 120 -#define REKEY_AFTER_TIME_RECV 165 -#define REJECT_AFTER_TIME 180 -#define REJECT_INTERVAL (1000000000 / 50) /* fifty times per sec */ -/* 24 = floor(log2(REJECT_INTERVAL)) */ -#define REJECT_INTERVAL_MASK (~((1ull<<24)-1)) - -enum noise_state_hs { - HS_ZEROED = 0, - CREATED_INITIATION, - CONSUMED_INITIATION, - CREATED_RESPONSE, - CONSUMED_RESPONSE, -}; - -struct noise_handshake { - enum noise_state_hs hs_state; - uint32_t hs_local_index; - uint32_t hs_remote_index; - uint8_t hs_e[NOISE_PUBLIC_KEY_LEN]; - uint8_t hs_hash[NOISE_HASH_LEN]; - uint8_t hs_ck[NOISE_HASH_LEN]; -}; - -struct noise_counter { - struct rwlock c_lock; - uint64_t c_send; - uint64_t c_recv; - unsigned long c_backtrack[COUNTER_NUM]; -}; - -struct noise_keypair { - SLIST_ENTRY(noise_keypair) kp_entry; - int kp_valid; - int kp_is_initiator; - uint32_t kp_local_index; - uint32_t kp_remote_index; - uint8_t kp_send[NOISE_SYMMETRIC_KEY_LEN]; - uint8_t kp_recv[NOISE_SYMMETRIC_KEY_LEN]; - struct timespec kp_birthdate; /* nanouptime */ - struct noise_counter kp_ctr; -}; - -struct noise_remote { - uint8_t r_public[NOISE_PUBLIC_KEY_LEN]; - struct noise_local *r_local; - uint8_t r_ss[NOISE_PUBLIC_KEY_LEN]; - - struct rwlock r_handshake_lock; - struct noise_handshake r_handshake; - uint8_t r_psk[NOISE_SYMMETRIC_KEY_LEN]; - uint8_t r_timestamp[NOISE_TIMESTAMP_LEN]; - struct timespec r_last_init; /* nanouptime */ - - struct rwlock r_keypair_lock; - SLIST_HEAD(,noise_keypair) r_unused_keypairs; - struct noise_keypair *r_next, *r_current, *r_previous; - struct noise_keypair r_keypair[3]; /* 3: next, current, previous. */ - -}; - -struct noise_local { - struct rwlock l_identity_lock; - int l_has_identity; - uint8_t l_public[NOISE_PUBLIC_KEY_LEN]; - uint8_t l_private[NOISE_PUBLIC_KEY_LEN]; - - struct noise_upcall { - void *u_arg; - struct noise_remote * - (*u_remote_get)(void *, uint8_t[NOISE_PUBLIC_KEY_LEN]); - uint32_t - (*u_index_set)(void *, struct noise_remote *); - void (*u_index_drop)(void *, uint32_t); - } l_upcall; -}; - -/* Set/Get noise parameters */ -void noise_local_init(struct noise_local *, struct noise_upcall *); -void noise_local_lock_identity(struct noise_local *); -void noise_local_unlock_identity(struct noise_local *); -int noise_local_set_private(struct noise_local *, - const uint8_t[NOISE_PUBLIC_KEY_LEN]); -int noise_local_keys(struct noise_local *, uint8_t[NOISE_PUBLIC_KEY_LEN], - uint8_t[NOISE_PUBLIC_KEY_LEN]); - -void noise_remote_init(struct noise_remote *, - const uint8_t[NOISE_PUBLIC_KEY_LEN], struct noise_local *); -int noise_remote_set_psk(struct noise_remote *, - const uint8_t[NOISE_SYMMETRIC_KEY_LEN]); -int noise_remote_keys(struct noise_remote *, uint8_t[NOISE_PUBLIC_KEY_LEN], - uint8_t[NOISE_SYMMETRIC_KEY_LEN]); - -/* Should be called anytime noise_local_set_private is called */ -void noise_remote_precompute(struct noise_remote *); - -/* Cryptographic functions */ -int noise_create_initiation( - struct noise_remote *, - uint32_t *s_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], - uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN], - uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]); - -int noise_consume_initiation( - struct noise_local *, - struct noise_remote **, - uint32_t s_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], - uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN], - uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]); - -int noise_create_response( - struct noise_remote *, - uint32_t *s_idx, - uint32_t *r_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], - uint8_t en[0 + NOISE_AUTHTAG_LEN]); - -int noise_consume_response( - struct noise_remote *, - uint32_t s_idx, - uint32_t r_idx, - uint8_t ue[NOISE_PUBLIC_KEY_LEN], - uint8_t en[0 + NOISE_AUTHTAG_LEN]); - -int noise_remote_begin_session(struct noise_remote *); -void noise_remote_clear(struct noise_remote *); -void noise_remote_expire_current(struct noise_remote *); - -int noise_remote_ready(struct noise_remote *); - -int noise_remote_encrypt( - struct noise_remote *, - uint32_t *r_idx, - uint64_t *nonce, - uint8_t *buf, - size_t buflen); -int noise_remote_decrypt( - struct noise_remote *, - uint32_t r_idx, - uint64_t nonce, - uint8_t *buf, - size_t buflen); - -#endif /* __NOISE_H__ */ diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 30499dce729c..b5c8f6ebf9be 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -3433,7 +3433,6 @@ prison_priv_check(struct ucred *cred, int priv) case PRIV_NET_GIF: case PRIV_NET_SETIFVNET: case PRIV_NET_SETIFFIB: - case PRIV_NET_WG: /* * 802.11-related privileges. diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index eb748928cd91..7f06b51cf096 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -850,17 +850,6 @@ sopeeloff(struct socket *head) } #endif /* SCTP */ -int -sogetsockaddr(struct socket *so, struct sockaddr **nam) -{ - int error; - - CURVNET_SET(so->so_vnet); - error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, nam); - CURVNET_RESTORE(); - return (error); -} - int sobind(struct socket *so, struct sockaddr *nam, struct thread *td) { diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index b9c2630561cb..23fae343924a 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -1386,7 +1386,9 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa, return (error); so = fp->f_data; *sa = NULL; - error = sogetsockaddr(so, sa); + CURVNET_SET(so->so_vnet); + error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, sa); + CURVNET_RESTORE(); if (error != 0) goto bad; if (*sa == NULL) diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 32919674901b..0195ce5064d2 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -163,7 +163,6 @@ SUBDIR= \ if_tuntap \ if_vlan \ if_vxlan \ - ${_if_wg} \ iflib \ ${_iir} \ imgact_binmisc \ diff --git a/sys/modules/if_wg/Makefile b/sys/modules/if_wg/Makefile deleted file mode 100644 index 851c55673738..000000000000 --- a/sys/modules/if_wg/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# $FreeBSD$ - - -KMOD= if_wg - -.PATH: ${SRCTOP}/sys/dev/if_wg - -SRCS= opt_inet.h opt_inet6.h device_if.h bus_if.h ifdi_if.h - -SRCS+= if_wg.c wg_noise.c wg_cookie.c crypto.c - -.include diff --git a/sys/net/if_types.h b/sys/net/if_types.h index 030f773d5654..1103d5f90928 100644 --- a/sys/net/if_types.h +++ b/sys/net/if_types.h @@ -256,7 +256,6 @@ typedef enum { IFT_ENC = 0xf4, /* Encapsulating interface */ IFT_PFLOG = 0xf6, /* PF packet filter logging */ IFT_PFSYNC = 0xf7, /* PF packet filter synchronization */ - IFT_WIREGUARD = 0xf8, /* WireGuard tunnel */ } ifType; /* diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 7937749c1299..62f0ac733a23 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -284,8 +284,8 @@ nd6_ifattach(struct ifnet *ifp) * default regardless of the V_ip6_auto_linklocal configuration to * give a reasonable default behavior. */ - if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE && - ifp->if_type != IFT_WIREGUARD) || (ifp->if_flags & IFF_LOOPBACK)) + if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) || + (ifp->if_flags & IFF_LOOPBACK)) nd->flags |= ND6_IFF_AUTO_LINKLOCAL; /* * A loopback interface does not need to accept RTADV. diff --git a/sys/sys/priv.h b/sys/sys/priv.h index 9d8a3204add5..7ef54782a60d 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -347,7 +347,6 @@ #define PRIV_NET_VXLAN 420 /* Administer vxlan. */ #define PRIV_NET_SETLANPCP 421 /* Set LAN priority. */ #define PRIV_NET_SETVLANPCP PRIV_NET_SETLANPCP /* Alias Set VLAN priority */ -#define PRIV_NET_WG 422 /* Administrate if_wg. */ /* * 802.11-related privileges. diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index a5bb8a2587ea..295a1cf3d37f 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -414,7 +414,6 @@ void soaio_enqueue(struct task *task); void soaio_rcv(void *context, int pending); void soaio_snd(void *context, int pending); int socheckuid(struct socket *so, uid_t uid); -int sogetsockaddr(struct socket *so, struct sockaddr **nam); int sobind(struct socket *so, struct sockaddr *nam, struct thread *td); int sobindat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td); diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile index 95cd044f7031..56a1cf877135 100644 --- a/tests/sys/netinet/Makefile +++ b/tests/sys/netinet/Makefile @@ -10,15 +10,7 @@ ATF_TESTS_C= ip_reass_test \ socket_afinet \ tcp_connect_port_test -ATF_TESTS_SH= carp fibs \ - fibs_test \ - redirect \ - divert \ - forward \ - output \ - lpm \ - arp \ - if_wg_test +ATF_TESTS_SH= carp fibs fibs_test redirect divert forward output lpm arp TEST_METADATA.output+= required_programs="python" PROGS= udp_dontroute tcp_user_cookie diff --git a/tests/sys/netinet/if_wg_test.sh b/tests/sys/netinet/if_wg_test.sh deleted file mode 100644 index b0ab70108cf4..000000000000 --- a/tests/sys/netinet/if_wg_test.sh +++ /dev/null @@ -1,188 +0,0 @@ -# $FreeBSD$ -# -# SPDX-License-Identifier: BSD-2-Clause-FreeBSD -# -# Copyright (c) 2021 The FreeBSD Foundation -# -# This software was developed by Mark Johnston under sponsorship -# from the FreeBSD Foundation. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. - -. $(atf_get_srcdir)/../common/vnet.subr - -atf_test_case "wg_basic" "cleanup" -wg_basic_head() -{ - atf_set descr 'Create a wg(4) tunnel over an epair and pass traffic between jails' - atf_set require.user root -} - -wg_basic_body() -{ - local epair pri1 pri2 pub1 pub2 wg1 wg2 - local endpoint1 endpoint2 tunnel1 tunnel2 - - kldload -n if_wg - - pri1=$(openssl rand -base64 32) - pri2=$(openssl rand -base64 32) - - endpoint1=192.168.2.1 - endpoint2=192.168.2.2 - tunnel1=169.254.0.1 - tunnel2=169.254.0.2 - - epair=$(vnet_mkepair) - - vnet_init - - vnet_mkjail wgtest1 ${epair}a - vnet_mkjail wgtest2 ${epair}b - - # Workaround for PR 254212. - jexec wgtest1 ifconfig lo0 up - jexec wgtest2 ifconfig lo0 up - - jexec wgtest1 ifconfig ${epair}a $endpoint1 up - jexec wgtest2 ifconfig ${epair}b $endpoint2 up - - wg1=$(jexec wgtest1 ifconfig wg create listen-port 12345 private-key "$pri1") - pub1=$(jexec wgtest1 ifconfig $wg1 | awk '/public-key:/ {print $2}') - wg2=$(jexec wgtest2 ifconfig wg create listen-port 12345 private-key "$pri2") - pub2=$(jexec wgtest2 ifconfig $wg2 | awk '/public-key:/ {print $2}') - - atf_check -s exit:0 -o ignore \ - jexec wgtest1 ifconfig $wg1 peer public-key "$pub2" \ - endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/32 - atf_check -s exit:0 \ - jexec wgtest1 ifconfig $wg1 inet $tunnel1 up - - atf_check -s exit:0 -o ignore \ - jexec wgtest2 ifconfig $wg2 peer public-key "$pub1" \ - endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/32 - atf_check -s exit:0 \ - jexec wgtest2 ifconfig $wg2 inet $tunnel2 up - - # Generous timeout since the handshake takes some time. - atf_check -s exit:0 -o ignore jexec wgtest1 ping -o -t 5 -i 0.25 $tunnel2 - atf_check -s exit:0 -o ignore jexec wgtest2 ping -o -t 5 -i 0.25 $tunnel1 -} - -wg_basic_cleanup() -{ - vnet_cleanup -} - -# The kernel is expecteld to silently ignore any attempt to add a peer with a -# public key identical to the host's. -atf_test_case "wg_key_peerdev_shared" "cleanup" -wg_key_peerdev_shared_head() -{ - atf_set descr 'Create a wg(4) interface with a shared pubkey between device and a peer' - atf_set require.user root -} - -wg_key_peerdev_shared_body() -{ - local epair pri1 pub1 wg1 - local endpoint1 tunnel1 - - kldload -n if_wg - - pri1=$(openssl rand -base64 32) - - endpoint1=192.168.2.1 - tunnel1=169.254.0.1 - - vnet_mkjail wgtest1 - - wg1=$(jexec wgtest1 ifconfig wg create listen-port 12345 private-key "$pri1") - pub1=$(jexec wgtest1 ifconfig $wg1 | awk '/public-key:/ {print $2}') - - atf_check -s exit:0 \ - jexec wgtest1 ifconfig ${wg1} peer public-key "${pub1}" \ - allowed-ips "${tunnel1}/32" - - atf_check -o empty jexec wgtest1 ifconfig ${wg1} peers -} - -wg_key_peerdev_shared_cleanup() -{ - vnet_cleanup -} - -# When a wg(8) interface has a private key reassigned that corresponds to the -# public key already on a peer, the kernel is expected to deconfigure the peer -# to resolve the conflict. -atf_test_case "wg_key_peerdev_makeshared" "cleanup" -wg_key_peerdev_makeshared_head() -{ - atf_set descr 'Create a wg(4) interface and assign peer key to device' - atf_set require.progs wg -} - -wg_key_peerdev_makeshared_body() -{ - local epair pri1 pub1 pri2 wg1 wg2 - local endpoint1 tunnel1 - - kldload -n if_wg - - pri1=$(openssl rand -base64 32) - pri2=$(openssl rand -base64 32) - - endpoint1=192.168.2.1 - tunnel1=169.254.0.1 - - vnet_mkjail wgtest1 - - wg1=$(jexec wgtest1 ifconfig wg create listen-port 12345 private-key "$pri1") - pub1=$(jexec wgtest1 ifconfig $wg1 | awk '/public-key:/ {print $2}') - - wg2=$(jexec wgtest1 ifconfig wg create listen-port 12345 private-key "$pri2") - - atf_check -s exit:0 -o ignore \ - jexec wgtest1 ifconfig ${wg2} peer public-key "${pub1}" \ - allowed-ips "${tunnel1}/32" - - atf_check -o not-empty jexec wgtest1 ifconfig ${wg2} peers - - jexec wgtest1 sh -c "echo '${pri1}' > pri1" - - atf_check -s exit:0 \ - jexec wgtest1 wg set ${wg2} private-key pri1 - - atf_check -o empty jexec wgtest1 ifconfig ${wg2} peers -} - -wg_key_peerdev_makeshared_cleanup() -{ - vnet_cleanup -} - -atf_init_test_cases() -{ - atf_add_test_case "wg_basic" - atf_add_test_case "wg_key_peerdev_shared" - atf_add_test_case "wg_key_peerdev_makeshared" -}