summaryrefslogtreecommitdiffstats
path: root/gl/base64.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/base64.c')
-rw-r--r--gl/base64.c126
1 files changed, 75 insertions, 51 deletions
diff --git a/gl/base64.c b/gl/base64.c
index 8da969c..95b669a 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. */
56static unsigned char 58static unsigned char
57to_uchar (char ch) 59to_uchar (char ch)
58{ 60{
59 return ch; 61 return ch;
60} 62}
61 63
64static 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. */
70static void
71base64_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. */
66void 89void
67base64_encode (const char *restrict in, size_t inlen, 90base64_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. */
113size_t 146idx_t
114base64_encode_alloc (const char *in, size_t inlen, char **out) 147base64_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)
317static char * 341static char *
318get_4 (struct base64_decode_context *ctx, 342get_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. */
371static bool 395static bool
372decode_4 (char const *restrict in, size_t inlen, 396decode_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
456bool 480bool
457base64_decode_ctx (struct base64_decode_context *ctx, 481base64_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. */
547bool 571bool
548base64_decode_alloc_ctx (struct base64_decode_context *ctx, 572base64_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