base32.c revision 1.5
1/*	$NetBSD: base32.c,v 1.5 2020/05/24 19:46:26 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14/*! \file */
15
16#include <stdbool.h>
17
18#include <isc/base32.h>
19#include <isc/buffer.h>
20#include <isc/lex.h>
21#include <isc/region.h>
22#include <isc/string.h>
23#include <isc/util.h>
24
25#define RETERR(x)                        \
26	do {                             \
27		isc_result_t _r = (x);   \
28		if (_r != ISC_R_SUCCESS) \
29			return ((_r));   \
30	} while (/*CONSTCOND*/0)
31
32/*@{*/
33/*!
34 * These static functions are also present in lib/dns/rdata.c.  I'm not
35 * sure where they should go. -- bwelling
36 */
37static isc_result_t
38str_totext(const char *source, isc_buffer_t *target);
39
40static isc_result_t
41mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
42
43/*@}*/
44
45static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
46			     "abcdefghijklmnopqrstuvwxyz234567";
47static const char base32hex[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV="
48				"0123456789abcdefghijklmnopqrstuv";
49
50static isc_result_t
51base32_totext(isc_region_t *source, int wordlength, const char *wordbreak,
52	      isc_buffer_t *target, const char base[], char pad) {
53	char buf[9];
54	unsigned int loops = 0;
55
56	if (wordlength >= 0 && wordlength < 8) {
57		wordlength = 8;
58	}
59
60	memset(buf, 0, sizeof(buf));
61	while (source->length > 0) {
62		buf[0] = base[((source->base[0] >> 3) & 0x1f)]; /* 5 + */
63		if (source->length == 1) {
64			buf[1] = base[(source->base[0] << 2) & 0x1c];
65			buf[2] = buf[3] = buf[4] = pad;
66			buf[5] = buf[6] = buf[7] = pad;
67			RETERR(str_totext(buf, target));
68			break;
69		}
70		buf[1] = base[((source->base[0] << 2) & 0x1c) | /* 3 = 8 */
71			      ((source->base[1] >> 6) & 0x03)]; /* 2 + */
72		buf[2] = base[((source->base[1] >> 1) & 0x1f)]; /* 5 + */
73		if (source->length == 2) {
74			buf[3] = base[(source->base[1] << 4) & 0x10];
75			buf[4] = buf[5] = buf[6] = buf[7] = pad;
76			RETERR(str_totext(buf, target));
77			break;
78		}
79		buf[3] = base[((source->base[1] << 4) & 0x10) | /* 1 = 8 */
80			      ((source->base[2] >> 4) & 0x0f)]; /* 4 + */
81		if (source->length == 3) {
82			buf[4] = base[(source->base[2] << 1) & 0x1e];
83			buf[5] = buf[6] = buf[7] = pad;
84			RETERR(str_totext(buf, target));
85			break;
86		}
87		buf[4] = base[((source->base[2] << 1) & 0x1e) | /* 4 = 8 */
88			      ((source->base[3] >> 7) & 0x01)]; /* 1 + */
89		buf[5] = base[((source->base[3] >> 2) & 0x1f)]; /* 5 + */
90		if (source->length == 4) {
91			buf[6] = base[(source->base[3] << 3) & 0x18];
92			buf[7] = pad;
93			RETERR(str_totext(buf, target));
94			break;
95		}
96		buf[6] = base[((source->base[3] << 3) & 0x18) | /* 2 = 8 */
97			      ((source->base[4] >> 5) & 0x07)]; /* 3 + */
98		buf[7] = base[source->base[4] & 0x1f];		/* 5 = 8 */
99		RETERR(str_totext(buf, target));
100		isc_region_consume(source, 5);
101
102		loops++;
103		if (source->length != 0 && wordlength >= 0 &&
104		    (int)((loops + 1) * 8) >= wordlength)
105		{
106			loops = 0;
107			RETERR(str_totext(wordbreak, target));
108		}
109	}
110	if (source->length > 0) {
111		isc_region_consume(source, source->length);
112	}
113	return (ISC_R_SUCCESS);
114}
115
116isc_result_t
117isc_base32_totext(isc_region_t *source, int wordlength, const char *wordbreak,
118		  isc_buffer_t *target) {
119	return (base32_totext(source, wordlength, wordbreak, target, base32,
120			      '='));
121}
122
123isc_result_t
124isc_base32hex_totext(isc_region_t *source, int wordlength,
125		     const char *wordbreak, isc_buffer_t *target) {
126	return (base32_totext(source, wordlength, wordbreak, target, base32hex,
127			      '='));
128}
129
130isc_result_t
131isc_base32hexnp_totext(isc_region_t *source, int wordlength,
132		       const char *wordbreak, isc_buffer_t *target) {
133	return (base32_totext(source, wordlength, wordbreak, target, base32hex,
134			      0));
135}
136
137/*%
138 * State of a base32 decoding process in progress.
139 */
140typedef struct {
141	int length;	      /*%< Desired length of binary data or -1 */
142	isc_buffer_t *target; /*%< Buffer for resulting binary data */
143	int digits;	      /*%< Number of buffered base32 digits */
144	bool seen_end;	      /*%< True if "=" end marker seen */
145	int val[8];
146	const char *base; /*%< Which encoding we are using */
147	int seen_32;	  /*%< Number of significant bytes if non
148			   * zero */
149	bool pad;	  /*%< Expect padding */
150} base32_decode_ctx_t;
151
152static inline void
153base32_decode_init(base32_decode_ctx_t *ctx, int length, const char base[],
154		   bool pad, isc_buffer_t *target) {
155	ctx->digits = 0;
156	ctx->seen_end = false;
157	ctx->seen_32 = 0;
158	ctx->length = length;
159	ctx->target = target;
160	ctx->base = base;
161	ctx->pad = pad;
162}
163
164static inline isc_result_t
165base32_decode_char(base32_decode_ctx_t *ctx, int c) {
166	const char *s;
167	unsigned int last;
168
169	if (ctx->seen_end) {
170		return (ISC_R_BADBASE32);
171	}
172	if ((s = strchr(ctx->base, c)) == NULL) {
173		return (ISC_R_BADBASE32);
174	}
175	last = (unsigned int)(s - ctx->base);
176
177	/*
178	 * Handle lower case.
179	 */
180	if (last > 32) {
181		last -= 33;
182	}
183
184	/*
185	 * Check that padding is contiguous.
186	 */
187	if (last != 32 && ctx->seen_32 != 0) {
188		return (ISC_R_BADBASE32);
189	}
190
191	/*
192	 * If padding is not permitted flag padding as a error.
193	 */
194	if (last == 32 && !ctx->pad) {
195		return (ISC_R_BADBASE32);
196	}
197
198	/*
199	 * Check that padding starts at the right place and that
200	 * bits that should be zero are.
201	 * Record how many significant bytes in answer (seen_32).
202	 */
203	if (last == 32 && ctx->seen_32 == 0) {
204		switch (ctx->digits) {
205		case 0:
206		case 1:
207			return (ISC_R_BADBASE32);
208		case 2:
209			if ((ctx->val[1] & 0x03) != 0) {
210				return (ISC_R_BADBASE32);
211			}
212			ctx->seen_32 = 1;
213			break;
214		case 3:
215			return (ISC_R_BADBASE32);
216		case 4:
217			if ((ctx->val[3] & 0x0f) != 0) {
218				return (ISC_R_BADBASE32);
219			}
220			ctx->seen_32 = 3;
221			break;
222		case 5:
223			if ((ctx->val[4] & 0x01) != 0) {
224				return (ISC_R_BADBASE32);
225			}
226			ctx->seen_32 = 3;
227			break;
228		case 6:
229			return (ISC_R_BADBASE32);
230		case 7:
231			if ((ctx->val[6] & 0x07) != 0) {
232				return (ISC_R_BADBASE32);
233			}
234			ctx->seen_32 = 4;
235			break;
236		}
237	}
238
239	/*
240	 * Zero fill pad values.
241	 */
242	ctx->val[ctx->digits++] = (last == 32) ? 0 : last;
243
244	if (ctx->digits == 8) {
245		int n = 5;
246		unsigned char buf[5];
247
248		if (ctx->seen_32 != 0) {
249			ctx->seen_end = true;
250			n = ctx->seen_32;
251		}
252		buf[0] = (ctx->val[0] << 3) | (ctx->val[1] >> 2);
253		buf[1] = (ctx->val[1] << 6) | (ctx->val[2] << 1) |
254			 (ctx->val[3] >> 4);
255		buf[2] = (ctx->val[3] << 4) | (ctx->val[4] >> 1);
256		buf[3] = (ctx->val[4] << 7) | (ctx->val[5] << 2) |
257			 (ctx->val[6] >> 3);
258		buf[4] = (ctx->val[6] << 5) | (ctx->val[7]);
259		RETERR(mem_tobuffer(ctx->target, buf, n));
260		if (ctx->length >= 0) {
261			if (n > ctx->length) {
262				return (ISC_R_BADBASE32);
263			} else {
264				ctx->length -= n;
265			}
266		}
267		ctx->digits = 0;
268	}
269	return (ISC_R_SUCCESS);
270}
271
272static inline isc_result_t
273base32_decode_finish(base32_decode_ctx_t *ctx) {
274	if (ctx->length > 0) {
275		return (ISC_R_UNEXPECTEDEND);
276	}
277	/*
278	 * Add missing padding if required.
279	 */
280	if (!ctx->pad && ctx->digits != 0) {
281		ctx->pad = true;
282		do {
283			RETERR(base32_decode_char(ctx, '='));
284		} while (ctx->digits != 0);
285	}
286	if (ctx->digits != 0) {
287		return (ISC_R_BADBASE32);
288	}
289	return (ISC_R_SUCCESS);
290}
291
292static isc_result_t
293base32_tobuffer(isc_lex_t *lexer, const char base[], bool pad,
294		isc_buffer_t *target, int length) {
295	unsigned int before, after;
296	base32_decode_ctx_t ctx;
297	isc_textregion_t *tr;
298	isc_token_t token;
299	bool eol;
300
301	REQUIRE(length >= -2);
302
303	base32_decode_init(&ctx, length, base, pad, target);
304
305	before = isc_buffer_usedlength(target);
306	while (!ctx.seen_end && (ctx.length != 0)) {
307		unsigned int i;
308
309		if (length > 0) {
310			eol = false;
311		} else {
312			eol = true;
313		}
314		RETERR(isc_lex_getmastertoken(lexer, &token,
315					      isc_tokentype_string, eol));
316		if (token.type != isc_tokentype_string) {
317			break;
318		}
319		tr = &token.value.as_textregion;
320		for (i = 0; i < tr->length; i++) {
321			RETERR(base32_decode_char(&ctx, tr->base[i]));
322		}
323	}
324	after = isc_buffer_usedlength(target);
325	if (ctx.length < 0 && !ctx.seen_end) {
326		isc_lex_ungettoken(lexer, &token);
327	}
328	RETERR(base32_decode_finish(&ctx));
329	if (length == -2 && before == after) {
330		return (ISC_R_UNEXPECTEDEND);
331	}
332	return (ISC_R_SUCCESS);
333}
334
335isc_result_t
336isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
337	return (base32_tobuffer(lexer, base32, true, target, length));
338}
339
340isc_result_t
341isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
342	return (base32_tobuffer(lexer, base32hex, true, target, length));
343}
344
345isc_result_t
346isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
347	return (base32_tobuffer(lexer, base32hex, false, target, length));
348}
349
350static isc_result_t
351base32_decodestring(const char *cstr, const char base[], bool pad,
352		    isc_buffer_t *target) {
353	base32_decode_ctx_t ctx;
354
355	base32_decode_init(&ctx, -1, base, pad, target);
356	for (;;) {
357		int c = *cstr++;
358		if (c == '\0') {
359			break;
360		}
361		if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
362			continue;
363		}
364		RETERR(base32_decode_char(&ctx, c));
365	}
366	RETERR(base32_decode_finish(&ctx));
367	return (ISC_R_SUCCESS);
368}
369
370isc_result_t
371isc_base32_decodestring(const char *cstr, isc_buffer_t *target) {
372	return (base32_decodestring(cstr, base32, true, target));
373}
374
375isc_result_t
376isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target) {
377	return (base32_decodestring(cstr, base32hex, true, target));
378}
379
380isc_result_t
381isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target) {
382	return (base32_decodestring(cstr, base32hex, false, target));
383}
384
385static isc_result_t
386base32_decoderegion(isc_region_t *source, const char base[], bool pad,
387		    isc_buffer_t *target) {
388	base32_decode_ctx_t ctx;
389
390	base32_decode_init(&ctx, -1, base, pad, target);
391	while (source->length != 0) {
392		int c = *source->base;
393		RETERR(base32_decode_char(&ctx, c));
394		isc_region_consume(source, 1);
395	}
396	RETERR(base32_decode_finish(&ctx));
397	return (ISC_R_SUCCESS);
398}
399
400isc_result_t
401isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target) {
402	return (base32_decoderegion(source, base32, true, target));
403}
404
405isc_result_t
406isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target) {
407	return (base32_decoderegion(source, base32hex, true, target));
408}
409
410isc_result_t
411isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) {
412	return (base32_decoderegion(source, base32hex, false, target));
413}
414
415static isc_result_t
416str_totext(const char *source, isc_buffer_t *target) {
417	unsigned int l;
418	isc_region_t region;
419
420	isc_buffer_availableregion(target, &region);
421	l = strlen(source);
422
423	if (l > region.length) {
424		return (ISC_R_NOSPACE);
425	}
426
427	memmove(region.base, source, l);
428	isc_buffer_add(target, l);
429	return (ISC_R_SUCCESS);
430}
431
432static isc_result_t
433mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
434	isc_region_t tr;
435
436	isc_buffer_availableregion(target, &tr);
437	if (length > tr.length) {
438		return (ISC_R_NOSPACE);
439	}
440	memmove(tr.base, base, length);
441	isc_buffer_add(target, length);
442	return (ISC_R_SUCCESS);
443}
444