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 : #include "Base64.h"
26 :
27 : #include <ctype.h>
28 : #include <stdint.h>
29 :
30 : namespace chip {
31 :
32 : // Convert a value in the range 0..63 to its equivalent base64 character.
33 : // Return '=' for any value >= 64.
34 19440 : static char Base64ValToChar(uint8_t val)
35 : {
36 19440 : if (val < 26)
37 8545 : return static_cast<char>('A' + val);
38 10895 : val = static_cast<uint8_t>(val - 26);
39 10895 : if (val < 26)
40 6639 : return static_cast<char>('a' + val);
41 4256 : val = static_cast<uint8_t>(val - 26);
42 4256 : if (val < 10)
43 2316 : return static_cast<char>('0' + val);
44 1940 : if (val == 10)
45 277 : return '+';
46 1663 : if (val == 11)
47 248 : return '/';
48 1415 : return '=';
49 : }
50 :
51 : // Convert a base64 character to a value in the range 0..63, or UINT8_MAX if the character is invalid.
52 5600 : static uint8_t Base64CharToVal(uint8_t c)
53 : {
54 5600 : if (c == 43)
55 40 : return 62;
56 5560 : if (c == 47)
57 49 : return 63;
58 : // NOTE: c < 48 will fall through to return UINT8_MAX below.
59 5511 : c = static_cast<uint8_t>(c - 48);
60 5511 : if (c < 10)
61 534 : return static_cast<uint8_t>(c + 52);
62 : // NOTE: c < 17 here will fall through to return UINT8_MAX below.
63 4977 : c = static_cast<uint8_t>(c - 17);
64 4977 : if (c < 26)
65 3273 : return c;
66 : // NOTE: c < 32 here will fall through to return UINT8_MAX below.
67 1704 : c = static_cast<uint8_t>(c - 32);
68 1704 : if (c < 26)
69 1703 : return static_cast<uint8_t>(c + 26);
70 1 : return UINT8_MAX;
71 : }
72 :
73 : // Convert a value in the range 0..63 to its equivalent base64url character (see RFC-4648, section 5).
74 : // Return '=' for any value >= 64.
75 0 : static char Base64URLValToChar(uint8_t val)
76 : {
77 0 : if (val < 26)
78 0 : return static_cast<char>('A' + val);
79 0 : val = static_cast<uint8_t>(val - 26);
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 < 10)
84 0 : return static_cast<char>('0' + val);
85 0 : if (val == 10)
86 0 : return '-';
87 0 : if (val == 11)
88 0 : return '_';
89 0 : return '=';
90 : }
91 :
92 : // Convert a base64url character to a value in the range 0..63, or UINT8_MAX if the character is invalid.
93 0 : static uint8_t Base64URLCharToVal(uint8_t c)
94 : {
95 0 : if (c == 45)
96 0 : return 62;
97 0 : if (c == 95)
98 0 : return 63;
99 : // NOTE: c < 48 will fall through to return UINT8_MAX below.
100 0 : c = static_cast<uint8_t>(c - 48);
101 0 : if (c < 10)
102 0 : return static_cast<uint8_t>(c + 52);
103 : // NOTE: c < 17 here will fall through to return UINT8_MAX below.
104 0 : c = static_cast<uint8_t>(c - 17);
105 0 : if (c < 26)
106 0 : return c;
107 : // NOTE: c < 32 here will fall through to return UINT8_MAX below.
108 0 : c = static_cast<uint8_t>(c - 32);
109 0 : if (c < 26)
110 0 : return static_cast<uint8_t>(c + 26);
111 0 : return UINT8_MAX;
112 : }
113 :
114 736 : uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
115 : {
116 736 : char * outStart = out;
117 :
118 5596 : while (inLen > 0)
119 : {
120 : uint8_t val1, val2, val3, val4;
121 :
122 4860 : val1 = static_cast<uint8_t>(*in >> 2);
123 4860 : val2 = static_cast<uint8_t>((*in << 4) & 0x3F);
124 4860 : in++;
125 4860 : inLen--;
126 4860 : if (inLen > 0)
127 : {
128 4162 : val2 = static_cast<uint8_t>(val2 | *in >> 4);
129 4162 : val3 = static_cast<uint8_t>((*in << 2) & 0x3F);
130 4162 : in++;
131 4162 : inLen--;
132 4162 : if (inLen > 0)
133 : {
134 4143 : val3 = static_cast<uint8_t>(val3 | *in >> 6);
135 4143 : val4 = static_cast<uint8_t>(*in & 0x3F);
136 4143 : in++;
137 4143 : inLen--;
138 : }
139 : else
140 19 : val4 = UINT8_MAX;
141 : }
142 : else
143 698 : val3 = val4 = UINT8_MAX;
144 :
145 4860 : *out++ = valToCharFunct(val1);
146 4860 : *out++ = valToCharFunct(val2);
147 4860 : *out++ = valToCharFunct(val3);
148 4860 : *out++ = valToCharFunct(val4);
149 : }
150 :
151 736 : return static_cast<uint16_t>(out - outStart);
152 : }
153 :
154 736 : uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out)
155 : {
156 736 : return Base64Encode(in, inLen, out, Base64ValToChar);
157 : }
158 :
159 0 : uint16_t Base64URLEncode(const uint8_t * in, uint16_t inLen, char * out)
160 : {
161 0 : return Base64Encode(in, inLen, out, Base64URLValToChar);
162 : }
163 :
164 0 : uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
165 : {
166 0 : uint32_t outLen = 0;
167 :
168 : // Maximum number of input bytes to convert to base-64 in a single call to Base64Encode.
169 : // Number is the largest multiple of 3 bytes where the resulting number of base-64 characters
170 : // fits within a uint16_t.
171 : enum
172 : {
173 : kMaxConvert = (UINT16_MAX / 4) * 3
174 : };
175 :
176 : while (true)
177 : {
178 0 : uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
179 :
180 0 : uint16_t outChunkLen = Base64Encode(in, inChunkLen, out, valToCharFunct);
181 :
182 0 : inLen -= inChunkLen;
183 0 : outLen += outChunkLen;
184 :
185 0 : if (inLen == 0)
186 0 : break;
187 :
188 0 : in += inChunkLen;
189 0 : out += outChunkLen;
190 0 : }
191 :
192 0 : return outLen;
193 : }
194 :
195 0 : uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out)
196 : {
197 0 : return Base64Encode32(in, inLen, out, Base64ValToChar);
198 : }
199 :
200 35 : uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
201 : {
202 35 : uint8_t * outStart = out;
203 :
204 : // isgraph() returns false for space and ctrl chars
205 1418 : while (inLen > 0 && isgraph(*in))
206 : {
207 1411 : if (inLen == 1)
208 0 : goto fail;
209 :
210 1411 : uint8_t a = charToValFunct(static_cast<uint8_t>(*in++));
211 1411 : uint8_t b = charToValFunct(static_cast<uint8_t>(*in++));
212 1411 : inLen = static_cast<uint16_t>(inLen - 2);
213 :
214 1411 : if (a == UINT8_MAX || b == UINT8_MAX)
215 0 : goto fail;
216 :
217 1411 : *out++ = static_cast<uint8_t>((a << 2) | (b >> 4));
218 :
219 1411 : if (inLen == 0 || *in == '=')
220 : break;
221 :
222 1394 : uint8_t c = charToValFunct(static_cast<uint8_t>(*in++));
223 1394 : inLen--;
224 :
225 1394 : if (c == UINT8_MAX)
226 0 : goto fail;
227 :
228 1394 : *out++ = static_cast<uint8_t>((b << 4) | (c >> 2));
229 :
230 1394 : if (inLen == 0 || *in == '=')
231 : break;
232 :
233 1384 : uint8_t d = charToValFunct(static_cast<uint8_t>(*in++));
234 1384 : inLen--;
235 :
236 1384 : if (d == UINT8_MAX)
237 1 : goto fail;
238 :
239 1383 : *out++ = static_cast<uint8_t>((c << 6) | d);
240 : }
241 :
242 34 : return static_cast<uint16_t>(out - outStart);
243 :
244 1 : fail:
245 1 : return UINT16_MAX;
246 : }
247 :
248 35 : uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out)
249 : {
250 35 : return Base64Decode(in, inLen, out, Base64CharToVal);
251 : }
252 :
253 0 : uint16_t Base64URLDecode(const char * in, uint16_t inLen, uint8_t * out)
254 : {
255 0 : return Base64Decode(in, inLen, out, Base64URLCharToVal);
256 : }
257 :
258 0 : uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
259 : {
260 0 : uint32_t outLen = 0;
261 :
262 : // Maximum number of base-64 characters to convert in a single call to Base64Decode.
263 : // Number is the largest multiple of 4 characters that fits in a uint16_t.
264 : enum
265 : {
266 : kMaxConvert = (UINT16_MAX / 4) * 4
267 : };
268 :
269 : while (true)
270 : {
271 0 : uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
272 :
273 0 : uint16_t outChunkLen = Base64Decode(in, inChunkLen, out, charToValFunct);
274 0 : if (outChunkLen == UINT16_MAX)
275 0 : return UINT32_MAX;
276 :
277 0 : inLen -= inChunkLen;
278 0 : outLen += outChunkLen;
279 :
280 0 : if (inLen == 0)
281 0 : break;
282 :
283 0 : in += inChunkLen;
284 0 : out += outChunkLen;
285 0 : }
286 :
287 0 : return outLen;
288 : }
289 :
290 0 : uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out)
291 : {
292 0 : return Base64Decode32(in, inLen, out, Base64CharToVal);
293 : }
294 :
295 : } // namespace chip
296 :
297 : #ifdef TEST
298 :
299 : #include <stdio.h>
300 : #include <string.h>
301 :
302 : void TestBase64(const char * test, bool base64URL = false)
303 : {
304 : uint8_t buf[256];
305 : char buf2[256];
306 : uint16_t len;
307 :
308 : strcpy((char *) buf, test);
309 :
310 : len = (base64URL) ? nl::Base64URLDecode((char *) buf, strlen((char *) buf), buf)
311 : : nl::Base64Decode((char *) buf, strlen((char *) buf), buf);
312 : printf("%s: ", test);
313 : if (len != UINT16_MAX)
314 : {
315 : printf("(%d) ", len);
316 : for (uint16_t i = 0; i < len; i++)
317 : printf("%c", buf[i]);
318 :
319 : len = (base64URL) ? nl::Base64URLEncode(buf, len, buf2) : nl::Base64Encode(buf, len, buf2);
320 : printf(" (%d) ", len);
321 : for (uint16_t i = 0; i < len; i++)
322 : printf("%c", buf2[i]);
323 : }
324 : else
325 : printf("ERROR");
326 : printf("\n");
327 : }
328 :
329 : int main(int argc, char * argv[])
330 : {
331 : TestBase64("");
332 : TestBase64("Zg==");
333 : TestBase64("Zm8=");
334 : TestBase64("Zm9v");
335 : TestBase64("Zm9vYg==");
336 : TestBase64("Zm9vYmE=");
337 : TestBase64("Zm9vYmFy");
338 : TestBase64("QmFzZTY0D+8xMjM0D/8=");
339 :
340 : TestBase64("Zg");
341 : TestBase64("Zm8");
342 : TestBase64("Zm9vYg");
343 : TestBase64("Zm9vYmE");
344 :
345 : TestBase64("QmFzZTY0D-8xMjM0D_8=", true);
346 :
347 : // Error cases
348 : TestBase64("Z");
349 : TestBase64("Z\x019vYmFy");
350 : TestBase64("Zm9vY");
351 : TestBase64("Zm9vY;");
352 : TestBase64("Zm9 vYg");
353 : }
354 :
355 : #endif // TEST
|