diff options
Diffstat (limited to 'gl/base64.c')
| -rw-r--r-- | gl/base64.c | 126 |
1 files changed, 75 insertions, 51 deletions
diff --git a/gl/base64.c b/gl/base64.c index 8da969c0..95b669aa 100644 --- a/gl/base64.c +++ b/gl/base64.c | |||
| @@ -1,24 +1,24 @@ | |||
| 1 | /* base64.c -- Encode binary data using printable characters. | 1 | /* base64.c -- Encode binary data using printable characters. |
| 2 | Copyright (C) 1999-2001, 2004-2006, 2009-2013 Free Software Foundation, Inc. | 2 | Copyright (C) 1999-2001, 2004-2006, 2009-2023 Free Software Foundation, Inc. |
| 3 | 3 | ||
| 4 | This program is free software; you can redistribute it and/or modify | 4 | This file is free software: you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by | 5 | it under the terms of the GNU Lesser General Public License as |
| 6 | the Free Software Foundation; either version 3, or (at your option) | 6 | published by the Free Software Foundation; either version 2.1 of the |
| 7 | any later version. | 7 | License, or (at your option) any later version. |
| 8 | 8 | ||
| 9 | This program is distributed in the hope that it will be useful, | 9 | This file is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | GNU General Public License for more details. | 12 | GNU Lesser General Public License for more details. |
| 13 | 13 | ||
| 14 | You should have received a copy of the GNU General Public License | 14 | You should have received a copy of the GNU Lesser General Public License |
| 15 | along with this program; if not, see <http://www.gnu.org/licenses/>. */ | 15 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| 16 | 16 | ||
| 17 | /* Written by Simon Josefsson. Partially adapted from GNU MailUtils | 17 | /* Written by Simon Josefsson. Partially adapted from GNU MailUtils |
| 18 | * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review | 18 | * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review |
| 19 | * from Paul Eggert, Bruno Haible, and Stepan Kasal. | 19 | * from Paul Eggert, Bruno Haible, and Stepan Kasal. |
| 20 | * | 20 | * |
| 21 | * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>. | 21 | * See also RFC 4648 <https://www.ietf.org/rfc/rfc4648.txt>. |
| 22 | * | 22 | * |
| 23 | * Be careful with error checking. Here is how you would typically | 23 | * Be careful with error checking. Here is how you would typically |
| 24 | * use these functions: | 24 | * use these functions: |
| @@ -30,7 +30,7 @@ | |||
| 30 | * FAIL: memory allocation error | 30 | * FAIL: memory allocation error |
| 31 | * OK: data in OUT/OUTLEN | 31 | * OK: data in OUT/OUTLEN |
| 32 | * | 32 | * |
| 33 | * size_t outlen = base64_encode_alloc (in, inlen, &out); | 33 | * idx_t outlen = base64_encode_alloc (in, inlen, &out); |
| 34 | * if (out == NULL && outlen == 0 && inlen != 0) | 34 | * if (out == NULL && outlen == 0 && inlen != 0) |
| 35 | * FAIL: input too long | 35 | * FAIL: input too long |
| 36 | * if (out == NULL) | 36 | * if (out == NULL) |
| @@ -44,51 +44,84 @@ | |||
| 44 | /* Get prototype. */ | 44 | /* Get prototype. */ |
| 45 | #include "base64.h" | 45 | #include "base64.h" |
| 46 | 46 | ||
| 47 | /* Get malloc. */ | 47 | /* Get imalloc. */ |
| 48 | #include <stdlib.h> | 48 | #include <ialloc.h> |
| 49 | |||
| 50 | #include <intprops.h> | ||
| 49 | 51 | ||
| 50 | /* Get UCHAR_MAX. */ | 52 | /* Get UCHAR_MAX. */ |
| 51 | #include <limits.h> | 53 | #include <limits.h> |
| 52 | 54 | ||
| 53 | #include <string.h> | 55 | #include <string.h> |
| 54 | 56 | ||
| 55 | /* C89 compliant way to cast 'char' to 'unsigned char'. */ | 57 | /* Convert 'char' to 'unsigned char' without casting. */ |
| 56 | static unsigned char | 58 | static unsigned char |
| 57 | to_uchar (char ch) | 59 | to_uchar (char ch) |
| 58 | { | 60 | { |
| 59 | return ch; | 61 | return ch; |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 64 | static const char b64c[64] = | ||
| 65 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
| 66 | |||
| 67 | /* Base64 encode IN array of size INLEN into OUT array. OUT needs | ||
| 68 | to be of length >= BASE64_LENGTH(INLEN), and INLEN needs to be | ||
| 69 | a multiple of 3. */ | ||
| 70 | static void | ||
| 71 | base64_encode_fast (const char *restrict in, idx_t inlen, char *restrict out) | ||
| 72 | { | ||
| 73 | while (inlen) | ||
| 74 | { | ||
| 75 | *out++ = b64c[(to_uchar (in[0]) >> 2) & 0x3f]; | ||
| 76 | *out++ = b64c[((to_uchar (in[0]) << 4) + (to_uchar (in[1]) >> 4)) & 0x3f]; | ||
| 77 | *out++ = b64c[((to_uchar (in[1]) << 2) + (to_uchar (in[2]) >> 6)) & 0x3f]; | ||
| 78 | *out++ = b64c[to_uchar (in[2]) & 0x3f]; | ||
| 79 | |||
| 80 | inlen -= 3; | ||
| 81 | in += 3; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 62 | /* Base64 encode IN array of size INLEN into OUT array of size OUTLEN. | 85 | /* Base64 encode IN array of size INLEN into OUT array of size OUTLEN. |
| 63 | If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as | 86 | If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as |
| 64 | possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero | 87 | possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero |
| 65 | terminate the output buffer. */ | 88 | terminate the output buffer. */ |
| 66 | void | 89 | void |
| 67 | base64_encode (const char *restrict in, size_t inlen, | 90 | base64_encode (const char *restrict in, idx_t inlen, |
| 68 | char *restrict out, size_t outlen) | 91 | char *restrict out, idx_t outlen) |
| 69 | { | 92 | { |
| 70 | static const char b64str[64] = | 93 | /* Note this outlen constraint can be enforced at compile time. |
| 71 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | 94 | I.E. that the output buffer is exactly large enough to hold |
| 95 | the encoded inlen bytes. The inlen constraints (of corresponding | ||
| 96 | to outlen, and being a multiple of 3) can change at runtime | ||
| 97 | at the end of input. However the common case when reading | ||
| 98 | large inputs is to have both constraints satisfied, so we depend | ||
| 99 | on both in base_encode_fast(). */ | ||
| 100 | if (outlen % 4 == 0 && inlen == (outlen >> 2) * 3) | ||
| 101 | { | ||
| 102 | base64_encode_fast (in, inlen, out); | ||
| 103 | return; | ||
| 104 | } | ||
| 72 | 105 | ||
| 73 | while (inlen && outlen) | 106 | while (inlen && outlen) |
| 74 | { | 107 | { |
| 75 | *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f]; | 108 | *out++ = b64c[(to_uchar (in[0]) >> 2) & 0x3f]; |
| 76 | if (!--outlen) | 109 | if (!--outlen) |
| 77 | break; | 110 | break; |
| 78 | *out++ = b64str[((to_uchar (in[0]) << 4) | 111 | *out++ = b64c[((to_uchar (in[0]) << 4) |
| 79 | + (--inlen ? to_uchar (in[1]) >> 4 : 0)) | 112 | + (--inlen ? to_uchar (in[1]) >> 4 : 0)) |
| 80 | & 0x3f]; | 113 | & 0x3f]; |
| 81 | if (!--outlen) | 114 | if (!--outlen) |
| 82 | break; | 115 | break; |
| 83 | *out++ = | 116 | *out++ = |
| 84 | (inlen | 117 | (inlen |
| 85 | ? b64str[((to_uchar (in[1]) << 2) | 118 | ? b64c[((to_uchar (in[1]) << 2) |
| 86 | + (--inlen ? to_uchar (in[2]) >> 6 : 0)) | 119 | + (--inlen ? to_uchar (in[2]) >> 6 : 0)) |
| 87 | & 0x3f] | 120 | & 0x3f] |
| 88 | : '='); | 121 | : '='); |
| 89 | if (!--outlen) | 122 | if (!--outlen) |
| 90 | break; | 123 | break; |
| 91 | *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '='; | 124 | *out++ = inlen ? b64c[to_uchar (in[2]) & 0x3f] : '='; |
| 92 | if (!--outlen) | 125 | if (!--outlen) |
| 93 | break; | 126 | break; |
| 94 | if (inlen) | 127 | if (inlen) |
| @@ -110,30 +143,21 @@ base64_encode (const char *restrict in, size_t inlen, | |||
| 110 | memory allocation failed, OUT is set to NULL, and the return value | 143 | memory allocation failed, OUT is set to NULL, and the return value |
| 111 | indicates length of the requested memory block, i.e., | 144 | indicates length of the requested memory block, i.e., |
| 112 | BASE64_LENGTH(inlen) + 1. */ | 145 | BASE64_LENGTH(inlen) + 1. */ |
| 113 | size_t | 146 | idx_t |
| 114 | base64_encode_alloc (const char *in, size_t inlen, char **out) | 147 | base64_encode_alloc (const char *in, idx_t inlen, char **out) |
| 115 | { | 148 | { |
| 116 | size_t outlen = 1 + BASE64_LENGTH (inlen); | ||
| 117 | |||
| 118 | /* Check for overflow in outlen computation. | 149 | /* Check for overflow in outlen computation. |
| 119 | * | 150 | Treat negative INLEN as overflow, for better compatibility with |
| 120 | * If there is no overflow, outlen >= inlen. | 151 | pre-2021-08-27 API, which used size_t. */ |
| 121 | * | 152 | idx_t in_over_3 = inlen / 3 + (inlen % 3 != 0), outlen; |
| 122 | * If the operation (inlen + 2) overflows then it yields at most +1, so | 153 | if (! INT_MULTIPLY_OK (in_over_3, 4, &outlen) || inlen < 0) |
| 123 | * outlen is 0. | ||
| 124 | * | ||
| 125 | * If the multiplication overflows, we lose at least half of the | ||
| 126 | * correct value, so the result is < ((inlen + 2) / 3) * 2, which is | ||
| 127 | * less than (inlen + 2) * 0.66667, which is less than inlen as soon as | ||
| 128 | * (inlen > 4). | ||
| 129 | */ | ||
| 130 | if (inlen > outlen) | ||
| 131 | { | 154 | { |
| 132 | *out = NULL; | 155 | *out = NULL; |
| 133 | return 0; | 156 | return 0; |
| 134 | } | 157 | } |
| 158 | outlen++; | ||
| 135 | 159 | ||
| 136 | *out = malloc (outlen); | 160 | *out = imalloc (outlen); |
| 137 | if (!*out) | 161 | if (!*out) |
| 138 | return outlen; | 162 | return outlen; |
| 139 | 163 | ||
| @@ -317,7 +341,7 @@ base64_decode_ctx_init (struct base64_decode_context *ctx) | |||
| 317 | static char * | 341 | static char * |
| 318 | get_4 (struct base64_decode_context *ctx, | 342 | get_4 (struct base64_decode_context *ctx, |
| 319 | char const *restrict *in, char const *restrict in_end, | 343 | char const *restrict *in, char const *restrict in_end, |
| 320 | size_t *n_non_newline) | 344 | idx_t *n_non_newline) |
| 321 | { | 345 | { |
| 322 | if (ctx->i == 4) | 346 | if (ctx->i == 4) |
| 323 | ctx->i = 0; | 347 | ctx->i = 0; |
| @@ -369,8 +393,8 @@ get_4 (struct base64_decode_context *ctx, | |||
| 369 | *OUT to point to the byte after the last one written, and decrement | 393 | *OUT to point to the byte after the last one written, and decrement |
| 370 | *OUTLEN to reflect the number of bytes remaining in *OUT. */ | 394 | *OUTLEN to reflect the number of bytes remaining in *OUT. */ |
| 371 | static bool | 395 | static bool |
| 372 | decode_4 (char const *restrict in, size_t inlen, | 396 | decode_4 (char const *restrict in, idx_t inlen, |
| 373 | char *restrict *outp, size_t *outleft) | 397 | char *restrict *outp, idx_t *outleft) |
| 374 | { | 398 | { |
| 375 | char *out = *outp; | 399 | char *out = *outp; |
| 376 | if (inlen < 2) | 400 | if (inlen < 2) |
| @@ -455,10 +479,10 @@ decode_4 (char const *restrict in, size_t inlen, | |||
| 455 | 479 | ||
| 456 | bool | 480 | bool |
| 457 | base64_decode_ctx (struct base64_decode_context *ctx, | 481 | base64_decode_ctx (struct base64_decode_context *ctx, |
| 458 | const char *restrict in, size_t inlen, | 482 | const char *restrict in, idx_t inlen, |
| 459 | char *restrict out, size_t *outlen) | 483 | char *restrict out, idx_t *outlen) |
| 460 | { | 484 | { |
| 461 | size_t outleft = *outlen; | 485 | idx_t outleft = *outlen; |
| 462 | bool ignore_newlines = ctx != NULL; | 486 | bool ignore_newlines = ctx != NULL; |
| 463 | bool flush_ctx = false; | 487 | bool flush_ctx = false; |
| 464 | unsigned int ctx_i = 0; | 488 | unsigned int ctx_i = 0; |
| @@ -472,7 +496,7 @@ base64_decode_ctx (struct base64_decode_context *ctx, | |||
| 472 | 496 | ||
| 473 | while (true) | 497 | while (true) |
| 474 | { | 498 | { |
| 475 | size_t outleft_save = outleft; | 499 | idx_t outleft_save = outleft; |
| 476 | if (ctx_i == 0 && !flush_ctx) | 500 | if (ctx_i == 0 && !flush_ctx) |
| 477 | { | 501 | { |
| 478 | while (true) | 502 | while (true) |
| @@ -546,17 +570,17 @@ base64_decode_ctx (struct base64_decode_context *ctx, | |||
| 546 | undefined. */ | 570 | undefined. */ |
| 547 | bool | 571 | bool |
| 548 | base64_decode_alloc_ctx (struct base64_decode_context *ctx, | 572 | base64_decode_alloc_ctx (struct base64_decode_context *ctx, |
| 549 | const char *in, size_t inlen, char **out, | 573 | const char *in, idx_t inlen, char **out, |
| 550 | size_t *outlen) | 574 | idx_t *outlen) |
| 551 | { | 575 | { |
| 552 | /* This may allocate a few bytes too many, depending on input, | 576 | /* This may allocate a few bytes too many, depending on input, |
| 553 | but it's not worth the extra CPU time to compute the exact size. | 577 | but it's not worth the extra CPU time to compute the exact size. |
| 554 | The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the | 578 | The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the |
| 555 | input ends with "=" and minus another 1 if the input ends with "==". | 579 | input ends with "=" and minus another 1 if the input ends with "==". |
| 556 | Dividing before multiplying avoids the possibility of overflow. */ | 580 | Shifting before multiplying avoids the possibility of overflow. */ |
| 557 | size_t needlen = 3 * (inlen / 4) + 3; | 581 | idx_t needlen = 3 * ((inlen >> 2) + 1); |
| 558 | 582 | ||
| 559 | *out = malloc (needlen); | 583 | *out = imalloc (needlen); |
| 560 | if (!*out) | 584 | if (!*out) |
| 561 | return true; | 585 | return true; |
| 562 | 586 | ||
