Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / lib / support / Base64.cpp
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 static char Base64ValToChar(uint8_t val)
38 {
39     if (val < 26)
40         return static_cast<char>('A' + val);
41     val = static_cast<uint8_t>(val - 26);
42     if (val < 26)
43         return static_cast<char>('a' + val);
44     val = static_cast<uint8_t>(val - 26);
45     if (val < 10)
46         return static_cast<char>('0' + val);
47     if (val == 10)
48         return '+';
49     if (val == 11)
50         return '/';
51     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 static uint8_t Base64CharToVal(uint8_t c)
56 {
57     if (c == 43)
58         return 62;
59     if (c == 47)
60         return 63;
61     // NOTE: c < 48 will fall through to return UINT8_MAX below.
62     c = static_cast<uint8_t>(c - 48);
63     if (c < 10)
64         return static_cast<uint8_t>(c + 52);
65     // NOTE: c < 17 here will fall through to return UINT8_MAX below.
66     c = static_cast<uint8_t>(c - 17);
67     if (c < 26)
68         return c;
69     // NOTE: c < 32 here will fall through to return UINT8_MAX below.
70     c = static_cast<uint8_t>(c - 32);
71     if (c < 26)
72         return static_cast<uint8_t>(c + 26);
73     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 static char Base64URLValToChar(uint8_t val)
79 {
80     if (val < 26)
81         return static_cast<char>('A' + val);
82     val = static_cast<uint8_t>(val - 26);
83     if (val < 26)
84         return static_cast<char>('a' + val);
85     val = static_cast<uint8_t>(val - 26);
86     if (val < 10)
87         return static_cast<char>('0' + val);
88     if (val == 10)
89         return '-';
90     if (val == 11)
91         return '_';
92     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 static uint8_t Base64URLCharToVal(uint8_t c)
97 {
98     if (c == 45)
99         return 62;
100     if (c == 95)
101         return 63;
102     // NOTE: c < 48 will fall through to return UINT8_MAX below.
103     c = static_cast<uint8_t>(c - 48);
104     if (c < 10)
105         return static_cast<uint8_t>(c + 52);
106     // NOTE: c < 17 here will fall through to return UINT8_MAX below.
107     c = static_cast<uint8_t>(c - 17);
108     if (c < 26)
109         return c;
110     // NOTE: c < 32 here will fall through to return UINT8_MAX below.
111     c = static_cast<uint8_t>(c - 32);
112     if (c < 26)
113         return static_cast<uint8_t>(c + 26);
114     return UINT8_MAX;
115 }
116
117 uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
118 {
119     char * outStart = out;
120
121     while (inLen > 0)
122     {
123         uint8_t val1, val2, val3, val4;
124
125         val1 = static_cast<uint8_t>(*in >> 2);
126         val2 = (*in << 4) & 0x3F;
127         in++;
128         inLen--;
129         if (inLen > 0)
130         {
131             val2 = static_cast<uint8_t>(val2 | *in >> 4);
132             val3 = (*in << 2) & 0x3F;
133             in++;
134             inLen--;
135             if (inLen > 0)
136             {
137                 val3 = static_cast<uint8_t>(val3 | *in >> 6);
138                 val4 = *in & 0x3F;
139                 in++;
140                 inLen--;
141             }
142             else
143                 val4 = UINT8_MAX;
144         }
145         else
146             val3 = val4 = UINT8_MAX;
147
148         *out++ = valToCharFunct(val1);
149         *out++ = valToCharFunct(val2);
150         *out++ = valToCharFunct(val3);
151         *out++ = valToCharFunct(val4);
152     }
153
154     return static_cast<uint16_t>(out - outStart);
155 }
156
157 uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out)
158 {
159     return Base64Encode(in, inLen, out, Base64ValToChar);
160 }
161
162 uint16_t Base64URLEncode(const uint8_t * in, uint16_t inLen, char * out)
163 {
164     return Base64Encode(in, inLen, out, Base64URLValToChar);
165 }
166
167 uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
168 {
169     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         uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
182
183         uint16_t outChunkLen = Base64Encode(in, inChunkLen, out, valToCharFunct);
184
185         inLen -= inChunkLen;
186         outLen += outChunkLen;
187
188         if (inLen == 0)
189             break;
190
191         in += inChunkLen;
192         out += outChunkLen;
193     }
194
195     return outLen;
196 }
197
198 uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out)
199 {
200     return Base64Encode32(in, inLen, out, Base64ValToChar);
201 }
202
203 uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
204 {
205     uint8_t * outStart = out;
206
207     // isgraph() returns false for space and ctrl chars
208     while (inLen > 0 && isgraph(*in))
209     {
210         if (inLen == 1)
211             goto fail;
212
213         uint8_t a = charToValFunct(static_cast<uint8_t>(*in++));
214         uint8_t b = charToValFunct(static_cast<uint8_t>(*in++));
215         inLen     = static_cast<uint16_t>(inLen - 2);
216
217         if (a == UINT8_MAX || b == UINT8_MAX)
218             goto fail;
219
220         *out++ = static_cast<uint8_t>((a << 2) | (b >> 4));
221
222         if (inLen == 0 || *in == '=')
223             break;
224
225         uint8_t c = charToValFunct(static_cast<uint8_t>(*in++));
226         inLen--;
227
228         if (c == UINT8_MAX)
229             goto fail;
230
231         *out++ = static_cast<uint8_t>((b << 4) | (c >> 2));
232
233         if (inLen == 0 || *in == '=')
234             break;
235
236         uint8_t d = charToValFunct(static_cast<uint8_t>(*in++));
237         inLen--;
238
239         if (d == UINT8_MAX)
240             goto fail;
241
242         *out++ = static_cast<uint8_t>((c << 6) | d);
243     }
244
245     return static_cast<uint16_t>(out - outStart);
246
247 fail:
248     return UINT16_MAX;
249 }
250
251 uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out)
252 {
253     return Base64Decode(in, inLen, out, Base64CharToVal);
254 }
255
256 uint16_t Base64URLDecode(const char * in, uint16_t inLen, uint8_t * out)
257 {
258     return Base64Decode(in, inLen, out, Base64URLCharToVal);
259 }
260
261 uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
262 {
263     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         uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
275
276         uint16_t outChunkLen = Base64Decode(in, inChunkLen, out, charToValFunct);
277         if (outChunkLen == UINT16_MAX)
278             return UINT32_MAX;
279
280         inLen -= inChunkLen;
281         outLen += outChunkLen;
282
283         if (inLen == 0)
284             break;
285
286         in += inChunkLen;
287         out += outChunkLen;
288     }
289
290     return outLen;
291 }
292
293 uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out)
294 {
295     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