• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include "private-lib-core.h"
26 
27 void
lws_tls_kid_copy(union lws_tls_cert_info_results * ci,lws_tls_kid_t * kid)28 lws_tls_kid_copy(union lws_tls_cert_info_results *ci, lws_tls_kid_t *kid)
29 {
30 
31 	/*
32 	 * KIDs all seem to be 20 bytes / SHA1 or less.  If we get one that
33 	 * is bigger, treat only the first 20 bytes as significant.
34 	 */
35 
36 	if ((size_t)ci->ns.len > sizeof(kid->kid))
37 		kid->kid_len = sizeof(kid->kid);
38 	else
39 		kid->kid_len = (uint8_t)ci->ns.len;
40 
41 	memcpy(kid->kid, ci->ns.name, kid->kid_len);
42 }
43 
44 void
lws_tls_kid_copy_kid(lws_tls_kid_t * kid,const lws_tls_kid_t * src)45 lws_tls_kid_copy_kid(lws_tls_kid_t *kid, const lws_tls_kid_t *src)
46 {
47 	int klen = sizeof(kid->kid);
48 
49 	if (src->kid_len < klen)
50 		klen = src->kid_len;
51 
52 	kid->kid_len = (uint8_t)klen;
53 
54 	memcpy(kid->kid, src->kid, (size_t)klen);
55 }
56 
57 int
lws_tls_kid_cmp(const lws_tls_kid_t * a,const lws_tls_kid_t * b)58 lws_tls_kid_cmp(const lws_tls_kid_t *a, const lws_tls_kid_t *b)
59 {
60 	if (a->kid_len != b->kid_len)
61 		return 1;
62 
63 	return memcmp(a->kid, b->kid, a->kid_len);
64 }
65 
66 /*
67  * We have the SKID and AKID for every peer cert captured, but they may be
68  * in any order, and eg, falsely have sent the root CA, or an attacker may
69  * send unresolveable self-referencing loops of KIDs.
70  *
71  * Let's sort them into the SKID -> AKID hierarchy, so the last entry is the
72  * server cert and the first entry is the highest parent that the server sent.
73  * Normally the top one will be an intermediate, and its AKID is the ID of the
74  * root CA cert we would need to trust to validate the chain.
75  *
76  * It's not unknown the server is misconfigured to also send the root CA, if so
77  * the top slot's AKID is empty and we should look for its SKID in the trust
78  * blob.
79  *
80  * If we return 0, we succeeded and the AKID of ch[0] is the SKID we want to see
81  * try to import from the trust blob.
82  *
83  * If we return nonzero, we can't identify what we want and should abandon the
84  * connection.
85  */
86 
87 int
lws_tls_jit_trust_sort_kids(struct lws * wsi,lws_tls_kid_chain_t * ch)88 lws_tls_jit_trust_sort_kids(struct lws *wsi, lws_tls_kid_chain_t *ch)
89 {
90 	size_t hl;
91 	lws_tls_jit_inflight_t *inf;
92 	int n, m, sanity = 10;
93 	const char *host = wsi->cli_hostname_copy;
94 	char more = 1;
95 
96 	lwsl_info("%s\n", __func__);
97 
98 	if (!host) {
99 		if (wsi->stash && wsi->stash->cis[CIS_HOST])
100 			host = wsi->stash->cis[CIS_HOST];
101 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
102 		else
103 			host = lws_hdr_simple_ptr(wsi,
104 					      _WSI_TOKEN_CLIENT_PEER_ADDRESS);
105 	}
106 #endif
107 	if (!host)
108 		return 1;
109 
110 	hl = strlen(host);
111 
112 	/* something to work with? */
113 
114 	if (!ch->count)
115 		return 1;
116 
117 	/* do we need to sort? */
118 
119 	if (ch->count > 1) {
120 
121 		/* okie... */
122 
123 		while (more) {
124 
125 			if (!sanity--)
126 				/* let's not get fooled into spinning */
127 				return 1;
128 
129 			more = 0;
130 			for (n = 0; n < ch->count - 1; n++) {
131 
132 				if (!lws_tls_kid_cmp(&ch->skid[n],
133 						     &ch->akid[n + 1]))
134 					/* next belongs with this one */
135 					continue;
136 
137 				/*
138 				 * next doesn't belong with this one, let's
139 				 * try to figure out where this one does belong
140 				 * then
141 				 */
142 
143 				for (m = 0; m < ch->count; m++) {
144 					if (n == m)
145 						continue;
146 					if (!lws_tls_kid_cmp(&ch->skid[n],
147 							     &ch->akid[m])) {
148 						lws_tls_kid_t t;
149 
150 						/*
151 						 * m references us, so we
152 						 * need to go one step above m,
153 						 * swap m and n
154 						 */
155 
156 						more = 1;
157 						t = ch->akid[m];
158 						ch->akid[m] = ch->akid[n];
159 						ch->akid[n] = t;
160 						t = ch->skid[m];
161 						ch->skid[m] = ch->skid[n];
162 						ch->skid[n] = t;
163 
164 						break;
165 					}
166 				}
167 
168 				if (more)
169 					break;
170 			}
171 		}
172 
173 		/* then we should be sorted */
174 	}
175 
176 	for (n = 0; n < ch->count; n++) {
177 		lwsl_info("%s: AKID[%d]\n", __func__, n);
178 		lwsl_hexdump_info(ch->akid[n].kid, ch->akid[n].kid_len);
179 		lwsl_info("%s: SKID[%d]\n", __func__, n);
180 		lwsl_hexdump_info(ch->skid[n].kid, ch->skid[n].kid_len);
181 	}
182 
183 	/* to go further, user must provide a lookup helper */
184 
185 	if (!wsi->a.context->system_ops ||
186 	    !wsi->a.context->system_ops->jit_trust_query)
187 		return 1;
188 
189 	/*
190 	 * If there's already a pending lookup for this host, let's bail and
191 	 * just wait for that to complete (since it will be done async if we
192 	 * can see it)
193 	 */
194 
195 	lws_start_foreach_dll(struct lws_dll2 *, d,
196 			      wsi->a.context->jit_inflight.head) {
197 		inf = lws_container_of(d, lws_tls_jit_inflight_t, list);
198 
199 		if (!strcmp((const char *)&inf[1], host))
200 			/* already being handled */
201 			return 1;
202 
203 	} lws_end_foreach_dll(d);
204 
205 	/*
206 	 * No... let's make an inflight entry for this host, then
207 	 */
208 
209 	inf = lws_zalloc(sizeof(*inf) + hl + 1, __func__);
210 	if (!inf)
211 		return 1;
212 
213 	memcpy(&inf[1], host, hl + 1);
214 	inf->refcount = (char)ch->count;
215 	lws_dll2_add_tail(&inf->list, &wsi->a.context->jit_inflight);
216 
217 	/*
218 	 * ...kid_chain[0] AKID should indicate the right CA SKID that we want.
219 	 *
220 	 * Because of cross-signing, we check all of them and accept we may get
221 	 * multiple (the inflight accepts up to 2) CAs needed.
222 	 */
223 
224 	for (n = 0; n < ch->count; n++)
225 		wsi->a.context->system_ops->jit_trust_query(wsi->a.context,
226 			ch->akid[n].kid, (size_t)ch->akid[n].kid_len,
227 			(void *)inf);
228 
229 	return 0;
230 }
231 
232 static void
tag_to_vh_name(char * result,size_t max,uint32_t tag)233 tag_to_vh_name(char *result, size_t max, uint32_t tag)
234 {
235 	lws_snprintf(result, max, "jitt-%08X", tag);
236 }
237 
238 int
lws_tls_jit_trust_vhost_bind(struct lws_context * cx,const char * address,struct lws_vhost ** pvh)239 lws_tls_jit_trust_vhost_bind(struct lws_context *cx, const char *address,
240 			     struct lws_vhost **pvh)
241 {
242 	lws_tls_jit_cache_item_t *ci, jci;
243 	lws_tls_jit_inflight_t *inf;
244 	char vhtag[32];
245 	size_t size;
246 	int n;
247 
248 	if (lws_cache_item_get(cx->trust_cache, address, (const void **)&ci,
249 									&size))
250 		/*
251 		 * There's no cached info, we have to start from scratch on
252 		 * this one
253 		 */
254 		return 1;
255 
256 	/* gotten cache item may be evicted by jit_trust_query */
257 	jci = *ci;
258 
259 	/*
260 	 * We have some trust cache information for this host already, it tells
261 	 * us the trusted CA SKIDs we found before, and the xor tag used to name
262 	 * the vhost configured for these trust CAs in its SSL_CTX.
263 	 *
264 	 * Let's check first if the correct prepared vhost already exists, if
265 	 * so, we can just bind to that and go.
266 	 */
267 
268 	tag_to_vh_name(vhtag, sizeof(vhtag), jci.xor_tag);
269 
270 	*pvh = lws_get_vhost_by_name(cx, vhtag);
271 	if (*pvh) {
272 		lwsl_info("%s: %s -> existing %s\n", __func__, address, vhtag);
273 		/* hit, let's just use that then */
274 		return 0;
275 	}
276 
277 	/*
278 	 * ... so, we know the SKIDs of the missing CAs, but we don't have the
279 	 * DERs for them, and so no configured vhost trusting them yet.  We have
280 	 * had the DERs at some point, but we can't afford to cache them, so
281 	 * we will have to get them again.
282 	 *
283 	 * Let's make an inflight for this, it will create the vhost when it
284 	 * completes.  If syncrhronous, then it will complete before we leave
285 	 * here, otherwise it will have a life of its own until all the
286 	 * queries use the cb to succeed or fail.
287 	 */
288 
289 	size = strlen(address);
290 	inf = lws_zalloc(sizeof(*inf) + size + 1, __func__);
291 	if (!inf)
292 		return 1;
293 
294 	memcpy(&inf[1], address, size + 1);
295 	inf->refcount = (char)jci.count_skids;
296 	lws_dll2_add_tail(&inf->list, &cx->jit_inflight);
297 
298 	/*
299 	 * ...kid_chain[0] AKID should indicate the right CA SKID that we want.
300 	 *
301 	 * Because of cross-signing, we check all of them and accept we may get
302 	 * multiple (we can handle 3) CAs needed.
303 	 */
304 
305 	for (n = 0; n < jci.count_skids; n++)
306 		cx->system_ops->jit_trust_query(cx, jci.skids[n].kid,
307 						(size_t)jci.skids[n].kid_len,
308 						(void *)inf);
309 
310 	/* ... in case synchronous and it already finished the queries */
311 
312 	*pvh = lws_get_vhost_by_name(cx, vhtag);
313 	if (*pvh) {
314 		/* hit, let's just use that then */
315 		lwsl_info("%s: bind to created vhost %s\n", __func__, vhtag);
316 		return 0;
317 	} else
318 		lwsl_err("%s: unable to bind to %s\n", __func__, vhtag);
319 
320 	/* right now, nothing to offer */
321 
322 	return 1;
323 }
324 
325 void
lws_tls_jit_trust_inflight_destroy(lws_tls_jit_inflight_t * inf)326 lws_tls_jit_trust_inflight_destroy(lws_tls_jit_inflight_t *inf)
327 {
328 	int n;
329 
330 	for (n = 0; n < inf->ders; n++)
331 		lws_free_set_NULL(inf->der[n]);
332 	lws_dll2_remove(&inf->list);
333 
334 	lws_free(inf);
335 }
336 
337 static int
inflight_destroy(struct lws_dll2 * d,void * user)338 inflight_destroy(struct lws_dll2 *d, void *user)
339 {
340 	lws_tls_jit_inflight_t *inf;
341 
342 	inf = lws_container_of(d, lws_tls_jit_inflight_t, list);
343 
344 	lws_tls_jit_trust_inflight_destroy(inf);
345 
346 	return 0;
347 }
348 
349 void
lws_tls_jit_trust_inflight_destroy_all(struct lws_context * cx)350 lws_tls_jit_trust_inflight_destroy_all(struct lws_context *cx)
351 {
352 	lws_dll2_foreach_safe(&cx->jit_inflight, cx, inflight_destroy);
353 }
354 
355 static void
unref_vh_grace_cb(lws_sorted_usec_list_t * sul)356 unref_vh_grace_cb(lws_sorted_usec_list_t *sul)
357 {
358 	struct lws_vhost *vh = lws_container_of(sul, struct lws_vhost,
359 						sul_unref);
360 
361 	lwsl_info("%s: %s\n", __func__, vh->lc.gutag);
362 
363 	lws_vhost_destroy(vh);
364 }
365 
366 void
lws_tls_jit_trust_vh_start_grace(struct lws_vhost * vh)367 lws_tls_jit_trust_vh_start_grace(struct lws_vhost *vh)
368 {
369 	lwsl_info("%s: %s: unused, grace %dms\n", __func__, vh->lc.gutag,
370 			vh->context->vh_idle_grace_ms);
371 	lws_sul_schedule(vh->context, 0, &vh->sul_unref, unref_vh_grace_cb,
372 			 (lws_usec_t)vh->context->vh_idle_grace_ms *
373 								LWS_US_PER_MS);
374 }
375 
376 #if defined(_DEBUG)
377 static void
lws_tls_jit_trust_cert_info(const uint8_t * der,size_t der_len)378 lws_tls_jit_trust_cert_info(const uint8_t *der, size_t der_len)
379 {
380 	struct lws_x509_cert *x;
381 	union lws_tls_cert_info_results *u;
382 	char p = 0, buf[192 + sizeof(*u)];
383 
384 	if (lws_x509_create(&x))
385 		return;
386 
387 	if (!lws_x509_parse_from_pem(x, der, der_len)) {
388 
389 		u = (union lws_tls_cert_info_results *)buf;
390 
391 		if (!lws_x509_info(x, LWS_TLS_CERT_INFO_ISSUER_NAME, u, 192)) {
392 			lwsl_info("ISS: %s\n", u->ns.name);
393 			p = 1;
394 		}
395 		if (!lws_x509_info(x, LWS_TLS_CERT_INFO_COMMON_NAME, u, 192)) {
396 			lwsl_info("CN: %s\n", u->ns.name);
397 			p = 1;
398 		}
399 
400 		if (!p) {
401 			lwsl_err("%s: unable to get any info\n", __func__);
402 			lwsl_hexdump_err(der, der_len);
403 		}
404 	} else
405 		lwsl_err("%s: unable to load DER\n", __func__);
406 
407 	lws_x509_destroy(&x);
408 }
409 #endif
410 
411 /*
412  * This processes the JIT Trust lookup results independent of the tls backend.
413  */
414 
415 int
lws_tls_jit_trust_got_cert_cb(struct lws_context * cx,void * got_opaque,const uint8_t * skid,size_t skid_len,const uint8_t * der,size_t der_len)416 lws_tls_jit_trust_got_cert_cb(struct lws_context *cx, void *got_opaque,
417 			      const uint8_t *skid, size_t skid_len,
418 			      const uint8_t *der, size_t der_len)
419 {
420 	lws_tls_jit_inflight_t *inf = (lws_tls_jit_inflight_t *)got_opaque;
421 	struct lws_context_creation_info info;
422 	lws_tls_jit_cache_item_t jci;
423 	struct lws_vhost *v;
424 	char vhtag[20];
425 	char hit = 0;
426 	int n;
427 
428 	/*
429 	 * Before anything else, check the inf is still valid.  In the low
430 	 * probability but possible case it was reallocated to be a different
431 	 * inflight, that may cause different CA certs to apply to a connection,
432 	 * but since mbedtls will then validate the server cert using the wrong
433 	 * trusted CA, it will just cause temporary conn fail.
434 	 */
435 
436 	lws_start_foreach_dll(struct lws_dll2 *, e, cx->jit_inflight.head) {
437 		lws_tls_jit_inflight_t *i = lws_container_of(e,
438 						lws_tls_jit_inflight_t, list);
439 		if (i == inf) {
440 			hit = 1;
441 			break;
442 		}
443 
444 	} lws_end_foreach_dll(e);
445 
446 	if (!hit)
447 		/* inf has already gone */
448 		return 1;
449 
450 	inf->refcount--;
451 
452 	if (skid_len >= 4)
453 		inf->tag ^= *((uint32_t *)skid);
454 
455 	if (der && inf->ders < (int)LWS_ARRAY_SIZE(inf->der) && inf->refcount) {
456 		/*
457 		 * We have a trusted CA, but more results coming... stash it
458 		 * in heap.
459 		 */
460 
461 		inf->kid[inf->ders].kid_len = (uint8_t)((skid_len >
462 				     (uint8_t)sizeof(inf->kid[inf->ders].kid)) ?
463 				     sizeof(inf->kid[inf->ders].kid) : skid_len);
464 		memcpy(inf->kid[inf->ders].kid, skid,
465 		       inf->kid[inf->ders].kid_len);
466 
467 		inf->der[inf->ders] = lws_malloc(der_len, __func__);
468 		if (!inf->der[inf->ders])
469 			return 1;
470 		memcpy(inf->der[inf->ders], der, der_len);
471 		inf->der_len[inf->ders] = (short)der_len;
472 		inf->ders++;
473 
474 		return 0;
475 	}
476 
477 	/*
478 	 * We accept up to three valid CA, and then end the inflight early.
479 	 * Any further pending results are dropped, since we got all we could
480 	 * use.  Up to two valid CA would be held in the inflight and the other
481 	 * provided in the params.
482 	 *
483 	 * If we did not already fill up the inflight, keep waiting for any
484 	 * others expected
485 	 */
486 
487 	if (inf->refcount && inf->ders < (int)LWS_ARRAY_SIZE(inf->der))
488 		return 0;
489 
490 	if (!der && !inf->ders) {
491 		lwsl_warn("%s: no trusted CA certs matching\n", __func__);
492 
493 		goto destroy_inf;
494 	}
495 
496 	tag_to_vh_name(vhtag, sizeof(vhtag), inf->tag);
497 
498 	/*
499 	 * We have got at least one CA, it's all the CAs we're going to get,
500 	 * or that we can handle.  So we have to process and drop the inf.
501 	 *
502 	 * First let's make a cache entry with a shortish ttl, mapping the
503 	 * hostname we were trying to connect to, to the SKIDs that actually
504 	 * had trust results.  This may come in handy later when we want to
505 	 * connect to the same host again, but any vhost from before has been
506 	 * removed... we can just ask for the specific CAs to regenerate the
507 	 * vhost, without having to first fail the connection attempt to get the
508 	 * server cert.
509 	 *
510 	 * The cache entry can be evicted at any time, so it is selfcontained.
511 	 * If it's also lost, we start over with the initial failing connection
512 	 * to figure out what we need to make it work.
513 	 */
514 
515 	memset(&jci, 0, sizeof(jci));
516 
517 	jci.xor_tag = inf->tag;
518 
519 	/* copy the SKIDs from the inflight and params into the cache item */
520 
521 	for (n = 0; n < (int)LWS_ARRAY_SIZE(inf->der); n++)
522 		if (inf->kid[n].kid_len)
523 			lws_tls_kid_copy_kid(&jci.skids[jci.count_skids++],
524 						&inf->kid[n]);
525 
526 	if (skid_len) {
527 		if (skid_len > sizeof(inf->kid[0].kid))
528 			skid_len = sizeof(inf->kid[0].kid);
529 		jci.skids[jci.count_skids].kid_len = (uint8_t)skid_len;
530 		memcpy(jci.skids[jci.count_skids++].kid, skid, skid_len);
531 	}
532 
533 	lwsl_info("%s: adding cache mapping %s -> %s\n", __func__,
534 			(const char *)&inf[1], vhtag);
535 
536 	if (lws_cache_write_through(cx->trust_cache, (const char *)&inf[1],
537 				    (const uint8_t *)&jci, sizeof(jci),
538 				    lws_now_usecs() + (3600ll *LWS_US_PER_SEC),
539 				    NULL))
540 		lwsl_warn("%s: add to cache failed\n", __func__);
541 
542 	/* is there already a vhost for this commutative-xor SKID trust? */
543 
544 	if (lws_get_vhost_by_name(cx, vhtag)) {
545 		lwsl_info("%s: tag vhost %s already exists, skipping\n",
546 				__func__, vhtag);
547 		goto destroy_inf;
548 	}
549 
550 	/*
551 	 * We only end up here when we attempted a connection to this hostname.
552 	 *
553 	 * We have the identified CA trust DER(s) to hand, let's create the
554 	 * necessary vhost + prepared SSL_CTX for it to use on the retry, it
555 	 * will be used straight away if the retry comes before the idle vhost
556 	 * timeout.
557 	 *
558 	 * We also use this path in the case we have the cache entry but no
559 	 * matching vhost already existing, to create one.
560 	 */
561 
562 	memset(&info, 0, sizeof(info));
563 	info.vhost_name = vhtag;
564 	info.port = CONTEXT_PORT_NO_LISTEN;
565 	info.options = cx->options;
566 
567 	/*
568 	 * We have to create the vhost with the first valid trusted DER...
569 	 * if we have a params one, use that so the rest are all from inflight
570 	 */
571 
572 	if (der) {
573 		info.client_ssl_ca_mem = der;
574 		info.client_ssl_ca_mem_len = (unsigned int)der_len;
575 		n = 0;
576 	} else {
577 		info.client_ssl_ca_mem = inf->der[0];
578 		info.client_ssl_ca_mem_len = (unsigned int)inf->der_len[0];
579 		n = 1;
580 	}
581 
582 #if defined(_DEBUG)
583 	lws_tls_jit_trust_cert_info(info.client_ssl_ca_mem,
584 				    info.client_ssl_ca_mem_len);
585 #endif
586 
587 	info.protocols = cx->protocols_copy;
588 
589 	v = lws_create_vhost(cx, &info);
590 	if (!v)
591 		lwsl_err("%s: failed to create vh %s\n", __func__, vhtag);
592 
593 	v->grace_after_unref = 1;
594 	lws_tls_jit_trust_vh_start_grace(v);
595 
596 	/*
597 	 * Do we need to add more trusted certs from inflight?
598 	 */
599 
600 	while (n < inf->ders) {
601 
602 #if defined(_DEBUG)
603 		lws_tls_jit_trust_cert_info(inf->der[n],
604 					    (size_t)inf->der_len[n]);
605 #endif
606 
607 		if (lws_tls_client_vhost_extra_cert_mem(v, inf->der[n],
608 						(size_t)inf->der_len[n]))
609 			lwsl_err("%s: add extra cert failed\n", __func__);
610 		n++;
611 	}
612 
613 	lwsl_info("%s: created jitt %s -> vh %s\n", __func__,
614 				(const char *)&inf[1], vhtag);
615 
616 destroy_inf:
617 	lws_tls_jit_trust_inflight_destroy(inf);
618 
619 	return 0;
620 }
621 
622 /*
623  * Refer to ./READMEs/README.jit-trust.md for blob layout specification
624  */
625 
626 int
lws_tls_jit_trust_blob_queury_skid(const void * _blob,size_t blen,const uint8_t * skid,size_t skid_len,const uint8_t ** prpder,size_t * prder_len)627 lws_tls_jit_trust_blob_queury_skid(const void *_blob, size_t blen,
628 				   const uint8_t *skid, size_t skid_len,
629 				   const uint8_t **prpder, size_t *prder_len)
630 {
631 	const uint8_t *pskidlen, *pskids, *pder, *blob = (uint8_t *)_blob;
632 	const uint16_t *pderlen;
633 	int certs;
634 
635 	/* sanity check blob length and magic */
636 
637 	if (blen < 32768 ||
638 	   lws_ser_ru32be(blob) != LWS_JIT_TRUST_MAGIC_BE ||
639 	   lws_ser_ru32be(blob + LJT_OFS_END) != blen) {
640 		lwsl_err("%s: blob not sane\n", __func__);
641 
642 		return -1;
643 	}
644 
645 	if (!skid_len)
646 		return 1;
647 
648 	/* point into the various sub-tables */
649 
650 	certs		= (int)lws_ser_ru16be(blob + LJT_OFS_32_COUNT_CERTS);
651 
652 	pderlen		= (uint16_t *)(blob + lws_ser_ru32be(blob +
653 							LJT_OFS_32_DERLEN));
654 	pskidlen	= blob + lws_ser_ru32be(blob + LJT_OFS_32_SKIDLEN);
655 	pskids		= blob + lws_ser_ru32be(blob + LJT_OFS_32_SKID);
656 	pder		= blob + LJT_OFS_DER;
657 
658 	/* check each cert SKID in turn, return the DER if found */
659 
660 	while (certs--) {
661 
662 		/* paranoia / sanity */
663 
664 		assert(pskids < blob + blen);
665 		assert(pder < blob + blen);
666 		assert(pskidlen < blob + blen);
667 		assert((uint8_t *)pderlen < blob + blen);
668 
669 		/* we will accept to match on truncated SKIDs */
670 
671 		if (*pskidlen >= skid_len &&
672 		    !memcmp(skid, pskids, skid_len)) {
673 			/*
674 			 * We found a trusted CA cert of the right SKID
675 			 */
676 		        *prpder = pder;
677 		        *prder_len = lws_ser_ru16be((uint8_t *)pderlen);
678 
679 		        return 0;
680 		}
681 
682 		pder += lws_ser_ru16be((uint8_t *)pderlen);
683 		pskids += *pskidlen;
684 		pderlen++;
685 		pskidlen++;
686 	}
687 
688 	return 1;
689 }
690