| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010 - 2021 Andy Green <[email protected]> |
| * |
| * 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. |
| * |
| * Shared JWK handling that's the same whether JOSE or COSE |
| */ |
| |
| #include "private-lib-core.h" |
| #include "private-lib-jose.h" |
| |
| static const char *meta_names[] = { |
| "kty", "kid", "use", "key_ops", "x5c", "alg" |
| }; |
| |
| 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; |
| } |
| |
| int |
| _lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len) |
| { |
| e->buf = lws_malloc(len + 1, "jwk"); |
| if (!e->buf) |
| return -1; |
| |
| memcpy(e->buf, in, len); |
| e->buf[len] = '\0'; |
| e->len = (uint32_t)len; |
| |
| 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)); |
| } |
| |
| void |
| lws_jwk_init_jps(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; |
| jps->seen = 0; |
| jps->cose_state = 0; |
| } |
| |
| int |
| lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len) |
| { |
| unsigned int ulen = (unsigned int)len; |
| |
| jwk->e[LWS_GENCRYPTO_KTY_OCT].buf = lws_malloc(ulen, __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 = ulen; |
| |
| memcpy(jwk->e[LWS_GENCRYPTO_KTY_OCT].buf, key, ulen); |
| |
| 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 = (int)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 = (unsigned int)lws_gencrypto_bits_to_bytes(bits); |
| jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct"); |
| if (!jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf) |
| return 1; |
| jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = (uint32_t)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_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32) |
| { |
| struct lws_genhash_ctx hash_ctx; |
| size_t tmpsize = 2536; |
| char *tmp; |
| int n, m = (int)tmpsize; |
| |
| tmp = lws_malloc(tmpsize, "rfc7638 tmp"); |
| |
| n = lws_jwk_export(jwk, LWSJWKF_EXPORT_NOCRLF, tmp, &m); |
| if (n < 0) |
| goto bail; |
| |
| if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) |
| goto bail; |
| |
| if (lws_genhash_update(&hash_ctx, tmp, (unsigned int)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((unsigned int)len, __func__); |
| if (!jwk->meta[idx].buf) |
| return 1; |
| jwk->meta[idx].len = (uint32_t)(unsigned int)len; |
| memcpy(jwk->meta[idx].buf, in, (unsigned int)len); |
| |
| return 0; |
| } |
| |