libwebsockets/lib/jose/jwk/jwk.c
Andy Green 4db2ff872b cose: keys and signing + validation
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
2021-08-31 05:45:35 +01:00

298 lines
6.9 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.
*
* 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;
}