1/*	$NetBSD: base64.c,v 1.2.6.1 2012/06/05 21:15:07 bouyer Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1998-2001, 2003  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: base64.c,v 1.34 2009/10/21 23:48:05 tbox Exp  */
21
22/*! \file */
23
24#include <config.h>
25
26#include <isc/base64.h>
27#include <isc/buffer.h>
28#include <isc/lex.h>
29#include <isc/string.h>
30#include <isc/util.h>
31
32#define RETERR(x) do { \
33	isc_result_t _r = (x); \
34	if (_r != ISC_R_SUCCESS) \
35		return (_r); \
36	} while (/*CONSTCOND*/0)
37
38
39/*@{*/
40/*!
41 * These static functions are also present in lib/dns/rdata.c.  I'm not
42 * sure where they should go. -- bwelling
43 */
44static isc_result_t
45str_totext(const char *source, isc_buffer_t *target);
46
47static isc_result_t
48mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
49
50static const char base64[] =
51	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
52/*@}*/
53
54isc_result_t
55isc_base64_totext(isc_region_t *source, int wordlength,
56		  const char *wordbreak, isc_buffer_t *target)
57{
58	char buf[5];
59	unsigned int loops = 0;
60
61	if (wordlength < 4)
62		wordlength = 4;
63
64	memset(buf, 0, sizeof(buf));
65	while (source->length > 2) {
66		buf[0] = base64[(source->base[0]>>2)&0x3f];
67		buf[1] = base64[((source->base[0]<<4)&0x30)|
68				((source->base[1]>>4)&0x0f)];
69		buf[2] = base64[((source->base[1]<<2)&0x3c)|
70				((source->base[2]>>6)&0x03)];
71		buf[3] = base64[source->base[2]&0x3f];
72		RETERR(str_totext(buf, target));
73		isc_region_consume(source, 3);
74
75		loops++;
76		if (source->length != 0 &&
77		    (int)((loops + 1) * 4) >= wordlength)
78		{
79			loops = 0;
80			RETERR(str_totext(wordbreak, target));
81		}
82	}
83	if (source->length == 2) {
84		buf[0] = base64[(source->base[0]>>2)&0x3f];
85		buf[1] = base64[((source->base[0]<<4)&0x30)|
86				((source->base[1]>>4)&0x0f)];
87		buf[2] = base64[((source->base[1]<<2)&0x3c)];
88		buf[3] = '=';
89		RETERR(str_totext(buf, target));
90		isc_region_consume(source, 2);
91	} else if (source->length == 1) {
92		buf[0] = base64[(source->base[0]>>2)&0x3f];
93		buf[1] = base64[((source->base[0]<<4)&0x30)];
94		buf[2] = buf[3] = '=';
95		RETERR(str_totext(buf, target));
96		isc_region_consume(source, 1);
97	}
98	return (ISC_R_SUCCESS);
99}
100
101/*%
102 * State of a base64 decoding process in progress.
103 */
104typedef struct {
105	int length;		/*%< Desired length of binary data or -1 */
106	isc_buffer_t *target;	/*%< Buffer for resulting binary data */
107	int digits;		/*%< Number of buffered base64 digits */
108	isc_boolean_t seen_end;	/*%< True if "=" end marker seen */
109	int val[4];
110} base64_decode_ctx_t;
111
112static inline void
113base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target)
114{
115	ctx->digits = 0;
116	ctx->seen_end = ISC_FALSE;
117	ctx->length = length;
118	ctx->target = target;
119}
120
121static inline isc_result_t
122base64_decode_char(base64_decode_ctx_t *ctx, int c) {
123	char *s;
124
125	if (ctx->seen_end)
126		return (ISC_R_BADBASE64);
127	if ((s = strchr(base64, c)) == NULL)
128		return (ISC_R_BADBASE64);
129	ctx->val[ctx->digits++] = s - base64;
130	if (ctx->digits == 4) {
131		int n;
132		unsigned char buf[3];
133		if (ctx->val[0] == 64 || ctx->val[1] == 64)
134			return (ISC_R_BADBASE64);
135		if (ctx->val[2] == 64 && ctx->val[3] != 64)
136			return (ISC_R_BADBASE64);
137		/*
138		 * Check that bits that should be zero are.
139		 */
140		if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0)
141			return (ISC_R_BADBASE64);
142		/*
143		 * We don't need to test for ctx->val[2] != 64 as
144		 * the bottom two bits of 64 are zero.
145		 */
146		if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0)
147			return (ISC_R_BADBASE64);
148		n = (ctx->val[2] == 64) ? 1 :
149			(ctx->val[3] == 64) ? 2 : 3;
150		if (n != 3) {
151			ctx->seen_end = ISC_TRUE;
152			if (ctx->val[2] == 64)
153				ctx->val[2] = 0;
154			if (ctx->val[3] == 64)
155				ctx->val[3] = 0;
156		}
157		buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4);
158		buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2);
159		buf[2] = (ctx->val[2]<<6)|(ctx->val[3]);
160		RETERR(mem_tobuffer(ctx->target, buf, n));
161		if (ctx->length >= 0) {
162			if (n > ctx->length)
163				return (ISC_R_BADBASE64);
164			else
165				ctx->length -= n;
166		}
167		ctx->digits = 0;
168	}
169	return (ISC_R_SUCCESS);
170}
171
172static inline isc_result_t
173base64_decode_finish(base64_decode_ctx_t *ctx) {
174	if (ctx->length > 0)
175		return (ISC_R_UNEXPECTEDEND);
176	if (ctx->digits != 0)
177		return (ISC_R_BADBASE64);
178	return (ISC_R_SUCCESS);
179}
180
181isc_result_t
182isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
183	base64_decode_ctx_t ctx;
184	isc_textregion_t *tr;
185	isc_token_t token;
186	isc_boolean_t eol;
187
188	base64_decode_init(&ctx, length, target);
189
190	while (!ctx.seen_end && (ctx.length != 0)) {
191		unsigned int i;
192
193		if (length > 0)
194			eol = ISC_FALSE;
195		else
196			eol = ISC_TRUE;
197		RETERR(isc_lex_getmastertoken(lexer, &token,
198					      isc_tokentype_string, eol));
199		if (token.type != isc_tokentype_string)
200			break;
201		tr = &token.value.as_textregion;
202		for (i = 0; i < tr->length; i++)
203			RETERR(base64_decode_char(&ctx, tr->base[i]));
204	}
205	if (ctx.length < 0 && !ctx.seen_end)
206		isc_lex_ungettoken(lexer, &token);
207	RETERR(base64_decode_finish(&ctx));
208	return (ISC_R_SUCCESS);
209}
210
211isc_result_t
212isc_base64_decodestring(const char *cstr, isc_buffer_t *target) {
213	base64_decode_ctx_t ctx;
214
215	base64_decode_init(&ctx, -1, target);
216	for (;;) {
217		int c = *cstr++;
218		if (c == '\0')
219			break;
220		if (c == ' ' || c == '\t' || c == '\n' || c== '\r')
221			continue;
222		RETERR(base64_decode_char(&ctx, c));
223	}
224	RETERR(base64_decode_finish(&ctx));
225	return (ISC_R_SUCCESS);
226}
227
228static isc_result_t
229str_totext(const char *source, isc_buffer_t *target) {
230	unsigned int l;
231	isc_region_t region;
232
233	isc_buffer_availableregion(target, &region);
234	l = strlen(source);
235
236	if (l > region.length)
237		return (ISC_R_NOSPACE);
238
239	memcpy(region.base, source, l);
240	isc_buffer_add(target, l);
241	return (ISC_R_SUCCESS);
242}
243
244static isc_result_t
245mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
246	isc_region_t tr;
247
248	isc_buffer_availableregion(target, &tr);
249	if (length > tr.length)
250		return (ISC_R_NOSPACE);
251	memcpy(tr.base, base, length);
252	isc_buffer_add(target, length);
253	return (ISC_R_SUCCESS);
254}
255