Coverage Report

Created: 2026-09-27 07:07

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
0
{
46
0
    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
0
    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
0
    if ((!url && 0 != (inlen % 4)) || (inlen % 4 == 1))
70
0
    {
71
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
72
0
        return false;
73
0
    }
74
75
    // rlen takes a best guess on size;
76
    // might be too large for base64url, but never too small.
77
0
    if (inlen > SIZE_MAX / 3)
78
0
    {
79
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
80
0
        return false;
81
0
    }
82
83
0
    size_t rlen = ((inlen * 3) >> 2) + 3;
84
0
    uint8_t *buffer = cjose_get_alloc()(sizeof(uint8_t) * rlen);
85
0
    if (NULL == buffer)
86
0
    {
87
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
88
0
        return false;
89
0
    }
90
91
0
    size_t idx = 0;
92
0
    size_t pos = 0;
93
0
    size_t shift = 0;
94
0
    uint32_t packed = 0;
95
0
    while (inlen > idx)
96
0
    {
97
0
        uint8_t val;
98
0
        val = input[idx];
99
0
        if ('=' == val)
100
0
        {
101
0
            break;
102
0
        }
103
0
        else if (url && ('+' == val || '/' == val))
104
0
        {
105
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
106
0
            goto b64_decode_failed;
107
0
        }
108
0
        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
0
        val = TEBAHPLA_B64[val];
115
0
        if (0xff == val)
116
0
        {
117
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
118
0
            cjose_get_dealloc()(buffer);
119
0
            return false;
120
0
        }
121
0
        idx++;
122
123
0
        packed = packed | (val << (18 - (6 * shift++)));
124
0
        if (4 == shift)
125
0
        {
126
0
            buffer[pos++] = (packed >> 16) & 0xff;
127
0
            buffer[pos++] = (packed >> 8) & 0xff;
128
0
            buffer[pos++] = packed & 0xff;
129
0
            shift = 0;
130
0
            packed = 0;
131
0
        }
132
0
    }
133
134
0
    if ((shift == 1) || (shift == 4))
135
0
    {
136
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
137
0
        goto b64_decode_failed;
138
0
    }
139
140
0
    if (shift == 3)
141
0
    {
142
0
        buffer[pos++] = (packed >> 16) & 0xff;
143
0
        buffer[pos++] = (packed >> 8) & 0xff;
144
0
    }
145
146
0
    if (shift == 2)
147
0
    {
148
0
        buffer[pos++] = (packed >> 16) & 0xff;
149
0
    }
150
151
    // validate before publishing the out-params: the failure path frees buffer,
152
    // which would otherwise leave *output dangling
153
0
    if (pos > rlen)
154
0
    {
155
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
156
0
        goto b64_decode_failed;
157
0
    }
158
159
0
    *output = buffer;
160
0
    *outlen = pos;
161
162
0
    return true;
163
164
0
b64_decode_failed:
165
0
    if (NULL != buffer)
166
0
    {
167
0
        cjose_get_dealloc()(buffer);
168
0
    }
169
0
    return false;
170
0
}
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
0
{
175
0
    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
0
    if (!inlen)
183
0
    {
184
0
        char *retVal = (char *)cjose_get_alloc()(sizeof(char));
185
0
        if (!retVal)
186
0
        {
187
0
            CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
188
0
            return false;
189
0
        }
190
0
        retVal[0] = '\0';
191
0
        *output = retVal;
192
0
        *outlen = 0;
193
0
        return true;
194
0
    }
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
0
    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
0
    const bool padit = (ALPHABET_B64 == alphabet);
205
0
    size_t rlen = (((inlen + 2) / 3) << 2);
206
0
    char *base;
207
208
0
    base = (char *)cjose_get_alloc()(sizeof(char) * (rlen + 1));
209
0
    if (NULL == base)
210
0
    {
211
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
212
0
        return false;
213
0
    }
214
215
0
    size_t pos = 0, idx = 0;
216
0
    while ((idx + 2) < inlen)
217
0
    {
218
0
        base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
219
0
        base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
220
0
        base[pos++] = alphabet[(0x3f & (input[idx + 1] << 2)) | (0x3f & (input[idx + 2] >> 6))];
221
0
        base[pos++] = alphabet[0x3f & input[idx + 2]];
222
0
        idx += 3;
223
0
    }
224
225
0
    if (idx < inlen)
226
0
    {
227
0
        if ((inlen - 1) == idx)
228
0
        {
229
0
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
230
0
            base[pos++] = alphabet[0x3f & (input[idx] << 4)];
231
0
            if (padit)
232
0
            {
233
0
                base[pos++] = '=';
234
0
                base[pos++] = '=';
235
0
            }
236
0
        }
237
0
        else
238
0
        {
239
0
            base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
240
0
            base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
241
0
            base[pos++] = alphabet[0x3f & (input[idx + 1] << 2)];
242
0
            if (padit)
243
0
            {
244
0
                base[pos++] = '=';
245
0
            }
246
0
        }
247
0
        rlen = pos;
248
0
    }
249
0
    base[rlen] = '\0';
250
251
0
    *output = base;
252
0
    *outlen = rlen;
253
0
    return true;
254
0
}
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
0
{
264
0
    return _cjose_base64_encode_impl(input, inlen, output, outlen, ALPHABET_B64U, err);
265
0
}
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
0
{
273
    return _cjose_base64_decode_impl(input, inlen, output, outlen, true, err);
274
0
}