Implement base64 in constant-time.

This is not actually sensible, but it seemed really funny. PEM files
sometimes carry private keys so, in principle, we'd probably prefer not
to leak the contents when we encode or decode them?

Change-Id: I7b056612bd7f22c28853bc89f56aee1f5103b8fb
Reviewed-on: https://boringssl-review.googlesource.com/15047
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
David Benjamin 2017-04-13 20:50:15 -04:00 committed by CQ bot account: commit-bot@chromium.org
parent d075706ea5
commit 536036abf4

View File

@ -67,10 +67,16 @@
/* Encoding. */
static const unsigned char data_bin2ascii[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define conv_bin2ascii(a) (data_bin2ascii[(a) & 0x3f])
static uint8_t conv_bin2ascii(uint8_t a) {
/* Since PEM is sometimes used to carry private keys, we encode base64 data
* itself in constant-time. */
a &= 0x3f;
uint8_t ret = constant_time_select_8(constant_time_eq_8(a, 62), '+', '/');
ret = constant_time_select_8(constant_time_lt_8(a, 62), a - 52 + '0', ret);
ret = constant_time_select_8(constant_time_lt_8(a, 52), a - 26 + 'a', ret);
ret = constant_time_select_8(constant_time_lt_8(a, 26), a + 'A', ret);
return ret;
}
OPENSSL_COMPILE_ASSERT(sizeof(((EVP_ENCODE_CTX *)(NULL))->data) % 3 == 0,
data_length_must_be_multiple_of_base64_chunk_size);
@ -229,29 +235,28 @@ void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) {
OPENSSL_memset(ctx, 0, sizeof(EVP_ENCODE_CTX));
}
/* kBase64ASCIIToBinData maps characters (c < 128) to their base64 value, or
* else 0xff if they are invalid. As a special case, the padding character
* ('=') is mapped to zero. */
static const uint8_t kBase64ASCIIToBinData[128] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
};
static uint8_t base64_ascii_to_bin(uint8_t a) {
if (a >= 128) {
return 0xFF;
}
/* Since PEM is sometimes used to carry private keys, we decode base64 data
* itself in constant-time. */
const uint8_t is_upper =
constant_time_ge_8(a, 'A') & constant_time_ge_8('Z', a);
const uint8_t is_lower =
constant_time_ge_8(a, 'a') & constant_time_ge_8('z', a);
const uint8_t is_digit =
constant_time_ge_8(a, '0') & constant_time_ge_8('9', a);
const uint8_t is_plus = constant_time_eq_8(a, '+');
const uint8_t is_slash = constant_time_eq_8(a, '/');
const uint8_t is_equals = constant_time_eq_8(a, '=');
return kBase64ASCIIToBinData[a];
uint8_t ret = 0xff; /* 0xff signals invalid. */
ret = constant_time_select_8(is_upper, a - 'A', ret); /* [0,26) */
ret = constant_time_select_8(is_lower, a - 'a' + 26, ret); /* [26,52) */
ret = constant_time_select_8(is_digit, a - '0' + 52, ret); /* [52,62) */
ret = constant_time_select_8(is_plus, 62, ret);
ret = constant_time_select_8(is_slash, 63, ret);
/* Padding maps to zero, to be further handled by the caller. */
ret = constant_time_select_8(is_equals, 0, ret);
return ret;
}
/* base64_decode_quad decodes a single “quad” (i.e. four characters) of base64