mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 09:46:44 +00:00
4db2ff872b
Support for COSE keys and signing / validation - lws_cose_key_t and import / export / generation apis for EC / RSA / SYMMETRIC - cose_sign1 ES256/384/512,RS256/384/512 sign + validate, passes RFC8152 WG tests sign1-tests - cose_sign ES256/384/512,RS256/384/512 sign + validate, passes RFC8152 WG tests sign-tests - cose_mac0 HS256/HS256_64/384/512 sign + validate, passes RFC8152 WG tests hmac-examples - cose_mac HS256/HS256_64/384/512 validate, passes RFC8152 WG tests hmac-examples - lws-crypto-cose-key commandline tool for key / key set dumping and creation - lws-crypro-cose-sign commandline tool for signing / validation - lws-api-test-cose - large number of test vectors and tests from RFC8152
1189 lines
28 KiB
C
1189 lines
28 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
|
*
|
|
* 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_key code
|
|
*/
|
|
|
|
#include "private-lib-core.h"
|
|
//#include "private-lib-jose.h"
|
|
|
|
#define lwsl_cose lwsl_notice
|
|
#define lwsl_hexdump_cose lwsl_hexdump_notice
|
|
|
|
// #define VERBOSE 1
|
|
|
|
struct lws_cose_key_parse_state {
|
|
struct lws_cose_key *ck;
|
|
/**< single key created here if pkey_set is NULL */
|
|
char buf[(8192 / 8) + 1];
|
|
/**< enough for 8Kb key, only needed during parse */
|
|
lws_cose_key_import_callback per_key_cb;
|
|
lws_dll2_owner_t *pkey_set;
|
|
/**< if non-NULL, expects a [ key set ], else single key */
|
|
void *user;
|
|
size_t pos;
|
|
int cose_state;
|
|
cose_param_t seen[16];
|
|
int seen_count;
|
|
int gencrypto_eidx;
|
|
int meta_idx;
|
|
unsigned short possible;
|
|
};
|
|
|
|
/*
|
|
* A COSE key representation is a CBOR map with a specified structure. The
|
|
* keys are
|
|
*
|
|
* LWSCOSE_WKK_KTY MUST int / tstr
|
|
* LWSCOSE_WKK_KID OPT bstr
|
|
* LWSCOSE_WKK_ALG OPT int / tstr
|
|
* LWSCOSE_WKK_KEY_OPS OPT [ + (int / tstr) ]
|
|
* LWSCOSE_WKK_BASE_IV OPT bstr
|
|
*/
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
static const char *meta_names[] = {
|
|
"kty", "kid", "use", "key_ops", "base_iv", "alg"
|
|
};
|
|
|
|
static const char *oct_names[] = {
|
|
"k"
|
|
};
|
|
|
|
static const char *rsa_names[] = {
|
|
"e", "n", "d", "p", "q", "dp", "dq", "qi", "other", "ri", "di", "ti"
|
|
};
|
|
|
|
static const char *ec_names[] = {
|
|
"crv", "x", "d", "y",
|
|
};
|
|
|
|
void
|
|
lws_cose_key_dump(const struct lws_cose_key *ck)
|
|
{
|
|
const char **enames;
|
|
char hex[2048];
|
|
int elems;
|
|
int n;
|
|
|
|
(void)enames;
|
|
(void)meta_names;
|
|
|
|
switch (ck->gencrypto_kty) {
|
|
|
|
case LWS_GENCRYPTO_KTY_OCT:
|
|
elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT;
|
|
enames = oct_names;
|
|
break;
|
|
case LWS_GENCRYPTO_KTY_RSA:
|
|
elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT;
|
|
enames = rsa_names;
|
|
break;
|
|
case LWS_GENCRYPTO_KTY_EC:
|
|
elems = LWS_GENCRYPTO_EC_KEYEL_COUNT;
|
|
enames = ec_names;
|
|
break;
|
|
|
|
default:
|
|
lwsl_err("%s: jwk %p: unknown type\n", __func__, ck);
|
|
|
|
return;
|
|
}
|
|
|
|
lwsl_cose("%s: cose_key %p, kty: %lld (gc %d)\n", __func__, ck,
|
|
(long long)ck->kty, ck->gencrypto_kty);
|
|
|
|
for (n = 0; n < LWS_COUNT_COSE_KEY_ELEMENTS; n++) {
|
|
if (ck->meta[n].buf) {
|
|
lws_hex_from_byte_array(ck->meta[n].buf, ck->meta[n].len,
|
|
hex, sizeof(hex));
|
|
lwsl_cose(" meta: %s: %s\n", meta_names[n], hex);
|
|
}
|
|
}
|
|
|
|
for (n = 0; n < elems; n++) {
|
|
if (ck->e[n].buf) {
|
|
lws_hex_from_byte_array(ck->e[n].buf, ck->e[n].len,
|
|
hex, sizeof(hex));
|
|
lwsl_cose(" e: %s: %s\n", enames[n], hex);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static const char * const kty_strings[] = { NULL,
|
|
"OKP", "EC2", "RSA", "SYMMETRIC", "HSS_LMS", "WALNUTDSA"
|
|
};
|
|
|
|
int
|
|
lws_cose_key_checks(const lws_cose_key_t *key, int64_t kty, cose_param_t alg,
|
|
int key_op, const char *crv)
|
|
{
|
|
const struct lws_gencrypto_keyelem *ke;
|
|
|
|
/*
|
|
* we ourselves have to have a very clear idea what we need, even if
|
|
* matches are optional in the key itself
|
|
*/
|
|
assert(key);
|
|
assert(kty);
|
|
assert(alg);
|
|
assert(key_op);
|
|
assert((kty != LWSCOSE_WKKTV_OKP && kty != LWSCOSE_WKKTV_EC2) || crv);
|
|
|
|
/* RFC8152 8.1:
|
|
*
|
|
* The 'kty' field MUST be present, and it MUST be '...'.
|
|
*
|
|
* But kty can come as an int or a string, but we convert well-known
|
|
* kty ints to the corresponding string representation at key import
|
|
*/
|
|
if (!kty || kty >= (int)LWS_ARRAY_SIZE(kty_strings)) {
|
|
/* we don't understand it */
|
|
lwsl_notice("%s: unknown kty %d\n", __func__, (int)kty);
|
|
goto bail;
|
|
}
|
|
|
|
ke = &key->meta[COSEKEY_META_KTY];
|
|
if (ke->buf && (strlen(kty_strings[kty]) != ke->len ||
|
|
memcmp(kty_strings[kty], ke->buf, ke->len))) {
|
|
lwsl_notice("%s: key is of wrong kty\n", __func__);
|
|
lwsl_hexdump_notice(ke->buf, ke->len);
|
|
goto bail;
|
|
}
|
|
|
|
/* ...
|
|
* If the 'alg' field is present, it MUST match the ... signature
|
|
* algorithm being used.
|
|
*
|
|
* We attempt to convert key alg text representations to a well-known
|
|
* index, if we can't, then we don't know the alg anyway and should fail
|
|
* it
|
|
*/
|
|
|
|
if (!key->cose_alg && key->meta[COSEKEY_META_ALG].buf) {
|
|
lwsl_notice("%s: alg fail 1\n", __func__);
|
|
goto bail;
|
|
}
|
|
|
|
if (key->cose_alg && /* accept it being absent altogether */
|
|
key->cose_alg != alg) {
|
|
lwsl_notice("%s: alg fail 2\n", __func__);
|
|
|
|
goto bail;
|
|
}
|
|
|
|
/* ...
|
|
* If the 'key_ops' field is present, it MUST include 'sign' / 'verify'
|
|
* when creating /verifying an ... signature.
|
|
*/
|
|
|
|
ke = &key->meta[COSEKEY_META_KEY_OPS];
|
|
if (ke->buf && ke->len) {
|
|
uint32_t n;
|
|
|
|
for (n = 0; n < ke->len; n++)
|
|
if (ke->buf[n] == key_op)
|
|
break;
|
|
|
|
if (n == ke->len)
|
|
goto bail;
|
|
}
|
|
|
|
/*
|
|
* If it's related to EC, check there is a curve associated with the
|
|
* key, and check it is what we expect
|
|
*/
|
|
|
|
if (kty == LWSCOSE_WKKTV_OKP || kty == LWSCOSE_WKKTV_EC2) {
|
|
ke = &key->e[LWS_GENCRYPTO_EC_KEYEL_CRV];
|
|
|
|
if (!ke->buf)
|
|
goto bail;
|
|
if (ke->len != strlen(crv))
|
|
goto bail;
|
|
if (memcmp(ke->buf, crv, ke->len))
|
|
goto bail;
|
|
}
|
|
|
|
/* We're willing to use this key for this operation */
|
|
|
|
return 0;
|
|
|
|
bail:
|
|
lwsl_notice("%s: key rejected\n", __func__);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
lws_ck_set_el(struct lws_gencrypto_keyelem *e, char *in, size_t len)
|
|
{
|
|
e->buf = lws_malloc(len + 1, "ck");
|
|
if (!e->buf)
|
|
return -1;
|
|
|
|
memcpy(e->buf, in, len);
|
|
e->buf[len] = '\0';
|
|
e->len = (uint32_t)len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct {
|
|
const char *curve;
|
|
cose_param_t cose_id;
|
|
} cose_curves[] = {
|
|
{ "P-256", LWSCOSE_WKEC_P256 },
|
|
{ "P-384", LWSCOSE_WKEC_P384 },
|
|
{ "P-521", LWSCOSE_WKEC_P521 },
|
|
{ "X25519", LWSCOSE_WKEC_X25519 },
|
|
{ "X448", LWSCOSE_WKEC_X448 },
|
|
{ "ED25519", LWSCOSE_WKEC_ED25519 },
|
|
{ "ED448", LWSCOSE_WKEC_ED448 },
|
|
{ "SECP256K1", LWSCOSE_WKEC_SECP256K1 },
|
|
};
|
|
|
|
/* 0 means failed */
|
|
|
|
static cose_param_t
|
|
lws_cose_curve_name_to_id(const char *curve)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < (int)LWS_ARRAY_SIZE(cose_curves); n++)
|
|
if (!strcmp(cose_curves[n].curve, curve))
|
|
return cose_curves[n].cose_id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
lws_cose_curve_id_to_name(cose_param_t id)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < (int)LWS_ARRAY_SIZE(cose_curves); n++)
|
|
if (cose_curves[n].cose_id == id)
|
|
return cose_curves[n].curve;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char * const wk_algs[] = {
|
|
"ES256", "ES384", "ES512"
|
|
};
|
|
static signed char wk_alg_indexes[] = {
|
|
LWSCOSE_WKAECDSA_ALG_ES256,
|
|
LWSCOSE_WKAECDSA_ALG_ES384,
|
|
LWSCOSE_WKAECDSA_ALG_ES512,
|
|
};
|
|
|
|
static signed char
|
|
cb_cose_key(struct lecp_ctx *ctx, char reason)
|
|
{
|
|
struct lws_cose_key_parse_state *cps =
|
|
(struct lws_cose_key_parse_state *)ctx->user;
|
|
struct lws_gencrypto_keyelem *ke = NULL;
|
|
const char *p;
|
|
int n;
|
|
|
|
#if defined(VERBOSE)
|
|
lwsl_notice("%s: reason %d, path %s, ord %u, ppos %d\n", __func__,
|
|
reason & 0x3f,
|
|
ctx->path, ctx->st[ctx->sp - 1].ordinal,
|
|
ctx->pst[ctx->pst_sp].ppos);
|
|
#endif
|
|
|
|
switch (reason) {
|
|
case LECPCB_OBJECT_START:
|
|
if (cps->ck)
|
|
break;
|
|
goto ak;
|
|
case LECPCB_ARRAY_ITEM_START:
|
|
if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) {
|
|
ak:
|
|
cps->ck = lws_zalloc(sizeof(*cps->ck), __func__);
|
|
if (!cps->ck)
|
|
goto bail;
|
|
cps->cose_state = 0;
|
|
cps->meta_idx = -1;
|
|
cps->gencrypto_eidx = -1;
|
|
cps->seen_count = 0;
|
|
|
|
if (cps->pkey_set)
|
|
lws_dll2_add_tail(&cps->ck->list, cps->pkey_set);
|
|
}
|
|
break;
|
|
case LECPCB_ARRAY_ITEM_END:
|
|
if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) {
|
|
if (cps->per_key_cb)
|
|
cps->per_key_cb(cps->ck, cps->user);
|
|
}
|
|
break;
|
|
case LECPCB_TAG_START:
|
|
if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_KEY) {
|
|
lwsl_warn("%s: unexpected tag\n", __func__);
|
|
goto bail;
|
|
}
|
|
break;
|
|
|
|
case LECPCB_VAL_NUM_INT:
|
|
case LECPCB_VAL_NUM_UINT:
|
|
if (!ctx->sp) {
|
|
lwsl_warn("%s: unexpected uint %d, ppos %d\n",
|
|
__func__, ctx->sp, ctx->pst[ctx->sp].ppos);
|
|
goto bail;
|
|
}
|
|
|
|
if (!lecp_parse_map_is_key(ctx)) {
|
|
const char *kty_str;
|
|
|
|
/* value part of map */
|
|
|
|
switch (cps->cose_state) {
|
|
case LWSCOSE_WKK_KTY:
|
|
assert(cps->ck);
|
|
cps->ck->kty = (int)ctx->item.u.u64;
|
|
|
|
/* convert the cose key type to gencrypto one */
|
|
switch (ctx->item.u.u64) {
|
|
case LWSCOSE_WKKTV_OKP:
|
|
cps->ck->gencrypto_kty =
|
|
LWS_GENCRYPTO_KTY_EC;
|
|
kty_str = "OKP";
|
|
break;
|
|
case LWSCOSE_WKKTV_EC2:
|
|
kty_str = "EC2";
|
|
cps->ck->gencrypto_kty =
|
|
LWS_GENCRYPTO_KTY_EC;
|
|
break;
|
|
case LWSCOSE_WKKTV_RSA:
|
|
kty_str = "RSA";
|
|
cps->ck->gencrypto_kty =
|
|
LWS_GENCRYPTO_KTY_RSA;
|
|
break;
|
|
case LWSCOSE_WKKTV_SYMMETRIC:
|
|
kty_str = "SYMMETRIC";
|
|
cps->ck->gencrypto_kty =
|
|
LWS_GENCRYPTO_KTY_OCT;
|
|
break;
|
|
// case LWSCOSE_WKKTV_HSS_LMS:
|
|
// case LWSCOSE_WKKTV_WALNUTDSA:
|
|
default:
|
|
lwsl_warn("%s: unknown kty\n", __func__);
|
|
goto bail;
|
|
}
|
|
|
|
/* store the string version of the key type */
|
|
|
|
ke = &cps->ck->meta[COSEKEY_META_KTY];
|
|
ke->len = (uint32_t)strlen(kty_str);
|
|
ke->buf = lws_malloc(ke->len + 1, __func__);
|
|
if (!ke->buf)
|
|
goto bail;
|
|
memcpy(ke->buf, kty_str, ke->len + 1);
|
|
break;
|
|
case LWSCOSE_WKK_ALG:
|
|
/*
|
|
* He can tie the key to a cose alg code
|
|
*/
|
|
cps->ck->cose_alg = (int)ctx->item.u.u64;
|
|
break;
|
|
case LWSCOSE_WKK_KEY_OPS:
|
|
if (!cps->pkey_set &&
|
|
(ctx->pst[ctx->sp].ppos != 3 ||
|
|
strcmp(ctx->path, ".[]"))) {
|
|
lwsl_warn("%s: unexpected kops\n",
|
|
__func__);
|
|
goto bail;
|
|
}
|
|
if (cps->pkey_set &&
|
|
(ctx->pst[ctx->sp].ppos != 5 ||
|
|
strcmp(ctx->path, "[].[]"))) {
|
|
lwsl_warn("%s: unexpected kops\n",
|
|
__func__);
|
|
goto bail;
|
|
}
|
|
break;
|
|
case LWSCOSE_WKOKP_CRV:
|
|
cps->ck->cose_curve = (int)ctx->item.u.u64;
|
|
p = lws_cose_curve_id_to_name(cps->ck->cose_curve);
|
|
if (p) {
|
|
ke = &cps->ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV];
|
|
ke->len = (uint32_t)strlen(p);
|
|
ke->buf = lws_malloc(ke->len + 1, __func__);
|
|
if (!ke->buf)
|
|
goto bail;
|
|
memcpy(ke->buf, p, ke->len);
|
|
ke->buf[ke->len] = '\0';
|
|
}
|
|
break;
|
|
default:
|
|
lwsl_warn("%s: uint not allowed in state %d\n",
|
|
__func__, cps->cose_state);
|
|
/* int not allowed in this state */
|
|
goto bail;
|
|
}
|
|
|
|
cps->cose_state = 0;
|
|
break;
|
|
}
|
|
|
|
/* key part of map pair */
|
|
|
|
/*
|
|
* Disallow any of these coming more than once
|
|
*/
|
|
cps->cose_state = (int)ctx->item.u.u64;
|
|
for (n = 0 ; n < cps->seen_count; n++)
|
|
if (cps->seen[n] == cps->cose_state) {
|
|
/* dupe */
|
|
lwsl_warn("%s: duplicate map name %d\n",
|
|
__func__, cps->cose_state);
|
|
goto bail;
|
|
}
|
|
|
|
if (cps->seen_count >= (int)LWS_ARRAY_SIZE(cps->seen))
|
|
goto bail;
|
|
cps->seen[cps->seen_count++] = cps->cose_state;
|
|
|
|
cps->meta_idx = -1;
|
|
switch ((int)ctx->item.u.u64) {
|
|
case LWSCOSE_WKK_KTY:
|
|
cps->meta_idx = COSEKEY_META_KTY;
|
|
break;
|
|
case LWSCOSE_WKK_KID:
|
|
cps->meta_idx = COSEKEY_META_KID;
|
|
break;
|
|
case LWSCOSE_WKK_ALG:
|
|
cps->meta_idx = COSEKEY_META_ALG;
|
|
break;
|
|
case LWSCOSE_WKK_KEY_OPS:
|
|
cps->meta_idx = COSEKEY_META_KEY_OPS;
|
|
break;
|
|
case LWSCOSE_WKK_BASE_IV:
|
|
cps->meta_idx = COSEKEY_META_BASE_IV;
|
|
break;
|
|
|
|
default:
|
|
cps->gencrypto_eidx = -1;
|
|
|
|
switch (cps->ck->kty) {
|
|
case LWSCOSE_WKKTV_OKP:
|
|
switch ((int)ctx->item.u.u64) {
|
|
case LWSCOSE_WKOKP_CRV:
|
|
cps->cose_state = LWSCOSE_WKOKP_CRV;
|
|
break;
|
|
case LWSCOSE_WKOKP_X:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_EC_KEYEL_X;
|
|
break;
|
|
case LWSCOSE_WKOKP_D:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_EC_KEYEL_D;
|
|
break;
|
|
default:
|
|
goto bail;
|
|
}
|
|
break;
|
|
case LWSCOSE_WKKTV_EC2:
|
|
switch ((int)ctx->item.u.u64) {
|
|
case LWSCOSE_WKECKP_CRV:
|
|
cps->cose_state = LWSCOSE_WKOKP_CRV;
|
|
break;
|
|
case LWSCOSE_WKECKP_X:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_EC_KEYEL_X;
|
|
break;
|
|
case LWSCOSE_WKECKP_Y:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_EC_KEYEL_Y;
|
|
break;
|
|
case LWSCOSE_WKECKP_D:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_EC_KEYEL_D;
|
|
break;
|
|
default:
|
|
goto bail;
|
|
}
|
|
break;
|
|
case LWSCOSE_WKKTV_RSA:
|
|
switch ((int)ctx->item.u.u64) {
|
|
case LWSCOSE_WKKPRSA_N:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_N;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_E:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_E;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_D:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_D;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_P:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_P;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_Q:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_Q;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_DP:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_DP;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_DQ:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_DQ;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_QINV:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_QI;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_OTHER:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_OTHER;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_RI:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_RI;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_DI:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_DI;
|
|
break;
|
|
case LWSCOSE_WKKPRSA_TI:
|
|
cps->gencrypto_eidx =
|
|
LWS_GENCRYPTO_RSA_KEYEL_TI;
|
|
break;
|
|
default:
|
|
goto bail;
|
|
}
|
|
break;
|
|
case LWSCOSE_WKKTV_SYMMETRIC:
|
|
if (ctx->item.u.i64 != -1 &&
|
|
ctx->item.u.u64 != LWSCOSE_WKSYMKP_KEY_VALUE)
|
|
goto bail;
|
|
|
|
cps->gencrypto_eidx = LWS_GENCRYPTO_OCT_KEYEL_K;
|
|
break;
|
|
default:
|
|
lwsl_warn("%s: unknown kty\n", __func__);
|
|
goto bail;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LECPCB_VAL_BLOB_START:
|
|
if (!ctx->sp || !(ctx->st[ctx->sp - 1].ordinal & 1)) {
|
|
lwsl_warn("%s: unexpected blob\n", __func__);
|
|
goto bail;
|
|
}
|
|
|
|
if (cps->cose_state == COSEKEY_META_KID)
|
|
break;
|
|
|
|
/*
|
|
* Validate the association of the blob now, collect it into
|
|
* the temp buf in cps and then alloc and copy it into the
|
|
* related key element when it's at the end and the size known
|
|
*/
|
|
|
|
cps->pos = 0;
|
|
if (cps->gencrypto_eidx >= 0) {
|
|
if (cps->ck->e[cps->gencrypto_eidx].buf) {
|
|
lwsl_warn("%s: e[%d] set twice %d\n", __func__,
|
|
cps->gencrypto_eidx,
|
|
cps->ck->e[cps->gencrypto_eidx].len);
|
|
/* key elements must only come at most once */
|
|
goto bail;
|
|
}
|
|
break;
|
|
}
|
|
if (cps->meta_idx >= 0)
|
|
break;
|
|
|
|
goto bail;
|
|
|
|
case LECPCB_VAL_BLOB_CHUNK:
|
|
case LECPCB_VAL_BLOB_END:
|
|
if (cps->pos + ctx->npos > sizeof(cps->buf)) {
|
|
lwsl_warn("%s: oversize blob\n", __func__);
|
|
goto bail;
|
|
}
|
|
memcpy(cps->buf + cps->pos, ctx->buf, ctx->npos);
|
|
cps->pos += ctx->npos;
|
|
|
|
if (reason == LECPCB_VAL_BLOB_CHUNK)
|
|
break;
|
|
|
|
/* we have the key element data, let's make the ck element */
|
|
if (cps->gencrypto_eidx >= 0) {
|
|
|
|
if (cps->ck->e[cps->gencrypto_eidx].buf)
|
|
break;
|
|
|
|
lws_ck_set_el(&cps->ck->e[cps->gencrypto_eidx],
|
|
(char *)cps->buf, cps->pos);
|
|
cps->gencrypto_eidx = -1;
|
|
break;
|
|
}
|
|
|
|
|
|
if (cps->meta_idx >= 0) {
|
|
lws_ck_set_el(&cps->ck->meta[cps->meta_idx],
|
|
(char *)cps->buf, cps->pos);
|
|
cps->meta_idx = -1;
|
|
}
|
|
cps->pos = 0;
|
|
break;
|
|
case LECPCB_VAL_STR_END:
|
|
if (cps->cose_state == LWSCOSE_WKOKP_CRV) {
|
|
cps->ck->cose_curve = lws_cose_curve_name_to_id(ctx->buf);
|
|
ke = &cps->ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV];
|
|
ke->len = ctx->npos;
|
|
ke->buf = lws_malloc(ctx->npos, __func__);
|
|
if (!ke->buf)
|
|
goto bail;
|
|
memcpy(ke->buf, ctx->buf, ctx->npos);
|
|
}
|
|
|
|
if (!lecp_parse_map_is_key(ctx) &&
|
|
cps->cose_state == LWSCOSE_WKK_ALG) {
|
|
size_t n;
|
|
|
|
for (n = 0; n < LWS_ARRAY_SIZE(wk_algs); n++)
|
|
if (ctx->npos == strlen(wk_algs[n]) &&
|
|
!memcmp(ctx->buf, wk_algs[n], ctx->npos)) {
|
|
cps->ck->cose_alg = wk_alg_indexes[n];
|
|
break;
|
|
}
|
|
|
|
if (n == LWS_ARRAY_SIZE(wk_algs))
|
|
/* key is for an alg we don't understand */
|
|
lwsl_warn("%s: key for unknown alg %.*s\n",
|
|
__func__, (int)ctx->npos, ctx->buf);
|
|
|
|
ke = &cps->ck->meta[COSEKEY_META_ALG];
|
|
ke->len = ctx->npos;
|
|
ke->buf = lws_malloc(ctx->npos, __func__);
|
|
if (!ke->buf)
|
|
goto bail;
|
|
memcpy(ke->buf, ctx->buf, ctx->npos);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
bail:
|
|
lwsl_warn("%s: bail\n", __func__);
|
|
lws_cose_key_destroy(&cps->ck);
|
|
|
|
if (cps->pkey_set) {
|
|
lws_cose_key_set_destroy(cps->pkey_set);
|
|
cps->pkey_set = NULL;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
lws_cose_key_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
|
|
{
|
|
int n;
|
|
|
|
if (!el)
|
|
return;
|
|
|
|
for (n = 0; n < m; n++)
|
|
if (el[n].buf) {
|
|
/* wipe all key material when it goes out of scope */
|
|
lws_explicit_bzero(el[n].buf, el[n].len);
|
|
lws_free_set_NULL(el[n].buf);
|
|
el[n].len = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
lws_cose_key_destroy(struct lws_cose_key **pck)
|
|
{
|
|
struct lws_cose_key *ck = *pck;
|
|
|
|
if (!ck)
|
|
return;
|
|
|
|
lws_dll2_remove(&ck->list);
|
|
|
|
lws_cose_key_destroy_elements(ck->e, LWS_ARRAY_SIZE(ck->e));
|
|
lws_cose_key_destroy_elements(ck->meta, LWS_ARRAY_SIZE(ck->meta));
|
|
|
|
lws_free_set_NULL(*pck);
|
|
}
|
|
|
|
static int
|
|
lws_cose_key_set_memb_remove(struct lws_dll2 *d, void *user)
|
|
{
|
|
lws_cose_key_t *ck = lws_container_of(d, lws_cose_key_t, list);
|
|
|
|
lws_dll2_remove(d);
|
|
lws_cose_key_destroy(&ck);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
lws_cose_key_set_destroy(lws_dll2_owner_t *o)
|
|
{
|
|
lws_dll2_foreach_safe(o, NULL, lws_cose_key_set_memb_remove);
|
|
}
|
|
|
|
lws_cose_key_t *
|
|
lws_cose_key_from_set(lws_dll2_owner_t *set, const uint8_t *kid, size_t kl)
|
|
{
|
|
lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head(set)) {
|
|
lws_cose_key_t *ck = lws_container_of(p, lws_cose_key_t, list);
|
|
struct lws_gencrypto_keyelem *ke = &ck->meta[COSEKEY_META_KID];
|
|
|
|
if (!kid) /* always the first then */
|
|
return ck;
|
|
|
|
if (ke->buf && ke->len == (uint32_t)kl &&
|
|
!memcmp(ke->buf, kid, ke->len))
|
|
return ck;
|
|
|
|
} lws_end_foreach_dll(p);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
lws_cose_key_t *
|
|
lws_cose_key_generate(struct lws_context *context, cose_param_t cose_kty,
|
|
int use_mask, int bits, const char *curve,
|
|
const uint8_t *kid, size_t kl)
|
|
{
|
|
struct lws_gencrypto_keyelem *ke;
|
|
lws_cose_key_t *ck;
|
|
size_t sn;
|
|
int n;
|
|
|
|
ck = lws_zalloc(sizeof(*ck), __func__);
|
|
if (!ck)
|
|
return NULL;
|
|
|
|
ck->kty = cose_kty;
|
|
ck->private_key = 1;
|
|
|
|
if (use_mask & 0xfffe) {
|
|
int count = 0;
|
|
|
|
for (n = 1; n < 15; n++)
|
|
if (use_mask & (1 << n))
|
|
count++;
|
|
ke = &ck->meta[COSEKEY_META_KEY_OPS];
|
|
ke->buf = lws_malloc((size_t)count, __func__);
|
|
if (!ke->buf)
|
|
goto fail;
|
|
ke->len = (uint32_t)count;
|
|
count = 0;
|
|
for (n = 1; n < 15; n++)
|
|
if (use_mask & (1 << n))
|
|
ke->buf[count++] = (uint8_t)n;
|
|
}
|
|
|
|
if (kid) {
|
|
ke = &ck->meta[COSEKEY_META_KID];
|
|
ke->buf = lws_malloc(kl, __func__);
|
|
ke->len = (uint32_t)kl;
|
|
memcpy(ke->buf, kid, ke->len);
|
|
}
|
|
|
|
switch (cose_kty) {
|
|
case LWSCOSE_WKKTV_RSA:
|
|
{
|
|
struct lws_genrsa_ctx ctx;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
ck->gencrypto_kty = LWS_GENCRYPTO_KTY_RSA;
|
|
|
|
lwsl_notice("%s: generating %d bit RSA key\n",
|
|
__func__, bits);
|
|
n = lws_genrsa_new_keypair(context, &ctx,
|
|
LGRSAM_PKCS1_1_5,
|
|
ck->e, bits);
|
|
lws_genrsa_destroy(&ctx);
|
|
if (n) {
|
|
lwsl_err("%s: problem generating RSA key\n",
|
|
__func__);
|
|
goto fail;
|
|
}
|
|
}
|
|
break;
|
|
case LWSCOSE_WKKTV_SYMMETRIC:
|
|
|
|
ck->gencrypto_kty = LWS_GENCRYPTO_KTY_OCT;
|
|
sn = (unsigned int)lws_gencrypto_bits_to_bytes(bits);
|
|
ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K];
|
|
ke->buf = lws_malloc(sn, "oct");
|
|
if (!ke->buf)
|
|
goto fail;
|
|
ke->len = (uint32_t)sn;
|
|
if (lws_get_random(context, ke->buf, sn) != sn) {
|
|
lwsl_err("%s: problem getting random\n", __func__);
|
|
goto fail;
|
|
}
|
|
break;
|
|
|
|
case LWSCOSE_WKKTV_OKP:
|
|
case LWSCOSE_WKKTV_EC2:
|
|
{
|
|
struct lws_genec_ctx ctx;
|
|
|
|
ck->gencrypto_kty = LWS_GENCRYPTO_KTY_EC;
|
|
|
|
if (!curve) {
|
|
lwsl_err("%s: must have a named curve\n", __func__);
|
|
|
|
goto fail;
|
|
}
|
|
|
|
if (lws_genecdsa_create(&ctx, context, NULL))
|
|
goto fail;
|
|
|
|
ctx.genec_alg = LEGENEC_ECDSA;
|
|
lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__,
|
|
curve);
|
|
|
|
n = lws_genecdsa_new_keypair(&ctx, curve, ck->e);
|
|
lws_genec_destroy(&ctx);
|
|
if (n) {
|
|
lwsl_err("%s: problem generating ECDSA key\n", __func__);
|
|
goto fail;
|
|
}
|
|
/* trim the trailing NUL */
|
|
ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len = (uint32_t)strlen(curve);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
lwsl_err("%s: unknown kty\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
return ck;
|
|
|
|
fail:
|
|
lws_free_set_NULL(ck);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct lws_cose_key *
|
|
lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb,
|
|
void *user, const uint8_t *in, size_t len)
|
|
{
|
|
struct lws_cose_key_parse_state cps;
|
|
struct lecp_ctx ctx;
|
|
int m;
|
|
|
|
memset(&cps, 0, sizeof(cps));
|
|
|
|
cps.per_key_cb = cb;
|
|
cps.user = user;
|
|
cps.pkey_set = pkey_set;
|
|
cps.gencrypto_eidx = -1;
|
|
|
|
lecp_construct(&ctx, cb_cose_key, &cps, NULL, 0);
|
|
m = lecp_parse(&ctx, in, len);
|
|
lecp_destruct(&ctx);
|
|
|
|
if (m < 0) {
|
|
lwsl_notice("%s: parse got %d\n", __func__, m);
|
|
if (cps.pkey_set)
|
|
lws_cose_key_set_destroy(cps.pkey_set);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
switch (cps.ck->gencrypto_kty) {
|
|
case LWS_GENCRYPTO_KTY_UNKNOWN:
|
|
lwsl_notice("%s: missing or unknown ktys\n", __func__);
|
|
goto bail;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return cps.ck;
|
|
|
|
bail:
|
|
lws_cose_key_destroy(&cps.ck);
|
|
return NULL;
|
|
}
|
|
|
|
/* gencrypto element orering -> cose key parameters */
|
|
|
|
static const signed char ckp[3][12] = {
|
|
{ /* LWS_GENCRYPTO_KTY_OCT (1) */
|
|
/* LWS_GENCRYPTO_OCT_KEYEL_K */ LWSCOSE_WKSYMKP_KEY_VALUE,
|
|
},
|
|
{ /* LWS_GENCRYPTO_KTY_RSA (2) */
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_E */ LWSCOSE_WKKPRSA_E,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_N */ LWSCOSE_WKKPRSA_N,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_D */ LWSCOSE_WKKPRSA_D,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_P */ LWSCOSE_WKKPRSA_P,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_Q */ LWSCOSE_WKKPRSA_Q,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_DP */ LWSCOSE_WKKPRSA_DP,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_DQ */ LWSCOSE_WKKPRSA_DQ,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_QT */ LWSCOSE_WKKPRSA_QINV,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_OTHER */ LWSCOSE_WKKPRSA_OTHER,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_RI */ LWSCOSE_WKKPRSA_RI,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_DI */ LWSCOSE_WKKPRSA_DI,
|
|
/* LWS_GENCRYPTO_RSA_KEYEL_TI */ LWSCOSE_WKKPRSA_TI,
|
|
},
|
|
{ /* LWS_GENCRYPTO_KTY_EC (3) */
|
|
/* LWS_GENCRYPTO_EC_KEYEL_CRV */ LWSCOSE_WKECKP_CRV,
|
|
/* LWS_GENCRYPTO_EC_KEYEL_X */ LWSCOSE_WKECKP_X,
|
|
/* LWS_GENCRYPTO_EC_KEYEL_D */ LWSCOSE_WKECKP_D,
|
|
/* LWS_GENCRYPTO_EC_KEYEL_Y */ LWSCOSE_WKECKP_Y,
|
|
}
|
|
};
|
|
|
|
enum lws_lec_pctx_ret
|
|
lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags)
|
|
{
|
|
cose_param_t pa = 0;
|
|
int n;
|
|
|
|
if (!ctx->opaque[0]) {
|
|
|
|
ctx->opaque[0] = 1; /* map pair count */
|
|
ctx->opaque[1] = 1; /* element index */
|
|
ctx->opaque[2] = 0; /* public mask */
|
|
ctx->opaque[3] = 0; /* doing AGAIN */
|
|
|
|
switch (ck->gencrypto_kty) {
|
|
case LWS_GENCRYPTO_KTY_OCT:
|
|
/* nothing to differentiate */
|
|
ctx->opaque[2] = 1 << LWS_GENCRYPTO_OCT_KEYEL_K;
|
|
break;
|
|
case LWS_GENCRYPTO_KTY_RSA:
|
|
ctx->opaque[2] = 1 << LWS_GENCRYPTO_RSA_KEYEL_E;
|
|
break;
|
|
case LWS_GENCRYPTO_KTY_EC:
|
|
ctx->opaque[2] = (1 << LWS_GENCRYPTO_EC_KEYEL_X) |
|
|
(1 << LWS_GENCRYPTO_EC_KEYEL_Y);
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
|
|
if (flags & LWSJWKF_EXPORT_PRIVATE)
|
|
ctx->opaque[2] = 0xffff;
|
|
|
|
/*
|
|
* We first need to find out how many CBOR map pairs we are
|
|
* planning to create, so we can set a fixed length map of the
|
|
* right size.
|
|
*/
|
|
|
|
for (n = 0; n < (int)LWS_ARRAY_SIZE(ck->e); n++)
|
|
if ((ctx->opaque[2] & (1 << n)) && ck->e[n].buf)
|
|
ctx->opaque[0]++;
|
|
|
|
/*
|
|
* We always issue kty, others may be
|
|
*
|
|
* KID / ALG / KEY_OPS / BASE_IV
|
|
*/
|
|
|
|
if (ck->meta[COSEKEY_META_KID].buf)
|
|
ctx->opaque[0]++;
|
|
if (ck->meta[COSEKEY_META_ALG].buf)
|
|
ctx->opaque[0]++;
|
|
if (ck->meta[COSEKEY_META_KEY_OPS].buf)
|
|
ctx->opaque[0]++;
|
|
if (ck->meta[COSEKEY_META_BASE_IV].buf)
|
|
ctx->opaque[0]++;
|
|
|
|
lws_lec_int(ctx, LWS_CBOR_MAJTYP_MAP, 0, (uint64_t)ctx->opaque[0]);
|
|
lws_lec_signed(ctx, LWSCOSE_WKK_KTY);
|
|
lws_lec_signed(ctx, (int64_t)ck->kty);
|
|
|
|
if (ck->gencrypto_kty == LWS_GENCRYPTO_KTY_EC) {
|
|
struct lws_gencrypto_keyelem *ke =
|
|
&ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV];
|
|
|
|
if (!ke->buf ||
|
|
ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len > 10) {
|
|
lwsl_err("%s: no curve type\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
pa = lws_cose_curve_name_to_id((const char *)ke->buf);
|
|
lws_lec_signed(ctx, LWSCOSE_WKECKP_CRV);
|
|
if (pa)
|
|
lws_lec_signed(ctx, pa);
|
|
else
|
|
lws_lec_printf(ctx, "%.*s",
|
|
(int)ke->len, ke->buf);
|
|
}
|
|
|
|
|
|
ctx->opaque[1] = COSEKEY_META_KID;
|
|
}
|
|
|
|
/*
|
|
* Start from the second key meta, then do any elements that are set
|
|
*/
|
|
|
|
while (ctx->buf != ctx->end) {
|
|
struct lws_gencrypto_keyelem *ke = NULL;
|
|
int cose_key_param = 0;
|
|
|
|
if (lws_lec_scratch(ctx))
|
|
break;
|
|
|
|
if (ctx->opaque[1] == LWS_ARRAY_SIZE(ck->e) +
|
|
LWS_COUNT_COSE_KEY_ELEMENTS)
|
|
break;
|
|
|
|
if (ctx->opaque[1] >= LWS_COUNT_COSE_KEY_ELEMENTS) {
|
|
n = ctx->opaque[1] - LWS_COUNT_COSE_KEY_ELEMENTS;
|
|
|
|
if (ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC ||
|
|
n != LWS_GENCRYPTO_EC_KEYEL_CRV) {
|
|
/* we didn't already encode his curve */
|
|
|
|
if ((ctx->opaque[2] & (1 << n)) &&
|
|
ck->e[n].buf && ck->e[n].len) {
|
|
ke = &ck->e[n];
|
|
cose_key_param = ckp[ck->gencrypto_kty - 1][n];
|
|
}
|
|
}
|
|
} else
|
|
|
|
switch (ctx->opaque[1]) {
|
|
|
|
case COSEKEY_META_KID: /* bstr */
|
|
if (ck->meta[COSEKEY_META_KID].buf) {
|
|
ke = &ck->meta[COSEKEY_META_KID];
|
|
cose_key_param = LWSCOSE_WKK_KID;
|
|
// lwsl_hexdump_notice(ke->buf, ke->len);
|
|
}
|
|
break;
|
|
|
|
case COSEKEY_META_ALG: /* int, tstr */
|
|
if (ck->meta[COSEKEY_META_ALG].buf) {
|
|
ke = &ck->meta[COSEKEY_META_ALG];
|
|
cose_key_param = LWSCOSE_WKK_ALG;
|
|
}
|
|
break;
|
|
|
|
case COSEKEY_META_KEY_OPS: /* [ int ] */
|
|
if (!ck->meta[COSEKEY_META_KEY_OPS].buf)
|
|
break;
|
|
ke = &ck->meta[COSEKEY_META_KEY_OPS];
|
|
|
|
n = (int)ke->len;
|
|
if (n > 10)
|
|
n = 10;
|
|
|
|
/*
|
|
* We copy this array into scratch by hand now we
|
|
* made sure it will fit, we will never need AGAIN
|
|
*/
|
|
|
|
lws_lec_signed(ctx, LWSCOSE_WKK_KEY_OPS);
|
|
lws_lec_int(ctx, LWS_CBOR_MAJTYP_ARRAY, 0, (uint64_t)n);
|
|
memcpy(&ctx->scratch[ctx->scratch_len], ke->buf,
|
|
(size_t)n);
|
|
ctx->scratch_len = (uint8_t)(ctx->scratch_len + (uint8_t)n);
|
|
ke = NULL;
|
|
break;
|
|
|
|
case COSEKEY_META_BASE_IV: /* bstr */
|
|
if (ck->meta[COSEKEY_META_BASE_IV].buf) {
|
|
ke = &ck->meta[COSEKEY_META_BASE_IV];
|
|
cose_key_param = LWSCOSE_WKK_BASE_IV;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ke && ke->buf && ke->len) {
|
|
|
|
if (!ctx->opaque[3])
|
|
lws_lec_signed(ctx, cose_key_param);
|
|
|
|
/* binary string or text string? */
|
|
if (ctx->opaque[1] == COSEKEY_META_KID ||
|
|
ctx->opaque[1] == COSEKEY_META_BASE_IV ||
|
|
ctx->opaque[1] >= LWS_COUNT_COSE_KEY_ELEMENTS)
|
|
n = (int)lws_lec_printf(ctx, "%.*b",
|
|
(int)ke->len, ke->buf);
|
|
else
|
|
n = (int)lws_lec_printf(ctx, "%.*s",
|
|
(int)ke->len, ke->buf);
|
|
|
|
switch (n) {
|
|
case LWS_LECPCTX_RET_AGAIN:
|
|
ctx->opaque[3] = 1;
|
|
/* dump what we have and come back */
|
|
continue;
|
|
case LWS_LECPCTX_RET_FAIL:
|
|
goto fail;
|
|
case LWS_LECPCTX_RET_FINISHED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* move on if we finished that guy */
|
|
ctx->opaque[1]++;
|
|
ctx->opaque[3] = 0;
|
|
}
|
|
|
|
ctx->used = lws_ptr_diff_size_t(ctx->buf, ctx->start);
|
|
|
|
if (ctx->buf == ctx->end || ctx->scratch_len)
|
|
return LWS_LECPCTX_RET_AGAIN;
|
|
|
|
ctx->opaque[0] = 0;
|
|
|
|
return LWS_LECPCTX_RET_FINISHED;
|
|
|
|
fail:
|
|
lwsl_notice("%s: failed\n", __func__);
|
|
|
|
ctx->opaque[0] = 0;
|
|
|
|
return LWS_LECPCTX_RET_FAIL;
|
|
}
|