1/* 2 * RFC 1521 base64 encoding/decoding 3 * 4 * Copyright The Mbed TLS Contributors 5 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 6 */ 7 8#include <limits.h> 9 10#include "common.h" 11 12#if defined(MBEDTLS_BASE64_C) 13 14#include "mbedtls/base64.h" 15#include "base64_internal.h" 16#include "constant_time_internal.h" 17 18#include <stdint.h> 19 20#if defined(MBEDTLS_SELF_TEST) 21#include <string.h> 22#include "mbedtls/platform.h" 23#endif /* MBEDTLS_SELF_TEST */ 24 25MBEDTLS_STATIC_TESTABLE 26unsigned char mbedtls_ct_base64_enc_char(unsigned char value) 27{ 28 unsigned char digit = 0; 29 /* For each range of values, if value is in that range, mask digit with 30 * the corresponding value. Since value can only be in a single range, 31 * only at most one masking will change digit. */ 32 digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value); 33 digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26); 34 digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52); 35 digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+'); 36 digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/'); 37 return digit; 38} 39 40MBEDTLS_STATIC_TESTABLE 41signed char mbedtls_ct_base64_dec_value(unsigned char c) 42{ 43 unsigned char val = 0; 44 /* For each range of digits, if c is in that range, mask val with 45 * the corresponding value. Since c can only be in a single range, 46 * only at most one masking will change val. Set val to one plus 47 * the desired value so that it stays 0 if c is in none of the ranges. */ 48 val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1); 49 val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1); 50 val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1); 51 val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1); 52 val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1); 53 /* At this point, val is 0 if c is an invalid digit and v+1 if c is 54 * a digit with the value v. */ 55 return val - 1; 56} 57 58/* 59 * Encode a buffer into base64 format 60 */ 61int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, 62 const unsigned char *src, size_t slen) 63{ 64 size_t i, n; 65 int C1, C2, C3; 66 unsigned char *p; 67 68 if (slen == 0) { 69 *olen = 0; 70 return 0; 71 } 72 73 n = slen / 3 + (slen % 3 != 0); 74 75 if (n > (SIZE_MAX - 1) / 4) { 76 *olen = SIZE_MAX; 77 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 78 } 79 80 n *= 4; 81 82 if ((dlen < n + 1) || (NULL == dst)) { 83 *olen = n + 1; 84 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 85 } 86 87 n = (slen / 3) * 3; 88 89 for (i = 0, p = dst; i < n; i += 3) { 90 C1 = *src++; 91 C2 = *src++; 92 C3 = *src++; 93 94 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); 95 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) 96 & 0x3F); 97 *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6)) 98 & 0x3F); 99 *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F); 100 } 101 102 if (i < slen) { 103 C1 = *src++; 104 C2 = ((i + 1) < slen) ? *src++ : 0; 105 106 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); 107 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) 108 & 0x3F); 109 110 if ((i + 1) < slen) { 111 *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F); 112 } else { 113 *p++ = '='; 114 } 115 116 *p++ = '='; 117 } 118 119 *olen = (size_t) (p - dst); 120 *p = 0; 121 122 return 0; 123} 124 125/* 126 * Decode a base64-formatted buffer 127 */ 128int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, 129 const unsigned char *src, size_t slen) 130{ 131 size_t i; /* index in source */ 132 size_t n; /* number of digits or trailing = in source */ 133 uint32_t x; /* value accumulator */ 134 unsigned accumulated_digits = 0; 135 unsigned equals = 0; 136 int spaces_present = 0; 137 unsigned char *p; 138 139 /* First pass: check for validity and get output length */ 140 for (i = n = 0; i < slen; i++) { 141 /* Skip spaces before checking for EOL */ 142 spaces_present = 0; 143 while (i < slen && src[i] == ' ') { 144 ++i; 145 spaces_present = 1; 146 } 147 148 /* Spaces at end of buffer are OK */ 149 if (i == slen) { 150 break; 151 } 152 153 if ((slen - i) >= 2 && 154 src[i] == '\r' && src[i + 1] == '\n') { 155 continue; 156 } 157 158 if (src[i] == '\n') { 159 continue; 160 } 161 162 /* Space inside a line is an error */ 163 if (spaces_present) { 164 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 165 } 166 167 if (src[i] > 127) { 168 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 169 } 170 171 if (src[i] == '=') { 172 if (++equals > 2) { 173 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 174 } 175 } else { 176 if (equals != 0) { 177 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 178 } 179 if (mbedtls_ct_base64_dec_value(src[i]) < 0) { 180 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 181 } 182 } 183 n++; 184 } 185 186 if (n == 0) { 187 *olen = 0; 188 return 0; 189 } 190 191 /* The following expression is to calculate the following formula without 192 * risk of integer overflow in n: 193 * n = ( ( n * 6 ) + 7 ) >> 3; 194 */ 195 n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3); 196 n -= equals; 197 198 if (dst == NULL || dlen < n) { 199 *olen = n; 200 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 201 } 202 203 equals = 0; 204 for (x = 0, p = dst; i > 0; i--, src++) { 205 if (*src == '\r' || *src == '\n' || *src == ' ') { 206 continue; 207 } 208 209 x = x << 6; 210 if (*src == '=') { 211 ++equals; 212 } else { 213 x |= mbedtls_ct_base64_dec_value(*src); 214 } 215 216 if (++accumulated_digits == 4) { 217 accumulated_digits = 0; 218 *p++ = MBEDTLS_BYTE_2(x); 219 if (equals <= 1) { 220 *p++ = MBEDTLS_BYTE_1(x); 221 } 222 if (equals <= 0) { 223 *p++ = MBEDTLS_BYTE_0(x); 224 } 225 } 226 } 227 228 *olen = (size_t) (p - dst); 229 230 return 0; 231} 232 233#if defined(MBEDTLS_SELF_TEST) 234 235static const unsigned char base64_test_dec[64] = 236{ 237 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 238 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, 239 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 240 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 241 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, 242 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 243 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, 244 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 245}; 246 247static const unsigned char base64_test_enc[] = 248 "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" 249 "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; 250 251/* 252 * Checkup routine 253 */ 254int mbedtls_base64_self_test(int verbose) 255{ 256 size_t len; 257 const unsigned char *src; 258 unsigned char buffer[128]; 259 260 if (verbose != 0) { 261 mbedtls_printf(" Base64 encoding test: "); 262 } 263 264 src = base64_test_dec; 265 266 if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 || 267 memcmp(base64_test_enc, buffer, 88) != 0) { 268 if (verbose != 0) { 269 mbedtls_printf("failed\n"); 270 } 271 272 return 1; 273 } 274 275 if (verbose != 0) { 276 mbedtls_printf("passed\n Base64 decoding test: "); 277 } 278 279 src = base64_test_enc; 280 281 if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 || 282 memcmp(base64_test_dec, buffer, 64) != 0) { 283 if (verbose != 0) { 284 mbedtls_printf("failed\n"); 285 } 286 287 return 1; 288 } 289 290 if (verbose != 0) { 291 mbedtls_printf("passed\n\n"); 292 } 293 294 return 0; 295} 296 297#endif /* MBEDTLS_SELF_TEST */ 298 299#endif /* MBEDTLS_BASE64_C */ 300