Home | History | Annotate | Line # | Download | only in isc
base32.c revision 1.2.2.3
      1 /*	$NetBSD: base32.c,v 1.2.2.3 2019/01/18 08:49:57 pgoyette 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 (/*CONSTCOND*/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 	base32_decode_ctx_t ctx;
    288 	isc_textregion_t *tr;
    289 	isc_token_t token;
    290 	bool eol;
    291 
    292 	base32_decode_init(&ctx, length, base, pad, target);
    293 
    294 	while (!ctx.seen_end && (ctx.length != 0)) {
    295 		unsigned int i;
    296 
    297 		if (length > 0)
    298 			eol = false;
    299 		else
    300 			eol = true;
    301 		RETERR(isc_lex_getmastertoken(lexer, &token,
    302 					      isc_tokentype_string, eol));
    303 		if (token.type != isc_tokentype_string)
    304 			break;
    305 		tr = &token.value.as_textregion;
    306 		for (i = 0; i < tr->length; i++)
    307 			RETERR(base32_decode_char(&ctx, tr->base[i]));
    308 	}
    309 	if (ctx.length < 0 && !ctx.seen_end)
    310 		isc_lex_ungettoken(lexer, &token);
    311 	RETERR(base32_decode_finish(&ctx));
    312 	return (ISC_R_SUCCESS);
    313 }
    314 
    315 isc_result_t
    316 isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
    317 	return (base32_tobuffer(lexer, base32, true, target, length));
    318 }
    319 
    320 isc_result_t
    321 isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
    322 	return (base32_tobuffer(lexer, base32hex, true, target, length));
    323 }
    324 
    325 isc_result_t
    326 isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
    327 	return (base32_tobuffer(lexer, base32hex, false, target, length));
    328 }
    329 
    330 static isc_result_t
    331 base32_decodestring(const char *cstr, const char base[], bool pad,
    332 		    isc_buffer_t *target)
    333 {
    334 	base32_decode_ctx_t ctx;
    335 
    336 	base32_decode_init(&ctx, -1, base, pad, target);
    337 	for (;;) {
    338 		int c = *cstr++;
    339 		if (c == '\0')
    340 			break;
    341 		if (c == ' ' || c == '\t' || c == '\n' || c== '\r')
    342 			continue;
    343 		RETERR(base32_decode_char(&ctx, c));
    344 	}
    345 	RETERR(base32_decode_finish(&ctx));
    346 	return (ISC_R_SUCCESS);
    347 }
    348 
    349 isc_result_t
    350 isc_base32_decodestring(const char *cstr, isc_buffer_t *target) {
    351 	return (base32_decodestring(cstr, base32, true, target));
    352 }
    353 
    354 isc_result_t
    355 isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target) {
    356 	return (base32_decodestring(cstr, base32hex, true, target));
    357 }
    358 
    359 isc_result_t
    360 isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target) {
    361 	return (base32_decodestring(cstr, base32hex, false, target));
    362 }
    363 
    364 static isc_result_t
    365 base32_decoderegion(isc_region_t *source, const char base[],
    366 		    bool pad, isc_buffer_t *target)
    367 {
    368 	base32_decode_ctx_t ctx;
    369 
    370 	base32_decode_init(&ctx, -1, base, pad, target);
    371 	while (source->length != 0) {
    372 		int c = *source->base;
    373 		RETERR(base32_decode_char(&ctx, c));
    374 		isc_region_consume(source, 1);
    375 	}
    376 	RETERR(base32_decode_finish(&ctx));
    377 	return (ISC_R_SUCCESS);
    378 }
    379 
    380 isc_result_t
    381 isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target) {
    382 	return (base32_decoderegion(source, base32, true, target));
    383 }
    384 
    385 isc_result_t
    386 isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target) {
    387 	return (base32_decoderegion(source, base32hex, true, target));
    388 }
    389 
    390 isc_result_t
    391 isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) {
    392 	return (base32_decoderegion(source, base32hex, false, target));
    393 }
    394 
    395 static isc_result_t
    396 str_totext(const char *source, isc_buffer_t *target) {
    397 	unsigned int l;
    398 	isc_region_t region;
    399 
    400 	isc_buffer_availableregion(target, &region);
    401 	l = strlen(source);
    402 
    403 	if (l > region.length)
    404 		return (ISC_R_NOSPACE);
    405 
    406 	memmove(region.base, source, l);
    407 	isc_buffer_add(target, l);
    408 	return (ISC_R_SUCCESS);
    409 }
    410 
    411 static isc_result_t
    412 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
    413 	isc_region_t tr;
    414 
    415 	isc_buffer_availableregion(target, &tr);
    416 	if (length > tr.length)
    417 		return (ISC_R_NOSPACE);
    418 	memmove(tr.base, base, length);
    419 	isc_buffer_add(target, length);
    420 	return (ISC_R_SUCCESS);
    421 }
    422