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