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