bio_b64.c revision 55714
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); 72#define B64_BLOCK_SIZE 1024 73#define B64_BLOCK_SIZE2 768 74#define B64_NONE 0 75#define B64_ENCODE 1 76#define B64_DECODE 2 77 78typedef struct b64_struct 79 { 80 /*BIO *bio; moved to the BIO structure */ 81 int buf_len; 82 int buf_off; 83 int tmp_len; /* used to find the start when decoding */ 84 int tmp_nl; /* If true, scan until '\n' */ 85 int encode; 86 int start; /* have we started decoding yet? */ 87 int cont; /* <= 0 when finished */ 88 EVP_ENCODE_CTX base64; 89 char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE)+10]; 90 char tmp[B64_BLOCK_SIZE]; 91 } BIO_B64_CTX; 92 93static BIO_METHOD methods_b64= 94 { 95 BIO_TYPE_BASE64,"base64 encoding", 96 b64_write, 97 b64_read, 98 NULL, /* b64_puts, */ 99 NULL, /* b64_gets, */ 100 b64_ctrl, 101 b64_new, 102 b64_free, 103 }; 104 105BIO_METHOD *BIO_f_base64(void) 106 { 107 return(&methods_b64); 108 } 109 110static int b64_new(BIO *bi) 111 { 112 BIO_B64_CTX *ctx; 113 114 ctx=(BIO_B64_CTX *)Malloc(sizeof(BIO_B64_CTX)); 115 if (ctx == NULL) return(0); 116 117 ctx->buf_len=0; 118 ctx->tmp_len=0; 119 ctx->tmp_nl=0; 120 ctx->buf_off=0; 121 ctx->cont=1; 122 ctx->start=1; 123 ctx->encode=0; 124 125 bi->init=1; 126 bi->ptr=(char *)ctx; 127 bi->flags=0; 128 return(1); 129 } 130 131static int b64_free(BIO *a) 132 { 133 if (a == NULL) return(0); 134 Free(a->ptr); 135 a->ptr=NULL; 136 a->init=0; 137 a->flags=0; 138 return(1); 139 } 140 141static int b64_read(BIO *b, char *out, int outl) 142 { 143 int ret=0,i,ii,j,k,x,n,num,ret_code=0; 144 BIO_B64_CTX *ctx; 145 unsigned char *p,*q; 146 147 if (out == NULL) return(0); 148 ctx=(BIO_B64_CTX *)b->ptr; 149 150 if ((ctx == NULL) || (b->next_bio == NULL)) return(0); 151 152 if (ctx->encode != B64_DECODE) 153 { 154 ctx->encode=B64_DECODE; 155 ctx->buf_len=0; 156 ctx->buf_off=0; 157 ctx->tmp_len=0; 158 EVP_DecodeInit(&(ctx->base64)); 159 } 160 161 /* First check if there are bytes decoded/encoded */ 162 if (ctx->buf_len > 0) 163 { 164 i=ctx->buf_len-ctx->buf_off; 165 if (i > outl) i=outl; 166 memcpy(out,&(ctx->buf[ctx->buf_off]),i); 167 ret=i; 168 out+=i; 169 outl-=i; 170 ctx->buf_off+=i; 171 if (ctx->buf_len == ctx->buf_off) 172 { 173 ctx->buf_len=0; 174 ctx->buf_off=0; 175 } 176 } 177 178 /* At this point, we have room of outl bytes and an empty 179 * buffer, so we should read in some more. */ 180 181 ret_code=0; 182 while (outl > 0) 183 { 184 if (ctx->cont <= 0) break; 185 186 i=BIO_read(b->next_bio,&(ctx->tmp[ctx->tmp_len]), 187 B64_BLOCK_SIZE-ctx->tmp_len); 188 189 if (i <= 0) 190 { 191 ret_code=i; 192 193 /* Should be continue next time we are called? */ 194 if (!BIO_should_retry(b->next_bio)) 195 ctx->cont=i; 196 /* else we should continue when called again */ 197 break; 198 } 199 i+=ctx->tmp_len; 200 201 /* We need to scan, a line at a time until we 202 * have a valid line if we are starting. */ 203 if (ctx->start && (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)) 204 { 205 /* ctx->start=1; */ 206 ctx->tmp_len=0; 207 } 208 else if (ctx->start) 209 { 210 q=p=(unsigned char *)ctx->tmp; 211 for (j=0; j<i; j++) 212 { 213 if (*(q++) != '\n') continue; 214 215 /* due to a previous very long line, 216 * we need to keep on scanning for a '\n' 217 * before we even start looking for 218 * base64 encoded stuff. */ 219 if (ctx->tmp_nl) 220 { 221 p=q; 222 ctx->tmp_nl=0; 223 continue; 224 } 225 226 k=EVP_DecodeUpdate(&(ctx->base64), 227 (unsigned char *)ctx->buf, 228 &num,p,q-p); 229 if ((k <= 0) && (num == 0) && (ctx->start)) 230 EVP_DecodeInit(&ctx->base64); 231 else 232 { 233 if (p != (unsigned char *) 234 &(ctx->tmp[0])) 235 { 236 i-=(p- (unsigned char *) 237 &(ctx->tmp[0])); 238 for (x=0; x < i; x++) 239 ctx->tmp[x]=p[x]; 240 EVP_DecodeInit(&ctx->base64); 241 } 242 ctx->start=0; 243 break; 244 } 245 p=q; 246 } 247 248 /* we fell off the end without starting */ 249 if (j == i) 250 { 251 /* Is this is one long chunk?, if so, keep on 252 * reading until a new line. */ 253 if (p == (unsigned char *)&(ctx->tmp[0])) 254 { 255 ctx->tmp_nl=1; 256 ctx->tmp_len=0; 257 } 258 else if (p != q) /* finished on a '\n' */ 259 { 260 n=q-p; 261 for (ii=0; ii<n; ii++) 262 ctx->tmp[ii]=p[ii]; 263 ctx->tmp_len=n; 264 } 265 /* else finished on a '\n' */ 266 continue; 267 } 268 else 269 ctx->tmp_len=0; 270 } 271 272 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 273 { 274 int z,jj; 275 276 jj=(i>>2)<<2; 277 z=EVP_DecodeBlock((unsigned char *)ctx->buf, 278 (unsigned char *)ctx->tmp,jj); 279 if (jj > 2) 280 { 281 if (ctx->tmp[jj-1] == '=') 282 { 283 z--; 284 if (ctx->tmp[jj-2] == '=') 285 z--; 286 } 287 } 288 /* z is now number of output bytes and jj is the 289 * number consumed */ 290 if (jj != i) 291 { 292 memcpy((unsigned char *)ctx->tmp, 293 (unsigned char *)&(ctx->tmp[jj]),i-jj); 294 ctx->tmp_len=i-jj; 295 } 296 ctx->buf_len=0; 297 if (z > 0) 298 { 299 ctx->buf_len=z; 300 i=1; 301 } 302 else 303 i=z; 304 } 305 else 306 { 307 i=EVP_DecodeUpdate(&(ctx->base64), 308 (unsigned char *)ctx->buf,&ctx->buf_len, 309 (unsigned char *)ctx->tmp,i); 310 } 311 ctx->cont=i; 312 ctx->buf_off=0; 313 if (i < 0) 314 { 315 ret_code=0; 316 ctx->buf_len=0; 317 break; 318 } 319 320 if (ctx->buf_len <= outl) 321 i=ctx->buf_len; 322 else 323 i=outl; 324 325 memcpy(out,ctx->buf,i); 326 ret+=i; 327 ctx->buf_off=i; 328 if (ctx->buf_off == ctx->buf_len) 329 { 330 ctx->buf_len=0; 331 ctx->buf_off=0; 332 } 333 outl-=i; 334 out+=i; 335 } 336 BIO_clear_retry_flags(b); 337 BIO_copy_next_retry(b); 338 return((ret == 0)?ret_code:ret); 339 } 340 341static int b64_write(BIO *b, char *in, int inl) 342 { 343 int ret=inl,n,i; 344 BIO_B64_CTX *ctx; 345 346 ctx=(BIO_B64_CTX *)b->ptr; 347 BIO_clear_retry_flags(b); 348 349 if (ctx->encode != B64_ENCODE) 350 { 351 ctx->encode=B64_ENCODE; 352 ctx->buf_len=0; 353 ctx->buf_off=0; 354 ctx->tmp_len=0; 355 EVP_EncodeInit(&(ctx->base64)); 356 } 357 358 n=ctx->buf_len-ctx->buf_off; 359 while (n > 0) 360 { 361 i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n); 362 if (i <= 0) 363 { 364 BIO_copy_next_retry(b); 365 return(i); 366 } 367 ctx->buf_off+=i; 368 n-=i; 369 } 370 /* at this point all pending data has been written */ 371 372 if ((in == NULL) || (inl <= 0)) return(0); 373 374 ctx->buf_off=0; 375 while (inl > 0) 376 { 377 n=(inl > B64_BLOCK_SIZE)?B64_BLOCK_SIZE:inl; 378 379 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 380 { 381 if (ctx->tmp_len > 0) 382 { 383 n=3-ctx->tmp_len; 384 memcpy(&(ctx->tmp[ctx->tmp_len]),in,n); 385 ctx->tmp_len+=n; 386 n=ctx->tmp_len; 387 if (n < 3) 388 break; 389 ctx->buf_len=EVP_EncodeBlock( 390 (unsigned char *)ctx->buf, 391 (unsigned char *)ctx->tmp,n); 392 } 393 else 394 { 395 if (n < 3) 396 { 397 memcpy(&(ctx->tmp[0]),in,n); 398 ctx->tmp_len=n; 399 break; 400 } 401 n-=n%3; 402 ctx->buf_len=EVP_EncodeBlock( 403 (unsigned char *)ctx->buf, 404 (unsigned char *)in,n); 405 } 406 } 407 else 408 { 409 EVP_EncodeUpdate(&(ctx->base64), 410 (unsigned char *)ctx->buf,&ctx->buf_len, 411 (unsigned char *)in,n); 412 } 413 inl-=n; 414 in+=n; 415 416 ctx->buf_off=0; 417 n=ctx->buf_len; 418 while (n > 0) 419 { 420 i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n); 421 if (i <= 0) 422 { 423 BIO_copy_next_retry(b); 424 return((ret == 0)?i:ret); 425 } 426 n-=i; 427 ctx->buf_off+=i; 428 } 429 ctx->buf_len=0; 430 ctx->buf_off=0; 431 } 432 return(ret); 433 } 434 435static long b64_ctrl(BIO *b, int cmd, long num, char *ptr) 436 { 437 BIO_B64_CTX *ctx; 438 long ret=1; 439 int i; 440 441 ctx=(BIO_B64_CTX *)b->ptr; 442 443 switch (cmd) 444 { 445 case BIO_CTRL_RESET: 446 ctx->cont=1; 447 ctx->start=1; 448 ctx->encode=B64_NONE; 449 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 450 break; 451 case BIO_CTRL_EOF: /* More to read */ 452 if (ctx->cont <= 0) 453 ret=1; 454 else 455 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 456 break; 457 case BIO_CTRL_WPENDING: /* More to write in buffer */ 458 ret=ctx->buf_len-ctx->buf_off; 459 if ((ret == 0) && (ctx->base64.num != 0)) 460 ret=1; 461 else if (ret <= 0) 462 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 463 break; 464 case BIO_CTRL_PENDING: /* More to read in buffer */ 465 ret=ctx->buf_len-ctx->buf_off; 466 if (ret <= 0) 467 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 468 break; 469 case BIO_CTRL_FLUSH: 470 /* do a final write */ 471again: 472 while (ctx->buf_len != ctx->buf_off) 473 { 474 i=b64_write(b,NULL,0); 475 if (i < 0) 476 { 477 ret=i; 478 break; 479 } 480 } 481 if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) 482 { 483 if (ctx->tmp_len != 0) 484 { 485 ctx->buf_len=EVP_EncodeBlock( 486 (unsigned char *)ctx->buf, 487 (unsigned char *)ctx->tmp, 488 ctx->tmp_len); 489 ctx->buf_off=0; 490 ctx->tmp_len=0; 491 goto again; 492 } 493 } 494 else if (ctx->base64.num != 0) 495 { 496 ctx->buf_off=0; 497 EVP_EncodeFinal(&(ctx->base64), 498 (unsigned char *)ctx->buf, 499 &(ctx->buf_len)); 500 /* push out the bytes */ 501 goto again; 502 } 503 /* Finally flush the underlying BIO */ 504 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 505 break; 506 507 case BIO_C_DO_STATE_MACHINE: 508 BIO_clear_retry_flags(b); 509 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 510 BIO_copy_next_retry(b); 511 break; 512 513 case BIO_CTRL_DUP: 514 break; 515 case BIO_CTRL_INFO: 516 case BIO_CTRL_GET: 517 case BIO_CTRL_SET: 518 default: 519 ret=BIO_ctrl(b->next_bio,cmd,num,ptr); 520 break; 521 } 522 return(ret); 523 } 524 525