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