mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-23 00:00:06 +01:00
289 lines
5.7 KiB
C
289 lines
5.7 KiB
C
![]() |
/*
|
||
|
* libwebsockets - JSON Web Key support
|
||
|
*
|
||
|
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation:
|
||
|
* version 2.1 of the License.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||
|
* MA 02110-1301 USA
|
||
|
*/
|
||
|
|
||
|
#include "private-libwebsockets.h"
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
static const char * const jwk_tok[] = {
|
||
|
"e", "n", "d", "p", "q", "dp", "dq", "qi", "kty", "k",
|
||
|
};
|
||
|
|
||
|
static int
|
||
|
_lws_jwk_set_element(struct lws_genrsa_element *e, char *in, int len)
|
||
|
{
|
||
|
int dec_size = ((len * 3) / 4) + 4, n;
|
||
|
|
||
|
e->buf = lws_malloc(dec_size, "jwk");
|
||
|
if (!e->buf)
|
||
|
return -1;
|
||
|
|
||
|
n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
|
||
|
if (n < 0)
|
||
|
return -1;
|
||
|
e->len = n;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
struct cb_lws_jwk {
|
||
|
struct lws_jwk *s;
|
||
|
char *b64;
|
||
|
int b64max;
|
||
|
int pos;
|
||
|
};
|
||
|
|
||
|
static signed char
|
||
|
cb_jwk(struct lejp_ctx *ctx, char reason)
|
||
|
{
|
||
|
struct cb_lws_jwk *cbs = (struct cb_lws_jwk *)ctx->user;
|
||
|
struct lws_jwk *s = cbs->s;
|
||
|
int idx;
|
||
|
|
||
|
if (reason == LEJPCB_VAL_STR_START)
|
||
|
cbs->pos = 0;
|
||
|
|
||
|
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||
|
return 0;
|
||
|
|
||
|
switch (ctx->path_match - 1) {
|
||
|
case JWK_KTY:
|
||
|
strncpy(s->keytype, ctx->buf, sizeof(s->keytype) - 1);
|
||
|
s->keytype[sizeof(s->keytype) - 1] = '\0';
|
||
|
if (!strcmp(ctx->buf, "oct")) {
|
||
|
break;
|
||
|
}
|
||
|
if (!strcmp(ctx->buf, "RSA")) {
|
||
|
break;
|
||
|
}
|
||
|
return -1;
|
||
|
|
||
|
case JWK_KEY:
|
||
|
if (strcmp(s->keytype, "oct"))
|
||
|
return -1;
|
||
|
idx = JWK_KEY_E;
|
||
|
goto read_element1;
|
||
|
|
||
|
case JWK_KEY_N:
|
||
|
case JWK_KEY_E:
|
||
|
case JWK_KEY_D:
|
||
|
case JWK_KEY_P:
|
||
|
case JWK_KEY_Q:
|
||
|
case JWK_KEY_DP:
|
||
|
case JWK_KEY_DQ:
|
||
|
case JWK_KEY_QI:
|
||
|
idx = ctx->path_match - 1;
|
||
|
goto read_element;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
read_element:
|
||
|
if (strcmp(s->keytype, "RSA"))
|
||
|
return -1;
|
||
|
|
||
|
read_element1:
|
||
|
|
||
|
if (cbs->pos + ctx->npos >= cbs->b64max)
|
||
|
return -1;
|
||
|
|
||
|
memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
|
||
|
cbs->pos += ctx->npos;
|
||
|
|
||
|
if (reason == LEJPCB_VAL_STR_CHUNK)
|
||
|
return 0;
|
||
|
|
||
|
if (_lws_jwk_set_element(&s->el.e[idx], cbs->b64, cbs->pos) < 0) {
|
||
|
lws_jwk_destroy_genrsa_elements(&s->el);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
LWS_VISIBLE int
|
||
|
lws_jwk_import(struct lws_jwk *s, const char *in, size_t len)
|
||
|
{
|
||
|
struct lejp_ctx jctx;
|
||
|
struct cb_lws_jwk cbs;
|
||
|
const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
|
||
|
char b64[b64max];
|
||
|
int m;
|
||
|
|
||
|
memset(s, 0, sizeof(*s));
|
||
|
cbs.s = s;
|
||
|
cbs.b64 = b64;
|
||
|
cbs.b64max = b64max;
|
||
|
cbs.pos = 0;
|
||
|
lejp_construct(&jctx, cb_jwk, &cbs, jwk_tok, ARRAY_SIZE(jwk_tok));
|
||
|
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);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
LWS_VISIBLE void
|
||
|
lws_jwk_destroy(struct lws_jwk *s)
|
||
|
{
|
||
|
lws_jwk_destroy_genrsa_elements(&s->el);
|
||
|
}
|
||
|
|
||
|
LWS_VISIBLE int
|
||
|
lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
|
||
|
{
|
||
|
char *start = p, *end = &p[len - 1];
|
||
|
int n, m, limit = LWS_COUNT_RSA_ELEMENTS;
|
||
|
|
||
|
p += lws_snprintf(p, end - p, "{\"kty\":\"%s\",", s->keytype);
|
||
|
|
||
|
if (!strcmp(s->keytype, "oct")) {
|
||
|
if (!s->el.e[JWK_KEY_E].buf)
|
||
|
return -1;
|
||
|
|
||
|
p += lws_snprintf(p, end - p, "\"k\":\"");
|
||
|
n = lws_jws_base64_enc((const char *)s->el.e[JWK_KEY_E].buf,
|
||
|
s->el.e[JWK_KEY_E].len, p,
|
||
|
end - p - 4);
|
||
|
if (n < 0) {
|
||
|
lwsl_notice("%s: enc failed\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
p += n;
|
||
|
|
||
|
p += lws_snprintf(p, end - p, "\"}");
|
||
|
|
||
|
return p - start;
|
||
|
}
|
||
|
|
||
|
if (!strcmp(s->keytype, "RSA")) {
|
||
|
if (!s->el.e[JWK_KEY_E].buf ||
|
||
|
!s->el.e[JWK_KEY_N].buf ||
|
||
|
(private && (!s->el.e[JWK_KEY_D].buf ||
|
||
|
!s->el.e[JWK_KEY_P].buf ||
|
||
|
!s->el.e[JWK_KEY_Q].buf))
|
||
|
) {
|
||
|
lwsl_notice("%s: not enough elements filled\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!private)
|
||
|
limit = JWK_KEY_N + 1;
|
||
|
|
||
|
for (n = 0; n < limit; n++) {
|
||
|
if (!s->el.e[n].buf)
|
||
|
continue;
|
||
|
lwsl_info("%d: len %d\n", n, s->el.e[n].len);
|
||
|
|
||
|
if (n)
|
||
|
p += lws_snprintf(p, end - p, ",");
|
||
|
p += lws_snprintf(p, end - p, "\"%s\":\"", jwk_tok[n]);
|
||
|
m = lws_jws_base64_enc((const char *)s->el.e[n].buf,
|
||
|
s->el.e[n].len, p,
|
||
|
end - p - 4);
|
||
|
if (m < 0) {
|
||
|
lwsl_notice("%s: enc2 failed inlen %d outlen %d\n", __func__, (int)s->el.e[n].len, (int)(end-p-4));
|
||
|
return -1;
|
||
|
}
|
||
|
p += m;
|
||
|
*p++ = '\"';
|
||
|
}
|
||
|
|
||
|
p += lws_snprintf(p, end - p, "}");
|
||
|
|
||
|
return p - start;
|
||
|
}
|
||
|
|
||
|
lwsl_err("%s: unknown key type %s\n", __func__, s->keytype);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
LWS_VISIBLE int
|
||
|
lws_jwk_load(struct lws_jwk *s, const char *filename)
|
||
|
{
|
||
|
int buflen = 4096;
|
||
|
char *buf = lws_malloc(buflen, "jwk-load");
|
||
|
int fd, n;
|
||
|
|
||
|
if (!buf)
|
||
|
return -1;
|
||
|
|
||
|
fd = open(filename, O_RDONLY);
|
||
|
if (fd == -1)
|
||
|
goto bail;
|
||
|
|
||
|
n = read(fd, buf, buflen);
|
||
|
close(fd);
|
||
|
if (n < 0)
|
||
|
goto bail;
|
||
|
|
||
|
n = lws_jwk_import(s, buf, n);
|
||
|
lws_free(buf);
|
||
|
|
||
|
return n;
|
||
|
bail:
|
||
|
lws_free(buf);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
LWS_VISIBLE int
|
||
|
lws_jwk_save(struct lws_jwk *s, const char *filename)
|
||
|
{
|
||
|
int buflen = 4096;
|
||
|
char *buf = lws_malloc(buflen, "jwk-save");
|
||
|
int fd, n, m;
|
||
|
|
||
|
if (!buf)
|
||
|
return -1;
|
||
|
|
||
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||
|
if (fd == -1)
|
||
|
goto bail;
|
||
|
|
||
|
n = lws_jwk_export(s, 1, buf, buflen);
|
||
|
if (n < 0) {
|
||
|
close(fd);
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
m = write(fd, buf, n);
|
||
|
close(fd);
|
||
|
lws_free(buf);
|
||
|
if (m < 0 || m != n)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
bail:
|
||
|
lws_free(buf);
|
||
|
|
||
|
return -1;
|
||
|
}
|