]> git.ipfire.org Git - thirdparty/wireguard-tools.git/commitdiff
wg: side channel resistant base64
authorJason A. Donenfeld <Jason@zx2c4.com>
Sat, 15 Apr 2017 23:20:43 +0000 (01:20 +0200)
committerJason A. Donenfeld <Jason@zx2c4.com>
Wed, 19 Apr 2017 16:26:32 +0000 (18:26 +0200)
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
src/Makefile
src/base64.c
src/base64.h
src/config.c
src/genkey.c
src/ipc.c
src/pubkey.c
src/show.c
src/showconf.c

index 6502c3d47b1c8ba67c555eadbf5a678c29decc08..ec750529435f37cc7af033b8a5842bf297c878e3 100644 (file)
@@ -36,7 +36,6 @@ CFLAGS += -std=gnu11
 CFLAGS += -Wall -Wextra
 CFLAGS += -MMD -MP
 CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
-LDLIBS += -lresolv
 ifeq ($(shell uname -s),Linux)
 LIBMNL_CFLAGS := $(shell $(PKG_CONFIG) --cflags libmnl 2>/dev/null)
 LIBMNL_LDLIBS := $(shell $(PKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)
index cf3746408c6c9abbaa4ba86062d321c121f40b3d..48ac1be66c1c2398d192d8c58a125321b60e6dcd 100644 (file)
-/*
- * Copyright (c) 1996, 1998 by Internet Software Consortium.
+/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM 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.
- *
- * Portions Copyright (c) 1995 by International Business Machines, Inc.
- *
- * International Business Machines, Inc. (hereinafter called IBM) grants
- * permission under its copyrights to use, copy, modify, and distribute this
- * Software with or without fee, provided that the above copyright notice and
- * all paragraphs of this notice appear in all copies, and that the name of IBM
- * not be used in connection with the marketing of any product incorporating
- * the Software or modifications thereof, without specific, written prior
- * permission.
- *
- * To the extent it has a right to do so, IBM grants an immunity from suit
- * under its patents, if any, for the use, sale or manufacture of products to
- * the extent that such products are used for performing Domain Name System
- * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
- * granted for any product per se or for any other function of any product.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
- * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
- * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * This is a specialized constant-time base64 implementation that resists side-channel attacks.
  */
 
+#include <string.h>
 #include "base64.h"
-#include <sys/types.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <ctype.h>
-
-#if defined(NEED_B64_NTOP) || defined(NEED_B64_PTON)
-static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char pad64 = '=';
-#endif
 
-#ifdef NEED_B64_NTOP
-int b64_ntop(unsigned char const *src, size_t srclength, char *target, size_t targsize)
+static inline void encode(char dest[4], const uint8_t src[3])
 {
-       size_t datalength = 0;
-       uint8_t input[3];
-       uint8_t output[4];
-       size_t i;
+       const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
+       for (unsigned int i = 0; i < 4; ++i)
+               dest[i] = input[i] + 'A'
+                         + (((25 - input[i]) >> 8) & 6)
+                         - (((51 - input[i]) >> 8) & 75)
+                         - (((61 - input[i]) >> 8) & 15)
+                         + (((62 - input[i]) >> 8) & 3);
 
-       while (2 < srclength) {
-               input[0] = *src++;
-               input[1] = *src++;
-               input[2] = *src++;
-               srclength -= 3;
-
-               output[0] = input[0] >> 2;
-               output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
-               output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
-               output[3] = input[2] & 0x3f;
-               assert(output[0] < 64);
-               assert(output[1] < 64);
-               assert(output[2] < 64);
-               assert(output[3] < 64);
-
-               if (datalength + 4 > targsize)
-                       return -1;
-               target[datalength++] = base64[output[0]];
-               target[datalength++] = base64[output[1]];
-               target[datalength++] = base64[output[2]];
-               target[datalength++] = base64[output[3]];
-       }
-       if (0 != srclength) {
-               input[0] = input[1] = input[2] = '\0';
-               for (i = 0; i < srclength; i++)
-                       input[i] = *src++;
-               output[0] = input[0] >> 2;
-               output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
-               output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
-               assert(output[0] < 64);
-               assert(output[1] < 64);
-               assert(output[2] < 64);
-
-               if (datalength + 4 > targsize)
-                       return -1;
-               target[datalength++] = base64[output[0]];
-               target[datalength++] = base64[output[1]];
-               if (srclength == 1)
-                       target[datalength++] = pad64;
-               else
-                       target[datalength++] = base64[output[2]];
-               target[datalength++] = pad64;
-       }
-       if (datalength >= targsize)
-               return (-1);
-       target[datalength] = '\0';
-       return datalength;
 }
-#endif
 
-#ifdef NEED_B64_PTON
-int b64_pton(char const *src, uint8_t *target, size_t targsize)
+void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN])
 {
-       static int b64rmap_initialized = 0;
-       static uint8_t b64rmap[256];
-       static const uint8_t b64rmap_special = 0xf0;
-       static const uint8_t b64rmap_end = 0xfd;
-       static const uint8_t b64rmap_space = 0xfe;
-       static const uint8_t b64rmap_invalid = 0xff;
-       int tarindex, state, ch;
-       uint8_t ofs;
-
-       if (!b64rmap_initialized) {
-               int i;
-               char ch;
-               b64rmap[0] = b64rmap_end;
-               for (i = 1; i < 256; ++i) {
-                       ch = (char)i;
-                       if (isspace(ch))
-                               b64rmap[i] = b64rmap_space;
-                       else if (ch == pad64)
-                               b64rmap[i] = b64rmap_end;
-                       else
-                               b64rmap[i] = b64rmap_invalid;
-               }
-               for (i = 0; base64[i] != '\0'; ++i)
-                       b64rmap[(uint8_t)base64[i]] = i;
-               b64rmap_initialized = 1;
-       }
-
-       state = 0;
-       tarindex = 0;
-
-       for (;;) {
-               ch = *src++;
-               ofs = b64rmap[ch];
-
-               if (ofs >= b64rmap_special) {
-                       if (ofs == b64rmap_space)
-                               continue;
-                       if (ofs == b64rmap_end)
-                               break;
-                       return -1;
-               }
-
-               switch (state) {
-               case 0:
-                       if ((size_t)tarindex >= targsize)
-                               return -1;
-                       target[tarindex] = ofs << 2;
-                       state = 1;
-                       break;
-               case 1:
-                       if ((size_t)tarindex + 1 >= targsize)
-                               return -1;
-                       target[tarindex]   |=  ofs >> 4;
-                       target[tarindex+1]  = (ofs & 0x0f) << 4 ;
-                       tarindex++;
-                       state = 2;
-                       break;
-               case 2:
-                       if ((size_t)tarindex + 1 >= targsize)
-                               return -1;
-                       target[tarindex]   |=  ofs >> 2;
-                       target[tarindex+1]  = (ofs & 0x03) << 6;
-                       tarindex++;
-                       state = 3;
-                       break;
-               case 3:
-                       if ((size_t)tarindex >= targsize)
-                               return -1;
-                       target[tarindex] |= ofs;
-                       tarindex++;
-                       state = 0;
-                       break;
-               default:
-                       abort();
-               }
-       }
+       unsigned int i;
+       for (i = 0; i < WG_KEY_LEN / 3; ++i)
+               encode(&base64[i * 4], &key[i * 3]);
+       encode(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
+       base64[WG_KEY_LEN_BASE64 - 2] = '=';
+       base64[WG_KEY_LEN_BASE64 - 1] = '\0';
+}
 
-       if (ch == pad64) {
-               ch = *src++;
-               switch (state) {
-               case 0:
-               case 1:
-                       return -1;
+static inline int decode(const char src[4])
+{
+       int val = 0;
+       for (unsigned int i = 0; i < 4; ++i)
+               val |= (-1
+                           + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+                           + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+                           + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+                           + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+                           + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+                       ) << (18 - 6 * i);
+       return val;
+}
 
-               case 2:
-                       for (; ch; ch = *src++) {
-                               if (b64rmap[ch] != b64rmap_space)
-                                       break;
-                       }
-                       if (ch != pad64)
-                               return -1;
-                       ch = *src++;
-               case 3:
-                       for (; ch; ch = *src++) {
-                               if (b64rmap[ch] != b64rmap_space)
-                                       return -1;
-                       }
-                       if (target[tarindex] != 0)
-                               return -1;
-               }
-       } else {
-               if (state != 0)
-                       return -1;
+bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)
+{
+       unsigned int i;
+       int val;
+       if (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=')
+               return false;
+
+       for (i = 0; i < WG_KEY_LEN / 3; ++i) {
+               val = decode(&base64[i * 4]);
+               if (val < 0)
+                       return false;
+               key[i * 3 + 0] = (val >> 16) & 0xff;
+               key[i * 3 + 1] = (val >> 8) & 0xff;
+               key[i * 3 + 2] = val & 0xff;
        }
-
-       return tarindex;
+       val = decode((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+       if (val < 0 || val & 0xff)
+               return false;
+       key[i * 3 + 0] = (val >> 16) & 0xff;
+       key[i * 3 + 1] = (val >> 8) & 0xff;
+       return true;
 }
-#endif
index 4ad0ac359c990f0337180321fb1e0702d5dec3fd..37cf1b9c3edc5200775baa0431400f286645056c 100644 (file)
@@ -3,18 +3,13 @@
 #ifndef BASE64_H
 #define BASE64_H
 
-#include <resolv.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "../uapi.h"
 
-#define b64_len(len) ((((len) + 2) / 3) * 4 + 1)
+#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
 
-#ifndef b64_ntop
-int b64_ntop(unsigned char const *, size_t, char *, size_t);
-#define NEED_B64_NTOP
-#endif
-
-#ifndef b64_pton
-int b64_pton(char const *, unsigned char *, size_t);
-#define NEED_B64_PTON
-#endif
+void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]);
+bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64);
 
 #endif
index e95626cd9ba10c23538c4d95c10b751303671e72..8f75804243729db98701b4a000e6ec8376147429 100644 (file)
@@ -118,12 +118,10 @@ static inline bool parse_fwmark(uint32_t *fwmark, unsigned int *flags, const cha
 
 static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
 {
-       uint8_t tmp[WG_KEY_LEN + 1];
-       if (strlen(value) != b64_len(WG_KEY_LEN) - 1 || b64_pton(value, tmp, WG_KEY_LEN + 1) != WG_KEY_LEN) {
+       if (!key_from_base64(key, value)) {
                fprintf(stderr, "Key is not the correct length or format: `%s`\n", value);
                return false;
        }
-       memcpy(key, tmp, WG_KEY_LEN);
        return true;
 }
 
index d3bc846933dc9caeb4cf6564d81953b6ff7f8826..bf35aedf37560918b7ad61aa83cff6f045c6a4cf 100644 (file)
@@ -34,8 +34,8 @@ static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
 
 int genkey_main(int argc, char *argv[])
 {
-       unsigned char private_key[CURVE25519_POINT_SIZE];
-       char private_key_base64[b64_len(CURVE25519_POINT_SIZE)];
+       uint8_t key[WG_KEY_LEN];
+       char base64[WG_KEY_LEN_BASE64];
        struct stat stat;
 
        if (argc != 1) {
@@ -46,19 +46,14 @@ int genkey_main(int argc, char *argv[])
        if (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO)
                fputs("Warning: writing to world accessible file.\nConsider setting the umask to 077 and trying again.\n", stderr);
 
-       if (get_random_bytes(private_key, CURVE25519_POINT_SIZE) != CURVE25519_POINT_SIZE) {
+       if (get_random_bytes(key, WG_KEY_LEN) != WG_KEY_LEN) {
                perror("getrandom");
                return 1;
        }
        if (argc && !strcmp(argv[0], "genkey"))
-               curve25519_normalize_secret(private_key);
+               curve25519_normalize_secret(key);
 
-       if (b64_ntop(private_key, sizeof(private_key), private_key_base64, sizeof(private_key_base64)) != sizeof(private_key_base64) - 1) {
-               fprintf(stderr, "%s: Could not convert key to base64\n", PROG_NAME);
-               return 1;
-       }
-
-       puts(private_key_base64);
+       key_to_base64(base64, key);
+       puts(base64);
        return 0;
-
 }
index c26da213f94fc7b774db8faff5be90f4dde026e1..ed1812825e5f53098a5041fe377a266fe96f92f1 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -177,7 +177,7 @@ static int userspace_set_device(struct wgdevice *dev)
        if (fd < 0)
                return fd;
        for_each_wgpeer(dev, peer, len);
-       len = (unsigned char *)peer - (unsigned char *)dev;
+       len = (uint8_t *)peer - (uint8_t *)dev;
        ret = -EBADMSG;
        if (!len)
                goto out;
index f56722327e6eb2634c0829132323625bb0461341..009cd15366df8baeea4ecea9afddfaf5dbd740fd 100644 (file)
@@ -1,7 +1,6 @@
 /* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
 
 #include <errno.h>
-#include <resolv.h>
 #include <stdio.h>
 #include <ctype.h>
 
@@ -11,8 +10,8 @@
 
 int pubkey_main(int argc, char *argv[])
 {
-       unsigned char private_key[CURVE25519_POINT_SIZE + 1] = { 0 }, public_key[CURVE25519_POINT_SIZE] = { 0 };
-       char private_key_base64[b64_len(CURVE25519_POINT_SIZE)] = { 0 }, public_key_base64[b64_len(CURVE25519_POINT_SIZE)] = { 0 };
+       uint8_t key[WG_KEY_LEN];
+       char base64[WG_KEY_LEN_BASE64];
        int trailing_char;
 
        if (argc != 1) {
@@ -20,11 +19,12 @@ int pubkey_main(int argc, char *argv[])
                return 1;
        }
 
-       if (fread(private_key_base64, 1, sizeof(private_key_base64) - 1, stdin) != sizeof(private_key_base64) - 1) {
+       if (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) {
                errno = EINVAL;
                fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME);
                return 1;
        }
+       base64[WG_KEY_LEN_BASE64 - 1] = '\0';
 
        for (;;) {
                trailing_char = getc(stdin);
@@ -36,15 +36,12 @@ int pubkey_main(int argc, char *argv[])
                return 1;
        }
 
-       if (b64_pton(private_key_base64, private_key, sizeof(private_key)) != sizeof(private_key) - 1) {
+       if (!key_from_base64(key, base64)) {
                fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME);
                return 1;
        }
-       curve25519_generate_public(public_key, private_key);
-       if (b64_ntop(public_key, sizeof(public_key), public_key_base64, sizeof(public_key_base64)) != sizeof(public_key_base64) - 1) {
-               fprintf(stderr, "%s: Could not convert key to base64\n", PROG_NAME);
-               return 1;
-       }
-       puts(public_key_base64);
+       curve25519_generate_public(key, key);
+       key_to_base64(base64, key);
+       puts(base64);
        return 0;
 }
index 7b057cfcc75edcbcc90eb77f3a9b0360edddc937..c9ba473281e93b1de23b33d061ae64a7eddbda3f 100644 (file)
@@ -4,7 +4,6 @@
 #include <inttypes.h>
 #include <netinet/in.h>
 #include <net/if.h>
-#include <resolv.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -78,17 +77,16 @@ static void sort_peers(struct wgdevice *device)
 
 static const uint8_t zero[WG_KEY_LEN] = { 0 };
 
-static char *key(const unsigned char key[static WG_KEY_LEN])
+static char *key(const uint8_t key[static WG_KEY_LEN])
 {
-       static char b64[b64_len(WG_KEY_LEN)];
+       static char base64[WG_KEY_LEN_BASE64];
        if (!memcmp(key, zero, WG_KEY_LEN))
                return "(none)";
-       memset(b64, 0, b64_len(WG_KEY_LEN));
-       b64_ntop(key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
-       return b64;
+       key_to_base64(base64, key);
+       return base64;
 }
 
-static char *masked_key(const unsigned char masked_key[static WG_KEY_LEN])
+static char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
 {
        const char *var = getenv("WG_HIDE_KEYS");
        if (var && !strcmp(var, "never"))
index 585b08db13b74679ec3aa523668734f66fcb2b52..da4848648ea571419b924ec403f755d058b0bd4d 100644 (file)
@@ -3,7 +3,6 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <net/if.h>
-#include <resolv.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
@@ -18,7 +17,7 @@
 int showconf_main(int argc, char *argv[])
 {
        static const uint8_t zero[WG_KEY_LEN] = { 0 };
-       char b64[b64_len(WG_KEY_LEN)] = { 0 };
+       char base64[WG_KEY_LEN_BASE64];
        char ip[INET6_ADDRSTRLEN];
        struct wgdevice *device = NULL;
        struct wgpeer *peer;
@@ -48,17 +47,17 @@ int showconf_main(int argc, char *argv[])
        if (device->fwmark)
                printf("FwMark = 0x%x\n", device->fwmark);
        if (memcmp(device->private_key, zero, WG_KEY_LEN)) {
-               b64_ntop(device->private_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
-               printf("PrivateKey = %s\n", b64);
+               key_to_base64(base64, device->private_key);
+               printf("PrivateKey = %s\n", base64);
        }
        if (memcmp(device->preshared_key, zero, WG_KEY_LEN)) {
-               b64_ntop(device->preshared_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
-               printf("PresharedKey = %s\n", b64);
+               key_to_base64(base64, device->preshared_key);
+               printf("PresharedKey = %s\n", base64);
        }
        printf("\n");
        for_each_wgpeer(device, peer, i) {
-               b64_ntop(peer->public_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
-               printf("[Peer]\nPublicKey = %s\n", b64);
+               key_to_base64(base64, peer->public_key);
+               printf("[Peer]\nPublicKey = %s\n", base64);
                if (peer->num_ipmasks)
                        printf("AllowedIPs = ");
                for_each_wgipmask(peer, ipmask, j) {