1258945Sroberto/*
2280849Scy * Copyright (C) 2004, 2005, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 1998-2001, 2003  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id: base64.c,v 1.34 2009/10/21 23:48:05 tbox Exp $ */
19258945Sroberto
20258945Sroberto/*! \file */
21258945Sroberto
22258945Sroberto#include <config.h>
23258945Sroberto
24258945Sroberto#include <isc/base64.h>
25258945Sroberto#include <isc/buffer.h>
26258945Sroberto#include <isc/lex.h>
27258945Sroberto#include <isc/string.h>
28258945Sroberto#include <isc/util.h>
29258945Sroberto
30258945Sroberto#define RETERR(x) do { \
31258945Sroberto	isc_result_t _r = (x); \
32258945Sroberto	if (_r != ISC_R_SUCCESS) \
33258945Sroberto		return (_r); \
34258945Sroberto	} while (0)
35258945Sroberto
36258945Sroberto
37258945Sroberto/*@{*/
38258945Sroberto/*!
39258945Sroberto * These static functions are also present in lib/dns/rdata.c.  I'm not
40258945Sroberto * sure where they should go. -- bwelling
41258945Sroberto */
42258945Srobertostatic isc_result_t
43258945Srobertostr_totext(const char *source, isc_buffer_t *target);
44258945Sroberto
45258945Srobertostatic isc_result_t
46258945Srobertomem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
47258945Sroberto
48258945Srobertostatic const char base64[] =
49258945Sroberto	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
50258945Sroberto/*@}*/
51258945Sroberto
52258945Srobertoisc_result_t
53258945Srobertoisc_base64_totext(isc_region_t *source, int wordlength,
54258945Sroberto		  const char *wordbreak, isc_buffer_t *target)
55258945Sroberto{
56258945Sroberto	char buf[5];
57258945Sroberto	unsigned int loops = 0;
58258945Sroberto
59258945Sroberto	if (wordlength < 4)
60258945Sroberto		wordlength = 4;
61258945Sroberto
62258945Sroberto	memset(buf, 0, sizeof(buf));
63258945Sroberto	while (source->length > 2) {
64258945Sroberto		buf[0] = base64[(source->base[0]>>2)&0x3f];
65258945Sroberto		buf[1] = base64[((source->base[0]<<4)&0x30)|
66258945Sroberto				((source->base[1]>>4)&0x0f)];
67258945Sroberto		buf[2] = base64[((source->base[1]<<2)&0x3c)|
68258945Sroberto				((source->base[2]>>6)&0x03)];
69258945Sroberto		buf[3] = base64[source->base[2]&0x3f];
70258945Sroberto		RETERR(str_totext(buf, target));
71258945Sroberto		isc_region_consume(source, 3);
72258945Sroberto
73258945Sroberto		loops++;
74258945Sroberto		if (source->length != 0 &&
75258945Sroberto		    (int)((loops + 1) * 4) >= wordlength)
76258945Sroberto		{
77258945Sroberto			loops = 0;
78258945Sroberto			RETERR(str_totext(wordbreak, target));
79258945Sroberto		}
80258945Sroberto	}
81258945Sroberto	if (source->length == 2) {
82258945Sroberto		buf[0] = base64[(source->base[0]>>2)&0x3f];
83258945Sroberto		buf[1] = base64[((source->base[0]<<4)&0x30)|
84258945Sroberto				((source->base[1]>>4)&0x0f)];
85258945Sroberto		buf[2] = base64[((source->base[1]<<2)&0x3c)];
86258945Sroberto		buf[3] = '=';
87258945Sroberto		RETERR(str_totext(buf, target));
88280849Scy		isc_region_consume(source, 2);
89258945Sroberto	} else if (source->length == 1) {
90258945Sroberto		buf[0] = base64[(source->base[0]>>2)&0x3f];
91258945Sroberto		buf[1] = base64[((source->base[0]<<4)&0x30)];
92258945Sroberto		buf[2] = buf[3] = '=';
93258945Sroberto		RETERR(str_totext(buf, target));
94280849Scy		isc_region_consume(source, 1);
95258945Sroberto	}
96258945Sroberto	return (ISC_R_SUCCESS);
97258945Sroberto}
98258945Sroberto
99258945Sroberto/*%
100258945Sroberto * State of a base64 decoding process in progress.
101258945Sroberto */
102258945Srobertotypedef struct {
103258945Sroberto	int length;		/*%< Desired length of binary data or -1 */
104258945Sroberto	isc_buffer_t *target;	/*%< Buffer for resulting binary data */
105258945Sroberto	int digits;		/*%< Number of buffered base64 digits */
106258945Sroberto	isc_boolean_t seen_end;	/*%< True if "=" end marker seen */
107258945Sroberto	int val[4];
108258945Sroberto} base64_decode_ctx_t;
109258945Sroberto
110258945Srobertostatic inline void
111258945Srobertobase64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target)
112258945Sroberto{
113258945Sroberto	ctx->digits = 0;
114258945Sroberto	ctx->seen_end = ISC_FALSE;
115258945Sroberto	ctx->length = length;
116258945Sroberto	ctx->target = target;
117258945Sroberto}
118258945Sroberto
119258945Srobertostatic inline isc_result_t
120258945Srobertobase64_decode_char(base64_decode_ctx_t *ctx, int c) {
121258945Sroberto	char *s;
122258945Sroberto
123258945Sroberto	if (ctx->seen_end)
124258945Sroberto		return (ISC_R_BADBASE64);
125258945Sroberto	if ((s = strchr(base64, c)) == NULL)
126258945Sroberto		return (ISC_R_BADBASE64);
127258945Sroberto	ctx->val[ctx->digits++] = s - base64;
128258945Sroberto	if (ctx->digits == 4) {
129258945Sroberto		int n;
130258945Sroberto		unsigned char buf[3];
131258945Sroberto		if (ctx->val[0] == 64 || ctx->val[1] == 64)
132258945Sroberto			return (ISC_R_BADBASE64);
133258945Sroberto		if (ctx->val[2] == 64 && ctx->val[3] != 64)
134258945Sroberto			return (ISC_R_BADBASE64);
135258945Sroberto		/*
136258945Sroberto		 * Check that bits that should be zero are.
137258945Sroberto		 */
138258945Sroberto		if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0)
139258945Sroberto			return (ISC_R_BADBASE64);
140258945Sroberto		/*
141258945Sroberto		 * We don't need to test for ctx->val[2] != 64 as
142258945Sroberto		 * the bottom two bits of 64 are zero.
143258945Sroberto		 */
144258945Sroberto		if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0)
145258945Sroberto			return (ISC_R_BADBASE64);
146258945Sroberto		n = (ctx->val[2] == 64) ? 1 :
147258945Sroberto			(ctx->val[3] == 64) ? 2 : 3;
148258945Sroberto		if (n != 3) {
149258945Sroberto			ctx->seen_end = ISC_TRUE;
150258945Sroberto			if (ctx->val[2] == 64)
151258945Sroberto				ctx->val[2] = 0;
152258945Sroberto			if (ctx->val[3] == 64)
153258945Sroberto				ctx->val[3] = 0;
154258945Sroberto		}
155258945Sroberto		buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4);
156258945Sroberto		buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2);
157258945Sroberto		buf[2] = (ctx->val[2]<<6)|(ctx->val[3]);
158258945Sroberto		RETERR(mem_tobuffer(ctx->target, buf, n));
159258945Sroberto		if (ctx->length >= 0) {
160258945Sroberto			if (n > ctx->length)
161258945Sroberto				return (ISC_R_BADBASE64);
162258945Sroberto			else
163258945Sroberto				ctx->length -= n;
164258945Sroberto		}
165258945Sroberto		ctx->digits = 0;
166258945Sroberto	}
167258945Sroberto	return (ISC_R_SUCCESS);
168258945Sroberto}
169258945Sroberto
170258945Srobertostatic inline isc_result_t
171258945Srobertobase64_decode_finish(base64_decode_ctx_t *ctx) {
172258945Sroberto	if (ctx->length > 0)
173258945Sroberto		return (ISC_R_UNEXPECTEDEND);
174258945Sroberto	if (ctx->digits != 0)
175258945Sroberto		return (ISC_R_BADBASE64);
176258945Sroberto	return (ISC_R_SUCCESS);
177258945Sroberto}
178258945Sroberto
179258945Srobertoisc_result_t
180258945Srobertoisc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
181258945Sroberto	base64_decode_ctx_t ctx;
182258945Sroberto	isc_textregion_t *tr;
183258945Sroberto	isc_token_t token;
184258945Sroberto	isc_boolean_t eol;
185258945Sroberto
186258945Sroberto	base64_decode_init(&ctx, length, target);
187258945Sroberto
188258945Sroberto	while (!ctx.seen_end && (ctx.length != 0)) {
189258945Sroberto		unsigned int i;
190258945Sroberto
191258945Sroberto		if (length > 0)
192258945Sroberto			eol = ISC_FALSE;
193258945Sroberto		else
194258945Sroberto			eol = ISC_TRUE;
195258945Sroberto		RETERR(isc_lex_getmastertoken(lexer, &token,
196258945Sroberto					      isc_tokentype_string, eol));
197258945Sroberto		if (token.type != isc_tokentype_string)
198258945Sroberto			break;
199258945Sroberto		tr = &token.value.as_textregion;
200258945Sroberto		for (i = 0; i < tr->length; i++)
201258945Sroberto			RETERR(base64_decode_char(&ctx, tr->base[i]));
202258945Sroberto	}
203258945Sroberto	if (ctx.length < 0 && !ctx.seen_end)
204258945Sroberto		isc_lex_ungettoken(lexer, &token);
205258945Sroberto	RETERR(base64_decode_finish(&ctx));
206258945Sroberto	return (ISC_R_SUCCESS);
207258945Sroberto}
208258945Sroberto
209258945Srobertoisc_result_t
210258945Srobertoisc_base64_decodestring(const char *cstr, isc_buffer_t *target) {
211258945Sroberto	base64_decode_ctx_t ctx;
212258945Sroberto
213258945Sroberto	base64_decode_init(&ctx, -1, target);
214258945Sroberto	for (;;) {
215258945Sroberto		int c = *cstr++;
216258945Sroberto		if (c == '\0')
217258945Sroberto			break;
218258945Sroberto		if (c == ' ' || c == '\t' || c == '\n' || c== '\r')
219258945Sroberto			continue;
220258945Sroberto		RETERR(base64_decode_char(&ctx, c));
221258945Sroberto	}
222280849Scy	RETERR(base64_decode_finish(&ctx));
223258945Sroberto	return (ISC_R_SUCCESS);
224258945Sroberto}
225258945Sroberto
226258945Srobertostatic isc_result_t
227258945Srobertostr_totext(const char *source, isc_buffer_t *target) {
228258945Sroberto	unsigned int l;
229258945Sroberto	isc_region_t region;
230258945Sroberto
231258945Sroberto	isc_buffer_availableregion(target, &region);
232258945Sroberto	l = strlen(source);
233258945Sroberto
234258945Sroberto	if (l > region.length)
235258945Sroberto		return (ISC_R_NOSPACE);
236258945Sroberto
237258945Sroberto	memcpy(region.base, source, l);
238258945Sroberto	isc_buffer_add(target, l);
239258945Sroberto	return (ISC_R_SUCCESS);
240258945Sroberto}
241258945Sroberto
242258945Srobertostatic isc_result_t
243258945Srobertomem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
244258945Sroberto	isc_region_t tr;
245258945Sroberto
246258945Sroberto	isc_buffer_availableregion(target, &tr);
247258945Sroberto	if (length > tr.length)
248258945Sroberto		return (ISC_R_NOSPACE);
249258945Sroberto	memcpy(tr.base, base, length);
250258945Sroberto	isc_buffer_add(target, length);
251258945Sroberto	return (ISC_R_SUCCESS);
252258945Sroberto}
253