1 | /* |
2 | * Copyright 2008-2018 Aerospike, Inc. |
3 | * |
4 | * Portions may be licensed to Aerospike, Inc. under one or more contributor |
5 | * license agreements. |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
8 | * use this file except in compliance with the License. You may obtain a copy of |
9 | * the License at http://www.apache.org/licenses/LICENSE-2.0 |
10 | * |
11 | * Unless required by applicable law or agreed to in writing, software |
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
14 | * License for the specific language governing permissions and limitations under |
15 | * the License. |
16 | */ |
17 | #include "citrusleaf/cf_b64.h" |
18 | |
19 | /****************************************************************************** |
20 | * CONSTANTS |
21 | ******************************************************************************/ |
22 | |
23 | static const char base64_chars[] = { |
24 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', |
25 | 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', |
26 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', |
27 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', |
28 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' |
29 | }; |
30 | |
31 | static const bool base64_valid_a[] = { |
32 | /*00*/ /*01*/ /*02*/ /*03*/ /*04*/ /*05*/ /*06*/ /*07*/ /*08*/ /*09*/ /*0A*/ /*0B*/ /*0C*/ /*0D*/ /*0E*/ /*0F*/ |
33 | /*00*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
34 | /*10*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
35 | /*20*/ false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, |
36 | /*30*/ true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, |
37 | /*40*/ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, |
38 | /*50*/ true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, |
39 | /*60*/ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, |
40 | /*70*/ true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, |
41 | /*80*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
42 | /*90*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
43 | /*A0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
44 | /*B0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
45 | /*C0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
46 | /*D0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
47 | /*E0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
48 | /*F0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false |
49 | }; |
50 | |
51 | static const uint8_t base64_decode_a[] = { |
52 | /*00*/ /*01*/ /*02*/ /*03*/ /*04*/ /*05*/ /*06*/ /*07*/ /*08*/ /*09*/ /*0A*/ /*0B*/ /*0C*/ /*0D*/ /*0E*/ /*0F*/ |
53 | /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
54 | /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
55 | /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, |
56 | /*30*/ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, |
57 | /*40*/ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
58 | /*50*/ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, |
59 | /*60*/ 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
60 | /*70*/ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, |
61 | /*80*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
62 | /*90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
63 | /*A0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
64 | /*B0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
65 | /*C0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
66 | /*D0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
67 | /*E0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
68 | /*F0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
69 | }; |
70 | |
71 | // Shortcuts: |
72 | #define EA base64_chars |
73 | #define VA base64_valid_a |
74 | #define DA base64_decode_a |
75 | |
76 | |
77 | /****************************************************************************** |
78 | * FUNCTIONS |
79 | ******************************************************************************/ |
80 | |
81 | // Must have allocated big enough 'out' - e.g. use cf_b64_encoded_len(in_size). |
82 | void |
83 | cf_b64_encode(const uint8_t* in, uint32_t in_size, char* out) |
84 | { |
85 | int i = 0; |
86 | int j = 0; |
87 | |
88 | while (in_size >= 3) { |
89 | uint8_t i0 = in[i]; |
90 | uint8_t i1 = in[i + 1]; |
91 | uint8_t i2 = in[i + 2]; |
92 | |
93 | out[j] = EA[ (i0 & 0xfc) >> 2]; |
94 | out[j + 1] = EA[((i0 & 0x03) << 4) + ((i1 & 0xf0) >> 4)]; |
95 | out[j + 2] = EA[((i1 & 0x0f) << 2) + ((i2 & 0xc0) >> 6)]; |
96 | out[j + 3] = EA[ i2 & 0x3f]; |
97 | |
98 | i += 3; |
99 | j += 4; |
100 | |
101 | in_size -= 3; |
102 | } |
103 | |
104 | if (in_size == 1) { |
105 | uint8_t i0 = in[i]; |
106 | |
107 | out[j] = EA[(i0 & 0xfc) >> 2]; |
108 | out[j + 1] = EA[(i0 & 0x03) << 4]; |
109 | out[j + 2] = '='; |
110 | out[j + 3] = '='; |
111 | } |
112 | else if (in_size == 2) { |
113 | uint8_t i0 = in[i]; |
114 | uint8_t i1 = in[i + 1]; |
115 | |
116 | out[j] = EA[ (i0 & 0xfc) >> 2]; |
117 | out[j + 1] = EA[((i0 & 0x03) << 4) + ((i1 & 0xf0) >> 4)]; |
118 | out[j + 2] = EA[ (i1 & 0x0f) << 2]; |
119 | out[j + 3] = '='; |
120 | } |
121 | } |
122 | |
123 | // Must have allocated big enough 'out' - e.g. use cf_b64_decoded_buf_size(). |
124 | // Note that 'in_len' must be a padded encoded size - an exact multiple of 4. |
125 | // 'out_size' returns the decoded size after padding has been accounted for - |
126 | // i.e. it may be 1 or 2 less than the 'out' buffer size. Also, 'in_len' = 0 is |
127 | // handled, and gives 'out_size' = 0. |
128 | void |
129 | cf_b64_decode(const char* in, uint32_t in_len, uint8_t* out, uint32_t* out_size) |
130 | { |
131 | uint32_t i = 0; |
132 | uint32_t j = 0; |
133 | |
134 | while (i < in_len) { |
135 | uint8_t i0 = (uint8_t)in[i]; |
136 | uint8_t i1 = (uint8_t)in[i + 1]; |
137 | uint8_t i2 = (uint8_t)in[i + 2]; |
138 | uint8_t i3 = (uint8_t)in[i + 3]; |
139 | |
140 | out[j] = (DA[i0] << 2) | (DA[i1] >> 4); |
141 | out[j + 1] = (DA[i1] << 4) | (DA[i2] >> 2); |
142 | out[j + 2] = (DA[i2] << 6) | DA[i3]; |
143 | |
144 | i += 4; |
145 | j += 3; |
146 | } |
147 | |
148 | if (out_size) { |
149 | if (i != 0) { |
150 | if (in[i - 1] == '=') { |
151 | j--; |
152 | } |
153 | |
154 | if (in[i - 2] == '=') { |
155 | j--; |
156 | } |
157 | } |
158 | |
159 | *out_size = j; |
160 | } |
161 | } |
162 | |
163 | // Same as cf_b64_decode() but 'in' and 'out' are the same buffer. |
164 | void |
165 | cf_b64_decode_in_place(uint8_t* in_out, uint32_t in_len, uint32_t* out_size) |
166 | { |
167 | uint32_t i = 0; |
168 | uint32_t j = 0; |
169 | |
170 | uint32_t d = 0; |
171 | |
172 | if (out_size && in_len != 0) { |
173 | if (in_out[in_len - 1] == '=') { |
174 | d++; |
175 | } |
176 | |
177 | if (in_out[in_len - 2] == '=') { |
178 | d++; |
179 | } |
180 | } |
181 | |
182 | while (i < in_len) { |
183 | uint8_t i0 = in_out[i]; |
184 | uint8_t i1 = in_out[i + 1]; |
185 | uint8_t i2 = in_out[i + 2]; |
186 | uint8_t i3 = in_out[i + 3]; |
187 | |
188 | uint8_t b0 = (DA[i0] << 2) | (DA[i1] >> 4); |
189 | uint8_t b1 = (DA[i1] << 4) | (DA[i2] >> 2); |
190 | uint8_t b2 = (DA[i2] << 6) | DA[i3]; |
191 | |
192 | in_out[j] = b0; |
193 | in_out[j + 1] = b1; |
194 | in_out[j + 2] = b2; |
195 | |
196 | i += 4; |
197 | j += 3; |
198 | } |
199 | |
200 | if (out_size) { |
201 | *out_size = j - d; |
202 | } |
203 | } |
204 | |
205 | // Make sure the length is ok and all characters are valid base64 characters, |
206 | // including any '=' padding at the end. Note that 'in_len' = 0 is considered |
207 | // invalid. |
208 | static bool |
209 | is_valid_encoded(const char* in, uint32_t in_len) |
210 | { |
211 | if (! in || in_len == 0 || (in_len & 3) != 0) { |
212 | return false; |
213 | } |
214 | |
215 | const uint8_t* end = (const uint8_t*)in + in_len - 2; |
216 | const uint8_t* read = (const uint8_t*)in; |
217 | |
218 | while (read < end) { |
219 | if (! VA[*read++]) { |
220 | return false; |
221 | } |
222 | } |
223 | |
224 | if (*read == '=') { |
225 | return *(read + 1) == '='; |
226 | } |
227 | |
228 | if (! VA[*read++]) { |
229 | return false; |
230 | } |
231 | |
232 | return *read == '=' || VA[*read]; |
233 | } |
234 | |
235 | // Same as cf_b64_decode() but validates input as ok to decode. |
236 | bool |
237 | cf_b64_validate_and_decode(const char* in, uint32_t in_len, uint8_t* out, |
238 | uint32_t* out_size) |
239 | { |
240 | if (! is_valid_encoded(in, in_len)) { |
241 | return false; |
242 | } |
243 | |
244 | cf_b64_decode(in, in_len, out, out_size); |
245 | |
246 | return true; |
247 | } |
248 | |
249 | // Same as cf_b64_decode_in_place() but validates input as ok to decode. |
250 | bool |
251 | cf_b64_validate_and_decode_in_place(uint8_t* in_out, uint32_t in_len, |
252 | uint32_t* out_size) |
253 | { |
254 | if (! is_valid_encoded((const char*)in_out, in_len)) { |
255 | return false; |
256 | } |
257 | |
258 | cf_b64_decode_in_place(in_out, in_len, out_size); |
259 | |
260 | return true; |
261 | } |
262 | |