]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e4e73a63 LP |
2 | |
3 | #include <ctype.h> | |
11c3a366 TA |
4 | #include <errno.h> |
5 | #include <stdint.h> | |
6 | #include <stdlib.h> | |
e4e73a63 | 7 | |
b5efdb8a | 8 | #include "alloc-util.h" |
e4e73a63 | 9 | #include "hexdecoct.h" |
11c3a366 | 10 | #include "macro.h" |
0a970718 | 11 | #include "memory-util.h" |
7bf7ce28 | 12 | #include "string-util.h" |
e4e73a63 LP |
13 | |
14 | char octchar(int x) { | |
15 | return '0' + (x & 7); | |
16 | } | |
17 | ||
18 | int unoctchar(char c) { | |
19 | ||
20 | if (c >= '0' && c <= '7') | |
21 | return c - '0'; | |
22 | ||
23 | return -EINVAL; | |
24 | } | |
25 | ||
26 | char decchar(int x) { | |
27 | return '0' + (x % 10); | |
28 | } | |
29 | ||
30 | int undecchar(char c) { | |
31 | ||
32 | if (c >= '0' && c <= '9') | |
33 | return c - '0'; | |
34 | ||
35 | return -EINVAL; | |
36 | } | |
37 | ||
38 | char hexchar(int x) { | |
39 | static const char table[16] = "0123456789abcdef"; | |
40 | ||
41 | return table[x & 15]; | |
42 | } | |
43 | ||
44 | int unhexchar(char c) { | |
45 | ||
46 | if (c >= '0' && c <= '9') | |
47 | return c - '0'; | |
48 | ||
49 | if (c >= 'a' && c <= 'f') | |
50 | return c - 'a' + 10; | |
51 | ||
52 | if (c >= 'A' && c <= 'F') | |
53 | return c - 'A' + 10; | |
54 | ||
55 | return -EINVAL; | |
56 | } | |
57 | ||
58 | char *hexmem(const void *p, size_t l) { | |
e4e73a63 | 59 | const uint8_t *x; |
9ff233dc | 60 | char *r, *z; |
e4e73a63 | 61 | |
9ff233dc | 62 | z = r = new(char, l * 2 + 1); |
e4e73a63 LP |
63 | if (!r) |
64 | return NULL; | |
65 | ||
66 | for (x = p; x < (const uint8_t*) p + l; x++) { | |
67 | *(z++) = hexchar(*x >> 4); | |
68 | *(z++) = hexchar(*x & 15); | |
69 | } | |
70 | ||
71 | *z = 0; | |
72 | return r; | |
73 | } | |
74 | ||
4937b81a YW |
75 | static int unhex_next(const char **p, size_t *l) { |
76 | int r; | |
77 | ||
78 | assert(p); | |
79 | assert(l); | |
80 | ||
81 | /* Find the next non-whitespace character, and decode it. We | |
f21f31b2 | 82 | * greedily skip all preceding and all following whitespace. */ |
4937b81a YW |
83 | |
84 | for (;;) { | |
85 | if (*l == 0) | |
86 | return -EPIPE; | |
87 | ||
88 | if (!strchr(WHITESPACE, **p)) | |
89 | break; | |
90 | ||
91 | /* Skip leading whitespace */ | |
92 | (*p)++, (*l)--; | |
93 | } | |
94 | ||
95 | r = unhexchar(**p); | |
96 | if (r < 0) | |
97 | return r; | |
98 | ||
99 | for (;;) { | |
100 | (*p)++, (*l)--; | |
101 | ||
102 | if (*l == 0 || !strchr(WHITESPACE, **p)) | |
103 | break; | |
104 | ||
105 | /* Skip following whitespace */ | |
106 | } | |
107 | ||
108 | return r; | |
109 | } | |
110 | ||
7088befb | 111 | int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) { |
4937b81a | 112 | _cleanup_free_ uint8_t *buf = NULL; |
7088befb | 113 | size_t buf_size; |
e4e73a63 | 114 | const char *x; |
4937b81a | 115 | uint8_t *z; |
7088befb | 116 | int r; |
e4e73a63 | 117 | |
4937b81a YW |
118 | assert(ret); |
119 | assert(ret_len); | |
ae6e414a LP |
120 | assert(p || l == 0); |
121 | ||
f5fbe71d | 122 | if (l == SIZE_MAX) |
ae6e414a | 123 | l = strlen(p); |
e4e73a63 | 124 | |
4937b81a | 125 | /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */ |
7088befb YW |
126 | buf_size = (l + 1) / 2 + 1; |
127 | buf = malloc(buf_size); | |
4937b81a | 128 | if (!buf) |
e4e73a63 LP |
129 | return -ENOMEM; |
130 | ||
4937b81a | 131 | for (x = p, z = buf;;) { |
e4e73a63 LP |
132 | int a, b; |
133 | ||
4937b81a YW |
134 | a = unhex_next(&x, &l); |
135 | if (a == -EPIPE) /* End of string */ | |
136 | break; | |
7088befb YW |
137 | if (a < 0) { |
138 | r = a; | |
139 | goto on_failure; | |
140 | } | |
a93c4776 | 141 | |
4937b81a | 142 | b = unhex_next(&x, &l); |
7088befb YW |
143 | if (b < 0) { |
144 | r = b; | |
145 | goto on_failure; | |
146 | } | |
e4e73a63 LP |
147 | |
148 | *(z++) = (uint8_t) a << 4 | (uint8_t) b; | |
149 | } | |
150 | ||
151 | *z = 0; | |
152 | ||
4937b81a YW |
153 | *ret_len = (size_t) (z - buf); |
154 | *ret = TAKE_PTR(buf); | |
e4e73a63 LP |
155 | |
156 | return 0; | |
7088befb YW |
157 | |
158 | on_failure: | |
159 | if (secure) | |
160 | explicit_bzero_safe(buf, buf_size); | |
161 | ||
162 | return r; | |
e4e73a63 LP |
163 | } |
164 | ||
165 | /* https://tools.ietf.org/html/rfc4648#section-6 | |
166 | * Notice that base32hex differs from base32 in the alphabet it uses. | |
167 | * The distinction is that the base32hex representation preserves the | |
168 | * order of the underlying data when compared as bytestrings, this is | |
169 | * useful when representing NSEC3 hashes, as one can then verify the | |
170 | * order of hashes directly from their representation. */ | |
171 | char base32hexchar(int x) { | |
172 | static const char table[32] = "0123456789" | |
173 | "ABCDEFGHIJKLMNOPQRSTUV"; | |
174 | ||
175 | return table[x & 31]; | |
176 | } | |
177 | ||
178 | int unbase32hexchar(char c) { | |
179 | unsigned offset; | |
180 | ||
181 | if (c >= '0' && c <= '9') | |
182 | return c - '0'; | |
183 | ||
184 | offset = '9' - '0' + 1; | |
185 | ||
186 | if (c >= 'A' && c <= 'V') | |
187 | return c - 'A' + offset; | |
188 | ||
189 | return -EINVAL; | |
190 | } | |
191 | ||
192 | char *base32hexmem(const void *p, size_t l, bool padding) { | |
193 | char *r, *z; | |
194 | const uint8_t *x; | |
195 | size_t len; | |
196 | ||
ae6e414a LP |
197 | assert(p || l == 0); |
198 | ||
e4e73a63 LP |
199 | if (padding) |
200 | /* five input bytes makes eight output bytes, padding is added so we must round up */ | |
201 | len = 8 * (l + 4) / 5; | |
202 | else { | |
203 | /* same, but round down as there is no padding */ | |
204 | len = 8 * l / 5; | |
205 | ||
206 | switch (l % 5) { | |
207 | case 4: | |
208 | len += 7; | |
209 | break; | |
210 | case 3: | |
211 | len += 5; | |
212 | break; | |
213 | case 2: | |
214 | len += 4; | |
215 | break; | |
216 | case 1: | |
217 | len += 2; | |
218 | break; | |
219 | } | |
220 | } | |
221 | ||
222 | z = r = malloc(len + 1); | |
223 | if (!r) | |
224 | return NULL; | |
225 | ||
226 | for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { | |
227 | /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ | |
aba13524 | 228 | * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ |
e4e73a63 LP |
229 | *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ |
230 | *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ | |
231 | *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ | |
232 | *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ | |
233 | *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ | |
234 | *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ | |
235 | *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */ | |
236 | *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */ | |
237 | } | |
238 | ||
239 | switch (l % 5) { | |
240 | case 4: | |
241 | *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ | |
242 | *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ | |
243 | *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ | |
244 | *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ | |
245 | *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ | |
246 | *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ | |
247 | *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */ | |
248 | if (padding) | |
249 | *(z++) = '='; | |
250 | ||
251 | break; | |
252 | ||
253 | case 3: | |
254 | *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ | |
255 | *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ | |
256 | *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ | |
257 | *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ | |
258 | *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */ | |
259 | if (padding) { | |
260 | *(z++) = '='; | |
261 | *(z++) = '='; | |
262 | *(z++) = '='; | |
263 | } | |
264 | ||
265 | break; | |
266 | ||
267 | case 2: | |
268 | *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ | |
269 | *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ | |
270 | *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ | |
271 | *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */ | |
272 | if (padding) { | |
273 | *(z++) = '='; | |
274 | *(z++) = '='; | |
275 | *(z++) = '='; | |
276 | *(z++) = '='; | |
277 | } | |
278 | ||
279 | break; | |
280 | ||
281 | case 1: | |
282 | *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ | |
283 | *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */ | |
284 | if (padding) { | |
285 | *(z++) = '='; | |
286 | *(z++) = '='; | |
287 | *(z++) = '='; | |
288 | *(z++) = '='; | |
289 | *(z++) = '='; | |
290 | *(z++) = '='; | |
291 | } | |
292 | ||
293 | break; | |
294 | } | |
295 | ||
296 | *z = 0; | |
297 | return r; | |
298 | } | |
299 | ||
300 | int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { | |
301 | _cleanup_free_ uint8_t *r = NULL; | |
302 | int a, b, c, d, e, f, g, h; | |
303 | uint8_t *z; | |
304 | const char *x; | |
305 | size_t len; | |
306 | unsigned pad = 0; | |
307 | ||
ae6e414a LP |
308 | assert(p || l == 0); |
309 | assert(mem); | |
310 | assert(_len); | |
311 | ||
f5fbe71d | 312 | if (l == SIZE_MAX) |
ae6e414a | 313 | l = strlen(p); |
e4e73a63 LP |
314 | |
315 | /* padding ensures any base32hex input has input divisible by 8 */ | |
316 | if (padding && l % 8 != 0) | |
317 | return -EINVAL; | |
318 | ||
319 | if (padding) { | |
320 | /* strip the padding */ | |
321 | while (l > 0 && p[l - 1] == '=' && pad < 7) { | |
313cefa1 VC |
322 | pad++; |
323 | l--; | |
e4e73a63 LP |
324 | } |
325 | } | |
326 | ||
327 | /* a group of eight input bytes needs five output bytes, in case of | |
aba13524 | 328 | * padding we need to add some extra bytes */ |
e4e73a63 LP |
329 | len = (l / 8) * 5; |
330 | ||
331 | switch (l % 8) { | |
332 | case 7: | |
333 | len += 4; | |
334 | break; | |
335 | case 5: | |
336 | len += 3; | |
337 | break; | |
338 | case 4: | |
339 | len += 2; | |
340 | break; | |
341 | case 2: | |
342 | len += 1; | |
343 | break; | |
344 | case 0: | |
345 | break; | |
346 | default: | |
347 | return -EINVAL; | |
348 | } | |
349 | ||
350 | z = r = malloc(len + 1); | |
351 | if (!r) | |
352 | return -ENOMEM; | |
353 | ||
354 | for (x = p; x < p + (l / 8) * 8; x += 8) { | |
355 | /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW | |
aba13524 | 356 | * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ |
e4e73a63 LP |
357 | a = unbase32hexchar(x[0]); |
358 | if (a < 0) | |
359 | return -EINVAL; | |
360 | ||
361 | b = unbase32hexchar(x[1]); | |
362 | if (b < 0) | |
363 | return -EINVAL; | |
364 | ||
365 | c = unbase32hexchar(x[2]); | |
366 | if (c < 0) | |
367 | return -EINVAL; | |
368 | ||
369 | d = unbase32hexchar(x[3]); | |
370 | if (d < 0) | |
371 | return -EINVAL; | |
372 | ||
373 | e = unbase32hexchar(x[4]); | |
374 | if (e < 0) | |
375 | return -EINVAL; | |
376 | ||
377 | f = unbase32hexchar(x[5]); | |
378 | if (f < 0) | |
379 | return -EINVAL; | |
380 | ||
381 | g = unbase32hexchar(x[6]); | |
382 | if (g < 0) | |
383 | return -EINVAL; | |
384 | ||
385 | h = unbase32hexchar(x[7]); | |
386 | if (h < 0) | |
387 | return -EINVAL; | |
388 | ||
389 | *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ | |
390 | *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ | |
391 | *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ | |
392 | *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ | |
393 | *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */ | |
394 | } | |
395 | ||
396 | switch (l % 8) { | |
397 | case 7: | |
398 | a = unbase32hexchar(x[0]); | |
399 | if (a < 0) | |
400 | return -EINVAL; | |
401 | ||
402 | b = unbase32hexchar(x[1]); | |
403 | if (b < 0) | |
404 | return -EINVAL; | |
405 | ||
406 | c = unbase32hexchar(x[2]); | |
407 | if (c < 0) | |
408 | return -EINVAL; | |
409 | ||
410 | d = unbase32hexchar(x[3]); | |
411 | if (d < 0) | |
412 | return -EINVAL; | |
413 | ||
414 | e = unbase32hexchar(x[4]); | |
415 | if (e < 0) | |
416 | return -EINVAL; | |
417 | ||
418 | f = unbase32hexchar(x[5]); | |
419 | if (f < 0) | |
420 | return -EINVAL; | |
421 | ||
422 | g = unbase32hexchar(x[6]); | |
423 | if (g < 0) | |
424 | return -EINVAL; | |
425 | ||
426 | /* g == 000VV000 */ | |
427 | if (g & 7) | |
428 | return -EINVAL; | |
429 | ||
430 | *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ | |
431 | *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ | |
432 | *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ | |
433 | *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ | |
434 | ||
435 | break; | |
436 | case 5: | |
437 | a = unbase32hexchar(x[0]); | |
438 | if (a < 0) | |
439 | return -EINVAL; | |
440 | ||
441 | b = unbase32hexchar(x[1]); | |
442 | if (b < 0) | |
443 | return -EINVAL; | |
444 | ||
445 | c = unbase32hexchar(x[2]); | |
446 | if (c < 0) | |
447 | return -EINVAL; | |
448 | ||
449 | d = unbase32hexchar(x[3]); | |
450 | if (d < 0) | |
451 | return -EINVAL; | |
452 | ||
453 | e = unbase32hexchar(x[4]); | |
454 | if (e < 0) | |
455 | return -EINVAL; | |
456 | ||
457 | /* e == 000SSSS0 */ | |
458 | if (e & 1) | |
459 | return -EINVAL; | |
460 | ||
461 | *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ | |
462 | *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ | |
463 | *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ | |
464 | ||
465 | break; | |
466 | case 4: | |
467 | a = unbase32hexchar(x[0]); | |
468 | if (a < 0) | |
469 | return -EINVAL; | |
470 | ||
471 | b = unbase32hexchar(x[1]); | |
472 | if (b < 0) | |
473 | return -EINVAL; | |
474 | ||
475 | c = unbase32hexchar(x[2]); | |
476 | if (c < 0) | |
477 | return -EINVAL; | |
478 | ||
479 | d = unbase32hexchar(x[3]); | |
480 | if (d < 0) | |
481 | return -EINVAL; | |
482 | ||
483 | /* d == 000W0000 */ | |
484 | if (d & 15) | |
485 | return -EINVAL; | |
486 | ||
487 | *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ | |
488 | *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ | |
489 | ||
490 | break; | |
491 | case 2: | |
492 | a = unbase32hexchar(x[0]); | |
493 | if (a < 0) | |
494 | return -EINVAL; | |
495 | ||
496 | b = unbase32hexchar(x[1]); | |
497 | if (b < 0) | |
498 | return -EINVAL; | |
499 | ||
500 | /* b == 000YYY00 */ | |
501 | if (b & 3) | |
502 | return -EINVAL; | |
503 | ||
504 | *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ | |
505 | ||
506 | break; | |
507 | case 0: | |
508 | break; | |
509 | default: | |
510 | return -EINVAL; | |
511 | } | |
512 | ||
513 | *z = 0; | |
514 | ||
1cc6c93a | 515 | *mem = TAKE_PTR(r); |
e4e73a63 LP |
516 | *_len = len; |
517 | ||
518 | return 0; | |
519 | } | |
520 | ||
521 | /* https://tools.ietf.org/html/rfc4648#section-4 */ | |
522 | char base64char(int x) { | |
523 | static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
524 | "abcdefghijklmnopqrstuvwxyz" | |
525 | "0123456789+/"; | |
526 | return table[x & 63]; | |
527 | } | |
528 | ||
529 | int unbase64char(char c) { | |
530 | unsigned offset; | |
531 | ||
532 | if (c >= 'A' && c <= 'Z') | |
533 | return c - 'A'; | |
534 | ||
535 | offset = 'Z' - 'A' + 1; | |
536 | ||
537 | if (c >= 'a' && c <= 'z') | |
538 | return c - 'a' + offset; | |
539 | ||
540 | offset += 'z' - 'a' + 1; | |
541 | ||
542 | if (c >= '0' && c <= '9') | |
543 | return c - '0' + offset; | |
544 | ||
545 | offset += '9' - '0' + 1; | |
546 | ||
547 | if (c == '+') | |
548 | return offset; | |
549 | ||
313cefa1 | 550 | offset++; |
e4e73a63 LP |
551 | |
552 | if (c == '/') | |
553 | return offset; | |
554 | ||
555 | return -EINVAL; | |
556 | } | |
557 | ||
d7671a3e | 558 | ssize_t base64mem(const void *p, size_t l, char **out) { |
e4e73a63 LP |
559 | char *r, *z; |
560 | const uint8_t *x; | |
561 | ||
ae6e414a LP |
562 | assert(p || l == 0); |
563 | assert(out); | |
564 | ||
e4e73a63 LP |
565 | /* three input bytes makes four output bytes, padding is added so we must round up */ |
566 | z = r = malloc(4 * (l + 2) / 3 + 1); | |
567 | if (!r) | |
d7671a3e | 568 | return -ENOMEM; |
e4e73a63 LP |
569 | |
570 | for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { | |
571 | /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ | |
572 | *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ | |
573 | *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ | |
574 | *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ | |
575 | *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ | |
576 | } | |
577 | ||
578 | switch (l % 3) { | |
579 | case 2: | |
580 | *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ | |
581 | *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ | |
582 | *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ | |
583 | *(z++) = '='; | |
584 | ||
585 | break; | |
586 | case 1: | |
587 | *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ | |
588 | *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ | |
589 | *(z++) = '='; | |
590 | *(z++) = '='; | |
591 | ||
592 | break; | |
593 | } | |
594 | ||
595 | *z = 0; | |
d7671a3e ZJS |
596 | *out = r; |
597 | return z - r; | |
598 | } | |
599 | ||
ae6e414a LP |
600 | static int base64_append_width( |
601 | char **prefix, int plen, | |
0136b1d1 | 602 | char sep, int indent, |
ae6e414a LP |
603 | const void *p, size_t l, |
604 | int width) { | |
d7671a3e ZJS |
605 | |
606 | _cleanup_free_ char *x = NULL; | |
607 | char *t, *s; | |
0136b1d1 | 608 | ssize_t len, avail, line, lines; |
d7671a3e ZJS |
609 | |
610 | len = base64mem(p, l, &x); | |
611 | if (len <= 0) | |
612 | return len; | |
613 | ||
be6b0c21 | 614 | lines = DIV_ROUND_UP(len, width); |
d7671a3e | 615 | |
0136b1d1 ZJS |
616 | if ((size_t) plen >= SSIZE_MAX - 1 - 1 || |
617 | lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1)) | |
3d6c1844 ZJS |
618 | return -ENOMEM; |
619 | ||
0136b1d1 | 620 | t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines); |
d7671a3e ZJS |
621 | if (!t) |
622 | return -ENOMEM; | |
623 | ||
0136b1d1 | 624 | t[plen] = sep; |
d7671a3e | 625 | |
0136b1d1 | 626 | for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) { |
d7671a3e ZJS |
627 | int act = MIN(width, avail); |
628 | ||
0136b1d1 | 629 | if (line > 0 || sep == '\n') { |
d7671a3e ZJS |
630 | memset(s, ' ', indent); |
631 | s += indent; | |
632 | } | |
633 | ||
634 | memcpy(s, x + width * line, act); | |
635 | s += act; | |
636 | *(s++) = line < lines - 1 ? '\n' : '\0'; | |
637 | avail -= act; | |
638 | } | |
639 | assert(avail == 0); | |
640 | ||
641 | *prefix = t; | |
642 | return 0; | |
e4e73a63 LP |
643 | } |
644 | ||
ae6e414a LP |
645 | int base64_append( |
646 | char **prefix, int plen, | |
647 | const void *p, size_t l, | |
648 | int indent, int width) { | |
649 | ||
d7671a3e ZJS |
650 | if (plen > width / 2 || plen + indent > width) |
651 | /* leave indent on the left, keep last column free */ | |
0136b1d1 | 652 | return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1); |
d7671a3e ZJS |
653 | else |
654 | /* leave plen on the left, keep last column free */ | |
0136b1d1 | 655 | return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1); |
ae6e414a | 656 | } |
d7671a3e | 657 | |
081f36d8 LP |
658 | static int unbase64_next(const char **p, size_t *l) { |
659 | int ret; | |
660 | ||
661 | assert(p); | |
662 | assert(l); | |
663 | ||
664 | /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We | |
f21f31b2 | 665 | * greedily skip all preceding and all following whitespace. */ |
081f36d8 LP |
666 | |
667 | for (;;) { | |
668 | if (*l == 0) | |
669 | return -EPIPE; | |
670 | ||
671 | if (!strchr(WHITESPACE, **p)) | |
672 | break; | |
673 | ||
674 | /* Skip leading whitespace */ | |
675 | (*p)++, (*l)--; | |
676 | } | |
677 | ||
678 | if (**p == '=') | |
679 | ret = INT_MAX; /* return padding as INT_MAX */ | |
680 | else { | |
681 | ret = unbase64char(**p); | |
682 | if (ret < 0) | |
683 | return ret; | |
684 | } | |
685 | ||
686 | for (;;) { | |
687 | (*p)++, (*l)--; | |
688 | ||
689 | if (*l == 0) | |
690 | break; | |
691 | if (!strchr(WHITESPACE, **p)) | |
692 | break; | |
693 | ||
694 | /* Skip following whitespace */ | |
695 | } | |
696 | ||
697 | return ret; | |
698 | } | |
699 | ||
2432d09c | 700 | int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) { |
081f36d8 | 701 | _cleanup_free_ uint8_t *buf = NULL; |
e4e73a63 | 702 | const char *x; |
081f36d8 | 703 | uint8_t *z; |
e4e73a63 | 704 | size_t len; |
2432d09c | 705 | int r; |
e4e73a63 | 706 | |
ae6e414a | 707 | assert(p || l == 0); |
081f36d8 LP |
708 | assert(ret); |
709 | assert(ret_size); | |
ae6e414a | 710 | |
f5fbe71d | 711 | if (l == SIZE_MAX) |
ae6e414a | 712 | l = strlen(p); |
e4e73a63 | 713 | |
081f36d8 | 714 | /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra |
aba13524 | 715 | * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */ |
081f36d8 | 716 | len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); |
e4e73a63 | 717 | |
081f36d8 LP |
718 | buf = malloc(len + 1); |
719 | if (!buf) | |
e4e73a63 LP |
720 | return -ENOMEM; |
721 | ||
081f36d8 LP |
722 | for (x = p, z = buf;;) { |
723 | int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ | |
724 | ||
725 | a = unbase64_next(&x, &l); | |
726 | if (a == -EPIPE) /* End of string */ | |
727 | break; | |
2432d09c YW |
728 | if (a < 0) { |
729 | r = a; | |
730 | goto on_failure; | |
731 | } | |
732 | if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ | |
733 | r = -EINVAL; | |
734 | goto on_failure; | |
735 | } | |
e4e73a63 | 736 | |
081f36d8 | 737 | b = unbase64_next(&x, &l); |
2432d09c YW |
738 | if (b < 0) { |
739 | r = b; | |
740 | goto on_failure; | |
741 | } | |
742 | if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ | |
743 | r = -EINVAL; | |
744 | goto on_failure; | |
745 | } | |
e4e73a63 | 746 | |
081f36d8 | 747 | c = unbase64_next(&x, &l); |
2432d09c YW |
748 | if (c < 0) { |
749 | r = c; | |
750 | goto on_failure; | |
751 | } | |
e4e73a63 | 752 | |
081f36d8 | 753 | d = unbase64_next(&x, &l); |
2432d09c YW |
754 | if (d < 0) { |
755 | r = d; | |
756 | goto on_failure; | |
757 | } | |
ae6e414a | 758 | |
081f36d8 | 759 | if (c == INT_MAX) { /* Padding at the third character */ |
e4e73a63 | 760 | |
2432d09c YW |
761 | if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ |
762 | r = -EINVAL; | |
763 | goto on_failure; | |
764 | } | |
e4e73a63 | 765 | |
081f36d8 | 766 | /* b == 00YY0000 */ |
2432d09c YW |
767 | if (b & 15) { |
768 | r = -EINVAL; | |
769 | goto on_failure; | |
770 | } | |
e4e73a63 | 771 | |
2432d09c YW |
772 | if (l > 0) { /* Trailing rubbish? */ |
773 | r = -ENAMETOOLONG; | |
774 | goto on_failure; | |
775 | } | |
e4e73a63 | 776 | |
081f36d8 LP |
777 | *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ |
778 | break; | |
779 | } | |
e4e73a63 | 780 | |
081f36d8 LP |
781 | if (d == INT_MAX) { |
782 | /* c == 00ZZZZ00 */ | |
2432d09c YW |
783 | if (c & 3) { |
784 | r = -EINVAL; | |
785 | goto on_failure; | |
786 | } | |
e4e73a63 | 787 | |
2432d09c YW |
788 | if (l > 0) { /* Trailing rubbish? */ |
789 | r = -ENAMETOOLONG; | |
790 | goto on_failure; | |
791 | } | |
e4e73a63 | 792 | |
081f36d8 LP |
793 | *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ |
794 | *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ | |
795 | break; | |
796 | } | |
e4e73a63 | 797 | |
081f36d8 LP |
798 | *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ |
799 | *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ | |
800 | *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ | |
e4e73a63 LP |
801 | } |
802 | ||
803 | *z = 0; | |
804 | ||
9ec578a3 | 805 | *ret_size = (size_t) (z - buf); |
1cc6c93a | 806 | *ret = TAKE_PTR(buf); |
e4e73a63 LP |
807 | |
808 | return 0; | |
2432d09c YW |
809 | |
810 | on_failure: | |
811 | if (secure) | |
812 | explicit_bzero_safe(buf, len); | |
813 | ||
814 | return r; | |
e4e73a63 LP |
815 | } |
816 | ||
817 | void hexdump(FILE *f, const void *p, size_t s) { | |
818 | const uint8_t *b = p; | |
819 | unsigned n = 0; | |
820 | ||
a3ab8f2a LP |
821 | assert(b || s == 0); |
822 | ||
823 | if (!f) | |
824 | f = stdout; | |
e4e73a63 LP |
825 | |
826 | while (s > 0) { | |
827 | size_t i; | |
828 | ||
829 | fprintf(f, "%04x ", n); | |
830 | ||
831 | for (i = 0; i < 16; i++) { | |
832 | ||
833 | if (i >= s) | |
834 | fputs(" ", f); | |
835 | else | |
836 | fprintf(f, "%02x ", b[i]); | |
837 | ||
838 | if (i == 7) | |
839 | fputc(' ', f); | |
840 | } | |
841 | ||
842 | fputc(' ', f); | |
843 | ||
844 | for (i = 0; i < 16; i++) { | |
845 | ||
846 | if (i >= s) | |
847 | fputc(' ', f); | |
848 | else | |
849 | fputc(isprint(b[i]) ? (char) b[i] : '.', f); | |
850 | } | |
851 | ||
852 | fputc('\n', f); | |
853 | ||
854 | if (s < 16) | |
855 | break; | |
856 | ||
857 | n += 16; | |
858 | b += 16; | |
859 | s -= 16; | |
860 | } | |
861 | } |