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