Home | History | Annotate | Line # | Download | only in isc
base32.c revision 1.1.1.4
      1 /*	$NetBSD: base32.c,v 1.1.1.4 2020/05/24 19:36:45 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 (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  */
     37 static isc_result_t
     38 str_totext(const char *source, isc_buffer_t *target);
     39 
     40 static isc_result_t
     41 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
     42 
     43 /*@}*/
     44 
     45 static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
     46 			     "abcdefghijklmnopqrstuvwxyz234567";
     47 static const char base32hex[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV="
     48 				"0123456789abcdefghijklmnopqrstuv";
     49 
     50 static isc_result_t
     51 base32_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 
    116 isc_result_t
    117 isc_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 
    123 isc_result_t
    124 isc_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 
    130 isc_result_t
    131 isc_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  */
    140 typedef 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 
    152 static inline void
    153 base32_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 
    164 static inline isc_result_t
    165 base32_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 
    272 static inline isc_result_t
    273 base32_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 
    292 static isc_result_t
    293 base32_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 
    335 isc_result_t
    336 isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
    337 	return (base32_tobuffer(lexer, base32, true, target, length));
    338 }
    339 
    340 isc_result_t
    341 isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
    342 	return (base32_tobuffer(lexer, base32hex, true, target, length));
    343 }
    344 
    345 isc_result_t
    346 isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
    347 	return (base32_tobuffer(lexer, base32hex, false, target, length));
    348 }
    349 
    350 static isc_result_t
    351 base32_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 
    370 isc_result_t
    371 isc_base32_decodestring(const char *cstr, isc_buffer_t *target) {
    372 	return (base32_decodestring(cstr, base32, true, target));
    373 }
    374 
    375 isc_result_t
    376 isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target) {
    377 	return (base32_decodestring(cstr, base32hex, true, target));
    378 }
    379 
    380 isc_result_t
    381 isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target) {
    382 	return (base32_decodestring(cstr, base32hex, false, target));
    383 }
    384 
    385 static isc_result_t
    386 base32_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 
    400 isc_result_t
    401 isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target) {
    402 	return (base32_decoderegion(source, base32, true, target));
    403 }
    404 
    405 isc_result_t
    406 isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target) {
    407 	return (base32_decoderegion(source, base32hex, true, target));
    408 }
    409 
    410 isc_result_t
    411 isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) {
    412 	return (base32_decoderegion(source, base32hex, false, target));
    413 }
    414 
    415 static isc_result_t
    416 str_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 
    432 static isc_result_t
    433 mem_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