Line data Source code
1 : //
2 : // b64.c
3 : // cjose_cjson
4 : //
5 : // Created by Danny Goossen on 22/1/18.
6 : // Copyright (c) 2018 Danny Goossen. All rights reserved.
7 : //
8 :
9 : #include "b64.h"
10 : /*
11 : * Copyrights
12 : *
13 : * Portions created or assigned to Cisco Systems, Inc. are
14 : * Copyright (c) 2014-2016 Cisco Systems, Inc. All Rights Reserved.
15 : */
16 :
17 : #include <errno.h>
18 : #include <string.h>
19 : #include <stdlib.h>
20 : #include <assert.h>
21 : #include "utils.h"
22 : #include "jose_error.h"
23 :
24 : // defines
25 : #define B64_BYTE1(ptr) (((*ptr) & 0xfc) >> 2)
26 : #define B64_BYTE2(ptr) ((((*ptr) & 0x03) << 4) | ((*(ptr + 1) & 0xf0) >> 4))
27 : #define B64_BYTE3(ptr) (((*(ptr + 1) & 0x0f) << 2) | ((*(ptr + 2) & 0xc0) >> 6))
28 : #define B64_BYTE4(ptr) (*(ptr + 2) & 0x3f)
29 :
30 : // internal data
31 :
32 : static const char *ALPHABET_B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
33 : static const char *ALPHABET_B64U = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
34 :
35 : static const uint8_t TEBAHPLA_B64[]
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, 0x3e, 0xff, 0x3e, 0xff, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
39 : 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
40 : 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x1a, 0x1b, 0x1c,
41 : 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
42 : 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
43 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
46 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
47 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
49 :
50 : // internal functions
51 :
52 3 : static inline bool _decode(const char *input, size_t inlen, uint8_t **output, size_t *outlen, bool url, jose_err *err)
53 : {
54 3 : if ((NULL == input) || (NULL == output) || (NULL == outlen))
55 : {
56 0 : JOSE_ERROR(err, JOSE_ERR_INVALID_ARG);
57 : return false;
58 : }
59 :
60 : // return empty string on 0 length input
61 3 : if (0 == inlen)
62 : {
63 2 : uint8_t *retVal = calloc(1,sizeof(uint8_t));
64 2 : if (NULL == retVal)
65 : {
66 0 : JOSE_ERROR(err, JOSE_ERR_NO_MEMORY);
67 : return false;
68 : }
69 :
70 2 : retVal[0] = 0;
71 2 : *output = retVal;
72 2 : *outlen = 0;
73 2 : return true;
74 : }
75 :
76 : // extra validation -- inlen is a multiple of 4
77 1 : if ((!url && 0 != (inlen % 4)) || (inlen % 4 == 1))
78 : {
79 0 : JOSE_ERROR(err, JOSE_ERR_INVALID_ARG);
80 : return false;
81 : }
82 :
83 : // rlen takes a best guess on size;
84 : // might be too large for base64url, but never too small.
85 1 : size_t rlen = ((inlen * 3) >> 2) + 3 +1;
86 1 : uint8_t *buffer = calloc(sizeof(uint8_t), rlen);
87 1 : if (!buffer)
88 : {
89 0 : JOSE_ERROR(err, JOSE_ERR_NO_MEMORY);
90 : return false;
91 : }
92 :
93 : size_t idx = 0;
94 : size_t pos = 0;
95 : size_t shift = 0;
96 : uint32_t packed = 0;
97 39 : while (inlen > idx)
98 : {
99 : uint8_t val;
100 39 : val = input[idx];
101 39 : if ('=' == val)
102 : {
103 : break;
104 : }
105 38 : else if (url && ('+' == val || '/' == val))
106 : {
107 0 : JOSE_ERROR(err, JOSE_ERR_INVALID_ARG);
108 : goto b64_decode_failed;
109 : }
110 38 : else if (!url && ('-' == val || '_' == val))
111 : {
112 0 : JOSE_ERROR(err, JOSE_ERR_INVALID_ARG);
113 : goto b64_decode_failed;
114 : }
115 :
116 38 : val = TEBAHPLA_B64[val];
117 38 : if (0xff == val)
118 : {
119 0 : JOSE_ERROR(err, JOSE_ERR_INVALID_ARG);
120 0 : if (buffer) free(buffer);
121 : return false;
122 : }
123 38 : idx++;
124 :
125 38 : packed = packed | (val << (18 - (6 * shift++)));
126 38 : if (4 == shift)
127 : {
128 9 : buffer[pos++] = (packed >> 16) & 0xff;
129 9 : buffer[pos++] = (packed >> 8) & 0xff;
130 9 : buffer[pos++] = packed & 0xff;
131 9 : shift = 0;
132 9 : packed = 0;
133 : }
134 : }
135 :
136 1 : assert(shift != 1);
137 1 : assert(shift != 4);
138 :
139 1 : if (shift == 3)
140 : {
141 0 : buffer[pos++] = (packed >> 16) & 0xff;
142 0 : buffer[pos++] = (packed >> 8) & 0xff;
143 : }
144 :
145 1 : if (shift == 2)
146 : {
147 1 : buffer[pos++] = (packed >> 16) & 0xff;
148 : }
149 :
150 1 : *output = buffer;
151 1 : buffer[pos]='\0';
152 1 : *outlen = pos;
153 1 : assert(((*outlen) +1) <= rlen);
154 : return true;
155 :
156 : b64_decode_failed:
157 0 : if (buffer)
158 : {
159 0 : free(buffer);
160 : }
161 : return false;
162 : }
163 :
164 6 : static inline bool _encode(const uint8_t *input, size_t inlen, char **output, size_t *outlen, const char *alphabet, jose_err *err)
165 : {
166 6 : if ((inlen > 0 && NULL == input) || (NULL == output) || (NULL == outlen))
167 : {
168 0 : JOSE_ERROR(err, JOSE_ERR_INVALID_ARG);
169 : return false;
170 : }
171 :
172 : // return empty string on 0 length input
173 6 : if (!inlen)
174 : {
175 3 : char *retVal = calloc(1,1);
176 3 : if (!retVal)
177 : {
178 0 : JOSE_ERROR(err, JOSE_ERR_NO_MEMORY);
179 : return false;
180 : }
181 3 : retVal[0] = '\0';
182 3 : *output = retVal;
183 3 : *outlen = 0;
184 3 : return true;
185 : }
186 :
187 3 : const bool padit = (ALPHABET_B64 == alphabet);
188 3 : size_t rlen = (((inlen + 2) / 3) << 2);
189 : char *base;
190 :
191 3 : base = calloc(sizeof(char) , (rlen + 1));
192 3 : if (NULL == base)
193 : {
194 0 : JOSE_ERROR(err, JOSE_ERR_NO_MEMORY);
195 : return false;
196 : }
197 :
198 : size_t pos = 0, idx = 0;
199 30 : while ((idx + 2) < inlen)
200 : {
201 27 : base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
202 27 : base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
203 27 : base[pos++] = alphabet[(0x3f & (input[idx + 1] << 2)) | (0x3f & (input[idx + 2] >> 6))];
204 27 : base[pos++] = alphabet[0x3f & input[idx + 2]];
205 27 : idx += 3;
206 : }
207 :
208 3 : if (idx < inlen)
209 : {
210 3 : if ((inlen - 1) == idx)
211 : {
212 3 : base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
213 3 : base[pos++] = alphabet[0x3f & (input[idx] << 4)];
214 3 : if (padit)
215 : {
216 3 : base[pos++] = '=';
217 3 : base[pos++] = '=';
218 : }
219 : }
220 : else
221 : {
222 0 : base[pos++] = alphabet[0x3f & (input[idx] >> 2)];
223 0 : base[pos++] = alphabet[(0x3f & (input[idx] << 4)) | (0x3f & (input[idx + 1] >> 4))];
224 0 : base[pos++] = alphabet[0x3f & (input[idx + 1] << 2)];
225 0 : if (padit)
226 : {
227 0 : base[pos++] = '=';
228 : }
229 : }
230 3 : rlen = pos;
231 : }
232 3 : base[rlen] = '\0';
233 :
234 3 : *output = base;
235 3 : *outlen = rlen;
236 3 : return true;
237 : }
238 :
239 : // interface functions
240 :
241 3 : bool base64_encode(const uint8_t *input, size_t inlen, char **output, size_t *outlen, jose_err *err)
242 : {
243 6 : return _encode(input, inlen, output, outlen, ALPHABET_B64, err);
244 : }
245 0 : bool base64url_encode(const uint8_t *input, size_t inlen, char **output, size_t *outlen, jose_err *err)
246 : {
247 0 : return _encode(input, inlen, output, outlen, ALPHABET_B64U, err);
248 : }
249 :
250 3 : bool base64_decode(const char *input, size_t inlen, uint8_t **output, size_t *outlen, jose_err *err)
251 : {
252 3 : return _decode(input, inlen, output, outlen, false, err);
253 : }
254 0 : bool base64url_decode(const char *input, size_t inlen, uint8_t **output, size_t *outlen, jose_err *err)
255 : {
256 0 : return _decode(input, inlen, output, outlen, true, err);
257 : }
258 :
259 :
260 :
261 4 : char * Base64Encode_v(const char * prefix, const char * message, ...)
262 : {
263 4 : if (!message|| strlen(message)==0) return NULL;
264 :
265 3 : char * contents=CSPRINTF(message);
266 :
267 : //debug("base64 input (%d b): %s\n",strlen(contents),contents);
268 3 : char * result=NULL;
269 : size_t len_out;
270 3 : base64_encode((uint8_t *)contents,strlen(contents), &result, &len_out, NULL);
271 3 : if (contents) free(contents);
272 : //debug("base64 output (%d b): %s\n",strlen(result),result);
273 3 : size_t prefix_len=0;
274 3 : char *tmp=NULL;
275 3 : if (prefix)
276 : {
277 1 : prefix_len=strlen(prefix);
278 1 : tmp=calloc(1,len_out+prefix_len+1);
279 1 : sprintf(tmp,"%s%.*s",prefix,(int)(len_out),result);
280 1 : if (result) free(result);
281 : }
282 2 : else tmp=result;
283 3 : return (tmp);
284 : }
285 :
286 :
|