ascii-chat 0.6.0
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 "../common.h"
31#include "pem_utils.h"
32#include "asciichat_errno.h"
33
34#include <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
181typedef VECTOR(unsigned char) bvector;
182
183typedef struct {
184 char *name;
185 unsigned char *data;
186 size_t data_len;
187} pem_object;
188
189static void free_pem_object_contents(pem_object *po) {
190 if (po != NULL) {
191 xfree(po->name);
192 xfree(po->data);
193 }
194}
195
196static void vblob_append(void *cc, const void *data, size_t len) {
197 bvector *bv = cc;
198 VEC_ADDMANY(*bv, data, len);
199}
200
205static pem_object *decode_pem(const void *src, size_t len, size_t *num) {
206 VECTOR(pem_object) pem_list = VEC_INIT;
207 br_pem_decoder_context pc;
208 pem_object po, *pos;
209 const unsigned char *buf;
210 bvector bv = VEC_INIT;
211 int inobj;
212 int extra_nl;
213
214 *num = 0;
215 br_pem_decoder_init(&pc);
216 buf = src;
217 inobj = 0;
218 po.name = NULL;
219 po.data = NULL;
220 po.data_len = 0;
221 extra_nl = 1;
222
223 while (len > 0) {
224 size_t tlen;
225
226 tlen = br_pem_decoder_push(&pc, buf, len);
227 buf += tlen;
228 len -= tlen;
229
230 switch (br_pem_decoder_event(&pc)) {
231
232 case BR_PEM_BEGIN_OBJ:
233 po.name = xstrdup(br_pem_decoder_name(&pc));
234 br_pem_decoder_setdest(&pc, vblob_append, &bv);
235 inobj = 1;
236 break;
237
238 case BR_PEM_END_OBJ:
239 if (inobj) {
240 po.data = VEC_TOARRAY(bv);
241 po.data_len = VEC_LEN(bv);
242 VEC_ADD(pem_list, po);
243 VEC_CLEAR(bv);
244 po.name = NULL;
245 po.data = NULL;
246 po.data_len = 0;
247 inobj = 0;
248 }
249 break;
250
251 case BR_PEM_ERROR:
252 xfree(po.name);
253 VEC_CLEAR(bv);
254 log_error("Invalid PEM encoding");
255 VEC_CLEAREXT(pem_list, free_pem_object_contents);
256 return NULL;
257
258 default:
259 // Ignore other PEM events
260 break;
261 }
262
263 // Add extra newline at end to support PEM files without trailing newline
264 if (len == 0 && extra_nl) {
265 extra_nl = 0;
266 buf = (const unsigned char *)"\n";
267 len = 1;
268 }
269 }
270
271 if (inobj) {
272 log_error("Unfinished PEM object");
273 xfree(po.name);
274 VEC_CLEAR(bv);
275 VEC_CLEAREXT(pem_list, free_pem_object_contents);
276 return NULL;
277 }
278
279 *num = VEC_LEN(pem_list);
280 VEC_ADD(pem_list, po);
281 pos = VEC_TOARRAY(pem_list);
282 VEC_CLEAR(pem_list);
283 return pos;
284}
285
286/* ========== CERTIFICATE PARSING (adapted from BearSSL files.c) ========== */
287
292static br_x509_certificate *read_certificates_from_memory(const unsigned char *buf, size_t len, size_t *num) {
293 VECTOR(br_x509_certificate) cert_list = VEC_INIT;
294 pem_object *pos;
295 size_t u, num_pos;
296 br_x509_certificate *xcs;
297 br_x509_certificate dummy;
298
299 *num = 0;
300
301 // Check for DER-encoded certificate
302 if (looks_like_DER(buf, len)) {
303 xcs = xmalloc(2 * sizeof *xcs);
304 xcs[0].data = xblobdup(buf, len);
305 xcs[0].data_len = len;
306 xcs[1].data = NULL;
307 xcs[1].data_len = 0;
308 *num = 1;
309 return xcs;
310 }
311
312 // Decode PEM
313 pos = decode_pem(buf, len, &num_pos);
314 if (pos == NULL) {
315 return NULL;
316 }
317
318 // Extract certificates
319 for (u = 0; u < num_pos; u++) {
320 // Windows CryptBinaryToStringA may append dashes to the name, so check for:
321 // "CERTIFICATE", "CERTIFICATE-----", "X509 CERTIFICATE", "X509 CERTIFICATE-----"
322 const char *name = pos[u].name;
323 if (name) {
324 // Strip trailing dashes for comparison
325 size_t name_len = strlen(name);
326 while (name_len > 0 && name[name_len - 1] == '-') {
327 name_len--;
328 }
329
330 // Check if name matches CERTIFICATE or X509 CERTIFICATE (ignoring trailing dashes)
331 if ((name_len == 11 && strncmp(name, "CERTIFICATE", 11) == 0) ||
332 (name_len == 16 && strncmp(name, "X509 CERTIFICATE", 16) == 0)) {
333 br_x509_certificate xc;
334 xc.data = pos[u].data;
335 xc.data_len = pos[u].data_len;
336 pos[u].data = NULL; // Transfer ownership
337 VEC_ADD(cert_list, xc);
338 }
339 }
340 }
341
342 // Free PEM objects
343 for (u = 0; u < num_pos; u++) {
344 free_pem_object_contents(&pos[u]);
345 }
346 xfree(pos);
347
348 if (VEC_LEN(cert_list) == 0) {
349 log_error("No certificates found in PEM data");
350 return NULL;
351 }
352
353 *num = VEC_LEN(cert_list);
354 dummy.data = NULL;
355 dummy.data_len = 0;
356 VEC_ADD(cert_list, dummy);
357 xcs = VEC_TOARRAY(cert_list);
358 VEC_CLEAR(cert_list);
359 return xcs;
360}
361
362static void free_certificates(br_x509_certificate *certs, size_t num) {
363 size_t u;
364 for (u = 0; u < num; u++) {
365 xfree((void *)certs[u].data);
366 }
367 xfree(certs);
368}
369
370/* ========== TRUST ANCHOR CONVERSION (from BearSSL certs.c) ========== */
371
372static void dn_append(void *ctx, const void *buf, size_t len) {
373 VEC_ADDMANY(*(bvector *)ctx, buf, len);
374}
375
376static asciichat_error_t certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta, br_x509_certificate *xc) {
377 br_x509_decoder_context dc;
378 bvector vdn = VEC_INIT;
379 br_x509_pkey *pk;
380
381 br_x509_decoder_init(&dc, dn_append, &vdn);
382 br_x509_decoder_push(&dc, xc->data, xc->data_len);
383 pk = br_x509_decoder_get_pkey(&dc);
384 if (pk == NULL) {
385 VEC_CLEAR(vdn);
386 return SET_ERRNO(ERROR_CRYPTO, "CA decoding failed with error %d", br_x509_decoder_last_error(&dc));
387 }
388
389 ta->dn.data = VEC_TOARRAY(vdn);
390 ta->dn.len = VEC_LEN(vdn);
391 VEC_CLEAR(vdn);
392 ta->flags = 0;
393 if (br_x509_decoder_isCA(&dc)) {
394 ta->flags |= BR_X509_TA_CA;
395 }
396
397 switch (pk->key_type) {
398 case BR_KEYTYPE_RSA:
399 ta->pkey.key_type = BR_KEYTYPE_RSA;
400 ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
401 ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
402 ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
403 ta->pkey.key.rsa.elen = pk->key.rsa.elen;
404 break;
405 case BR_KEYTYPE_EC:
406 ta->pkey.key_type = BR_KEYTYPE_EC;
407 ta->pkey.key.ec.curve = pk->key.ec.curve;
408 ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
409 ta->pkey.key.ec.qlen = pk->key.ec.qlen;
410 break;
411 default:
412 xfree(ta->dn.data);
413 return SET_ERRNO(ERROR_CRYPTO, "Unsupported public key type in CA certificate");
414 }
415
416 return ASCIICHAT_OK;
417}
418
419void free_ta_contents(br_x509_trust_anchor *ta) {
420 if (!ta) {
421 return;
422 }
423 xfree(ta->dn.data);
424 switch (ta->pkey.key_type) {
425 case BR_KEYTYPE_RSA:
426 xfree((void *)ta->pkey.key.rsa.n);
427 xfree((void *)ta->pkey.key.rsa.e);
428 break;
429 case BR_KEYTYPE_EC:
430 xfree((void *)ta->pkey.key.ec.q);
431 break;
432 default:
433 SET_ERRNO(ERROR_CRYPTO, "Unknown public key type in CA");
434 break;
435 }
436}
437
438/* ========== PUBLIC API ========== */
439
440size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len) {
441 br_x509_certificate *xcs;
442 anchor_list tas = VEC_INIT;
443 size_t u, num;
444
445 xcs = read_certificates_from_memory(pem_data, pem_len, &num);
446 if (xcs == NULL) {
447 return 0;
448 }
449
450 for (u = 0; u < num; u++) {
451 br_x509_trust_anchor ta;
452
453 if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) != ASCIICHAT_OK) {
455 free_certificates(xcs, num);
456 return 0;
457 }
458 VEC_ADD(tas, ta);
459 }
460
461 VEC_ADDMANY(*dst, &VEC_ELT(tas, 0), num);
462 VEC_CLEAR(tas);
463 free_certificates(xcs, num);
464 return num;
465}
⚠️‼️ Error and/or exit() when things go bad.
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
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_utils.c:440
void free_ta_contents(br_x509_trust_anchor *ta)
Free the contents of a trust anchor.
Definition pem_utils.c:419
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_CRYPTO
Definition error_codes.h:88
#define log_error(...)
Log an ERROR message.
#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
#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
BearSSL PEM and trust anchor utilities adapted for in-memory data.
Vector type for trust anchors.
Definition pem_utils.h:78
unsigned char * data
Definition pem_utils.c:185
size_t data_len
Definition pem_utils.c:186
char * name
Definition pem_utils.c:184