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