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 * cose_sign handling
25 *
26 * Validation:
27 *
28 * - we put all our pieces and results in an lwsac in the parse state object
29 *
30 * - we collect pieces needed for sig validation into lwsac elements
31 *
32 * - we go through each signature making discrete results in the lwsac for
33 * the user code to assess
34 */
35
36 #include "private-lib-core.h"
37 #include "private-lib-cose.h"
38
39 const uint8_t *sig_mctx[] = { (uint8_t *)"",
40 (uint8_t *)"\x85\x69""Signature",
41 (uint8_t *)"\x84\x6a""Signature1",
42 (uint8_t *)"\x85\x6f""CounterSignature",
43 (uint8_t *)"\x84\x63""MAC",
44 (uint8_t *)"\x84\x64""MAC0",
45 };
46 uint8_t sig_mctx_len[] = { 0, 11, 12, 17, 5, 6 };
47
48 struct alg_names {
49 const char *name;
50 cose_param_t alg;
51 } alg_names[] = {
52 { "ES256", LWSCOSE_WKAECDSA_ALG_ES256 },
53 { "ES384", LWSCOSE_WKAECDSA_ALG_ES384 },
54 { "ES512", LWSCOSE_WKAECDSA_ALG_ES512 },
55 { "HS256_64", LWSCOSE_WKAHMAC_256_64 },
56 { "HS256", LWSCOSE_WKAHMAC_256_256 },
57 { "HS384", LWSCOSE_WKAHMAC_384_384 },
58 { "HS512", LWSCOSE_WKAHMAC_512_512 },
59 { "RS256", LWSCOSE_WKARSA_ALG_RS256 },
60 { "RS384", LWSCOSE_WKARSA_ALG_RS384 },
61 { "RS512", LWSCOSE_WKARSA_ALG_RS512 },
62 };
63
64 /*
65 * The Sig_structure plaintext is new temp CBOR made up from pieces from the
66 * cose_sign, cose_signature, and payload in a specific order
67 *
68 * tstr context string
69 * bstr 0-len or protected body headers
70 * bstr (Missing for sign1) 0-len or protected signer headers
71 * bstr 0-len or protected application part
72 * bstr the payload
73 *
74 * We are getting CBOR with an optional outer tag and then an array of exactly
75 * 4 items in a fixed order
76 *
77 * [
78 * protected headers: bstr containing a map (captured as CBOR in cps->ph[])
79 * unprotected: map: for sign1, eg, the alg (!?), the kid
80 * payload: bstr
81 * if sign: signatures: [ cose_signature struct array,
82 * each is a 3-element array
83 * [
84 * protected: bstr containing a map: (eg, the alg) (captured as CBOR)
85 * unprotected: map: (eg, the kid)
86 * signature: bstr
87 * ]
88 * if sign1: bstr containing signature
89 * ]
90 *
91 * The last signatures field may be an array of signatures, or a single
92 * cose_signature object for cose_sign1.
93 *
94 * For cose_sign1, we know the signature alg before the payload and can do it
95 * in a single pass. But for sign, we do not know the signature algs until
96 * after the payload, which is an unfortunate oversight in cose_sign, meaning we
97 * cannot hash the payload one or more ways in a single pass.
98 */
99
100 #if defined(VERBOSE)
101 const char *cose_sections[] = {
102 "ST_UNKNOWN",
103
104 "ST_OUTER_PROTECTED",
105 "ST_OUTER_UNPROTECTED",
106 "ST_OUTER_PAYLOAD",
107 "ST_OUTER_SIGN1_SIGNATURE",
108
109 "ST_OUTER_SIGN_SIGARRAY",
110
111 "ST_OUTER_MACTAG",
112
113 "ST_INNER_PROTECTED",
114 "ST_INNER_UNPROTECTED",
115 "ST_INNER_SIGNATURE",
116
117 "ST_INNER_EXCESS",
118 };
119 #endif
120
121 const char *
lws_cose_alg_to_name(cose_param_t alg)122 lws_cose_alg_to_name(cose_param_t alg)
123 {
124 size_t n;
125
126 for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++)
127 if (alg_names[n].alg == alg)
128 return alg_names[n].name;
129
130 return "unknown_alg";
131 }
132
133 cose_param_t
lws_cose_name_to_alg(const char * name)134 lws_cose_name_to_alg(const char *name)
135 {
136 size_t n;
137
138 for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++)
139 if (!strcmp(alg_names[n].name, name))
140 return alg_names[n].alg;
141
142 return 0;
143 }
144
145 static size_t
bstr_len(uint8_t * t,size_t buflen,uint8_t opcode,uint64_t len)146 bstr_len(uint8_t *t, size_t buflen, uint8_t opcode, uint64_t len)
147 {
148 uint8_t *ot = t;
149
150 if (buflen < 9)
151 return 0;
152
153 if (len < 24) {
154 *t = (uint8_t)(opcode | len);
155
156 return 1;
157 }
158 if (len < 256) {
159 *t++ = opcode | LWS_CBOR_1;
160 goto b;
161 }
162 if (len < 65536) {
163 *t++ = opcode | LWS_CBOR_2;
164 goto b1;
165 }
166 if (len < 0xffffffffu) {
167 *t++ = opcode | LWS_CBOR_4;
168 goto b2;
169 }
170
171 *t++ = opcode | LWS_CBOR_8;
172
173 *t++ = (uint8_t)(len >> 56);
174 *t++ = (uint8_t)(len >> 48);
175 *t++ = (uint8_t)(len >> 40);
176 *t++ = (uint8_t)(len >> 32);
177
178 b2:
179 *t++ = (uint8_t)(len >> 24);
180 *t++ = (uint8_t)(len >> 16);
181 b1:
182 *t++ = (uint8_t)(len >> 8);
183 b:
184 *t++ = (uint8_t)len;
185
186 return lws_ptr_diff_size_t(t, ot);
187 }
188
189 static int
apply_external(struct lws_cose_validate_context * cps)190 apply_external(struct lws_cose_validate_context *cps)
191 {
192 lws_cose_sig_alg_t *alg;
193 uint8_t t[9];
194
195 alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list);
196 if (!alg)
197 /* expected if no key */
198 return 0;
199
200 /* get the external payload first, if any indicated */
201
202 if (cps->info.ext_len) {
203 lws_cose_sig_ext_pay_t ex;
204 size_t s;
205
206 s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR,
207 cps->info.ext_len);
208 if (lws_cose_val_alg_hash(alg, t, s))
209 return 1;
210
211 memset(&ex, 0, sizeof(ex));
212 ex.cps = cps;
213
214 do {
215 int n;
216
217 ex.xl = 0;
218 n = cps->info.ext_cb(&ex);
219
220 if (ex.xl &&
221 lws_cose_val_alg_hash(alg, ex.ext, ex.xl))
222 return 1;
223
224 if (n == LCOSESIGEXTCB_RET_ERROR)
225 return 1;
226
227 if (n == LCOSESIGEXTCB_RET_FINISHED)
228 break;
229 } while (1);
230 }
231
232 return 0;
233 }
234
235 static int
create_alg(struct lecp_ctx * ctx,struct lws_cose_validate_context * cps)236 create_alg(struct lecp_ctx *ctx, struct lws_cose_validate_context *cps)
237 {
238 lws_cose_validate_param_stack_t *sl = &cps->st[cps->sp], *sl0 = &cps->st[0];
239 lws_cose_validate_res_t *res;
240 lws_cose_sig_alg_t *alg;
241 lws_cose_key_t *ck;
242 uint8_t *p;
243 size_t s;
244
245 /* with sign1, we can hash the payload in a
246 * single pass */
247
248 ck = lws_cose_key_from_set(cps->info.keyset, sl->kid.buf, sl->kid.len);
249 if (!ck) {
250 lwsl_notice("%s: no key\n", __func__);
251 lwsl_hexdump_notice(sl->kid.buf, sl->kid.len);
252 goto no_key_or_alg;
253 }
254
255 // lwsl_notice("%s: cps->alg %d\n", __func__, (int)cps->alg);
256
257 alg = lws_cose_val_alg_create(cps->info.cx, ck, cps->st[0].alg,
258 LWSCOSE_WKKO_VERIFY);
259 if (!alg) {
260 lwsl_info("%s: no alg\n", __func__);
261
262 no_key_or_alg:
263 /*
264 * We can't create the alg then, so we can't normally
265 * create a result object. Create one especially for this
266 * case and continue on
267 */
268
269 res = lws_zalloc(sizeof(*res), __func__);
270 if (res) {
271 res->result = -1001;
272
273 lws_dll2_add_tail(&res->list, &cps->results);
274 }
275
276 return 0;
277 }
278
279 lws_dll2_add_tail(&alg->list, &cps->algs);
280
281 /*
282 * Hash step 1: The first hash content depends on
283 * sign/sign1/csign/mac/mac0 constant bstr
284 */
285
286 if (lws_cose_val_alg_hash(alg, sig_mctx[cps->info.sigtype],
287 sig_mctx_len[cps->info.sigtype]))
288 goto bail;
289
290 /*
291 * Hash step 2: A zero-length bstr, or a copy of the
292 * OUTER protected headers
293 *
294 * A zero-entry map alone becomes a zero-
295 * length bstr
296 */
297
298 if (sl0->ph_pos[0] < 2) {
299 /* nothing to speak of */
300 sl0->ph[0][0] = LWS_CBOR_MAJTYP_BSTR;
301 p = &sl0->ph[0][0];
302 s = 1;
303 } else {
304 if (sl0->ph_pos[0] < 24) {
305 sl0->ph[0][2] = (uint8_t)
306 (LWS_CBOR_MAJTYP_BSTR | sl0->ph_pos[0]);
307 p = &sl0->ph[0][2];
308 s = (size_t)sl0->ph_pos[0] + 1;
309 } else {
310 sl0->ph[0][1] = LWS_CBOR_MAJTYP_BSTR |
311 LWS_CBOR_1;
312 sl0->ph[0][2] = (uint8_t)sl0->ph_pos[0];
313 p = &sl0->ph[0][1];
314 s = (size_t)sl0->ph_pos[0] + 2;
315 }
316 }
317
318 if (lws_cose_val_alg_hash(alg, p, s))
319 goto bail;
320
321 /*
322 * Hash step 3: Protected signer headers (Elided for sign1)
323 */
324
325 if (cps->info.sigtype == SIGTYPE_MULTI) {
326 if (sl->ph_pos[2] < 2) {
327 /* nothing to speak of */
328 sl->ph[2][0] = LWS_CBOR_MAJTYP_BSTR;
329 p = &sl->ph[2][0];
330 s = 1;
331 } else {
332 if (sl->ph_pos[2] < 24) {
333 sl->ph[2][2] = (uint8_t)
334 (LWS_CBOR_MAJTYP_BSTR | sl->ph_pos[2]);
335 p = &sl->ph[2][2];
336 s = (size_t)sl->ph_pos[2] + 1;
337 } else {
338 sl->ph[2][1] = LWS_CBOR_MAJTYP_BSTR |
339 LWS_CBOR_1;
340 sl->ph[2][2] = (uint8_t)sl->ph_pos[2];
341 p = &sl->ph[2][1];
342 s = (size_t)sl->ph_pos[2] + 2;
343 }
344 }
345
346 if (lws_cose_val_alg_hash(alg, p, s))
347 goto bail;
348 }
349
350 /* Hash step 4: bstr for applictation protected pieces
351 * empty for now
352 */
353
354 if (!cps->info.ext_len) { /* ie, if no app data */
355 uint8_t u = LWS_CBOR_MAJTYP_BSTR;
356 if (lws_cose_val_alg_hash(alg, &u, 1))
357 goto bail;
358 }
359
360 /*
361 * The final part is the payload in its own bstr, as
362 * we get it if sign1, else replayed from a cache in heap
363 */
364
365 if (cps->info.sigtype == SIGTYPE_SINGLE)
366 return 0;
367
368 if (!cps->payload_stash) {
369 lwsl_notice("%s: no payload stash\n", __func__);
370 goto bail;
371 }
372
373 apply_external(cps);
374
375 if (lws_cose_val_alg_hash(alg, cps->payload_stash, cps->payload_pos))
376 goto bail;
377 lwsl_notice("a %d\n", (int)cps->sig_agg_pos);
378
379 lws_cose_val_alg_destroy(cps, &alg, (const uint8_t *)cps->sig_agg,
380 cps->sig_agg_pos);
381
382 return 0;
383
384 bail:
385 return 1;
386 }
387
388 #if defined(VERBOSE)
389 static const char * const reason_names[] = {
390 "LECPCB_CONSTRUCTED",
391 "LECPCB_DESTRUCTED",
392 "LECPCB_START",
393 "LECPCB_COMPLETE",
394 "LECPCB_FAILED",
395 "LECPCB_PAIR_NAME",
396 "LECPCB_VAL_TRUE",
397 "LECPCB_VAL_FALSE",
398 "LECPCB_VAL_NULL",
399 "LECPCB_VAL_NUM_INT",
400 "LECPCB_VAL_RESERVED", /* float in lejp */
401 "LECPCB_VAL_STR_START",
402 "LECPCB_VAL_STR_CHUNK",
403 "LECPCB_VAL_STR_END",
404 "LECPCB_ARRAY_START",
405 "LECPCB_ARRAY_END",
406 "LECPCB_OBJECT_START",
407 "LECPCB_OBJECT_END",
408 "LECPCB_TAG_START",
409 "LECPCB_TAG_END",
410 "LECPCB_VAL_NUM_UINT",
411 "LECPCB_VAL_UNDEFINED",
412 "LECPCB_VAL_FLOAT16",
413 "LECPCB_VAL_FLOAT32",
414 "LECPCB_VAL_FLOAT64",
415 "LECPCB_VAL_SIMPLE",
416 "LECPCB_VAL_BLOB_START",
417 "LECPCB_VAL_BLOB_CHUNK",
418 "LECPCB_VAL_BLOB_END",
419 "LECPCB_ARRAY_ITEM_START",
420 "LECPCB_ARRAY_ITEM_END",
421 "LECPCB_LITERAL_CBOR"
422 };
423 #endif
424
425 static int
ph_index(struct lws_cose_validate_context * cps)426 ph_index(struct lws_cose_validate_context *cps)
427 {
428 switch (cps->tli) {
429 case ST_OUTER_PROTECTED:
430 return 0;
431 case ST_OUTER_UNPROTECTED:
432 return 1;
433 case ST_INNER_PROTECTED:
434 return 2;
435 case ST_INNER_UNPROTECTED:
436 return 3;
437 }
438
439 assert(0);
440 return 0;
441 }
442
443 static signed char
cb_cose_sig(struct lecp_ctx * ctx,char reason)444 cb_cose_sig(struct lecp_ctx *ctx, char reason)
445 {
446 struct lws_cose_validate_context *cps =
447 (struct lws_cose_validate_context *)ctx->user;
448 lws_cose_validate_param_stack_t *sl;
449 struct lws_gencrypto_keyelem *ke;
450 lws_cose_sig_alg_t *alg;
451 uint8_t t[9];
452 size_t s;
453 int hi;
454
455 #if defined(VERBOSE)
456 lwsl_notice("%s: %s, tli %s, sub %d, ppos %d, sp %d\n", __func__,
457 reason_names[reason & 0x1f], cose_sections[cps->tli],
458 cps->sub, ctx->pst[ctx->pst_sp].ppos, cps->sp);
459 #endif
460
461 switch (reason) {
462 case LECPCB_CONSTRUCTED:
463 break;
464
465 case LECPCB_TAG_START:
466
467 lwsl_notice("%s: tag sigtype %d\n", __func__, cps->info.sigtype);
468
469 switch (cps->info.sigtype) {
470 default:
471 assert(0);
472 break;
473 case SIGTYPE_UNKNOWN:
474 /* it means use the tag value to set the type */
475 switch (ctx->item.u.u64) {
476 case LWSCOAP_CONTENTFORMAT_COSE_SIGN:
477 cps->info.sigtype = SIGTYPE_MULTI;
478 break;
479 case LWSCOAP_CONTENTFORMAT_COSE_SIGN1:
480 cps->info.sigtype = SIGTYPE_SINGLE;
481 break;
482 // case LWSCOAP_CONTENTFORMAT_COSE_SIGN__:
483 // cps->info.sigtype = SIGTYPE_COUNTERSIGNED;
484 // break;
485 case LWSCOAP_CONTENTFORMAT_COSE_MAC0:
486 cps->info.sigtype = SIGTYPE_MAC0;
487 break;
488 case LWSCOAP_CONTENTFORMAT_COSE_MAC:
489 cps->info.sigtype = SIGTYPE_MAC;
490 break;
491 default:
492 goto unexpected_tag;
493 }
494 break;
495 case SIGTYPE_MULTI:
496 if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN)
497 goto unexpected_tag;
498 break;
499 case SIGTYPE_SINGLE:
500 if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN1)
501 goto unexpected_tag;
502 break;
503 case SIGTYPE_COUNTERSIGNED:
504 if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN)
505 goto unexpected_tag;
506 break;
507 case SIGTYPE_MAC0:
508 if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC0)
509 goto unexpected_tag;
510 break;
511 case SIGTYPE_MAC:
512 if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC) {
513 unexpected_tag:
514 lwsl_warn("%s: unexpected tag %d\n", __func__,
515 (int)ctx->item.u.u64);
516 goto bail;
517 }
518 break;
519 }
520
521 cps->depth++;
522 break;
523
524 case LECPCB_ARRAY_ITEM_START:
525
526 if (cps->sub)
527 break;
528
529 if (ctx->pst[ctx->pst_sp].ppos == 4 ||
530 ctx->pst[ctx->pst_sp].ppos == 6) {
531 switch (cps->tli) {
532 case ST_INNER_UNPROTECTED:
533 case ST_INNER_PROTECTED:
534 hi = ph_index(cps);
535 sl = &cps->st[cps->sp];
536 sl->ph_pos[hi] = 0;
537 lecp_parse_report_raw(ctx, 1);
538 break;
539 default:
540 break;
541 }
542 break;
543 }
544
545 if (ctx->pst[ctx->pst_sp].ppos != 2)
546 break;
547
548 switch (cps->tli) {
549 case ST_OUTER_UNPROTECTED:
550 case ST_OUTER_PROTECTED:
551 /*
552 * Holy type confusion, Batman... this is a CBOR bstr
553 * containing valid CBOR that must also be parsed as
554 * part of the containing array... we need to collect
555 * it anyway since it is part of the signing plaintext
556 * in bstr form, let's get it and then parse it at the
557 * END of the bstr.
558 */
559 lecp_parse_report_raw(ctx, 1);
560 break;
561
562 case ST_OUTER_PAYLOAD:
563 if (cps->info.sigtype != SIGTYPE_SINGLE)
564 break;
565
566 if (create_alg(ctx, cps))
567 goto bail;
568
569 break;
570
571 case ST_OUTER_SIGN_SIGARRAY:
572 cps->tli = ST_INNER_PROTECTED;
573 break;
574 }
575 break;
576
577 case LECPCB_ARRAY_ITEM_END:
578
579 if (cps->sub)
580 break;
581
582 if (ctx->pst[ctx->pst_sp].ppos == 2) {
583 sl = &cps->st[cps->sp];
584 switch (cps->tli) {
585 case ST_OUTER_UNPROTECTED:
586 break;
587 /* fallthru */
588 case ST_OUTER_PROTECTED:
589 lecp_parse_report_raw(ctx, 0);
590
591 hi = ph_index(cps);
592
593 if (!sl->ph_pos[hi] || cps->sub)
594 break;
595
596 cps->sub = 1;
597 s = (size_t)sl->ph_pos[hi];
598
599 if (lecp_parse_subtree(&cps->ctx,
600 sl->ph[hi] + 3, s) !=
601 LECP_CONTINUE)
602 goto bail;
603 cps->sub = 0;
604 break;
605
606 case ST_OUTER_PAYLOAD:
607 switch (cps->info.sigtype) {
608 case SIGTYPE_MULTI:
609 cps->tli = ST_OUTER_SIGN_SIGARRAY - 1;
610 break;
611 case SIGTYPE_MAC:
612 case SIGTYPE_MAC0:
613 cps->tli = ST_OUTER_MACTAG - 1;
614 break;
615 case SIGTYPE_COUNTERSIGNED:
616 break;
617 default:
618 break;
619 }
620 break;
621
622 case ST_OUTER_SIGN1_SIGNATURE:
623 case ST_OUTER_MACTAG:
624 cps->sp++;
625 cps->tli = ST_INNER_PROTECTED - 1;
626 break;
627
628 case ST_INNER_UNPROTECTED:
629 lwsl_notice("ST_INNER_UNPROTECTED end\n");
630 break;
631 case ST_INNER_PROTECTED:
632 lwsl_notice("ST_INNER_PROTECTED end\n");
633 break;
634
635 case ST_INNER_EXCESS:
636 case ST_OUTER_SIGN_SIGARRAY:
637 cps->tli--; /* so no change */
638 break;
639 }
640 if (!cps->sub)
641 cps->tli++;
642 }
643
644 if (ctx->pst[ctx->pst_sp].ppos >= 4) {
645 uint8_t *p;
646 uint8_t u;
647 size_t s1;
648
649 switch (cps->tli) {
650 case ST_INNER_UNPROTECTED:
651 case ST_INNER_PROTECTED:
652
653 hi = ph_index(cps);
654 sl = &cps->st[cps->sp];
655 p = sl->ph[hi] + 3;
656 lecp_parse_report_raw(ctx, 0);
657
658 if (!sl->ph_pos[hi] || cps->sub) {
659 if (!cps->sub)
660 cps->tli++;
661 break;
662 }
663
664 cps->sub = 1;
665 s = (size_t)sl->ph_pos[hi];
666
667 /*
668 * somehow the raw captures the
669 * initial BSTR container length,
670 * let's strip it
671 */
672
673 u = (*p) & LWS_CBOR_SUBMASK;
674 if (((*p) & LWS_CBOR_MAJTYP_MASK) ==
675 LWS_CBOR_MAJTYP_BSTR) {
676 s1 = 1;
677 if (u == LWS_CBOR_1)
678 s1 = 2;
679 else if (u == LWS_CBOR_2)
680 s1 = 3;
681 else if (u == LWS_CBOR_4)
682 s1 = 5;
683 else if (u == LWS_CBOR_8)
684 s1 = 9;
685
686 if (s1 > s)
687 goto bail;
688
689 sl->ph_pos[hi] = (int)
690 (sl->ph_pos[hi] - (ssize_t)s1);
691 s = s - s1;
692 memmove(p, p + s1, s);
693 }
694
695 if (lecp_parse_subtree(&cps->ctx, p, s) !=
696 LECP_CONTINUE)
697 goto bail;
698
699 cps->sub = 0;
700
701 if (!cps->sub)
702 cps->tli++;
703 break;
704
705 case ST_INNER_SIGNATURE:
706 if (cps->info.sigtype == SIGTYPE_MAC) {
707 // lwsl_err("Y: alg %d\n", (int)cps->alg);
708 if (create_alg(ctx, cps))
709 goto bail;
710 }
711 cps->tli++;
712 break;
713 default:
714 break;
715 }
716 }
717
718 break;
719
720 case LECPCB_VAL_NUM_INT:
721 case LECPCB_VAL_NUM_UINT:
722 switch (cps->tli) {
723 case ST_INNER_PROTECTED:
724 case ST_INNER_UNPROTECTED:
725 case ST_INNER_SIGNATURE:
726 case ST_OUTER_PROTECTED:
727 case ST_OUTER_UNPROTECTED:
728 if (lecp_parse_map_is_key(ctx)) {
729 cps->map_key = ctx->item.u.i64;
730 // lwsl_notice("%s: key %d\n", __func__, (int)cps->map_key);
731 break;
732 }
733
734 // lwsl_notice("%s: key %d val %d\n", __func__, (int)cps->map_key, (int)ctx->item.u.i64);
735
736 if (cps->map_key == LWSCOSE_WKL_ALG) {
737 sl = &cps->st[cps->sp];
738 cps->map_key = 0;
739 if (cps->tli == ST_INNER_PROTECTED ||
740 cps->tli == ST_INNER_UNPROTECTED ||
741 cps->tli == ST_INNER_SIGNATURE) {
742 sl->alg = ctx->item.u.i64;
743 if (!cps->st[0].alg)
744 cps->st[0].alg = sl->alg;
745 } else
746 sl->alg = ctx->item.u.i64;
747 break;
748 }
749 break;
750 }
751 break;
752
753 case LECPCB_VAL_STR_END:
754 switch (cps->tli) {
755 case ST_OUTER_UNPROTECTED:
756 break;
757 }
758 break;
759
760 case LECPCB_VAL_BLOB_START:
761
762 lwsl_notice("%s: blob size %d\n", __func__, (int)ctx->item.u.u64);
763
764 if (cps->tli == ST_OUTER_SIGN1_SIGNATURE ||
765 cps->tli == ST_INNER_SIGNATURE) {
766 if (ctx->item.u.u64 > sizeof(cps->sig_agg))
767 goto bail;
768 cps->sig_agg_pos = 0;
769 break;
770 }
771
772 if (cps->tli != ST_OUTER_PAYLOAD)
773 break;
774
775 if (apply_external(cps)) {
776 lwsl_notice("%s: ext\n", __func__);
777 goto bail;
778 }
779
780 s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR,
781 ctx->item.u.u64);
782
783 if (cps->info.sigtype == SIGTYPE_SINGLE) {
784 alg = lws_container_of(cps->algs.head,
785 lws_cose_sig_alg_t, list);
786 if (!alg)
787 /* expected if no key */
788 break;
789 if (lws_cose_val_alg_hash(alg, t, s)) {
790 lwsl_notice("%s: hash failed\n", __func__);
791 goto bail;
792 }
793
794 break;
795 }
796
797 cps->payload_stash_size = (size_t)(ctx->item.u.u64 + s);
798 cps->payload_stash = lws_malloc(cps->payload_stash_size,
799 __func__);
800 if (!cps->payload_stash) {
801 lwsl_notice("%s: oom\n", __func__);
802 goto bail;
803 }
804
805 memcpy(cps->payload_stash, t, s);
806 cps->payload_pos = s;
807
808 break;
809
810 case LECPCB_VAL_BLOB_CHUNK:
811 switch (cps->tli) {
812 case ST_OUTER_PAYLOAD:
813
814 if (cps->info.pay_cb && ctx->npos)
815 cps->info.pay_cb(cps, cps->info.pay_opaque,
816 (uint8_t *)ctx->buf, ctx->npos);
817
818 if (cps->payload_stash) {
819 if (cps->payload_pos + ctx->npos >
820 cps->payload_stash_size)
821 goto bail;
822 memcpy(cps->payload_stash + cps->payload_pos,
823 ctx->buf, ctx->npos);
824 cps->payload_pos += ctx->npos;
825 break;
826 }
827 alg = lws_container_of(cps->algs.head,
828 lws_cose_sig_alg_t, list);
829 if (!alg)
830 /* expected if no key */
831 break;
832 if (ctx->npos &&
833 lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf,
834 ctx->npos)) {
835 lwsl_notice("%s: chunk fail\n", __func__);
836 goto bail;
837 }
838 break;
839 case ST_INNER_SIGNATURE:
840 case ST_OUTER_SIGN1_SIGNATURE:
841 /* the sig is big compared to ctx->buf... we need to
842 * stash it then */
843 memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf,
844 ctx->npos);
845 cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos;
846 break;
847 }
848 break;
849
850 case LECPCB_VAL_BLOB_END:
851 switch (cps->tli) {
852
853 case ST_INNER_SIGNATURE:
854 if (cps->info.sigtype == SIGTYPE_MULTI) {
855 memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf,
856 ctx->npos);
857 cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos;
858 // lwsl_err("Y: alg %d\n", (int)cps->alg);
859 if (create_alg(ctx, cps))
860 goto bail;
861 break;
862 }
863 if (cps->info.sigtype != SIGTYPE_MAC)
864 break;
865 /* fallthru */
866 case ST_OUTER_PROTECTED:
867 case ST_OUTER_UNPROTECTED:
868 case ST_INNER_PROTECTED:
869 case ST_INNER_UNPROTECTED:
870 if (cps->map_key == LWSCOSE_WKL_KID) {
871 sl = &cps->st[cps->sp];
872 ke = &sl->kid;
873 if (ke->buf)
874 lws_free(ke->buf);
875 ke->buf = lws_malloc(ctx->npos, __func__);
876 if (!ke->buf)
877 goto bail;
878 ke->len = ctx->npos;
879 memcpy(ke->buf, ctx->buf, ctx->npos);
880 cps->map_key = 0;
881 }
882 break;
883
884 case ST_OUTER_PAYLOAD:
885 if (cps->info.pay_cb && ctx->npos)
886 cps->info.pay_cb(cps, cps->info.pay_opaque,
887 (uint8_t *)ctx->buf, ctx->npos);
888 if (cps->payload_stash) {
889 if (cps->payload_pos + ctx->npos >
890 cps->payload_stash_size)
891 goto bail;
892 memcpy(cps->payload_stash + cps->payload_pos,
893 ctx->buf, ctx->npos);
894 cps->payload_pos += ctx->npos;
895 break;
896 }
897 alg = lws_container_of(cps->algs.head,
898 lws_cose_sig_alg_t, list);
899 if (!alg)
900 /* expected if no key */
901 break;
902
903 if (ctx->npos &&
904 lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf,
905 ctx->npos))
906 goto bail;
907 break;
908
909 case ST_OUTER_SIGN1_SIGNATURE:
910 if (cps->info.sigtype == SIGTYPE_MULTI)
911 break;
912
913 memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf,
914 ctx->npos);
915 cps->sig_agg_pos += ctx->npos;
916
917 alg = lws_container_of(cps->algs.head,
918 lws_cose_sig_alg_t, list);
919 lwsl_notice("b\n");
920 if (alg)
921 lws_cose_val_alg_destroy(cps, &alg,
922 cps->sig_agg,
923 cps->sig_agg_pos);
924 break;
925
926 case ST_OUTER_MACTAG:
927 if (cps->mac_pos + ctx->npos > sizeof(cps->mac))
928 goto bail;
929 memcpy(cps->mac + cps->mac_pos, ctx->buf, ctx->npos);
930 cps->mac_pos += ctx->npos;
931
932 if (cps->info.sigtype == SIGTYPE_MAC0) {
933 if (create_alg(ctx, cps))
934 goto bail;
935 }
936
937 break;
938 }
939 break;
940
941 case LECPCB_LITERAL_CBOR:
942 /* only used for protected headers */
943 switch (cps->tli) {
944 case ST_INNER_PROTECTED:
945 case ST_OUTER_PROTECTED:
946 case ST_INNER_UNPROTECTED:
947 case ST_OUTER_UNPROTECTED:
948 sl = &cps->st[cps->sp];
949 hi = ph_index(cps);
950 if (sl->ph_pos[hi] + 3 + ctx->cbor_pos >
951 (int)sizeof(sl->ph[hi]) - 3)
952 /* more protected cbor than we can handle */
953 goto bail;
954 memcpy(sl->ph[hi] + 3 + sl->ph_pos[hi], ctx->cbor,
955 ctx->cbor_pos);
956 sl->ph_pos[hi] += ctx->cbor_pos;
957 break;
958 }
959 }
960
961 return 0;
962
963 bail:
964
965 return -1;
966 }
967
968 struct lws_cose_validate_context *
lws_cose_validate_create(const lws_cose_validate_create_info_t * info)969 lws_cose_validate_create(const lws_cose_validate_create_info_t *info)
970 {
971 struct lws_cose_validate_context *cps;
972
973 /* you have to provide at least one key in a cose_keyset */
974 assert(info->keyset);
975 /* you have to provide an lws_context (for crypto random) */
976 assert(info->cx);
977
978 cps = lws_zalloc(sizeof(*cps), __func__);
979 if (!cps)
980 return NULL;
981
982 cps->info = *info;
983 cps->tli = ST_OUTER_PROTECTED;
984
985 lecp_construct(&cps->ctx, cb_cose_sig, cps, NULL, 0);
986
987 return cps;
988 }
989
990 int
lws_cose_validate_chunk(struct lws_cose_validate_context * cps,const uint8_t * in,size_t in_len,size_t * used_in)991 lws_cose_validate_chunk(struct lws_cose_validate_context *cps,
992 const uint8_t *in, size_t in_len, size_t *used_in)
993 {
994 int n;
995
996 n = lecp_parse(&cps->ctx, in, in_len);
997 if (used_in)
998 *used_in = cps->ctx.used_in;
999
1000 if (n == LECP_CONTINUE)
1001 return LECP_CONTINUE;
1002
1003 lecp_destruct(&cps->ctx);
1004
1005 return n;
1006 }
1007
1008 lws_dll2_owner_t *
lws_cose_validate_results(struct lws_cose_validate_context * cps)1009 lws_cose_validate_results(struct lws_cose_validate_context *cps)
1010 {
1011 return &cps->results;
1012 }
1013
1014 void
lws_cose_validate_destroy(struct lws_cose_validate_context ** _cps)1015 lws_cose_validate_destroy(struct lws_cose_validate_context **_cps)
1016 {
1017 struct lws_cose_validate_context *cps = *_cps;
1018
1019 if (!cps)
1020 return;
1021
1022 lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
1023 lws_dll2_get_head(&cps->algs)) {
1024 lws_cose_sig_alg_t *alg = lws_container_of(p,
1025 lws_cose_sig_alg_t, list);
1026
1027 lws_dll2_remove(p);
1028 lws_cose_val_alg_destroy(cps, &alg, NULL, 0);
1029 } lws_end_foreach_dll_safe(p, tp);
1030
1031 lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
1032 lws_dll2_get_head(&cps->results)) {
1033 lws_cose_validate_res_t *res = lws_container_of(p,
1034 lws_cose_validate_res_t, list);
1035
1036 lws_dll2_remove(p);
1037 lws_free(res);
1038 } lws_end_foreach_dll_safe(p, tp);
1039
1040 lws_free_set_NULL(cps->payload_stash);
1041
1042 lwsac_free(&cps->ac);
1043
1044 while (cps->sp >= 0) {
1045 if (cps->st[cps->sp].kid.buf)
1046 lws_free(cps->st[cps->sp].kid.buf);
1047 cps->sp--;
1048 }
1049
1050 lws_free_set_NULL(*_cps);
1051 }
1052