1817466cbSJens Wiklander /* 2817466cbSJens Wiklander * RFC 1521 base64 encoding/decoding 3817466cbSJens Wiklander * 47901324dSJerome Forissier * Copyright The Mbed TLS Contributors 57901324dSJerome Forissier * SPDX-License-Identifier: Apache-2.0 6817466cbSJens Wiklander * 7817466cbSJens Wiklander * Licensed under the Apache License, Version 2.0 (the "License"); you may 8817466cbSJens Wiklander * not use this file except in compliance with the License. 9817466cbSJens Wiklander * You may obtain a copy of the License at 10817466cbSJens Wiklander * 11817466cbSJens Wiklander * http://www.apache.org/licenses/LICENSE-2.0 12817466cbSJens Wiklander * 13817466cbSJens Wiklander * Unless required by applicable law or agreed to in writing, software 14817466cbSJens Wiklander * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15817466cbSJens Wiklander * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16817466cbSJens Wiklander * See the License for the specific language governing permissions and 17817466cbSJens Wiklander * limitations under the License. 18817466cbSJens Wiklander */ 19817466cbSJens Wiklander 207901324dSJerome Forissier #include "common.h" 21817466cbSJens Wiklander 22817466cbSJens Wiklander #if defined(MBEDTLS_BASE64_C) 23817466cbSJens Wiklander 24817466cbSJens Wiklander #include "mbedtls/base64.h" 25039e02dfSJerome Forissier #include "constant_time_internal.h" 26817466cbSJens Wiklander 27817466cbSJens Wiklander #include <stdint.h> 28817466cbSJens Wiklander 29817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST) 30817466cbSJens Wiklander #include <string.h> 31817466cbSJens Wiklander #include "mbedtls/platform.h" 32817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */ 33817466cbSJens Wiklander 34817466cbSJens Wiklander #define BASE64_SIZE_T_MAX ((size_t) -1) /* SIZE_T_MAX is not standard */ 35817466cbSJens Wiklander 36817466cbSJens Wiklander /* 37817466cbSJens Wiklander * Encode a buffer into base64 format 38817466cbSJens Wiklander */ 39817466cbSJens Wiklander int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, 40817466cbSJens Wiklander const unsigned char *src, size_t slen) 41817466cbSJens Wiklander { 42817466cbSJens Wiklander size_t i, n; 43817466cbSJens Wiklander int C1, C2, C3; 44817466cbSJens Wiklander unsigned char *p; 45817466cbSJens Wiklander 46*32b31808SJens Wiklander if (slen == 0) { 47817466cbSJens Wiklander *olen = 0; 48*32b31808SJens Wiklander return 0; 49817466cbSJens Wiklander } 50817466cbSJens Wiklander 51817466cbSJens Wiklander n = slen / 3 + (slen % 3 != 0); 52817466cbSJens Wiklander 53*32b31808SJens Wiklander if (n > (BASE64_SIZE_T_MAX - 1) / 4) { 54817466cbSJens Wiklander *olen = BASE64_SIZE_T_MAX; 55*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 56817466cbSJens Wiklander } 57817466cbSJens Wiklander 58817466cbSJens Wiklander n *= 4; 59817466cbSJens Wiklander 60*32b31808SJens Wiklander if ((dlen < n + 1) || (NULL == dst)) { 61817466cbSJens Wiklander *olen = n + 1; 62*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 63817466cbSJens Wiklander } 64817466cbSJens Wiklander 65817466cbSJens Wiklander n = (slen / 3) * 3; 66817466cbSJens Wiklander 67*32b31808SJens Wiklander for (i = 0, p = dst; i < n; i += 3) { 68817466cbSJens Wiklander C1 = *src++; 69817466cbSJens Wiklander C2 = *src++; 70817466cbSJens Wiklander C3 = *src++; 71817466cbSJens Wiklander 72039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); 73039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) 74039e02dfSJerome Forissier & 0x3F); 75039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6)) 76039e02dfSJerome Forissier & 0x3F); 77039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F); 78817466cbSJens Wiklander } 79817466cbSJens Wiklander 80*32b31808SJens Wiklander if (i < slen) { 81817466cbSJens Wiklander C1 = *src++; 82817466cbSJens Wiklander C2 = ((i + 1) < slen) ? *src++ : 0; 83817466cbSJens Wiklander 84039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); 85039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) 86039e02dfSJerome Forissier & 0x3F); 87817466cbSJens Wiklander 88*32b31808SJens Wiklander if ((i + 1) < slen) { 89039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F); 90*32b31808SJens Wiklander } else { 91*32b31808SJens Wiklander *p++ = '='; 92*32b31808SJens Wiklander } 93817466cbSJens Wiklander 94817466cbSJens Wiklander *p++ = '='; 95817466cbSJens Wiklander } 96817466cbSJens Wiklander 97817466cbSJens Wiklander *olen = p - dst; 98817466cbSJens Wiklander *p = 0; 99817466cbSJens Wiklander 100*32b31808SJens Wiklander return 0; 101817466cbSJens Wiklander } 102817466cbSJens Wiklander 103817466cbSJens Wiklander /* 104817466cbSJens Wiklander * Decode a base64-formatted buffer 105817466cbSJens Wiklander */ 106817466cbSJens Wiklander int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, 107817466cbSJens Wiklander const unsigned char *src, size_t slen) 108817466cbSJens Wiklander { 109039e02dfSJerome Forissier size_t i; /* index in source */ 110039e02dfSJerome Forissier size_t n; /* number of digits or trailing = in source */ 111039e02dfSJerome Forissier uint32_t x; /* value accumulator */ 112039e02dfSJerome Forissier unsigned accumulated_digits = 0; 113039e02dfSJerome Forissier unsigned equals = 0; 114039e02dfSJerome Forissier int spaces_present = 0; 115817466cbSJens Wiklander unsigned char *p; 116817466cbSJens Wiklander 117817466cbSJens Wiklander /* First pass: check for validity and get output length */ 118*32b31808SJens Wiklander for (i = n = 0; i < slen; i++) { 119817466cbSJens Wiklander /* Skip spaces before checking for EOL */ 120039e02dfSJerome Forissier spaces_present = 0; 121*32b31808SJens Wiklander while (i < slen && src[i] == ' ') { 122817466cbSJens Wiklander ++i; 123039e02dfSJerome Forissier spaces_present = 1; 124817466cbSJens Wiklander } 125817466cbSJens Wiklander 126817466cbSJens Wiklander /* Spaces at end of buffer are OK */ 127*32b31808SJens Wiklander if (i == slen) { 128817466cbSJens Wiklander break; 129*32b31808SJens Wiklander } 130817466cbSJens Wiklander 131817466cbSJens Wiklander if ((slen - i) >= 2 && 132*32b31808SJens Wiklander src[i] == '\r' && src[i + 1] == '\n') { 133817466cbSJens Wiklander continue; 134*32b31808SJens Wiklander } 135817466cbSJens Wiklander 136*32b31808SJens Wiklander if (src[i] == '\n') { 137817466cbSJens Wiklander continue; 138*32b31808SJens Wiklander } 139817466cbSJens Wiklander 140817466cbSJens Wiklander /* Space inside a line is an error */ 141*32b31808SJens Wiklander if (spaces_present) { 142*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 143039e02dfSJerome Forissier } 144*32b31808SJens Wiklander 145*32b31808SJens Wiklander if (src[i] > 127) { 146*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 147*32b31808SJens Wiklander } 148*32b31808SJens Wiklander 149*32b31808SJens Wiklander if (src[i] == '=') { 150*32b31808SJens Wiklander if (++equals > 2) { 151*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 152*32b31808SJens Wiklander } 153*32b31808SJens Wiklander } else { 154*32b31808SJens Wiklander if (equals != 0) { 155*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 156*32b31808SJens Wiklander } 157*32b31808SJens Wiklander if (mbedtls_ct_base64_dec_value(src[i]) < 0) { 158*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; 159*32b31808SJens Wiklander } 160039e02dfSJerome Forissier } 161817466cbSJens Wiklander n++; 162817466cbSJens Wiklander } 163817466cbSJens Wiklander 164*32b31808SJens Wiklander if (n == 0) { 165817466cbSJens Wiklander *olen = 0; 166*32b31808SJens Wiklander return 0; 167817466cbSJens Wiklander } 168817466cbSJens Wiklander 169817466cbSJens Wiklander /* The following expression is to calculate the following formula without 170817466cbSJens Wiklander * risk of integer overflow in n: 171817466cbSJens Wiklander * n = ( ( n * 6 ) + 7 ) >> 3; 172817466cbSJens Wiklander */ 173817466cbSJens Wiklander n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3); 174039e02dfSJerome Forissier n -= equals; 175817466cbSJens Wiklander 176*32b31808SJens Wiklander if (dst == NULL || dlen < n) { 177817466cbSJens Wiklander *olen = n; 178*32b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; 179817466cbSJens Wiklander } 180817466cbSJens Wiklander 181039e02dfSJerome Forissier equals = 0; 182*32b31808SJens Wiklander for (x = 0, p = dst; i > 0; i--, src++) { 183*32b31808SJens Wiklander if (*src == '\r' || *src == '\n' || *src == ' ') { 184817466cbSJens Wiklander continue; 185*32b31808SJens Wiklander } 186817466cbSJens Wiklander 187039e02dfSJerome Forissier x = x << 6; 188*32b31808SJens Wiklander if (*src == '=') { 189039e02dfSJerome Forissier ++equals; 190*32b31808SJens Wiklander } else { 191039e02dfSJerome Forissier x |= mbedtls_ct_base64_dec_value(*src); 192*32b31808SJens Wiklander } 1937901324dSJerome Forissier 194*32b31808SJens Wiklander if (++accumulated_digits == 4) { 195039e02dfSJerome Forissier accumulated_digits = 0; 196039e02dfSJerome Forissier *p++ = MBEDTLS_BYTE_2(x); 197*32b31808SJens Wiklander if (equals <= 1) { 198*32b31808SJens Wiklander *p++ = MBEDTLS_BYTE_1(x); 199*32b31808SJens Wiklander } 200*32b31808SJens Wiklander if (equals <= 0) { 201*32b31808SJens Wiklander *p++ = MBEDTLS_BYTE_0(x); 202*32b31808SJens Wiklander } 203817466cbSJens Wiklander } 204817466cbSJens Wiklander } 205817466cbSJens Wiklander 206817466cbSJens Wiklander *olen = p - dst; 207817466cbSJens Wiklander 208*32b31808SJens Wiklander return 0; 209817466cbSJens Wiklander } 210817466cbSJens Wiklander 211817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST) 212817466cbSJens Wiklander 213817466cbSJens Wiklander static const unsigned char base64_test_dec[64] = 214817466cbSJens Wiklander { 215817466cbSJens Wiklander 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 216817466cbSJens Wiklander 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, 217817466cbSJens Wiklander 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 218817466cbSJens Wiklander 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 219817466cbSJens Wiklander 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, 220817466cbSJens Wiklander 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 221817466cbSJens Wiklander 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, 222817466cbSJens Wiklander 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 223817466cbSJens Wiklander }; 224817466cbSJens Wiklander 225817466cbSJens Wiklander static const unsigned char base64_test_enc[] = 226817466cbSJens Wiklander "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" 227817466cbSJens Wiklander "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; 228817466cbSJens Wiklander 229817466cbSJens Wiklander /* 230817466cbSJens Wiklander * Checkup routine 231817466cbSJens Wiklander */ 232817466cbSJens Wiklander int mbedtls_base64_self_test(int verbose) 233817466cbSJens Wiklander { 234817466cbSJens Wiklander size_t len; 235817466cbSJens Wiklander const unsigned char *src; 236817466cbSJens Wiklander unsigned char buffer[128]; 237817466cbSJens Wiklander 238*32b31808SJens Wiklander if (verbose != 0) { 239817466cbSJens Wiklander mbedtls_printf(" Base64 encoding test: "); 240*32b31808SJens Wiklander } 241817466cbSJens Wiklander 242817466cbSJens Wiklander src = base64_test_dec; 243817466cbSJens Wiklander 244817466cbSJens Wiklander if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 || 245*32b31808SJens Wiklander memcmp(base64_test_enc, buffer, 88) != 0) { 246*32b31808SJens Wiklander if (verbose != 0) { 247817466cbSJens Wiklander mbedtls_printf("failed\n"); 248817466cbSJens Wiklander } 249817466cbSJens Wiklander 250*32b31808SJens Wiklander return 1; 251*32b31808SJens Wiklander } 252*32b31808SJens Wiklander 253*32b31808SJens Wiklander if (verbose != 0) { 254817466cbSJens Wiklander mbedtls_printf("passed\n Base64 decoding test: "); 255*32b31808SJens Wiklander } 256817466cbSJens Wiklander 257817466cbSJens Wiklander src = base64_test_enc; 258817466cbSJens Wiklander 259817466cbSJens Wiklander if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 || 260*32b31808SJens Wiklander memcmp(base64_test_dec, buffer, 64) != 0) { 261*32b31808SJens Wiklander if (verbose != 0) { 262817466cbSJens Wiklander mbedtls_printf("failed\n"); 263817466cbSJens Wiklander } 264817466cbSJens Wiklander 265*32b31808SJens Wiklander return 1; 266*32b31808SJens Wiklander } 267817466cbSJens Wiklander 268*32b31808SJens Wiklander if (verbose != 0) { 269*32b31808SJens Wiklander mbedtls_printf("passed\n\n"); 270*32b31808SJens Wiklander } 271*32b31808SJens Wiklander 272*32b31808SJens Wiklander return 0; 273817466cbSJens Wiklander } 274817466cbSJens Wiklander 275817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */ 276817466cbSJens Wiklander 277817466cbSJens Wiklander #endif /* MBEDTLS_BASE64_C */ 278