From 94273094f6607923f7e52b16717325f6ed7d9d46 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sun, 16 Apr 2017 01:20:43 +0200 Subject: tools: side channel resistant base64 --- src/tools/Makefile | 1 - src/tools/base64.c | 258 +++++++++++---------------------------------------- src/tools/base64.h | 17 ++-- src/tools/config.c | 4 +- src/tools/genkey.c | 17 ++-- src/tools/ipc.c | 2 +- src/tools/pubkey.c | 19 ++-- src/tools/show.c | 12 +-- src/tools/showconf.c | 15 ++- 9 files changed, 86 insertions(+), 259 deletions(-) (limited to 'src/tools') diff --git a/src/tools/Makefile b/src/tools/Makefile index 6502c3d..ec75052 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -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) diff --git a/src/tools/base64.c b/src/tools/base64.c index cf37464..48ac1be 100644 --- a/src/tools/base64.c +++ b/src/tools/base64.c @@ -1,220 +1,66 @@ -/* - * Copyright (c) 1996, 1998 by Internet Software Consortium. +/* Copyright (C) 2015-2017 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 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 #include "base64.h" -#include -#include -#include -#include - -#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 diff --git a/src/tools/base64.h b/src/tools/base64.h index 4ad0ac3..37cf1b9 100644 --- a/src/tools/base64.h +++ b/src/tools/base64.h @@ -3,18 +3,13 @@ #ifndef BASE64_H #define BASE64_H -#include +#include +#include +#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 diff --git a/src/tools/config.c b/src/tools/config.c index e95626c..8f75804 100644 --- a/src/tools/config.c +++ b/src/tools/config.c @@ -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; } diff --git a/src/tools/genkey.c b/src/tools/genkey.c index d3bc846..bf35aed 100644 --- a/src/tools/genkey.c +++ b/src/tools/genkey.c @@ -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; - } diff --git a/src/tools/ipc.c b/src/tools/ipc.c index c26da21..ed18128 100644 --- a/src/tools/ipc.c +++ b/src/tools/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; diff --git a/src/tools/pubkey.c b/src/tools/pubkey.c index f567223..009cd15 100644 --- a/src/tools/pubkey.c +++ b/src/tools/pubkey.c @@ -1,7 +1,6 @@ /* Copyright (C) 2015-2017 Jason A. Donenfeld . All Rights Reserved. */ #include -#include #include #include @@ -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; } diff --git a/src/tools/show.c b/src/tools/show.c index 7b057cf..c9ba473 100644 --- a/src/tools/show.c +++ b/src/tools/show.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -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")) diff --git a/src/tools/showconf.c b/src/tools/showconf.c index 585b08d..da48486 100644 --- a/src/tools/showconf.c +++ b/src/tools/showconf.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -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) { -- cgit v1.2.3-59-g8ed1b