bio_b64.c revision 89837
1/* crypto/evp/bio_b64.c */ 2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 3 * All rights reserved. 4 * 5 * This package is an SSL implementation written 6 * by Eric Young (eay@cryptsoft.com). 7 * The implementation was written so as to conform with Netscapes SSL. 8 * 9 * This library is free for commercial and non-commercial use as long as 10 * the following conditions are aheared to. The following conditions 11 * apply to all code found in this distribution, be it the RC4, RSA, 12 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13 * included with this distribution is covered by the same copyright terms 14 * except that the holder is Tim Hudson (tjh@cryptsoft.com). 15 * 16 * Copyright remains Eric Young's, and as such any Copyright notices in 17 * the code are not to be removed. 18 * If this package is used in a product, Eric Young should be given attribution 19 * as the author of the parts of the library used. 20 * This can be in the form of a textual message at program startup or 21 * in documentation (online or textual) provided with the package. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * "This product includes cryptographic software written by 34 * Eric Young (eay@cryptsoft.com)" 35 * The word 'cryptographic' can be left out if the rouines from the library 36 * being used are not cryptographic related :-). 37 * 4. If you include any Windows specific code (or a derivative thereof) from 38 * the apps directory (application code) you must include an acknowledgement: 39 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 40 * 41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * The licence and distribution terms for any publically available version or 54 * derivative of this code cannot be changed. i.e. this code cannot simply be 55 * copied and put under another distribution licence 56 * [including the GNU Public Licence.] 57 */ 58 59#include <stdio.h> 60#include <errno.h> 61#include "cryptlib.h" 62#include <openssl/buffer.h> 63#include <openssl/evp.h> 64 65static int b64_write(BIO *h, const char *buf, int num); 66static int b64_read(BIO *h, char *buf, int size); 67/*static int b64_puts(BIO *h, const char *str); */ 68/*static int b64_gets(BIO *h, char *str, int size); */ 69static long b64_ctrl(BIO *h, int cmd, long arg1, void *arg2); 70static int b64_new(BIO *h); 71static int b64_free(BIO *data); 72static long b64_callback_ctrl(BIO *h,int cmd,bio_info_cb *fp); 73#define B64_BLOCK_SIZE 1024 74#define B64_BLOCK_SIZE2 768 75#define B64_NONE 0 76#define B64_ENCODE 1 77#define B64_DECODE 2 78 79typedef struct b64_struct 80 { 81 /*BIO *bio; moved to the BIO structure */ 82 int buf_len; 83 int buf_off; 84 int tmp_len; /* used to find the start when decoding */ 85 int tmp_nl; /* If true, scan until '\n' */ 86 int encode; 87 int start; /* have we started decoding yet? */ 88 int cont; /* <= 0 when finished */ 89 EVP_ENCODE_CTX base64; 90 char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE)+10]; 91 char tmp[B64_BLOCK_SIZE]; 92 } BIO_B64_CTX; 93 94static BIO_METHOD methods_b64= 95 { 96 BIO_TYPE_BASE64,"base64 encoding", 97 b64_write, 98 b64_read, 99 NULL, /* b64_puts, */ 100 NULL, /* b64_gets, */ 101 b64_ctrl, 102 b64_new, 103 b64_free, 104 b64_callback_ctrl, 105 }; 106 107BIO_METHOD *BIO_f_base64(void) 108 { 109 return(&methods_b64); 110 } 111 112static int b64_new(BIO *bi) 113 { 114 BIO_B64_CTX *ctx; 115 116 ctx=(BIO_B64_CTX *)OPENSSL_malloc(sizeof(BIO_B64_CTX)); 117 if (ctx == NULL) return(0); 118 119 ctx->buf_len=0; 120 ctx->tmp_len=0; 121 ctx->tmp_nl=0; 122 ctx->buf_off=0; 123 ctx->cont=1; 124 ctx->start=1; 125 ctx->encode=0; 126 127 bi->init=1; 128 bi->ptr=(char *)ctx; 129 bi->flags=0; 130 return(1); 131 } 132 133static int b64_free(BIO *a) 134 { 135 if (a == NULL) return(0); 136 OPENSSL_free(a->ptr); 137 a->ptr=NULL; 138 a->init=0; 139 a->flags=0; 140 return(1); 141 } 142 143static int b64_read(BIO *b, char *out, int outl) 144 { 145 int ret=0,i,ii,j,k,x,n,num,ret_code=0; 146 BIO_B64_CTX *ctx; 147 unsigned char *p,*q; 148 149 if (out == NULL) return(0); 150 ctx=(BIO_B64_CTX *)b->ptr; 151 152 if ((ctx == NULL) || (b->next_bio == NULL)) return(0); 153 154 if (ctx->encode != B64_DECODE) 155 { 156 ctx->encode=B64_DECODE; 157 ctx->buf_len=0; 158 ctx->buf_off=0; 159 ctx->tmp_len=0; 160 EVP_DecodeInit(&(ctx->base64)); 161 } 162 163 /* First check if there are bytes decoded/encoded */ 164 if (ctx->buf_len > 0) 165 { 166 i=ctx->buf_len-ctx->buf_off; 167 if (i > outl) i=outl; 168 memcpy(out,&(ctx->buf[ctx->buf_off]),i); 169 ret=i; 170 out+=i; 171 outl-=i; 172 ctx->buf_off+=i; 173 if (ctx->buf_len == ctx->buf_off) 174 { 175 ctx->buf_len=0; 176 ctx->buf_off=0; 177 } 178 } 179 180 /* At this point, we have room of outl bytes and an empty 181 * buffer, so we should read in some more. */ 182 183 ret_code=0; 184 while (outl > 0) 185 { 186 if (ctx->cont <= 0) break; 187 188 i=BIO_read(b->next_bio,&(ctx->tmp[ctx->tmp_len]), 189 B64_BLOCK_SIZE-ctx->tmp_len); 190 191 if (i <= 0) 192 { 193 ret_code=i; 194 195 /* Should be continue next time we are called? */ 196 if (!BIO_should_retry(b->next_bio)) 197 ctx->cont=i; 198 /* else we should continue when called again */ 199 break; 200 } 201 i+=ctx->tmp_len; 202 203 /* We need to scan, a line at a time until we 204 * have a valid line if we are starting. */ 205 if (ctx->start && (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)) 206 { 207 /* ctx->start=1; */ 208 ctx->tmp_len=0; 209 } 210 else if (ctx->start) 211 { 212 q=p=(unsigned char *)ctx->tmp; 213 for (j=0; j<i; j++) 214 { 215 if (*(q++) != '\n') continue; 216 217 /* due to a previous very long line, 218 * we need to keep on scanning for a '\n' 219 * before we even start looking for 220 * base64 encoded stuff. */ 221 if (ctx->tmp_nl) 222 { 223 p=q; 224 ctx->tmp_nl=0; 225 continue; 226 } 227 228 k=EVP_DecodeUpdate(&(ctx->base64), 229 (unsigned char *)ctx->buf, 230 &num,p,q-p); 231 if ((k <= 0) && (num == 0) && (ctx->start)) 232 EVP_DecodeInit(&ctx->base64); 233 else 234 { 235 if (p != (unsigned char *) 236 &(ctx->tmp[0])) 237 { 238 i-=(p- (unsigned char *) 239 &(ctx->tmp[0])); 240 for (x=0; x < i; x++) 241 ctx->tmp[x]=p[x]; 242 } 243 EVP_DecodeInit(&ctx->base64); 244 ctx->start=0; 245 break; 246 } 247 p=q; 248 } 249 250 /* we fell off the end without starting */ 251 if (j == i) 252 { 253 /* Is this is one long chunk?, if so, keep on 254 * reading until a new line. */ 255 if (p == (unsigned char *)&(ctx->tmp[0])) 256 { 257 ctx->tmp_nl=1; 258 ctx->tmp_len=0; 259 } 260 else if (p != q) /* finished on a '\n' */ 261 { 262 n=q-p; 263 for (ii=0; ii<n; ii++) 264 ctx->tmp[ii]=p[ii]; 265 ctx->tmp_len=n; 266 } 267 /* else finished on a '\n' */ 268 continue; 269 } 270 else 271 ctx->tmp_len=0; 272 } 273 274 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 275 { 276 int z,jj; 277 278 jj=(i>>2)<<2; 279 z=EVP_DecodeBlock((unsigned char *)ctx->buf, 280 (unsigned char *)ctx->tmp,jj); 281 if (jj > 2) 282 { 283 if (ctx->tmp[jj-1] == '=') 284 { 285 z--; 286 if (ctx->tmp[jj-2] == '=') 287 z--; 288 } 289 } 290 /* z is now number of output bytes and jj is the 291 * number consumed */ 292 if (jj != i) 293 { 294 memcpy((unsigned char *)ctx->tmp, 295 (unsigned char *)&(ctx->tmp[jj]),i-jj); 296 ctx->tmp_len=i-jj; 297 } 298 ctx->buf_len=0; 299 if (z > 0) 300 { 301 ctx->buf_len=z; 302 i=1; 303 } 304 else 305 i=z; 306 } 307 else 308 { 309 i=EVP_DecodeUpdate(&(ctx->base64), 310 (unsigned char *)ctx->buf,&ctx->buf_len, 311 (unsigned char *)ctx->tmp,i); 312 } 313 ctx->cont=i; 314 ctx->buf_off=0; 315 if (i < 0) 316 { 317 ret_code=0; 318 ctx->buf_len=0; 319 break; 320 } 321 322 if (ctx->buf_len <= outl) 323 i=ctx->buf_len; 324 else 325 i=outl; 326 327 memcpy(out,ctx->buf,i); 328 ret+=i; 329 ctx->buf_off=i; 330 if (ctx->buf_off == ctx->buf_len) 331 { 332 ctx->buf_len=0; 333 ctx->buf_off=0; 334 } 335 outl-=i; 336 out+=i; 337 } 338 BIO_clear_retry_flags(b); 339 BIO_copy_next_retry(b); 340 return((ret == 0)?ret_code:ret); 341 } 342 343static int b64_write(BIO *b, const char *in, int inl) 344 { 345 int ret=inl,n,i; 346 BIO_B64_CTX *ctx; 347 348 ctx=(BIO_B64_CTX *)b->ptr; 349 BIO_clear_retry_flags(b); 350 351 if (ctx->encode != B64_ENCODE) 352 { 353 ctx->encode=B64_ENCODE; 354 ctx->buf_len=0; 355 ctx->buf_off=0; 356 ctx->tmp_len=0; 357 EVP_EncodeInit(&(ctx->base64)); 358 } 359 360 n=ctx->buf_len-ctx->buf_off; 361 while (n > 0) 362 { 363 i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n); 364 if (i <= 0) 365 { 366 BIO_copy_next_retry(b); 367 return(i); 368 } 369 ctx->buf_off+=i; 370 n-=i; 371 } 372 /* at this point all pending data has been written */ 373 ctx->buf_off=0; 374 ctx->buf_len=0; 375 376 if ((in == NULL) || (inl <= 0)) return(0); 377 378 while (inl > 0) 379 { 380 n=(inl > B64_BLOCK_SIZE)?B64_BLOCK_SIZE:inl; 381 382 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 383 { 384 if (ctx->tmp_len > 0) 385 { 386 n=3-ctx->tmp_len; 387 /* There's a teoretical possibility for this */ 388 if (n > inl) 389 n=inl; 390 memcpy(&(ctx->tmp[ctx->tmp_len]),in,n); 391 ctx->tmp_len+=n; 392 if (ctx->tmp_len < 3) 393 break; 394 ctx->buf_len=EVP_EncodeBlock( 395 (unsigned char *)ctx->buf, 396 (unsigned char *)ctx->tmp, 397 ctx->tmp_len); 398 /* Since we're now done using the temporary 399 buffer, the length should be 0'd */ 400 ctx->tmp_len=0; 401 } 402 else 403 { 404 if (n < 3) 405 { 406 memcpy(&(ctx->tmp[0]),in,n); 407 ctx->tmp_len=n; 408 break; 409 } 410 n-=n%3; 411 ctx->buf_len=EVP_EncodeBlock( 412 (unsigned char *)ctx->buf, 413 (unsigned char *)in,n); 414 } 415 } 416 else 417 { 418 EVP_EncodeUpdate(&(ctx->base64), 419 (unsigned char *)ctx->buf,&ctx->buf_len, 420 (unsigned char *)in,n); 421 } 422 inl-=n; 423 in+=n; 424 425 ctx->buf_off=0; 426 n=ctx->buf_len; 427 while (n > 0) 428 { 429 i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n); 430 if (i <= 0) 431 { 432 BIO_copy_next_retry(b); 433 return((ret == 0)?i:ret); 434 } 435 n-=i; 436 ctx->buf_off+=i; 437 } 438 ctx->buf_len=0; 439 ctx->buf_off=0; 440 } 441 return(ret); 442 } 443 444static long b64_ctrl(BIO *b, int cmd, long num, void *ptr) 445 { 446 BIO_B64_CTX *ctx; 447 long ret=1; 448 int i; 449 450 ctx=(BIO_B64_CTX *)b->ptr; 451 452 switch (cmd) 453 { 454 case BIO_CTRL_RESET: 455 ctx->cont=1; 456 ctx->start=1; 457 ctx->encode=B64_NONE; 458 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 459 break; 460 case BIO_CTRL_EOF: /* More to read */ 461 if (ctx->cont <= 0) 462 ret=1; 463 else 464 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 465 break; 466 case BIO_CTRL_WPENDING: /* More to write in buffer */ 467 ret=ctx->buf_len-ctx->buf_off; 468 if ((ret == 0) && (ctx->encode != B64_NONE) 469 && (ctx->base64.num != 0)) 470 ret=1; 471 else if (ret <= 0) 472 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 473 break; 474 case BIO_CTRL_PENDING: /* More to read in buffer */ 475 ret=ctx->buf_len-ctx->buf_off; 476 if (ret <= 0) 477 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 478 break; 479 case BIO_CTRL_FLUSH: 480 /* do a final write */ 481again: 482 while (ctx->buf_len != ctx->buf_off) 483 { 484 i=b64_write(b,NULL,0); 485 if (i < 0) 486 { 487 ret=i; 488 break; 489 } 490 } 491 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 492 { 493 if (ctx->tmp_len != 0) 494 { 495 ctx->buf_len=EVP_EncodeBlock( 496 (unsigned char *)ctx->buf, 497 (unsigned char *)ctx->tmp, 498 ctx->tmp_len); 499 ctx->buf_off=0; 500 ctx->tmp_len=0; 501 goto again; 502 } 503 } 504 else if (ctx->encode != B64_NONE && ctx->base64.num != 0) 505 { 506 ctx->buf_off=0; 507 EVP_EncodeFinal(&(ctx->base64), 508 (unsigned char *)ctx->buf, 509 &(ctx->buf_len)); 510 /* push out the bytes */ 511 goto again; 512 } 513 /* Finally flush the underlying BIO */ 514 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 515 break; 516 517 case BIO_C_DO_STATE_MACHINE: 518 BIO_clear_retry_flags(b); 519 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 520 BIO_copy_next_retry(b); 521 break; 522 523 case BIO_CTRL_DUP: 524 break; 525 case BIO_CTRL_INFO: 526 case BIO_CTRL_GET: 527 case BIO_CTRL_SET: 528 default: 529 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 530 break; 531 } 532 return(ret); 533 } 534 535static long b64_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp) 536 { 537 long ret=1; 538 539 if (b->next_bio == NULL) return(0); 540 switch (cmd) 541 { 542 default: 543 ret=BIO_callback_ctrl(b->next_bio,cmd,fp); 544 break; 545 } 546 return(ret); 547 } 548 549