blob: 3f13fdab06a3d501e6dc139d9fdaf77251cb9fb9 [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"
Gabor Mezei9a4074a2021-11-15 16:18:54 +010027#include "constant_time_internal.h"
Paul Bakker5121ce52009-01-03 21:22:43 +000028
Manuel Pégourié-Gonnard93866642015-06-22 19:21:23 +020029#include <stdint.h>
Paul Bakker5c2364c2012-10-01 14:41:15 +000030
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020031#if defined(MBEDTLS_SELF_TEST)
Rich Evans00ab4702015-02-06 13:43:58 +000032#include <string.h>
Manuel Pégourié-Gonnard7f809972015-03-09 17:05:11 +000033#include "mbedtls/platform.h"
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +020034#endif /* MBEDTLS_SELF_TEST */
Paul Bakker7dc4c442014-02-01 22:50:26 +010035
Dave Rodgman0ee96832023-05-09 09:49:01 +010036MBEDTLS_STATIC_TESTABLE
37unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
38{
39 unsigned char digit = 0;
40 /* For each range of values, if value is in that range, mask digit with
41 * the corresponding value. Since value can only be in a single range,
42 * only at most one masking will change digit. */
43 digit |= mbedtls_ct_uchar_mask_of_range(0, 25, value) & ('A' + value);
44 digit |= mbedtls_ct_uchar_mask_of_range(26, 51, value) & ('a' + value - 26);
45 digit |= mbedtls_ct_uchar_mask_of_range(52, 61, value) & ('0' + value - 52);
46 digit |= mbedtls_ct_uchar_mask_of_range(62, 62, value) & '+';
47 digit |= mbedtls_ct_uchar_mask_of_range(63, 63, value) & '/';
48 return digit;
49}
50
51MBEDTLS_STATIC_TESTABLE
52signed char mbedtls_ct_base64_dec_value(unsigned char c)
53{
54 unsigned char val = 0;
55 /* For each range of digits, if c is in that range, mask val with
56 * the corresponding value. Since c can only be in a single range,
57 * only at most one masking will change val. Set val to one plus
58 * the desired value so that it stays 0 if c is in none of the ranges. */
59 val |= mbedtls_ct_uchar_mask_of_range('A', 'Z', c) & (c - 'A' + 0 + 1);
60 val |= mbedtls_ct_uchar_mask_of_range('a', 'z', c) & (c - 'a' + 26 + 1);
61 val |= mbedtls_ct_uchar_mask_of_range('0', '9', c) & (c - '0' + 52 + 1);
62 val |= mbedtls_ct_uchar_mask_of_range('+', '+', c) & (c - '+' + 62 + 1);
63 val |= mbedtls_ct_uchar_mask_of_range('/', '/', c) & (c - '/' + 63 + 1);
64 /* At this point, val is 0 if c is an invalid digit and v+1 if c is
65 * a digit with the value v. */
66 return val - 1;
67}
68
Paul Bakker5121ce52009-01-03 21:22:43 +000069/*
70 * Encode a buffer into base64 format
71 */
Gilles Peskine449bd832023-01-11 14:50:10 +010072int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
73 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +000074{
Paul Bakker23986e52011-04-24 08:57:21 +000075 size_t i, n;
Paul Bakker5121ce52009-01-03 21:22:43 +000076 int C1, C2, C3;
77 unsigned char *p;
78
Gilles Peskine449bd832023-01-11 14:50:10 +010079 if (slen == 0) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010080 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +010081 return 0;
Manuel Pégourié-Gonnard65fc6a82015-01-28 16:49:26 +000082 }
Paul Bakker5121ce52009-01-03 21:22:43 +000083
Gilles Peskine449bd832023-01-11 14:50:10 +010084 n = slen / 3 + (slen % 3 != 0);
Paul Bakker5121ce52009-01-03 21:22:43 +000085
Dave Rodgman68ef1d62023-05-18 20:49:03 +010086 if (n > (SIZE_MAX - 1) / 4) {
87 *olen = SIZE_MAX;
Gilles Peskine449bd832023-01-11 14:50:10 +010088 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000089 }
90
Manuel Pégourié-Gonnard0aa45c22015-09-30 16:30:28 +020091 n *= 4;
92
Gilles Peskine449bd832023-01-11 14:50:10 +010093 if ((dlen < n + 1) || (NULL == dst)) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +010094 *olen = n + 1;
Gilles Peskine449bd832023-01-11 14:50:10 +010095 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +000096 }
97
Gilles Peskine449bd832023-01-11 14:50:10 +010098 n = (slen / 3) * 3;
Paul Bakker5121ce52009-01-03 21:22:43 +000099
Gilles Peskine449bd832023-01-11 14:50:10 +0100100 for (i = 0, p = dst; i < n; i += 3) {
Paul Bakker5121ce52009-01-03 21:22:43 +0000101 C1 = *src++;
102 C2 = *src++;
103 C3 = *src++;
104
Gilles Peskine449bd832023-01-11 14:50:10 +0100105 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
106 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
107 & 0x3F);
108 *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
109 & 0x3F);
110 *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000111 }
112
Gilles Peskine449bd832023-01-11 14:50:10 +0100113 if (i < slen) {
Paul Bakker5121ce52009-01-03 21:22:43 +0000114 C1 = *src++;
Gilles Peskine449bd832023-01-11 14:50:10 +0100115 C2 = ((i + 1) < slen) ? *src++ : 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000116
Gilles Peskine449bd832023-01-11 14:50:10 +0100117 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
118 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
119 & 0x3F);
Paul Bakker5121ce52009-01-03 21:22:43 +0000120
Gilles Peskine449bd832023-01-11 14:50:10 +0100121 if ((i + 1) < slen) {
122 *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
123 } else {
124 *p++ = '=';
125 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000126
127 *p++ = '=';
128 }
129
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100130 *olen = p - dst;
Paul Bakker5121ce52009-01-03 21:22:43 +0000131 *p = 0;
132
Gilles Peskine449bd832023-01-11 14:50:10 +0100133 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000134}
135
136/*
137 * Decode a base64-formatted buffer
138 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100139int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
140 const unsigned char *src, size_t slen)
Paul Bakker5121ce52009-01-03 21:22:43 +0000141{
Gilles Peskine1121cd22021-07-28 14:20:06 +0200142 size_t i; /* index in source */
143 size_t n; /* number of digits or trailing = in source */
144 uint32_t x; /* value accumulator */
Gilles Peskine67468e82021-07-30 12:56:21 +0200145 unsigned accumulated_digits = 0;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200146 unsigned equals = 0;
147 int spaces_present = 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000148 unsigned char *p;
149
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200150 /* First pass: check for validity and get output length */
Gilles Peskine449bd832023-01-11 14:50:10 +0100151 for (i = n = 0; i < slen; i++) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200152 /* Skip spaces before checking for EOL */
Gilles Peskine1121cd22021-07-28 14:20:06 +0200153 spaces_present = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100154 while (i < slen && src[i] == ' ') {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200155 ++i;
Gilles Peskine1121cd22021-07-28 14:20:06 +0200156 spaces_present = 1;
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200157 }
158
159 /* Spaces at end of buffer are OK */
Gilles Peskine449bd832023-01-11 14:50:10 +0100160 if (i == slen) {
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200161 break;
Gilles Peskine449bd832023-01-11 14:50:10 +0100162 }
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200163
Gilles Peskine449bd832023-01-11 14:50:10 +0100164 if ((slen - i) >= 2 &&
165 src[i] == '\r' && src[i + 1] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000166 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100167 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000168
Gilles Peskine449bd832023-01-11 14:50:10 +0100169 if (src[i] == '\n') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000170 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100171 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000172
Manuel Pégourié-Gonnard64938c62014-10-15 21:45:39 +0200173 /* Space inside a line is an error */
Gilles Peskine449bd832023-01-11 14:50:10 +0100174 if (spaces_present) {
175 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200176 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100177
178 if (src[i] > 127) {
179 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
180 }
181
182 if (src[i] == '=') {
183 if (++equals > 2) {
184 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
185 }
186 } else {
187 if (equals != 0) {
188 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
189 }
190 if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
191 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
192 }
Gilles Peskineb553eaa2021-07-28 11:33:04 +0200193 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000194 n++;
195 }
196
Gilles Peskine449bd832023-01-11 14:50:10 +0100197 if (n == 0) {
Simon Butchera45aa132015-10-05 00:26:36 +0100198 *olen = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100199 return 0;
Simon Butchera45aa132015-10-05 00:26:36 +0100200 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000201
Simon Butchera29c5e9e2017-02-02 08:46:53 +0000202 /* The following expression is to calculate the following formula without
203 * risk of integer overflow in n:
204 * n = ( ( n * 6 ) + 7 ) >> 3;
205 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100206 n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
Gilles Peskine1121cd22021-07-28 14:20:06 +0200207 n -= equals;
Paul Bakker5121ce52009-01-03 21:22:43 +0000208
Gilles Peskine449bd832023-01-11 14:50:10 +0100209 if (dst == NULL || dlen < n) {
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100210 *olen = n;
Gilles Peskine449bd832023-01-11 14:50:10 +0100211 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
Paul Bakker5121ce52009-01-03 21:22:43 +0000212 }
213
Gilles Peskine1121cd22021-07-28 14:20:06 +0200214 equals = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100215 for (x = 0, p = dst; i > 0; i--, src++) {
216 if (*src == '\r' || *src == '\n' || *src == ' ') {
Paul Bakker5121ce52009-01-03 21:22:43 +0000217 continue;
Gilles Peskine449bd832023-01-11 14:50:10 +0100218 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000219
Gilles Peskine1121cd22021-07-28 14:20:06 +0200220 x = x << 6;
Gilles Peskine449bd832023-01-11 14:50:10 +0100221 if (*src == '=') {
Gilles Peskine1121cd22021-07-28 14:20:06 +0200222 ++equals;
Gilles Peskine449bd832023-01-11 14:50:10 +0100223 } else {
224 x |= mbedtls_ct_base64_dec_value(*src);
225 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000226
Gilles Peskine449bd832023-01-11 14:50:10 +0100227 if (++accumulated_digits == 4) {
Gilles Peskine67468e82021-07-30 12:56:21 +0200228 accumulated_digits = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100229 *p++ = MBEDTLS_BYTE_2(x);
230 if (equals <= 1) {
231 *p++ = MBEDTLS_BYTE_1(x);
232 }
233 if (equals <= 0) {
234 *p++ = MBEDTLS_BYTE_0(x);
235 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000236 }
237 }
238
Manuel Pégourié-Gonnardba561362015-06-02 16:30:35 +0100239 *olen = p - dst;
Paul Bakker5121ce52009-01-03 21:22:43 +0000240
Gilles Peskine449bd832023-01-11 14:50:10 +0100241 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000242}
243
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200244#if defined(MBEDTLS_SELF_TEST)
Paul Bakker5121ce52009-01-03 21:22:43 +0000245
Paul Bakker5121ce52009-01-03 21:22:43 +0000246static const unsigned char base64_test_dec[64] =
247{
248 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
249 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
250 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
251 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
252 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
253 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
254 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
255 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
256};
257
258static const unsigned char base64_test_enc[] =
259 "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
260 "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
261
262/*
263 * Checkup routine
264 */
Gilles Peskine449bd832023-01-11 14:50:10 +0100265int mbedtls_base64_self_test(int verbose)
Paul Bakker5121ce52009-01-03 21:22:43 +0000266{
Paul Bakker23986e52011-04-24 08:57:21 +0000267 size_t len;
Paul Bakker3c2122f2013-06-24 19:03:14 +0200268 const unsigned char *src;
269 unsigned char buffer[128];
Paul Bakker5121ce52009-01-03 21:22:43 +0000270
Gilles Peskine449bd832023-01-11 14:50:10 +0100271 if (verbose != 0) {
272 mbedtls_printf(" Base64 encoding test: ");
273 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000274
Paul Bakker3c2122f2013-06-24 19:03:14 +0200275 src = base64_test_dec;
Paul Bakker5121ce52009-01-03 21:22:43 +0000276
Gilles Peskine449bd832023-01-11 14:50:10 +0100277 if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
278 memcmp(base64_test_enc, buffer, 88) != 0) {
279 if (verbose != 0) {
280 mbedtls_printf("failed\n");
281 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000282
Gilles Peskine449bd832023-01-11 14:50:10 +0100283 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000284 }
285
Gilles Peskine449bd832023-01-11 14:50:10 +0100286 if (verbose != 0) {
287 mbedtls_printf("passed\n Base64 decoding test: ");
288 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000289
Paul Bakker3c2122f2013-06-24 19:03:14 +0200290 src = base64_test_enc;
Paul Bakker5121ce52009-01-03 21:22:43 +0000291
Gilles Peskine449bd832023-01-11 14:50:10 +0100292 if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
293 memcmp(base64_test_dec, buffer, 64) != 0) {
294 if (verbose != 0) {
295 mbedtls_printf("failed\n");
296 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000297
Gilles Peskine449bd832023-01-11 14:50:10 +0100298 return 1;
Paul Bakker5121ce52009-01-03 21:22:43 +0000299 }
300
Gilles Peskine449bd832023-01-11 14:50:10 +0100301 if (verbose != 0) {
302 mbedtls_printf("passed\n\n");
303 }
Paul Bakker5121ce52009-01-03 21:22:43 +0000304
Gilles Peskine449bd832023-01-11 14:50:10 +0100305 return 0;
Paul Bakker5121ce52009-01-03 21:22:43 +0000306}
307
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200308#endif /* MBEDTLS_SELF_TEST */
Paul Bakker5121ce52009-01-03 21:22:43 +0000309
Manuel Pégourié-Gonnard2cf5a7c2015-04-08 12:49:31 +0200310#endif /* MBEDTLS_BASE64_C */