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