xref: /netbsd-src/external/mpl/bind/dist/lib/isc/base64.c (revision 325dc460fcb903ba21d515d6422d8abf39bc692e)
1 /*	$NetBSD: base64.c,v 1.8 2022/09/23 12:15:33 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <stdbool.h>
19 
20 #include <isc/base64.h>
21 #include <isc/buffer.h>
22 #include <isc/lex.h>
23 #include <isc/string.h>
24 #include <isc/util.h>
25 
26 #define RETERR(x)                        \
27 	do {                             \
28 		isc_result_t _r = (x);   \
29 		if (_r != ISC_R_SUCCESS) \
30 			return ((_r));   \
31 	} while (0)
32 
33 /*@{*/
34 /*!
35  * These static functions are also present in lib/dns/rdata.c.  I'm not
36  * sure where they should go. -- bwelling
37  */
38 static isc_result_t
39 str_totext(const char *source, isc_buffer_t *target);
40 
41 static isc_result_t
42 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
43 
44 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw"
45 			     "xyz0123456789+/=";
46 /*@}*/
47 
48 isc_result_t
49 isc_base64_totext(isc_region_t *source, int wordlength, const char *wordbreak,
50 		  isc_buffer_t *target) {
51 	char buf[5];
52 	unsigned int loops = 0;
53 
54 	if (wordlength < 4) {
55 		wordlength = 4;
56 	}
57 
58 	memset(buf, 0, sizeof(buf));
59 	while (source->length > 2) {
60 		buf[0] = base64[(source->base[0] >> 2) & 0x3f];
61 		buf[1] = base64[((source->base[0] << 4) & 0x30) |
62 				((source->base[1] >> 4) & 0x0f)];
63 		buf[2] = base64[((source->base[1] << 2) & 0x3c) |
64 				((source->base[2] >> 6) & 0x03)];
65 		buf[3] = base64[source->base[2] & 0x3f];
66 		RETERR(str_totext(buf, target));
67 		isc_region_consume(source, 3);
68 
69 		loops++;
70 		if (source->length != 0 && (int)((loops + 1) * 4) >= wordlength)
71 		{
72 			loops = 0;
73 			RETERR(str_totext(wordbreak, target));
74 		}
75 	}
76 	if (source->length == 2) {
77 		buf[0] = base64[(source->base[0] >> 2) & 0x3f];
78 		buf[1] = base64[((source->base[0] << 4) & 0x30) |
79 				((source->base[1] >> 4) & 0x0f)];
80 		buf[2] = base64[((source->base[1] << 2) & 0x3c)];
81 		buf[3] = '=';
82 		RETERR(str_totext(buf, target));
83 		isc_region_consume(source, 2);
84 	} else if (source->length == 1) {
85 		buf[0] = base64[(source->base[0] >> 2) & 0x3f];
86 		buf[1] = base64[((source->base[0] << 4) & 0x30)];
87 		buf[2] = buf[3] = '=';
88 		RETERR(str_totext(buf, target));
89 		isc_region_consume(source, 1);
90 	}
91 	return (ISC_R_SUCCESS);
92 }
93 
94 /*%
95  * State of a base64 decoding process in progress.
96  */
97 typedef struct {
98 	int length;	      /*%< Desired length of binary data or -1 */
99 	isc_buffer_t *target; /*%< Buffer for resulting binary data */
100 	int digits;	      /*%< Number of buffered base64 digits */
101 	bool seen_end;	      /*%< True if "=" end marker seen */
102 	int val[4];
103 } base64_decode_ctx_t;
104 
105 static void
106 base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) {
107 	ctx->digits = 0;
108 	ctx->seen_end = false;
109 	ctx->length = length;
110 	ctx->target = target;
111 }
112 
113 static isc_result_t
114 base64_decode_char(base64_decode_ctx_t *ctx, int c) {
115 	const char *s;
116 
117 	if (ctx->seen_end) {
118 		return (ISC_R_BADBASE64);
119 	}
120 	if ((s = strchr(base64, c)) == NULL) {
121 		return (ISC_R_BADBASE64);
122 	}
123 	ctx->val[ctx->digits++] = (int)(s - base64);
124 	if (ctx->digits == 4) {
125 		int n;
126 		unsigned char buf[3];
127 		if (ctx->val[0] == 64 || ctx->val[1] == 64) {
128 			return (ISC_R_BADBASE64);
129 		}
130 		if (ctx->val[2] == 64 && ctx->val[3] != 64) {
131 			return (ISC_R_BADBASE64);
132 		}
133 		/*
134 		 * Check that bits that should be zero are.
135 		 */
136 		if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) {
137 			return (ISC_R_BADBASE64);
138 		}
139 		/*
140 		 * We don't need to test for ctx->val[2] != 64 as
141 		 * the bottom two bits of 64 are zero.
142 		 */
143 		if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) {
144 			return (ISC_R_BADBASE64);
145 		}
146 		n = (ctx->val[2] == 64) ? 1 : (ctx->val[3] == 64) ? 2 : 3;
147 		if (n != 3) {
148 			ctx->seen_end = true;
149 			if (ctx->val[2] == 64) {
150 				ctx->val[2] = 0;
151 			}
152 			if (ctx->val[3] == 64) {
153 				ctx->val[3] = 0;
154 			}
155 		}
156 		buf[0] = (ctx->val[0] << 2) | (ctx->val[1] >> 4);
157 		buf[1] = (ctx->val[1] << 4) | (ctx->val[2] >> 2);
158 		buf[2] = (ctx->val[2] << 6) | (ctx->val[3]);
159 		RETERR(mem_tobuffer(ctx->target, buf, n));
160 		if (ctx->length >= 0) {
161 			if (n > ctx->length) {
162 				return (ISC_R_BADBASE64);
163 			} else {
164 				ctx->length -= n;
165 			}
166 		}
167 		ctx->digits = 0;
168 	}
169 	return (ISC_R_SUCCESS);
170 }
171 
172 static isc_result_t
173 base64_decode_finish(base64_decode_ctx_t *ctx) {
174 	if (ctx->length > 0) {
175 		return (ISC_R_UNEXPECTEDEND);
176 	}
177 	if (ctx->digits != 0) {
178 		return (ISC_R_BADBASE64);
179 	}
180 	return (ISC_R_SUCCESS);
181 }
182 
183 isc_result_t
184 isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
185 	unsigned int before, after;
186 	base64_decode_ctx_t ctx;
187 	isc_textregion_t *tr;
188 	isc_token_t token;
189 	bool eol;
190 
191 	REQUIRE(length >= -2);
192 
193 	base64_decode_init(&ctx, length, target);
194 
195 	before = isc_buffer_usedlength(target);
196 	while (!ctx.seen_end && (ctx.length != 0)) {
197 		unsigned int i;
198 
199 		if (length > 0) {
200 			eol = false;
201 		} else {
202 			eol = true;
203 		}
204 		RETERR(isc_lex_getmastertoken(lexer, &token,
205 					      isc_tokentype_string, eol));
206 		if (token.type != isc_tokentype_string) {
207 			break;
208 		}
209 		tr = &token.value.as_textregion;
210 		for (i = 0; i < tr->length; i++) {
211 			RETERR(base64_decode_char(&ctx, tr->base[i]));
212 		}
213 	}
214 	after = isc_buffer_usedlength(target);
215 	if (ctx.length < 0 && !ctx.seen_end) {
216 		isc_lex_ungettoken(lexer, &token);
217 	}
218 	RETERR(base64_decode_finish(&ctx));
219 	if (length == -2 && before == after) {
220 		return (ISC_R_UNEXPECTEDEND);
221 	}
222 	return (ISC_R_SUCCESS);
223 }
224 
225 isc_result_t
226 isc_base64_decodestring(const char *cstr, isc_buffer_t *target) {
227 	base64_decode_ctx_t ctx;
228 
229 	base64_decode_init(&ctx, -1, target);
230 	for (;;) {
231 		int c = *cstr++;
232 		if (c == '\0') {
233 			break;
234 		}
235 		if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
236 			continue;
237 		}
238 		RETERR(base64_decode_char(&ctx, c));
239 	}
240 	RETERR(base64_decode_finish(&ctx));
241 	return (ISC_R_SUCCESS);
242 }
243 
244 static isc_result_t
245 str_totext(const char *source, isc_buffer_t *target) {
246 	unsigned int l;
247 	isc_region_t region;
248 
249 	isc_buffer_availableregion(target, &region);
250 	l = strlen(source);
251 
252 	if (l > region.length) {
253 		return (ISC_R_NOSPACE);
254 	}
255 
256 	memmove(region.base, source, l);
257 	isc_buffer_add(target, l);
258 	return (ISC_R_SUCCESS);
259 }
260 
261 static isc_result_t
262 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
263 	isc_region_t tr;
264 
265 	isc_buffer_availableregion(target, &tr);
266 	if (length > tr.length) {
267 		return (ISC_R_NOSPACE);
268 	}
269 	memmove(tr.base, base, length);
270 	isc_buffer_add(target, length);
271 	return (ISC_R_SUCCESS);
272 }
273