blob: 0601bf673e1984e497492c251346f6153d088ee0 [file] [log] [blame]
willy tarreau9e138862006-05-14 23:06:28 +02001/*
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +01002 * ASCII <-> Base64 conversion as described in RFC1421.
Willy Tarreaubaaee002006-06-26 02:48:02 +02003 *
Willy Tarreauc01062b2010-10-07 19:27:29 +02004 * Copyright 2006-2010 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +01005 * Copyright 2009-2010 Krzysztof Piotr Oledzki <ole@ans.pl>
willy tarreau9e138862006-05-14 23:06:28 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +010014#include <stdlib.h>
15#include <string.h>
16
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020017#include <haproxy/api.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020018#include <haproxy/base64.h>
willy tarreau9e138862006-05-14 23:06:28 +020019
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +010020#define B64BASE '#' /* arbitrary chosen base value */
21#define B64CMIN '+'
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +020022#define UB64CMIN '-'
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +010023#define B64CMAX 'z'
24#define B64PADV 64 /* Base64 chosen special pad value */
25
Willy Tarreau69e989c2008-06-29 17:17:38 +020026const char base64tab[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +010027const char base64rev[]="b###cXYZ[\\]^_`a###d###$%&'()*+,-./0123456789:;<=######>?@ABCDEFGHIJKLMNOPQRSTUVW";
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +020028const char ubase64tab[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
29const char ubase64rev[]="b##XYZ[\\]^_`a###c###$%&'()*+,-./0123456789:;<=####c#>?@ABCDEFGHIJKLMNOPQRSTUVW";
willy tarreau9e138862006-05-14 23:06:28 +020030
31/* Encodes <ilen> bytes from <in> to <out> for at most <olen> chars (including
32 * the trailing zero). Returns the number of bytes written. No check is made
33 * for <in> or <out> to be NULL. Returns negative value if <olen> is too short
34 * to accept <ilen>. 4 output bytes are produced for 1 to 3 input bytes.
35 */
36int a2base64(char *in, int ilen, char *out, int olen)
37{
willy tarreau9e138862006-05-14 23:06:28 +020038 int convlen;
39
40 convlen = ((ilen + 2) / 3) * 4;
41
42 if (convlen >= olen)
43 return -1;
44
45 /* we don't need to check olen anymore */
46 while (ilen >= 3) {
Willy Tarreaubaaee002006-06-26 02:48:02 +020047 out[0] = base64tab[(((unsigned char)in[0]) >> 2)];
48 out[1] = base64tab[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)];
49 out[2] = base64tab[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)];
50 out[3] = base64tab[(((unsigned char)in[2] & 0x3F))];
willy tarreau9e138862006-05-14 23:06:28 +020051 out += 4;
52 in += 3; ilen -= 3;
53 }
54
55 if (!ilen) {
56 out[0] = '\0';
57 } else {
Willy Tarreaubaaee002006-06-26 02:48:02 +020058 out[0] = base64tab[((unsigned char)in[0]) >> 2];
willy tarreau9e138862006-05-14 23:06:28 +020059 if (ilen == 1) {
Willy Tarreaubaaee002006-06-26 02:48:02 +020060 out[1] = base64tab[((unsigned char)in[0] & 0x03) << 4];
willy tarreau9e138862006-05-14 23:06:28 +020061 out[2] = '=';
62 } else {
Willy Tarreaubaaee002006-06-26 02:48:02 +020063 out[1] = base64tab[(((unsigned char)in[0] & 0x03) << 4) |
willy tarreau9e138862006-05-14 23:06:28 +020064 (((unsigned char)in[1]) >> 4)];
Willy Tarreaubaaee002006-06-26 02:48:02 +020065 out[2] = base64tab[((unsigned char)in[1] & 0x0F) << 2];
willy tarreau9e138862006-05-14 23:06:28 +020066 }
67 out[3] = '=';
68 out[4] = '\0';
69 }
70
71 return convlen;
72}
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +010073
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +020074/* url variant of a2base64 */
75int a2base64url(const char *in, size_t ilen, char *out, size_t olen)
76{
77 int convlen;
78
79 convlen = ((ilen + 2) / 3) * 4;
80
81 if (convlen >= olen)
82 return -1;
83
84 /* we don't need to check olen anymore */
85 while (ilen >= 3) {
86 out[0] = ubase64tab[(((unsigned char)in[0]) >> 2)];
87 out[1] = ubase64tab[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)];
88 out[2] = ubase64tab[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)];
89 out[3] = ubase64tab[(((unsigned char)in[2] & 0x3F))];
90 out += 4;
91 in += 3;
92 ilen -= 3;
93 }
94
95 if (!ilen) {
96 out[0] = '\0';
97 return convlen;
98 }
99
100 out[0] = ubase64tab[((unsigned char)in[0]) >> 2];
101 if (ilen == 1) {
102 out[1] = ubase64tab[((unsigned char)in[0] & 0x03) << 4];
103 out[2] = '\0';
104 convlen -= 2;
105 } else {
106 out[1] = ubase64tab[(((unsigned char)in[0] & 0x03) << 4) |
107 (((unsigned char)in[1]) >> 4)];
108 out[2] = ubase64tab[((unsigned char)in[1] & 0x0F) << 2];
109 out[3] = '\0';
110 convlen -= 1;
111 }
112
113 return convlen;
114}
115
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +0100116/* Decodes <ilen> bytes from <in> to <out> for at most <olen> chars.
117 * Returns the number of bytes converted. No check is made for
118 * <in> or <out> to be NULL. Returns -1 if <in> is invalid or ilen
119 * has wrong size, -2 if <olen> is too short.
120 * 1 to 3 output bytes are produced for 4 input bytes.
121 */
122int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
123
124 unsigned char t[4];
125 signed char b;
126 int convlen = 0, i = 0, pad = 0;
127
128 if (ilen % 4)
129 return -1;
130
Emeric Bruned697e42019-01-14 14:38:39 +0100131 if (olen < ((ilen / 4 * 3)
132 - (in[ilen-1] == '=' ? 1 : 0)
133 - (in[ilen-2] == '=' ? 1 : 0)))
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +0100134 return -2;
135
136 while (ilen) {
137
138 /* if (*p < B64CMIN || *p > B64CMAX) */
139 b = (signed char)*in - B64CMIN;
140 if ((unsigned char)b > (B64CMAX-B64CMIN))
141 return -1;
142
143 b = base64rev[b] - B64BASE - 1;
144
145 /* b == -1: invalid character */
146 if (b < 0)
147 return -1;
148
Joseph Herlant3b4e8e12018-11-25 13:16:35 -0800149 /* padding has to be continuous */
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +0100150 if (pad && b != B64PADV)
151 return -1;
152
153 /* valid padding: "XX==" or "XXX=", but never "X===" or "====" */
154 if (pad && i < 2)
155 return -1;
156
157 if (b == B64PADV)
158 pad++;
159
160 t[i++] = b;
161
162 if (i == 4) {
163 /*
164 * WARNING: we allow to write little more data than we
165 * should, but the checks from the beginning of the
166 * functions guarantee that we can safely do that.
167 */
168
169 /* xx000000 xx001111 xx111122 xx222222 */
Dragan Dosenaec6e022021-08-24 09:48:04 +0200170 if (convlen < olen)
171 out[convlen] = ((t[0] << 2) + (t[1] >> 4));
172 if (convlen+1 < olen)
173 out[convlen+1] = ((t[1] << 4) + (t[2] >> 2));
174 if (convlen+2 < olen)
175 out[convlen+2] = ((t[2] << 6) + (t[3] >> 0));
Krzysztof Piotr Oledzkifccbdc82010-01-29 13:36:23 +0100176
177 convlen += 3-pad;
178
179 pad = i = 0;
180 }
181
182 in++;
183 ilen--;
184 }
185
186 return convlen;
187}
Willy Tarreauc01062b2010-10-07 19:27:29 +0200188
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +0200189/* url variant of base64dec */
190/* The reverse tab used to decode base64 is generated via /dev/base64/base64rev-gen.c */
191int base64urldec(const char *in, size_t ilen, char *out, size_t olen)
192{
193 unsigned char t[4];
194 signed char b;
195 int convlen = 0, i = 0, pad = 0, padlen = 0;
196
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +0200197 switch (ilen % 4) {
198 case 0:
199 break;
200 case 2:
201 padlen = pad = 2;
202 break;
203 case 3:
204 padlen = pad = 1;
205 break;
206 default:
207 return -1;
208 }
209
Dragan Dosen48f010f2021-08-25 11:57:01 +0200210 if (olen < (((ilen + pad) / 4 * 3) - pad))
211 return -2;
212
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +0200213 while (ilen + pad) {
214 if (ilen) {
215 /* if (*p < UB64CMIN || *p > B64CMAX) */
216 b = (signed char) * in - UB64CMIN;
217 if ((unsigned char)b > (B64CMAX - UB64CMIN))
218 return -1;
219
220 b = ubase64rev[b] - B64BASE - 1;
221 /* b == -1: invalid character */
222 if (b < 0)
223 return -1;
224
225 in++;
226 ilen--;
227
228 } else {
229 b = B64PADV;
230 pad--;
231 }
232
233 t[i++] = b;
234
235 if (i == 4) {
236 /*
237 * WARNING: we allow to write little more data than we
238 * should, but the checks from the beginning of the
239 * functions guarantee that we can safely do that.
240 */
241
242 /* xx000000 xx001111 xx111122 xx222222 */
Dragan Dosenaec6e022021-08-24 09:48:04 +0200243 if (convlen < olen)
244 out[convlen] = ((t[0] << 2) + (t[1] >> 4));
245 if (convlen+1 < olen)
246 out[convlen+1] = ((t[1] << 4) + (t[2] >> 2));
247 if (convlen+2 < olen)
248 out[convlen+2] = ((t[2] << 6) + (t[3] >> 0));
Moemen MHEDHBI92f7d432021-04-01 20:53:59 +0200249
250 convlen += 3;
251 i = 0;
252 }
253 }
254 convlen -= padlen;
255
256 return convlen;
257}
Willy Tarreauc01062b2010-10-07 19:27:29 +0200258
259/* Converts the lower 30 bits of an integer to a 5-char base64 string. The
260 * caller is responsible for ensuring that the output buffer can accept 6 bytes
261 * (5 + the trailing zero). The pointer to the string is returned. The
262 * conversion is performed with MSB first and in a format that can be
263 * decoded with b64tos30(). This format is not padded and thus is not
264 * compatible with usual base64 routines.
265 */
266const char *s30tob64(int in, char *out)
267{
268 int i;
269 for (i = 0; i < 5; i++) {
270 out[i] = base64tab[(in >> 24) & 0x3F];
271 in <<= 6;
272 }
273 out[5] = '\0';
274 return out;
275}
276
277/* Converts a 5-char base64 string encoded by s30tob64() into a 30-bit integer.
278 * The caller is responsible for ensuring that the input contains at least 5
279 * chars. If any unexpected character is encountered, a negative value is
280 * returned. Otherwise the decoded value is returned.
281 */
282int b64tos30(const char *in)
283{
284 int i, out;
285 signed char b;
286
287 out = 0;
288 for (i = 0; i < 5; i++) {
289 b = (signed char)in[i] - B64CMIN;
290 if ((unsigned char)b > (B64CMAX - B64CMIN))
291 return -1; /* input character out of range */
292
293 b = base64rev[b] - B64BASE - 1;
294 if (b < 0) /* invalid character */
295 return -1;
296
297 if (b == B64PADV) /* padding not allowed */
298 return -1;
299
300 out = (out << 6) + b;
301 }
302 return out;
303}