/* * 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. */ #include "private-lib-core.h" #include "private-lib-cose.h" struct lws_cose_sign_context * lws_cose_sign_create(const lws_cose_sign_create_info_t *info) { struct lws_cose_sign_context *csc; /* you have to have prepared a cbor output context for us to use */ assert(info->lec); /* 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); if (info->sigtype == SIGTYPE_MAC) { lwsl_err("%s: only mac0 supported for signing\n", __func__); return NULL; } csc = lws_zalloc(sizeof(*csc), __func__); if (!csc) return NULL; csc->info = *info; return csc; } int lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, const lws_cose_key_t *ck) { lws_cose_sig_alg_t *si = lws_cose_sign_alg_create(csc->info.cx, ck, alg, LWSCOSE_WKKO_SIGN); if (!si) return 1; lws_dll2_add_tail(&si->list, &csc->algs); return 0; } static signed char cose_tags[] = { 0, LWSCOAP_CONTENTFORMAT_COSE_SIGN, LWSCOAP_CONTENTFORMAT_COSE_SIGN1, LWSCOAP_CONTENTFORMAT_COSE_SIGN, LWSCOAP_CONTENTFORMAT_COSE_MAC, LWSCOAP_CONTENTFORMAT_COSE_MAC0 }; static void lws_cose_sign_hashing(struct lws_cose_sign_context *csc, const uint8_t *in, size_t in_len) { //lwsl_hexdump_warn(in, in_len); assert(in_len); lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, lws_dll2_get_head(&csc->algs)) { lws_cose_sig_alg_t *alg = lws_container_of(p, lws_cose_sig_alg_t, list); if (lws_cose_sign_alg_hash(alg, in, in_len)) alg->failed = 1; } lws_end_foreach_dll_safe(p, tp); } /* * These chunks may be payload or application AAD being emitted into the * signed object somewhere else. But we do not emit them ourselves here * (since other non-emitted things are also hashed by us) and so can always * deal with the whole in_len in one step. */ enum lws_lec_pctx_ret lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, const uint8_t *in, size_t in_len) { uint8_t lbuf[MAX_BLOBBED_PARAMS], lb[9]; const struct lws_gencrypto_keyelem *ke; enum lws_lec_pctx_ret ret; lws_lec_pctx_t lec, lec1; lws_cose_sig_alg_t *alg; uint8_t c; size_t s; switch (csc->tli) { case ST_UNKNOWN: /* * We need to figure out what signing structure we need to use, * given the algorithms that are in it. So let's have a look * and decide. */ if (!csc->algs.count) { lwsl_err("%s: must add at least one signature\n", __func__); return 1; } csc->type = SIGTYPE_MULTI; alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); switch (alg->cose_alg) { case LWSCOSE_WKAHMAC_256_64: case LWSCOSE_WKAHMAC_256_256: case LWSCOSE_WKAHMAC_384_384: case LWSCOSE_WKAHMAC_512_512: // if (csc->info.sigtype == SIGTYPE_MAC0) csc->type = SIGTYPE_MAC0; // else // csc->type = SIGTYPE_MAC; break; } if (csc->algs.count == 1) { if (!csc->info.sigtype && csc->type == SIGTYPE_MAC) { if (csc->info.flags & LCSC_FL_ADD_CBOR_PREFER_MAC0) csc->type = SIGTYPE_MAC0; } else if (!csc->info.sigtype || csc->info.sigtype == SIGTYPE_SINGLE) /* ie, if no hint */ csc->type = SIGTYPE_SINGLE; } lwsl_notice("%s: decided on type %d\n", __func__, csc->type); /* * Start emitting the appropriate tag if that's requested */ if (csc->info.flags & LCSC_FL_ADD_CBOR_TAG) { ret = lws_lec_printf(csc->info.lec, "%t(", cose_tags[csc->type]); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; } /* The */ c = 0; switch (csc->type) { case SIGTYPE_MAC0: case SIGTYPE_MULTI: case SIGTYPE_SINGLE: c = 0x84; break; case SIGTYPE_MAC: c = 0x85; break; default: break; } /* The outer array */ csc->info.lec->scratch[csc->info.lec->scratch_len++] = c; /* * Then, let's start hashing with the sigtype constant part */ lws_cose_sign_hashing(csc, sig_mctx[csc->type], sig_mctx_len[csc->type]); csc->tli = ST_OUTER_PROTECTED; csc->subsequent = 0; /* fallthru */ case ST_OUTER_PROTECTED: /* * We need to list and emit any outer protected data as a map * into its own buffer, then emit that into the output as a bstr */ switch (csc->type) { case SIGTYPE_SINGLE: case SIGTYPE_MAC0: alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); lws_lec_init(&lec, lbuf, sizeof(lbuf)); /* we know it will fit */ lws_lec_printf(&lec, "{1:%lld}", (long long)alg->cose_alg); lws_lec_scratch(&lec); if (!csc->subsequent) { lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, lec.used); lws_cose_sign_hashing(csc, lec1.scratch, lec1.scratch_len); lws_cose_sign_hashing(csc, lec.start, lec.used); ret = lws_lec_printf(csc->info.lec, "%.*b", (int)lec.used, lec.start); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; csc->subsequent = 1; } break; case SIGTYPE_MAC: case SIGTYPE_MULTI: lws_lec_init(&lec, lbuf, sizeof(lbuf)); lws_lec_int(&lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); lws_lec_scratch(&lec); lec.used = lws_ptr_diff_size_t(lec.buf, lec.start); lws_cose_sign_hashing(csc, lec.start, lec.used); break; default: lec.used = 0; break; } csc->tli = ST_OUTER_UNPROTECTED; /* fallthru */ case ST_OUTER_UNPROTECTED: /* * We need to list and emit any outer unprotected data, as * an inline cbor map */ switch (csc->type) { case SIGTYPE_SINGLE: case SIGTYPE_MAC0: alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); ke = &alg->cose_key->meta[COSEKEY_META_KID]; if (ke->len) { ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}", LWSCOSE_WKL_KID, (int)ke->len, ke->buf); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; } /* hack for no extra data */ lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0); lws_cose_sign_hashing(csc, lec1.scratch, lec1.scratch_len); break; case SIGTYPE_MAC: case SIGTYPE_MULTI: lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); /* * For cose-sign, we need to feed each sig alg its alg- * specific protected data into the hash before letting * all the hashes see the payload */ lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, lws_dll2_get_head(&csc->algs)) { alg = lws_container_of(p, lws_cose_sig_alg_t, list); lws_lec_init(&lec, lbuf, sizeof(lbuf)); /* we know it will fit */ lws_lec_printf(&lec, "{1:%lld}", (long long)alg->cose_alg); lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, lec.used); // lwsl_hexdump_warn(lec1.scratch, lec1.scratch_len); // lwsl_hexdump_warn(lec.start, lec.used); if (lws_cose_sign_alg_hash(alg, lec1.scratch, lec1.scratch_len)) alg->failed = 1; if (lws_cose_sign_alg_hash(alg, lec.start, lec.used)) alg->failed = 1; } lws_end_foreach_dll_safe(p, tp); lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0); lws_cose_sign_hashing(csc, lec1.scratch, lec1.scratch_len); break; default: ret = lws_lec_printf(csc->info.lec, "{}"); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; break; } csc->tli = ST_OUTER_PAYLOAD; csc->subsequent = 0; /* Prepare the payload BSTR */ lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, csc->info.inline_payload_len); lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, csc->info.inline_payload_len); lws_cose_sign_hashing(csc, lec1.scratch, lec1.scratch_len); lws_lec_scratch(csc->info.lec); csc->rem_pay = csc->info.inline_payload_len; /* fallthru */ case ST_OUTER_PAYLOAD: if (csc->along) { in += csc->along; in_len -= csc->along; } lws_lec_scratch(csc->info.lec); if (csc->rem_pay) { lws_cose_sign_hashing(csc, in, in_len); /* * in / in_len is the payload chunk */ s = lws_ptr_diff_size_t(csc->info.lec->end, csc->info.lec->buf); if (s > (size_t)csc->rem_pay) s = (size_t)csc->rem_pay; if (s > in_len) s = in_len; memcpy(csc->info.lec->buf, in, s); csc->info.lec->buf += s; csc->info.lec->used = lws_ptr_diff_size_t( csc->info.lec->buf, csc->info.lec->start); csc->rem_pay -= s; csc->along = s; return LWS_LECPCTX_RET_AGAIN; } /* finished with rem_pay */ if (csc->type == SIGTYPE_MULTI) { csc->alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, csc->algs.count); lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, csc->algs.count); csc->tli = ST_INNER_PROTECTED; goto inner_protected; } csc->tli = ST_OUTER_SIGN1_SIGNATURE; csc->along = 0; /* fallthru */ case ST_OUTER_SIGN1_SIGNATURE: alg = lws_container_of(lws_dll2_get_head(&csc->algs), lws_cose_sig_alg_t, list); if (!alg->completed) lws_cose_sign_alg_complete(alg); if (alg->failed) return LWS_LECPCTX_RET_FAIL; ret = lws_lec_printf(csc->info.lec, "%.*b", (int)alg->rhash_len, alg->rhash); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; if (csc->type == SIGTYPE_MAC) { csc->alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, csc->algs.count); lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, csc->algs.count); csc->tli = ST_INNER_PROTECTED; goto inner_protected; } break; case ST_INNER_PROTECTED: inner_protected: /* * We need to list and emit any outer protected data as a map * into its own buffer, then emit that into the output as a bstr */ switch (csc->type) { case SIGTYPE_MAC: case SIGTYPE_MULTI: lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, 3); lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, 3); lws_lec_init(&lec, lbuf, sizeof(lbuf)); /* we know it will fit */ lws_lec_printf(&lec, "{1:%lld}", (long long)csc->alg->cose_alg); lws_lec_init(&lec1, lb, sizeof(lb)); lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, lec.used); lws_lec_printf(csc->info.lec, "{1:%lld}", (long long)csc->alg->cose_alg); break; default: lec.used = 0; break; } csc->tli = ST_INNER_UNPROTECTED; /* fallthru */ case ST_INNER_UNPROTECTED: switch (csc->type) { case SIGTYPE_MULTI: alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); ke = &alg->cose_key->meta[COSEKEY_META_KID]; if (ke->len) { ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}", LWSCOSE_WKL_KID, (int)ke->len, ke->buf); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; } break; default: ret = lws_lec_printf(csc->info.lec, "{}"); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; break; } lws_cose_sign_alg_complete(csc->alg); if (csc->alg->failed) return LWS_LECPCTX_RET_FAIL; csc->tli = ST_INNER_SIGNATURE; /* fallthru */ case ST_INNER_SIGNATURE: ret = lws_lec_printf(csc->info.lec, "%.*b", (int)csc->alg->rhash_len, csc->alg->rhash); if (ret != LWS_LECPCTX_RET_FINISHED) return ret; if (csc->alg->list.next) { csc->alg = (lws_cose_sig_alg_t *)csc->alg->list.next; csc->tli = ST_INNER_PROTECTED; } break; } return 0; } void lws_cose_sign_destroy(struct lws_cose_sign_context **_csc) { struct lws_cose_sign_context *csc = *_csc; if (!csc) return; lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, lws_dll2_get_head(&csc->algs)) { lws_cose_sig_alg_t *alg = lws_container_of(p, lws_cose_sig_alg_t, list); lws_dll2_remove(p); lws_cose_sign_alg_destroy(&alg); } lws_end_foreach_dll_safe(p, tp); lws_free_set_NULL(*_csc); }