/src/h2o/deps/picotls/lib/pembase64.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2016 Christian Huitema <huitema@huitema.net> |
3 | | * |
4 | | * Permission to use, copy, modify, and distribute this software for any |
5 | | * purpose with or without fee is hereby granted, provided that the above |
6 | | * copyright notice and this permission notice appear in all copies. |
7 | | * |
8 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * Manage Base64 encoding. |
19 | | */ |
20 | | #ifdef _WINDOWS |
21 | | #include "wincompat.h" |
22 | | #else |
23 | | #include <sys/time.h> |
24 | | #endif |
25 | | #include <errno.h> |
26 | | #include <stdlib.h> |
27 | | #include <string.h> |
28 | | #include <stdio.h> |
29 | | #include "picotls.h" |
30 | | #include "picotls/pembase64.h" |
31 | | |
32 | | static char ptls_base64_alphabet[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', |
33 | | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', |
34 | | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', |
35 | | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; |
36 | | |
37 | | static signed char ptls_base64_values[] = { |
38 | | /* 0x00 to 0x0F */ |
39 | | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
40 | | /* 0x10 to 0x1F */ |
41 | | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
42 | | /* 0x20 to 0x2F. '+' at 2B, '/' at 2F */ |
43 | | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, |
44 | | /* 0x30 to 0x3F -- digits 0 to 9 at 0x30 to 0x39*/ |
45 | | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, |
46 | | /* 0x40 to 0x4F -- chars 'A' to 'O' at 0x41 to 0x4F */ |
47 | | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
48 | | /* 0x50 to 0x5F -- chars 'P' to 'Z' at 0x50 to 0x5A */ |
49 | | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, |
50 | | /* 0x60 to 0x6F -- chars 'a' to 'o' at 0x61 to 0x6F */ |
51 | | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
52 | | /* 0x70 to 0x7F -- chars 'p' to 'z' at 0x70 to 0x7A */ |
53 | | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; |
54 | | |
55 | | static void ptls_base64_cell(const uint8_t *data, char *text) |
56 | 0 | { |
57 | 0 | int n[4]; |
58 | |
|
59 | 0 | n[0] = data[0] >> 2; |
60 | 0 | n[1] = ((data[0] & 3) << 4) | (data[1] >> 4); |
61 | 0 | n[2] = ((data[1] & 15) << 2) | (data[2] >> 6); |
62 | 0 | n[3] = data[2] & 63; |
63 | |
|
64 | 0 | for (int i = 0; i < 4; i++) { |
65 | 0 | text[i] = ptls_base64_alphabet[n[i]]; |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | size_t ptls_base64_howlong(size_t data_length) |
70 | 0 | { |
71 | 0 | return (((data_length + 2) / 3) * 4); |
72 | 0 | } |
73 | | |
74 | | int ptls_base64_encode(const uint8_t *data, size_t data_len, char *ptls_base64_text) |
75 | 0 | { |
76 | 0 | int l = 0; |
77 | 0 | int lt = 0; |
78 | |
|
79 | 0 | while ((data_len - l) >= 3) { |
80 | 0 | ptls_base64_cell(data + l, ptls_base64_text + lt); |
81 | 0 | l += 3; |
82 | 0 | lt += 4; |
83 | 0 | } |
84 | |
|
85 | 0 | switch (data_len - l) { |
86 | 0 | case 0: |
87 | 0 | break; |
88 | 0 | case 1: |
89 | 0 | ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2]; |
90 | 0 | ptls_base64_text[lt++] = ptls_base64_alphabet[(data[l] & 3) << 4]; |
91 | 0 | ptls_base64_text[lt++] = '='; |
92 | 0 | ptls_base64_text[lt++] = '='; |
93 | 0 | break; |
94 | 0 | case 2: |
95 | 0 | ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2]; |
96 | 0 | ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l] & 3) << 4) | (data[l + 1] >> 4)]; |
97 | 0 | ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l + 1] & 15) << 2)]; |
98 | 0 | ptls_base64_text[lt++] = '='; |
99 | 0 | break; |
100 | 0 | default: |
101 | 0 | break; |
102 | 0 | } |
103 | 0 | ptls_base64_text[lt++] = 0; |
104 | |
|
105 | 0 | return lt; |
106 | 0 | } |
107 | | |
108 | | /* |
109 | | * Take into input a line of text, so as to work by increments. |
110 | | * The intermediate text of the decoding is kept in a state variable. |
111 | | * The decoded data is accumulated in a PTLS buffer. |
112 | | * The parsing is consistent with the lax definition in RFC 7468 |
113 | | */ |
114 | | |
115 | | void ptls_base64_decode_init(ptls_base64_decode_state_t *state) |
116 | 0 | { |
117 | 0 | state->nbc = 0; |
118 | 0 | state->nbo = 3; |
119 | 0 | state->v = 0; |
120 | 0 | state->status = PTLS_BASE64_DECODE_IN_PROGRESS; |
121 | 0 | } |
122 | | |
123 | | int ptls_base64_decode(const char *text, ptls_base64_decode_state_t *state, ptls_buffer_t *buf) |
124 | 0 | { |
125 | 0 | int ret = 0; |
126 | 0 | uint8_t decoded[3]; |
127 | 0 | size_t text_index = 0; |
128 | 0 | int c; |
129 | 0 | signed char vc; |
130 | | |
131 | | /* skip initial blanks */ |
132 | 0 | while (text[text_index] != 0) { |
133 | 0 | c = text[text_index]; |
134 | |
|
135 | 0 | if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { |
136 | 0 | text_index++; |
137 | 0 | } else { |
138 | 0 | break; |
139 | 0 | } |
140 | 0 | } |
141 | |
|
142 | 0 | while (text[text_index] != 0 && ret == 0 && state->status == PTLS_BASE64_DECODE_IN_PROGRESS) { |
143 | 0 | c = text[text_index++]; |
144 | |
|
145 | 0 | vc = 0 < c && c < 0x7f ? ptls_base64_values[c] : -1; |
146 | 0 | if (vc == -1) { |
147 | 0 | if (state->nbc == 2 && c == '=' && text[text_index] == '=') { |
148 | 0 | state->nbc = 4; |
149 | 0 | text_index++; |
150 | 0 | state->nbo = 1; |
151 | 0 | state->v <<= 12; |
152 | 0 | } else if (state->nbc == 3 && c == '=') { |
153 | 0 | state->nbc = 4; |
154 | 0 | state->nbo = 2; |
155 | 0 | state->v <<= 6; |
156 | 0 | } else { |
157 | | /* Skip final blanks */ |
158 | 0 | for (--text_index; text[text_index] != 0; ++text_index) { |
159 | 0 | c = text[text_index]; |
160 | 0 | if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C)) |
161 | 0 | break; |
162 | 0 | } |
163 | | |
164 | | /* Should now be at end of buffer */ |
165 | 0 | if (text[text_index] == 0) { |
166 | 0 | break; |
167 | 0 | } else { |
168 | | /* Not at end of buffer, signal a decoding error */ |
169 | 0 | state->nbo = 0; |
170 | 0 | state->status = PTLS_BASE64_DECODE_FAILED; |
171 | 0 | ret = PTLS_ERROR_INCORRECT_BASE64; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | } else { |
175 | 0 | state->nbc++; |
176 | 0 | state->v <<= 6; |
177 | 0 | state->v |= vc; |
178 | 0 | } |
179 | | |
180 | 0 | if (ret == 0 && state->nbc == 4) { |
181 | | /* Convert to up to 3 octets */ |
182 | 0 | for (int j = 0; j < state->nbo; j++) { |
183 | 0 | decoded[j] = (uint8_t)(state->v >> (8 * (2 - j))); |
184 | 0 | } |
185 | |
|
186 | 0 | ret = ptls_buffer__do_pushv(buf, decoded, state->nbo); |
187 | |
|
188 | 0 | if (ret == 0) { |
189 | | /* test for fin or continuation */ |
190 | 0 | if (state->nbo < 3) { |
191 | | /* Check that there are only trainling blanks on this line */ |
192 | 0 | while (text[text_index] != 0) { |
193 | 0 | c = text[text_index++]; |
194 | |
|
195 | 0 | if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C) { |
196 | 0 | continue; |
197 | 0 | } |
198 | 0 | } |
199 | 0 | if (text[text_index] == 0) { |
200 | 0 | state->status = PTLS_BASE64_DECODE_DONE; |
201 | 0 | } else { |
202 | 0 | state->status = PTLS_BASE64_DECODE_FAILED; |
203 | 0 | ret = PTLS_ERROR_INCORRECT_BASE64; |
204 | 0 | } |
205 | 0 | break; |
206 | 0 | } else { |
207 | 0 | state->v = 0; |
208 | 0 | state->nbo = 3; |
209 | 0 | state->nbc = 0; |
210 | 0 | } |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | return ret; |
215 | 0 | } |
216 | | |
217 | | /* |
218 | | * Reading a PEM file, to get an object: |
219 | | * |
220 | | * - Find first object, get the object name. |
221 | | * - If object label is what the application expects, parse, else skip to end. |
222 | | * |
223 | | * The following labels are defined in RFC 7468: |
224 | | * |
225 | | * Sec. Label ASN.1 Type Reference Module |
226 | | * ----+----------------------+-----------------------+---------+---------- |
227 | | * 5 CERTIFICATE Certificate [RFC5280] id-pkix1-e |
228 | | * 6 X509 CRL CertificateList [RFC5280] id-pkix1-e |
229 | | * 7 CERTIFICATE REQUEST CertificationRequest [RFC2986] id-pkcs10 |
230 | | * 8 PKCS7 ContentInfo [RFC2315] id-pkcs7* |
231 | | * 9 CMS ContentInfo [RFC5652] id-cms2004 |
232 | | * 10 PRIVATE KEY PrivateKeyInfo ::= [RFC5208] id-pkcs8 |
233 | | * OneAsymmetricKey [RFC5958] id-aKPV1 |
234 | | * 11 ENCRYPTED PRIVATE KEY EncryptedPrivateKeyInfo [RFC5958] id-aKPV1 |
235 | | * 12 ATTRIBUTE CERTIFICATE AttributeCertificate [RFC5755] id-acv2 |
236 | | * 13 PUBLIC KEY SubjectPublicKeyInfo [RFC5280] id-pkix1-e |
237 | | */ |
238 | | |
239 | | static int ptls_compare_separator_line(const char *line, const char *begin_or_end, const char *label) |
240 | 0 | { |
241 | 0 | int ret = strncmp(line, "-----", 5); |
242 | 0 | size_t text_index = 5; |
243 | |
|
244 | 0 | if (ret == 0) { |
245 | 0 | size_t begin_or_end_length = strlen(begin_or_end); |
246 | 0 | ret = strncmp(line + text_index, begin_or_end, begin_or_end_length); |
247 | 0 | text_index += begin_or_end_length; |
248 | 0 | } |
249 | |
|
250 | 0 | if (ret == 0) { |
251 | 0 | ret = line[text_index] - ' '; |
252 | 0 | text_index++; |
253 | 0 | } |
254 | |
|
255 | 0 | if (ret == 0) { |
256 | 0 | size_t label_length = strlen(label); |
257 | 0 | ret = strncmp(line + text_index, label, label_length); |
258 | 0 | text_index += label_length; |
259 | 0 | } |
260 | |
|
261 | 0 | if (ret == 0) { |
262 | 0 | ret = strncmp(line + text_index, "-----", 5); |
263 | 0 | } |
264 | |
|
265 | 0 | return ret; |
266 | 0 | } |
267 | | |
268 | | static int ptls_get_pem_object(FILE *F, const char *label, ptls_buffer_t *buf) |
269 | 0 | { |
270 | 0 | int ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND; |
271 | 0 | char line[256]; |
272 | 0 | ptls_base64_decode_state_t state; |
273 | | |
274 | | /* Get the label on a line by itself */ |
275 | 0 | while (fgets(line, 256, F)) { |
276 | 0 | if (ptls_compare_separator_line(line, "BEGIN", label) == 0) { |
277 | 0 | ret = 0; |
278 | 0 | ptls_base64_decode_init(&state); |
279 | 0 | break; |
280 | 0 | } |
281 | 0 | } |
282 | | /* Get the data in the buffer */ |
283 | 0 | while (ret == 0 && fgets(line, 256, F)) { |
284 | 0 | if (ptls_compare_separator_line(line, "END", label) == 0) { |
285 | 0 | if (state.status == PTLS_BASE64_DECODE_DONE || (state.status == PTLS_BASE64_DECODE_IN_PROGRESS && state.nbc == 0)) { |
286 | 0 | ret = 0; |
287 | 0 | } else { |
288 | 0 | ret = PTLS_ERROR_INCORRECT_BASE64; |
289 | 0 | } |
290 | 0 | break; |
291 | 0 | } else { |
292 | 0 | ret = ptls_base64_decode(line, &state, buf); |
293 | 0 | } |
294 | 0 | } |
295 | |
|
296 | 0 | return ret; |
297 | 0 | } |
298 | | |
299 | | int ptls_load_pem_objects(char const *pem_fname, const char *label, ptls_iovec_t *list, size_t list_max, size_t *nb_objects) |
300 | 0 | { |
301 | 0 | FILE *F; |
302 | 0 | int ret = 0; |
303 | 0 | size_t count = 0; |
304 | | #ifdef _WINDOWS |
305 | | errno_t err = fopen_s(&F, pem_fname, "r"); |
306 | | if (err != 0) { |
307 | | ret = -1; |
308 | | } |
309 | | #else |
310 | 0 | F = fopen(pem_fname, "r"); |
311 | 0 | if (F == NULL) { |
312 | 0 | ret = -1; |
313 | 0 | } |
314 | 0 | #endif |
315 | |
|
316 | 0 | *nb_objects = 0; |
317 | |
|
318 | 0 | if (ret == 0) { |
319 | 0 | while (count < list_max) { |
320 | 0 | ptls_buffer_t buf; |
321 | |
|
322 | 0 | ptls_buffer_init(&buf, "", 0); |
323 | |
|
324 | 0 | ret = ptls_get_pem_object(F, label, &buf); |
325 | |
|
326 | 0 | if (ret == 0) { |
327 | 0 | if (buf.off > 0 && buf.is_allocated) { |
328 | 0 | list[count].base = buf.base; |
329 | 0 | list[count].len = buf.off; |
330 | 0 | count++; |
331 | 0 | } else { |
332 | 0 | ptls_buffer_dispose(&buf); |
333 | 0 | } |
334 | 0 | } else { |
335 | 0 | ptls_buffer_dispose(&buf); |
336 | 0 | break; |
337 | 0 | } |
338 | 0 | } |
339 | 0 | } |
340 | |
|
341 | 0 | if (ret == PTLS_ERROR_PEM_LABEL_NOT_FOUND && count > 0) { |
342 | 0 | ret = 0; |
343 | 0 | } |
344 | |
|
345 | 0 | *nb_objects = count; |
346 | |
|
347 | 0 | if (F != NULL) { |
348 | 0 | fclose(F); |
349 | 0 | } |
350 | |
|
351 | 0 | return ret; |
352 | 0 | } |
353 | | |
354 | 0 | #define PTLS_MAX_CERTS_IN_CONTEXT 16 |
355 | | |
356 | | int ptls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file) |
357 | 0 | { |
358 | 0 | int ret = 0; |
359 | |
|
360 | 0 | ctx->certificates.list = (ptls_iovec_t *)malloc(PTLS_MAX_CERTS_IN_CONTEXT * sizeof(ptls_iovec_t)); |
361 | |
|
362 | 0 | if (ctx->certificates.list == NULL) { |
363 | 0 | ret = PTLS_ERROR_NO_MEMORY; |
364 | 0 | } else { |
365 | 0 | ret = ptls_load_pem_objects(cert_pem_file, "CERTIFICATE", ctx->certificates.list, PTLS_MAX_CERTS_IN_CONTEXT, |
366 | 0 | &ctx->certificates.count); |
367 | 0 | } |
368 | |
|
369 | 0 | return ret; |
370 | 0 | } |