/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2019 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-jose.h" #if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) #include #endif static const char * const kty_names[] = { "unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */ "oct", /* LWS_GENCRYPTO_KTY_OCT */ "RSA", /* LWS_GENCRYPTO_KTY_RSA */ "EC" /* LWS_GENCRYPTO_KTY_EC */ }; /* * These are the entire legal token set for names in jwk. * * The first version is used to parse a detached single jwk that don't have any * parent JSON context. The second version is used to parse full jwk objects * that has a "keys": [ ] array containing the keys. */ static const char * const jwk_tok[] = { "keys[]", /* dummy */ "e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */ "kty", /* generic */ "k", /* symmetric key data */ "crv", "x", "y", /* EC (also "D") */ "kid", /* generic */ "use" /* mutually exclusive with "key_ops" */, "key_ops" /* mutually exclusive with "use" */, "x5c", /* generic */ "alg" /* generic */ }, * const jwk_outer_tok[] = { "keys[]", "keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp", "keys[].dq", "keys[].qi", "keys[].kty", "keys[].k", /* generic */ "keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */ "keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */, "keys[].key_ops", /* mutually exclusive with "use" */ "keys[].x5c", "keys[].alg" }; /* information about each token declared above */ #define F_M (1 << 9) /* Mandatory for key type */ #define F_B64 (1 << 10) /* Base64 coded octets */ #define F_B64U (1 << 11) /* Base64 Url coded octets */ #define F_META (1 << 12) /* JWK key metainformation */ #define F_RSA (1 << 13) /* RSA key */ #define F_EC (1 << 14) /* Elliptic curve key */ #define F_OCT (1 << 15) /* octet key */ static unsigned short tok_map[] = { F_RSA | F_EC | F_OCT | F_META | 0xff, F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E, F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N, F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D, F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P, F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q, F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP, F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ, F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI, F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY, F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K, F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV, F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X, F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y, F_RSA | F_EC | F_OCT | F_META | JWK_META_KID, F_RSA | F_EC | F_OCT | F_META | JWK_META_USE, F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS, F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C, F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG, }; static const char *meta_names[] = { "kty", "kid", "use", "key_ops", "x5c", "alg" }; struct lexico { const char *name; int idx; char meta; } lexico_ec[] = { { "alg", JWK_META_ALG, 1 }, { "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 }, { "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 }, { "key_ops", JWK_META_KEY_OPS, 1 }, { "kid", JWK_META_KID, 1 }, { "kty", JWK_META_KTY, 1 }, { "use", JWK_META_USE, 1 }, { "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 }, { "x5c", JWK_META_X5C, 1 }, { "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 } }, lexico_oct[] = { { "alg", JWK_META_ALG, 1 }, { "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 }, { "key_ops", JWK_META_KEY_OPS, 1 }, { "kid", JWK_META_KID, 1 }, { "kty", JWK_META_KTY, 1 }, { "use", JWK_META_USE, 1 }, { "x5c", JWK_META_X5C, 1 } }, lexico_rsa[] = { { "alg", JWK_META_ALG, 1 }, { "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 }, { "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 }, { "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 }, { "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 }, { "key_ops", JWK_META_KEY_OPS, 1 }, { "kid", JWK_META_KID, 1 }, { "kty", JWK_META_KTY, 1 }, { "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 }, { "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 }, { "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 }, { "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 }, { "use", JWK_META_USE, 1 }, { "x5c", JWK_META_X5C, 1 } }; static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 }; static const char *oct_names[] = { "k" }; static const char oct_b64[] = { 1 }; static const char *rsa_names[] = { "e", "n", "d", "p", "q", "dp", "dq", "qi" }; static const char rsa_b64[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; static const char *ec_names[] = { "crv", "x", "d", "y", }; static const char ec_b64[] = { 0, 1, 1, 1 }; int lws_jwk_dump(struct lws_jwk *jwk) { const char **enames, *b64; int elems; int n; (void)enames; (void)meta_names; switch (jwk->kty) { default: case LWS_GENCRYPTO_KTY_UNKNOWN: lwsl_err("%s: jwk %p: unknown type\n", __func__, jwk); return 1; case LWS_GENCRYPTO_KTY_OCT: elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT; enames = oct_names; b64 = oct_b64; break; case LWS_GENCRYPTO_KTY_RSA: elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT; enames = rsa_names; b64 = rsa_b64; break; case LWS_GENCRYPTO_KTY_EC: elems = LWS_GENCRYPTO_EC_KEYEL_COUNT; enames = ec_names; b64 = ec_b64; break; } lwsl_info("%s: jwk %p\n", __func__, jwk); for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++) { if (jwk->meta[n].buf && meta_b64[n]) { lwsl_info(" meta: %s\n", meta_names[n]); lwsl_hexdump_info(jwk->meta[n].buf, jwk->meta[n].len); } if (jwk->meta[n].buf && !meta_b64[n]) lwsl_info(" meta: %s: '%s'\n", meta_names[n], jwk->meta[n].buf); } for (n = 0; n < elems; n++) { if (jwk->e[n].buf && b64[n]) { lwsl_info(" e: %s\n", enames[n]); lwsl_hexdump_info(jwk->e[n].buf, jwk->e[n].len); } if (jwk->e[n].buf && !b64[n]) lwsl_info(" e: %s: '%s'\n", enames[n], jwk->e[n].buf); } return 0; } static int _lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, int len) { e->buf = lws_malloc(len + 1, "jwk"); if (!e->buf) return -1; memcpy(e->buf, in, len); e->buf[len] = '\0'; e->len = len; return 0; } static int _lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len) { int dec_size = lws_base64_size(len), n; e->buf = lws_malloc(dec_size, "jwk"); if (!e->buf) return -1; /* same decoder accepts both url or original styles */ n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1); if (n < 0) return -1; e->len = n; return 0; } static int _lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len) { int dec_size = lws_base64_size(len), n; e->buf = lws_malloc(dec_size, "jwk"); if (!e->buf) return -1; /* same decoder accepts both url or original styles */ n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1); if (n < 0) return -1; e->len = n; return 0; } void lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m) { int n; 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_jwk_destroy(struct lws_jwk *jwk) { lws_jwk_destroy_elements(jwk->e, LWS_ARRAY_SIZE(jwk->e)); lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->meta)); } static signed char cb_jwk(struct lejp_ctx *ctx, char reason) { struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user; struct lws_jwk *jwk = jps->jwk; unsigned int idx, poss, n; char dotstar[64]; if (reason == LEJPCB_VAL_STR_START) jps->pos = 0; if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1) /* * new keys[] member is starting * * Until we see some JSON names, it could be anything... * there is no requirement for kty to be given first and eg, * ACME specifies the keys must be ordered in lexographic * order - where kty is not first. */ jps->possible = F_RSA | F_EC | F_OCT; if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) { /* we completed parsing a key */ if (jps->per_key_cb && jps->possible) { if (jps->per_key_cb(jps->jwk, jps->user)) { lwsl_notice("%s: user cb halts import\n", __func__); return -2; } /* clear it down */ lws_jwk_destroy(jps->jwk); jps->possible = 0; } } if (reason == LEJPCB_COMPLETE) { /* * Now we saw the whole jwk and know the key type, let'jwk insist * that as a whole, it must be consistent and complete. * * The tracking of ->possible bits from even before we know the * kty already makes certain we cannot have key element members * defined that are inconsistent with the key type. */ for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++) /* * All mandataory elements for the key type * must be present */ if ((tok_map[n] & jps->possible) && ( ((tok_map[n] & (F_M | F_META)) == (F_M | F_META) && !jwk->meta[tok_map[n] & 0xff].buf) || ((tok_map[n] & (F_M | F_META)) == F_M && !jwk->e[tok_map[n] & 0xff].buf))) { lwsl_notice("%s: missing %s\n", __func__, jwk_tok[n]); return -3; } /* * When the key may be public or public + private, ensure the * intra-key members related to that are consistent. * * Only RSA keys need extra care, since EC keys are already * confirmed by making CRV, X and Y mandatory and only D * (the singular private part) optional. For RSA, N and E are * also already known to be present using mandatory checking. */ /* * If a private key, it must have all D, P and Q. Public key * must have none of them. */ if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) && (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) && (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) || (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf && jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) ) { lwsl_notice("%s: RSA requires D, P and Q for private\n", __func__); return -3; } /* * If the precomputed private key terms appear, they must all * appear together. */ if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) && (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) && (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) || (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf && jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf && jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) ) { lwsl_notice("%s: RSA DP, DQ, QI must all appear " "or none\n", __func__); return -3; } /* * The precomputed private key terms must not appear without * the private key itself also appearing. */ if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && !jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) { lwsl_notice("%s: RSA DP, DQ, QI can appear only with " "private key\n", __func__); return -3; } if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA || jwk->kty == LWS_GENCRYPTO_KTY_EC) && jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) jwk->private_key = 1; } if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) return 0; if (ctx->path_match == 0 + 1) return 0; idx = tok_map[ctx->path_match - 1]; if ((idx & 0xff) == 0xff) return 0; switch (idx) { /* note: kty is not necessarily first... we have to keep track of * what could match given which element names have already been * seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk * not trying to tell us that it'jwk RSA now when we saw a "crv" * earlier) and then reduce the possibilities to just the one that * kty told. */ case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY: if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) { if (!(jps->possible & F_OCT)) goto elements_mismatch; jwk->kty = LWS_GENCRYPTO_KTY_OCT; jps->possible = F_OCT; goto cont; } if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) { if (!(jps->possible & F_RSA)) goto elements_mismatch; jwk->kty = LWS_GENCRYPTO_KTY_RSA; jps->possible = F_RSA; goto cont; } if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) { if (!(jps->possible & F_EC)) goto elements_mismatch; jwk->kty = LWS_GENCRYPTO_KTY_EC; jps->possible = F_EC; goto cont; } lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); lwsl_err("%s: Unknown KTY '%s'\n", __func__, dotstar); return -1; default: cont: if (jps->pos + ctx->npos >= (int)sizeof(jps->b64)) goto bail; memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos); jps->pos += ctx->npos; if (reason == LEJPCB_VAL_STR_CHUNK) return 0; /* chunking has been collated */ poss = idx & (F_RSA | F_EC | F_OCT); jps->possible &= poss; if (!jps->possible) goto elements_mismatch; if (idx & F_META) { if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f], jps->b64, jps->pos) < 0) goto bail; break; } if (idx & F_B64U) { /* key data... do the base64 decode as needed */ if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f], jps->b64, jps->pos) < 0) goto bail; if (jwk->e[idx & 0x7f].len > LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) { lwsl_notice("%s: oversize keydata\n", __func__); goto bail; } return 0; } if (idx & F_B64) { /* cert data... do non-urlcoded base64 decode */ if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f], jps->b64, jps->pos) < 0) goto bail; return 0; } if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f], jps->b64, jps->pos) < 0) goto bail; break; } return 0; elements_mismatch: lwsl_err("%s: jwk elements mismatch\n", __func__); bail: lwsl_err("%s: element failed\n", __func__); return -1; } void lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps, struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user) { if (jwk) memset(jwk, 0, sizeof(*jwk)); jps->jwk = jwk; jps->possible = F_RSA | F_EC | F_OCT; jps->per_key_cb = cb; jps->user = user; jps->pos = 0; lejp_construct(jctx, cb_jwk, jps, cb ? jwk_outer_tok: jwk_tok, LWS_ARRAY_SIZE(jwk_tok)); } int lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len) { jwk->e[LWS_GENCRYPTO_KTY_OCT].buf = lws_malloc(len, __func__); if (!jwk->e[LWS_GENCRYPTO_KTY_OCT].buf) return -1; jwk->kty = LWS_GENCRYPTO_KTY_OCT; jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = len; memcpy(jwk->e[LWS_GENCRYPTO_KTY_OCT].buf, key, len); return 0; } int lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk, enum lws_gencrypto_kty kty, int bits, const char *curve) { size_t sn; int n; memset(jwk, 0, sizeof(*jwk)); jwk->kty = kty; jwk->private_key = 1; switch (kty) { case LWS_GENCRYPTO_KTY_RSA: { struct lws_genrsa_ctx ctx; lwsl_notice("%s: generating %d bit RSA key\n", __func__, bits); n = lws_genrsa_new_keypair(context, &ctx, LGRSAM_PKCS1_1_5, jwk->e, bits); lws_genrsa_destroy(&ctx); if (n) { lwsl_err("%s: problem generating RSA key\n", __func__); return 1; } } break; case LWS_GENCRYPTO_KTY_OCT: sn = lws_gencrypto_bits_to_bytes(bits); jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct"); jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = sn; if (lws_get_random(context, jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, sn) != sn) { lwsl_err("%s: problem getting random\n", __func__); return 1; } break; case LWS_GENCRYPTO_KTY_EC: { struct lws_genec_ctx ctx; if (!curve) { lwsl_err("%s: must have a named curve\n", __func__); return 1; } if (lws_genecdsa_create(&ctx, context, NULL)) return 1; lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__, curve); n = lws_genecdsa_new_keypair(&ctx, curve, jwk->e); lws_genec_destroy(&ctx); if (n) { lwsl_err("%s: problem generating ECDSA key\n", __func__); return 1; } } break; case LWS_GENCRYPTO_KTY_UNKNOWN: default: lwsl_err("%s: unknown kty\n", __func__); return 1; } return 0; } int lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user, const char *in, size_t len) { struct lejp_ctx jctx; struct lws_jwk_parse_state jps; int m; lws_jwk_init_jps(&jctx, &jps, jwk, cb, user); m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len); lejp_destruct(&jctx); if (m < 0) { lwsl_notice("%s: parse got %d\n", __func__, m); lws_jwk_destroy(jwk); return -1; } switch (jwk->kty) { case LWS_GENCRYPTO_KTY_UNKNOWN: lwsl_notice("%s: missing or unknown kyt\n", __func__); lws_jwk_destroy(jwk); return -1; default: break; } return 0; } int lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len) { char *start = p, *end = &p[*len - 1]; int n, m, limit, first = 1, asym = 0; struct lexico *l; /* RFC7638 lexicographic order requires * RSA: e -> kty -> n * oct: k -> kty * * ie, meta and key data elements appear interleaved in name alpha order */ p += lws_snprintf(p, end - p, "{"); switch (jwk->kty) { case LWS_GENCRYPTO_KTY_OCT: l = lexico_oct; limit = LWS_ARRAY_SIZE(lexico_oct); break; case LWS_GENCRYPTO_KTY_RSA: l = lexico_rsa; limit = LWS_ARRAY_SIZE(lexico_rsa); asym = 1; break; case LWS_GENCRYPTO_KTY_EC: l = lexico_ec; limit = LWS_ARRAY_SIZE(lexico_ec); asym = 1; break; default: return -1; } for (n = 0; n < limit; n++) { const char *q, *q_end; char tok[12]; int pos = 0, f = 1; if ((l->meta & 1) && (jwk->meta[l->idx].buf || l->idx == (int)JWK_META_KTY)) { switch (l->idx) { case JWK_META_KTY: if (!first) *p++ = ','; first = 0; p += lws_snprintf(p, end - p, "\"%s\":\"%s\"", l->name, kty_names[jwk->kty]); break; case JWK_META_KEY_OPS: if (!first) *p++ = ','; first = 0; q = (const char *)jwk->meta[l->idx].buf; q_end = q + jwk->meta[l->idx].len; p += lws_snprintf(p, end - p, "\"%s\":[", l->name); /* * For the public version, usages that * require the private part must be * snipped */ while (q < q_end) { if (*q != ' ' && pos < (int)sizeof(tok) - 1) { tok[pos++] = *q++; if (q != q_end) continue; } tok[pos] = '\0'; pos = 0; if ((flags & LWSJWKF_EXPORT_PRIVATE) || !asym || (strcmp(tok, "sign") && strcmp(tok, "encrypt"))) { if (!f) *p++ = ','; f = 0; p += lws_snprintf(p, end - p, "\"%s\"", tok); } q++; } *p++ = ']'; break; default: /* both sig and enc require asym private key */ if (!(flags & LWSJWKF_EXPORT_PRIVATE) && asym && l->idx == (int)JWK_META_USE) break; if (!first) *p++ = ','; first = 0; p += lws_snprintf(p, end - p, "\"%s\":\"", l->name); lws_strnncpy(p, (const char *)jwk->meta[l->idx].buf, jwk->meta[l->idx].len, end - p); p += strlen(p); p += lws_snprintf(p, end - p, "\""); break; } } if ((!(l->meta & 1)) && jwk->e[l->idx].buf && ((flags & LWSJWKF_EXPORT_PRIVATE) || !(l->meta & 2))) { if (!first) *p++ = ','; first = 0; p += lws_snprintf(p, end - p, "\"%s\":\"", l->name); if (jwk->kty == LWS_GENCRYPTO_KTY_EC && l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV) { lws_strnncpy(p, (const char *)jwk->e[l->idx].buf, jwk->e[l->idx].len, end - p); m = strlen(p); } else m = lws_jws_base64_enc( (const char *)jwk->e[l->idx].buf, jwk->e[l->idx].len, p, end - p - 4); if (m < 0) { lwsl_notice("%s: enc failed\n", __func__); return -1; } p += m; p += lws_snprintf(p, end - p, "\""); } l++; } p += lws_snprintf(p, end - p, (flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n"); *len -= p - start; return p - start; } int lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32) { struct lws_genhash_ctx hash_ctx; int tmpsize = 2536, n; char *tmp; tmp = lws_malloc(tmpsize, "rfc7638 tmp"); n = lws_jwk_export(jwk, LWSJWKF_EXPORT_NOCRLF, tmp, &tmpsize); if (n < 0) goto bail; if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) goto bail; if (lws_genhash_update(&hash_ctx, tmp, n)) { lws_genhash_destroy(&hash_ctx, NULL); goto bail; } lws_free(tmp); if (lws_genhash_destroy(&hash_ctx, digest32)) return -1; return 0; bail: lws_free(tmp); return -1; } int lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx, const char *in, int len) { jwk->meta[idx].buf = lws_malloc(len, __func__); if (!jwk->meta[idx].buf) return 1; jwk->meta[idx].len = len; memcpy(jwk->meta[idx].buf, in, len); return 0; } int lws_jwk_load(struct lws_jwk *jwk, const char *filename, lws_jwk_key_import_callback cb, void *user) { int buflen = 4096; char *buf = lws_malloc(buflen, "jwk-load"); int n; if (!buf) return -1; n = lws_plat_read_file(filename, buf, buflen); if (n < 0) goto bail; n = lws_jwk_import(jwk, cb, user, buf, n); lws_free(buf); return n; bail: lws_free(buf); return -1; } int lws_jwk_save(struct lws_jwk *jwk, const char *filename) { int buflen = 4096; char *buf = lws_malloc(buflen, "jwk-save"); int n, m; if (!buf) return -1; n = lws_jwk_export(jwk, LWSJWKF_EXPORT_PRIVATE, buf, &buflen); if (n < 0) goto bail; m = lws_plat_write_file(filename, buf, n); lws_free(buf); if (m) return -1; return 0; bail: lws_free(buf); return -1; }