Coverage Report

Created: 2026-09-01 06:14

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
40.8k
{
45
40.8k
    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
40.8k
    if (0 == inlen)
53
11.5k
    {
54
11.5k
        uint8_t *retVal = (uint8_t *)cjose_get_alloc()(sizeof(uint8_t));
55
11.5k
        if (NULL == retVal)
56
0
        {
57
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
58
0
            return false;
59
0
        }
60
61
11.5k
        retVal[0] = 0;
62
11.5k
        *output = retVal;
63
11.5k
        *outlen = 0;
64
11.5k
        return true;
65
11.5k
    }
66
67
    // extra validation -- inlen is a multiple of 4
68
29.3k
    if ((!url && 0 != (inlen % 4)) || (inlen % 4 == 1))
69
430
    {
70
430
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
71
430
        return false;
72
430
    }
73
74
    // rlen takes a best guess on size;
75
    // might be too large for base64url, but never too small.
76
28.8k
    if (inlen > SIZE_MAX / 3)
77
0
    {
78
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
79
0
        return false;
80
0
    }
81
82
28.8k
    size_t rlen = ((inlen * 3) >> 2) + 3;
83
28.8k
    uint8_t *buffer = cjose_get_alloc()(sizeof(uint8_t) * rlen);
84
28.8k
    if (NULL == buffer)
85
0
    {
86
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
87
0
        return false;
88
0
    }
89
90
28.8k
    size_t idx = 0;
91
28.8k
    size_t pos = 0;
92
28.8k
    size_t shift = 0;
93
28.8k
    uint32_t packed = 0;
94
2.25M
    while (inlen > idx)
95
2.22M
    {
96
2.22M
        uint8_t val;
97
2.22M
        val = input[idx];
98
2.22M
        if ('=' == val)
99
1.28k
        {
100
1.28k
            break;
101
1.28k
        }
102
2.22M
        else if (url && ('+' == val || '/' == val))
103
1.69k
        {
104
1.69k
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
105
1.69k
            goto b64_decode_failed;
106
1.69k
        }
107
2.22M
        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
2.22M
        val = TEBAHPLA_B64[val];
114
2.22M
        if (0xff == val)
115
279
        {
116
279
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
117
279
            cjose_get_dealloc()(buffer);
118
279
            return false;
119
279
        }
120
2.22M
        idx++;
121
122
2.22M
        packed = packed | (val << (18 - (6 * shift++)));
123
2.22M
        if (4 == shift)
124
541k
        {
125
541k
            buffer[pos++] = (packed >> 16) & 0xff;
126
541k
            buffer[pos++] = (packed >> 8) & 0xff;
127
541k
            buffer[pos++] = packed & 0xff;
128
541k
            shift = 0;
129
541k
            packed = 0;
130
541k
        }
131
2.22M
    }
132
133
26.8k
    if ((shift == 1) || (shift == 4))
134
211
    {
135
211
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
136
211
        goto b64_decode_failed;
137
211
    }
138
139
26.6k
    if (shift == 3)
140
14.2k
    {
141
14.2k
        buffer[pos++] = (packed >> 16) & 0xff;
142
14.2k
        buffer[pos++] = (packed >> 8) & 0xff;
143
14.2k
    }
144
145
26.6k
    if (shift == 2)
146
6.90k
    {
147
6.90k
        buffer[pos++] = (packed >> 16) & 0xff;
148
6.90k
    }
149
150
    // validate before publishing the out-params: the failure path frees buffer,
151
    // which would otherwise leave *output dangling
152
26.6k
    if (pos > rlen)
153
0
    {
154
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
155
0
        goto b64_decode_failed;
156
0
    }
157
158
26.6k
    *output = buffer;
159
26.6k
    *outlen = pos;
160
161
26.6k
    return true;
162
163
1.90k
b64_decode_failed:
164
1.90k
    if (NULL != buffer)
165
1.90k
    {
166
1.90k
        cjose_get_dealloc()(buffer);
167
1.90k
    }
168
1.90k
    return false;
169
26.6k
}
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
19.2k
{
173
19.2k
    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
19.2k
    if (!inlen)
181
3.84k
    {
182
3.84k
        char *retVal = (char *)cjose_get_alloc()(sizeof(char));
183
3.84k
        if (!retVal)
184
0
        {
185
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
186
0
            return false;
187
0
        }
188
3.84k
        retVal[0] = '\0';
189
3.84k
        *output = retVal;
190
3.84k
        *outlen = 0;
191
3.84k
        return true;
192
3.84k
    }
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
15.3k
    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
15.3k
    const bool padit = (ALPHABET_B64 == alphabet);
203
15.3k
    size_t rlen = (((inlen + 2) / 3) << 2);
204
15.3k
    char *base;
205
206
15.3k
    base = (char *)cjose_get_alloc()(sizeof(char) * (rlen + 1));
207
15.3k
    if (NULL == base)
208
0
    {
209
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
210
0
        return false;
211
0
    }
212
213
15.3k
    size_t pos = 0, idx = 0;
214
325k
    while ((idx + 2) < inlen)
215
309k
    {
216
309k
        base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
217
309k
        base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
218
309k
        base[pos++] = alphabet[(0x3f & (input[idx + 1] << 2)) | (0x3f & (input[idx + 2] >> 6))];
219
309k
        base[pos++] = alphabet[0x3f & input[idx + 2]];
220
309k
        idx += 3;
221
309k
    }
222
223
15.3k
    if (idx < inlen)
224
10.3k
    {
225
10.3k
        if ((inlen - 1) == idx)
226
5.13k
        {
227
5.13k
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
228
5.13k
            base[pos++] = alphabet[0x3f & (input[idx] << 4)];
229
5.13k
            if (padit)
230
0
            {
231
0
                base[pos++] = '=';
232
0
                base[pos++] = '=';
233
0
            }
234
5.13k
        }
235
5.21k
        else
236
5.21k
        {
237
5.21k
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
238
5.21k
            base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
239
5.21k
            base[pos++] = alphabet[0x3f & (input[idx + 1] << 2)];
240
5.21k
            if (padit)
241
0
            {
242
0
                base[pos++] = '=';
243
0
            }
244
5.21k
        }
245
10.3k
        rlen = pos;
246
10.3k
    }
247
15.3k
    base[rlen] = '\0';
248
249
15.3k
    *output = base;
250
15.3k
    *outlen = rlen;
251
15.3k
    return true;
252
15.3k
}
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
19.2k
{
262
19.2k
    return _encode(input, inlen, output, outlen, ALPHABET_B64U, err);
263
19.2k
}
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
40.8k
{
271
    return _decode(input, inlen, output, outlen, true, err);
272
40.8k
}