bio_b64.c revision 59191
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,char *buf,int num); 66static int b64_read(BIO *h,char *buf,int size); 67/*static int b64_puts(BIO *h,char *str); */ 68/*static int b64_gets(BIO *h,char *str,int size); */ 69static long b64_ctrl(BIO *h,int cmd,long arg1,char *arg2); 70static int b64_new(BIO *h); 71static int b64_free(BIO *data); 72static long b64_callback_ctrl(BIO *h,int cmd,void (*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 *)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 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, 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 374 if ((in == NULL) || (inl <= 0)) return(0); 375 376 ctx->buf_off=0; 377 while (inl > 0) 378 { 379 n=(inl > B64_BLOCK_SIZE)?B64_BLOCK_SIZE:inl; 380 381 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 382 { 383 if (ctx->tmp_len > 0) 384 { 385 n=3-ctx->tmp_len; 386 memcpy(&(ctx->tmp[ctx->tmp_len]),in,n); 387 ctx->tmp_len+=n; 388 n=ctx->tmp_len; 389 if (n < 3) 390 break; 391 ctx->buf_len=EVP_EncodeBlock( 392 (unsigned char *)ctx->buf, 393 (unsigned char *)ctx->tmp,n); 394 } 395 else 396 { 397 if (n < 3) 398 { 399 memcpy(&(ctx->tmp[0]),in,n); 400 ctx->tmp_len=n; 401 break; 402 } 403 n-=n%3; 404 ctx->buf_len=EVP_EncodeBlock( 405 (unsigned char *)ctx->buf, 406 (unsigned char *)in,n); 407 } 408 } 409 else 410 { 411 EVP_EncodeUpdate(&(ctx->base64), 412 (unsigned char *)ctx->buf,&ctx->buf_len, 413 (unsigned char *)in,n); 414 } 415 inl-=n; 416 in+=n; 417 418 ctx->buf_off=0; 419 n=ctx->buf_len; 420 while (n > 0) 421 { 422 i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n); 423 if (i <= 0) 424 { 425 BIO_copy_next_retry(b); 426 return((ret == 0)?i:ret); 427 } 428 n-=i; 429 ctx->buf_off+=i; 430 } 431 ctx->buf_len=0; 432 ctx->buf_off=0; 433 } 434 return(ret); 435 } 436 437static long b64_ctrl(BIO *b, int cmd, long num, char *ptr) 438 { 439 BIO_B64_CTX *ctx; 440 long ret=1; 441 int i; 442 443 ctx=(BIO_B64_CTX *)b->ptr; 444 445 switch (cmd) 446 { 447 case BIO_CTRL_RESET: 448 ctx->cont=1; 449 ctx->start=1; 450 ctx->encode=B64_NONE; 451 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 452 break; 453 case BIO_CTRL_EOF: /* More to read */ 454 if (ctx->cont <= 0) 455 ret=1; 456 else 457 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 458 break; 459 case BIO_CTRL_WPENDING: /* More to write in buffer */ 460 ret=ctx->buf_len-ctx->buf_off; 461 if ((ret == 0) && (ctx->base64.num != 0)) 462 ret=1; 463 else if (ret <= 0) 464 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 465 break; 466 case BIO_CTRL_PENDING: /* More to read in buffer */ 467 ret=ctx->buf_len-ctx->buf_off; 468 if (ret <= 0) 469 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 470 break; 471 case BIO_CTRL_FLUSH: 472 /* do a final write */ 473again: 474 while (ctx->buf_len != ctx->buf_off) 475 { 476 i=b64_write(b,NULL,0); 477 if (i < 0) 478 { 479 ret=i; 480 break; 481 } 482 } 483 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 484 { 485 if (ctx->tmp_len != 0) 486 { 487 ctx->buf_len=EVP_EncodeBlock( 488 (unsigned char *)ctx->buf, 489 (unsigned char *)ctx->tmp, 490 ctx->tmp_len); 491 ctx->buf_off=0; 492 ctx->tmp_len=0; 493 goto again; 494 } 495 } 496 else if (ctx->base64.num != 0) 497 { 498 ctx->buf_off=0; 499 EVP_EncodeFinal(&(ctx->base64), 500 (unsigned char *)ctx->buf, 501 &(ctx->buf_len)); 502 /* push out the bytes */ 503 goto again; 504 } 505 /* Finally flush the underlying BIO */ 506 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 507 break; 508 509 case BIO_C_DO_STATE_MACHINE: 510 BIO_clear_retry_flags(b); 511 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 512 BIO_copy_next_retry(b); 513 break; 514 515 case BIO_CTRL_DUP: 516 break; 517 case BIO_CTRL_INFO: 518 case BIO_CTRL_GET: 519 case BIO_CTRL_SET: 520 default: 521 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 522 break; 523 } 524 return(ret); 525 } 526 527static long b64_callback_ctrl(BIO *b, int cmd, void (*fp)()) 528 { 529 long ret=1; 530 531 if (b->next_bio == NULL) return(0); 532 switch (cmd) 533 { 534 default: 535 ret=BIO_callback_ctrl(b->next_bio,cmd,fp); 536 break; 537 } 538 return(ret); 539 } 540 541