Coverage Report

Created: 2026-11-12 07:05

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
44
_cjose_base64_decode_impl(const char *input, size_t inlen, uint8_t **output, size_t *outlen, bool url, cjose_err *err)
45
16.3k
{
46
16.3k
    if ((NULL == input) || (NULL == output) || (NULL == outlen))
47
0
    {
48
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
49
0
        return false;
50
0
    }
51
52
    // return empty string on 0 length input
53
16.3k
    if (0 == inlen)
54
0
    {
55
0
        uint8_t *retVal = (uint8_t *)cjose_get_alloc()(sizeof(uint8_t));
56
0
        if (NULL == retVal)
57
0
        {
58
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
59
0
            return false;
60
0
        }
61
62
0
        retVal[0] = 0;
63
0
        *output = retVal;
64
0
        *outlen = 0;
65
0
        return true;
66
0
    }
67
68
    // extra validation -- inlen is a multiple of 4
69
16.3k
    if ((!url && 0 != (inlen % 4)) || (inlen % 4 == 1))
70
16
    {
71
16
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
72
16
        return false;
73
16
    }
74
75
    // rlen takes a best guess on size;
76
    // might be too large for base64url, but never too small.
77
16.3k
    if (inlen > SIZE_MAX / 3)
78
0
    {
79
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
80
0
        return false;
81
0
    }
82
83
16.3k
    size_t rlen = ((inlen * 3) >> 2) + 3;
84
16.3k
    uint8_t *buffer = cjose_get_alloc()(sizeof(uint8_t) * rlen);
85
16.3k
    if (NULL == buffer)
86
0
    {
87
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
88
0
        return false;
89
0
    }
90
91
16.3k
    size_t idx = 0;
92
16.3k
    size_t pos = 0;
93
16.3k
    size_t shift = 0;
94
16.3k
    uint32_t packed = 0;
95
24.1M
    while (inlen > idx)
96
24.1M
    {
97
24.1M
        uint8_t val;
98
24.1M
        val = input[idx];
99
24.1M
        if ('=' == val)
100
912
        {
101
912
            break;
102
912
        }
103
24.1M
        else if (url && ('+' == val || '/' == val))
104
2
        {
105
2
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
106
2
            goto b64_decode_failed;
107
2
        }
108
24.1M
        else if (!url && ('-' == val || '_' == val))
109
0
        {
110
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
111
0
            goto b64_decode_failed;
112
0
        }
113
114
24.1M
        val = TEBAHPLA_B64[val];
115
24.1M
        if (0xff == val)
116
18
        {
117
18
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
118
18
            cjose_get_dealloc()(buffer);
119
18
            return false;
120
18
        }
121
24.1M
        idx++;
122
123
24.1M
        packed = packed | (val << (18 - (6 * shift++)));
124
24.1M
        if (4 == shift)
125
6.02M
        {
126
6.02M
            buffer[pos++] = (packed >> 16) & 0xff;
127
6.02M
            buffer[pos++] = (packed >> 8) & 0xff;
128
6.02M
            buffer[pos++] = packed & 0xff;
129
6.02M
            shift = 0;
130
6.02M
            packed = 0;
131
6.02M
        }
132
24.1M
    }
133
134
16.3k
    if ((shift == 1) || (shift == 4))
135
4
    {
136
4
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
137
4
        goto b64_decode_failed;
138
4
    }
139
140
16.2k
    if (shift == 3)
141
6.01k
    {
142
6.01k
        buffer[pos++] = (packed >> 16) & 0xff;
143
6.01k
        buffer[pos++] = (packed >> 8) & 0xff;
144
6.01k
    }
145
146
16.2k
    if (shift == 2)
147
8.15k
    {
148
8.15k
        buffer[pos++] = (packed >> 16) & 0xff;
149
8.15k
    }
150
151
    // validate before publishing the out-params: the failure path frees buffer,
152
    // which would otherwise leave *output dangling
153
16.2k
    if (pos > rlen)
154
0
    {
155
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
156
0
        goto b64_decode_failed;
157
0
    }
158
159
16.2k
    *output = buffer;
160
16.2k
    *outlen = pos;
161
162
16.2k
    return true;
163
164
6
b64_decode_failed:
165
6
    if (NULL != buffer)
166
6
    {
167
6
        cjose_get_dealloc()(buffer);
168
6
    }
169
6
    return false;
170
16.2k
}
171
172
static inline bool
173
_cjose_base64_encode_impl(const uint8_t *input, size_t inlen, char **output, size_t *outlen, const char *alphabet, cjose_err *err)
174
18.7k
{
175
18.7k
    if ((inlen > 0 && NULL == input) || (NULL == output) || (NULL == outlen))
176
0
    {
177
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
178
0
        return false;
179
0
    }
180
181
    // return empty string on 0 length input
182
18.7k
    if (!inlen)
183
954
    {
184
954
        char *retVal = (char *)cjose_get_alloc()(sizeof(char));
185
954
        if (!retVal)
186
0
        {
187
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
188
0
            return false;
189
0
        }
190
954
        retVal[0] = '\0';
191
954
        *output = retVal;
192
954
        *outlen = 0;
193
954
        return true;
194
954
    }
195
196
    // guard the ~4/3 size expansion (the +2, the <<2, and the +1 below) against
197
    // size_t overflow, mirroring the SIZE_MAX/3 guard on the decode side
198
17.8k
    if (inlen > (SIZE_MAX - 4) / 4 * 3)
199
0
    {
200
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
201
0
        return false;
202
0
    }
203
204
17.8k
    const bool padit = (ALPHABET_B64 == alphabet);
205
17.8k
    size_t rlen = (((inlen + 2) / 3) << 2);
206
17.8k
    char *base;
207
208
17.8k
    base = (char *)cjose_get_alloc()(sizeof(char) * (rlen + 1));
209
17.8k
    if (NULL == base)
210
0
    {
211
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
212
0
        return false;
213
0
    }
214
215
17.8k
    size_t pos = 0, idx = 0;
216
4.77M
    while ((idx + 2) < inlen)
217
4.75M
    {
218
4.75M
        base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
219
4.75M
        base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
220
4.75M
        base[pos++] = alphabet[(0x3f & (input[idx + 1] << 2)) | (0x3f & (input[idx + 2] >> 6))];
221
4.75M
        base[pos++] = alphabet[0x3f & input[idx + 2]];
222
4.75M
        idx += 3;
223
4.75M
    }
224
225
17.8k
    if (idx < inlen)
226
12.3k
    {
227
12.3k
        if ((inlen - 1) == idx)
228
5.50k
        {
229
5.50k
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
230
5.50k
            base[pos++] = alphabet[0x3f & (input[idx] << 4)];
231
5.50k
            if (padit)
232
0
            {
233
0
                base[pos++] = '=';
234
0
                base[pos++] = '=';
235
0
            }
236
5.50k
        }
237
6.84k
        else
238
6.84k
        {
239
6.84k
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
240
6.84k
            base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
241
6.84k
            base[pos++] = alphabet[0x3f & (input[idx + 1] << 2)];
242
6.84k
            if (padit)
243
0
            {
244
0
                base[pos++] = '=';
245
0
            }
246
6.84k
        }
247
12.3k
        rlen = pos;
248
12.3k
    }
249
17.8k
    base[rlen] = '\0';
250
251
17.8k
    *output = base;
252
17.8k
    *outlen = rlen;
253
17.8k
    return true;
254
17.8k
}
255
256
// interface functions
257
258
bool cjose_base64_encode(const uint8_t *input, size_t inlen, char **output, size_t *outlen, cjose_err *err)
259
0
{
260
0
    return _cjose_base64_encode_impl(input, inlen, output, outlen, ALPHABET_B64, err);
261
0
}
262
bool cjose_base64url_encode(const uint8_t *input, size_t inlen, char **output, size_t *outlen, cjose_err *err)
263
18.7k
{
264
18.7k
    return _cjose_base64_encode_impl(input, inlen, output, outlen, ALPHABET_B64U, err);
265
18.7k
}
266
267
bool cjose_base64_decode(const char *input, size_t inlen, uint8_t **output, size_t *outlen, cjose_err *err)
268
0
{
269
0
    return _cjose_base64_decode_impl(input, inlen, output, outlen, false, err);
270
0
}
271
bool cjose_base64url_decode(const char *input, size_t inlen, uint8_t **output, size_t *outlen, cjose_err *err)
272
16.3k
{
273
    return _cjose_base64_decode_impl(input, inlen, output, outlen, true, err);
274
16.3k
}