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 #include "private-lib-cose.h"
27
28 struct lws_cose_sign_context *
lws_cose_sign_create(const lws_cose_sign_create_info_t *info)29 lws_cose_sign_create(const lws_cose_sign_create_info_t *info)
30 {
31 struct lws_cose_sign_context *csc;
32
33 /* you have to have prepared a cbor output context for us to use */
34 assert(info->lec);
35 /* you have to provide at least one key in a cose_keyset */
36 assert(info->keyset);
37 /* you have to provide an lws_context (for crypto random) */
38 assert(info->cx);
39
40 if (info->sigtype == SIGTYPE_MAC) {
41 lwsl_err("%s: only mac0 supported for signing\n", __func__);
42 return NULL;
43 }
44
45 csc = lws_zalloc(sizeof(*csc), __func__);
46 if (!csc)
47 return NULL;
48
49 csc->info = *info;
50
51 return csc;
52 }
53
54 int
lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, const lws_cose_key_t *ck)55 lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg,
56 const lws_cose_key_t *ck)
57 {
58 lws_cose_sig_alg_t *si = lws_cose_sign_alg_create(csc->info.cx, ck, alg,
59 LWSCOSE_WKKO_SIGN);
60
61 if (!si)
62 return 1;
63
64 lws_dll2_add_tail(&si->list, &csc->algs);
65
66 return 0;
67 }
68
69 static signed char cose_tags[] = {
70 0,
71 LWSCOAP_CONTENTFORMAT_COSE_SIGN,
72 LWSCOAP_CONTENTFORMAT_COSE_SIGN1,
73 LWSCOAP_CONTENTFORMAT_COSE_SIGN,
74 LWSCOAP_CONTENTFORMAT_COSE_MAC,
75 LWSCOAP_CONTENTFORMAT_COSE_MAC0
76 };
77
78 static void
lws_cose_sign_hashing(struct lws_cose_sign_context *csc, const uint8_t *in, size_t in_len)79 lws_cose_sign_hashing(struct lws_cose_sign_context *csc,
80 const uint8_t *in, size_t in_len)
81 {
82 //lwsl_hexdump_warn(in, in_len);
83
84 assert(in_len);
85
86 lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
87 lws_dll2_get_head(&csc->algs)) {
88 lws_cose_sig_alg_t *alg = lws_container_of(p,
89 lws_cose_sig_alg_t, list);
90
91 if (lws_cose_sign_alg_hash(alg, in, in_len))
92 alg->failed = 1;
93 } lws_end_foreach_dll_safe(p, tp);
94 }
95
96 /*
97 * These chunks may be payload or application AAD being emitted into the
98 * signed object somewhere else. But we do not emit them ourselves here
99 * (since other non-emitted things are also hashed by us) and so can always
100 * deal with the whole in_len in one step.
101 */
102
103 enum lws_lec_pctx_ret
lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, const uint8_t *in, size_t in_len)104 lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc,
105 const uint8_t *in, size_t in_len)
106 {
107 uint8_t lbuf[MAX_BLOBBED_PARAMS], lb[9];
108 const struct lws_gencrypto_keyelem *ke;
109 enum lws_lec_pctx_ret ret;
110 lws_lec_pctx_t lec, lec1;
111 lws_cose_sig_alg_t *alg;
112 uint8_t c;
113 size_t s;
114
115 switch (csc->tli) {
116 case ST_UNKNOWN:
117 /*
118 * We need to figure out what signing structure we need to use,
119 * given the algorithms that are in it. So let's have a look
120 * and decide.
121 */
122
123 if (!csc->algs.count) {
124 lwsl_err("%s: must add at least one signature\n", __func__);
125 return 1;
126 }
127
128 csc->type = SIGTYPE_MULTI;
129 alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list);
130
131 switch (alg->cose_alg) {
132 case LWSCOSE_WKAHMAC_256_64:
133 case LWSCOSE_WKAHMAC_256_256:
134 case LWSCOSE_WKAHMAC_384_384:
135 case LWSCOSE_WKAHMAC_512_512:
136 // if (csc->info.sigtype == SIGTYPE_MAC0)
137 csc->type = SIGTYPE_MAC0;
138 // else
139 // csc->type = SIGTYPE_MAC;
140 break;
141 }
142
143 if (csc->algs.count == 1) {
144 if (!csc->info.sigtype && csc->type == SIGTYPE_MAC) {
145 if (csc->info.flags & LCSC_FL_ADD_CBOR_PREFER_MAC0)
146 csc->type = SIGTYPE_MAC0;
147 } else
148 if (!csc->info.sigtype ||
149 csc->info.sigtype == SIGTYPE_SINGLE) /* ie, if no hint */
150 csc->type = SIGTYPE_SINGLE;
151 }
152
153 lwsl_notice("%s: decided on type %d\n", __func__, csc->type);
154
155 /*
156 * Start emitting the appropriate tag if that's requested
157 */
158
159 if (csc->info.flags & LCSC_FL_ADD_CBOR_TAG) {
160 ret = lws_lec_printf(csc->info.lec, "%t(",
161 cose_tags[csc->type]);
162
163 if (ret != LWS_LECPCTX_RET_FINISHED)
164 return ret;
165 }
166
167 /* The */
168 c = 0;
169 switch (csc->type) {
170 case SIGTYPE_MAC0:
171 case SIGTYPE_MULTI:
172 case SIGTYPE_SINGLE:
173 c = 0x84;
174 break;
175 case SIGTYPE_MAC:
176 c = 0x85;
177 break;
178 default:
179 break;
180 }
181
182 /* The outer array */
183 csc->info.lec->scratch[csc->info.lec->scratch_len++] = c;
184
185 /*
186 * Then, let's start hashing with the sigtype constant part
187 */
188
189 lws_cose_sign_hashing(csc, sig_mctx[csc->type],
190 sig_mctx_len[csc->type]);
191
192 csc->tli = ST_OUTER_PROTECTED;
193 csc->subsequent = 0;
194
195 /* fallthru */
196
197 case ST_OUTER_PROTECTED:
198
199 /*
200 * We need to list and emit any outer protected data as a map
201 * into its own buffer, then emit that into the output as a bstr
202 */
203
204 switch (csc->type) {
205 case SIGTYPE_SINGLE:
206 case SIGTYPE_MAC0:
207 alg = lws_container_of(csc->algs.head,
208 lws_cose_sig_alg_t, list);
209
210 lws_lec_init(&lec, lbuf, sizeof(lbuf));
211
212 /* we know it will fit... but coverity doesn't */
213 ret = lws_lec_printf(&lec, "{1:%lld}",
214 (long long)alg->cose_alg);
215 if (ret != LWS_LECPCTX_RET_FINISHED)
216 return ret;
217
218 lws_lec_scratch(&lec);
219
220 if (!csc->subsequent) {
221 lws_lec_init(&lec1, lb, sizeof(lb));
222 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0,
223 lec.used);
224 lws_cose_sign_hashing(csc, lec1.scratch,
225 lec1.scratch_len);
226 lws_cose_sign_hashing(csc, lec.start, lec.used);
227 ret = lws_lec_printf(csc->info.lec, "%.*b",
228 (int)lec.used, lec.start);
229
230 if (ret != LWS_LECPCTX_RET_FINISHED)
231 return ret;
232 csc->subsequent = 1;
233 }
234 break;
235 case SIGTYPE_MAC:
236 case SIGTYPE_MULTI:
237 lws_lec_init(&lec, lbuf, sizeof(lbuf));
238 lws_lec_int(&lec, LWS_CBOR_MAJTYP_BSTR, 0, 0);
239 lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0);
240 lws_lec_scratch(&lec);
241 lec.used = lws_ptr_diff_size_t(lec.buf, lec.start);
242 lws_cose_sign_hashing(csc, lec.start,
243 lec.used);
244 break;
245 default:
246 lec.used = 0;
247 break;
248 }
249
250 csc->tli = ST_OUTER_UNPROTECTED;
251
252 /* fallthru */
253
254 case ST_OUTER_UNPROTECTED:
255
256 /*
257 * We need to list and emit any outer unprotected data, as
258 * an inline cbor map
259 */
260
261 switch (csc->type) {
262 case SIGTYPE_SINGLE:
263 case SIGTYPE_MAC0:
264 alg = lws_container_of(csc->algs.head,
265 lws_cose_sig_alg_t, list);
266 ke = &alg->cose_key->meta[COSEKEY_META_KID];
267 if (ke->len) {
268 ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}",
269 LWSCOSE_WKL_KID,
270 (int)ke->len, ke->buf);
271
272 if (ret != LWS_LECPCTX_RET_FINISHED)
273 return ret;
274 }
275 /* hack for no extra data */
276
277 lws_lec_init(&lec1, lb, sizeof(lb));
278 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0);
279 lws_cose_sign_hashing(csc, lec1.scratch,
280 lec1.scratch_len);
281 break;
282 case SIGTYPE_MAC:
283 case SIGTYPE_MULTI:
284
285 lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0);
286
287 /*
288 * For cose-sign, we need to feed each sig alg its alg-
289 * specific protected data into the hash before letting
290 * all the hashes see the payload
291 */
292
293 lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
294 lws_dll2_get_head(&csc->algs)) {
295 alg = lws_container_of(p, lws_cose_sig_alg_t, list);
296
297 lws_lec_init(&lec, lbuf, sizeof(lbuf));
298
299 /* we know it will fit... but coverity doesn't... */
300 ret = lws_lec_printf(&lec, "{1:%lld}",
301 (long long)alg->cose_alg);
302 if (ret != LWS_LECPCTX_RET_FINISHED)
303 return ret;
304
305 lws_lec_init(&lec1, lb, sizeof(lb));
306 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0,
307 lec.used);
308
309 // lwsl_hexdump_warn(lec1.scratch, lec1.scratch_len);
310 // lwsl_hexdump_warn(lec.start, lec.used);
311 if (lws_cose_sign_alg_hash(alg, lec1.scratch,
312 lec1.scratch_len))
313 alg->failed = 1;
314 if (lws_cose_sign_alg_hash(alg, lec.start,
315 lec.used))
316 alg->failed = 1;
317
318 } lws_end_foreach_dll_safe(p, tp);
319
320 lws_lec_init(&lec1, lb, sizeof(lb));
321 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0);
322 lws_cose_sign_hashing(csc, lec1.scratch,
323 lec1.scratch_len);
324
325 break;
326 default:
327 ret = lws_lec_printf(csc->info.lec, "{}");
328 if (ret != LWS_LECPCTX_RET_FINISHED)
329 return ret;
330 break;
331 }
332
333 csc->tli = ST_OUTER_PAYLOAD;
334 csc->subsequent = 0;
335
336 /* Prepare the payload BSTR */
337
338 lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0,
339 csc->info.inline_payload_len);
340
341 lws_lec_init(&lec1, lb, sizeof(lb));
342 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0,
343 csc->info.inline_payload_len);
344 lws_cose_sign_hashing(csc, lec1.scratch,
345 lec1.scratch_len);
346
347 lws_lec_scratch(csc->info.lec);
348
349 csc->rem_pay = csc->info.inline_payload_len;
350
351 /* fallthru */
352
353 case ST_OUTER_PAYLOAD:
354
355 if (csc->along) {
356 in += csc->along;
357 in_len -= csc->along;
358 }
359
360 lws_lec_scratch(csc->info.lec);
361
362 if (csc->rem_pay) {
363
364 lws_cose_sign_hashing(csc, in, in_len);
365
366 /*
367 * in / in_len is the payload chunk
368 */
369
370 s = lws_ptr_diff_size_t(csc->info.lec->end,
371 csc->info.lec->buf);
372 if (s > (size_t)csc->rem_pay)
373 s = (size_t)csc->rem_pay;
374 if (s > in_len)
375 s = in_len;
376
377 memcpy(csc->info.lec->buf, in, s);
378 csc->info.lec->buf += s;
379 csc->info.lec->used = lws_ptr_diff_size_t(
380 csc->info.lec->buf,
381 csc->info.lec->start);
382 csc->rem_pay -= s;
383
384 csc->along = s;
385
386 return LWS_LECPCTX_RET_AGAIN;
387 }
388
389 /* finished with rem_pay */
390
391 if (csc->type == SIGTYPE_MULTI) {
392
393 csc->alg = lws_container_of(csc->algs.head,
394 lws_cose_sig_alg_t, list);
395 lws_lec_init(&lec1, lb, sizeof(lb));
396 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0,
397 csc->algs.count);
398 lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0,
399 csc->algs.count);
400 csc->tli = ST_INNER_PROTECTED;
401 goto inner_protected;
402 }
403 csc->tli = ST_OUTER_SIGN1_SIGNATURE;
404 csc->along = 0;
405
406 /* fallthru */
407
408 case ST_OUTER_SIGN1_SIGNATURE:
409
410 alg = lws_container_of(lws_dll2_get_head(&csc->algs),
411 lws_cose_sig_alg_t, list);
412
413 if (!alg->completed)
414 lws_cose_sign_alg_complete(alg);
415 if (alg->failed)
416 return LWS_LECPCTX_RET_FAIL;
417
418 ret = lws_lec_printf(csc->info.lec, "%.*b",
419 (int)alg->rhash_len, alg->rhash);
420 if (ret != LWS_LECPCTX_RET_FINISHED)
421 return ret;
422
423 if (csc->type == SIGTYPE_MAC) {
424 csc->alg = lws_container_of(csc->algs.head,
425 lws_cose_sig_alg_t, list);
426 lws_lec_init(&lec1, lb, sizeof(lb));
427 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0,
428 csc->algs.count);
429 lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0,
430 csc->algs.count);
431 csc->tli = ST_INNER_PROTECTED;
432 goto inner_protected;
433 }
434
435 break;
436
437 case ST_INNER_PROTECTED:
438 inner_protected:
439
440 /*
441 * We need to list and emit any outer protected data as a map
442 * into its own buffer, then emit that into the output as a bstr
443 */
444
445 switch (csc->type) {
446 case SIGTYPE_MAC:
447 case SIGTYPE_MULTI:
448 lws_lec_init(&lec1, lb, sizeof(lb));
449 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, 3);
450
451 lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, 3);
452
453 lws_lec_init(&lec, lbuf, sizeof(lbuf));
454
455 /* we know it will fit */
456 lws_lec_printf(&lec, "{1:%lld}",
457 (long long)csc->alg->cose_alg);
458
459 lws_lec_init(&lec1, lb, sizeof(lb));
460 lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0,
461 lec.used);
462 if (lws_lec_printf(csc->info.lec, "{1:%lld}",
463 (long long)csc->alg->cose_alg) != LWS_LECPCTX_RET_FINISHED)
464 /* coverity */
465 return 0;
466 break;
467 default:
468 lec.used = 0;
469 break;
470 }
471
472
473 csc->tli = ST_INNER_UNPROTECTED;
474
475 /* fallthru */
476
477 case ST_INNER_UNPROTECTED:
478
479 switch (csc->type) {
480 case SIGTYPE_MULTI:
481 alg = lws_container_of(csc->algs.head,
482 lws_cose_sig_alg_t, list);
483 ke = &alg->cose_key->meta[COSEKEY_META_KID];
484 if (ke->len) {
485 ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}",
486 LWSCOSE_WKL_KID,
487 (int)ke->len, ke->buf);
488
489 if (ret != LWS_LECPCTX_RET_FINISHED)
490 return ret;
491 }
492 break;
493 default:
494 ret = lws_lec_printf(csc->info.lec, "{}");
495 if (ret != LWS_LECPCTX_RET_FINISHED)
496 return ret;
497 break;
498 }
499
500 lws_cose_sign_alg_complete(csc->alg);
501 if (csc->alg->failed)
502 return LWS_LECPCTX_RET_FAIL;
503 csc->tli = ST_INNER_SIGNATURE;
504
505 /* fallthru */
506
507 case ST_INNER_SIGNATURE:
508
509 ret = lws_lec_printf(csc->info.lec, "%.*b",
510 (int)csc->alg->rhash_len, csc->alg->rhash);
511 if (ret != LWS_LECPCTX_RET_FINISHED)
512 return ret;
513
514 if (csc->alg->list.next) {
515 csc->alg = (lws_cose_sig_alg_t *)csc->alg->list.next;
516 csc->tli = ST_INNER_PROTECTED;
517 }
518 break;
519
520 }
521
522 return 0;
523 }
524
525 void
lws_cose_sign_destroy(struct lws_cose_sign_context **_csc)526 lws_cose_sign_destroy(struct lws_cose_sign_context **_csc)
527 {
528 struct lws_cose_sign_context *csc = *_csc;
529
530 if (!csc)
531 return;
532
533 lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
534 lws_dll2_get_head(&csc->algs)) {
535 lws_cose_sig_alg_t *alg = lws_container_of(p,
536 lws_cose_sig_alg_t, list);
537
538 lws_dll2_remove(p);
539 lws_cose_sign_alg_destroy(&alg);
540 } lws_end_foreach_dll_safe(p, tp);
541
542 lws_free_set_NULL(*_csc);
543 }
544