blob: fa22e5375269b53397c412512ad04d445e5794a6 [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
Manuel Pégourié-Gonnard37ff1402015-09-04 14:21:07 +02005 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may
8 * not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
Paul Bakker5121ce52009-01-03 21:22:43 +000018 */
19
Dave Rodgman68ef1d62023-05-18 20:49:03 +010020#include <limits.h>
21
Gilles Peskinedb09ef62020-06-03 01:43:33 +020022#include "common.h"
Paul Bakker5121ce52009-01-03 21:22:43 +000023
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020024#if defined(MBEDTLS_BASE64_C)
Paul Bakker5121ce52009-01-03 21:22:43 +000025
Manuel Pégourié-Gonnard7f809972015-03-09 17:05:11 +000026#include "mbedtls/base64.h"
Dave Rodgman0fec4392023-05-18 15:24:36 +010027#include "base64_internal.h"
Gabor Mezei9a4074a2021-11-15 16:18:54 +010028#include "constant_time_internal.h"
Paul Bakker5121ce52009-01-03 21:22:43 +000029
Manuel Pégourié-Gonnard93866642015-06-22 19:21:23 +020030#include <stdint.h>
Paul Bakker5c2364c2012-10-01 14:41:15 +000031
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020032#if defined(MBEDTLS_SELF_TEST)
Rich Evans00ab4702015-02-06 13:43:58 +000033#include <string.h>
Manuel Pégourié-Gonnard7f809972015-03-09 17:05:11 +000034#include "mbedtls/platform.h"
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020035#endif /* MBEDTLS_SELF_TEST */
Paul Bakker7dc4c442014-02-01 22:50:26 +010036
Dave Rodgman0ee96832023-05-09 09:49:01 +010037MBEDTLS_STATIC_TESTABLE
38unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
39{
40 unsigned char digit = 0;
41 /* For each range of values, if value is in that range, mask digit with
42 * the corresponding value. Since value can only be in a single range,
43 * only at most one masking will change digit. */
Dave Rodgman8c94e212023-05-09 10:39:03 +010044 digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
45 digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
46 digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
47 digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
48 digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
Dave Rodgman0ee96832023-05-09 09:49:01 +010049 return digit;
50}
51
52MBEDTLS_STATIC_TESTABLE
53signed char mbedtls_ct_base64_dec_value(unsigned char c)
54{
55 unsigned char val = 0;
56 /* For each range of digits, if c is in that range, mask val with
57 * the corresponding value. Since c can only be in a single range,
58 * only at most one masking will change val. Set val to one plus
59 * the desired value so that it stays 0 if c is in none of the ranges. */
Dave Rodgman8c94e212023-05-09 10:39:03 +010060 val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1);
61 val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
62 val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
63 val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
64 val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
Dave Rodgman0ee96832023-05-09 09:49:01 +010065 /* At this point, val is 0 if c is an invalid digit and v+1 if c is
66 * a digit with the value v. */
67 return val - 1;
68}
69
Paul Bakker5121ce52009-01-03 21:22:43 +000070/*
71 * Encode a buffer into base64 format
72 */
Gilles Peskine449bd832023-01-11 14:50:10 +010073int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
74 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +000075{
Paul Bakker23986e52011-04-24 08:57:21 +000076 size_t i, n;
Paul Bakker5121ce52009-01-03 21:22:43 +000077 int C1, C2, C3;
78 unsigned char *p;
79
Gilles Peskine449bd832023-01-11 14:50:10 +010080 if (slen == 0) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010081 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +010082 return 0;
Manuel Pégourié-Gonnard65fc6a82015-01-28 16:49:26 +000083 }
Paul Bakker5121ce52009-01-03 21:22:43 +000084
Gilles Peskine449bd832023-01-11 14:50:10 +010085 n = slen / 3 + (slen % 3 != 0);
Paul Bakker5121ce52009-01-03 21:22:43 +000086
Dave Rodgman68ef1d62023-05-18 20:49:03 +010087 if (n > (SIZE_MAX - 1) / 4) {
88 *olen = SIZE_MAX;
Gilles Peskine449bd832023-01-11 14:50:10 +010089 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000090 }
91
Manuel Pégourié-Gonnard0aa45c22015-09-30 16:30:28 +020092 n *= 4;
93
Gilles Peskine449bd832023-01-11 14:50:10 +010094 if ((dlen < n + 1) || (NULL == dst)) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010095 *olen = n + 1;
Gilles Peskine449bd832023-01-11 14:50:10 +010096 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000097 }
98
Gilles Peskine449bd832023-01-11 14:50:10 +010099 n = (slen / 3) * 3;
Paul Bakker5121ce52009-01-03 21:22:43 +0000100
Gilles Peskine449bd832023-01-11 14:50:10 +0100101 for (i = 0, p = dst; i < n; i += 3) {
Paul Bakker5121ce52009-01-03 21:22:43 +0000102 C1 = *src++;
103 C2 = *src++;
104 C3 = *src++;
105
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);
109 *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
110 & 0x3F);
111 *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000112 }
113
Gilles Peskine449bd832023-01-11 14:50:10 +0100114 if (i < slen) {
Paul Bakker5121ce52009-01-03 21:22:43 +0000115 C1 = *src++;
Gilles Peskine449bd832023-01-11 14:50:10 +0100116 C2 = ((i + 1) < slen) ? *src++ : 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000117
Gilles Peskine449bd832023-01-11 14:50:10 +0100118 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
119 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
120 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000121
Gilles Peskine449bd832023-01-11 14:50:10 +0100122 if ((i + 1) < slen) {
123 *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
124 } else {
125 *p++ = '=';
126 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000127
128 *p++ = '=';
129 }
130
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100131 *olen = p - dst;
Paul Bakker5121ce52009-01-03 21:22:43 +0000132 *p = 0;
133
Gilles Peskine449bd832023-01-11 14:50:10 +0100134 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000135}
136
137/*
138 * Decode a base64-formatted buffer
139 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100140int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
141 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +0000142{
Gilles Peskine1121cd22021-07-28 14:20:06 +0200143 size_t i; /* index in source */
144 size_t n; /* number of digits or trailing = in source */
145 uint32_t x; /* value accumulator */
Gilles Peskine67468e82021-07-30 12:56:21 +0200146 unsigned accumulated_digits = 0;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200147 unsigned equals = 0;
148 int spaces_present = 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000149 unsigned char *p;
150
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200151 /* First pass: check for validity and get output length */
Gilles Peskine449bd832023-01-11 14:50:10 +0100152 for (i = n = 0; i < slen; i++) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200153 /* Skip spaces before checking for EOL */
Gilles Peskine1121cd22021-07-28 14:20:06 +0200154 spaces_present = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100155 while (i < slen && src[i] == ' ') {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200156 ++i;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200157 spaces_present = 1;
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200158 }
159
160 /* Spaces at end of buffer are OK */
Gilles Peskine449bd832023-01-11 14:50:10 +0100161 if (i == slen) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200162 break;
Gilles Peskine449bd832023-01-11 14:50:10 +0100163 }
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200164
Gilles Peskine449bd832023-01-11 14:50:10 +0100165 if ((slen - i) >= 2 &&
166 src[i] == '\r' && src[i + 1] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000167 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100168 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000169
Gilles Peskine449bd832023-01-11 14:50:10 +0100170 if (src[i] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000171 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100172 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000173
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200174 /* Space inside a line is an error */
Gilles Peskine449bd832023-01-11 14:50:10 +0100175 if (spaces_present) {
176 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200177 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100178
179 if (src[i] > 127) {
180 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
181 }
182
183 if (src[i] == '=') {
184 if (++equals > 2) {
185 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
186 }
187 } else {
188 if (equals != 0) {
189 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
190 }
191 if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
192 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
193 }
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200194 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000195 n++;
196 }
197
Gilles Peskine449bd832023-01-11 14:50:10 +0100198 if (n == 0) {
Simon Butchera45aa132015-10-05 00:26:36 +0100199 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100200 return 0;
Simon Butchera45aa132015-10-05 00:26:36 +0100201 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000202
Simon Butchera29c5e9e2017-02-02 08:46:53 +0000203 /* The following expression is to calculate the following formula without
204 * risk of integer overflow in n:
205 * n = ( ( n * 6 ) + 7 ) >> 3;
206 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100207 n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
Gilles Peskine1121cd22021-07-28 14:20:06 +0200208 n -= equals;
Paul Bakker5121ce52009-01-03 21:22:43 +0000209
Gilles Peskine449bd832023-01-11 14:50:10 +0100210 if (dst == NULL || dlen < n) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100211 *olen = n;
Gilles Peskine449bd832023-01-11 14:50:10 +0100212 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +0000213 }
214
Gilles Peskine1121cd22021-07-28 14:20:06 +0200215 equals = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100216 for (x = 0, p = dst; i > 0; i--, src++) {
217 if (*src == '\r' || *src == '\n' || *src == ' ') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000218 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100219 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000220
Gilles Peskine1121cd22021-07-28 14:20:06 +0200221 x = x << 6;
Gilles Peskine449bd832023-01-11 14:50:10 +0100222 if (*src == '=') {
Gilles Peskine1121cd22021-07-28 14:20:06 +0200223 ++equals;
Gilles Peskine449bd832023-01-11 14:50:10 +0100224 } else {
225 x |= mbedtls_ct_base64_dec_value(*src);
226 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000227
Gilles Peskine449bd832023-01-11 14:50:10 +0100228 if (++accumulated_digits == 4) {
Gilles Peskine67468e82021-07-30 12:56:21 +0200229 accumulated_digits = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100230 *p++ = MBEDTLS_BYTE_2(x);
231 if (equals <= 1) {
232 *p++ = MBEDTLS_BYTE_1(x);
233 }
234 if (equals <= 0) {
235 *p++ = MBEDTLS_BYTE_0(x);
236 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000237 }
238 }
239
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100240 *olen = p - dst;
Paul Bakker5121ce52009-01-03 21:22:43 +0000241
Gilles Peskine449bd832023-01-11 14:50:10 +0100242 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000243}
244
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200245#if defined(MBEDTLS_SELF_TEST)
Paul Bakker5121ce52009-01-03 21:22:43 +0000246
Paul Bakker5121ce52009-01-03 21:22:43 +0000247static const unsigned char base64_test_dec[64] =
248{
249 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
250 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
251 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
252 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
253 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
254 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
255 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
256 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
257};
258
259static const unsigned char base64_test_enc[] =
260 "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
261 "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
262
263/*
264 * Checkup routine
265 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100266int mbedtls_base64_self_test(int verbose)
Paul Bakker5121ce52009-01-03 21:22:43 +0000267{
Paul Bakker23986e52011-04-24 08:57:21 +0000268 size_t len;
Paul Bakker3c2122f2013-06-24 19:03:14 +0200269 const unsigned char *src;
270 unsigned char buffer[128];
Paul Bakker5121ce52009-01-03 21:22:43 +0000271
Gilles Peskine449bd832023-01-11 14:50:10 +0100272 if (verbose != 0) {
273 mbedtls_printf(" Base64 encoding test: ");
274 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000275
Paul Bakker3c2122f2013-06-24 19:03:14 +0200276 src = base64_test_dec;
Paul Bakker5121ce52009-01-03 21:22:43 +0000277
Gilles Peskine449bd832023-01-11 14:50:10 +0100278 if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
279 memcmp(base64_test_enc, buffer, 88) != 0) {
280 if (verbose != 0) {
281 mbedtls_printf("failed\n");
282 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000283
Gilles Peskine449bd832023-01-11 14:50:10 +0100284 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000285 }
286
Gilles Peskine449bd832023-01-11 14:50:10 +0100287 if (verbose != 0) {
288 mbedtls_printf("passed\n Base64 decoding test: ");
289 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000290
Paul Bakker3c2122f2013-06-24 19:03:14 +0200291 src = base64_test_enc;
Paul Bakker5121ce52009-01-03 21:22:43 +0000292
Gilles Peskine449bd832023-01-11 14:50:10 +0100293 if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
294 memcmp(base64_test_dec, buffer, 64) != 0) {
295 if (verbose != 0) {
296 mbedtls_printf("failed\n");
297 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000298
Gilles Peskine449bd832023-01-11 14:50:10 +0100299 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000300 }
301
Gilles Peskine449bd832023-01-11 14:50:10 +0100302 if (verbose != 0) {
303 mbedtls_printf("passed\n\n");
304 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000305
Gilles Peskine449bd832023-01-11 14:50:10 +0100306 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000307}
308
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200309#endif /* MBEDTLS_SELF_TEST */
Paul Bakker5121ce52009-01-03 21:22:43 +0000310
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200311#endif /* MBEDTLS_BASE64_C */