2025-01-22 17:22:38 +01:00

792 lines
22 KiB
C
Executable File

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 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.
*/
#include "private-lib-core.h"
#include "private-lib-jose.h"
#include "private-lib-jose-jwe.h"
/*
* Currently only support flattened or compact (implicitly single signature)
*/
static const char * const jwe_json[] = {
"protected",
"iv",
"ciphertext",
"tag",
"encrypted_key"
};
enum enum_jwe_complete_tokens {
LWS_EJCT_PROTECTED,
LWS_EJCT_IV,
LWS_EJCT_CIPHERTEXT,
LWS_EJCT_TAG,
LWS_EJCT_RECIP_ENC_KEY,
};
/* parse a JWS complete or flattened JSON object */
struct jwe_cb_args {
struct lws_jws *jws;
char *temp;
int *temp_len;
};
static signed char
lws_jwe_json_cb(struct lejp_ctx *ctx, char reason)
{
struct jwe_cb_args *args = (struct jwe_cb_args *)ctx->user;
int n, m;
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
/* strings */
case LWS_EJCT_PROTECTED: /* base64u: JOSE: must contain 'alg' */
m = LJWS_JOSE;
goto append_string;
case LWS_EJCT_IV: /* base64u */
m = LJWE_IV;
goto append_string;
case LWS_EJCT_CIPHERTEXT: /* base64u */
m = LJWE_CTXT;
goto append_string;
case LWS_EJCT_TAG: /* base64u */
m = LJWE_ATAG;
goto append_string;
case LWS_EJCT_RECIP_ENC_KEY: /* base64u */
m = LJWE_EKEY;
goto append_string;
default:
return -1;
}
return 0;
append_string:
if (*args->temp_len < ctx->npos) {
lwsl_err("%s: out of parsing space\n", __func__);
return -1;
}
/*
* We keep both b64u and decoded in temp mapped using map / map_b64,
* the jws signature is actually over the b64 content not the plaintext,
* and we can't do it until we see the protected alg.
*/
if (!args->jws->map_b64.buf[m]) {
args->jws->map_b64.buf[m] = args->temp;
args->jws->map_b64.len[m] = 0;
}
memcpy(args->temp, ctx->buf, ctx->npos);
args->temp += ctx->npos;
*args->temp_len -= ctx->npos;
args->jws->map_b64.len[m] += ctx->npos;
if (reason == LEJPCB_VAL_STR_END) {
args->jws->map.buf[m] = args->temp;
n = lws_b64_decode_string_len(
(const char *)args->jws->map_b64.buf[m],
(int)args->jws->map_b64.len[m],
(char *)args->temp, *args->temp_len);
if (n < 0) {
lwsl_err("%s: b64 decode failed\n", __func__);
return -1;
}
args->temp += n;
*args->temp_len -= n;
args->jws->map.len[m] = (uint32_t)n;
}
return 0;
}
int
lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len,
char *temp, int *temp_len)
{
struct jwe_cb_args args;
struct lejp_ctx jctx;
int m = 0;
args.jws = &jwe->jws;
args.temp = temp;
args.temp_len = temp_len;
lejp_construct(&jctx, lws_jwe_json_cb, &args, jwe_json,
LWS_ARRAY_SIZE(jwe_json));
m = lejp_parse(&jctx, (uint8_t *)buf, len);
lejp_destruct(&jctx);
if (m < 0) {
lwsl_notice("%s: parse returned %d\n", __func__, m);
return -1;
}
return 0;
}
void
lws_jwe_init(struct lws_jwe *jwe, struct lws_context *context)
{
lws_jose_init(&jwe->jose);
lws_jws_init(&jwe->jws, &jwe->jwk, context);
memset(&jwe->jwk, 0, sizeof(jwe->jwk));
jwe->recip = 0;
jwe->cek_valid = 0;
}
void
lws_jwe_destroy(struct lws_jwe *jwe)
{
lws_jws_destroy(&jwe->jws);
lws_jose_destroy(&jwe->jose);
lws_jwk_destroy(&jwe->jwk);
/* cleanse the CEK we held on to in case of further encryptions of it */
lws_explicit_bzero(jwe->cek, sizeof(jwe->cek));
jwe->cek_valid = 0;
}
static uint8_t *
be32(uint32_t i, uint32_t *p32)
{
uint8_t *p = (uint8_t *)p32;
*p++ = (uint8_t)((i >> 24) & 0xff);
*p++ = (uint8_t)((i >> 16) & 0xff);
*p++ = (uint8_t)((i >> 8) & 0xff);
*p++ = (uint8_t)(i & 0xff);
return (uint8_t *)p32;
}
/*
* The key derivation process derives the agreed-upon key from the
* shared secret Z established through the ECDH algorithm, per
* Section 6.2.2.2 of [NIST.800-56A].
*
*
* Key derivation is performed using the Concat KDF, as defined in
* Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256.
*
* out must be prepared to take at least 32 bytes or the encrypted key size,
* whichever is larger.
*/
int
lws_jwa_concat_kdf(struct lws_jwe *jwe, int direct, uint8_t *out,
const uint8_t *shared_secret, int sslen)
{
int hlen = (int)lws_genhash_size(LWS_GENHASH_TYPE_SHA256), aidlen;
struct lws_genhash_ctx hash_ctx;
uint32_t ctr = 1, t;
const char *aid;
if (!jwe->jose.enc_alg || !jwe->jose.alg)
return -1;
/*
* Hash
*
* AlgorithmID || PartyUInfo || PartyVInfo
* {|| SuppPubInfo }{|| SuppPrivInfo }
*
* AlgorithmID
*
* The AlgorithmID value is of the form Datalen || Data, where Data
* is a variable-length string of zero or more octets, and Datalen is
* a fixed-length, big-endian 32-bit counter that indicates the
* length (in octets) of Data. In the Direct Key Agreement case,
* Data is set to the octets of the ASCII representation of the "enc"
* Header Parameter value. In the Key Agreement with Key Wrapping
* case, Data is set to the octets of the ASCII representation of the
* "alg" (algorithm) Header Parameter value.
*/
aid = direct ? jwe->jose.enc_alg->alg : jwe->jose.alg->alg;
aidlen = (int)strlen(aid);
/*
* PartyUInfo (PartyVInfo is the same deal)
*
* The PartyUInfo value is of the form Datalen || Data, where Data is
* a variable-length string of zero or more octets, and Datalen is a
* fixed-length, big-endian 32-bit counter that indicates the length
* (in octets) of Data. If an "apu" (agreement PartyUInfo) Header
* Parameter is present, Data is set to the result of base64url
* decoding the "apu" value and Datalen is set to the number of
* octets in Data. Otherwise, Datalen is set to 0 and Data is set to
* the empty octet sequence
*
* SuppPubInfo
*
* This is set to the keydatalen represented as a 32-bit big-endian
* integer.
*
* keydatalen
*
* This is set to the number of bits in the desired output key. For
* "ECDH-ES", this is length of the key used by the "enc" algorithm.
* For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this
* is 128, 192, and 256, respectively.
*
* Compute Hash i = H(counter || Z || OtherInfo).
*
* We must iteratively hash over key material that's larger than
* one hash output size (256b for SHA-256)
*/
while (ctr <= (uint32_t)((jwe->jose.enc_alg->keybits_fixed + (hlen - 1)) / hlen)) {
/*
* Key derivation is performed using the Concat KDF, as defined
* in Section 5.8.1 of [NIST.800-56A], where the Digest Method
* is SHA-256.
*/
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
return -1;
if (/* counter */
lws_genhash_update(&hash_ctx, be32(ctr++, &t), 4) ||
/* Z */
lws_genhash_update(&hash_ctx, shared_secret, (unsigned int)sslen) ||
/* other info */
lws_genhash_update(&hash_ctx, be32((uint32_t)strlen(aid), &t), 4) ||
lws_genhash_update(&hash_ctx, aid, (unsigned int)aidlen) ||
lws_genhash_update(&hash_ctx,
be32(jwe->jose.e[LJJHI_APU].len, &t), 4) ||
lws_genhash_update(&hash_ctx, jwe->jose.e[LJJHI_APU].buf,
jwe->jose.e[LJJHI_APU].len) ||
lws_genhash_update(&hash_ctx,
be32(jwe->jose.e[LJJHI_APV].len, &t), 4) ||
lws_genhash_update(&hash_ctx, jwe->jose.e[LJJHI_APV].buf,
jwe->jose.e[LJJHI_APV].len) ||
lws_genhash_update(&hash_ctx,
be32(jwe->jose.enc_alg->keybits_fixed, &t),
4) ||
lws_genhash_destroy(&hash_ctx, out)) {
lwsl_err("%s: fail\n", __func__);
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
out += hlen;
}
return 0;
}
void
lws_jwe_be64(uint64_t c, uint8_t *p8)
{
int n;
for (n = 56; n >= 0; n -= 8)
*p8++ = (uint8_t)((c >> n) & 0xff);
}
int
lws_jwe_auth_and_decrypt(struct lws_jwe *jwe, char *temp, int *temp_len)
{
int valid_aescbc_hmac, valid_aesgcm;
char dotstar[96];
if (lws_jwe_parse_jose(&jwe->jose, jwe->jws.map.buf[LJWS_JOSE],
(int)jwe->jws.map.len[LJWS_JOSE],
temp, temp_len) < 0) {
lws_strnncpy(dotstar, jwe->jws.map.buf[LJWS_JOSE],
jwe->jws.map.len[LJWS_JOSE], sizeof(dotstar));
lwsl_err("%s: JOSE parse '%s' failed\n", __func__, dotstar);
return -1;
}
if (!jwe->jose.alg) {
lws_strnncpy(dotstar, jwe->jws.map.buf[LJWS_JOSE],
jwe->jws.map.len[LJWS_JOSE], sizeof(dotstar));
lwsl_err("%s: no jose.alg: %s\n", __func__, dotstar);
return -1;
}
valid_aescbc_hmac = jwe->jose.enc_alg &&
jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
(jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 ||
jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512);
valid_aesgcm = jwe->jose.enc_alg &&
jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM;
if ((jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5 ||
jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP)) {
/* RSA + AESCBC */
if (valid_aescbc_hmac)
return lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(jwe);
/* RSA + AESGCM */
if (valid_aesgcm)
return lws_jwe_auth_and_decrypt_rsa_aes_gcm(jwe);
}
/* AESKW */
if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_AES_ECB &&
valid_aescbc_hmac)
return lws_jwe_auth_and_decrypt_aeskw_cbc_hs(jwe);
/* ECDH-ES + AESKW */
if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_ECDHES &&
valid_aescbc_hmac)
return lws_jwe_auth_and_decrypt_ecdh_cbc_hs(jwe,
temp, temp_len);
lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__,
jwe->jose.alg->alg, jwe->jose.enc_alg ?
jwe->jose.enc_alg->alg : "NULL");
return -1;
}
int
lws_jwe_encrypt(struct lws_jwe *jwe, char *temp, int *temp_len)
{
int valid_aescbc_hmac, valid_aesgcm, ot = *temp_len, ret = -1;
if (jwe->jose.recipients >= (int)LWS_ARRAY_SIZE(jwe->jose.recipient)) {
lwsl_err("%s: max recipients reached\n", __func__);
return -1;
}
valid_aesgcm = jwe->jose.enc_alg &&
jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM;
if (lws_jwe_parse_jose(&jwe->jose, jwe->jws.map.buf[LJWS_JOSE],
(int)jwe->jws.map.len[LJWS_JOSE], temp, temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
goto bail;
}
temp += ot - *temp_len;
valid_aescbc_hmac = jwe->jose.enc_alg &&
jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
(jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 ||
jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512);
if ((jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5 ||
jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP)) {
/* RSA + AESCBC */
if (valid_aescbc_hmac) {
ret = lws_jwe_encrypt_rsa_aes_cbc_hs(jwe, temp, temp_len);
goto bail;
}
/* RSA + AESGCM */
if (valid_aesgcm) {
ret = lws_jwe_encrypt_rsa_aes_gcm(jwe, temp, temp_len);
goto bail;
}
}
/* AESKW */
if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_AES_ECB &&
valid_aescbc_hmac) {
ret = lws_jwe_encrypt_aeskw_cbc_hs(jwe, temp, temp_len);
goto bail;
}
/* ECDH-ES + AESKW */
if (jwe->jose.alg->algtype_signing == LWS_JOSE_ENCTYPE_ECDHES &&
valid_aescbc_hmac) {
ret = lws_jwe_encrypt_ecdh_cbc_hs(jwe, temp, temp_len);
goto bail;
}
lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__,
jwe->jose.alg->alg, jwe->jose.enc_alg ?
jwe->jose.enc_alg->alg : "NULL");
bail:
if (ret)
memset(&jwe->jose.recipient[jwe->jose.recipients], 0,
sizeof(jwe->jose.recipient[0]));
else
jwe->jose.recipients++;
return ret;
}
/*
* JWE Compact Serialization consists of
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||
* BASE64URL(JWE Encrypted Key) || '.' ||
* BASE64URL(JWE Initialization Vector) || '.' ||
* BASE64URL(JWE Ciphertext) || '.' ||
* BASE64URL(JWE Authentication Tag)
*
*
* In the JWE Compact Serialization, no JWE Shared Unprotected Header or
* JWE Per-Recipient Unprotected Header are used. In this case, the
* JOSE Header and the JWE Protected Header are the same.
*
* Therefore:
*
* - Everything needed in the header part must go in the protected header
* (it's the only part emitted). We expect the caller did this.
*
* - You can't emit Compact representation if there are multiple recipients
*/
int
lws_jwe_render_compact(struct lws_jwe *jwe, char *out, size_t out_len)
{
size_t orig = out_len;
int n;
if (jwe->jose.recipients > 1) {
lwsl_notice("%s: can't issue compact representation for"
" multiple recipients (%d)\n", __func__,
jwe->jose.recipients);
return -1;
}
n = lws_jws_base64_enc(jwe->jws.map.buf[LJWS_JOSE],
jwe->jws.map.len[LJWS_JOSE], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode JOSE\n", __func__);
return -1;
}
out += n;
*out++ = '.';
out_len -= (unsigned int)n + 1;
n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode EKEY\n", __func__);
return -1;
}
out += n;
*out++ = '.';
out_len -= (unsigned int)n + 1;
n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_IV],
jwe->jws.map.len[LJWE_IV], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode IV\n", __func__);
return -1;
}
out += n;
*out++ = '.';
out_len -= (unsigned int)n + 1;
n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode CTXT\n", __func__);
return -1;
}
out += n;
*out++ = '.';
out_len -= (unsigned int)n + 1;
n = lws_jws_base64_enc(jwe->jws.map.buf[LJWE_ATAG],
jwe->jws.map.len[LJWE_ATAG], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode ATAG\n", __func__);
return -1;
}
out += n;
*out++ = '\0';
out_len -= (unsigned int)n;
return (int)(orig - out_len);
}
int
lws_jwe_create_packet(struct lws_jwe *jwe, const char *payload, size_t len,
const char *nonce, char *out, size_t out_len,
struct lws_context *context)
{
char *buf, *start, *p, *end, *p1, *end1;
struct lws_jws jws;
int n, m;
lws_jws_init(&jws, &jwe->jwk, context);
/*
* This buffer is local to the function, the actual output is prepared
* into out. Only the plaintext protected header
* (which contains the public key, 512 bytes for 4096b) goes in
* here temporarily.
*/
n = LWS_PRE + 2048;
buf = malloc((unsigned int)n);
if (!buf) {
lwsl_notice("%s: malloc %d failed\n", __func__, n);
return -1;
}
p = start = buf + LWS_PRE;
end = buf + n - LWS_PRE - 1;
/*
* temporary JWS protected header plaintext
*/
if (!jwe->jose.alg || !jwe->jose.alg->alg)
goto bail;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{\"alg\":\"%s\",\"jwk\":",
jwe->jose.alg->alg);
m = lws_ptr_diff(end, p);
n = lws_jwk_export(&jwe->jwk, 0, p, &m);
if (n < 0) {
lwsl_notice("failed to export jwk\n");
goto bail;
}
p += n;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ",\"nonce\":\"%s\"}", nonce);
/*
* prepare the signed outer JSON with all the parts in
*/
p1 = out;
end1 = out + out_len - 1;
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "{\"protected\":\"");
jws.map_b64.buf[LJWS_JOSE] = p1;
n = lws_jws_base64_enc(start, lws_ptr_diff_size_t(p, start), p1, lws_ptr_diff_size_t(end1, p1));
if (n < 0) {
lwsl_notice("%s: failed to encode protected\n", __func__);
goto bail;
}
jws.map_b64.len[LJWS_JOSE] = (unsigned int)n;
p1 += n;
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"payload\":\"");
jws.map_b64.buf[LJWS_PYLD] = p1;
n = lws_jws_base64_enc(payload, len, p1, lws_ptr_diff_size_t(end1, p1));
if (n < 0) {
lwsl_notice("%s: failed to encode payload\n", __func__);
goto bail;
}
jws.map_b64.len[LJWS_PYLD] = (unsigned int)n;
p1 += n;
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"header\":\"");
jws.map_b64.buf[LJWS_UHDR] = p1;
n = lws_jws_base64_enc(payload, len, p1, lws_ptr_diff_size_t(end1, p1));
if (n < 0) {
lwsl_notice("%s: failed to encode payload\n", __func__);
goto bail;
}
jws.map_b64.len[LJWS_UHDR] = (unsigned int)n;
p1 += n;
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"signature\":\"");
/*
* taking the b64 protected header and the b64 payload, sign them
* and place the signature into the packet
*/
n = lws_jws_sign_from_b64(&jwe->jose, &jws, p1, lws_ptr_diff_size_t(end1, p1));
if (n < 0) {
lwsl_notice("sig gen failed\n");
goto bail;
}
jws.map_b64.buf[LJWS_SIG] = p1;
jws.map_b64.len[LJWS_SIG] = (unsigned int)n;
p1 += n;
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\"}");
free(buf);
return lws_ptr_diff(p1, out);
bail:
lws_jws_destroy(&jws);
free(buf);
return -1;
}
static const char *protected_en[] = {
"encrypted_key", "aad", "iv", "ciphertext", "tag"
};
static int protected_idx[] = {
LJWE_EKEY, LJWE_AAD, LJWE_IV, LJWE_CTXT, LJWE_ATAG
};
/*
* The complete JWE may look something like this:
*
* {
* "protected":
* "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
* "unprotected":
* {"jku":"https://server.example.com/keys.jwks"},
* "recipients":[
* {"header":
* {"alg":"RSA1_5","kid":"2011-04-29"},
* "encrypted_key":
* "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-
* kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx
* GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3
* YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh
* cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg
* wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"},
* {"header":
* {"alg":"A128KW","kid":"7"},
* "encrypted_key":
* "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"}],
* "iv":
* "AxY8DCtDaGlsbGljb3RoZQ",
* "ciphertext":
* "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
* "tag":
* "Mz-VPPyU4RlcuYv1IwIvzw"
* }
*
* The flattened JWE ends up like this
*
* {
* "protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
* "unprotected": {"jku":"https://server.example.com/keys.jwks"},
* "header": {"alg":"A128KW","kid":"7"},
* "encrypted_key": "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",
* "iv": "AxY8DCtDaGlsbGljb3RoZQ",
* "ciphertext": "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
* "tag": "Mz-VPPyU4RlcuYv1IwIvzw"
* }
*
* {
* "protected":"<integrity-protected header contents>",
* "unprotected":<non-integrity-protected header contents>,
* "header":<more non-integrity-protected header contents>,
* "encrypted_key":"<encrypted key contents>",
* "aad":"<additional authenticated data contents>",
* "iv":"<initialization vector contents>",
* "ciphertext":"<ciphertext contents>",
* "tag":"<authentication tag contents>"
* }
*/
int
lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len)
{
char buf[3072], *p1, *end1, protected[128];
int m, n, jlen, plen;
jlen = lws_jose_render(&jwe->jose, jwe->jws.jwk, buf, sizeof(buf));
if (jlen < 0) {
lwsl_err("%s: lws_jose_render failed\n", __func__);
return -1;
}
/*
* prepare the JWE JSON with all the parts in
*/
p1 = out;
end1 = out + out_len - 1;
/*
* The protected header is b64url encoding of the JOSE header part
*/
plen = lws_snprintf(protected, sizeof(protected),
"{\"alg\":\"%s\",\"enc\":\"%s\"}",
jwe->jose.alg->alg, jwe->jose.enc_alg->alg);
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "{\"protected\":\"");
jwe->jws.map_b64.buf[LJWS_JOSE] = p1;
n = lws_jws_base64_enc(protected, (size_t)plen, p1, lws_ptr_diff_size_t(end1, p1));
if (n < 0) {
lwsl_notice("%s: failed to encode protected\n", __func__);
goto bail;
}
jwe->jws.map_b64.len[LJWS_JOSE] = (unsigned int)n;
p1 += n;
/* unprotected not supported atm */
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\n\"header\":");
lws_strnncpy(p1, buf, jlen, end1 - p1);
p1 += strlen(p1);
for (m = 0; m < (int)LWS_ARRAY_SIZE(protected_en); m++)
if (jwe->jws.map.buf[protected_idx[m]]) {
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), ",\n\"%s\":\"",
protected_en[m]);
//jwe->jws.map_b64.buf[protected_idx[m]] = p1;
n = lws_jws_base64_enc(jwe->jws.map.buf[protected_idx[m]],
jwe->jws.map.len[protected_idx[m]],
p1, lws_ptr_diff_size_t(end1, p1));
if (n < 0) {
lwsl_notice("%s: failed to encode %s\n",
__func__, protected_en[m]);
goto bail;
}
//jwe->jws.map_b64.len[protected_idx[m]] = n;
p1 += n;
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\"");
}
p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\n}\n");
return lws_ptr_diff(p1, out);
bail:
lws_jws_destroy(&jwe->jws);
return -1;
}