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