Coverage Report

Created: 2026-04-01 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/misc/base64-decode.c
Line
Count
Source
1
/*
2
 * This code originally came from here
3
 *
4
 * http://base64.sourceforge.net/b64.c
5
 *
6
 * already with MIT license, which is retained.
7
 *
8
 * LICENCE:        Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
9
 *
10
 *                Permission is hereby granted, free of charge, to any person
11
 *                obtaining a copy of this software and associated
12
 *                documentation files (the "Software"), to deal in the
13
 *                Software without restriction, including without limitation
14
 *                the rights to use, copy, modify, merge, publish, distribute,
15
 *                sublicense, and/or sell copies of the Software, and to
16
 *                permit persons to whom the Software is furnished to do so,
17
 *                subject to the following conditions:
18
 *
19
 *                The above copyright notice and this permission notice shall
20
 *                be included in all copies or substantial portions of the
21
 *                Software.
22
 *
23
 *                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
24
 *                KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
25
 *                WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
26
 *                PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
27
 *                OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
28
 *                OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
29
 *                OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
30
 *                SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
 *
32
 * VERSION HISTORY:
33
 *               Bob Trower 08/04/01 -- Create Version 0.00.00B
34
 *
35
 * I cleaned it up quite a bit to match the (linux kernel) style of the rest
36
 * of libwebsockets
37
 */
38
39
#include "private-lib-core.h"
40
41
#include <stdio.h>
42
#include <string.h>
43
44
static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
45
           "abcdefghijklmnopqrstuvwxyz0123456789+/";
46
static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
47
           "abcdefghijklmnopqrstuvwxyz0123456789-_";
48
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
49
           "$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
50
51
static int
52
_lws_b64_encode_string(const char *encode, const char *in, int in_len,
53
           char *out, int out_size)
54
0
{
55
0
  unsigned char triple[3];
56
0
  int i, done = 0;
57
58
0
  while (in_len) {
59
0
    int len = 0;
60
0
    for (i = 0; i < 3; i++) {
61
0
      if (in_len) {
62
0
        triple[i] = (unsigned char)*in++;
63
0
        len++;
64
0
        in_len--;
65
0
      } else
66
0
        triple[i] = 0;
67
0
    }
68
69
0
    if (done + 4 >= out_size)
70
0
      return -1;
71
72
0
    *out++ = encode[triple[0] >> 2];
73
0
    *out++ = encode[(((triple[0] & 0x03) << 4) & 0x30) |
74
0
               (((triple[1] & 0xf0) >> 4) & 0x0f)];
75
0
    *out++ = (char)(len > 1 ? encode[(((triple[1] & 0x0f) << 2) & 0x3c) |
76
0
          (((triple[2] & 0xc0) >> 6) & 3)] : '=');
77
0
    *out++ = (char)(len > 2 ? encode[triple[2] & 0x3f] : '=');
78
79
0
    done += 4;
80
0
  }
81
82
0
  if (done + 1 >= out_size)
83
0
    return -1;
84
85
0
  *out++ = '\0';
86
87
0
  return done;
88
0
}
89
90
int
91
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
92
0
{
93
0
  return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size);
94
0
}
95
96
int
97
lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size)
98
0
{
99
0
  return _lws_b64_encode_string(encode_url, in, in_len, out, out_size);
100
0
}
101
102
103
void
104
lws_b64_decode_state_init(struct lws_b64state *state)
105
0
{
106
0
  memset(state, 0, sizeof(*state));
107
0
}
108
109
int
110
lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len,
111
      uint8_t *out, size_t *out_size, int final)
112
0
{
113
0
  const char *orig_in = in, *end_in = in + *in_len;
114
0
  uint8_t *orig_out = out, *end_out = out + *out_size;
115
0
  int equals = 0;
116
117
0
  while (in < end_in && *in && out + 3 <= end_out) {
118
119
0
    for (; s->i < 4 && in < end_in && *in; s->i++) {
120
0
      uint8_t v;
121
122
0
      v = 0;
123
0
      s->c = 0;
124
0
      while (in < end_in && *in && !v) {
125
0
        s->c = v = (unsigned char)*in++;
126
127
0
        if (v == '\x0a' || v == '\x0d') {
128
0
          v = 0;
129
0
          continue;
130
0
        }
131
132
0
        if (v == '=') {
133
0
          equals++;
134
0
          v = 0;
135
0
          continue;
136
0
        }
137
138
        /* Sanity check this is part of the charset */
139
140
0
        if ((v < '0' || v > '9') &&
141
0
            (v < 'A' || v > 'Z') &&
142
0
            (v < 'a' || v > 'z') &&
143
0
            v != '-' && v != '+' && v != '_' && v != '/') {
144
0
          lwsl_err("%s: bad base64 0x%02X '%c' @+%d\n", __func__, v, v, lws_ptr_diff(in, orig_in));
145
0
          return -1;
146
0
        }
147
148
0
        if (equals) {
149
0
          lwsl_err("%s: non = after =\n", __func__);
150
0
          return -1;
151
0
        }
152
153
        /* support the url base64 variant too */
154
0
        if (v == '-')
155
0
          s->c = v = '+';
156
0
        if (v == '_')
157
0
          s->c = v = '/';
158
0
        v = (uint8_t)decode[v - 43];
159
0
        if (v)
160
0
          v = (uint8_t)((v == '$') ? 0 : v - 61);
161
0
      }
162
0
      if (s->c) {
163
0
        s->len++;
164
0
        if (v)
165
0
          s->quad[s->i] = (uint8_t)(v - 1);
166
0
      } else
167
0
        s->quad[s->i] = 0;
168
0
    }
169
170
0
    if (s->i != 4 && !final)
171
0
      continue;
172
173
0
    s->i = 0;
174
175
    /*
176
     * Normally we convert a group of 4 incoming symbols into 3 bytes.
177
     *
178
     * "The 'XX==' sequence indicates that the last group contained
179
     * only one byte, and 'XXX=' indicates that it contained two
180
     * bytes." (wikipedia)
181
     *
182
     */
183
184
0
    if (s->len >= 2)
185
0
      *out++ = (uint8_t)(s->quad[0] << 2 | s->quad[1] >> 4);
186
187
0
    if (s->len >= 3 && equals != 2)
188
0
      *out++ = (uint8_t)(s->quad[1] << 4 | s->quad[2] >> 2);
189
190
0
    if (s->len >= 4 && equals != 1)
191
0
      *out++ = (uint8_t)(((s->quad[2] << 6) & 0xc0) | s->quad[3]);
192
193
0
    s->done += s->len - 1;
194
0
    s->len = 0;
195
0
  }
196
197
0
  if (out < end_out)
198
0
    *out = '\0';
199
200
0
  *in_len = (unsigned int)(in - orig_in);
201
0
  *out_size = (unsigned int)(out - orig_out);
202
203
0
  return 0;
204
0
}
205
206
207
/*
208
 * returns length of decoded string in out, or -1 if out was too small
209
 * according to out_size
210
 *
211
 * Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until
212
 * the first NUL in the input.
213
 */
214
215
static size_t
216
_lws_b64_decode_string(const char *in, int in_len, char *out, size_t out_size)
217
0
{
218
0
  struct lws_b64state state;
219
0
  size_t il = (size_t)in_len, ol = out_size;
220
221
0
  if (in_len == -1) {
222
0
    il = strlen(in);
223
0
    in_len = (int)il;
224
0
  }
225
226
0
  lws_b64_decode_state_init(&state);
227
0
  if (lws_b64_decode_stateful(&state, in, &il, (uint8_t *)out, &ol, 1) < 0)
228
    /* pass on the failure */
229
0
    return 0;
230
231
0
  if ((int)il != in_len) {
232
0
    lwsl_err("%s: base64 must end at end of input\n", __func__);
233
0
    return 0;
234
0
  }
235
236
0
  return ol;
237
0
}
238
239
int
240
lws_b64_decode_string(const char *in, char *out, int out_size)
241
0
{
242
0
  return (int)_lws_b64_decode_string(in, -1, out, (unsigned int)out_size);
243
0
}
244
245
int
246
lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size)
247
0
{
248
0
  size_t s = _lws_b64_decode_string(in, in_len, out, (unsigned int)out_size);
249
250
0
  return !s ? -1 : (int)s;
251
0
}
252
253
static const char encode_b32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
254
255
int
256
lws_b32_encode_string(const char *in, int in_len, char *out, int out_size)
257
0
{
258
0
  unsigned char buf[5];
259
0
  int i, done = 0;
260
261
0
  while (in_len) {
262
0
    int len = 0;
263
0
    for (i = 0; i < 5; i++) {
264
0
      if (in_len) {
265
0
        buf[i] = (unsigned char)*in++;
266
0
        len++;
267
0
        in_len--;
268
0
      } else
269
0
        buf[i] = 0;
270
0
    }
271
272
0
    if (done + 8 >= out_size)
273
0
      return -1;
274
275
0
    out[0] = encode_b32[buf[0] >> 3];
276
0
    out[1] = encode_b32[((buf[0] & 0x07) << 2) | (buf[1] >> 6)];
277
0
    out[2] = len > 1 ? encode_b32[((buf[1] & 0x3e) >> 1)] : '=';
278
0
    out[3] = len > 1 ? encode_b32[((buf[1] & 0x01) << 4) | (buf[2] >> 4)] : '=';
279
0
    out[4] = len > 2 ? encode_b32[((buf[2] & 0x0f) << 1) | (buf[3] >> 7)] : '=';
280
0
    out[5] = len > 3 ? encode_b32[((buf[3] & 0x7c) >> 2)] : '=';
281
0
    out[6] = len > 3 ? encode_b32[((buf[3] & 0x03) << 3) | (buf[4] >> 5)] : '=';
282
0
    out[7] = len > 4 ? encode_b32[(buf[4] & 0x1f)] : '=';
283
284
0
    out += 8;
285
0
    done += 8;
286
0
  }
287
288
0
  if (done + 1 >= out_size)
289
0
    return -1;
290
291
0
  *out++ = '\0';
292
293
0
  return done;
294
0
}
295
296
static const int8_t decode_b32[256] = {
297
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
298
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
299
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
300
  -1,-1,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,
301
  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
302
  15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
303
  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
304
  15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
305
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
306
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
307
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
308
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
309
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
310
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
311
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
312
  -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
313
};
314
315
int
316
lws_b32_decode_string_len(const char *in, int in_len, char *out, int out_size)
317
0
{
318
0
  int done = 0;
319
0
  int buf[8] = {0};
320
0
  int i;
321
322
0
  if (in_len == -1)
323
0
    in_len = (int)strlen(in);
324
325
0
  while (in_len > 0) {
326
0
    int len = 0;
327
0
    for (i = 0; i < 8; i++) {
328
0
      while (in_len > 0 && (*in == ' ' || *in == '\t' || *in == '\n' || *in == '\r')) {
329
0
        in++;
330
0
        in_len--;
331
0
      }
332
0
      if (in_len > 0) {
333
0
        char c = *in++;
334
0
        in_len--;
335
0
        if (c == '=') {
336
0
          buf[i] = 0;
337
0
        } else {
338
0
          int8_t v = decode_b32[(unsigned char)c];
339
0
          if (v < 0) return -1;
340
0
          buf[i] = v;
341
0
          len++;
342
0
        }
343
0
      } else {
344
0
        buf[i] = 0;
345
0
      }
346
0
    }
347
348
0
    if (len == 0) break;
349
350
0
    if (done + 5 > out_size) return -1;
351
352
0
    out[0] = (char)((buf[0] << 3) | (buf[1] >> 2));
353
0
    out[1] = (char)(((buf[1] & 0x03) << 6) | (buf[2] << 1) | (buf[3] >> 4));
354
0
    out[2] = (char)(((buf[3] & 0x0f) << 4) | (buf[4] >> 1));
355
0
    out[3] = (char)(((buf[4] & 0x01) << 7) | (buf[5] << 2) | (buf[6] >> 3));
356
0
    out[4] = (char)(((buf[6] & 0x07) << 5) | buf[7]);
357
358
0
    if (len == 2) done += 1;
359
0
    else if (len == 4) done += 2;
360
0
    else if (len == 5) done += 3;
361
0
    else if (len == 7) done += 4;
362
0
    else if (len == 8) done += 5;
363
0
    else return -1; /* invalid base32 chunk */
364
365
0
    out += 5;
366
0
  }
367
368
0
  if (done < out_size)
369
0
    *out = '\0';
370
371
0
  return done;
372
0
}
373
374
int
375
lws_b32_decode_string(const char *in, char *out, int out_size)
376
0
{
377
0
  return lws_b32_decode_string_len(in, -1, out, out_size);
378
0
}
379
380
#if 0
381
static const char * const plaintext[] = {
382
  "any carnal pleasure.",
383
  "any carnal pleasure",
384
  "any carnal pleasur",
385
  "any carnal pleasu",
386
  "any carnal pleas",
387
  "Admin:kloikloi"
388
};
389
static const char * const coded[] = {
390
  "YW55IGNhcm5hbCBwbGVhc3VyZS4=",
391
  "YW55IGNhcm5hbCBwbGVhc3VyZQ==",
392
  "YW55IGNhcm5hbCBwbGVhc3Vy",
393
  "YW55IGNhcm5hbCBwbGVhc3U=",
394
  "YW55IGNhcm5hbCBwbGVhcw==",
395
  "QWRtaW46a2xvaWtsb2k="
396
};
397
398
int
399
lws_b64_selftest(void)
400
{
401
  char buf[64];
402
  unsigned int n,  r = 0;
403
  unsigned int test;
404
405
  lwsl_notice("%s\n", __func__);
406
407
  /* examples from https://en.wikipedia.org/wiki/Base64 */
408
409
  for (test = 0; test < (int)LWS_ARRAY_SIZE(plaintext); test++) {
410
411
    buf[sizeof(buf) - 1] = '\0';
412
    n = lws_b64_encode_string(plaintext[test],
413
              strlen(plaintext[test]), buf, sizeof buf);
414
    if (n != strlen(coded[test]) || strcmp(buf, coded[test])) {
415
      lwsl_err("Failed lws_b64 encode selftest "
416
             "%d result '%s' %d\n", test, buf, n);
417
      r = -1;
418
    }
419
420
    buf[sizeof(buf) - 1] = '\0';
421
    n = lws_b64_decode_string(coded[test], buf, sizeof buf);
422
    if (n != strlen(plaintext[test]) ||
423
        strcmp(buf, plaintext[test])) {
424
      lwsl_err("Failed lws_b64 decode selftest "
425
         "%d result '%s' / '%s', %d / %zu\n",
426
         test, buf, plaintext[test], n,
427
         strlen(plaintext[test]));
428
      lwsl_hexdump_err(buf, n);
429
      r = -1;
430
    }
431
  }
432
433
  if (!r)
434
    lwsl_notice("Base 64 selftests passed\n");
435
  else
436
    lwsl_notice("Base64 selftests failed\n");
437
438
  return r;
439
}
440
#endif