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, ®ion); 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