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, &region);
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