blob: a58717d6b3fb3a126da15e000d674729acf5f1af [file] [log] [blame]
Paul Bakker5121ce52009-01-03 21:22:43 +00001/*
2 * RFC 1521 base64 encoding/decoding
3 *
Bence Szépkúti1e148272020-08-07 13:07:28 +02004 * Copyright The Mbed TLS Contributors
Dave Rodgman16799db2023-11-02 19:47:20 +00005 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
Paul Bakker5121ce52009-01-03 21:22:43 +00006 */
7
Dave Rodgman68ef1d62023-05-18 20:49:03 +01008#include <limits.h>
9
Gilles Peskinedb09ef62020-06-03 01:43:33 +020010#include "common.h"
Paul Bakker5121ce52009-01-03 21:22:43 +000011
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020012#if defined(MBEDTLS_BASE64_C)
Paul Bakker5121ce52009-01-03 21:22:43 +000013
Manuel Pégourié-Gonnard7f809972015-03-09 17:05:11 +000014#include "mbedtls/base64.h"
Dave Rodgman0fec4392023-05-18 15:24:36 +010015#include "base64_internal.h"
Gabor Mezei9a4074a2021-11-15 16:18:54 +010016#include "constant_time_internal.h"
Paul Bakker5121ce52009-01-03 21:22:43 +000017
Manuel Pégourié-Gonnard93866642015-06-22 19:21:23 +020018#include <stdint.h>
Paul Bakker5c2364c2012-10-01 14:41:15 +000019
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020020#if defined(MBEDTLS_SELF_TEST)
Rich Evans00ab4702015-02-06 13:43:58 +000021#include <string.h>
Manuel Pégourié-Gonnard7f809972015-03-09 17:05:11 +000022#include "mbedtls/platform.h"
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020023#endif /* MBEDTLS_SELF_TEST */
Paul Bakker7dc4c442014-02-01 22:50:26 +010024
Dave Rodgman0ee96832023-05-09 09:49:01 +010025MBEDTLS_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. */
Dave Rodgman8c94e212023-05-09 10:39:03 +010032 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, '/');
Dave Rodgman0ee96832023-05-09 09:49:01 +010037 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. */
Dave Rodgman8c94e212023-05-09 10:39:03 +010048 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);
Dave Rodgman0ee96832023-05-09 09:49:01 +010053 /* 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
Paul Bakker5121ce52009-01-03 21:22:43 +000058/*
59 * Encode a buffer into base64 format
60 */
Gilles Peskine449bd832023-01-11 14:50:10 +010061int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
62 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +000063{
Paul Bakker23986e52011-04-24 08:57:21 +000064 size_t i, n;
Paul Bakker5121ce52009-01-03 21:22:43 +000065 int C1, C2, C3;
66 unsigned char *p;
67
Gilles Peskine449bd832023-01-11 14:50:10 +010068 if (slen == 0) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010069 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +010070 return 0;
Manuel Pégourié-Gonnard65fc6a82015-01-28 16:49:26 +000071 }
Paul Bakker5121ce52009-01-03 21:22:43 +000072
Gilles Peskine449bd832023-01-11 14:50:10 +010073 n = slen / 3 + (slen % 3 != 0);
Paul Bakker5121ce52009-01-03 21:22:43 +000074
Dave Rodgman68ef1d62023-05-18 20:49:03 +010075 if (n > (SIZE_MAX - 1) / 4) {
76 *olen = SIZE_MAX;
Gilles Peskine449bd832023-01-11 14:50:10 +010077 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000078 }
79
Manuel Pégourié-Gonnard0aa45c22015-09-30 16:30:28 +020080 n *= 4;
81
Gilles Peskine449bd832023-01-11 14:50:10 +010082 if ((dlen < n + 1) || (NULL == dst)) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010083 *olen = n + 1;
Gilles Peskine449bd832023-01-11 14:50:10 +010084 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000085 }
86
Gilles Peskine449bd832023-01-11 14:50:10 +010087 n = (slen / 3) * 3;
Paul Bakker5121ce52009-01-03 21:22:43 +000088
Gilles Peskine449bd832023-01-11 14:50:10 +010089 for (i = 0, p = dst; i < n; i += 3) {
Paul Bakker5121ce52009-01-03 21:22:43 +000090 C1 = *src++;
91 C2 = *src++;
92 C3 = *src++;
93
Gilles Peskine449bd832023-01-11 14:50:10 +010094 *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);
Paul Bakker5121ce52009-01-03 21:22:43 +0000100 }
101
Gilles Peskine449bd832023-01-11 14:50:10 +0100102 if (i < slen) {
Paul Bakker5121ce52009-01-03 21:22:43 +0000103 C1 = *src++;
Gilles Peskine449bd832023-01-11 14:50:10 +0100104 C2 = ((i + 1) < slen) ? *src++ : 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000105
Gilles Peskine449bd832023-01-11 14:50:10 +0100106 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
107 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
108 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000109
Gilles Peskine449bd832023-01-11 14:50:10 +0100110 if ((i + 1) < slen) {
111 *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
112 } else {
113 *p++ = '=';
114 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000115
116 *p++ = '=';
117 }
118
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100119 *olen = p - dst;
Paul Bakker5121ce52009-01-03 21:22:43 +0000120 *p = 0;
121
Gilles Peskine449bd832023-01-11 14:50:10 +0100122 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000123}
124
125/*
126 * Decode a base64-formatted buffer
127 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100128int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
129 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +0000130{
Gilles Peskine1121cd22021-07-28 14:20:06 +0200131 size_t i; /* index in source */
132 size_t n; /* number of digits or trailing = in source */
133 uint32_t x; /* value accumulator */
Gilles Peskine67468e82021-07-30 12:56:21 +0200134 unsigned accumulated_digits = 0;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200135 unsigned equals = 0;
136 int spaces_present = 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000137 unsigned char *p;
138
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200139 /* First pass: check for validity and get output length */
Gilles Peskine449bd832023-01-11 14:50:10 +0100140 for (i = n = 0; i < slen; i++) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200141 /* Skip spaces before checking for EOL */
Gilles Peskine1121cd22021-07-28 14:20:06 +0200142 spaces_present = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100143 while (i < slen && src[i] == ' ') {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200144 ++i;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200145 spaces_present = 1;
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200146 }
147
148 /* Spaces at end of buffer are OK */
Gilles Peskine449bd832023-01-11 14:50:10 +0100149 if (i == slen) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200150 break;
Gilles Peskine449bd832023-01-11 14:50:10 +0100151 }
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200152
Gilles Peskine449bd832023-01-11 14:50:10 +0100153 if ((slen - i) >= 2 &&
154 src[i] == '\r' && src[i + 1] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000155 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100156 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000157
Gilles Peskine449bd832023-01-11 14:50:10 +0100158 if (src[i] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000159 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100160 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000161
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200162 /* Space inside a line is an error */
Gilles Peskine449bd832023-01-11 14:50:10 +0100163 if (spaces_present) {
164 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200165 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100166
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 }
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200182 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000183 n++;
184 }
185
Gilles Peskine449bd832023-01-11 14:50:10 +0100186 if (n == 0) {
Simon Butchera45aa132015-10-05 00:26:36 +0100187 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100188 return 0;
Simon Butchera45aa132015-10-05 00:26:36 +0100189 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000190
Simon Butchera29c5e9e2017-02-02 08:46:53 +0000191 /* The following expression is to calculate the following formula without
192 * risk of integer overflow in n:
193 * n = ( ( n * 6 ) + 7 ) >> 3;
194 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100195 n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
Gilles Peskine1121cd22021-07-28 14:20:06 +0200196 n -= equals;
Paul Bakker5121ce52009-01-03 21:22:43 +0000197
Gilles Peskine449bd832023-01-11 14:50:10 +0100198 if (dst == NULL || dlen < n) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100199 *olen = n;
Gilles Peskine449bd832023-01-11 14:50:10 +0100200 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +0000201 }
202
Gilles Peskine1121cd22021-07-28 14:20:06 +0200203 equals = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100204 for (x = 0, p = dst; i > 0; i--, src++) {
205 if (*src == '\r' || *src == '\n' || *src == ' ') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000206 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100207 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000208
Gilles Peskine1121cd22021-07-28 14:20:06 +0200209 x = x << 6;
Gilles Peskine449bd832023-01-11 14:50:10 +0100210 if (*src == '=') {
Gilles Peskine1121cd22021-07-28 14:20:06 +0200211 ++equals;
Gilles Peskine449bd832023-01-11 14:50:10 +0100212 } else {
213 x |= mbedtls_ct_base64_dec_value(*src);
214 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000215
Gilles Peskine449bd832023-01-11 14:50:10 +0100216 if (++accumulated_digits == 4) {
Gilles Peskine67468e82021-07-30 12:56:21 +0200217 accumulated_digits = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100218 *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 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000225 }
226 }
227
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100228 *olen = p - dst;
Paul Bakker5121ce52009-01-03 21:22:43 +0000229
Gilles Peskine449bd832023-01-11 14:50:10 +0100230 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000231}
232
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200233#if defined(MBEDTLS_SELF_TEST)
Paul Bakker5121ce52009-01-03 21:22:43 +0000234
Paul Bakker5121ce52009-01-03 21:22:43 +0000235static 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 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100254int mbedtls_base64_self_test(int verbose)
Paul Bakker5121ce52009-01-03 21:22:43 +0000255{
Paul Bakker23986e52011-04-24 08:57:21 +0000256 size_t len;
Paul Bakker3c2122f2013-06-24 19:03:14 +0200257 const unsigned char *src;
258 unsigned char buffer[128];
Paul Bakker5121ce52009-01-03 21:22:43 +0000259
Gilles Peskine449bd832023-01-11 14:50:10 +0100260 if (verbose != 0) {
261 mbedtls_printf(" Base64 encoding test: ");
262 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000263
Paul Bakker3c2122f2013-06-24 19:03:14 +0200264 src = base64_test_dec;
Paul Bakker5121ce52009-01-03 21:22:43 +0000265
Gilles Peskine449bd832023-01-11 14:50:10 +0100266 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 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000271
Gilles Peskine449bd832023-01-11 14:50:10 +0100272 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000273 }
274
Gilles Peskine449bd832023-01-11 14:50:10 +0100275 if (verbose != 0) {
276 mbedtls_printf("passed\n Base64 decoding test: ");
277 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000278
Paul Bakker3c2122f2013-06-24 19:03:14 +0200279 src = base64_test_enc;
Paul Bakker5121ce52009-01-03 21:22:43 +0000280
Gilles Peskine449bd832023-01-11 14:50:10 +0100281 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 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000286
Gilles Peskine449bd832023-01-11 14:50:10 +0100287 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000288 }
289
Gilles Peskine449bd832023-01-11 14:50:10 +0100290 if (verbose != 0) {
291 mbedtls_printf("passed\n\n");
292 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000293
Gilles Peskine449bd832023-01-11 14:50:10 +0100294 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000295}
296
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200297#endif /* MBEDTLS_SELF_TEST */
Paul Bakker5121ce52009-01-03 21:22:43 +0000298
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200299#endif /* MBEDTLS_BASE64_C */