Coverage Report

Created: 2026-10-17 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/base64.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "base64.h"
5
#include "buffer.h"
6
7
/*
8
 * Low-level Base64 encoder
9
 */
10
11
uoff_t base64_get_full_encoded_size(struct base64_encoder *enc, uoff_t src_size)
12
0
{
13
0
  bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
14
0
  bool no_padding = HAS_ALL_BITS(enc->flags,
15
0
               BASE64_ENCODE_FLAG_NO_PADDING);
16
0
  uoff_t out_size;
17
0
  uoff_t newlines;
18
19
0
  if (src_size == 0)
20
0
    return 0;
21
22
  /* calculate size of encoded data */
23
0
  out_size = MAX_BASE64_ENCODED_SIZE(src_size);
24
0
  if (no_padding) {
25
0
    switch (src_size % 3) {
26
0
    case 0:
27
0
      break;
28
0
    case 1:
29
0
      i_assert(out_size > 2);
30
0
      out_size -= 2;
31
0
      break;
32
0
    case 2:
33
0
      i_assert(out_size > 1);
34
0
      out_size -= 1;
35
0
      break;
36
0
    }
37
0
  }
38
39
0
  if (out_size > enc->max_line_len) {
40
    /* newline between each full line */
41
0
    i_assert(enc->max_line_len > 0);
42
0
    newlines = (out_size / enc->max_line_len) - 1;
43
    /* an extra newline to separate the partial last line from the
44
       previous full line */
45
0
    if ((out_size % enc->max_line_len) != 0)
46
0
      newlines++;
47
48
    /* update size with added newlines */
49
0
    out_size += newlines * (crlf ? 2 : 1);
50
0
  }
51
52
0
  return out_size;
53
0
}
54
55
static size_t
56
base64_encode_get_out_size(struct base64_encoder *enc, size_t src_size)
57
15.2k
{
58
15.2k
  size_t res_size = enc->w_buf_len;
59
60
15.2k
  i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
61
62
15.2k
  if (src_size == 0)
63
0
    return res_size;
64
65
  /* Handle sub-position */
66
15.2k
  switch (enc->sub_pos) {
67
15.2k
  case 0:
68
15.2k
    break;
69
0
  case 1:
70
0
    res_size++;
71
0
    src_size--;
72
0
    if (src_size == 0)
73
0
      return res_size;
74
    /* fall through */
75
0
  case 2:
76
0
    res_size += 2;
77
0
    src_size--;
78
0
    break;
79
0
  default:
80
0
    i_unreached();
81
15.2k
  }
82
83
  /* We're now at a 3-byte boundary */
84
15.2k
  if (src_size == 0)
85
0
    return res_size;
86
87
  /* Calculate size we can append to the output from remaining input */
88
15.2k
  res_size += ((src_size) / 3) * 4;
89
15.2k
  switch (src_size % 3) {
90
1.16k
  case 0:
91
1.16k
    break;
92
8.61k
  case 1:
93
8.61k
    res_size += 1;
94
8.61k
    break;
95
5.51k
  case 2:
96
5.51k
    res_size += 2;
97
5.51k
    break;
98
15.2k
  }
99
15.2k
  return res_size;
100
15.2k
}
101
102
size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size)
103
0
{
104
0
  bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
105
0
  size_t out_size = base64_encode_get_out_size(enc, src_size);
106
107
0
  if (src_size == 0) {
108
    /* last block */
109
0
    switch (enc->sub_pos) {
110
0
    case 0:
111
0
      break;
112
0
    case 1:
113
0
      out_size += 3;
114
0
      break;
115
0
    case 2:
116
0
      out_size += 2;
117
0
      break;
118
0
    default:
119
0
      i_unreached();
120
0
    }
121
0
  }
122
123
0
  if (enc->max_line_len < SIZE_MAX) {
124
0
    size_t line_part, lines;
125
126
    /* Calculate how many line endings must be added */
127
0
    i_assert(enc->max_line_len > 0);
128
0
    lines = out_size / enc->max_line_len;
129
0
    line_part = out_size % enc->max_line_len;
130
0
    if (enc->cur_line_len > (enc->max_line_len - line_part))
131
0
      lines++;
132
133
0
    out_size += lines * (crlf ? 2 : 1);
134
0
  }
135
136
0
  if (enc->pending_lf)
137
0
    out_size++;
138
139
0
  return out_size;
140
0
}
141
142
size_t base64_encode_get_full_space(struct base64_encoder *enc,
143
            size_t dst_space)
144
0
{
145
0
  bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
146
0
  bool no_padding = HAS_ALL_BITS(enc->flags,
147
0
               BASE64_ENCODE_FLAG_NO_PADDING);
148
0
  size_t src_space = 0;
149
150
0
  i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
151
152
0
  if (enc->max_line_len < SIZE_MAX) {
153
0
    size_t max_line_space, lines, nl_space;
154
155
    /* Calculate how many line endings must be added if all space
156
       were used. */
157
0
    i_assert(enc->max_line_len < SIZE_MAX-2);
158
0
    max_line_space = enc->max_line_len + (crlf ? 2 : 1);
159
0
    lines = dst_space / max_line_space;
160
161
    /* Calculate how much space is used by newline characters and
162
       subtract this from the available space. */
163
0
    nl_space = lines * (crlf ? 2 : 1);
164
0
    if (dst_space <= nl_space)
165
0
      return 0;
166
0
    dst_space -= nl_space;
167
0
  }
168
169
0
  if (dst_space <= enc->w_buf_len)
170
0
    return 0;
171
0
  dst_space -= enc->w_buf_len;
172
173
0
  if (enc->pending_lf)
174
0
    dst_space--;
175
0
  if (dst_space == 0)
176
0
    return 0;
177
178
  /* Handle sub-position */
179
0
  switch (enc->sub_pos) {
180
0
  case 0:
181
0
    break;
182
0
  case 1:
183
0
    dst_space--;
184
0
    src_space++;
185
    /* fall through */
186
0
  case 2:
187
0
    if (dst_space < 2)
188
0
      return src_space;
189
0
    dst_space -= 2;
190
0
    src_space++;
191
0
    break;
192
0
  default:
193
0
    i_unreached();
194
0
  }
195
196
0
  if (dst_space == 0)
197
0
    return src_space;
198
199
0
  src_space += dst_space / 4 * 3;
200
0
  if (no_padding) {
201
0
    switch (dst_space % 4) {
202
0
    case 0:
203
0
    case 1:
204
0
      break;
205
0
    case 2:
206
0
      src_space += 1;
207
0
      break;
208
0
    case 3:
209
0
      src_space += 2;
210
0
      break;
211
0
    }
212
0
  }
213
214
0
  return src_space;
215
0
}
216
217
static void
218
base64_encode_more_data(struct base64_encoder *enc,
219
      const unsigned char *src_c, size_t src_size,
220
      size_t *src_pos_r, size_t dst_avail, buffer_t *dest)
221
15.2k
{
222
15.2k
  const struct base64_scheme *b64 = enc->b64;
223
15.2k
  const char *b64enc = b64->encmap;
224
15.2k
  size_t res_size;
225
15.2k
  unsigned char *start, *ptr, *end;
226
15.2k
  size_t src_pos;
227
228
15.2k
  i_assert(!enc->pending_lf);
229
230
  /* determine how much we can write in destination buffer */
231
15.2k
  if (dst_avail == 0) {
232
0
    *src_pos_r = 0;
233
0
    return;
234
0
  }
235
236
  /* pre-allocate space in the destination buffer */
237
15.2k
  res_size = base64_encode_get_out_size(enc, src_size);
238
15.2k
  if (res_size > dst_avail)
239
0
    res_size = dst_avail;
240
241
15.2k
  start = buffer_append_space_unsafe(dest, res_size);
242
15.2k
  end = start + res_size;
243
15.2k
  ptr = start;
244
245
  /* write bytes not written in previous call */
246
15.2k
  i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
247
15.2k
  if (enc->w_buf_len > res_size) {
248
0
    memcpy(ptr, enc->w_buf, res_size);
249
0
    ptr += res_size;
250
0
    enc->w_buf_len -= res_size;
251
0
    memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len);
252
15.2k
  } else if (enc->w_buf_len > 0) {
253
0
    memcpy(ptr, enc->w_buf, enc->w_buf_len);
254
0
    ptr += enc->w_buf_len;
255
0
    enc->w_buf_len = 0;
256
0
  }
257
15.2k
  if (ptr == end) {
258
0
    *src_pos_r = 0;
259
0
    return;
260
0
  }
261
15.2k
  i_assert(enc->w_buf_len == 0);
262
15.2k
  i_assert(src_size != 0);
263
264
  /* Handle sub-position */
265
15.2k
  src_pos = 0;
266
15.2k
  switch (enc->sub_pos) {
267
15.2k
  case 0:
268
15.2k
    break;
269
0
  case 1:
270
0
    i_assert(ptr < end);
271
0
    ptr[0] = b64enc[enc->buf | (src_c[src_pos] >> 4)];
272
0
    ptr++;
273
0
    enc->buf = (src_c[src_pos] & 0x0f) << 2;
274
0
    src_pos++;
275
0
    if (src_pos == src_size || ptr == end) {
276
0
      enc->sub_pos = 2;
277
0
      *src_pos_r = src_pos;
278
0
      return;
279
0
    }
280
    /* fall through */
281
0
  case 2:
282
0
    ptr[0] = b64enc[enc->buf | ((src_c[src_pos] & 0xc0) >> 6)];
283
0
    enc->w_buf[0] = b64enc[src_c[src_pos] & 0x3f];
284
0
    ptr++;
285
0
    src_pos++;
286
0
    if (ptr < end) {
287
0
      ptr[0] = enc->w_buf[0];
288
0
      ptr++;
289
0
      enc->w_buf_len = 0;
290
0
    } else {
291
0
      enc->sub_pos = 0;
292
0
      enc->w_buf_len = 1;
293
0
      *src_pos_r = src_pos;
294
0
      return;
295
0
    }
296
0
    break;
297
0
  default:
298
0
    i_unreached();
299
15.2k
  }
300
15.2k
  enc->sub_pos = 0;
301
302
  /* We're now at a 3-byte boundary */
303
15.2k
  if (src_pos == src_size) {
304
0
    i_assert(ptr == end);
305
0
    *src_pos_r = src_pos;
306
0
    return;
307
0
  }
308
309
  /* Convert the bulk */
310
22.1M
  for (; src_size - src_pos > 2 && &ptr[3] < end;
311
22.1M
       src_pos += 3, ptr += 4) {
312
22.1M
    ptr[0] = b64enc[src_c[src_pos] >> 2];
313
22.1M
    ptr[1] = b64enc[((src_c[src_pos] & 0x03) << 4) |
314
22.1M
        (src_c[src_pos+1] >> 4)];
315
22.1M
    ptr[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) |
316
22.1M
        ((src_c[src_pos+2] & 0xc0) >> 6)];
317
22.1M
    ptr[3] = b64enc[src_c[src_pos+2] & 0x3f];
318
22.1M
  }
319
320
  /* Convert the bytes beyond the last 3-byte boundary and update state
321
     for next call */
322
15.2k
  switch (src_size - src_pos) {
323
1.16k
  case 0:
324
1.16k
    enc->sub_pos = 0;
325
1.16k
    enc->buf = 0;
326
1.16k
    break;
327
8.61k
  case 1:
328
8.61k
    enc->sub_pos = 1;
329
8.61k
    enc->w_buf[0] = b64enc[src_c[src_pos] >> 2];
330
8.61k
    enc->w_buf_len = 1;
331
8.61k
    enc->buf = (src_c[src_pos] & 0x03) << 4;
332
8.61k
    src_pos++;
333
8.61k
    break;
334
5.51k
  case 2:
335
5.51k
    enc->sub_pos = 2;
336
5.51k
    enc->w_buf[0] = b64enc[src_c[src_pos] >> 2];
337
5.51k
    enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) |
338
5.51k
               (src_c[src_pos+1] >> 4)];
339
5.51k
    enc->w_buf_len = 2;
340
5.51k
    enc->buf = (src_c[src_pos+1] & 0x0f) << 2;
341
5.51k
    src_pos += 2;
342
5.51k
    break;
343
0
  default:
344
    /* hit the end of the destination buffer */
345
0
    enc->sub_pos = 0;
346
0
    enc->w_buf[0] = b64enc[src_c[src_pos] >> 2];
347
0
    enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) |
348
0
               (src_c[src_pos+1] >> 4)];
349
0
    enc->w_buf[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) |
350
0
               ((src_c[src_pos+2] & 0xc0) >> 6)];
351
0
    enc->w_buf[3] = b64enc[src_c[src_pos+2] & 0x3f];
352
0
    enc->w_buf_len = 4;
353
0
    enc->buf = 0;
354
0
    src_pos += 3;
355
15.2k
  }
356
357
  /* fill the remaining allocated space */
358
15.2k
  i_assert(ptr <= end);
359
15.2k
  res_size = end - ptr;
360
15.2k
  i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
361
15.2k
  if (enc->w_buf_len > res_size) {
362
0
    memcpy(ptr, enc->w_buf, res_size);
363
0
    ptr += res_size;
364
0
    enc->w_buf_len -= res_size;
365
0
    memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len);
366
15.2k
  } else if (enc->w_buf_len > 0) {
367
14.1k
    memcpy(ptr, enc->w_buf, enc->w_buf_len);
368
14.1k
    ptr += enc->w_buf_len;
369
14.1k
    enc->w_buf_len = 0;
370
14.1k
  }
371
372
15.2k
  i_assert(ptr == end);
373
15.2k
  *src_pos_r = src_pos;
374
15.2k
}
375
376
bool base64_encode_more(struct base64_encoder *enc,
377
      const void *src, size_t src_size, size_t *src_pos_r,
378
      buffer_t *dest)
379
15.2k
{
380
15.2k
  bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
381
15.2k
  const unsigned char *src_c, *src_p;
382
15.2k
  size_t src_pos, src_left;
383
384
15.2k
  i_assert(!enc->finishing);
385
15.2k
  i_assert(!enc->finished);
386
387
15.2k
  src_p = src_c = src;
388
15.2k
  src_left = src_size;
389
30.5k
  while (src_left > 0) {
390
15.2k
    size_t dst_avail, dst_pos, line_avail, written;
391
392
    /* determine how much we can write in destination buffer */
393
15.2k
    dst_avail = buffer_get_avail_size(dest);
394
15.2k
    if (dst_avail == 0)
395
0
      break;
396
397
    /* Emit pending newline immediately */
398
15.2k
    if (enc->pending_lf) {
399
0
      i_assert(crlf);
400
0
      buffer_append_c(dest, '\n');
401
0
      enc->pending_lf = FALSE;
402
0
      dst_avail--;
403
0
      if (dst_avail == 0)
404
0
        break;
405
0
    }
406
407
15.2k
    i_assert(enc->max_line_len > 0);
408
15.2k
    i_assert(enc->cur_line_len <= enc->max_line_len);
409
15.2k
    line_avail = I_MIN(enc->max_line_len - enc->cur_line_len,
410
15.2k
           dst_avail);
411
412
15.2k
    if (line_avail > 0) {
413
15.2k
      dst_pos = dest->used;
414
15.2k
      base64_encode_more_data(enc, src_p, src_left, &src_pos,
415
15.2k
            line_avail, dest);
416
15.2k
      i_assert(src_pos <= src_left);
417
15.2k
      src_p += src_pos;
418
15.2k
      src_left -= src_pos;
419
15.2k
      i_assert(dest->used >= dst_pos);
420
15.2k
      written = dest->used - dst_pos;
421
422
15.2k
      i_assert(written <= line_avail);
423
15.2k
      i_assert(written <= enc->max_line_len);
424
15.2k
      i_assert(enc->cur_line_len <=
425
15.2k
         (enc->max_line_len - written));
426
15.2k
      enc->cur_line_len += written;
427
15.2k
      dst_avail -= written;
428
15.2k
    }
429
430
15.2k
    if (dst_avail == 0)
431
0
      break;
432
433
15.2k
    if (src_left > 0 && enc->cur_line_len == enc->max_line_len) {
434
0
      if (crlf) {
435
0
        if (dst_avail >= 2) {
436
          /* emit the full CRLF sequence */
437
0
          buffer_append(dest, "\r\n", 2);
438
0
        } else {
439
          /* emit CR */
440
0
          buffer_append_c(dest, '\r');
441
          /* remember the LF */
442
0
          enc->pending_lf = TRUE;
443
0
        }
444
0
      } else {
445
0
        buffer_append_c(dest, '\n');
446
0
      }
447
0
      enc->cur_line_len = 0;
448
0
    }
449
15.2k
  }
450
451
15.2k
  i_assert(src_p >= src_c);
452
15.2k
  src_pos = (src_p - src_c);
453
15.2k
  if (src_pos_r != NULL)
454
0
    *src_pos_r = src_pos;
455
15.2k
  return (src_pos == src_size);
456
15.2k
}
457
458
bool base64_encode_finish(struct base64_encoder *enc, buffer_t *dest)
459
15.2k
{
460
15.2k
  const struct base64_scheme *b64 = enc->b64;
461
15.2k
  const char *b64enc = b64->encmap;
462
15.2k
  bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF);
463
15.2k
  bool padding = HAS_NO_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING);
464
15.2k
  unsigned char *ptr, *end;
465
15.2k
  size_t dst_avail, line_avail, write_full, write;
466
15.2k
  unsigned int w_buf_pos = 0;
467
468
15.2k
  i_assert(!enc->finished);
469
15.2k
  enc->finishing = TRUE;
470
471
15.2k
  dst_avail = 0;
472
15.2k
  if (dest != NULL)
473
15.2k
    dst_avail = buffer_get_avail_size(dest);
474
475
15.2k
  if (enc->w_buf_len > 0 || enc->pending_lf)  {
476
0
    if (dst_avail == 0)
477
0
      return FALSE;
478
0
    i_assert(enc->w_buf_len <= sizeof(enc->w_buf));
479
0
  }
480
481
15.2k
  i_assert(enc->max_line_len > 0);
482
15.2k
  i_assert(enc->cur_line_len <= enc->max_line_len);
483
15.2k
  line_avail = enc->max_line_len - enc->cur_line_len;
484
485
15.2k
  switch (enc->sub_pos) {
486
1.16k
  case 0:
487
1.16k
    break;
488
8.61k
  case 1:
489
8.61k
    i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 3));
490
8.61k
    enc->w_buf[enc->w_buf_len] = b64enc[enc->buf];
491
8.61k
    enc->w_buf_len++;
492
8.61k
    if (padding) {
493
8.61k
      enc->w_buf[enc->w_buf_len + 0] =  '=';
494
8.61k
      enc->w_buf[enc->w_buf_len + 1] =  '=';
495
8.61k
      enc->w_buf_len += 2;
496
8.61k
    }
497
8.61k
    break;
498
5.51k
  case 2:
499
5.51k
    i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 2));
500
5.51k
    enc->w_buf[enc->w_buf_len] = b64enc[enc->buf];
501
5.51k
    enc->w_buf_len++;
502
5.51k
    if (padding) {
503
5.51k
      enc->w_buf[enc->w_buf_len + 0] =  '=';
504
5.51k
      enc->w_buf_len++;
505
5.51k
    }
506
5.51k
    break;
507
0
  default:
508
0
    i_unreached();
509
15.2k
  }
510
15.2k
  enc->sub_pos = 0;
511
512
15.2k
  write_full = write = enc->w_buf_len;
513
15.2k
  if (enc->pending_lf)
514
0
    write_full++;
515
15.2k
  if (enc->max_line_len < SIZE_MAX && line_avail < write) {
516
0
    unsigned int lines;
517
518
0
    lines = I_MAX((write - line_avail) / enc->max_line_len, 1);
519
0
    write_full += lines * (crlf ? 2 : 1);
520
15.2k
  } else {
521
15.2k
    line_avail = write;
522
15.2k
  }
523
524
15.2k
  if (write_full == 0) {
525
1.16k
    enc->finished = TRUE;
526
1.16k
    return TRUE;
527
1.16k
  }
528
529
15.2k
  i_assert(dest != NULL);
530
14.1k
  if (write_full > dst_avail)
531
0
    write_full = dst_avail;
532
533
14.1k
  ptr = buffer_append_space_unsafe(dest, write_full);
534
14.1k
  end = ptr + write_full;
535
14.1k
  if (enc->pending_lf) {
536
0
    ptr[0] = '\n';
537
0
    dst_avail--;
538
0
    ptr++;
539
0
    enc->pending_lf = FALSE;
540
0
  }
541
14.1k
  if (line_avail > dst_avail)
542
0
    line_avail = dst_avail;
543
14.1k
  if (line_avail > 0) {
544
14.1k
    memcpy(ptr, enc->w_buf, line_avail);
545
14.1k
    ptr += line_avail;
546
14.1k
    w_buf_pos += line_avail;
547
14.1k
  }
548
14.1k
  while (ptr < end && w_buf_pos < enc->w_buf_len) {
549
0
    enc->cur_line_len = 0;
550
0
    if (crlf) {
551
0
      ptr[0] = '\r';
552
0
      ptr++;
553
0
      if (ptr == end) {
554
0
        enc->pending_lf = TRUE;
555
0
        break;
556
0
      }
557
0
    }
558
0
    ptr[0] = '\n';
559
0
    ptr++;
560
0
    if (ptr == end)
561
0
      break;
562
563
0
    write = I_MIN(enc->w_buf_len - w_buf_pos, enc->max_line_len);
564
0
    write = I_MIN(write, (size_t)(end - ptr));
565
0
    memcpy(ptr, &enc->w_buf[w_buf_pos], write);
566
0
    ptr += write;
567
0
    w_buf_pos += write;
568
0
    enc->cur_line_len += write;
569
0
    i_assert(ptr <= end);
570
0
  }
571
14.1k
  i_assert(ptr == end);
572
14.1k
  if (w_buf_pos < enc->w_buf_len) {
573
0
    enc->w_buf_len -= w_buf_pos;
574
0
    memmove(enc->w_buf, enc->w_buf + w_buf_pos, enc->w_buf_len);
575
0
    return FALSE;
576
0
  }
577
14.1k
  if (enc->pending_lf)
578
0
    return FALSE;
579
14.1k
  enc->finished = TRUE;
580
14.1k
  return TRUE;
581
14.1k
}
582
583
/*
584
 * Low-level Base64 decoder
585
 */
586
587
#define IS_EMPTY(c) \
588
3.63k
  ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t')
589
590
static inline void
591
base64_skip_whitespace(struct base64_decoder *dec, const unsigned char *src_c,
592
           size_t src_size, size_t *src_pos)
593
6.55k
{
594
6.55k
  if (HAS_ALL_BITS(dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE))
595
0
    return;
596
597
  /* skip any whitespace in the padding */
598
8.77k
  while ((*src_pos) < src_size && IS_EMPTY(src_c[(*src_pos)]))
599
2.21k
    (*src_pos)++;
600
6.55k
}
601
602
int base64_decode_more(struct base64_decoder *dec,
603
           const void *src, size_t src_size, size_t *src_pos_r,
604
           buffer_t *dest)
605
5.29k
{
606
5.29k
  const struct base64_scheme *b64 = dec->b64;
607
5.29k
  const unsigned char *src_c = src;
608
5.29k
  bool expect_boundary = HAS_ALL_BITS(
609
5.29k
    dec->flags, BASE64_DECODE_FLAG_EXPECT_BOUNDARY);
610
5.29k
  bool no_whitespace = HAS_ALL_BITS(
611
5.29k
    dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE);
612
5.29k
  bool no_padding = HAS_ALL_BITS(
613
5.29k
    dec->flags, BASE64_DECODE_FLAG_NO_PADDING);
614
5.29k
  size_t src_pos, dst_avail;
615
5.29k
  int ret = 1;
616
617
5.29k
  i_assert(!dec->finished);
618
5.29k
  i_assert(!dec->failed);
619
620
5.29k
  if (dec->seen_boundary) {
621
    /* already seen the boundary/end of base64 data */
622
0
    if (src_pos_r != NULL)
623
0
      *src_pos_r = 0;
624
0
    dec->failed = TRUE;
625
0
    return -1;
626
0
  }
627
628
5.29k
  src_pos = 0;
629
5.29k
  if (dec->seen_end) {
630
    /* skip any whitespace at the end */
631
0
    base64_skip_whitespace(dec, src_c, src_size, &src_pos);
632
0
    if (src_pos_r != NULL)
633
0
      *src_pos_r = src_pos;
634
0
    if (src_pos < src_size) {
635
0
      if (!expect_boundary) {
636
0
        dec->failed = TRUE;
637
0
        return -1;
638
0
      }
639
0
      dec->seen_boundary = TRUE;
640
0
      return 0;
641
0
    }
642
0
    if (no_whitespace) {
643
0
      dec->seen_boundary = TRUE;
644
0
      return 0;
645
0
    }
646
    /* more whitespace may follow */
647
0
    return 1;
648
0
  }
649
650
5.29k
  if (src_size == 0) {
651
3
    if (src_pos_r != NULL)
652
0
      *src_pos_r = 0;
653
3
    return 1;
654
3
  }
655
656
5.29k
  dst_avail = buffer_get_avail_size(dest);
657
5.29k
  if (dst_avail == 0) {
658
0
    i_assert(src_pos_r != NULL);
659
0
    *src_pos_r = 0;
660
0
    return 1;
661
0
  }
662
663
295k
  for (; !dec->seen_padding && src_pos < src_size; src_pos++) {
664
295k
    unsigned char in = src_c[src_pos];
665
295k
    unsigned char dm = b64->decmap[in];
666
667
295k
    if (dm == 0xff) {
668
11.8k
      if (no_whitespace) {
669
0
        ret = -1;
670
0
        break;
671
0
      }
672
11.8k
      if (unlikely(!IS_EMPTY(in))) {
673
5.27k
        ret = -1;
674
5.27k
        break;
675
5.27k
      }
676
6.53k
      continue;
677
11.8k
    }
678
679
283k
    if (dst_avail == 0) {
680
0
      i_assert(src_pos_r != NULL);
681
0
      *src_pos_r = src_pos;
682
0
      return 1;
683
0
    }
684
685
283k
    switch (dec->sub_pos) {
686
72.6k
    case 0:
687
72.6k
      dec->buf = dm;
688
72.6k
      dec->sub_pos++;
689
72.6k
      break;
690
72.6k
    case 1:
691
72.6k
      dec->buf = (dec->buf << 2) | (dm >> 4);
692
72.6k
      buffer_append_c(dest, dec->buf);
693
72.6k
      dst_avail--;
694
72.6k
      dec->buf = dm;
695
72.6k
      dec->sub_pos++;
696
72.6k
      break;
697
71.2k
    case 2:
698
71.2k
      dec->buf = ((dec->buf << 4) & 0xff) | (dm >> 2);
699
71.2k
      buffer_append_c(dest, dec->buf);
700
71.2k
      dst_avail--;
701
71.2k
      dec->buf = dm;
702
71.2k
      dec->sub_pos++;
703
71.2k
      break;
704
67.3k
    case 3:
705
67.3k
      dec->buf = ((dec->buf << 6) & 0xc0) | dm;
706
67.3k
      buffer_append_c(dest, dec->buf);
707
67.3k
      dst_avail--;
708
67.3k
      dec->buf = 0;
709
67.3k
      dec->sub_pos = 0;
710
67.3k
      break;
711
0
    default:
712
0
      i_unreached();
713
283k
    }
714
283k
  }
715
716
5.29k
  if (dec->seen_padding) {
717
    /* skip any whitespace in or after the padding */
718
0
    base64_skip_whitespace(dec, src_c, src_size, &src_pos);
719
0
    if (src_pos == src_size) {
720
0
      if (src_pos_r != NULL)
721
0
        *src_pos_r = src_pos;
722
0
      return 1;
723
0
    }
724
0
  }
725
726
5.29k
  if (dec->seen_padding || ret < 0) {
727
    /* try to parse the end (padding) of the base64 input */
728
5.27k
    i_assert(src_pos < src_size);
729
730
5.27k
    if (no_padding) {
731
      /* no padding allowed */
732
0
      i_assert(!dec->seen_padding);
733
0
      ret = -1;
734
5.27k
    } else {
735
5.27k
      switch (dec->sub_pos) {
736
10
      case 0:
737
22
      case 1:
738
        /* no padding expected */
739
22
        ret = -1;
740
22
        break;
741
1.38k
      case 2:
742
1.38k
        if (unlikely(src_c[src_pos] != '=')) {
743
          /* invalid character */
744
13
          ret = -1;
745
13
          break;
746
13
        }
747
1.37k
        dec->seen_padding = TRUE;
748
1.37k
        dec->sub_pos++;
749
1.37k
        src_pos++;
750
1.37k
        if (src_pos == src_size) {
751
2
          ret = 1;
752
2
          break;
753
2
        }
754
        /* skip any whitespace in the padding */
755
1.37k
        base64_skip_whitespace(dec, src_c, src_size,
756
1.37k
                   &src_pos);
757
1.37k
        if (src_pos == src_size) {
758
7
          ret = 1;
759
7
          break;
760
7
        }
761
        /* fall through */
762
5.22k
      case 3:
763
5.22k
        if (unlikely(src_c[src_pos] != '=')) {
764
          /* invalid character */
765
43
          ret = -1;
766
43
          break;
767
43
        }
768
5.18k
        dec->seen_padding = TRUE;
769
5.18k
        dec->seen_end = TRUE;
770
5.18k
        dec->sub_pos = 0;
771
5.18k
        src_pos++;
772
        /* skip any trailing whitespace */
773
5.18k
        base64_skip_whitespace(dec, src_c, src_size,
774
5.18k
                   &src_pos);
775
5.18k
        if (src_pos < src_size) {
776
51
          ret = -1;
777
51
          break;
778
51
        }
779
5.13k
        if (no_whitespace) {
780
0
          dec->seen_boundary = TRUE;
781
0
          ret = 0;
782
5.13k
        } else {
783
          /* more whitespace may follow */
784
5.13k
          ret = 1;
785
5.13k
        }
786
5.13k
        break;
787
5.27k
      }
788
5.27k
    }
789
5.27k
  }
790
791
5.29k
  if (ret < 0) {
792
129
    if (!expect_boundary) {
793
129
      dec->failed = TRUE;
794
129
    } else {
795
0
      dec->seen_boundary = TRUE;
796
0
      ret = 0;
797
0
    }
798
129
  }
799
5.29k
  if (src_pos_r != NULL)
800
0
    *src_pos_r = src_pos;
801
5.29k
  return ret;
802
5.29k
}
803
804
int base64_decode_finish(struct base64_decoder *dec)
805
5.16k
{
806
5.16k
  i_assert(!dec->finished);
807
5.16k
  dec->finished = TRUE;
808
809
5.16k
  if (dec->failed)
810
0
    return -1;
811
812
5.16k
  if (HAS_ALL_BITS(dec->flags,
813
5.16k
       BASE64_DECODE_FLAG_NO_PADDING)) {
814
0
    i_assert(!dec->seen_padding);
815
0
    return 0;
816
0
  }
817
5.16k
  if (HAS_ALL_BITS(dec->flags,
818
5.16k
       BASE64_DECODE_FLAG_IGNORE_PADDING))
819
0
    return 0;
820
5.16k
  return (dec->sub_pos == 0 ? 0 : -1);
821
5.16k
}
822
823
/*
824
 * Generic Base64 API
825
 */
826
827
buffer_t *t_base64_scheme_encode(const struct base64_scheme *b64,
828
         enum base64_encode_flags flags,
829
         size_t max_line_len,
830
         const void *src, size_t src_size)
831
1.48k
{
832
1.48k
  buffer_t *buf;
833
834
1.48k
        buf = t_buffer_create(MAX_BASE64_ENCODED_SIZE(src_size));
835
1.48k
        base64_scheme_encode(b64, flags, max_line_len, src, src_size, buf);
836
1.48k
        return buf;
837
1.48k
}
838
839
840
int base64_scheme_decode(const struct base64_scheme *b64,
841
       enum base64_decode_flags flags,
842
       const void *src, size_t src_size, buffer_t *dest)
843
5.29k
{
844
5.29k
  struct base64_decoder dec;
845
5.29k
  int ret;
846
847
5.29k
  base64_decode_init(&dec, b64, flags);
848
5.29k
  ret = base64_decode_more(&dec, src, src_size, NULL, dest);
849
5.29k
  if (ret >= 0)
850
5.16k
    ret = base64_decode_finish(&dec);
851
852
5.29k
  return ret;
853
5.29k
}
854
855
buffer_t *t_base64_scheme_decode(const struct base64_scheme *b64,
856
         enum base64_decode_flags flags,
857
         const void *src, size_t src_size)
858
0
{
859
0
  buffer_t *buf;
860
861
0
  buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(src_size));
862
0
  (void)base64_scheme_decode(b64, flags, src, src_size, buf);
863
0
  return buf;
864
0
}
865
866
/*
867
 * "base64" encoding scheme (RFC 4648, Section 4)
868
 */
869
870
struct base64_scheme base64_scheme = {
871
  .encmap = {
872
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
873
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
874
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
875
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
876
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
877
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
878
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
879
    '4', '5', '6', '7', '8', '9', '+', '/',
880
  },
881
  .decmap = {
882
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */
883
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */
884
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */
885
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */
886
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */
887
    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, /* 40-47 */
888
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */
889
    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */
890
    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */
891
    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */
892
    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */
893
    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */
894
    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */
895
    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */
896
    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */
897
    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */
898
899
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */
900
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
901
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
902
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
903
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
904
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
905
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
906
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
907
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
908
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
909
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
910
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
911
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
912
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
913
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
914
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
915
  },
916
};
917
918
/*
919
 * "base64url" encoding scheme (RFC 4648, Section 5)
920
 */
921
922
struct base64_scheme base64url_scheme = {
923
  .encmap = {
924
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
925
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
926
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
927
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
928
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
929
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
930
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
931
    '4', '5', '6', '7', '8', '9', '-', '_',
932
  },
933
  .decmap = {
934
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */
935
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */
936
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */
937
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */
938
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */
939
    0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, /* 40-47 */
940
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */
941
    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */
942
    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */
943
    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */
944
    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */
945
    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f, /* 88-95 */
946
    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */
947
    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */
948
    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */
949
    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */
950
951
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */
952
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
953
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
954
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
955
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
956
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
957
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
958
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
959
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
960
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
961
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
962
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
963
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
964
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
965
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
966
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
967
  },
968
};