xref: /openbsd/usr.bin/dig/lib/isc/base32.c (revision 1fb015a8)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
17*1fb015a8Sflorian /* $Id: base32.c,v 1.8 2020/09/14 08:40:44 florian Exp $ */
185185a700Sflorian 
195185a700Sflorian /*! \file */
205185a700Sflorian 
215185a700Sflorian #include <isc/base32.h>
225185a700Sflorian #include <isc/buffer.h>
235185a700Sflorian #include <isc/region.h>
245185a700Sflorian #include <string.h>
255185a700Sflorian #include <isc/util.h>
265185a700Sflorian 
275185a700Sflorian #define RETERR(x) do { \
285185a700Sflorian 	isc_result_t _r = (x); \
295185a700Sflorian 	if (_r != ISC_R_SUCCESS) \
305185a700Sflorian 		return (_r); \
315185a700Sflorian 	} while (0)
325185a700Sflorian 
335185a700Sflorian /*@}*/
345185a700Sflorian 
355185a700Sflorian static const char base32hex[] =
365185a700Sflorian 	"0123456789ABCDEFGHIJKLMNOPQRSTUV=0123456789abcdefghijklmnopqrstuv";
375185a700Sflorian 
385185a700Sflorian static isc_result_t
base32_totext(isc_region_t * source,int wordlength,const char * wordbreak,isc_buffer_t * target,const char base[],char pad)395185a700Sflorian base32_totext(isc_region_t *source, int wordlength, const char *wordbreak,
405185a700Sflorian 	      isc_buffer_t *target, const char base[], char pad)
415185a700Sflorian {
425185a700Sflorian 	char buf[9];
435185a700Sflorian 	unsigned int loops = 0;
445185a700Sflorian 
455185a700Sflorian 	if (wordlength >= 0 && wordlength < 8)
465185a700Sflorian 		wordlength = 8;
475185a700Sflorian 
485185a700Sflorian 	memset(buf, 0, sizeof(buf));
495185a700Sflorian 	while (source->length > 0) {
505185a700Sflorian 		buf[0] = base[((source->base[0]>>3)&0x1f)];	/* 5 + */
515185a700Sflorian 		if (source->length == 1) {
525185a700Sflorian 			buf[1] = base[(source->base[0]<<2)&0x1c];
535185a700Sflorian 			buf[2] = buf[3] = buf[4] = pad;
545185a700Sflorian 			buf[5] = buf[6] = buf[7] = pad;
55873f12b9Sflorian 			RETERR(isc_str_tobuffer(buf, target));
565185a700Sflorian 			break;
575185a700Sflorian 		}
585185a700Sflorian 		buf[1] = base[((source->base[0]<<2)&0x1c)|	/* 3 = 8 */
595185a700Sflorian 			      ((source->base[1]>>6)&0x03)];	/* 2 + */
605185a700Sflorian 		buf[2] = base[((source->base[1]>>1)&0x1f)];	/* 5 + */
615185a700Sflorian 		if (source->length == 2) {
625185a700Sflorian 			buf[3] = base[(source->base[1]<<4)&0x10];
635185a700Sflorian 			buf[4] = buf[5] = buf[6] = buf[7] = pad;
64873f12b9Sflorian 			RETERR(isc_str_tobuffer(buf, target));
655185a700Sflorian 			break;
665185a700Sflorian 		}
675185a700Sflorian 		buf[3] = base[((source->base[1]<<4)&0x10)|	/* 1 = 8 */
685185a700Sflorian 			      ((source->base[2]>>4)&0x0f)];	/* 4 + */
695185a700Sflorian 		if (source->length == 3) {
705185a700Sflorian 			buf[4] = base[(source->base[2]<<1)&0x1e];
715185a700Sflorian 			buf[5] = buf[6] = buf[7] = pad;
72873f12b9Sflorian 			RETERR(isc_str_tobuffer(buf, target));
735185a700Sflorian 			break;
745185a700Sflorian 		}
755185a700Sflorian 		buf[4] = base[((source->base[2]<<1)&0x1e)|	/* 4 = 8 */
765185a700Sflorian 			      ((source->base[3]>>7)&0x01)];	/* 1 + */
775185a700Sflorian 		buf[5] = base[((source->base[3]>>2)&0x1f)];	/* 5 + */
785185a700Sflorian 		if (source->length == 4) {
795185a700Sflorian 			buf[6] = base[(source->base[3]<<3)&0x18];
805185a700Sflorian 			buf[7] = pad;
81873f12b9Sflorian 			RETERR(isc_str_tobuffer(buf, target));
825185a700Sflorian 			break;
835185a700Sflorian 		}
845185a700Sflorian 		buf[6] = base[((source->base[3]<<3)&0x18)|	/* 2 = 8 */
855185a700Sflorian 			      ((source->base[4]>>5)&0x07)];	/* 3 + */
865185a700Sflorian 		buf[7] = base[source->base[4]&0x1f];		/* 5 = 8 */
87873f12b9Sflorian 		RETERR(isc_str_tobuffer(buf, target));
885185a700Sflorian 		isc_region_consume(source, 5);
895185a700Sflorian 
905185a700Sflorian 		loops++;
915185a700Sflorian 		if (source->length != 0 && wordlength >= 0 &&
925185a700Sflorian 		    (int)((loops + 1) * 8) >= wordlength)
935185a700Sflorian 		{
945185a700Sflorian 			loops = 0;
95873f12b9Sflorian 			RETERR(isc_str_tobuffer(wordbreak, target));
965185a700Sflorian 		}
975185a700Sflorian 	}
985185a700Sflorian 	if (source->length > 0)
995185a700Sflorian 		isc_region_consume(source, source->length);
1005185a700Sflorian 	return (ISC_R_SUCCESS);
1015185a700Sflorian }
1025185a700Sflorian 
1035185a700Sflorian isc_result_t
isc_base32hexnp_totext(isc_region_t * source,int wordlength,const char * wordbreak,isc_buffer_t * target)1045185a700Sflorian isc_base32hexnp_totext(isc_region_t *source, int wordlength,
1055185a700Sflorian 		     const char *wordbreak, isc_buffer_t *target)
1065185a700Sflorian {
1075185a700Sflorian 	return (base32_totext(source, wordlength, wordbreak, target,
1085185a700Sflorian 			      base32hex, 0));
1095185a700Sflorian }
1105185a700Sflorian 
1115185a700Sflorian /*%
1125185a700Sflorian  * State of a base32 decoding process in progress.
1135185a700Sflorian  */
1145185a700Sflorian typedef struct {
1155185a700Sflorian 	int length;		/*%< Desired length of binary data or -1 */
1165185a700Sflorian 	isc_buffer_t *target;	/*%< Buffer for resulting binary data */
1175185a700Sflorian 	int digits;		/*%< Number of buffered base32 digits */
118*1fb015a8Sflorian 	int seen_end;	/*%< True if "=" end marker seen */
1195185a700Sflorian 	int val[8];
1205185a700Sflorian 	const char *base;	/*%< Which encoding we are using */
1215185a700Sflorian 	int seen_32;		/*%< Number of significant bytes if non zero */
122*1fb015a8Sflorian 	int pad;	/*%< Expect padding */
1235185a700Sflorian } base32_decode_ctx_t;
1245185a700Sflorian 
1255185a700Sflorian static inline void
base32_decode_init(base32_decode_ctx_t * ctx,int length,const char base[],int pad,isc_buffer_t * target)1265185a700Sflorian base32_decode_init(base32_decode_ctx_t *ctx, int length, const char base[],
127*1fb015a8Sflorian 		   int pad, isc_buffer_t *target)
1285185a700Sflorian {
1295185a700Sflorian 	ctx->digits = 0;
130*1fb015a8Sflorian 	ctx->seen_end = 0;
1315185a700Sflorian 	ctx->seen_32 = 0;
1325185a700Sflorian 	ctx->length = length;
1335185a700Sflorian 	ctx->target = target;
1345185a700Sflorian 	ctx->base = base;
1355185a700Sflorian 	ctx->pad = pad;
1365185a700Sflorian }
1375185a700Sflorian 
1385185a700Sflorian static inline isc_result_t
base32_decode_char(base32_decode_ctx_t * ctx,int c)1395185a700Sflorian base32_decode_char(base32_decode_ctx_t *ctx, int c) {
1405185a700Sflorian 	const char *s;
1415185a700Sflorian 	unsigned int last;
1425185a700Sflorian 
1435185a700Sflorian 	if (ctx->seen_end)
1445185a700Sflorian 		return (ISC_R_BADBASE32);
1455185a700Sflorian 	if ((s = strchr(ctx->base, c)) == NULL)
1465185a700Sflorian 		return (ISC_R_BADBASE32);
1475185a700Sflorian 	last = (unsigned int)(s - ctx->base);
1485185a700Sflorian 
1495185a700Sflorian 	/*
1505185a700Sflorian 	 * Handle lower case.
1515185a700Sflorian 	 */
1525185a700Sflorian 	if (last > 32)
1535185a700Sflorian 		last -= 33;
1545185a700Sflorian 
1555185a700Sflorian 	/*
1565185a700Sflorian 	 * Check that padding is contiguous.
1575185a700Sflorian 	 */
1585185a700Sflorian 	if (last != 32 && ctx->seen_32 != 0)
1595185a700Sflorian 		return (ISC_R_BADBASE32);
1605185a700Sflorian 
1615185a700Sflorian 	/*
1625185a700Sflorian 	 * If padding is not permitted flag padding as a error.
1635185a700Sflorian 	 */
1645185a700Sflorian 	if (last == 32 && !ctx->pad)
1655185a700Sflorian 		return (ISC_R_BADBASE32);
1665185a700Sflorian 
1675185a700Sflorian 	/*
1685185a700Sflorian 	 * Check that padding starts at the right place and that
1695185a700Sflorian 	 * bits that should be zero are.
1705185a700Sflorian 	 * Record how many significant bytes in answer (seen_32).
1715185a700Sflorian 	 */
1725185a700Sflorian 	if (last == 32 && ctx->seen_32 == 0)
1735185a700Sflorian 		switch (ctx->digits) {
1745185a700Sflorian 		case 0:
1755185a700Sflorian 		case 1:
1765185a700Sflorian 			return (ISC_R_BADBASE32);
1775185a700Sflorian 		case 2:
1785185a700Sflorian 			if ((ctx->val[1]&0x03) != 0)
1795185a700Sflorian 				return (ISC_R_BADBASE32);
1805185a700Sflorian 			ctx->seen_32 = 1;
1815185a700Sflorian 			break;
1825185a700Sflorian 		case 3:
1835185a700Sflorian 			return (ISC_R_BADBASE32);
1845185a700Sflorian 		case 4:
1855185a700Sflorian 			if ((ctx->val[3]&0x0f) != 0)
1865185a700Sflorian 				return (ISC_R_BADBASE32);
1875185a700Sflorian 			ctx->seen_32 = 3;
1885185a700Sflorian 			break;
1895185a700Sflorian 		case 5:
1905185a700Sflorian 			if ((ctx->val[4]&0x01) != 0)
1915185a700Sflorian 				return (ISC_R_BADBASE32);
1925185a700Sflorian 			ctx->seen_32 = 3;
1935185a700Sflorian 			break;
1945185a700Sflorian 		case 6:
1955185a700Sflorian 			return (ISC_R_BADBASE32);
1965185a700Sflorian 		case 7:
1975185a700Sflorian 			if ((ctx->val[6]&0x07) != 0)
1985185a700Sflorian 				return (ISC_R_BADBASE32);
1995185a700Sflorian 			ctx->seen_32 = 4;
2005185a700Sflorian 			break;
2015185a700Sflorian 		}
2025185a700Sflorian 
2035185a700Sflorian 	/*
2045185a700Sflorian 	 * Zero fill pad values.
2055185a700Sflorian 	 */
2065185a700Sflorian 	ctx->val[ctx->digits++] = (last == 32) ? 0 : last;
2075185a700Sflorian 
2085185a700Sflorian 	if (ctx->digits == 8) {
2095185a700Sflorian 		int n = 5;
2105185a700Sflorian 		unsigned char buf[5];
2115185a700Sflorian 
2125185a700Sflorian 		if (ctx->seen_32 != 0) {
213*1fb015a8Sflorian 			ctx->seen_end = 1;
2145185a700Sflorian 			n = ctx->seen_32;
2155185a700Sflorian 		}
2165185a700Sflorian 		buf[0] = (ctx->val[0]<<3)|(ctx->val[1]>>2);
2175185a700Sflorian 		buf[1] = (ctx->val[1]<<6)|(ctx->val[2]<<1)|(ctx->val[3]>>4);
2185185a700Sflorian 		buf[2] = (ctx->val[3]<<4)|(ctx->val[4]>>1);
2195185a700Sflorian 		buf[3] = (ctx->val[4]<<7)|(ctx->val[5]<<2)|(ctx->val[6]>>3);
2205185a700Sflorian 		buf[4] = (ctx->val[6]<<5)|(ctx->val[7]);
221637d8eb6Sflorian 		RETERR(isc_mem_tobuffer(ctx->target, buf, n));
2225185a700Sflorian 		if (ctx->length >= 0) {
2235185a700Sflorian 			if (n > ctx->length)
2245185a700Sflorian 				return (ISC_R_BADBASE32);
2255185a700Sflorian 			else
2265185a700Sflorian 				ctx->length -= n;
2275185a700Sflorian 		}
2285185a700Sflorian 		ctx->digits = 0;
2295185a700Sflorian 	}
2305185a700Sflorian 	return (ISC_R_SUCCESS);
2315185a700Sflorian }
2325185a700Sflorian 
2335185a700Sflorian static inline isc_result_t
base32_decode_finish(base32_decode_ctx_t * ctx)2345185a700Sflorian base32_decode_finish(base32_decode_ctx_t *ctx) {
2355185a700Sflorian 
2365185a700Sflorian 	if (ctx->length > 0)
2375185a700Sflorian 		return (ISC_R_UNEXPECTEDEND);
2385185a700Sflorian 	/*
2395185a700Sflorian 	 * Add missing padding if required.
2405185a700Sflorian 	 */
2415185a700Sflorian 	if (!ctx->pad && ctx->digits != 0) {
242*1fb015a8Sflorian 		ctx->pad = 1;
2435185a700Sflorian 		do {
2445185a700Sflorian 			RETERR(base32_decode_char(ctx, '='));
2455185a700Sflorian 		} while (ctx->digits != 0);
2465185a700Sflorian 	}
2475185a700Sflorian 	if (ctx->digits != 0)
2485185a700Sflorian 		return (ISC_R_BADBASE32);
2495185a700Sflorian 	return (ISC_R_SUCCESS);
2505185a700Sflorian }
2515185a700Sflorian 
2525185a700Sflorian static isc_result_t
base32_decoderegion(isc_region_t * source,const char base[],int pad,isc_buffer_t * target)2535185a700Sflorian base32_decoderegion(isc_region_t *source, const char base[],
254*1fb015a8Sflorian 		    int pad, isc_buffer_t *target)
2555185a700Sflorian {
2565185a700Sflorian 	base32_decode_ctx_t ctx;
2575185a700Sflorian 
2585185a700Sflorian 	base32_decode_init(&ctx, -1, base, pad, target);
2595185a700Sflorian 	while (source->length != 0) {
2605185a700Sflorian 		int c = *source->base;
2615185a700Sflorian 		RETERR(base32_decode_char(&ctx, c));
2625185a700Sflorian 		isc_region_consume(source, 1);
2635185a700Sflorian 	}
2645185a700Sflorian 	RETERR(base32_decode_finish(&ctx));
2655185a700Sflorian 	return (ISC_R_SUCCESS);
2665185a700Sflorian }
2675185a700Sflorian 
2685185a700Sflorian isc_result_t
isc_base32hexnp_decoderegion(isc_region_t * source,isc_buffer_t * target)2695185a700Sflorian isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) {
270*1fb015a8Sflorian 	return (base32_decoderegion(source, base32hex, 0, target));
2715185a700Sflorian }
272