blob: cc6a73d150fef17d7da52e99c9d459f32e4ab371 [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
198 * n digits-plus-trailing-equal-signs, which means (n - equals) digits.
199 * Now set *olen to the exact length of the output. */
200 /* Each block of 4 digits in the input map to 3 bytes of output.
201 * The last block can have one or two equal signs, in which case
202 * there are that many fewer output bytes. */
203 *olen = (n / 4) * 3 - equals;
Paul Bakker5121ce52009-01-03 21:22:43 +0000204
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200205 if ((*olen != 0 && dst == NULL) || dlen < *olen) {
Gilles Peskine449bd832023-01-11 14:50:10 +0100206 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +0000207 }
208
Gilles Peskine449bd832023-01-11 14:50:10 +0100209 for (x = 0, p = dst; i > 0; i--, src++) {
210 if (*src == '\r' || *src == '\n' || *src == ' ') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000211 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100212 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100213 if (*src == '=') {
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200214 /* We already know from the first loop that equal signs are
215 * only at the end. */
216 break;
Gilles Peskine449bd832023-01-11 14:50:10 +0100217 }
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200218 x = x << 6;
219 x |= mbedtls_ct_base64_dec_value(*src);
Paul Bakker5121ce52009-01-03 21:22:43 +0000220
Gilles Peskine449bd832023-01-11 14:50:10 +0100221 if (++accumulated_digits == 4) {
Gilles Peskine67468e82021-07-30 12:56:21 +0200222 accumulated_digits = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100223 *p++ = MBEDTLS_BYTE_2(x);
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200224 *p++ = MBEDTLS_BYTE_1(x);
225 *p++ = MBEDTLS_BYTE_0(x);
Paul Bakker5121ce52009-01-03 21:22:43 +0000226 }
227 }
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200228 if (accumulated_digits == 3) {
229 *p++ = MBEDTLS_BYTE_2(x << 6);
230 *p++ = MBEDTLS_BYTE_1(x << 6);
231 } else if (accumulated_digits == 2) {
232 *p++ = MBEDTLS_BYTE_2(x << 12);
233 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000234
Gilles Peskine2b3d6a82025-06-04 11:22:25 +0200235 if (*olen != (size_t) (p - dst)) {
236 return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
237 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000238
Gilles Peskine449bd832023-01-11 14:50:10 +0100239 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000240}
241
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200242#if defined(MBEDTLS_SELF_TEST)
Paul Bakker5121ce52009-01-03 21:22:43 +0000243
Paul Bakker5121ce52009-01-03 21:22:43 +0000244static const unsigned char base64_test_dec[64] =
245{
246 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
247 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
248 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
249 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
250 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
251 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
252 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
253 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
254};
255
256static const unsigned char base64_test_enc[] =
257 "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
258 "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
259
260/*
261 * Checkup routine
262 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100263int mbedtls_base64_self_test(int verbose)
Paul Bakker5121ce52009-01-03 21:22:43 +0000264{
Paul Bakker23986e52011-04-24 08:57:21 +0000265 size_t len;
Paul Bakker3c2122f2013-06-24 19:03:14 +0200266 const unsigned char *src;
267 unsigned char buffer[128];
Paul Bakker5121ce52009-01-03 21:22:43 +0000268
Gilles Peskine449bd832023-01-11 14:50:10 +0100269 if (verbose != 0) {
270 mbedtls_printf(" Base64 encoding test: ");
271 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000272
Paul Bakker3c2122f2013-06-24 19:03:14 +0200273 src = base64_test_dec;
Paul Bakker5121ce52009-01-03 21:22:43 +0000274
Gilles Peskine449bd832023-01-11 14:50:10 +0100275 if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
276 memcmp(base64_test_enc, buffer, 88) != 0) {
277 if (verbose != 0) {
278 mbedtls_printf("failed\n");
279 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000280
Gilles Peskine449bd832023-01-11 14:50:10 +0100281 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000282 }
283
Gilles Peskine449bd832023-01-11 14:50:10 +0100284 if (verbose != 0) {
285 mbedtls_printf("passed\n Base64 decoding test: ");
286 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000287
Paul Bakker3c2122f2013-06-24 19:03:14 +0200288 src = base64_test_enc;
Paul Bakker5121ce52009-01-03 21:22:43 +0000289
Gilles Peskine449bd832023-01-11 14:50:10 +0100290 if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
291 memcmp(base64_test_dec, buffer, 64) != 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\n");
301 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000302
Gilles Peskine449bd832023-01-11 14:50:10 +0100303 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000304}
305
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200306#endif /* MBEDTLS_SELF_TEST */
Paul Bakker5121ce52009-01-03 21:22:43 +0000307
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200308#endif /* MBEDTLS_BASE64_C */