aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/b64url.c
blob: 4d08947e8446426eb609d141ded661e5b09dfdf1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

/*
 * Base64url (RFC 4648 section 5) decoding function. Since SLUL explicitly
 * does NOT use padding, the variant without padding is used (see section 3.2).
 *
 * Copyright © 2021-2026 Samuel Lidén Borell <samuel@kodafritt.se>
 *
 * SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later
 */

#include "compiler.h"
#include <assert.h>
#include <string.h>

#define X 0xFF
static const unsigned char unb64url[128-32] = {
    /* 20-2F: special characters */
     X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X, 62,  X,  X,
    /* 30-3F: 01234... */
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  X,  X,  X,  X,  X,  X,
    /* 40-4F: @ABCD... */
     X,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    /* 50-5F: PQRST... */
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  X,  X,  X,  X, 63,
    /* 60-6F: `abcd... */
     X, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    /* 70-7F: pqrst... */
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  X,  X,  X,  X,  X
};
#undef X

#define GETCHAR \
    c = (unsigned char)*(in++); \
    if (c < 32 || c > 127) return 0; \
    c = unb64url[c-32]; \
    if (c > 63) return 0;

bool unbase64url(const char *in, size_t inlen,
                 unsigned char *out, size_t outlen)
{
    SlulInt v;
    unsigned char c;
    for (;;) {
        if (inlen < 4) goto end_chunk;
        if (outlen < 3) return 0;

        GETCHAR
        v = (SlulInt)c << 18;
        GETCHAR
        v |= (SlulInt)c << 12;
        GETCHAR
        v |= (SlulInt)c << 6;
        GETCHAR
        v |= (SlulInt)c;

        *(out++) = (v >> 16) & 0xFF;
        *(out++) = (v >> 8) & 0xFF;
        *(out++) = v & 0xFF;
        inlen -= 4;
        outlen -= 3;
    }
  end_chunk:
    if (!inlen) return true;
    /* We can have 2 or 3 trailing characters */
    if (inlen == 1) return false;
    GETCHAR
    v = (SlulInt)c << 18;
    GETCHAR
    v |= (SlulInt)c << 12;
    if (inlen >= 3) {
        GETCHAR
        v |= (SlulInt)c << 6;
        if (outlen < 2) return false;
    } else if (outlen < 1) return false;

    *(out++) = (v >> 16) & 0xFF;
    /* Check that there are no extranous bits */
    v &= ~0xFF0000U;
    if (inlen >= 3) {
        *(out++) = (v >> 8) & 0xFF;
        v &= ~0xFF00U;
    }
    return !v;
}