blob: 139d5625093e19b69608a88761a4feebc3ef48b1 [file] [log] [blame]
Kenny Rootb8494592015-09-25 02:29:14 +00001/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2 * All rights reserved.
3 *
4 * This package is an SSL implementation written
5 * by Eric Young (eay@cryptsoft.com).
6 * The implementation was written so as to conform with Netscapes SSL.
7 *
8 * This library is free for commercial and non-commercial use as long as
9 * the following conditions are aheared to. The following conditions
10 * apply to all code found in this distribution, be it the RC4, RSA,
11 * lhash, DES, etc., code; not just the SSL code. The SSL documentation
12 * included with this distribution is covered by the same copyright terms
13 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
14 *
15 * Copyright remains Eric Young's, and as such any Copyright notices in
16 * the code are not to be removed.
17 * If this package is used in a product, Eric Young should be given attribution
18 * as the author of the parts of the library used.
19 * This can be in the form of a textual message at program startup or
20 * in documentation (online or textual) provided with the package.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the copyright
26 * notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in the
29 * documentation and/or other materials provided with the distribution.
30 * 3. All advertising materials mentioning features or use of this software
31 * must display the following acknowledgement:
32 * "This product includes cryptographic software written by
33 * Eric Young (eay@cryptsoft.com)"
34 * The word 'cryptographic' can be left out if the rouines from the library
35 * being used are not cryptographic related :-).
36 * 4. If you include any Windows specific code (or a derivative thereof) from
37 * the apps directory (application code) you must include an acknowledgement:
38 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
39 *
40 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 *
52 * The licence and distribution terms for any publically available version or
53 * derivative of this code cannot be changed. i.e. this code cannot simply be
54 * copied and put under another distribution licence
55 * [including the GNU Public Licence.] */
56
57#include <assert.h>
58#include <errno.h>
59#include <stdio.h>
60#include <string.h>
61
62#include <openssl/base64.h>
63#include <openssl/bio.h>
64#include <openssl/buffer.h>
65#include <openssl/evp.h>
66#include <openssl/mem.h>
67
Robert Sloan69939df2017-01-09 10:53:07 -080068#include "../../crypto/internal.h"
69
Kenny Rootb8494592015-09-25 02:29:14 +000070
71#define B64_BLOCK_SIZE 1024
72#define B64_BLOCK_SIZE2 768
73#define B64_NONE 0
74#define B64_ENCODE 1
75#define B64_DECODE 2
76#define EVP_ENCODE_LENGTH(l) (((l+2)/3*4)+(l/48+1)*2+80)
77
78typedef struct b64_struct {
79 int buf_len;
80 int buf_off;
Robert Sloan8f860b12017-08-28 07:37:06 -070081 int tmp_len; // used to find the start when decoding
82 int tmp_nl; // If true, scan until '\n'
Kenny Rootb8494592015-09-25 02:29:14 +000083 int encode;
Robert Sloan8f860b12017-08-28 07:37:06 -070084 int start; // have we started decoding yet?
85 int cont; // <= 0 when finished
Kenny Rootb8494592015-09-25 02:29:14 +000086 EVP_ENCODE_CTX base64;
87 char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE) + 10];
88 char tmp[B64_BLOCK_SIZE];
89} BIO_B64_CTX;
90
91static int b64_new(BIO *bio) {
92 BIO_B64_CTX *ctx;
93
94 ctx = OPENSSL_malloc(sizeof(*ctx));
95 if (ctx == NULL) {
96 return 0;
97 }
98
Robert Sloan69939df2017-01-09 10:53:07 -080099 OPENSSL_memset(ctx, 0, sizeof(*ctx));
Kenny Rootb8494592015-09-25 02:29:14 +0000100
101 ctx->cont = 1;
102 ctx->start = 1;
103
104 bio->init = 1;
105 bio->ptr = (char *)ctx;
106 return 1;
107}
108
109static int b64_free(BIO *bio) {
110 if (bio == NULL) {
111 return 0;
112 }
113 OPENSSL_free(bio->ptr);
114 bio->ptr = NULL;
115 bio->init = 0;
116 bio->flags = 0;
117 return 1;
118}
119
120static int b64_read(BIO *b, char *out, int outl) {
121 int ret = 0, i, ii, j, k, x, n, num, ret_code = 0;
122 BIO_B64_CTX *ctx;
123 uint8_t *p, *q;
124
125 if (out == NULL) {
126 return 0;
127 }
128 ctx = (BIO_B64_CTX *) b->ptr;
129
130 if (ctx == NULL || b->next_bio == NULL) {
131 return 0;
132 }
133
134 BIO_clear_retry_flags(b);
135
136 if (ctx->encode != B64_DECODE) {
137 ctx->encode = B64_DECODE;
138 ctx->buf_len = 0;
139 ctx->buf_off = 0;
140 ctx->tmp_len = 0;
141 EVP_DecodeInit(&ctx->base64);
142 }
143
Robert Sloan8f860b12017-08-28 07:37:06 -0700144 // First check if there are bytes decoded/encoded
Kenny Rootb8494592015-09-25 02:29:14 +0000145 if (ctx->buf_len > 0) {
146 assert(ctx->buf_len >= ctx->buf_off);
147 i = ctx->buf_len - ctx->buf_off;
148 if (i > outl) {
149 i = outl;
150 }
151 assert(ctx->buf_off + i < (int)sizeof(ctx->buf));
Robert Sloan69939df2017-01-09 10:53:07 -0800152 OPENSSL_memcpy(out, &ctx->buf[ctx->buf_off], i);
Kenny Rootb8494592015-09-25 02:29:14 +0000153 ret = i;
154 out += i;
155 outl -= i;
156 ctx->buf_off += i;
157 if (ctx->buf_len == ctx->buf_off) {
158 ctx->buf_len = 0;
159 ctx->buf_off = 0;
160 }
161 }
162
Robert Sloan8f860b12017-08-28 07:37:06 -0700163 // At this point, we have room of outl bytes and an empty buffer, so we
164 // should read in some more.
Kenny Rootb8494592015-09-25 02:29:14 +0000165
166 ret_code = 0;
167 while (outl > 0) {
168 if (ctx->cont <= 0) {
169 break;
170 }
171
172 i = BIO_read(b->next_bio, &(ctx->tmp[ctx->tmp_len]),
173 B64_BLOCK_SIZE - ctx->tmp_len);
174
175 if (i <= 0) {
176 ret_code = i;
177
Robert Sloan8f860b12017-08-28 07:37:06 -0700178 // Should we continue next time we are called?
Kenny Rootb8494592015-09-25 02:29:14 +0000179 if (!BIO_should_retry(b->next_bio)) {
180 ctx->cont = i;
Robert Sloan8f860b12017-08-28 07:37:06 -0700181 // If buffer empty break
Kenny Rootb8494592015-09-25 02:29:14 +0000182 if (ctx->tmp_len == 0) {
183 break;
184 } else {
Robert Sloan8f860b12017-08-28 07:37:06 -0700185 // Fall through and process what we have
Kenny Rootb8494592015-09-25 02:29:14 +0000186 i = 0;
187 }
188 } else {
Robert Sloan8f860b12017-08-28 07:37:06 -0700189 // else we retry and add more data to buffer
Kenny Rootb8494592015-09-25 02:29:14 +0000190 break;
191 }
192 }
193 i += ctx->tmp_len;
194 ctx->tmp_len = i;
195
Robert Sloan8f860b12017-08-28 07:37:06 -0700196 // We need to scan, a line at a time until we have a valid line if we are
197 // starting.
Kenny Rootb8494592015-09-25 02:29:14 +0000198 if (ctx->start && (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL))) {
Robert Sloan8f860b12017-08-28 07:37:06 -0700199 // ctx->start = 1;
Kenny Rootb8494592015-09-25 02:29:14 +0000200 ctx->tmp_len = 0;
201 } else if (ctx->start) {
202 q = p = (uint8_t *)ctx->tmp;
203 num = 0;
204 for (j = 0; j < i; j++) {
205 if (*(q++) != '\n') {
206 continue;
207 }
208
Robert Sloan8f860b12017-08-28 07:37:06 -0700209 // due to a previous very long line, we need to keep on scanning for a
210 // '\n' before we even start looking for base64 encoded stuff.
Kenny Rootb8494592015-09-25 02:29:14 +0000211 if (ctx->tmp_nl) {
212 p = q;
213 ctx->tmp_nl = 0;
214 continue;
215 }
216
217 k = EVP_DecodeUpdate(&(ctx->base64), (uint8_t *)ctx->buf, &num, p,
218 q - p);
219
220 if (k <= 0 && num == 0 && ctx->start) {
221 EVP_DecodeInit(&ctx->base64);
222 } else {
223 if (p != (uint8_t *)&(ctx->tmp[0])) {
224 i -= (p - (uint8_t *)&(ctx->tmp[0]));
225 for (x = 0; x < i; x++) {
226 ctx->tmp[x] = p[x];
227 }
228 }
229 EVP_DecodeInit(&ctx->base64);
230 ctx->start = 0;
231 break;
232 }
233 p = q;
234 }
235
Robert Sloan8f860b12017-08-28 07:37:06 -0700236 // we fell off the end without starting
Kenny Rootb8494592015-09-25 02:29:14 +0000237 if (j == i && num == 0) {
Robert Sloan8f860b12017-08-28 07:37:06 -0700238 // Is this is one long chunk?, if so, keep on reading until a new
239 // line.
Kenny Rootb8494592015-09-25 02:29:14 +0000240 if (p == (uint8_t *)&(ctx->tmp[0])) {
Robert Sloan8f860b12017-08-28 07:37:06 -0700241 // Check buffer full
Kenny Rootb8494592015-09-25 02:29:14 +0000242 if (i == B64_BLOCK_SIZE) {
243 ctx->tmp_nl = 1;
244 ctx->tmp_len = 0;
245 }
Robert Sloan8f860b12017-08-28 07:37:06 -0700246 } else if (p != q) { // finished on a '\n'
Kenny Rootb8494592015-09-25 02:29:14 +0000247 n = q - p;
248 for (ii = 0; ii < n; ii++) {
249 ctx->tmp[ii] = p[ii];
250 }
251 ctx->tmp_len = n;
252 }
Robert Sloan8f860b12017-08-28 07:37:06 -0700253 // else finished on a '\n'
Kenny Rootb8494592015-09-25 02:29:14 +0000254 continue;
255 } else {
256 ctx->tmp_len = 0;
257 }
258 } else if (i < B64_BLOCK_SIZE && ctx->cont > 0) {
Robert Sloan8f860b12017-08-28 07:37:06 -0700259 // If buffer isn't full and we can retry then restart to read in more
260 // data.
Kenny Rootb8494592015-09-25 02:29:14 +0000261 continue;
262 }
263
264 if (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL)) {
265 int z, jj;
266
Robert Sloan8f860b12017-08-28 07:37:06 -0700267 jj = i & ~3; // process per 4
Kenny Rootb8494592015-09-25 02:29:14 +0000268 z = EVP_DecodeBlock((uint8_t *)ctx->buf, (uint8_t *)ctx->tmp, jj);
269 if (jj > 2) {
270 if (ctx->tmp[jj - 1] == '=') {
271 z--;
272 if (ctx->tmp[jj - 2] == '=') {
273 z--;
274 }
275 }
276 }
Robert Sloan8f860b12017-08-28 07:37:06 -0700277 // z is now number of output bytes and jj is the number consumed.
Kenny Rootb8494592015-09-25 02:29:14 +0000278 if (jj != i) {
Robert Sloan69939df2017-01-09 10:53:07 -0800279 OPENSSL_memmove(ctx->tmp, &ctx->tmp[jj], i - jj);
Kenny Rootb8494592015-09-25 02:29:14 +0000280 ctx->tmp_len = i - jj;
281 }
282 ctx->buf_len = 0;
283 if (z > 0) {
284 ctx->buf_len = z;
285 }
286 i = z;
287 } else {
288 i = EVP_DecodeUpdate(&(ctx->base64), (uint8_t *)ctx->buf,
289 &ctx->buf_len, (uint8_t *)ctx->tmp, i);
290 ctx->tmp_len = 0;
291 }
292 ctx->buf_off = 0;
293 if (i < 0) {
294 ret_code = 0;
295 ctx->buf_len = 0;
296 break;
297 }
298
299 if (ctx->buf_len <= outl) {
300 i = ctx->buf_len;
301 } else {
302 i = outl;
303 }
304
Robert Sloan69939df2017-01-09 10:53:07 -0800305 OPENSSL_memcpy(out, ctx->buf, i);
Kenny Rootb8494592015-09-25 02:29:14 +0000306 ret += i;
307 ctx->buf_off = i;
308 if (ctx->buf_off == ctx->buf_len) {
309 ctx->buf_len = 0;
310 ctx->buf_off = 0;
311 }
312 outl -= i;
313 out += i;
314 }
315
316 BIO_copy_next_retry(b);
317 return ret == 0 ? ret_code : ret;
318}
319
320static int b64_write(BIO *b, const char *in, int inl) {
321 int ret = 0, n, i;
322 BIO_B64_CTX *ctx;
323
324 ctx = (BIO_B64_CTX *)b->ptr;
325 BIO_clear_retry_flags(b);
326
327 if (ctx->encode != B64_ENCODE) {
328 ctx->encode = B64_ENCODE;
329 ctx->buf_len = 0;
330 ctx->buf_off = 0;
331 ctx->tmp_len = 0;
332 EVP_EncodeInit(&(ctx->base64));
333 }
334
335 assert(ctx->buf_off < (int)sizeof(ctx->buf));
336 assert(ctx->buf_len <= (int)sizeof(ctx->buf));
337 assert(ctx->buf_len >= ctx->buf_off);
338
339 n = ctx->buf_len - ctx->buf_off;
340 while (n > 0) {
341 i = BIO_write(b->next_bio, &(ctx->buf[ctx->buf_off]), n);
342 if (i <= 0) {
343 BIO_copy_next_retry(b);
344 return i;
345 }
346 assert(i <= n);
347 ctx->buf_off += i;
348 assert(ctx->buf_off <= (int)sizeof(ctx->buf));
349 assert(ctx->buf_len >= ctx->buf_off);
350 n -= i;
351 }
352
Robert Sloan8f860b12017-08-28 07:37:06 -0700353 // at this point all pending data has been written.
Kenny Rootb8494592015-09-25 02:29:14 +0000354 ctx->buf_off = 0;
355 ctx->buf_len = 0;
356
357 if (in == NULL || inl <= 0) {
358 return 0;
359 }
360
361 while (inl > 0) {
362 n = (inl > B64_BLOCK_SIZE) ? B64_BLOCK_SIZE : inl;
363
364 if (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL)) {
365 if (ctx->tmp_len > 0) {
366 assert(ctx->tmp_len <= 3);
367 n = 3 - ctx->tmp_len;
Robert Sloan8f860b12017-08-28 07:37:06 -0700368 // There's a theoretical possibility of this.
Kenny Rootb8494592015-09-25 02:29:14 +0000369 if (n > inl) {
370 n = inl;
371 }
Robert Sloan69939df2017-01-09 10:53:07 -0800372 OPENSSL_memcpy(&(ctx->tmp[ctx->tmp_len]), in, n);
Kenny Rootb8494592015-09-25 02:29:14 +0000373 ctx->tmp_len += n;
374 ret += n;
375 if (ctx->tmp_len < 3) {
376 break;
377 }
378 ctx->buf_len = EVP_EncodeBlock((uint8_t *)ctx->buf, (uint8_t *)ctx->tmp,
379 ctx->tmp_len);
380 assert(ctx->buf_len <= (int)sizeof(ctx->buf));
381 assert(ctx->buf_len >= ctx->buf_off);
382
Robert Sloan8f860b12017-08-28 07:37:06 -0700383 // Since we're now done using the temporary buffer, the length should
384 // be zeroed.
Kenny Rootb8494592015-09-25 02:29:14 +0000385 ctx->tmp_len = 0;
386 } else {
387 if (n < 3) {
Robert Sloan69939df2017-01-09 10:53:07 -0800388 OPENSSL_memcpy(ctx->tmp, in, n);
Kenny Rootb8494592015-09-25 02:29:14 +0000389 ctx->tmp_len = n;
390 ret += n;
391 break;
392 }
393 n -= n % 3;
394 ctx->buf_len =
395 EVP_EncodeBlock((uint8_t *)ctx->buf, (const uint8_t *)in, n);
396 assert(ctx->buf_len <= (int)sizeof(ctx->buf));
397 assert(ctx->buf_len >= ctx->buf_off);
398 ret += n;
399 }
400 } else {
401 EVP_EncodeUpdate(&(ctx->base64), (uint8_t *)ctx->buf, &ctx->buf_len,
402 (uint8_t *)in, n);
403 assert(ctx->buf_len <= (int)sizeof(ctx->buf));
404 assert(ctx->buf_len >= ctx->buf_off);
405 ret += n;
406 }
407 inl -= n;
408 in += n;
409
410 ctx->buf_off = 0;
411 n = ctx->buf_len;
412
413 while (n > 0) {
414 i = BIO_write(b->next_bio, &(ctx->buf[ctx->buf_off]), n);
415 if (i <= 0) {
416 BIO_copy_next_retry(b);
417 return ret == 0 ? i : ret;
418 }
419 assert(i <= n);
420 n -= i;
421 ctx->buf_off += i;
422 assert(ctx->buf_off <= (int)sizeof(ctx->buf));
423 assert(ctx->buf_len >= ctx->buf_off);
424 }
425 ctx->buf_len = 0;
426 ctx->buf_off = 0;
427 }
428 return ret;
429}
430
431static long b64_ctrl(BIO *b, int cmd, long num, void *ptr) {
432 BIO_B64_CTX *ctx;
433 long ret = 1;
434 int i;
435
436 ctx = (BIO_B64_CTX *)b->ptr;
437
438 switch (cmd) {
439 case BIO_CTRL_RESET:
440 ctx->cont = 1;
441 ctx->start = 1;
442 ctx->encode = B64_NONE;
443 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
444 break;
445
Robert Sloan8f860b12017-08-28 07:37:06 -0700446 case BIO_CTRL_EOF: // More to read
Kenny Rootb8494592015-09-25 02:29:14 +0000447 if (ctx->cont <= 0) {
448 ret = 1;
449 } else {
450 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
451 }
452 break;
453
Robert Sloan8f860b12017-08-28 07:37:06 -0700454 case BIO_CTRL_WPENDING: // More to write in buffer
Kenny Rootb8494592015-09-25 02:29:14 +0000455 assert(ctx->buf_len >= ctx->buf_off);
456 ret = ctx->buf_len - ctx->buf_off;
David Benjamind316cba2016-06-02 16:17:39 -0400457 if ((ret == 0) && (ctx->encode != B64_NONE) && (ctx->base64.data_used != 0)) {
Kenny Rootb8494592015-09-25 02:29:14 +0000458 ret = 1;
459 } else if (ret <= 0) {
460 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
461 }
462 break;
463
Robert Sloan8f860b12017-08-28 07:37:06 -0700464 case BIO_CTRL_PENDING: // More to read in buffer
Kenny Rootb8494592015-09-25 02:29:14 +0000465 assert(ctx->buf_len >= ctx->buf_off);
466 ret = ctx->buf_len - ctx->buf_off;
467 if (ret <= 0) {
468 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
469 }
470 break;
471
472 case BIO_CTRL_FLUSH:
Robert Sloan8f860b12017-08-28 07:37:06 -0700473 // do a final write
Kenny Rootb8494592015-09-25 02:29:14 +0000474 again:
475 while (ctx->buf_len != ctx->buf_off) {
476 i = b64_write(b, NULL, 0);
477 if (i < 0) {
478 return i;
479 }
480 }
481 if (BIO_test_flags(b, BIO_FLAGS_BASE64_NO_NL)) {
482 if (ctx->tmp_len != 0) {
483 ctx->buf_len = EVP_EncodeBlock((uint8_t *)ctx->buf,
484 (uint8_t *)ctx->tmp, ctx->tmp_len);
485 ctx->buf_off = 0;
486 ctx->tmp_len = 0;
487 goto again;
488 }
David Benjamind316cba2016-06-02 16:17:39 -0400489 } else if (ctx->encode != B64_NONE && ctx->base64.data_used != 0) {
Kenny Rootb8494592015-09-25 02:29:14 +0000490 ctx->buf_off = 0;
491 EVP_EncodeFinal(&(ctx->base64), (uint8_t *)ctx->buf, &(ctx->buf_len));
Robert Sloan8f860b12017-08-28 07:37:06 -0700492 // push out the bytes
Kenny Rootb8494592015-09-25 02:29:14 +0000493 goto again;
494 }
Robert Sloan8f860b12017-08-28 07:37:06 -0700495 // Finally flush the underlying BIO
Kenny Rootb8494592015-09-25 02:29:14 +0000496 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
497 break;
498
499 case BIO_C_DO_STATE_MACHINE:
500 BIO_clear_retry_flags(b);
501 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
502 BIO_copy_next_retry(b);
503 break;
504
505 case BIO_CTRL_INFO:
506 case BIO_CTRL_GET:
507 case BIO_CTRL_SET:
508 default:
509 ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
510 break;
511 }
512 return ret;
513}
514
515static long b64_callback_ctrl(BIO *b, int cmd, bio_info_cb fp) {
516 long ret = 1;
517
518 if (b->next_bio == NULL) {
519 return 0;
520 }
521 switch (cmd) {
522 default:
523 ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
524 break;
525 }
526 return ret;
527}
528
Kenny Rootb8494592015-09-25 02:29:14 +0000529static const BIO_METHOD b64_method = {
Robert Sloan4d1ac502017-02-06 08:36:14 -0800530 BIO_TYPE_BASE64, "base64 encoding", b64_write, b64_read, NULL /* puts */,
Kenny Rootb8494592015-09-25 02:29:14 +0000531 NULL /* gets */, b64_ctrl, b64_new, b64_free, b64_callback_ctrl,
532};
533
534const BIO_METHOD *BIO_f_base64(void) { return &b64_method; }