Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cjose/src/base64.c
Line
Count
Source
1
/*
2
 * Copyrights
3
 *
4
 * Portions created or assigned to Cisco Systems, Inc. are
5
 * Copyright (c) 2014-2016 Cisco Systems, Inc.  All Rights Reserved.
6
 */
7
8
#include <cjose/base64.h>
9
#include <cjose/util.h>
10
11
#include <errno.h>
12
#include <string.h>
13
#include <stdlib.h>
14
15
// defines
16
#define B64_BYTE1(ptr) (((*ptr) & 0xfc) >> 2)
17
#define B64_BYTE2(ptr) ((((*ptr) & 0x03) << 4) | ((*(ptr + 1) & 0xf0) >> 4))
18
#define B64_BYTE3(ptr) (((*(ptr + 1) & 0x0f) << 2) | ((*(ptr + 2) & 0xc0) >> 6))
19
#define B64_BYTE4(ptr) (*(ptr + 2) & 0x3f)
20
21
// internal data
22
23
static const char *ALPHABET_B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24
static const char *ALPHABET_B64U = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
25
26
static const uint8_t TEBAHPLA_B64[]
27
    = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
28
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
29
        0xff, 0xff, 0xff, 0x3e, 0xff, 0x3e, 0xff, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
30
        0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
31
        0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x1a, 0x1b, 0x1c,
32
        0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
33
        0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
34
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
35
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
36
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
37
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
38
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
39
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
40
41
// internal functions
42
43
static inline bool _decode(const char *input, size_t inlen, uint8_t **output, size_t *outlen, bool url, cjose_err *err)
44
28.0k
{
45
28.0k
    if ((NULL == input) || (NULL == output) || (NULL == outlen))
46
0
    {
47
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
48
0
        return false;
49
0
    }
50
51
    // return empty string on 0 length input
52
28.0k
    if (0 == inlen)
53
11.4k
    {
54
11.4k
        uint8_t *retVal = (uint8_t *)cjose_get_alloc()(sizeof(uint8_t));
55
11.4k
        if (NULL == retVal)
56
0
        {
57
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
58
0
            return false;
59
0
        }
60
61
11.4k
        retVal[0] = 0;
62
11.4k
        *output = retVal;
63
11.4k
        *outlen = 0;
64
11.4k
        return true;
65
11.4k
    }
66
67
    // extra validation -- inlen is a multiple of 4
68
16.5k
    if ((!url && 0 != (inlen % 4)) || (inlen % 4 == 1))
69
62
    {
70
62
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
71
62
        return false;
72
62
    }
73
74
    // rlen takes a best guess on size;
75
    // might be too large for base64url, but never too small.
76
16.5k
    if (inlen > SIZE_MAX / 3)
77
0
    {
78
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
79
0
        return false;
80
0
    }
81
82
16.5k
    size_t rlen = ((inlen * 3) >> 2) + 3;
83
16.5k
    uint8_t *buffer = cjose_get_alloc()(sizeof(uint8_t) * rlen);
84
16.5k
    if (NULL == buffer)
85
0
    {
86
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
87
0
        return false;
88
0
    }
89
90
16.5k
    size_t idx = 0;
91
16.5k
    size_t pos = 0;
92
16.5k
    size_t shift = 0;
93
16.5k
    uint32_t packed = 0;
94
61.8M
    while (inlen > idx)
95
61.7M
    {
96
61.7M
        uint8_t val;
97
61.7M
        val = input[idx];
98
61.7M
        if ('=' == val)
99
554
        {
100
554
            break;
101
554
        }
102
61.7M
        else if (url && ('+' == val || '/' == val))
103
9
        {
104
9
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
105
9
            goto b64_decode_failed;
106
9
        }
107
61.7M
        else if (!url && ('-' == val || '_' == val))
108
0
        {
109
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
110
0
            goto b64_decode_failed;
111
0
        }
112
113
61.7M
        val = TEBAHPLA_B64[val];
114
61.7M
        if (0xff == val)
115
77
        {
116
77
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
117
77
            cjose_get_dealloc()(buffer);
118
77
            return false;
119
77
        }
120
61.7M
        idx++;
121
122
61.7M
        packed = packed | (val << (18 - (6 * shift++)));
123
61.7M
        if (4 == shift)
124
15.4M
        {
125
15.4M
            buffer[pos++] = (packed >> 16) & 0xff;
126
15.4M
            buffer[pos++] = (packed >> 8) & 0xff;
127
15.4M
            buffer[pos++] = packed & 0xff;
128
15.4M
            shift = 0;
129
15.4M
            packed = 0;
130
15.4M
        }
131
61.7M
    }
132
133
16.4k
    if ((shift == 1) || (shift == 4))
134
7
    {
135
7
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
136
7
        goto b64_decode_failed;
137
7
    }
138
139
16.4k
    if (shift == 3)
140
7.66k
    {
141
7.66k
        buffer[pos++] = (packed >> 16) & 0xff;
142
7.66k
        buffer[pos++] = (packed >> 8) & 0xff;
143
7.66k
    }
144
145
16.4k
    if (shift == 2)
146
4.63k
    {
147
4.63k
        buffer[pos++] = (packed >> 16) & 0xff;
148
4.63k
    }
149
150
    // validate before publishing the out-params: the failure path frees buffer,
151
    // which would otherwise leave *output dangling
152
16.4k
    if (pos > rlen)
153
0
    {
154
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
155
0
        goto b64_decode_failed;
156
0
    }
157
158
16.4k
    *output = buffer;
159
16.4k
    *outlen = pos;
160
161
16.4k
    return true;
162
163
16
b64_decode_failed:
164
16
    if (NULL != buffer)
165
16
    {
166
16
        cjose_get_dealloc()(buffer);
167
16
    }
168
16
    return false;
169
16.4k
}
170
171
static inline bool _encode(const uint8_t *input, size_t inlen, char **output, size_t *outlen, const char *alphabet, cjose_err *err)
172
87
{
173
87
    if ((inlen > 0 && NULL == input) || (NULL == output) || (NULL == outlen))
174
0
    {
175
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
176
0
        return false;
177
0
    }
178
179
    // return empty string on 0 length input
180
87
    if (!inlen)
181
0
    {
182
0
        char *retVal = (char *)cjose_get_alloc()(sizeof(char));
183
0
        if (!retVal)
184
0
        {
185
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
186
0
            return false;
187
0
        }
188
0
        retVal[0] = '\0';
189
0
        *output = retVal;
190
0
        *outlen = 0;
191
0
        return true;
192
0
    }
193
194
    // guard the ~4/3 size expansion (the +2, the <<2, and the +1 below) against
195
    // size_t overflow, mirroring the SIZE_MAX/3 guard on the decode side
196
87
    if (inlen > (SIZE_MAX - 4) / 4 * 3)
197
0
    {
198
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
199
0
        return false;
200
0
    }
201
202
87
    const bool padit = (ALPHABET_B64 == alphabet);
203
87
    size_t rlen = (((inlen + 2) / 3) << 2);
204
87
    char *base;
205
206
87
    base = (char *)cjose_get_alloc()(sizeof(char) * (rlen + 1));
207
87
    if (NULL == base)
208
0
    {
209
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
210
0
        return false;
211
0
    }
212
213
87
    size_t pos = 0, idx = 0;
214
1.15M
    while ((idx + 2) < inlen)
215
1.15M
    {
216
1.15M
        base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
217
1.15M
        base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
218
1.15M
        base[pos++] = alphabet[(0x3f & (input[idx + 1] << 2)) | (0x3f & (input[idx + 2] >> 6))];
219
1.15M
        base[pos++] = alphabet[0x3f & input[idx + 2]];
220
1.15M
        idx += 3;
221
1.15M
    }
222
223
87
    if (idx < inlen)
224
70
    {
225
70
        if ((inlen - 1) == idx)
226
30
        {
227
30
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
228
30
            base[pos++] = alphabet[0x3f & (input[idx] << 4)];
229
30
            if (padit)
230
0
            {
231
0
                base[pos++] = '=';
232
0
                base[pos++] = '=';
233
0
            }
234
30
        }
235
40
        else
236
40
        {
237
40
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
238
40
            base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
239
40
            base[pos++] = alphabet[0x3f & (input[idx + 1] << 2)];
240
40
            if (padit)
241
0
            {
242
0
                base[pos++] = '=';
243
0
            }
244
40
        }
245
70
        rlen = pos;
246
70
    }
247
87
    base[rlen] = '\0';
248
249
87
    *output = base;
250
87
    *outlen = rlen;
251
87
    return true;
252
87
}
253
254
// interface functions
255
256
bool cjose_base64_encode(const uint8_t *input, const size_t inlen, char **output, size_t *outlen, cjose_err *err)
257
0
{
258
0
    return _encode(input, inlen, output, outlen, ALPHABET_B64, err);
259
0
}
260
bool cjose_base64url_encode(const uint8_t *input, const size_t inlen, char **output, size_t *outlen, cjose_err *err)
261
87
{
262
87
    return _encode(input, inlen, output, outlen, ALPHABET_B64U, err);
263
87
}
264
265
bool cjose_base64_decode(const char *input, const size_t inlen, uint8_t **output, size_t *outlen, cjose_err *err)
266
0
{
267
0
    return _decode(input, inlen, output, outlen, false, err);
268
0
}
269
bool cjose_base64url_decode(const char *input, const size_t inlen, uint8_t **output, size_t *outlen, cjose_err *err)
270
28.0k
{
271
    return _decode(input, inlen, output, outlen, true, err);
272
28.0k
}