Coverage Report

Created: 2026-07-30 06:31

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.72k
        isc_buffer_t *target, const char base[], char pad) {
46
3.72k
  char buf[9];
47
3.72k
  unsigned int loops = 0;
48
49
3.72k
  if (wordlength >= 0 && wordlength < 8) {
50
3.72k
    wordlength = 8;
51
3.72k
  }
52
53
3.72k
  memset(buf, 0, sizeof(buf));
54
7.70k
  while (source->length > 0) {
55
6.56k
    buf[0] = base[((source->base[0] >> 3) & 0x1f)]; /* 5 + */
56
6.56k
    if (source->length == 1) {
57
749
      buf[1] = base[(source->base[0] << 2) & 0x1c];
58
749
      buf[2] = buf[3] = buf[4] = pad;
59
749
      buf[5] = buf[6] = buf[7] = pad;
60
749
      RETERR(str_totext(buf, target));
61
749
      break;
62
749
    }
63
5.81k
    buf[1] = base[((source->base[0] << 2) & 0x1c) | /* 3 = 8 */
64
5.81k
            ((source->base[1] >> 6) & 0x03)]; /* 2 + */
65
5.81k
    buf[2] = base[((source->base[1] >> 1) & 0x1f)]; /* 5 + */
66
5.81k
    if (source->length == 2) {
67
867
      buf[3] = base[(source->base[1] << 4) & 0x10];
68
867
      buf[4] = buf[5] = buf[6] = buf[7] = pad;
69
867
      RETERR(str_totext(buf, target));
70
867
      break;
71
867
    }
72
4.94k
    buf[3] = base[((source->base[1] << 4) & 0x10) | /* 1 = 8 */
73
4.94k
            ((source->base[2] >> 4) & 0x0f)]; /* 4 + */
74
4.94k
    if (source->length == 3) {
75
317
      buf[4] = base[(source->base[2] << 1) & 0x1e];
76
317
      buf[5] = buf[6] = buf[7] = pad;
77
317
      RETERR(str_totext(buf, target));
78
317
      break;
79
317
    }
80
4.63k
    buf[4] = base[((source->base[2] << 1) & 0x1e) | /* 4 = 8 */
81
4.63k
            ((source->base[3] >> 7) & 0x01)]; /* 1 + */
82
4.63k
    buf[5] = base[((source->base[3] >> 2) & 0x1f)]; /* 5 + */
83
4.63k
    if (source->length == 4) {
84
648
      buf[6] = base[(source->base[3] << 3) & 0x18];
85
648
      buf[7] = pad;
86
648
      RETERR(str_totext(buf, target));
87
648
      break;
88
648
    }
89
3.98k
    buf[6] = base[((source->base[3] << 3) & 0x18) | /* 2 = 8 */
90
3.98k
            ((source->base[4] >> 5) & 0x07)]; /* 3 + */
91
3.98k
    buf[7] = base[source->base[4] & 0x1f];    /* 5 = 8 */
92
3.98k
    RETERR(str_totext(buf, target));
93
3.98k
    isc_region_consume(source, 5);
94
95
3.98k
    loops++;
96
3.98k
    if (source->length != 0 && wordlength >= 0 &&
97
2.84k
        (int)((loops + 1) * 8) >= wordlength)
98
2.84k
    {
99
2.84k
      loops = 0;
100
2.84k
      RETERR(str_totext(wordbreak, target));
101
2.84k
    }
102
3.98k
  }
103
3.72k
  if (source->length > 0) {
104
2.58k
    isc_region_consume(source, source->length);
105
2.58k
  }
106
3.72k
  return ISC_R_SUCCESS;
107
3.72k
}
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.72k
           const char *wordbreak, isc_buffer_t *target) {
126
3.72k
  return base32_totext(source, wordlength, wordbreak, target, base32hex,
127
3.72k
           0);
128
3.72k
}
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
71.8k
base32_decode_char(base32_decode_ctx_t *ctx, int c) {
147
71.8k
  const char *s;
148
71.8k
  unsigned int last;
149
150
71.8k
  if (ctx->seen_end) {
151
0
    return ISC_R_BADBASE32;
152
0
  }
153
71.8k
  if ((s = strchr(ctx->base, c)) == NULL) {
154
30
    return ISC_R_BADBASE32;
155
30
  }
156
71.8k
  last = (unsigned int)(s - ctx->base);
157
158
  /*
159
   * Handle lower case.
160
   */
161
71.8k
  if (last > 32) {
162
10.5k
    last -= 33;
163
10.5k
  }
164
165
  /*
166
   * Check that padding is contiguous.
167
   */
168
71.8k
  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
71.8k
  if (last == 32 && !ctx->pad) {
176
5
    return ISC_R_BADBASE32;
177
5
  }
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
71.8k
  if (last == 32 && ctx->seen_32 == 0) {
185
4.11k
    switch (ctx->digits) {
186
0
    case 0:
187
13
    case 1:
188
13
      return ISC_R_BADBASE32;
189
1.25k
    case 2:
190
1.25k
      if ((ctx->val[1] & 0x03) != 0) {
191
6
        return ISC_R_BADBASE32;
192
6
      }
193
1.25k
      ctx->seen_32 = 1;
194
1.25k
      break;
195
6
    case 3:
196
6
      return ISC_R_BADBASE32;
197
671
    case 4:
198
671
      if ((ctx->val[3] & 0x0f) != 0) {
199
6
        return ISC_R_BADBASE32;
200
6
      }
201
665
      ctx->seen_32 = 2;
202
665
      break;
203
1.65k
    case 5:
204
1.65k
      if ((ctx->val[4] & 0x01) != 0) {
205
3
        return ISC_R_BADBASE32;
206
3
      }
207
1.65k
      ctx->seen_32 = 3;
208
1.65k
      break;
209
3
    case 6:
210
3
      return ISC_R_BADBASE32;
211
502
    case 7:
212
502
      if ((ctx->val[6] & 0x07) != 0) {
213
3
        return ISC_R_BADBASE32;
214
3
      }
215
499
      ctx->seen_32 = 4;
216
499
      break;
217
4.11k
    }
218
4.11k
  }
219
220
  /*
221
   * Zero fill pad values.
222
   */
223
71.8k
  ctx->val[ctx->digits++] = (last == 32) ? 0 : last;
224
225
71.8k
  if (ctx->digits == 8) {
226
8.95k
    int n = 5;
227
8.95k
    unsigned char buf[5];
228
229
8.95k
    if (ctx->seen_32 != 0) {
230
4.07k
      ctx->seen_end = true;
231
4.07k
      n = ctx->seen_32;
232
4.07k
    }
233
8.95k
    buf[0] = (ctx->val[0] << 3) | (ctx->val[1] >> 2);
234
8.95k
    buf[1] = (ctx->val[1] << 6) | (ctx->val[2] << 1) |
235
8.95k
       (ctx->val[3] >> 4);
236
8.95k
    buf[2] = (ctx->val[3] << 4) | (ctx->val[4] >> 1);
237
8.95k
    buf[3] = (ctx->val[4] << 7) | (ctx->val[5] << 2) |
238
8.95k
       (ctx->val[6] >> 3);
239
8.95k
    buf[4] = (ctx->val[6] << 5) | (ctx->val[7]);
240
8.95k
    RETERR(mem_tobuffer(ctx->target, buf, n));
241
8.95k
    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
8.95k
    ctx->digits = 0;
249
8.95k
  }
250
71.8k
  return ISC_R_SUCCESS;
251
71.8k
}
252
253
static isc_result_t
254
8.18k
base32_decode_finish(base32_decode_ctx_t *ctx) {
255
8.18k
  if (ctx->length > 0) {
256
0
    return ISC_R_UNEXPECTEDEND;
257
0
  }
258
  /*
259
   * Add missing padding if required.
260
   */
261
8.18k
  if (!ctx->pad && ctx->digits != 0) {
262
4.11k
    ctx->pad = true;
263
15.6k
    do {
264
15.6k
      RETERR(base32_decode_char(ctx, '='));
265
15.6k
    } while (ctx->digits != 0);
266
4.11k
  }
267
8.14k
  if (ctx->digits != 0) {
268
0
    return ISC_R_BADBASE32;
269
0
  }
270
8.14k
  return ISC_R_SUCCESS;
271
8.14k
}
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.16k
        isc_buffer_t *target) {
334
3.16k
  base32_decode_ctx_t ctx = {
335
3.16k
    .length = -1, .base = base, .target = target, .pad = pad
336
3.16k
  };
337
338
23.5k
  for (;;) {
339
23.5k
    int c = *cstr++;
340
23.5k
    if (c == '\0') {
341
3.13k
      break;
342
3.13k
    }
343
20.4k
    if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
344
0
      continue;
345
0
    }
346
20.4k
    RETERR(base32_decode_char(&ctx, c));
347
20.4k
  }
348
3.13k
  RETERR(base32_decode_finish(&ctx));
349
3.10k
  return ISC_R_SUCCESS;
350
3.13k
}
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.16k
isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target) {
364
3.16k
  return base32_decodestring(cstr, base32hex, false, target);
365
3.16k
}
366
367
static isc_result_t
368
base32_decoderegion(isc_region_t *source, const char base[], bool pad,
369
5.06k
        isc_buffer_t *target) {
370
5.06k
  base32_decode_ctx_t ctx = {
371
5.06k
    .length = -1, .base = base, .target = target, .pad = pad
372
5.06k
  };
373
374
40.8k
  while (source->length != 0) {
375
35.7k
    int c = *source->base;
376
35.7k
    RETERR(base32_decode_char(&ctx, c));
377
35.7k
    isc_region_consume(source, 1);
378
35.7k
  }
379
5.05k
  RETERR(base32_decode_finish(&ctx));
380
5.04k
  return ISC_R_SUCCESS;
381
5.05k
}
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
5.06k
isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) {
395
5.06k
  return base32_decoderegion(source, base32hex, false, target);
396
5.06k
}
397
398
static isc_result_t
399
9.40k
str_totext(const char *source, isc_buffer_t *target) {
400
9.40k
  unsigned int l;
401
9.40k
  isc_region_t region;
402
403
9.40k
  isc_buffer_availableregion(target, &region);
404
9.40k
  l = strlen(source);
405
406
9.40k
  if (l > region.length) {
407
0
    return ISC_R_NOSPACE;
408
0
  }
409
410
9.40k
  memmove(region.base, source, l);
411
9.40k
  isc_buffer_add(target, l);
412
9.40k
  return ISC_R_SUCCESS;
413
9.40k
}
414
415
static isc_result_t
416
8.95k
mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
417
8.95k
  isc_region_t tr;
418
419
8.95k
  isc_buffer_availableregion(target, &tr);
420
8.95k
  if (length > tr.length) {
421
4
    return ISC_R_NOSPACE;
422
4
  }
423
8.95k
  memmove(tr.base, base, length);
424
8.95k
  isc_buffer_add(target, length);
425
8.95k
  return ISC_R_SUCCESS;
426
8.95k
}