1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

jwt: sign and create jws helper

This commit is contained in:
Andy Green 2020-07-10 18:27:45 +01:00
parent 27bb7ed5a0
commit 865f2e4a6c
3 changed files with 181 additions and 7 deletions

View file

@ -406,8 +406,8 @@ lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
* lws_jwt_signed_validate() - check a compact JWT against a key and alg
*
* \param ctx: the lws_context
* \param alg_list: the expected alg name, like "ES512"
* \param jwk: the key for checking the signature
* \param alg_list: the expected alg name, like "ES512"
* \param com: the compact JWT
* \param len: the length of com
* \param temp: a temp scratchpad
@ -426,7 +426,32 @@ lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
* transformations of the B64 JWS in the JWT.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_signed_validate(struct lws_context *ctx, const char *alg_list,
struct lws_jwk *jwk, const char *com, size_t len,
lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk,
const char *alg_list, const char *com, size_t len,
char *temp, int tl, char *out, size_t *out_len);
/**
* lws_jwt_sign_compact() - generate a compact JWT using a key and alg
*
* \param ctx: the lws_context
* \param jwk: the signing key
* \param alg: the signing alg name, like "ES512"
* \param out: the output buffer to hold the signed JWT in compact form
* \param out_len: on entry, the length of out; on exit, the used amount of out
* \param temp: a temp scratchpad
* \param tl: available length of temp scratchpad
* \param format: a printf style format specification
* \param ...: zero or more args for the format specification
*
* Creates a JWT in a single step, from the format string and args through to
* outputting a well-formed compact JWT representation in out.
*
* Returns 0 if all is well and *out_len is the amount of data in out, else
* nonzero if failed. Temp must be large enough to hold various intermediate
* representations.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk,
const char *alg, char *out, size_t *out_len, char *temp,
int tl, const char *format, ...) LWS_FORMAT(8);
///@}

View file

@ -951,14 +951,15 @@ lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len)
}
int
lws_jwt_signed_validate(struct lws_context *ctx, const char *alg_list,
struct lws_jwk *jwk, const char *com, size_t len,
lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk,
const char *alg_list, const char *com, size_t len,
char *temp, int tl, char *out, size_t *out_len)
{
struct lws_tokenize ts;
struct lws_jose jose;
struct lws_jws jws;
size_t n, r = 1;
int otl = tl;
memset(&jws, 0, sizeof(jws));
lws_jose_init(&jose);
@ -974,13 +975,15 @@ lws_jwt_signed_validate(struct lws_context *ctx, const char *alg_list,
goto bail;
}
temp += otl - tl;
otl = tl;
/*
* Parse the JOSE header
*/
if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE],
lws_concat_temp(temp, tl), &tl) < 0) {
jws.map.len[LJWS_JOSE], temp, &tl) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
goto bail;
}
@ -1034,3 +1037,112 @@ bail:
return r;
}
int
lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk,
const char *alg, char *out, size_t *out_len, char *temp,
int tl, const char *format, ...)
{
int n, r = 1, otl = tl;
struct lws_jose jose;
struct lws_jws jws;
va_list ap;
char *q;
lws_jws_init(&jws, jwk, ctx);
lws_jose_init(&jose);
if (lws_gencrypto_jws_alg_to_definition(alg, &jose.alg)) {
lwsl_err("%s: unknown alg %s\n", __func__, alg);
goto bail;
}
/* create JOSE header, also needed for output */
if (lws_jws_alloc_element(&jws.map, LJWS_JOSE, temp, &tl,
strlen(alg) + 10, 0)) {
lwsl_err("%s: temp space too small\n", __func__);
return 1;
}
jws.map.len[LJWS_JOSE] = lws_snprintf((char *)jws.map.buf[LJWS_JOSE],
tl, "{\"alg\":\"%s\"}", alg);
temp += otl - tl;
otl = tl;
va_start(ap, format);
n = vsnprintf(NULL, 0, format, ap);
va_end(ap);
if (n + 2 >= tl)
goto bail;
q = lws_malloc(n + 2, __func__);
if (!q)
goto bail;
va_start(ap, format);
vsnprintf(q, n + 2, format, ap);
va_end(ap);
/* add the plaintext from stdin to the map and a b64 version */
jws.map.buf[LJWS_PYLD] = q;
jws.map.len[LJWS_PYLD] = n;
if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_PYLD, temp, &tl,
jws.map.buf[LJWS_PYLD],
jws.map.len[LJWS_PYLD]))
goto bail1;
temp += otl - tl;
otl = tl;
/* add the b64 JOSE header to the b64 map */
if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_JOSE, temp, &tl,
jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE]))
goto bail1;
temp += otl - tl;
otl = tl;
/* prepare the space for the b64 signature in the map */
if (lws_jws_alloc_element(&jws.map_b64, LJWS_SIG, temp, &tl,
lws_base64_size(LWS_JWE_LIMIT_KEY_ELEMENT_BYTES),
0))
goto bail1;
/* sign the plaintext */
n = lws_jws_sign_from_b64(&jose, &jws,
(char *)jws.map_b64.buf[LJWS_SIG],
jws.map_b64.len[LJWS_SIG]);
if (n < 0)
goto bail1;
/* set the actual b64 signature size */
jws.map_b64.len[LJWS_SIG] = n;
/* create the compact JWS representation */
if (lws_jws_write_compact(&jws, out, *out_len))
goto bail1;
*out_len = strlen(out);
r = 0;
bail1:
lws_free(q);
bail:
jws.map.buf[LJWS_PYLD] = NULL;
jws.map.len[LJWS_PYLD] = 0;
lws_jws_destroy(&jws);
lws_jose_destroy(&jose);
return r;
}

View file

@ -685,6 +685,43 @@ test_jws_ES512(struct lws_context *context)
goto bail1;
}
/* jwt test */
{
unsigned long long ull = lws_now_secs();
char buf[8192];
size_t cml = 2048, cml2 = 2048;
if (lws_jwt_sign_compact(context, &jwk, "ES512",
(char *)buf, &cml2,
(char *)buf + 2048, 4096,
"{\"iss\":\"warmcat.com\",\"aud\":"
"\"https://libwebsockets.org/sai\","
"\"iat\":%llu,"
"\"nbf\":%llu,"
"\"exp\":%llu,"
"\"sub\":\"manage\"}", ull,
ull - 60, ull + (30 * 24 * 3600)
)) {
lwsl_err("%s: failed to create JWT\n", __func__);
goto bail1;
}
lwsl_notice("%s: jwt test '%s'\n", __func__, buf);
if (lws_jwt_signed_validate(context, &jwk, "ES512",
(const char *)buf, cml2,
(char *)buf + 2048, 2048,
(char *)buf + 4096, &cml)) {
lwsl_err("%s: failed to parse JWT\n", __func__);
goto bail1;
}
lwsl_notice("%s: jwt valid, payload '%s'\n",
__func__, buf + 4096);
}
/* end */
ret = 0;