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