base64.c revision 290001
1290001Sglebius/* 2290001Sglebius * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") 3290001Sglebius * Copyright (C) 1998-2001, 2003 Internet Software Consortium. 4290001Sglebius * 5290001Sglebius * Permission to use, copy, modify, and/or distribute this software for any 6290001Sglebius * purpose with or without fee is hereby granted, provided that the above 7290001Sglebius * copyright notice and this permission notice appear in all copies. 8290001Sglebius * 9290001Sglebius * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10290001Sglebius * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11290001Sglebius * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12290001Sglebius * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13290001Sglebius * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14290001Sglebius * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15290001Sglebius * PERFORMANCE OF THIS SOFTWARE. 16290001Sglebius */ 17290001Sglebius 18290001Sglebius/* $Id: base64.c,v 1.34 2009/10/21 23:48:05 tbox Exp $ */ 19290001Sglebius 20290001Sglebius/*! \file */ 21290001Sglebius 22290001Sglebius#include <config.h> 23290001Sglebius 24290001Sglebius#include <isc/base64.h> 25290001Sglebius#include <isc/buffer.h> 26290001Sglebius#include <isc/lex.h> 27290001Sglebius#include <isc/string.h> 28290001Sglebius#include <isc/util.h> 29290001Sglebius 30290001Sglebius#define RETERR(x) do { \ 31290001Sglebius isc_result_t _r = (x); \ 32290001Sglebius if (_r != ISC_R_SUCCESS) \ 33290001Sglebius return (_r); \ 34290001Sglebius } while (0) 35290001Sglebius 36290001Sglebius 37290001Sglebius/*@{*/ 38290001Sglebius/*! 39290001Sglebius * These static functions are also present in lib/dns/rdata.c. I'm not 40290001Sglebius * sure where they should go. -- bwelling 41290001Sglebius */ 42290001Sglebiusstatic isc_result_t 43290001Sglebiusstr_totext(const char *source, isc_buffer_t *target); 44290001Sglebius 45290001Sglebiusstatic isc_result_t 46290001Sglebiusmem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); 47290001Sglebius 48290001Sglebiusstatic const char base64[] = 49290001Sglebius "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 50290001Sglebius/*@}*/ 51290001Sglebius 52290001Sglebiusisc_result_t 53290001Sglebiusisc_base64_totext(isc_region_t *source, int wordlength, 54290001Sglebius const char *wordbreak, isc_buffer_t *target) 55290001Sglebius{ 56290001Sglebius char buf[5]; 57290001Sglebius unsigned int loops = 0; 58290001Sglebius 59290001Sglebius if (wordlength < 4) 60290001Sglebius wordlength = 4; 61290001Sglebius 62290001Sglebius memset(buf, 0, sizeof(buf)); 63290001Sglebius while (source->length > 2) { 64290001Sglebius buf[0] = base64[(source->base[0]>>2)&0x3f]; 65290001Sglebius buf[1] = base64[((source->base[0]<<4)&0x30)| 66290001Sglebius ((source->base[1]>>4)&0x0f)]; 67290001Sglebius buf[2] = base64[((source->base[1]<<2)&0x3c)| 68290001Sglebius ((source->base[2]>>6)&0x03)]; 69290001Sglebius buf[3] = base64[source->base[2]&0x3f]; 70290001Sglebius RETERR(str_totext(buf, target)); 71290001Sglebius isc_region_consume(source, 3); 72290001Sglebius 73290001Sglebius loops++; 74290001Sglebius if (source->length != 0 && 75290001Sglebius (int)((loops + 1) * 4) >= wordlength) 76290001Sglebius { 77290001Sglebius loops = 0; 78290001Sglebius RETERR(str_totext(wordbreak, target)); 79290001Sglebius } 80290001Sglebius } 81290001Sglebius if (source->length == 2) { 82290001Sglebius buf[0] = base64[(source->base[0]>>2)&0x3f]; 83290001Sglebius buf[1] = base64[((source->base[0]<<4)&0x30)| 84290001Sglebius ((source->base[1]>>4)&0x0f)]; 85290001Sglebius buf[2] = base64[((source->base[1]<<2)&0x3c)]; 86290001Sglebius buf[3] = '='; 87290001Sglebius RETERR(str_totext(buf, target)); 88290001Sglebius isc_region_consume(source, 2); 89290001Sglebius } else if (source->length == 1) { 90290001Sglebius buf[0] = base64[(source->base[0]>>2)&0x3f]; 91290001Sglebius buf[1] = base64[((source->base[0]<<4)&0x30)]; 92290001Sglebius buf[2] = buf[3] = '='; 93290001Sglebius RETERR(str_totext(buf, target)); 94290001Sglebius isc_region_consume(source, 1); 95290001Sglebius } 96290001Sglebius return (ISC_R_SUCCESS); 97290001Sglebius} 98290001Sglebius 99290001Sglebius/*% 100290001Sglebius * State of a base64 decoding process in progress. 101290001Sglebius */ 102290001Sglebiustypedef struct { 103290001Sglebius int length; /*%< Desired length of binary data or -1 */ 104290001Sglebius isc_buffer_t *target; /*%< Buffer for resulting binary data */ 105290001Sglebius int digits; /*%< Number of buffered base64 digits */ 106290001Sglebius isc_boolean_t seen_end; /*%< True if "=" end marker seen */ 107290001Sglebius int val[4]; 108290001Sglebius} base64_decode_ctx_t; 109290001Sglebius 110290001Sglebiusstatic inline void 111290001Sglebiusbase64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) 112290001Sglebius{ 113290001Sglebius ctx->digits = 0; 114290001Sglebius ctx->seen_end = ISC_FALSE; 115290001Sglebius ctx->length = length; 116290001Sglebius ctx->target = target; 117290001Sglebius} 118290001Sglebius 119290001Sglebiusstatic inline isc_result_t 120290001Sglebiusbase64_decode_char(base64_decode_ctx_t *ctx, int c) { 121290001Sglebius char *s; 122290001Sglebius 123290001Sglebius if (ctx->seen_end) 124290001Sglebius return (ISC_R_BADBASE64); 125290001Sglebius if ((s = strchr(base64, c)) == NULL) 126290001Sglebius return (ISC_R_BADBASE64); 127290001Sglebius ctx->val[ctx->digits++] = s - base64; 128290001Sglebius if (ctx->digits == 4) { 129290001Sglebius int n; 130290001Sglebius unsigned char buf[3]; 131290001Sglebius if (ctx->val[0] == 64 || ctx->val[1] == 64) 132290001Sglebius return (ISC_R_BADBASE64); 133290001Sglebius if (ctx->val[2] == 64 && ctx->val[3] != 64) 134290001Sglebius return (ISC_R_BADBASE64); 135290001Sglebius /* 136290001Sglebius * Check that bits that should be zero are. 137290001Sglebius */ 138290001Sglebius if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) 139290001Sglebius return (ISC_R_BADBASE64); 140290001Sglebius /* 141290001Sglebius * We don't need to test for ctx->val[2] != 64 as 142290001Sglebius * the bottom two bits of 64 are zero. 143290001Sglebius */ 144290001Sglebius if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) 145290001Sglebius return (ISC_R_BADBASE64); 146290001Sglebius n = (ctx->val[2] == 64) ? 1 : 147290001Sglebius (ctx->val[3] == 64) ? 2 : 3; 148290001Sglebius if (n != 3) { 149290001Sglebius ctx->seen_end = ISC_TRUE; 150290001Sglebius if (ctx->val[2] == 64) 151290001Sglebius ctx->val[2] = 0; 152290001Sglebius if (ctx->val[3] == 64) 153290001Sglebius ctx->val[3] = 0; 154290001Sglebius } 155290001Sglebius buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); 156290001Sglebius buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); 157290001Sglebius buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); 158290001Sglebius RETERR(mem_tobuffer(ctx->target, buf, n)); 159290001Sglebius if (ctx->length >= 0) { 160290001Sglebius if (n > ctx->length) 161290001Sglebius return (ISC_R_BADBASE64); 162290001Sglebius else 163290001Sglebius ctx->length -= n; 164290001Sglebius } 165290001Sglebius ctx->digits = 0; 166290001Sglebius } 167290001Sglebius return (ISC_R_SUCCESS); 168290001Sglebius} 169290001Sglebius 170290001Sglebiusstatic inline isc_result_t 171290001Sglebiusbase64_decode_finish(base64_decode_ctx_t *ctx) { 172290001Sglebius if (ctx->length > 0) 173290001Sglebius return (ISC_R_UNEXPECTEDEND); 174290001Sglebius if (ctx->digits != 0) 175290001Sglebius return (ISC_R_BADBASE64); 176290001Sglebius return (ISC_R_SUCCESS); 177290001Sglebius} 178290001Sglebius 179290001Sglebiusisc_result_t 180290001Sglebiusisc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { 181290001Sglebius base64_decode_ctx_t ctx; 182290001Sglebius isc_textregion_t *tr; 183290001Sglebius isc_token_t token; 184290001Sglebius isc_boolean_t eol; 185290001Sglebius 186290001Sglebius base64_decode_init(&ctx, length, target); 187290001Sglebius 188290001Sglebius while (!ctx.seen_end && (ctx.length != 0)) { 189290001Sglebius unsigned int i; 190290001Sglebius 191290001Sglebius if (length > 0) 192290001Sglebius eol = ISC_FALSE; 193290001Sglebius else 194290001Sglebius eol = ISC_TRUE; 195290001Sglebius RETERR(isc_lex_getmastertoken(lexer, &token, 196290001Sglebius isc_tokentype_string, eol)); 197290001Sglebius if (token.type != isc_tokentype_string) 198290001Sglebius break; 199290001Sglebius tr = &token.value.as_textregion; 200290001Sglebius for (i = 0; i < tr->length; i++) 201290001Sglebius RETERR(base64_decode_char(&ctx, tr->base[i])); 202290001Sglebius } 203290001Sglebius if (ctx.length < 0 && !ctx.seen_end) 204290001Sglebius isc_lex_ungettoken(lexer, &token); 205290001Sglebius RETERR(base64_decode_finish(&ctx)); 206290001Sglebius return (ISC_R_SUCCESS); 207290001Sglebius} 208290001Sglebius 209290001Sglebiusisc_result_t 210290001Sglebiusisc_base64_decodestring(const char *cstr, isc_buffer_t *target) { 211290001Sglebius base64_decode_ctx_t ctx; 212290001Sglebius 213290001Sglebius base64_decode_init(&ctx, -1, target); 214290001Sglebius for (;;) { 215290001Sglebius int c = *cstr++; 216290001Sglebius if (c == '\0') 217290001Sglebius break; 218290001Sglebius if (c == ' ' || c == '\t' || c == '\n' || c== '\r') 219290001Sglebius continue; 220290001Sglebius RETERR(base64_decode_char(&ctx, c)); 221290001Sglebius } 222290001Sglebius RETERR(base64_decode_finish(&ctx)); 223290001Sglebius return (ISC_R_SUCCESS); 224290001Sglebius} 225290001Sglebius 226290001Sglebiusstatic isc_result_t 227290001Sglebiusstr_totext(const char *source, isc_buffer_t *target) { 228290001Sglebius unsigned int l; 229290001Sglebius isc_region_t region; 230290001Sglebius 231290001Sglebius isc_buffer_availableregion(target, ®ion); 232290001Sglebius l = strlen(source); 233290001Sglebius 234290001Sglebius if (l > region.length) 235290001Sglebius return (ISC_R_NOSPACE); 236290001Sglebius 237290001Sglebius memcpy(region.base, source, l); 238290001Sglebius isc_buffer_add(target, l); 239290001Sglebius return (ISC_R_SUCCESS); 240290001Sglebius} 241290001Sglebius 242290001Sglebiusstatic isc_result_t 243290001Sglebiusmem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { 244290001Sglebius isc_region_t tr; 245290001Sglebius 246290001Sglebius isc_buffer_availableregion(target, &tr); 247290001Sglebius if (length > tr.length) 248290001Sglebius return (ISC_R_NOSPACE); 249290001Sglebius memcpy(tr.base, base, length); 250290001Sglebius isc_buffer_add(target, length); 251290001Sglebius return (ISC_R_SUCCESS); 252290001Sglebius} 253