Coverage Report

Created: 2026-08-31 07:21

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