/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * cose_sign handling * * Validation: * * - we put all our pieces and results in an lwsac in the parse state object * * - we collect pieces needed for sig validation into lwsac elements * * - we go through each signature making discrete results in the lwsac for * the user code to assess */ #include "private-lib-core.h" #include "private-lib-cose.h" const uint8_t *sig_mctx[] = { (uint8_t *)"", (uint8_t *)"\x85\x69""Signature", (uint8_t *)"\x84\x6a""Signature1", (uint8_t *)"\x85\x6f""CounterSignature", (uint8_t *)"\x84\x63""MAC", (uint8_t *)"\x84\x64""MAC0", }; uint8_t sig_mctx_len[] = { 0, 11, 12, 17, 5, 6 }; struct alg_names { const char *name; cose_param_t alg; } alg_names[] = { { "ES256", LWSCOSE_WKAECDSA_ALG_ES256 }, { "ES384", LWSCOSE_WKAECDSA_ALG_ES384 }, { "ES512", LWSCOSE_WKAECDSA_ALG_ES512 }, { "HS256_64", LWSCOSE_WKAHMAC_256_64 }, { "HS256", LWSCOSE_WKAHMAC_256_256 }, { "HS384", LWSCOSE_WKAHMAC_384_384 }, { "HS512", LWSCOSE_WKAHMAC_512_512 }, { "RS256", LWSCOSE_WKARSA_ALG_RS256 }, { "RS384", LWSCOSE_WKARSA_ALG_RS384 }, { "RS512", LWSCOSE_WKARSA_ALG_RS512 }, }; /* * The Sig_structure plaintext is new temp CBOR made up from pieces from the * cose_sign, cose_signature, and payload in a specific order * * tstr context string * bstr 0-len or protected body headers * bstr (Missing for sign1) 0-len or protected signer headers * bstr 0-len or protected application part * bstr the payload * * We are getting CBOR with an optional outer tag and then an array of exactly * 4 items in a fixed order * * [ * protected headers: bstr containing a map (captured as CBOR in cps->ph[]) * unprotected: map: for sign1, eg, the alg (!?), the kid * payload: bstr * if sign: signatures: [ cose_signature struct array, * each is a 3-element array * [ * protected: bstr containing a map: (eg, the alg) (captured as CBOR) * unprotected: map: (eg, the kid) * signature: bstr * ] * if sign1: bstr containing signature * ] * * The last signatures field may be an array of signatures, or a single * cose_signature object for cose_sign1. * * For cose_sign1, we know the signature alg before the payload and can do it * in a single pass. But for sign, we do not know the signature algs until * after the payload, which is an unfortunate oversight in cose_sign, meaning we * cannot hash the payload one or more ways in a single pass. */ #if defined(VERBOSE) const char *cose_sections[] = { "ST_UNKNOWN", "ST_OUTER_PROTECTED", "ST_OUTER_UNPROTECTED", "ST_OUTER_PAYLOAD", "ST_OUTER_SIGN1_SIGNATURE", "ST_OUTER_SIGN_SIGARRAY", "ST_OUTER_MACTAG", "ST_INNER_PROTECTED", "ST_INNER_UNPROTECTED", "ST_INNER_SIGNATURE", "ST_INNER_EXCESS", }; #endif const char * lws_cose_alg_to_name(cose_param_t alg) { size_t n; for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++) if (alg_names[n].alg == alg) return alg_names[n].name; return "unknown_alg"; } cose_param_t lws_cose_name_to_alg(const char *name) { size_t n; for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++) if (!strcmp(alg_names[n].name, name)) return alg_names[n].alg; return 0; } static size_t bstr_len(uint8_t *t, size_t buflen, uint8_t opcode, uint64_t len) { uint8_t *ot = t; if (buflen < 9) return 0; if (len < 24) { *t = (uint8_t)(opcode | len); return 1; } if (len < 256) { *t++ = opcode | LWS_CBOR_1; goto b; } if (len < 65536) { *t++ = opcode | LWS_CBOR_2; goto b1; } if (len < 0xffffffffu) { *t++ = opcode | LWS_CBOR_4; goto b2; } *t++ = opcode | LWS_CBOR_8; *t++ = (uint8_t)(len >> 56); *t++ = (uint8_t)(len >> 48); *t++ = (uint8_t)(len >> 40); *t++ = (uint8_t)(len >> 32); b2: *t++ = (uint8_t)(len >> 24); *t++ = (uint8_t)(len >> 16); b1: *t++ = (uint8_t)(len >> 8); b: *t++ = (uint8_t)len; return lws_ptr_diff_size_t(t, ot); } static int apply_external(struct lws_cose_validate_context *cps) { lws_cose_sig_alg_t *alg; uint8_t t[9]; alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); if (!alg) /* expected if no key */ return 0; /* get the external payload first, if any indicated */ if (cps->info.ext_len) { lws_cose_sig_ext_pay_t ex; size_t s; s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR, cps->info.ext_len); if (lws_cose_val_alg_hash(alg, t, s)) return 1; memset(&ex, 0, sizeof(ex)); ex.cps = cps; do { int n; ex.xl = 0; n = cps->info.ext_cb(&ex); if (ex.xl && lws_cose_val_alg_hash(alg, ex.ext, ex.xl)) return 1; if (n == LCOSESIGEXTCB_RET_ERROR) return 1; if (n == LCOSESIGEXTCB_RET_FINISHED) break; } while (1); } return 0; } static int create_alg(struct lecp_ctx *ctx, struct lws_cose_validate_context *cps) { lws_cose_validate_param_stack_t *sl = &cps->st[cps->sp], *sl0 = &cps->st[0]; lws_cose_validate_res_t *res; lws_cose_sig_alg_t *alg; lws_cose_key_t *ck; uint8_t *p; size_t s; /* with sign1, we can hash the payload in a * single pass */ ck = lws_cose_key_from_set(cps->info.keyset, sl->kid.buf, sl->kid.len); if (!ck) { lwsl_notice("%s: no key\n", __func__); lwsl_hexdump_notice(sl->kid.buf, sl->kid.len); goto no_key_or_alg; } // lwsl_notice("%s: cps->alg %d\n", __func__, (int)cps->alg); alg = lws_cose_val_alg_create(cps->info.cx, ck, cps->st[0].alg, LWSCOSE_WKKO_VERIFY); if (!alg) { lwsl_info("%s: no alg\n", __func__); no_key_or_alg: /* * We can't create the alg then, so we can't normally * create a result object. Create one especially for this * case and continue on */ res = lws_zalloc(sizeof(*res), __func__); if (res) { res->result = -1001; lws_dll2_add_tail(&res->list, &cps->results); } return 0; } lws_dll2_add_tail(&alg->list, &cps->algs); /* * Hash step 1: The first hash content depends on * sign/sign1/csign/mac/mac0 constant bstr */ if (lws_cose_val_alg_hash(alg, sig_mctx[cps->info.sigtype], sig_mctx_len[cps->info.sigtype])) goto bail; /* * Hash step 2: A zero-length bstr, or a copy of the * OUTER protected headers * * A zero-entry map alone becomes a zero- * length bstr */ if (sl0->ph_pos[0] < 2) { /* nothing to speak of */ sl0->ph[0][0] = LWS_CBOR_MAJTYP_BSTR; p = &sl0->ph[0][0]; s = 1; } else { if (sl0->ph_pos[0] < 24) { sl0->ph[0][2] = (uint8_t) (LWS_CBOR_MAJTYP_BSTR | sl0->ph_pos[0]); p = &sl0->ph[0][2]; s = (size_t)sl0->ph_pos[0] + 1; } else { sl0->ph[0][1] = LWS_CBOR_MAJTYP_BSTR | LWS_CBOR_1; sl0->ph[0][2] = (uint8_t)sl0->ph_pos[0]; p = &sl0->ph[0][1]; s = (size_t)sl0->ph_pos[0] + 2; } } if (lws_cose_val_alg_hash(alg, p, s)) goto bail; /* * Hash step 3: Protected signer headers (Elided for sign1) */ if (cps->info.sigtype == SIGTYPE_MULTI) { if (sl->ph_pos[2] < 2) { /* nothing to speak of */ sl->ph[2][0] = LWS_CBOR_MAJTYP_BSTR; p = &sl->ph[2][0]; s = 1; } else { if (sl->ph_pos[2] < 24) { sl->ph[2][2] = (uint8_t) (LWS_CBOR_MAJTYP_BSTR | sl->ph_pos[2]); p = &sl->ph[2][2]; s = (size_t)sl->ph_pos[2] + 1; } else { sl->ph[2][1] = LWS_CBOR_MAJTYP_BSTR | LWS_CBOR_1; sl->ph[2][2] = (uint8_t)sl->ph_pos[2]; p = &sl->ph[2][1]; s = (size_t)sl->ph_pos[2] + 2; } } if (lws_cose_val_alg_hash(alg, p, s)) goto bail; } /* Hash step 4: bstr for applictation protected pieces * empty for now */ if (!cps->info.ext_len) { /* ie, if no app data */ uint8_t u = LWS_CBOR_MAJTYP_BSTR; if (lws_cose_val_alg_hash(alg, &u, 1)) goto bail; } /* * The final part is the payload in its own bstr, as * we get it if sign1, else replayed from a cache in heap */ if (cps->info.sigtype == SIGTYPE_SINGLE) return 0; if (!cps->payload_stash) { lwsl_notice("%s: no payload stash\n", __func__); goto bail; } apply_external(cps); if (lws_cose_val_alg_hash(alg, cps->payload_stash, cps->payload_pos)) goto bail; lwsl_notice("a %d\n", (int)cps->sig_agg_pos); lws_cose_val_alg_destroy(cps, &alg, (const uint8_t *)cps->sig_agg, cps->sig_agg_pos); return 0; bail: return 1; } #if defined(VERBOSE) static const char * const reason_names[] = { "LECPCB_CONSTRUCTED", "LECPCB_DESTRUCTED", "LECPCB_START", "LECPCB_COMPLETE", "LECPCB_FAILED", "LECPCB_PAIR_NAME", "LECPCB_VAL_TRUE", "LECPCB_VAL_FALSE", "LECPCB_VAL_NULL", "LECPCB_VAL_NUM_INT", "LECPCB_VAL_RESERVED", /* float in lejp */ "LECPCB_VAL_STR_START", "LECPCB_VAL_STR_CHUNK", "LECPCB_VAL_STR_END", "LECPCB_ARRAY_START", "LECPCB_ARRAY_END", "LECPCB_OBJECT_START", "LECPCB_OBJECT_END", "LECPCB_TAG_START", "LECPCB_TAG_END", "LECPCB_VAL_NUM_UINT", "LECPCB_VAL_UNDEFINED", "LECPCB_VAL_FLOAT16", "LECPCB_VAL_FLOAT32", "LECPCB_VAL_FLOAT64", "LECPCB_VAL_SIMPLE", "LECPCB_VAL_BLOB_START", "LECPCB_VAL_BLOB_CHUNK", "LECPCB_VAL_BLOB_END", "LECPCB_ARRAY_ITEM_START", "LECPCB_ARRAY_ITEM_END", "LECPCB_LITERAL_CBOR" }; #endif static int ph_index(struct lws_cose_validate_context *cps) { switch (cps->tli) { case ST_OUTER_PROTECTED: return 0; case ST_OUTER_UNPROTECTED: return 1; case ST_INNER_PROTECTED: return 2; case ST_INNER_UNPROTECTED: return 3; } assert(0); return 0; } static signed char cb_cose_sig(struct lecp_ctx *ctx, char reason) { struct lws_cose_validate_context *cps = (struct lws_cose_validate_context *)ctx->user; lws_cose_validate_param_stack_t *sl; struct lws_gencrypto_keyelem *ke; lws_cose_sig_alg_t *alg; uint8_t t[9]; size_t s; int hi; #if defined(VERBOSE) lwsl_notice("%s: %s, tli %s, sub %d, ppos %d, sp %d\n", __func__, reason_names[reason & 0x1f], cose_sections[cps->tli], cps->sub, ctx->pst[ctx->pst_sp].ppos, cps->sp); #endif switch (reason) { case LECPCB_CONSTRUCTED: break; case LECPCB_TAG_START: lwsl_notice("%s: tag sigtype %d\n", __func__, cps->info.sigtype); switch (cps->info.sigtype) { default: assert(0); break; case SIGTYPE_UNKNOWN: /* it means use the tag value to set the type */ switch (ctx->item.u.u64) { case LWSCOAP_CONTENTFORMAT_COSE_SIGN: cps->info.sigtype = SIGTYPE_MULTI; break; case LWSCOAP_CONTENTFORMAT_COSE_SIGN1: cps->info.sigtype = SIGTYPE_SINGLE; break; // case LWSCOAP_CONTENTFORMAT_COSE_SIGN__: // cps->info.sigtype = SIGTYPE_COUNTERSIGNED; // break; case LWSCOAP_CONTENTFORMAT_COSE_MAC0: cps->info.sigtype = SIGTYPE_MAC0; break; case LWSCOAP_CONTENTFORMAT_COSE_MAC: cps->info.sigtype = SIGTYPE_MAC; break; default: goto unexpected_tag; } break; case SIGTYPE_MULTI: if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN) goto unexpected_tag; break; case SIGTYPE_SINGLE: if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN1) goto unexpected_tag; break; case SIGTYPE_COUNTERSIGNED: if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN) goto unexpected_tag; break; case SIGTYPE_MAC0: if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC0) goto unexpected_tag; break; case SIGTYPE_MAC: if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC) { unexpected_tag: lwsl_warn("%s: unexpected tag %d\n", __func__, (int)ctx->item.u.u64); goto bail; } break; } cps->depth++; break; case LECPCB_ARRAY_ITEM_START: if (cps->sub) break; if (ctx->pst[ctx->pst_sp].ppos == 4 || ctx->pst[ctx->pst_sp].ppos == 6) { switch (cps->tli) { case ST_INNER_UNPROTECTED: case ST_INNER_PROTECTED: hi = ph_index(cps); sl = &cps->st[cps->sp]; sl->ph_pos[hi] = 0; lecp_parse_report_raw(ctx, 1); break; default: break; } break; } if (ctx->pst[ctx->pst_sp].ppos != 2) break; switch (cps->tli) { case ST_OUTER_UNPROTECTED: case ST_OUTER_PROTECTED: /* * Holy type confusion, Batman... this is a CBOR bstr * containing valid CBOR that must also be parsed as * part of the containing array... we need to collect * it anyway since it is part of the signing plaintext * in bstr form, let's get it and then parse it at the * END of the bstr. */ lecp_parse_report_raw(ctx, 1); break; case ST_OUTER_PAYLOAD: if (cps->info.sigtype != SIGTYPE_SINGLE) break; if (create_alg(ctx, cps)) goto bail; break; case ST_OUTER_SIGN_SIGARRAY: cps->tli = ST_INNER_PROTECTED; break; } break; case LECPCB_ARRAY_ITEM_END: if (cps->sub) break; if (ctx->pst[ctx->pst_sp].ppos == 2) { sl = &cps->st[cps->sp]; switch (cps->tli) { case ST_OUTER_UNPROTECTED: break; /* fallthru */ case ST_OUTER_PROTECTED: lecp_parse_report_raw(ctx, 0); hi = ph_index(cps); if (!sl->ph_pos[hi] || cps->sub) break; cps->sub = 1; s = (size_t)sl->ph_pos[hi]; if (lecp_parse_subtree(&cps->ctx, sl->ph[hi] + 3, s) != LECP_CONTINUE) goto bail; cps->sub = 0; break; case ST_OUTER_PAYLOAD: switch (cps->info.sigtype) { case SIGTYPE_MULTI: cps->tli = ST_OUTER_SIGN_SIGARRAY - 1; break; case SIGTYPE_MAC: case SIGTYPE_MAC0: cps->tli = ST_OUTER_MACTAG - 1; break; case SIGTYPE_COUNTERSIGNED: break; default: break; } break; case ST_OUTER_SIGN1_SIGNATURE: case ST_OUTER_MACTAG: cps->sp++; cps->tli = ST_INNER_PROTECTED - 1; break; case ST_INNER_UNPROTECTED: lwsl_notice("ST_INNER_UNPROTECTED end\n"); break; case ST_INNER_PROTECTED: lwsl_notice("ST_INNER_PROTECTED end\n"); break; case ST_INNER_EXCESS: case ST_OUTER_SIGN_SIGARRAY: cps->tli--; /* so no change */ break; } if (!cps->sub) cps->tli++; } if (ctx->pst[ctx->pst_sp].ppos >= 4) { uint8_t *p; uint8_t u; size_t s1; switch (cps->tli) { case ST_INNER_UNPROTECTED: case ST_INNER_PROTECTED: hi = ph_index(cps); sl = &cps->st[cps->sp]; p = sl->ph[hi] + 3; lecp_parse_report_raw(ctx, 0); if (!sl->ph_pos[hi] || cps->sub) { if (!cps->sub) cps->tli++; break; } cps->sub = 1; s = (size_t)sl->ph_pos[hi]; /* * somehow the raw captures the * initial BSTR container length, * let's strip it */ u = (*p) & LWS_CBOR_SUBMASK; if (((*p) & LWS_CBOR_MAJTYP_MASK) == LWS_CBOR_MAJTYP_BSTR) { s1 = 1; if (u == LWS_CBOR_1) s1 = 2; else if (u == LWS_CBOR_2) s1 = 3; else if (u == LWS_CBOR_4) s1 = 5; else if (u == LWS_CBOR_8) s1 = 9; if (s1 > s) goto bail; sl->ph_pos[hi] = (int) (sl->ph_pos[hi] - (ssize_t)s1); s = s - s1; memmove(p, p + s1, s); } if (lecp_parse_subtree(&cps->ctx, p, s) != LECP_CONTINUE) goto bail; cps->sub = 0; if (!cps->sub) cps->tli++; break; case ST_INNER_SIGNATURE: if (cps->info.sigtype == SIGTYPE_MAC) { // lwsl_err("Y: alg %d\n", (int)cps->alg); if (create_alg(ctx, cps)) goto bail; } cps->tli++; break; default: break; } } break; case LECPCB_VAL_NUM_INT: case LECPCB_VAL_NUM_UINT: switch (cps->tli) { case ST_INNER_PROTECTED: case ST_INNER_UNPROTECTED: case ST_INNER_SIGNATURE: case ST_OUTER_PROTECTED: case ST_OUTER_UNPROTECTED: if (lecp_parse_map_is_key(ctx)) { cps->map_key = ctx->item.u.i64; // lwsl_notice("%s: key %d\n", __func__, (int)cps->map_key); break; } // lwsl_notice("%s: key %d val %d\n", __func__, (int)cps->map_key, (int)ctx->item.u.i64); if (cps->map_key == LWSCOSE_WKL_ALG) { sl = &cps->st[cps->sp]; cps->map_key = 0; if (cps->tli == ST_INNER_PROTECTED || cps->tli == ST_INNER_UNPROTECTED || cps->tli == ST_INNER_SIGNATURE) { sl->alg = ctx->item.u.i64; if (!cps->st[0].alg) cps->st[0].alg = sl->alg; } else sl->alg = ctx->item.u.i64; break; } break; } break; case LECPCB_VAL_STR_END: switch (cps->tli) { case ST_OUTER_UNPROTECTED: break; } break; case LECPCB_VAL_BLOB_START: lwsl_notice("%s: blob size %d\n", __func__, (int)ctx->item.u.u64); if (cps->tli == ST_OUTER_SIGN1_SIGNATURE || cps->tli == ST_INNER_SIGNATURE) { if (ctx->item.u.u64 > sizeof(cps->sig_agg)) goto bail; cps->sig_agg_pos = 0; break; } if (cps->tli != ST_OUTER_PAYLOAD) break; if (apply_external(cps)) { lwsl_notice("%s: ext\n", __func__); goto bail; } s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR, ctx->item.u.u64); if (cps->info.sigtype == SIGTYPE_SINGLE) { alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); if (!alg) /* expected if no key */ break; if (lws_cose_val_alg_hash(alg, t, s)) { lwsl_notice("%s: hash failed\n", __func__); goto bail; } break; } cps->payload_stash_size = (size_t)(ctx->item.u.u64 + s); cps->payload_stash = lws_malloc(cps->payload_stash_size, __func__); if (!cps->payload_stash) { lwsl_notice("%s: oom\n", __func__); goto bail; } memcpy(cps->payload_stash, t, s); cps->payload_pos = s; break; case LECPCB_VAL_BLOB_CHUNK: switch (cps->tli) { case ST_OUTER_PAYLOAD: if (cps->info.pay_cb && ctx->npos) cps->info.pay_cb(cps, cps->info.pay_opaque, (uint8_t *)ctx->buf, ctx->npos); if (cps->payload_stash) { if (cps->payload_pos + ctx->npos > cps->payload_stash_size) goto bail; memcpy(cps->payload_stash + cps->payload_pos, ctx->buf, ctx->npos); cps->payload_pos += ctx->npos; break; } alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); if (!alg) /* expected if no key */ break; if (ctx->npos && lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf, ctx->npos)) { lwsl_notice("%s: chunk fail\n", __func__); goto bail; } break; case ST_INNER_SIGNATURE: case ST_OUTER_SIGN1_SIGNATURE: /* the sig is big compared to ctx->buf... we need to * stash it then */ memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, ctx->npos); cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos; break; } break; case LECPCB_VAL_BLOB_END: switch (cps->tli) { case ST_INNER_SIGNATURE: if (cps->info.sigtype == SIGTYPE_MULTI) { memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, ctx->npos); cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos; // lwsl_err("Y: alg %d\n", (int)cps->alg); if (create_alg(ctx, cps)) goto bail; break; } if (cps->info.sigtype != SIGTYPE_MAC) break; /* fallthru */ case ST_OUTER_PROTECTED: case ST_OUTER_UNPROTECTED: case ST_INNER_PROTECTED: case ST_INNER_UNPROTECTED: if (cps->map_key == LWSCOSE_WKL_KID) { sl = &cps->st[cps->sp]; ke = &sl->kid; if (ke->buf) lws_free(ke->buf); ke->buf = lws_malloc(ctx->npos, __func__); if (!ke->buf) goto bail; ke->len = ctx->npos; memcpy(ke->buf, ctx->buf, ctx->npos); cps->map_key = 0; } break; case ST_OUTER_PAYLOAD: if (cps->info.pay_cb && ctx->npos) cps->info.pay_cb(cps, cps->info.pay_opaque, (uint8_t *)ctx->buf, ctx->npos); if (cps->payload_stash) { if (cps->payload_pos + ctx->npos > cps->payload_stash_size) goto bail; memcpy(cps->payload_stash + cps->payload_pos, ctx->buf, ctx->npos); cps->payload_pos += ctx->npos; break; } alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); if (!alg) /* expected if no key */ break; if (ctx->npos && lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf, ctx->npos)) goto bail; break; case ST_OUTER_SIGN1_SIGNATURE: if (cps->info.sigtype == SIGTYPE_MULTI) break; memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, ctx->npos); cps->sig_agg_pos += ctx->npos; alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); lwsl_notice("b\n"); if (alg) lws_cose_val_alg_destroy(cps, &alg, cps->sig_agg, cps->sig_agg_pos); break; case ST_OUTER_MACTAG: if (cps->mac_pos + ctx->npos > sizeof(cps->mac)) goto bail; memcpy(cps->mac + cps->mac_pos, ctx->buf, ctx->npos); cps->mac_pos += ctx->npos; if (cps->info.sigtype == SIGTYPE_MAC0) { if (create_alg(ctx, cps)) goto bail; } break; } break; case LECPCB_LITERAL_CBOR: /* only used for protected headers */ switch (cps->tli) { case ST_INNER_PROTECTED: case ST_OUTER_PROTECTED: case ST_INNER_UNPROTECTED: case ST_OUTER_UNPROTECTED: sl = &cps->st[cps->sp]; hi = ph_index(cps); if (sl->ph_pos[hi] + 3 + ctx->cbor_pos > (int)sizeof(sl->ph[hi]) - 3) /* more protected cbor than we can handle */ goto bail; memcpy(sl->ph[hi] + 3 + sl->ph_pos[hi], ctx->cbor, ctx->cbor_pos); sl->ph_pos[hi] += ctx->cbor_pos; break; } } return 0; bail: return -1; } struct lws_cose_validate_context * lws_cose_validate_create(const lws_cose_validate_create_info_t *info) { struct lws_cose_validate_context *cps; /* you have to provide at least one key in a cose_keyset */ assert(info->keyset); /* you have to provide an lws_context (for crypto random) */ assert(info->cx); cps = lws_zalloc(sizeof(*cps), __func__); if (!cps) return NULL; cps->info = *info; cps->tli = ST_OUTER_PROTECTED; lecp_construct(&cps->ctx, cb_cose_sig, cps, NULL, 0); return cps; } int lws_cose_validate_chunk(struct lws_cose_validate_context *cps, const uint8_t *in, size_t in_len, size_t *used_in) { int n; n = lecp_parse(&cps->ctx, in, in_len); if (used_in) *used_in = cps->ctx.used_in; if (n == LECP_CONTINUE) return LECP_CONTINUE; lecp_destruct(&cps->ctx); return n; } lws_dll2_owner_t * lws_cose_validate_results(struct lws_cose_validate_context *cps) { return &cps->results; } void lws_cose_validate_destroy(struct lws_cose_validate_context **_cps) { struct lws_cose_validate_context *cps = *_cps; if (!cps) return; lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, lws_dll2_get_head(&cps->algs)) { lws_cose_sig_alg_t *alg = lws_container_of(p, lws_cose_sig_alg_t, list); lws_dll2_remove(p); lws_cose_val_alg_destroy(cps, &alg, NULL, 0); } lws_end_foreach_dll_safe(p, tp); lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, lws_dll2_get_head(&cps->results)) { lws_cose_validate_res_t *res = lws_container_of(p, lws_cose_validate_res_t, list); lws_dll2_remove(p); lws_free(res); } lws_end_foreach_dll_safe(p, tp); lws_free_set_NULL(cps->payload_stash); lwsac_free(&cps->ac); while (cps->sp >= 0) { if (cps->st[cps->sp].kid.buf) lws_free(cps->st[cps->sp].kid.buf); cps->sp--; } lws_free_set_NULL(*_cps); }