blob: 388fa9f388e51b03eb4a3b1c1cb0ddc0c932661b [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"
Gilles Peskine2b3d6a82025-06-04 11:22:25 +020017#include "mbedtls/error.h"
Paul Bakker5121ce52009-01-03 21:22:43 +000018
Manuel Pégourié-Gonnard93866642015-06-22 19:21:23 +020019#include <stdint.h>
Paul Bakker5c2364c2012-10-01 14:41:15 +000020
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020021#if defined(MBEDTLS_SELF_TEST)
Rich Evans00ab4702015-02-06 13:43:58 +000022#include <string.h>
Manuel Pégourié-Gonnard7f809972015-03-09 17:05:11 +000023#include "mbedtls/platform.h"
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020024#endif /* MBEDTLS_SELF_TEST */
Paul Bakker7dc4c442014-02-01 22:50:26 +010025
Dave Rodgman0ee96832023-05-09 09:49:01 +010026MBEDTLS_STATIC_TESTABLE
27unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
28{
29 unsigned char digit = 0;
30 /* For each range of values, if value is in that range, mask digit with
31 * the corresponding value. Since value can only be in a single range,
32 * only at most one masking will change digit. */
Dave Rodgman8c94e212023-05-09 10:39:03 +010033 digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
34 digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
35 digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
36 digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
37 digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
Dave Rodgman0ee96832023-05-09 09:49:01 +010038 return digit;
39}
40
41MBEDTLS_STATIC_TESTABLE
42signed char mbedtls_ct_base64_dec_value(unsigned char c)
43{
44 unsigned char val = 0;
45 /* For each range of digits, if c is in that range, mask val with
46 * the corresponding value. Since c can only be in a single range,
47 * only at most one masking will change val. Set val to one plus
48 * the desired value so that it stays 0 if c is in none of the ranges. */
Dave Rodgman8c94e212023-05-09 10:39:03 +010049 val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1);
50 val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
51 val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
52 val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
53 val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
Dave Rodgman0ee96832023-05-09 09:49:01 +010054 /* At this point, val is 0 if c is an invalid digit and v+1 if c is
55 * a digit with the value v. */
56 return val - 1;
57}
58
Paul Bakker5121ce52009-01-03 21:22:43 +000059/*
60 * Encode a buffer into base64 format
61 */
Gilles Peskine449bd832023-01-11 14:50:10 +010062int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
63 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +000064{
Paul Bakker23986e52011-04-24 08:57:21 +000065 size_t i, n;
Paul Bakker5121ce52009-01-03 21:22:43 +000066 int C1, C2, C3;
67 unsigned char *p;
68
Gilles Peskine449bd832023-01-11 14:50:10 +010069 if (slen == 0) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010070 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +010071 return 0;
Manuel Pégourié-Gonnard65fc6a82015-01-28 16:49:26 +000072 }
Paul Bakker5121ce52009-01-03 21:22:43 +000073
Gilles Peskine449bd832023-01-11 14:50:10 +010074 n = slen / 3 + (slen % 3 != 0);
Paul Bakker5121ce52009-01-03 21:22:43 +000075
Dave Rodgman68ef1d62023-05-18 20:49:03 +010076 if (n > (SIZE_MAX - 1) / 4) {
77 *olen = SIZE_MAX;
Gilles Peskine449bd832023-01-11 14:50:10 +010078 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000079 }
80
Manuel Pégourié-Gonnard0aa45c22015-09-30 16:30:28 +020081 n *= 4;
82
Gilles Peskine449bd832023-01-11 14:50:10 +010083 if ((dlen < n + 1) || (NULL == dst)) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010084 *olen = n + 1;
Gilles Peskine449bd832023-01-11 14:50:10 +010085 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000086 }
87
Gilles Peskine449bd832023-01-11 14:50:10 +010088 n = (slen / 3) * 3;
Paul Bakker5121ce52009-01-03 21:22:43 +000089
Gilles Peskine449bd832023-01-11 14:50:10 +010090 for (i = 0, p = dst; i < n; i += 3) {
Paul Bakker5121ce52009-01-03 21:22:43 +000091 C1 = *src++;
92 C2 = *src++;
93 C3 = *src++;
94
Gilles Peskine449bd832023-01-11 14:50:10 +010095 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
96 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
97 & 0x3F);
98 *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
99 & 0x3F);
100 *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000101 }
102
Gilles Peskine449bd832023-01-11 14:50:10 +0100103 if (i < slen) {
Paul Bakker5121ce52009-01-03 21:22:43 +0000104 C1 = *src++;
Gilles Peskine449bd832023-01-11 14:50:10 +0100105 C2 = ((i + 1) < slen) ? *src++ : 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000106
Gilles Peskine449bd832023-01-11 14:50:10 +0100107 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
108 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
109 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000110
Gilles Peskine449bd832023-01-11 14:50:10 +0100111 if ((i + 1) < slen) {
112 *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
113 } else {
114 *p++ = '=';
115 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000116
117 *p++ = '=';
118 }
119
Dave Rodgmane4a6f5a2023-11-04 12:20:09 +0000120 *olen = (size_t) (p - dst);
Paul Bakker5121ce52009-01-03 21:22:43 +0000121 *p = 0;
122
Gilles Peskine449bd832023-01-11 14:50:10 +0100123 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000124}
125
126/*
127 * Decode a base64-formatted buffer
128 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100129int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
130 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +0000131{
Gilles Peskine1121cd22021-07-28 14:20:06 +0200132 size_t i; /* index in source */
133 size_t n; /* number of digits or trailing = in source */
134 uint32_t x; /* value accumulator */
Gilles Peskine67468e82021-07-30 12:56:21 +0200135 unsigned accumulated_digits = 0;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200136 unsigned equals = 0;
137 int spaces_present = 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000138 unsigned char *p;
139
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200140 /* First pass: check for validity and get output length */
Gilles Peskine449bd832023-01-11 14:50:10 +0100141 for (i = n = 0; i < slen; i++) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200142 /* Skip spaces before checking for EOL */
Gilles Peskine1121cd22021-07-28 14:20:06 +0200143 spaces_present = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100144 while (i < slen && src[i] == ' ') {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200145 ++i;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200146 spaces_present = 1;
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200147 }
148
149 /* Spaces at end of buffer are OK */
Gilles Peskine449bd832023-01-11 14:50:10 +0100150 if (i == slen) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200151 break;
Gilles Peskine449bd832023-01-11 14:50:10 +0100152 }
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200153
Gilles Peskine449bd832023-01-11 14:50:10 +0100154 if ((slen - i) >= 2 &&
155 src[i] == '\r' && src[i + 1] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000156 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100157 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000158
Gilles Peskine449bd832023-01-11 14:50:10 +0100159 if (src[i] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000160 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100161 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000162
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200163 /* Space inside a line is an error */
Gilles Peskine449bd832023-01-11 14:50:10 +0100164 if (spaces_present) {
165 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200166 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100167
168 if (src[i] > 127) {
169 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
170 }
171
172 if (src[i] == '=') {
173 if (++equals > 2) {
174 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
175 }
176 } else {
177 if (equals != 0) {
178 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
179 }
180 if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
181 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
182 }
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200183 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000184 n++;
185 }
186
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200187 /* In valid base64, the number of digits (n-equals) is always of the form
188 * 4*k, 4*k+2 or *4k+3. Also, the number n of digits plus the number of
189 * equal signs at the end is always a multiple of 4. */
Gilles Peskine84999d12025-06-04 10:33:31 +0200190 if ((n - equals) % 4 == 1) {
191 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
192 }
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200193 if (n % 4 != 0) {
194 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
Simon Butchera45aa132015-10-05 00:26:36 +0100195 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000196
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200197 /* We've determined that the input is valid, and that it contains
Gilles Peskine51dccfb2025-06-11 18:47:31 +0200198 * exactly k blocks of digits-or-equals, with n = 4 * k,
199 * and equals only present at the end of the last block if at all.
200 * Now we can calculate the length of the output.
201 *
202 * Each block of 4 digits in the input map to 3 bytes of output.
203 * For the last block:
204 * - abcd (where abcd are digits) is a full 3-byte block;
205 * - abc= means 1 byte less than a full 3-byte block of output;
206 * - ab== means 2 bytes less than a full 3-byte block of output;
207 * - a==== and ==== is rejected above.
208 */
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200209 *olen = (n / 4) * 3 - equals;
Paul Bakker5121ce52009-01-03 21:22:43 +0000210
Gilles Peskine51dccfb2025-06-11 18:47:31 +0200211 /* If the output buffer is too small, signal this and stop here.
212 * Also, as documented, stop here if `dst` is null, independently of
213 * `dlen`.
214 *
215 * There is an edge case when the output is empty: in this case,
216 * `dlen == 0` with `dst == NULL` is valid (on some platforms,
217 * `malloc(0)` returns `NULL`). Since the call is valid, we return
218 * 0 in this case.
219 */
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200220 if ((*olen != 0 && dst == NULL) || dlen < *olen) {
Gilles Peskine449bd832023-01-11 14:50:10 +0100221 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +0000222 }
223
Gilles Peskine449bd832023-01-11 14:50:10 +0100224 for (x = 0, p = dst; i > 0; i--, src++) {
225 if (*src == '\r' || *src == '\n' || *src == ' ') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000226 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100227 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100228 if (*src == '=') {
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200229 /* We already know from the first loop that equal signs are
230 * only at the end. */
231 break;
Gilles Peskine449bd832023-01-11 14:50:10 +0100232 }
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200233 x = x << 6;
234 x |= mbedtls_ct_base64_dec_value(*src);
Paul Bakker5121ce52009-01-03 21:22:43 +0000235
Gilles Peskine449bd832023-01-11 14:50:10 +0100236 if (++accumulated_digits == 4) {
Gilles Peskine67468e82021-07-30 12:56:21 +0200237 accumulated_digits = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100238 *p++ = MBEDTLS_BYTE_2(x);
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200239 *p++ = MBEDTLS_BYTE_1(x);
240 *p++ = MBEDTLS_BYTE_0(x);
Paul Bakker5121ce52009-01-03 21:22:43 +0000241 }
242 }
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200243 if (accumulated_digits == 3) {
244 *p++ = MBEDTLS_BYTE_2(x << 6);
245 *p++ = MBEDTLS_BYTE_1(x << 6);
246 } else if (accumulated_digits == 2) {
247 *p++ = MBEDTLS_BYTE_2(x << 12);
248 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000249
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200250 if (*olen != (size_t) (p - dst)) {
251 return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
252 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000253
Gilles Peskine449bd832023-01-11 14:50:10 +0100254 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000255}
256
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200257#if defined(MBEDTLS_SELF_TEST)
Paul Bakker5121ce52009-01-03 21:22:43 +0000258
Paul Bakker5121ce52009-01-03 21:22:43 +0000259static const unsigned char base64_test_dec[64] =
260{
261 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
262 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
263 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
264 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
265 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
266 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
267 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
268 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
269};
270
271static const unsigned char base64_test_enc[] =
272 "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
273 "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
274
275/*
276 * Checkup routine
277 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100278int mbedtls_base64_self_test(int verbose)
Paul Bakker5121ce52009-01-03 21:22:43 +0000279{
Paul Bakker23986e52011-04-24 08:57:21 +0000280 size_t len;
Paul Bakker3c2122f2013-06-24 19:03:14 +0200281 const unsigned char *src;
282 unsigned char buffer[128];
Paul Bakker5121ce52009-01-03 21:22:43 +0000283
Gilles Peskine449bd832023-01-11 14:50:10 +0100284 if (verbose != 0) {
285 mbedtls_printf(" Base64 encoding test: ");
286 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000287
Paul Bakker3c2122f2013-06-24 19:03:14 +0200288 src = base64_test_dec;
Paul Bakker5121ce52009-01-03 21:22:43 +0000289
Gilles Peskine449bd832023-01-11 14:50:10 +0100290 if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
291 memcmp(base64_test_enc, buffer, 88) != 0) {
292 if (verbose != 0) {
293 mbedtls_printf("failed\n");
294 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000295
Gilles Peskine449bd832023-01-11 14:50:10 +0100296 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000297 }
298
Gilles Peskine449bd832023-01-11 14:50:10 +0100299 if (verbose != 0) {
300 mbedtls_printf("passed\n Base64 decoding test: ");
301 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000302
Paul Bakker3c2122f2013-06-24 19:03:14 +0200303 src = base64_test_enc;
Paul Bakker5121ce52009-01-03 21:22:43 +0000304
Gilles Peskine449bd832023-01-11 14:50:10 +0100305 if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
306 memcmp(base64_test_dec, buffer, 64) != 0) {
307 if (verbose != 0) {
308 mbedtls_printf("failed\n");
309 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000310
Gilles Peskine449bd832023-01-11 14:50:10 +0100311 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000312 }
313
Gilles Peskine449bd832023-01-11 14:50:10 +0100314 if (verbose != 0) {
315 mbedtls_printf("passed\n\n");
316 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000317
Gilles Peskine449bd832023-01-11 14:50:10 +0100318 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000319}
320
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200321#endif /* MBEDTLS_SELF_TEST */
Paul Bakker5121ce52009-01-03 21:22:43 +0000322
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200323#endif /* MBEDTLS_BASE64_C */