ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
pem.c
Go to the documentation of this file.
1
29
30#include <ascii-chat/common.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
189typedef VECTOR(unsigned char) bvector;
190
200typedef struct {
201 char *name;
202 unsigned char *data;
203 size_t data_len;
205
206static void free_pem_object_t_contents(pem_object_t *po) {
207 if (po != NULL) {
208 xfree(po->name);
209 xfree(po->data);
210 }
211}
212
213static void vblob_append(void *cc, const void *data, size_t len) {
214 bvector *bv = cc;
215 VEC_ADDMANY(*bv, data, len);
216}
217
222static pem_object_t *decode_pem(const void *src, size_t len, size_t *num) {
223 VECTOR(pem_object_t) pem_list = VEC_INIT;
224 br_pem_decoder_context pc;
225 pem_object_t po, *pos;
226 const unsigned char *buf;
227 bvector bv = VEC_INIT;
228 int inobj;
229 int extra_nl;
230
231 *num = 0;
232 br_pem_decoder_init(&pc);
233 buf = src;
234 inobj = 0;
235 po.name = NULL;
236 po.data = NULL;
237 po.data_len = 0;
238 extra_nl = 1;
239
240 while (len > 0) {
241 size_t tlen;
242
243 tlen = br_pem_decoder_push(&pc, buf, len);
244 buf += tlen;
245 len -= tlen;
246
247 switch (br_pem_decoder_event(&pc)) {
248
249 case BR_PEM_BEGIN_OBJ:
250 po.name = xstrdup(br_pem_decoder_name(&pc));
251 br_pem_decoder_setdest(&pc, vblob_append, &bv);
252 inobj = 1;
253 break;
254
255 case BR_PEM_END_OBJ:
256 if (inobj) {
257 po.data = VEC_TOARRAY(bv);
258 po.data_len = VEC_LEN(bv);
259 VEC_ADD(pem_list, po);
260 VEC_CLEAR(bv);
261 po.name = NULL;
262 po.data = NULL;
263 po.data_len = 0;
264 inobj = 0;
265 }
266 break;
267
268 case BR_PEM_ERROR:
269 xfree(po.name);
270 VEC_CLEAR(bv);
271 log_error("Invalid PEM encoding");
272 VEC_CLEAREXT(pem_list, free_pem_object_t_contents);
273 return NULL;
274
275 default:
276 // Ignore other PEM events
277 break;
278 }
279
280 // Add extra newline at end to support PEM files without trailing newline
281 if (len == 0 && extra_nl) {
282 extra_nl = 0;
283 buf = (const unsigned char *)"\n";
284 len = 1;
285 }
286 }
287
288 if (inobj) {
289 log_error("Unfinished PEM object");
290 xfree(po.name);
291 VEC_CLEAR(bv);
292 VEC_CLEAREXT(pem_list, free_pem_object_t_contents);
293 return NULL;
294 }
295
296 *num = VEC_LEN(pem_list);
297 VEC_ADD(pem_list, po);
298 pos = VEC_TOARRAY(pem_list);
299 VEC_CLEAR(pem_list);
300 return pos;
301}
302
303/* ========== CERTIFICATE PARSING (adapted from BearSSL files.c) ========== */
304
309static br_x509_certificate *read_certificates_from_memory(const unsigned char *buf, size_t len, size_t *num) {
310 VECTOR(br_x509_certificate) cert_list = VEC_INIT;
311 pem_object_t *pos;
312 size_t u, num_pos;
313 br_x509_certificate *xcs;
314 br_x509_certificate dummy;
315
316 *num = 0;
317
318 // Check for DER-encoded certificate
319 if (looks_like_DER(buf, len)) {
320 xcs = xmalloc(2 * sizeof *xcs);
321 xcs[0].data = xblobdup(buf, len);
322 xcs[0].data_len = len;
323 xcs[1].data = NULL;
324 xcs[1].data_len = 0;
325 *num = 1;
326 return xcs;
327 }
328
329 // Decode PEM
330 pos = decode_pem(buf, len, &num_pos);
331 if (pos == NULL) {
332 return NULL;
333 }
334
335 // Extract certificates
336 for (u = 0; u < num_pos; u++) {
337 // Windows CryptBinaryToStringA may append dashes to the name, so check for:
338 // "CERTIFICATE", "CERTIFICATE-----", "X509 CERTIFICATE", "X509 CERTIFICATE-----"
339 const char *name = pos[u].name;
340 if (name) {
341 // Strip trailing dashes for comparison
342 size_t name_len = strlen(name);
343 while (name_len > 0 && name[name_len - 1] == '-') {
344 name_len--;
345 }
346
347 // Check if name matches CERTIFICATE or X509 CERTIFICATE (ignoring trailing dashes)
348 if ((name_len == 11 && strncmp(name, "CERTIFICATE", 11) == 0) ||
349 (name_len == 16 && strncmp(name, "X509 CERTIFICATE", 16) == 0)) {
350 br_x509_certificate xc;
351 xc.data = pos[u].data;
352 xc.data_len = pos[u].data_len;
353 pos[u].data = NULL; // Transfer ownership
354 VEC_ADD(cert_list, xc);
355 }
356 }
357 }
358
359 // Free PEM objects
360 for (u = 0; u < num_pos; u++) {
361 free_pem_object_t_contents(&pos[u]);
362 }
363 xfree(pos);
364
365 if (VEC_LEN(cert_list) == 0) {
366 log_error("No certificates found in PEM data");
367 return NULL;
368 }
369
370 *num = VEC_LEN(cert_list);
371 dummy.data = NULL;
372 dummy.data_len = 0;
373 VEC_ADD(cert_list, dummy);
374 xcs = VEC_TOARRAY(cert_list);
375 VEC_CLEAR(cert_list);
376 return xcs;
377}
378
379static void free_certificates(br_x509_certificate *certs, size_t num) {
380 size_t u;
381 for (u = 0; u < num; u++) {
382 xfree((void *)certs[u].data);
383 }
384 xfree(certs);
385}
386
387/* ========== TRUST ANCHOR CONVERSION (from BearSSL certs.c) ========== */
388
389static void dn_append(void *ctx, const void *buf, size_t len) {
390 VEC_ADDMANY(*(bvector *)ctx, buf, len);
391}
392
393static asciichat_error_t certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta, br_x509_certificate *xc) {
394 br_x509_decoder_context dc;
395 bvector vdn = VEC_INIT;
396 br_x509_pkey *pk;
397
398 br_x509_decoder_init(&dc, dn_append, &vdn);
399 br_x509_decoder_push(&dc, xc->data, xc->data_len);
400 pk = br_x509_decoder_get_pkey(&dc);
401 if (pk == NULL) {
402 VEC_CLEAR(vdn);
403 return SET_ERRNO(ERROR_CRYPTO, "CA decoding failed with error %d", br_x509_decoder_last_error(&dc));
404 }
405
406 ta->dn.data = VEC_TOARRAY(vdn);
407 ta->dn.len = VEC_LEN(vdn);
408 VEC_CLEAR(vdn);
409 ta->flags = 0;
410 if (br_x509_decoder_isCA(&dc)) {
411 ta->flags |= BR_X509_TA_CA;
412 }
413
414 switch (pk->key_type) {
415 case BR_KEYTYPE_RSA:
416 ta->pkey.key_type = BR_KEYTYPE_RSA;
417 ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
418 ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
419 ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
420 ta->pkey.key.rsa.elen = pk->key.rsa.elen;
421 break;
422 case BR_KEYTYPE_EC:
423 ta->pkey.key_type = BR_KEYTYPE_EC;
424 ta->pkey.key.ec.curve = pk->key.ec.curve;
425 ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
426 ta->pkey.key.ec.qlen = pk->key.ec.qlen;
427 break;
428 default:
429 xfree(ta->dn.data);
430 return SET_ERRNO(ERROR_CRYPTO, "Unsupported public key type in CA certificate");
431 }
432
433 return ASCIICHAT_OK;
434}
435
436void free_ta_contents(br_x509_trust_anchor *ta) {
437 if (!ta) {
438 return;
439 }
440 xfree(ta->dn.data);
441 switch (ta->pkey.key_type) {
442 case BR_KEYTYPE_RSA:
443 xfree((void *)ta->pkey.key.rsa.n);
444 xfree((void *)ta->pkey.key.rsa.e);
445 break;
446 case BR_KEYTYPE_EC:
447 xfree((void *)ta->pkey.key.ec.q);
448 break;
449 default:
450 SET_ERRNO(ERROR_CRYPTO, "Unknown public key type in CA");
451 break;
452 }
453}
454
455/* ========== PUBLIC API ========== */
456
457size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len) {
458 br_x509_certificate *xcs;
459 anchor_list tas = VEC_INIT;
460 size_t u, num;
461
462 xcs = read_certificates_from_memory(pem_data, pem_len, &num);
463 if (xcs == NULL) {
464 return 0;
465 }
466
467 for (u = 0; u < num; u++) {
468 br_x509_trust_anchor ta;
469
470 if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) != ASCIICHAT_OK) {
472 free_certificates(xcs, num);
473 return 0;
474 }
475 VEC_ADD(tas, ta);
476 }
477
478 VEC_ADDMANY(*dst, &VEC_ELT(tas, 0), num);
479 VEC_CLEAR(tas);
480 free_certificates(xcs, num);
481 return num;
482}
Synchronized per-thread pending error stacks and value snapshots. SET_ERRNO pushes,...
#define SET_ERRNO(code,...)
⚙️ Common definitions, error codes, macros, and types shared throughout the application
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len)
Read trust anchors from PEM-encoded data in memory.
Definition pem.c:457
void free_ta_contents(br_x509_trust_anchor *ta)
Free the contents of a trust anchor.
Definition pem.c:436
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_CRYPTO
Definition error_codes.h:93
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define VEC_TOARRAY(vec)
Definition pem.c:84
#define VEC_LEN(vec)
Definition pem.c:83
#define VEC_CLEAR(vec)
Definition pem.c:51
#define VEC_CLEAREXT(vec, fun)
Definition pem.c:59
#define VEC_ADDMANY(vec, xp, num)
Definition pem.c:74
#define VEC_ELT(vec, idx)
Definition pem.c:82
#define VEC_INIT
Definition pem.c:48
#define VECTOR(type)
Definition pem.c:41
#define VEC_ADD(vec, x)
Definition pem.c:68
BearSSL PEM and trust anchor utilities adapted for in-memory data.
🔤 String Manipulation and Shell Escaping Utilities
Vector type for trust anchors.
Definition pem.h:79
Dynamic byte vector for PEM decoding.
Definition pem.c:189
One decoded PEM (Privacy-Enhanced Mail) block.
Definition pem.c:200
char * name
PEM object name (e.g., "CERTIFICATE", "PRIVATE KEY").
Definition pem.c:201
size_t data_len
Length of data in bytes.
Definition pem.c:203
unsigned char * data
Decoded binary data.
Definition pem.c:202