Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * Copyright (c) 2013-2017 Nest Labs, Inc.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * Base-64 utility functions.
22 : *
23 : */
24 :
25 : #ifndef __STDC_LIMIT_MACROS
26 : #define __STDC_LIMIT_MACROS
27 : #endif
28 : #include "Base64.h"
29 :
30 : #include <ctype.h>
31 : #include <stdint.h>
32 :
33 : namespace chip {
34 :
35 : // Convert a value in the range 0..63 to its equivalent base64 character.
36 : // Return '=' for any value >= 64.
37 17048 : static char Base64ValToChar(uint8_t val)
38 : {
39 17048 : if (val < 26)
40 6677 : return static_cast<char>('A' + val);
41 10371 : val = static_cast<uint8_t>(val - 26);
42 10371 : if (val < 26)
43 6206 : return static_cast<char>('a' + val);
44 4165 : val = static_cast<uint8_t>(val - 26);
45 4165 : if (val < 10)
46 2208 : return static_cast<char>('0' + val);
47 1957 : if (val == 10)
48 267 : return '+';
49 1690 : if (val == 11)
50 289 : return '/';
51 1401 : return '=';
52 : }
53 :
54 : // Convert a base64 character to a value in the range 0..63, or UINT8_MAX if the character is invalid.
55 678 : static uint8_t Base64CharToVal(uint8_t c)
56 : {
57 678 : if (c == 43)
58 8 : return 62;
59 670 : if (c == 47)
60 11 : return 63;
61 : // NOTE: c < 48 will fall through to return UINT8_MAX below.
62 659 : c = static_cast<uint8_t>(c - 48);
63 659 : if (c < 10)
64 37 : return static_cast<uint8_t>(c + 52);
65 : // NOTE: c < 17 here will fall through to return UINT8_MAX below.
66 622 : c = static_cast<uint8_t>(c - 17);
67 622 : if (c < 26)
68 402 : return c;
69 : // NOTE: c < 32 here will fall through to return UINT8_MAX below.
70 220 : c = static_cast<uint8_t>(c - 32);
71 220 : if (c < 26)
72 220 : return static_cast<uint8_t>(c + 26);
73 0 : return UINT8_MAX;
74 : }
75 :
76 : // Convert a value in the range 0..63 to its equivalent base64url character (see RFC-4648, section 5).
77 : // Return '=' for any value >= 64.
78 0 : static char Base64URLValToChar(uint8_t val)
79 : {
80 0 : if (val < 26)
81 0 : return static_cast<char>('A' + val);
82 0 : val = static_cast<uint8_t>(val - 26);
83 0 : if (val < 26)
84 0 : return static_cast<char>('a' + val);
85 0 : val = static_cast<uint8_t>(val - 26);
86 0 : if (val < 10)
87 0 : return static_cast<char>('0' + val);
88 0 : if (val == 10)
89 0 : return '-';
90 0 : if (val == 11)
91 0 : return '_';
92 0 : return '=';
93 : }
94 :
95 : // Convert a base64url character to a value in the range 0..63, or UINT8_MAX if the character is invalid.
96 0 : static uint8_t Base64URLCharToVal(uint8_t c)
97 : {
98 0 : if (c == 45)
99 0 : return 62;
100 0 : if (c == 95)
101 0 : return 63;
102 : // NOTE: c < 48 will fall through to return UINT8_MAX below.
103 0 : c = static_cast<uint8_t>(c - 48);
104 0 : if (c < 10)
105 0 : return static_cast<uint8_t>(c + 52);
106 : // NOTE: c < 17 here will fall through to return UINT8_MAX below.
107 0 : c = static_cast<uint8_t>(c - 17);
108 0 : if (c < 26)
109 0 : return c;
110 : // NOTE: c < 32 here will fall through to return UINT8_MAX below.
111 0 : c = static_cast<uint8_t>(c - 32);
112 0 : if (c < 26)
113 0 : return static_cast<uint8_t>(c + 26);
114 0 : return UINT8_MAX;
115 : }
116 :
117 706 : uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
118 : {
119 706 : char * outStart = out;
120 :
121 4968 : while (inLen > 0)
122 : {
123 : uint8_t val1, val2, val3, val4;
124 :
125 4262 : val1 = static_cast<uint8_t>(*in >> 2);
126 4262 : val2 = static_cast<uint8_t>((*in << 4) & 0x3F);
127 4262 : in++;
128 4262 : inLen--;
129 4262 : if (inLen > 0)
130 : {
131 3565 : val2 = static_cast<uint8_t>(val2 | *in >> 4);
132 3565 : val3 = static_cast<uint8_t>((*in << 2) & 0x3F);
133 3565 : in++;
134 3565 : inLen--;
135 3565 : if (inLen > 0)
136 : {
137 3558 : val3 = static_cast<uint8_t>(val3 | *in >> 6);
138 3558 : val4 = static_cast<uint8_t>(*in & 0x3F);
139 3558 : in++;
140 3558 : inLen--;
141 : }
142 : else
143 7 : val4 = UINT8_MAX;
144 : }
145 : else
146 697 : val3 = val4 = UINT8_MAX;
147 :
148 4262 : *out++ = valToCharFunct(val1);
149 4262 : *out++ = valToCharFunct(val2);
150 4262 : *out++ = valToCharFunct(val3);
151 4262 : *out++ = valToCharFunct(val4);
152 : }
153 :
154 706 : return static_cast<uint16_t>(out - outStart);
155 : }
156 :
157 706 : uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out)
158 : {
159 706 : return Base64Encode(in, inLen, out, Base64ValToChar);
160 : }
161 :
162 0 : uint16_t Base64URLEncode(const uint8_t * in, uint16_t inLen, char * out)
163 : {
164 0 : return Base64Encode(in, inLen, out, Base64URLValToChar);
165 : }
166 :
167 0 : uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
168 : {
169 0 : uint32_t outLen = 0;
170 :
171 : // Maximum number of input bytes to convert to base-64 in a single call to Base64Encode.
172 : // Number is the largest multiple of 3 bytes where the resulting number of base-64 characters
173 : // fits within a uint16_t.
174 : enum
175 : {
176 : kMaxConvert = (UINT16_MAX / 4) * 3
177 : };
178 :
179 : while (true)
180 : {
181 0 : uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
182 :
183 0 : uint16_t outChunkLen = Base64Encode(in, inChunkLen, out, valToCharFunct);
184 :
185 0 : inLen -= inChunkLen;
186 0 : outLen += outChunkLen;
187 :
188 0 : if (inLen == 0)
189 0 : break;
190 :
191 0 : in += inChunkLen;
192 0 : out += outChunkLen;
193 0 : }
194 :
195 0 : return outLen;
196 : }
197 :
198 0 : uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out)
199 : {
200 0 : return Base64Encode32(in, inLen, out, Base64ValToChar);
201 : }
202 :
203 23 : uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
204 : {
205 23 : uint8_t * outStart = out;
206 :
207 : // isgraph() returns false for space and ctrl chars
208 181 : while (inLen > 0 && isgraph(*in))
209 : {
210 177 : if (inLen == 1)
211 0 : goto fail;
212 :
213 177 : uint8_t a = charToValFunct(static_cast<uint8_t>(*in++));
214 177 : uint8_t b = charToValFunct(static_cast<uint8_t>(*in++));
215 177 : inLen = static_cast<uint16_t>(inLen - 2);
216 :
217 177 : if (a == UINT8_MAX || b == UINT8_MAX)
218 0 : goto fail;
219 :
220 177 : *out++ = static_cast<uint8_t>((a << 2) | (b >> 4));
221 :
222 177 : if (inLen == 0 || *in == '=')
223 : break;
224 :
225 166 : uint8_t c = charToValFunct(static_cast<uint8_t>(*in++));
226 166 : inLen--;
227 :
228 166 : if (c == UINT8_MAX)
229 0 : goto fail;
230 :
231 166 : *out++ = static_cast<uint8_t>((b << 4) | (c >> 2));
232 :
233 166 : if (inLen == 0 || *in == '=')
234 : break;
235 :
236 158 : uint8_t d = charToValFunct(static_cast<uint8_t>(*in++));
237 158 : inLen--;
238 :
239 158 : if (d == UINT8_MAX)
240 0 : goto fail;
241 :
242 158 : *out++ = static_cast<uint8_t>((c << 6) | d);
243 : }
244 :
245 23 : return static_cast<uint16_t>(out - outStart);
246 :
247 0 : fail:
248 0 : return UINT16_MAX;
249 : }
250 :
251 23 : uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out)
252 : {
253 23 : return Base64Decode(in, inLen, out, Base64CharToVal);
254 : }
255 :
256 0 : uint16_t Base64URLDecode(const char * in, uint16_t inLen, uint8_t * out)
257 : {
258 0 : return Base64Decode(in, inLen, out, Base64URLCharToVal);
259 : }
260 :
261 0 : uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
262 : {
263 0 : uint32_t outLen = 0;
264 :
265 : // Maximum number of base-64 characters to convert in a single call to Base64Decode.
266 : // Number is the largest multiple of 4 characters that fits in a uint16_t.
267 : enum
268 : {
269 : kMaxConvert = (UINT16_MAX / 4) * 4
270 : };
271 :
272 : while (true)
273 : {
274 0 : uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
275 :
276 0 : uint16_t outChunkLen = Base64Decode(in, inChunkLen, out, charToValFunct);
277 0 : if (outChunkLen == UINT16_MAX)
278 0 : return UINT32_MAX;
279 :
280 0 : inLen -= inChunkLen;
281 0 : outLen += outChunkLen;
282 :
283 0 : if (inLen == 0)
284 0 : break;
285 :
286 0 : in += inChunkLen;
287 0 : out += outChunkLen;
288 0 : }
289 :
290 0 : return outLen;
291 : }
292 :
293 0 : uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out)
294 : {
295 0 : return Base64Decode32(in, inLen, out, Base64CharToVal);
296 : }
297 :
298 : } // namespace chip
299 :
300 : #ifdef TEST
301 :
302 : #include <stdio.h>
303 : #include <string.h>
304 :
305 : void TestBase64(const char * test, bool base64URL = false)
306 : {
307 : uint8_t buf[256];
308 : char buf2[256];
309 : uint16_t len;
310 :
311 : strcpy((char *) buf, test);
312 :
313 : len = (base64URL) ? nl::Base64URLDecode((char *) buf, strlen((char *) buf), buf)
314 : : nl::Base64Decode((char *) buf, strlen((char *) buf), buf);
315 : printf("%s: ", test);
316 : if (len != UINT16_MAX)
317 : {
318 : printf("(%d) ", len);
319 : for (uint16_t i = 0; i < len; i++)
320 : printf("%c", buf[i]);
321 :
322 : len = (base64URL) ? nl::Base64URLEncode(buf, len, buf2) : nl::Base64Encode(buf, len, buf2);
323 : printf(" (%d) ", len);
324 : for (uint16_t i = 0; i < len; i++)
325 : printf("%c", buf2[i]);
326 : }
327 : else
328 : printf("ERROR");
329 : printf("\n");
330 : }
331 :
332 : int main(int argc, char * argv[])
333 : {
334 : TestBase64("");
335 : TestBase64("Zg==");
336 : TestBase64("Zm8=");
337 : TestBase64("Zm9v");
338 : TestBase64("Zm9vYg==");
339 : TestBase64("Zm9vYmE=");
340 : TestBase64("Zm9vYmFy");
341 : TestBase64("QmFzZTY0D+8xMjM0D/8=");
342 :
343 : TestBase64("Zg");
344 : TestBase64("Zm8");
345 : TestBase64("Zm9vYg");
346 : TestBase64("Zm9vYmE");
347 :
348 : TestBase64("QmFzZTY0D-8xMjM0D_8=", true);
349 :
350 : // Error cases
351 : TestBase64("Z");
352 : TestBase64("Z\x019vYmFy");
353 : TestBase64("Zm9vY");
354 : TestBase64("Zm9vY;");
355 : TestBase64("Zm9 vYg");
356 : }
357 :
358 : #endif // TEST
|