base64.c revision 135446
1135446Strhodes/* 2135446Strhodes * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1998-2001, 2003 Internet Software Consortium. 4135446Strhodes * 5135446Strhodes * Permission to use, copy, modify, and distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18135446Strhodes/* $Id: base64.c,v 1.23.2.2.2.3 2004/03/06 08:14:27 marka Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <isc/base64.h> 23135446Strhodes#include <isc/buffer.h> 24135446Strhodes#include <isc/lex.h> 25135446Strhodes#include <isc/string.h> 26135446Strhodes#include <isc/util.h> 27135446Strhodes 28135446Strhodes#define RETERR(x) do { \ 29135446Strhodes isc_result_t _r = (x); \ 30135446Strhodes if (_r != ISC_R_SUCCESS) \ 31135446Strhodes return (_r); \ 32135446Strhodes } while (0) 33135446Strhodes 34135446Strhodes 35135446Strhodes/* 36135446Strhodes * These static functions are also present in lib/dns/rdata.c. I'm not 37135446Strhodes * sure where they should go. -- bwelling 38135446Strhodes */ 39135446Strhodesstatic isc_result_t 40135446Strhodesstr_totext(const char *source, isc_buffer_t *target); 41135446Strhodes 42135446Strhodesstatic isc_result_t 43135446Strhodesmem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); 44135446Strhodes 45135446Strhodesstatic const char base64[] = 46135446Strhodes "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 47135446Strhodes 48135446Strhodesisc_result_t 49135446Strhodesisc_base64_totext(isc_region_t *source, int wordlength, 50135446Strhodes const char *wordbreak, isc_buffer_t *target) 51135446Strhodes{ 52135446Strhodes char buf[5]; 53135446Strhodes unsigned int loops = 0; 54135446Strhodes 55135446Strhodes if (wordlength < 4) 56135446Strhodes wordlength = 4; 57135446Strhodes 58135446Strhodes memset(buf, 0, sizeof(buf)); 59135446Strhodes while (source->length > 2) { 60135446Strhodes buf[0] = base64[(source->base[0]>>2)&0x3f]; 61135446Strhodes buf[1] = base64[((source->base[0]<<4)&0x30)| 62135446Strhodes ((source->base[1]>>4)&0x0f)]; 63135446Strhodes buf[2] = base64[((source->base[1]<<2)&0x3c)| 64135446Strhodes ((source->base[2]>>6)&0x03)]; 65135446Strhodes buf[3] = base64[source->base[2]&0x3f]; 66135446Strhodes RETERR(str_totext(buf, target)); 67135446Strhodes isc_region_consume(source, 3); 68135446Strhodes 69135446Strhodes loops++; 70135446Strhodes if (source->length != 0 && 71135446Strhodes (int)((loops + 1) * 4) >= wordlength) 72135446Strhodes { 73135446Strhodes loops = 0; 74135446Strhodes RETERR(str_totext(wordbreak, target)); 75135446Strhodes } 76135446Strhodes } 77135446Strhodes if (source->length == 2) { 78135446Strhodes buf[0] = base64[(source->base[0]>>2)&0x3f]; 79135446Strhodes buf[1] = base64[((source->base[0]<<4)&0x30)| 80135446Strhodes ((source->base[1]>>4)&0x0f)]; 81135446Strhodes buf[2] = base64[((source->base[1]<<2)&0x3c)]; 82135446Strhodes buf[3] = '='; 83135446Strhodes RETERR(str_totext(buf, target)); 84135446Strhodes } else if (source->length == 1) { 85135446Strhodes buf[0] = base64[(source->base[0]>>2)&0x3f]; 86135446Strhodes buf[1] = base64[((source->base[0]<<4)&0x30)]; 87135446Strhodes buf[2] = buf[3] = '='; 88135446Strhodes RETERR(str_totext(buf, target)); 89135446Strhodes } 90135446Strhodes return (ISC_R_SUCCESS); 91135446Strhodes} 92135446Strhodes 93135446Strhodes/* 94135446Strhodes * State of a base64 decoding process in progress. 95135446Strhodes */ 96135446Strhodestypedef struct { 97135446Strhodes int length; /* Desired length of binary data or -1 */ 98135446Strhodes isc_buffer_t *target; /* Buffer for resulting binary data */ 99135446Strhodes int digits; /* Number of buffered base64 digits */ 100135446Strhodes isc_boolean_t seen_end; /* True if "=" end marker seen */ 101135446Strhodes int val[4]; 102135446Strhodes} base64_decode_ctx_t; 103135446Strhodes 104135446Strhodesstatic inline void 105135446Strhodesbase64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) 106135446Strhodes{ 107135446Strhodes ctx->digits = 0; 108135446Strhodes ctx->seen_end = ISC_FALSE; 109135446Strhodes ctx->length = length; 110135446Strhodes ctx->target = target; 111135446Strhodes} 112135446Strhodes 113135446Strhodesstatic inline isc_result_t 114135446Strhodesbase64_decode_char(base64_decode_ctx_t *ctx, int c) { 115135446Strhodes char *s; 116135446Strhodes 117135446Strhodes if (ctx->seen_end) 118135446Strhodes return (ISC_R_BADBASE64); 119135446Strhodes if ((s = strchr(base64, c)) == NULL) 120135446Strhodes return (ISC_R_BADBASE64); 121135446Strhodes ctx->val[ctx->digits++] = s - base64; 122135446Strhodes if (ctx->digits == 4) { 123135446Strhodes int n; 124135446Strhodes unsigned char buf[3]; 125135446Strhodes if (ctx->val[0] == 64 || ctx->val[1] == 64) 126135446Strhodes return (ISC_R_BADBASE64); 127135446Strhodes if (ctx->val[2] == 64 && ctx->val[3] != 64) 128135446Strhodes return (ISC_R_BADBASE64); 129135446Strhodes /* 130135446Strhodes * Check that bits that should be zero are. 131135446Strhodes */ 132135446Strhodes if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) 133135446Strhodes return (ISC_R_BADBASE64); 134135446Strhodes /* 135135446Strhodes * We don't need to test for ctx->val[2] != 64 as 136135446Strhodes * the bottom two bits of 64 are zero. 137135446Strhodes */ 138135446Strhodes if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) 139135446Strhodes return (ISC_R_BADBASE64); 140135446Strhodes n = (ctx->val[2] == 64) ? 1 : 141135446Strhodes (ctx->val[3] == 64) ? 2 : 3; 142135446Strhodes if (n != 3) { 143135446Strhodes ctx->seen_end = ISC_TRUE; 144135446Strhodes if (ctx->val[2] == 64) 145135446Strhodes ctx->val[2] = 0; 146135446Strhodes if (ctx->val[3] == 64) 147135446Strhodes ctx->val[3] = 0; 148135446Strhodes } 149135446Strhodes buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); 150135446Strhodes buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); 151135446Strhodes buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); 152135446Strhodes RETERR(mem_tobuffer(ctx->target, buf, n)); 153135446Strhodes if (ctx->length >= 0) { 154135446Strhodes if (n > ctx->length) 155135446Strhodes return (ISC_R_BADBASE64); 156135446Strhodes else 157135446Strhodes ctx->length -= n; 158135446Strhodes } 159135446Strhodes ctx->digits = 0; 160135446Strhodes } 161135446Strhodes return (ISC_R_SUCCESS); 162135446Strhodes} 163135446Strhodes 164135446Strhodesstatic inline isc_result_t 165135446Strhodesbase64_decode_finish(base64_decode_ctx_t *ctx) { 166135446Strhodes if (ctx->length > 0) 167135446Strhodes return (ISC_R_UNEXPECTEDEND); 168135446Strhodes if (ctx->digits != 0) 169135446Strhodes return (ISC_R_BADBASE64); 170135446Strhodes return (ISC_R_SUCCESS); 171135446Strhodes} 172135446Strhodes 173135446Strhodesisc_result_t 174135446Strhodesisc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { 175135446Strhodes base64_decode_ctx_t ctx; 176135446Strhodes isc_textregion_t *tr; 177135446Strhodes isc_token_t token; 178135446Strhodes isc_boolean_t eol; 179135446Strhodes 180135446Strhodes base64_decode_init(&ctx, length, target); 181135446Strhodes 182135446Strhodes while (!ctx.seen_end && (ctx.length != 0)) { 183135446Strhodes unsigned int i; 184135446Strhodes 185135446Strhodes if (length > 0) 186135446Strhodes eol = ISC_FALSE; 187135446Strhodes else 188135446Strhodes eol = ISC_TRUE; 189135446Strhodes RETERR(isc_lex_getmastertoken(lexer, &token, 190135446Strhodes isc_tokentype_string, eol)); 191135446Strhodes if (token.type != isc_tokentype_string) 192135446Strhodes break; 193135446Strhodes tr = &token.value.as_textregion; 194135446Strhodes for (i = 0; i < tr->length; i++) 195135446Strhodes RETERR(base64_decode_char(&ctx, tr->base[i])); 196135446Strhodes } 197135446Strhodes if (ctx.length < 0 && !ctx.seen_end) 198135446Strhodes isc_lex_ungettoken(lexer, &token); 199135446Strhodes RETERR(base64_decode_finish(&ctx)); 200135446Strhodes return (ISC_R_SUCCESS); 201135446Strhodes} 202135446Strhodes 203135446Strhodesisc_result_t 204135446Strhodesisc_base64_decodestring(const char *cstr, isc_buffer_t *target) { 205135446Strhodes base64_decode_ctx_t ctx; 206135446Strhodes 207135446Strhodes base64_decode_init(&ctx, -1, target); 208135446Strhodes for (;;) { 209135446Strhodes int c = *cstr++; 210135446Strhodes if (c == '\0') 211135446Strhodes break; 212135446Strhodes if (c == ' ' || c == '\t' || c == '\n' || c== '\r') 213135446Strhodes continue; 214135446Strhodes RETERR(base64_decode_char(&ctx, c)); 215135446Strhodes } 216135446Strhodes RETERR(base64_decode_finish(&ctx)); 217135446Strhodes return (ISC_R_SUCCESS); 218135446Strhodes} 219135446Strhodes 220135446Strhodesstatic isc_result_t 221135446Strhodesstr_totext(const char *source, isc_buffer_t *target) { 222135446Strhodes unsigned int l; 223135446Strhodes isc_region_t region; 224135446Strhodes 225135446Strhodes isc_buffer_availableregion(target, ®ion); 226135446Strhodes l = strlen(source); 227135446Strhodes 228135446Strhodes if (l > region.length) 229135446Strhodes return (ISC_R_NOSPACE); 230135446Strhodes 231135446Strhodes memcpy(region.base, source, l); 232135446Strhodes isc_buffer_add(target, l); 233135446Strhodes return (ISC_R_SUCCESS); 234135446Strhodes} 235135446Strhodes 236135446Strhodesstatic isc_result_t 237135446Strhodesmem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { 238135446Strhodes isc_region_t tr; 239135446Strhodes 240135446Strhodes isc_buffer_availableregion(target, &tr); 241135446Strhodes if (length > tr.length) 242135446Strhodes return (ISC_R_NOSPACE); 243135446Strhodes memcpy(tr.base, base, length); 244135446Strhodes isc_buffer_add(target, length); 245135446Strhodes return (ISC_R_SUCCESS); 246135446Strhodes} 247