1817466cbSJens Wiklander /* 2817466cbSJens Wiklander * RFC 1521 base64 encoding/decoding 3817466cbSJens Wiklander * 47901324dSJerome Forissier * Copyright The Mbed TLS Contributors 5*b0563631STom Van Eyck * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 6817466cbSJens Wiklander */ 7817466cbSJens Wiklander 8*b0563631STom Van Eyck #include <limits.h> 9*b0563631STom Van Eyck 107901324dSJerome Forissier #include "common.h" 11817466cbSJens Wiklander 12817466cbSJens Wiklander #if defined(MBEDTLS_BASE64_C) 13817466cbSJens Wiklander 14817466cbSJens Wiklander #include "mbedtls/base64.h" 15*b0563631STom Van Eyck #include "base64_internal.h" 16039e02dfSJerome Forissier #include "constant_time_internal.h" 17817466cbSJens Wiklander 18817466cbSJens Wiklander #include <stdint.h> 19817466cbSJens Wiklander 20817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST) 21817466cbSJens Wiklander #include <string.h> 22817466cbSJens Wiklander #include "mbedtls/platform.h" 23817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */ 24817466cbSJens Wiklander 25*b0563631STom Van Eyck MBEDTLS_STATIC_TESTABLE 26*b0563631STom Van Eyck unsigned char mbedtls_ct_base64_enc_char(unsigned char value) 27*b0563631STom Van Eyck { 28*b0563631STom Van Eyck unsigned char digit = 0; 29*b0563631STom Van Eyck /* For each range of values, if value is in that range, mask digit with 30*b0563631STom Van Eyck * the corresponding value. Since value can only be in a single range, 31*b0563631STom Van Eyck * only at most one masking will change digit. */ 32*b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value); 33*b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26); 34*b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52); 35*b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+'); 36*b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/'); 37*b0563631STom Van Eyck return digit; 38*b0563631STom Van Eyck } 39*b0563631STom Van Eyck 40*b0563631STom Van Eyck MBEDTLS_STATIC_TESTABLE 41*b0563631STom Van Eyck signed char mbedtls_ct_base64_dec_value(unsigned char c) 42*b0563631STom Van Eyck { 43*b0563631STom Van Eyck unsigned char val = 0; 44*b0563631STom Van Eyck /* For each range of digits, if c is in that range, mask val with 45*b0563631STom Van Eyck * the corresponding value. Since c can only be in a single range, 46*b0563631STom Van Eyck * only at most one masking will change val. Set val to one plus 47*b0563631STom Van Eyck * the desired value so that it stays 0 if c is in none of the ranges. */ 48*b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1); 49*b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1); 50*b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1); 51*b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1); 52*b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1); 53*b0563631STom Van Eyck /* At this point, val is 0 if c is an invalid digit and v+1 if c is 54*b0563631STom Van Eyck * a digit with the value v. */ 55*b0563631STom Van Eyck return val - 1; 56*b0563631STom Van Eyck } 57817466cbSJens Wiklander 58817466cbSJens Wiklander /* 59817466cbSJens Wiklander * Encode a buffer into base64 format 60817466cbSJens Wiklander */ 61817466cbSJens Wiklander int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, 62817466cbSJens Wiklander const unsigned char *src, size_t slen) 63817466cbSJens Wiklander { 64817466cbSJens Wiklander size_t i, n; 65817466cbSJens Wiklander int C1, C2, C3; 66817466cbSJens Wiklander unsigned char *p; 67817466cbSJens Wiklander 6832b31808SJens Wiklander if (slen == 0) { 69817466cbSJens Wiklander *olen = 0; 7032b31808SJens Wiklander return 0; 71817466cbSJens Wiklander } 72817466cbSJens Wiklander 73817466cbSJens Wiklander n = slen / 3 + (slen % 3 != 0); 74817466cbSJens Wiklander 75*b0563631STom Van Eyck if (n > (SIZE_MAX - 1) / 4) { 76*b0563631STom Van Eyck *olen = SIZE_MAX; 7732b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 78817466cbSJens Wiklander } 79817466cbSJens Wiklander 80817466cbSJens Wiklander n *= 4; 81817466cbSJens Wiklander 8232b31808SJens Wiklander if ((dlen < n + 1) || (NULL == dst)) { 83817466cbSJens Wiklander *olen = n + 1; 8432b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 85817466cbSJens Wiklander } 86817466cbSJens Wiklander 87817466cbSJens Wiklander n = (slen / 3) * 3; 88817466cbSJens Wiklander 8932b31808SJens Wiklander for (i = 0, p = dst; i < n; i += 3) { 90817466cbSJens Wiklander C1 = *src++; 91817466cbSJens Wiklander C2 = *src++; 92817466cbSJens Wiklander C3 = *src++; 93817466cbSJens Wiklander 94039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); 95039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) 96039e02dfSJerome Forissier & 0x3F); 97039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6)) 98039e02dfSJerome Forissier & 0x3F); 99039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F); 100817466cbSJens Wiklander } 101817466cbSJens Wiklander 10232b31808SJens Wiklander if (i < slen) { 103817466cbSJens Wiklander C1 = *src++; 104817466cbSJens Wiklander C2 = ((i + 1) < slen) ? *src++ : 0; 105817466cbSJens Wiklander 106039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); 107039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) 108039e02dfSJerome Forissier & 0x3F); 109817466cbSJens Wiklander 11032b31808SJens Wiklander if ((i + 1) < slen) { 111039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F); 11232b31808SJens Wiklander } else { 11332b31808SJens Wiklander *p++ = '='; 11432b31808SJens Wiklander } 115817466cbSJens Wiklander 116817466cbSJens Wiklander *p++ = '='; 117817466cbSJens Wiklander } 118817466cbSJens Wiklander 119*b0563631STom Van Eyck *olen = (size_t) (p - dst); 120817466cbSJens Wiklander *p = 0; 121817466cbSJens Wiklander 12232b31808SJens Wiklander return 0; 123817466cbSJens Wiklander } 124817466cbSJens Wiklander 125817466cbSJens Wiklander /* 126817466cbSJens Wiklander * Decode a base64-formatted buffer 127817466cbSJens Wiklander */ 128817466cbSJens Wiklander int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, 129817466cbSJens Wiklander const unsigned char *src, size_t slen) 130817466cbSJens Wiklander { 131039e02dfSJerome Forissier size_t i; /* index in source */ 132039e02dfSJerome Forissier size_t n; /* number of digits or trailing = in source */ 133039e02dfSJerome Forissier uint32_t x; /* value accumulator */ 134039e02dfSJerome Forissier unsigned accumulated_digits = 0; 135039e02dfSJerome Forissier unsigned equals = 0; 136039e02dfSJerome Forissier int spaces_present = 0; 137817466cbSJens Wiklander unsigned char *p; 138817466cbSJens Wiklander 139817466cbSJens Wiklander /* First pass: check for validity and get output length */ 14032b31808SJens Wiklander for (i = n = 0; i < slen; i++) { 141817466cbSJens Wiklander /* Skip spaces before checking for EOL */ 142039e02dfSJerome Forissier spaces_present = 0; 14332b31808SJens Wiklander while (i < slen && src[i] == ' ') { 144817466cbSJens Wiklander ++i; 145039e02dfSJerome Forissier spaces_present = 1; 146817466cbSJens Wiklander } 147817466cbSJens Wiklander 148817466cbSJens Wiklander /* Spaces at end of buffer are OK */ 14932b31808SJens Wiklander if (i == slen) { 150817466cbSJens Wiklander break; 15132b31808SJens Wiklander } 152817466cbSJens Wiklander 153817466cbSJens Wiklander if ((slen - i) >= 2 && 15432b31808SJens Wiklander src[i] == '\r' && src[i + 1] == '\n') { 155817466cbSJens Wiklander continue; 15632b31808SJens Wiklander } 157817466cbSJens Wiklander 15832b31808SJens Wiklander if (src[i] == '\n') { 159817466cbSJens Wiklander continue; 16032b31808SJens Wiklander } 161817466cbSJens Wiklander 162817466cbSJens Wiklander /* Space inside a line is an error */ 16332b31808SJens Wiklander if (spaces_present) { 16432b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 165039e02dfSJerome Forissier } 16632b31808SJens Wiklander 16732b31808SJens Wiklander if (src[i] > 127) { 16832b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 16932b31808SJens Wiklander } 17032b31808SJens Wiklander 17132b31808SJens Wiklander if (src[i] == '=') { 17232b31808SJens Wiklander if (++equals > 2) { 17332b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 17432b31808SJens Wiklander } 17532b31808SJens Wiklander } else { 17632b31808SJens Wiklander if (equals != 0) { 17732b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 17832b31808SJens Wiklander } 17932b31808SJens Wiklander if (mbedtls_ct_base64_dec_value(src[i]) < 0) { 18032b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 18132b31808SJens Wiklander } 182039e02dfSJerome Forissier } 183817466cbSJens Wiklander n++; 184817466cbSJens Wiklander } 185817466cbSJens Wiklander 18632b31808SJens Wiklander if (n == 0) { 187817466cbSJens Wiklander *olen = 0; 18832b31808SJens Wiklander return 0; 189817466cbSJens Wiklander } 190817466cbSJens Wiklander 191817466cbSJens Wiklander /* The following expression is to calculate the following formula without 192817466cbSJens Wiklander * risk of integer overflow in n: 193817466cbSJens Wiklander * n = ( ( n * 6 ) + 7 ) >> 3; 194817466cbSJens Wiklander */ 195817466cbSJens Wiklander n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3); 196039e02dfSJerome Forissier n -= equals; 197817466cbSJens Wiklander 19832b31808SJens Wiklander if (dst == NULL || dlen < n) { 199817466cbSJens Wiklander *olen = n; 20032b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 201817466cbSJens Wiklander } 202817466cbSJens Wiklander 203039e02dfSJerome Forissier equals = 0; 20432b31808SJens Wiklander for (x = 0, p = dst; i > 0; i--, src++) { 20532b31808SJens Wiklander if (*src == '\r' || *src == '\n' || *src == ' ') { 206817466cbSJens Wiklander continue; 20732b31808SJens Wiklander } 208817466cbSJens Wiklander 209039e02dfSJerome Forissier x = x << 6; 21032b31808SJens Wiklander if (*src == '=') { 211039e02dfSJerome Forissier ++equals; 21232b31808SJens Wiklander } else { 213039e02dfSJerome Forissier x |= mbedtls_ct_base64_dec_value(*src); 21432b31808SJens Wiklander } 2157901324dSJerome Forissier 21632b31808SJens Wiklander if (++accumulated_digits == 4) { 217039e02dfSJerome Forissier accumulated_digits = 0; 218039e02dfSJerome Forissier *p++ = MBEDTLS_BYTE_2(x); 21932b31808SJens Wiklander if (equals <= 1) { 22032b31808SJens Wiklander *p++ = MBEDTLS_BYTE_1(x); 22132b31808SJens Wiklander } 22232b31808SJens Wiklander if (equals <= 0) { 22332b31808SJens Wiklander *p++ = MBEDTLS_BYTE_0(x); 22432b31808SJens Wiklander } 225817466cbSJens Wiklander } 226817466cbSJens Wiklander } 227817466cbSJens Wiklander 228*b0563631STom Van Eyck *olen = (size_t) (p - dst); 229817466cbSJens Wiklander 23032b31808SJens Wiklander return 0; 231817466cbSJens Wiklander } 232817466cbSJens Wiklander 233817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST) 234817466cbSJens Wiklander 235817466cbSJens Wiklander static const unsigned char base64_test_dec[64] = 236817466cbSJens Wiklander { 237817466cbSJens Wiklander 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 238817466cbSJens Wiklander 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, 239817466cbSJens Wiklander 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 240817466cbSJens Wiklander 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 241817466cbSJens Wiklander 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, 242817466cbSJens Wiklander 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 243817466cbSJens Wiklander 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, 244817466cbSJens Wiklander 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 245817466cbSJens Wiklander }; 246817466cbSJens Wiklander 247817466cbSJens Wiklander static const unsigned char base64_test_enc[] = 248817466cbSJens Wiklander "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" 249817466cbSJens Wiklander "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; 250817466cbSJens Wiklander 251817466cbSJens Wiklander /* 252817466cbSJens Wiklander * Checkup routine 253817466cbSJens Wiklander */ 254817466cbSJens Wiklander int mbedtls_base64_self_test(int verbose) 255817466cbSJens Wiklander { 256817466cbSJens Wiklander size_t len; 257817466cbSJens Wiklander const unsigned char *src; 258817466cbSJens Wiklander unsigned char buffer[128]; 259817466cbSJens Wiklander 26032b31808SJens Wiklander if (verbose != 0) { 261817466cbSJens Wiklander mbedtls_printf(" Base64 encoding test: "); 26232b31808SJens Wiklander } 263817466cbSJens Wiklander 264817466cbSJens Wiklander src = base64_test_dec; 265817466cbSJens Wiklander 266817466cbSJens Wiklander if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 || 26732b31808SJens Wiklander memcmp(base64_test_enc, buffer, 88) != 0) { 26832b31808SJens Wiklander if (verbose != 0) { 269817466cbSJens Wiklander mbedtls_printf("failed\n"); 270817466cbSJens Wiklander } 271817466cbSJens Wiklander 27232b31808SJens Wiklander return 1; 27332b31808SJens Wiklander } 27432b31808SJens Wiklander 27532b31808SJens Wiklander if (verbose != 0) { 276817466cbSJens Wiklander mbedtls_printf("passed\n Base64 decoding test: "); 27732b31808SJens Wiklander } 278817466cbSJens Wiklander 279817466cbSJens Wiklander src = base64_test_enc; 280817466cbSJens Wiklander 281817466cbSJens Wiklander if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 || 28232b31808SJens Wiklander memcmp(base64_test_dec, buffer, 64) != 0) { 28332b31808SJens Wiklander if (verbose != 0) { 284817466cbSJens Wiklander mbedtls_printf("failed\n"); 285817466cbSJens Wiklander } 286817466cbSJens Wiklander 28732b31808SJens Wiklander return 1; 28832b31808SJens Wiklander } 289817466cbSJens Wiklander 29032b31808SJens Wiklander if (verbose != 0) { 29132b31808SJens Wiklander mbedtls_printf("passed\n\n"); 29232b31808SJens Wiklander } 29332b31808SJens Wiklander 29432b31808SJens Wiklander return 0; 295817466cbSJens Wiklander } 296817466cbSJens Wiklander 297817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */ 298817466cbSJens Wiklander 299817466cbSJens Wiklander #endif /* MBEDTLS_BASE64_C */ 300