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