ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
pem_utils.c
Go to the documentation of this file.
1
30#include <ascii-chat/common.h>
31#include <ascii-chat/crypto/pem_utils.h>
32#include <ascii-chat/asciichat_errno.h>
33
34#include <ascii-chat-deps/bearssl/inc/bearssl.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39/* ========== VECTOR MACROS (from BearSSL tools) ========== */
40
41#define VECTOR(type) \
42 struct { \
43 type *buf; \
44 size_t ptr, len; \
45 }
46
47// clang-format off
48#define VEC_INIT {0, 0, 0}
49// clang-format on
50
51#define VEC_CLEAR(vec) \
52 do { \
53 SAFE_FREE((vec).buf); \
54 (vec).buf = NULL; \
55 (vec).ptr = 0; \
56 (vec).len = 0; \
57 } while (0)
58
59#define VEC_CLEAREXT(vec, fun) \
60 do { \
61 size_t vec_tmp; \
62 for (vec_tmp = 0; vec_tmp < (vec).ptr; vec_tmp++) { \
63 (fun)(&(vec).buf[vec_tmp]); \
64 } \
65 VEC_CLEAR(vec); \
66 } while (0)
67
68#define VEC_ADD(vec, x) \
69 do { \
70 (vec).buf = vector_expand((vec).buf, sizeof *((vec).buf), &(vec).ptr, &(vec).len, 1); \
71 (vec).buf[(vec).ptr++] = (x); \
72 } while (0)
73
74#define VEC_ADDMANY(vec, xp, num) \
75 do { \
76 size_t vec_num = (num); \
77 (vec).buf = vector_expand((vec).buf, sizeof *((vec).buf), &(vec).ptr, &(vec).len, vec_num); \
78 memcpy((vec).buf + (vec).ptr, (xp), vec_num * sizeof *((vec).buf)); \
79 (vec).ptr += vec_num; \
80 } while (0)
81
82#define VEC_ELT(vec, idx) ((vec).buf[idx])
83#define VEC_LEN(vec) ((vec).ptr)
84#define VEC_TOARRAY(vec) xblobdup((vec).buf, sizeof *((vec).buf) * (vec).ptr)
85
86/* ========== MEMORY UTILITIES (adapted from BearSSL xmem.c) ========== */
87
88static void *xmalloc(size_t len) {
89 void *buf;
90 if (len == 0) {
91 return NULL;
92 }
93 buf = SAFE_MALLOC(len, void *);
94 return buf;
95}
96
97static void xfree(void *buf) {
98 if (buf != NULL) {
99 SAFE_FREE(buf);
100 }
101}
102
103static void *xblobdup(const void *src, size_t len) {
104 void *buf;
105 if (len == 0) {
106 return NULL;
107 }
108 buf = SAFE_MALLOC(len, void *);
109 memcpy(buf, src, len);
110 return buf;
111}
112
113static char *xstrdup(const char *src) {
114 return (char *)xblobdup(src, strlen(src) + 1);
115}
116
117/* ========== VECTOR EXPANSION (from BearSSL vector.c) ========== */
118
119static void *vector_expand(void *buf, size_t esize, size_t *ptr, size_t *len, size_t extra) {
120 size_t nlen;
121 void *nbuf;
122
123 if (*len - *ptr >= extra) {
124 return buf;
125 }
126 nlen = (*len << 1);
127 if (nlen - *ptr < extra) {
128 nlen = extra + *ptr;
129 if (nlen < 8) {
130 nlen = 8;
131 }
132 }
133 nbuf = xmalloc(nlen * esize);
134 if (buf != NULL) {
135 memcpy(nbuf, buf, *len * esize);
136 xfree(buf);
137 }
138 *len = nlen;
139 return nbuf;
140}
141
142/* ========== DER DETECTION (from BearSSL files.c) ========== */
143
144static int looks_like_DER(const unsigned char *buf, size_t len) {
145 int fb;
146 size_t dlen;
147
148 if (len < 2) {
149 return 0;
150 }
151 if (*buf++ != 0x30) {
152 return 0;
153 }
154 fb = *buf++;
155 len -= 2;
156 if (fb < 0x80) {
157 return (size_t)fb == len;
158 }
159 if (fb == 0x80) {
160 return 0;
161 }
162 {
163 fb -= 0x80;
164 if (len < (size_t)fb + 2) {
165 return 0;
166 }
167 len -= (size_t)fb;
168 dlen = 0;
169 while (fb-- > 0) {
170 if (dlen > (len >> 8)) {
171 return 0;
172 }
173 dlen = (dlen << 8) + (size_t)*buf++;
174 }
175 return dlen == len;
176 }
177}
178
179/* ========== PEM DECODING (from BearSSL files.c) ========== */
180
187typedef VECTOR(unsigned char) bvector;
188
192typedef struct {
193 char *name;
194 unsigned char *data;
195 size_t data_len;
197
198static void free_pem_object_t_contents(pem_object_t *po) {
199 if (po != NULL) {
200 xfree(po->name);
201 xfree(po->data);
202 }
203}
204
205static void vblob_append(void *cc, const void *data, size_t len) {
206 bvector *bv = cc;
207 VEC_ADDMANY(*bv, data, len);
208}
209
214static pem_object_t *decode_pem(const void *src, size_t len, size_t *num) {
215 VECTOR(pem_object_t) pem_list = VEC_INIT;
216 br_pem_decoder_context pc;
217 pem_object_t po, *pos;
218 const unsigned char *buf;
219 bvector bv = VEC_INIT;
220 int inobj;
221 int extra_nl;
222
223 *num = 0;
224 br_pem_decoder_init(&pc);
225 buf = src;
226 inobj = 0;
227 po.name = NULL;
228 po.data = NULL;
229 po.data_len = 0;
230 extra_nl = 1;
231
232 while (len > 0) {
233 size_t tlen;
234
235 tlen = br_pem_decoder_push(&pc, buf, len);
236 buf += tlen;
237 len -= tlen;
238
239 switch (br_pem_decoder_event(&pc)) {
240
241 case BR_PEM_BEGIN_OBJ:
242 po.name = xstrdup(br_pem_decoder_name(&pc));
243 br_pem_decoder_setdest(&pc, vblob_append, &bv);
244 inobj = 1;
245 break;
246
247 case BR_PEM_END_OBJ:
248 if (inobj) {
249 po.data = VEC_TOARRAY(bv);
250 po.data_len = VEC_LEN(bv);
251 VEC_ADD(pem_list, po);
252 VEC_CLEAR(bv);
253 po.name = NULL;
254 po.data = NULL;
255 po.data_len = 0;
256 inobj = 0;
257 }
258 break;
259
260 case BR_PEM_ERROR:
261 xfree(po.name);
262 VEC_CLEAR(bv);
263 log_error("Invalid PEM encoding");
264 VEC_CLEAREXT(pem_list, free_pem_object_t_contents);
265 return NULL;
266
267 default:
268 // Ignore other PEM events
269 break;
270 }
271
272 // Add extra newline at end to support PEM files without trailing newline
273 if (len == 0 && extra_nl) {
274 extra_nl = 0;
275 buf = (const unsigned char *)"\n";
276 len = 1;
277 }
278 }
279
280 if (inobj) {
281 log_error("Unfinished PEM object");
282 xfree(po.name);
283 VEC_CLEAR(bv);
284 VEC_CLEAREXT(pem_list, free_pem_object_t_contents);
285 return NULL;
286 }
287
288 *num = VEC_LEN(pem_list);
289 VEC_ADD(pem_list, po);
290 pos = VEC_TOARRAY(pem_list);
291 VEC_CLEAR(pem_list);
292 return pos;
293}
294
295/* ========== CERTIFICATE PARSING (adapted from BearSSL files.c) ========== */
296
301static br_x509_certificate *read_certificates_from_memory(const unsigned char *buf, size_t len, size_t *num) {
302 VECTOR(br_x509_certificate) cert_list = VEC_INIT;
303 pem_object_t *pos;
304 size_t u, num_pos;
305 br_x509_certificate *xcs;
306 br_x509_certificate dummy;
307
308 *num = 0;
309
310 // Check for DER-encoded certificate
311 if (looks_like_DER(buf, len)) {
312 xcs = xmalloc(2 * sizeof *xcs);
313 xcs[0].data = xblobdup(buf, len);
314 xcs[0].data_len = len;
315 xcs[1].data = NULL;
316 xcs[1].data_len = 0;
317 *num = 1;
318 return xcs;
319 }
320
321 // Decode PEM
322 pos = decode_pem(buf, len, &num_pos);
323 if (pos == NULL) {
324 return NULL;
325 }
326
327 // Extract certificates
328 for (u = 0; u < num_pos; u++) {
329 // Windows CryptBinaryToStringA may append dashes to the name, so check for:
330 // "CERTIFICATE", "CERTIFICATE-----", "X509 CERTIFICATE", "X509 CERTIFICATE-----"
331 const char *name = pos[u].name;
332 if (name) {
333 // Strip trailing dashes for comparison
334 size_t name_len = strlen(name);
335 while (name_len > 0 && name[name_len - 1] == '-') {
336 name_len--;
337 }
338
339 // Check if name matches CERTIFICATE or X509 CERTIFICATE (ignoring trailing dashes)
340 if ((name_len == 11 && strncmp(name, "CERTIFICATE", 11) == 0) ||
341 (name_len == 16 && strncmp(name, "X509 CERTIFICATE", 16) == 0)) {
342 br_x509_certificate xc;
343 xc.data = pos[u].data;
344 xc.data_len = pos[u].data_len;
345 pos[u].data = NULL; // Transfer ownership
346 VEC_ADD(cert_list, xc);
347 }
348 }
349 }
350
351 // Free PEM objects
352 for (u = 0; u < num_pos; u++) {
353 free_pem_object_t_contents(&pos[u]);
354 }
355 xfree(pos);
356
357 if (VEC_LEN(cert_list) == 0) {
358 log_error("No certificates found in PEM data");
359 return NULL;
360 }
361
362 *num = VEC_LEN(cert_list);
363 dummy.data = NULL;
364 dummy.data_len = 0;
365 VEC_ADD(cert_list, dummy);
366 xcs = VEC_TOARRAY(cert_list);
367 VEC_CLEAR(cert_list);
368 return xcs;
369}
370
371static void free_certificates(br_x509_certificate *certs, size_t num) {
372 size_t u;
373 for (u = 0; u < num; u++) {
374 xfree((void *)certs[u].data);
375 }
376 xfree(certs);
377}
378
379/* ========== TRUST ANCHOR CONVERSION (from BearSSL certs.c) ========== */
380
381static void dn_append(void *ctx, const void *buf, size_t len) {
382 VEC_ADDMANY(*(bvector *)ctx, buf, len);
383}
384
385static asciichat_error_t certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta, br_x509_certificate *xc) {
386 br_x509_decoder_context dc;
387 bvector vdn = VEC_INIT;
388 br_x509_pkey *pk;
389
390 br_x509_decoder_init(&dc, dn_append, &vdn);
391 br_x509_decoder_push(&dc, xc->data, xc->data_len);
392 pk = br_x509_decoder_get_pkey(&dc);
393 if (pk == NULL) {
394 VEC_CLEAR(vdn);
395 return SET_ERRNO(ERROR_CRYPTO, "CA decoding failed with error %d", br_x509_decoder_last_error(&dc));
396 }
397
398 ta->dn.data = VEC_TOARRAY(vdn);
399 ta->dn.len = VEC_LEN(vdn);
400 VEC_CLEAR(vdn);
401 ta->flags = 0;
402 if (br_x509_decoder_isCA(&dc)) {
403 ta->flags |= BR_X509_TA_CA;
404 }
405
406 switch (pk->key_type) {
407 case BR_KEYTYPE_RSA:
408 ta->pkey.key_type = BR_KEYTYPE_RSA;
409 ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
410 ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
411 ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
412 ta->pkey.key.rsa.elen = pk->key.rsa.elen;
413 break;
414 case BR_KEYTYPE_EC:
415 ta->pkey.key_type = BR_KEYTYPE_EC;
416 ta->pkey.key.ec.curve = pk->key.ec.curve;
417 ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
418 ta->pkey.key.ec.qlen = pk->key.ec.qlen;
419 break;
420 default:
421 xfree(ta->dn.data);
422 return SET_ERRNO(ERROR_CRYPTO, "Unsupported public key type in CA certificate");
423 }
424
425 return ASCIICHAT_OK;
426}
427
428void free_ta_contents(br_x509_trust_anchor *ta) {
429 if (!ta) {
430 return;
431 }
432 xfree(ta->dn.data);
433 switch (ta->pkey.key_type) {
434 case BR_KEYTYPE_RSA:
435 xfree((void *)ta->pkey.key.rsa.n);
436 xfree((void *)ta->pkey.key.rsa.e);
437 break;
438 case BR_KEYTYPE_EC:
439 xfree((void *)ta->pkey.key.ec.q);
440 break;
441 default:
442 SET_ERRNO(ERROR_CRYPTO, "Unknown public key type in CA");
443 break;
444 }
445}
446
447/* ========== PUBLIC API ========== */
448
449size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len) {
450 br_x509_certificate *xcs;
451 anchor_list tas = VEC_INIT;
452 size_t u, num;
453
454 xcs = read_certificates_from_memory(pem_data, pem_len, &num);
455 if (xcs == NULL) {
456 return 0;
457 }
458
459 for (u = 0; u < num; u++) {
460 br_x509_trust_anchor ta;
461
462 if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) != ASCIICHAT_OK) {
464 free_certificates(xcs, num);
465 return 0;
466 }
467 VEC_ADD(tas, ta);
468 }
469
470 VEC_ADDMANY(*dst, &VEC_ELT(tas, 0), num);
471 VEC_CLEAR(tas);
472 free_certificates(xcs, num);
473 return num;
474}
#define VEC_TOARRAY(vec)
Definition pem_utils.c:84
#define VEC_LEN(vec)
Definition pem_utils.c:83
#define VEC_CLEAR(vec)
Definition pem_utils.c:51
#define VEC_CLEAREXT(vec, fun)
Definition pem_utils.c:59
#define VEC_ADDMANY(vec, xp, num)
Definition pem_utils.c:74
#define VEC_ELT(vec, idx)
Definition pem_utils.c:82
size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len)
Definition pem_utils.c:449
void free_ta_contents(br_x509_trust_anchor *ta)
Definition pem_utils.c:428
#define VEC_INIT
Definition pem_utils.c:48
#define VECTOR(type)
Definition pem_utils.c:41
#define VEC_ADD(vec, x)
Definition pem_utils.c:68
Dynamic byte vector for PEM decoding.
Definition pem_utils.c:187
Parsed PEM object with name and data.
Definition pem_utils.c:192
char * name
PEM object name (e.g., "CERTIFICATE", "PRIVATE KEY")
Definition pem_utils.c:193
size_t data_len
Length of data in bytes.
Definition pem_utils.c:195
unsigned char * data
Decoded binary data.
Definition pem_utils.c:194