Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,271 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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-jwe.h"
int
lws_jwe_encrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *cek,
uint8_t *aad, int aad_len)
{
int n, hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_gencrypto_keyelem el;
struct lws_genhmac_ctx hmacctx;
struct lws_genaes_ctx aesctx;
size_t paddedlen;
uint8_t al[8];
/* Caller must have prepared space for the results */
if (jwe->jws.map.len[LJWE_ATAG] != (unsigned int)hlen / 2) {
lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
hlen / 2, (int)jwe->jws.map.len[LJWE_ATAG]);
return -1;
}
if (jwe->jws.map.len[LJWE_IV] != 16) {
lwsl_notice("expected iv len %d, got %d\n", 16,
(int)jwe->jws.map.len[LJWE_IV]);
return -1;
}
/* first create the authentication hmac */
/* JWA Section 5.2.2.1
*
* 1. The secondary keys MAC_KEY and ENC_KEY are generated from the
* input key K as follows. Each of these two keys is an octet
* string.
*
* MAC_KEY consists of the initial MAC_KEY_LEN octets of K, in
* order.
* ENC_KEY consists of the final ENC_KEY_LEN octets of K, in
* order.
*/
/*
* 2. The IV used is a 128-bit value generated randomly or
* pseudorandomly for use in the cipher.
*/
lws_get_random(jwe->jws.context, (void *)jwe->jws.map.buf[LJWE_IV], 16);
/*
* 3. The plaintext is CBC encrypted using PKCS #7 padding using
* ENC_KEY as the key and the IV. We denote the ciphertext output
* from this step as E.
*/
/* second half is the AES ENC_KEY */
el.buf = cek + (hlen / 2);
el.len = (uint32_t)(hlen / 2);
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_CBC, &el,
LWS_GAESP_WITH_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
/*
* the plaintext gets delivered to us in LJWE_CTXT, this replaces the
* plaintext there with the ciphertext, which will be larger by some
* padding bytes
*/
n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_IV],
NULL, NULL, LWS_AES_CBC_BLOCKLEN);
paddedlen = lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN,
jwe->jws.map.len[LJWE_CTXT]);
jwe->jws.map.len[LJWE_CTXT] = (uint32_t)paddedlen;
lws_genaes_destroy(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT] +
paddedlen - LWS_AES_CBC_BLOCKLEN, LWS_AES_CBC_BLOCKLEN);
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
/*
* 4. The octet string AL is equal to the number of bits in the
* Additional Authenticated Data A expressed as a 64-bit unsigned
* big-endian integer.
*/
lws_jwe_be64((unsigned int)aad_len * 8, al);
/* first half of the CEK is the MAC key */
if (lws_genhmac_init(&hmacctx, jwe->jose.enc_alg->hmac_type,
cek, (unsigned int)hlen / 2))
return -1;
/*
* 5. A message Authentication Tag T is computed by applying HMAC
* [RFC2104] to the following data, in order:
*
* - the Additional Authenticated Data A,
* - the Initialization Vector IV,
* - the ciphertext E computed in the previous step, and
* - the octet string AL defined above.
*
* The string MAC_KEY is used as the MAC key. We denote the output
* of the MAC computed in this step as M. The first T_LEN octets of
* M are used as T.
*/
if (lws_genhmac_update(&hmacctx, aad, (unsigned int)aad_len) ||
lws_genhmac_update(&hmacctx, jwe->jws.map.buf[LJWE_IV],
LWS_JWE_AES_IV_BYTES) ||
/* since we encrypted it, this is the ciphertext */
lws_genhmac_update(&hmacctx,
(uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT]) ||
lws_genhmac_update(&hmacctx, al, 8)) {
lwsl_err("%s: hmac computation failed\n", __func__);
lws_genhmac_destroy(&hmacctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&hmacctx, digest)) {
lwsl_err("%s: problem destroying hmac\n", __func__);
return -1;
}
/* create tag */
memcpy((void *)jwe->jws.map.buf[LJWE_ATAG], digest, (unsigned int)hlen / 2);
return (int)jwe->jws.map.len[LJWE_CTXT];
}
int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek,
uint8_t *aad, int aad_len)
{
int n, hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_gencrypto_keyelem el;
struct lws_genhmac_ctx hmacctx;
struct lws_genaes_ctx aesctx;
uint8_t al[8];
/* Some sanity checks on what came in */
if (jwe->jws.map.len[LJWE_ATAG] != (unsigned int)hlen / 2) {
lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
hlen / 2, (int)jwe->jws.map.len[LJWE_ATAG]);
return -1;
}
if (jwe->jws.map.len[LJWE_IV] != 16) {
lwsl_notice("expected iv len %d, got %d\n", 16,
(int)jwe->jws.map.len[LJWE_IV]);
return -1;
}
/* Prepare to check authentication
*
* AAD is the b64 JOSE header.
*
* The octet string AL, which is the number of bits in AAD expressed as
* a big-endian 64-bit unsigned integer is:
*
* [0, 0, 0, 0, 0, 0, 1, 152]
*
* Concatenate the AAD, the Initialization Vector, the ciphertext, and
* the AL value.
*
*/
lws_jwe_be64((unsigned int)aad_len * 8, al);
/* first half of enc_cek is the MAC key */
if (lws_genhmac_init(&hmacctx, jwe->jose.enc_alg->hmac_type, enc_cek,
(unsigned int)hlen / 2)) {
lwsl_err("%s: lws_genhmac_init fail\n", __func__);
return -1;
}
if (lws_genhmac_update(&hmacctx, aad, (unsigned int)aad_len) ||
lws_genhmac_update(&hmacctx, (uint8_t *)jwe->jws.map.buf[LJWE_IV],
jwe->jws.map.len[LJWE_IV]) ||
lws_genhmac_update(&hmacctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT]) ||
lws_genhmac_update(&hmacctx, al, 8)) {
lwsl_err("%s: hmac computation failed\n", __func__);
lws_genhmac_destroy(&hmacctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&hmacctx, digest)) {
lwsl_err("%s: problem destroying hmac\n", __func__);
return -1;
}
/* first half of digest is the auth tag */
if (lws_timingsafe_bcmp(digest, jwe->jws.map.buf[LJWE_ATAG], (unsigned int)hlen / 2)) {
lwsl_err("%s: auth failed: hmac tag (%d) != ATAG (%d)\n",
__func__, hlen / 2, (int)jwe->jws.map.len[LJWE_ATAG]);
lwsl_hexdump_notice(jwe->jws.map.buf[LJWE_ATAG], (unsigned int)hlen / 2);
lwsl_hexdump_notice(digest, (unsigned int)hlen / 2);
return -1;
}
/* second half of enc cek is the CEK KEY */
el.buf = enc_cek + (hlen / 2);
el.len = (unsigned int)hlen / 2;
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_CBC,
&el, LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_IV], NULL, NULL, 16);
/* Strip the PKCS #7 padding */
if (jwe->jws.map.len[LJWE_CTXT] < LWS_AES_CBC_BLOCKLEN ||
jwe->jws.map.len[LJWE_CTXT] <= (unsigned char)jwe->jws.map.buf[LJWE_CTXT]
[jwe->jws.map.len[LJWE_CTXT] - 1]) {
lwsl_err("%s: invalid padded ciphertext length: %d. Corrupt data?\n",
__func__, (int)jwe->jws.map.len[LJWE_CTXT]);
return -1;
}
jwe->jws.map.len[LJWE_CTXT] = (uint32_t)((int)jwe->jws.map.len[LJWE_CTXT] -
jwe->jws.map.buf[LJWE_CTXT][jwe->jws.map.len[LJWE_CTXT] - 1]);
n |= lws_genaes_destroy(&aesctx, NULL, 0);
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
return (int)jwe->jws.map.len[LJWE_CTXT];
}

View File

@ -0,0 +1,173 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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-jwe.h"
/*
* NOTICE this is AESGCM content encryption, it's not AES GCM key wrapping
*
*
* This section defines the specifics of performing authenticated
* encryption with AES in Galois/Counter Mode (GCM) ([AES] and
* [NIST.800-38D]).
*
* The CEK is used as the encryption key.
*
* Use of an IV of size 96 bits is REQUIRED with this algorithm.
*
* The requested size of the Authentication Tag output MUST be 128 bits,
* regardless of the key size.
*
* For decrypt: decrypt the KEK, then decrypt the payload
*
* For encrypt: encrypt the payload, then encrypt the KEK
*/
/*
* encrypting... enc_cek is unencrypted
*/
int
lws_jwe_encrypt_gcm(struct lws_jwe *jwe,
uint8_t *enc_cek, uint8_t *aad, int aad_len)
{
struct lws_gencrypto_keyelem el;
struct lws_genaes_ctx aesctx;
size_t ivs = LWS_AESGCM_IV;
int n;
/* Some sanity checks on what came in */
/* MUST be 128-bit for all sizes */
if (jwe->jws.map.len[LJWE_ATAG] != LWS_AESGCM_TAG) {
lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n",
__func__, (int)jwe->jws.map.len[LJWE_ATAG]);
return -1;
}
if (jwe->jws.map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */
lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__,
(int)jwe->jws.map.len[LJWE_IV]);
return -1;
}
/* EKEY is directly the CEK KEY */
el.buf = enc_cek;
el.len = jwe->jose.enc_alg->keybits_fixed / 8;
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_GCM,
&el, LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
/* aad */
n = lws_genaes_crypt(&aesctx, aad, (unsigned int)aad_len, NULL,
(uint8_t *)jwe->jws.map.buf[LJWE_IV],
(uint8_t *)jwe->jws.map.buf[LJWE_ATAG], &ivs,
LWS_AESGCM_TAG);
if (n) {
lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__);
return -1;
}
/* payload */
n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_IV],
NULL, &ivs,
LWS_AESGCM_TAG);
n |= lws_genaes_destroy(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_ATAG],
LWS_AESGCM_TAG);
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
return (int)jwe->jws.map.len[LJWE_CTXT];
}
int
lws_jwe_auth_and_decrypt_gcm(struct lws_jwe *jwe,
uint8_t *enc_cek, uint8_t *aad, int aad_len)
{
struct lws_gencrypto_keyelem el;
struct lws_genaes_ctx aesctx;
size_t ivs = LWS_AESGCM_IV;
uint8_t tag[LWS_AESGCM_TAG];
int n;
/* Some sanity checks on what came in */
/* Tag MUST be 128-bit for all sizes */
if (jwe->jws.map.len[LJWE_ATAG] != LWS_AESGCM_TAG) {
lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n",
__func__, (int)jwe->jws.map.len[LJWE_ATAG]);
return -1;
}
if (jwe->jws.map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */
lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__,
(int)jwe->jws.map.len[LJWE_IV]);
return -1;
}
/* EKEY is directly the CEK KEY */
el.buf = enc_cek;
el.len = jwe->jose.enc_alg->keybits_fixed / 8;
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_GCM,
&el, LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
n = lws_genaes_crypt(&aesctx, aad, (unsigned int)aad_len,
NULL,
(uint8_t *)jwe->jws.map.buf[LJWE_IV],
(uint8_t *)jwe->jws.map.buf[LJWE_ATAG], &ivs, 16);
if (n) {
lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__);
return -1;
}
n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
jwe->jws.map.len[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_CTXT],
(uint8_t *)jwe->jws.map.buf[LJWE_IV],
(uint8_t *)jwe->jws.map.buf[LJWE_ATAG], &ivs, 16);
n |= lws_genaes_destroy(&aesctx, tag, sizeof(tag));
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
return (int)jwe->jws.map.len[LJWE_CTXT];
}

View File

@ -0,0 +1,177 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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-jwe.h"
/*
* RFC3394 Key Wrap uses a 128-bit key, and bloats what it is wrapping by
* one 8-byte block. So, if you had a 32 byte plaintext CEK to wrap, after
* wrapping it becomes a 40 byte wrapped, enciphered, key.
*
* The CEK comes in from and goes out in LJWE_EKEY. So LJWE_EKEY length
* increases by 8 from calling this.
*/
int
lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jwe *jwe, char *temp, int *temp_len)
{
struct lws_genaes_ctx aesctx;
/* we are wrapping a key, so size for the worst case after wrap */
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES +
LWS_JWE_RFC3394_OVERHEAD_BYTES];
int n, m, hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type),
ot = *temp_len;
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_OCT) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
/* create a b64 version of the JOSE header, needed for hashing */
if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
temp, temp_len,
jwe->jws.map.buf[LJWE_JOSE],
jwe->jws.map.len[LJWE_JOSE]))
return -1;
/* Allocate temp space for ATAG and IV */
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG, temp + (ot - *temp_len),
temp_len, (unsigned int)hlen / 2, 0))
return -1;
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV, temp + (ot - *temp_len),
temp_len, LWS_JWE_AES_IV_BYTES, 0))
return -1;
/* 1) Encrypt the payload... */
/* the CEK is 256-bit in the example encrypted with a 128-bit key */
n = lws_jwe_encrypt_cbc_hs(jwe, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
return -1;
}
/* 2) Encrypt the JWE Encrypted Key: RFC3394 Key Wrap uses 64 bit blocks
* and 128-bit input key*/
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW,
jwe->jws.jwk->e, 1, NULL)) {
lwsl_notice("%s: lws_genaes_create\n", __func__);
return -1;
}
/* tag size is determined by enc cipher key length */
n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL,
lws_gencrypto_bits_to_bytes(
jwe->jose.enc_alg->keybits_fixed));
m = lws_genaes_destroy(&aesctx, NULL, 0);
if (n < 0) {
lwsl_err("%s: encrypt cek fail\n", __func__);
return -1;
}
if (m < 0) {
lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
return -1;
}
jwe->jws.map.len[LJWE_EKEY] += LWS_JWE_RFC3394_OVERHEAD_BYTES;
memcpy((uint8_t *)jwe->jws.map.buf[LJWE_EKEY], enc_cek,
jwe->jws.map.len[LJWE_EKEY]);
return (int)jwe->jws.map.len[LJWE_CTXT];
}
int
lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jwe *jwe)
{
struct lws_genaes_ctx aesctx;
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES +
LWS_JWE_RFC3394_OVERHEAD_BYTES];
int n, m;
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_OCT) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
/* the CEK is 256-bit in the example encrypted with a 128-bit key */
if (jwe->jws.map.len[LJWE_EKEY] > sizeof(enc_cek))
return -1;
/* 1) Decrypt the JWE Encrypted Key to get the raw MAC / CEK */
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW,
jwe->jws.jwk->e, 1, NULL)) {
lwsl_notice("%s: lws_genaes_create\n", __func__);
return -1;
}
/*
* Decrypt the CEK into enc_cek
* tag size is determined by enc cipher key length */
n = lws_genaes_crypt(&aesctx, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL,
lws_gencrypto_bits_to_bytes(
jwe->jose.enc_alg->keybits_fixed));
m = lws_genaes_destroy(&aesctx, NULL, 0);
if (n < 0) {
lwsl_err("%s: decrypt CEK fail\n", __func__);
return -1;
}
if (m < 0) {
lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
return -1;
}
/* 2) Decrypt the payload */
n = lws_jwe_auth_and_decrypt_cbc_hs(jwe, enc_cek,
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n",
__func__);
return -1;
}
return (int)jwe->jws.map.len[LJWE_CTXT];
}

View File

@ -0,0 +1,616 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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-jwe.h"
/*
* From RFC7518 JWA
*
* 4.6. Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static
* (ECDH-ES)
*
* This section defines the specifics of key agreement with Elliptic
* Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with
* the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A]. The
* key agreement result can be used in one of two ways:
*
* 1. directly as the Content Encryption Key (CEK) for the "enc"
* algorithm, in the Direct Key Agreement mode, or
*
* 2. as a symmetric key used to wrap the CEK with the "A128KW",
* "A192KW", or "A256KW" algorithms, in the Key Agreement with Key
* Wrapping mode.
*
* A new ephemeral public key value MUST be generated for each key
* agreement operation.
*
* In Direct Key Agreement mode, the output of the Concat KDF MUST be a
* key of the same length as that used by the "enc" algorithm. In this
* case, the empty octet sequence is used as the JWE Encrypted Key
* value. The "alg" (algorithm) Header Parameter value "ECDH-ES" is
* used in the Direct Key Agreement mode.
*
* In Key Agreement with Key Wrapping mode, the output of the Concat KDF
* MUST be a key of the length needed for the specified key wrapping
* algorithm. In this case, the JWE Encrypted Key is the CEK wrapped
* with the agreed-upon key.
*
* The following "alg" (algorithm) Header Parameter values are used to
* indicate that the JWE Encrypted Key is the result of encrypting the
* CEK using the result of the key agreement algorithm as the key
* encryption key for the corresponding key wrapping algorithm:
*
* +-----------------+-------------------------------------------------+
* | "alg" Param | Key Management Algorithm |
* | Value | |
* +-----------------+-------------------------------------------------+
* | ECDH-ES+A128KW | ECDH-ES using Concat KDF and CEK wrapped with |
* | | "A128KW" |
* | ECDH-ES+A192KW | ECDH-ES using Concat KDF and CEK wrapped with |
* | | "A192KW" |
* | ECDH-ES+A256KW | ECDH-ES using Concat KDF and CEK wrapped with |
* | | "A256KW" |
* +-----------------+-------------------------------------------------+
*
* 4.6.1. Header Parameters Used for ECDH Key Agreement
*
* The following Header Parameter names are used for key agreement as
* defined below.
*
* 4.6.1.1. "epk" (Ephemeral Public Key) Header Parameter
*
* The "epk" (ephemeral public key) value created by the originator for
* the use in key agreement algorithms. This key is represented as a
* JSON Web Key [JWK] public key value. It MUST contain only public key
* parameters and SHOULD contain only the minimum JWK parameters
* necessary to represent the key; other JWK parameters included can be
* checked for consistency and honored, or they can be ignored. This
* Header Parameter MUST be present and MUST be understood and processed
* by implementations when these algorithms are used.
*
* 4.6.1.2. "apu" (Agreement PartyUInfo) Header Parameter
*
* The "apu" (agreement PartyUInfo) value for key agreement algorithms
* using it (such as "ECDH-ES"), represented as a base64url-encoded
* string. When used, the PartyUInfo value contains information about
* the producer. Use of this Header Parameter is OPTIONAL. This Header
* Parameter MUST be understood and processed by implementations when
* these algorithms are used.
*
* 4.6.1.3. "apv" (Agreement PartyVInfo) Header Parameter
*
* The "apv" (agreement PartyVInfo) value for key agreement algorithms
* using it (such as "ECDH-ES"), represented as a base64url encoded
* string. When used, the PartyVInfo value contains information about
* the recipient. Use of this Header Parameter is OPTIONAL. This
* Header Parameter MUST be understood and processed by implementations
* when these algorithms are used.
*
* 4.6.2. Key Derivation for ECDH Key Agreement
*
* 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.
* The Concat KDF parameters are set as follows:
*
* Z
* This is set to the representation of the shared secret Z as an
* octet sequence.
*
* 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.
*
* 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.
*
* PartyUInfo
* 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.
*
* PartyVInfo
* The PartyVInfo 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 "apv" (agreement PartyVInfo) Header
* Parameter is present, Data is set to the result of base64url
* decoding the "apv" 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.
*
* SuppPrivInfo
* This is set to the empty octet sequence.
*
* Applications need to specify how the "apu" and "apv" Header
* Parameters are used for that application. The "apu" and "apv" values
* MUST be distinct, when used. Applications wishing to conform to
* [NIST.800-56A] need to provide values that meet the requirements of
* that document, e.g., by using values that identify the producer and
* consumer. Alternatively, applications MAY conduct key derivation in
* a manner similar to "Diffie-Hellman Key Agreement Method" [RFC2631]:
* in that case, the "apu" parameter MAY either be omitted or represent
* a random 512-bit value (analogous to PartyAInfo in Ephemeral-Static
* mode in RFC 2631) and the "apv" parameter SHOULD NOT be present.
*
*/
/*
* - ECDH-ES[-variant] comes in the jose "alg" and just covers key agreement.
* The "enc" action is completely separate and handled elsewhere. However
* the key size throughout is determined by the needs of the "enc" action.
*
* - The jwe->jws.jwk is the PEER - the encryption consumer's - public key.
*
* - The public part of the ephemeral key comes out in jose.jwk_ephemeral
*
* - Return shared secret length or < 0 for error
*
* - Unwrapped CEK in EKEY. If any, wrapped CEK in "wrapped".
*
* - Caller responsibility to cleanse EKEY.
*/
static int
lws_jwe_encrypt_ecdh(struct lws_jwe *jwe, char *temp, int *temp_len,
uint8_t *cek)
{
uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES],
derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
int m, n, ret = -1, ot = *temp_len, ss_len = sizeof(shared_secret),
// kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type),
enc_hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type),
ekbytes = 32; //jwe->jose.alg->keybits_fixed / 8;
struct lws_genec_ctx ecctx;
struct lws_jwk *ephem = &jwe->jose.recipient[jwe->recip].jwk_ephemeral;
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
ephem->kty = LWS_GENCRYPTO_KTY_EC;
ephem->private_key = 1;
/* Generate jose.jwk_ephemeral on the peer public key curve */
if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL))
goto bail;
/* ephemeral context gets random key on same curve as recip pubkey */
if (lws_genecdh_new_keypair(&ecctx, LDHS_OURS, (const char *)
jwe->jws.jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
ephem->e))
goto bail;
/* peer context gets js->jwk key */
if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_THEIRS)) {
lwsl_err("%s: setting peer pubkey failed\n", __func__);
goto bail;
}
/* combine our ephemeral key and the peer pubkey to get the secret */
if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) {
lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n",
__func__);
goto bail;
}
/*
* The private part of the ephemeral key is finished with...
* cleanse and free it. We need to keep the public part around so we
* can publish it with the JWE as "epk".
*/
lws_explicit_bzero(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf,
ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len);
lws_free_set_NULL(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf);
ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len = 0;
ephem->private_key = 0;
/*
* Derive the CEK from the shared secret... amount of bytes written to
* derived matches bitcount in jwe->jose.enc_alg->keybits_fixed
*
* In Direct Key Agreement mode, the output of the Concat KDF MUST be a
* key of the same length as that used by the "enc" algorithm.
*/
if (lws_jwa_concat_kdf(jwe,
jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE,
derived, shared_secret, ss_len)) {
lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__);
goto bail;
}
/* in P-521 case, we get a 66-byte shared secret for a 64-byte key */
if (ss_len < enc_hlen) {
lwsl_err("%s: concat KDF bad derived key len %d\n", __func__,
ss_len);
goto bail;
}
/*
* For "ECDH-ES", that was it, and we use what we just wrapped in
* wrapped as the CEK without publishing it.
*
* For "ECDH-ES-AES[128,192,256]KW", we generate a new, random CEK and
* then wrap it using the key we just wrapped, and make the wrapped
* version available in EKEY.
*/
if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
struct lws_gencrypto_keyelem el;
struct lws_genaes_ctx aesctx;
/* generate the actual CEK in cek */
if (lws_get_random(jwe->jws.context, cek, (unsigned int)enc_hlen) !=
(size_t)enc_hlen) {
lwsl_err("Problem getting random\n");
goto bail;
}
/* wrap with the derived key */
el.buf = derived;
el.len = (unsigned int)enc_hlen / 2;
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW, &el,
1, NULL)) {
lwsl_notice("%s: lws_genaes_create\n", __func__);
goto bail;
}
/* wrap CEK into EKEY */
n = lws_genaes_crypt(&aesctx, cek, (unsigned int)enc_hlen,
(void *)jwe->jws.map.buf[LJWE_EKEY],
NULL, NULL, NULL, 0);
m = lws_genaes_destroy(&aesctx, NULL, 0);
if (n < 0) {
lwsl_err("%s: encrypt cek fail\n", __func__);
goto bail;
}
if (m < 0) {
lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
goto bail;
}
jwe->jws.map.len[LJWE_EKEY] = (unsigned int)enc_hlen + 8;
/* Wrapped CEK is in EKEY. Random CEK is in cek. */
} else /* direct derived CEK is in cek */
memcpy(cek, derived, (unsigned int)enc_hlen);
/* rewrite the protected JOSE header to have the epk pieces */
jwe->jws.map.buf[LJWE_JOSE] = temp;
m = n = lws_snprintf(temp, (size_t)*temp_len,
"{\"alg\":\"%s\", \"enc\":\"%s\", \"epk\":",
jwe->jose.alg->alg, jwe->jose.enc_alg->alg);
*temp_len -= n;
n = lws_jwk_export(ephem, 0, temp + (ot - *temp_len), temp_len);
if (n < 0) {
lwsl_err("%s: ephemeral export failed\n", __func__);
goto bail;
}
m += n;
n = lws_snprintf(temp + (ot - *temp_len), (size_t)*temp_len, "}");
*temp_len -= n + 1;
m += n;
jwe->jws.map.len[LJWE_JOSE] = (unsigned int)m;
/* create a b64 version of the JOSE header, needed later for AAD */
if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
temp + (ot - *temp_len), temp_len,
jwe->jws.map.buf[LJWE_JOSE],
jwe->jws.map.len[LJWE_JOSE]))
return -1;
ret = enc_hlen;
bail:
lws_genec_destroy(&ecctx);
/* cleanse the shared secret (watch out for cek at parent too) */
lws_explicit_bzero(shared_secret, (unsigned int)ekbytes);
lws_explicit_bzero(derived, (unsigned int)ekbytes);
return ret;
}
int
lws_jwe_encrypt_ecdh_cbc_hs(struct lws_jwe *jwe, char *temp, int *temp_len)
{
int ss_len, // kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type),
enc_hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
int ekbytes = jwe->jose.alg->keybits_fixed / 8;
int n, ot = *temp_len, ret = -1;
/* if we will produce an EKEY, make space for it */
if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_EKEY,
temp + (ot - *temp_len), temp_len,
(unsigned int)enc_hlen + 8, 0))
goto bail;
}
/* decrypt the CEK */
ss_len = lws_jwe_encrypt_ecdh(jwe, temp + (ot - *temp_len), temp_len, cek);
if (ss_len < 0) {
lwsl_err("%s: lws_jwe_encrypt_ecdh failed\n", __func__);
return -1;
}
/* cek contains the unwrapped CEK. EKEY may contain wrapped CEK */
/* make space for the payload encryption pieces */
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG,
temp + (ot - *temp_len),
temp_len, (unsigned int)enc_hlen / 2, 0))
goto bail;
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV,
temp + (ot - *temp_len),
temp_len, LWS_JWE_AES_IV_BYTES, 0))
goto bail;
/* Perform the authenticated encryption on CTXT...
* ...the AAD is b64u(protected JOSE header) */
n = lws_jwe_encrypt_cbc_hs(jwe, cek,
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_notice("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
goto bail;
}
ret = 0;
bail:
/* if fail or direct CEK, cleanse and remove EKEY */
if (ret || jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE) {
if (jwe->jws.map.len[LJWE_EKEY])
lws_explicit_bzero((void *)jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY]);
jwe->jws.map.len[LJWE_EKEY] = 0;
}
lws_explicit_bzero(cek, (unsigned int)ekbytes);
return ret;
}
/*
* jwe->jws.jwk is recipient private key
*
* If kw mode, then EKEY is the wrapped CEK
*
*
*/
static int
lws_jwe_auth_and_decrypt_ecdh(struct lws_jwe *jwe)
{
uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES],
derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
int ekbytes = jwe->jose.enc_alg->keybits_fixed / 8,
enc_hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
struct lws_genec_ctx ecctx;
int n, ret = -1, ss_len = sizeof(shared_secret);
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
if (jwe->jose.recipient[jwe->recip].jwk_ephemeral.kty !=
LWS_GENCRYPTO_KTY_EC) {
lwsl_err("%s: missing epk\n", __func__);
return -1;
}
/*
* Recompute the shared secret...
*
* - direct: it's the CEK
*
* - aeskw: apply it as AES keywrap to EKEY to get the CEK
*/
/* Generate jose.jwk_ephemeral on the peer public key curve */
if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL))
goto bail;
/* Load our private key into our side of the ecdh context */
if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_OURS)) {
lwsl_err("%s: setting our private key failed\n", __func__);
goto bail;
}
/* Import the ephemeral public key into the peer side */
if (lws_genecdh_set_key(&ecctx,
jwe->jose.recipient[jwe->recip].jwk_ephemeral.e,
LDHS_THEIRS)) {
lwsl_err("%s: setting epk pubkey failed\n", __func__);
goto bail;
}
/* combine their ephemeral key and our private key to get the secret */
if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) {
lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n",
__func__);
goto bail;
}
lws_genec_destroy(&ecctx);
if (ss_len < enc_hlen) {
lwsl_err("%s: ss_len %d ekbytes %d\n", __func__, ss_len, enc_hlen);
goto bail;
}
/*
* Derive the CEK from the shared secret... amount of bytes written to
* cek[] matches bitcount in jwe->jose.enc_alg->keybits_fixed
*/
if (lws_jwa_concat_kdf(jwe,
jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE,
derived, shared_secret, ss_len)) {
lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__);
goto bail;
}
/*
* "ECDH-ES": derived is the CEK
* "ECDH-ES-AES[128,192,256]KW": wrapped key is in EKEY,
* "derived" contains KEK
*/
if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
struct lws_gencrypto_keyelem el;
struct lws_genaes_ctx aesctx;
int m;
/* Confirm space for EKEY */
if (jwe->jws.map.len[LJWE_EKEY] < (unsigned int)enc_hlen) {
lwsl_err("%s: missing EKEY\n", __func__);
goto bail;
}
/* unwrap with the KEK we derived */
el.buf = derived;
el.len = (unsigned int)enc_hlen / 2;
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW,
&el, 1, NULL)) {
lwsl_notice("%s: lws_genaes_create\n", __func__);
goto bail;
}
/* decrypt the EKEY to end up with CEK in "shared_secret" */
n = lws_genaes_crypt(&aesctx,
(const uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY],
(uint8_t *)shared_secret,
NULL, NULL, NULL, 0);
m = lws_genaes_destroy(&aesctx, NULL, 0);
if (n < 0) {
lwsl_err("%s: decrypt cek fail\n", __func__);
goto bail;
}
if (m < 0) {
lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
goto bail;
}
} else
memcpy(shared_secret, derived, (unsigned int)enc_hlen);
/* either way, the recovered CEK is in shared_secret */
if (lws_jwe_auth_and_decrypt_cbc_hs(jwe, shared_secret,
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]) < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs fail\n", __func__);
goto bail;
}
/* if all went well, then CTXT is now the plaintext */
ret = 0;
bail:
/* cleanse wrapped on stack that contained the CEK / wrapped key */
lws_explicit_bzero(derived, (unsigned int)ekbytes);
/* cleanse the shared secret */
lws_explicit_bzero(shared_secret, (unsigned int)ekbytes);
return ret;
}
int
lws_jwe_auth_and_decrypt_ecdh_cbc_hs(struct lws_jwe *jwe,
char *temp, int *temp_len)
{
/* create a b64 version of the JOSE header, needed later for AAD */
if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
temp, temp_len,
jwe->jws.map.buf[LJWE_JOSE],
jwe->jws.map.len[LJWE_JOSE]))
return -1;
return lws_jwe_auth_and_decrypt_ecdh(jwe);
}

View File

@ -0,0 +1,196 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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-jwe.h"
/*
* Requirements on entry:
*
* - jwe->jws.map LJWE_JOSE contains the ASCII JOSE header
* - jwe->jws.map LJWE_EKEY contains cek of enc_alg hmac length
* - jwe->jws.map LJWE_CTXT contains the plaintext
*
* On successful exit:
*
* - jwe->jws.map LJWE_ATAG contains the tag
* - jwe->jws.map LJWE_IV contains the new random IV that was used
* - jwe->jws.map LJWE_EKEY contains the encrypted CEK
* - jwe->jws.map LJWE_CTXT contains the ciphertext
*
* Return the amount of temp used, or -1
*/
int
lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe,
char *temp, int *temp_len)
{
int n, hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type),
ot = *temp_len;
char ekey[LWS_GENHASH_LARGEST];
struct lws_genrsa_ctx rsactx;
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
/*
* Notice that the unencrypted EKEY coming in is smaller than the
* RSA-encrypted EKEY going out, which is going to be the RSA key size
*
* Create a b64 version of the JOSE header, needed as aad
*/
if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
temp, temp_len,
jwe->jws.map.buf[LJWE_JOSE],
jwe->jws.map.len[LJWE_JOSE]))
return -1;
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG, temp + (ot - *temp_len),
temp_len, (unsigned int)hlen / 2, 0))
return -1;
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV, temp + (ot - *temp_len),
temp_len, LWS_JWE_AES_IV_BYTES, 0))
return -1;
/*
* Without changing the unencrypted CEK in EKEY, reallocate enough
* space to write the RSA-encrypted version in-situ.
*/
if (lws_jws_dup_element(&jwe->jws.map, LJWE_EKEY, temp + (ot - *temp_len),
temp_len, jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY],
jwe->jws.jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len))
return -1;
/* Encrypt using the raw CEK (treated as MAC KEY | ENC KEY) */
n = lws_jwe_encrypt_cbc_hs(jwe, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
return -1;
}
if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context,
!strcmp(jwe->jose.alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_create\n",
__func__);
return -1;
}
/* encrypt the CEK using RSA, mbedtls can't handle both in and out are
* the EKEY, so copy the unencrypted ekey out temporarily */
memcpy(ekey, jwe->jws.map.buf[LJWE_EKEY], (unsigned int)hlen);
n = lws_genrsa_public_encrypt(&rsactx, (uint8_t *)ekey, (unsigned int)hlen,
(uint8_t *)jwe->jws.map.buf[LJWE_EKEY]);
lws_genrsa_destroy(&rsactx);
lws_explicit_bzero(ekey, (unsigned int)hlen); /* cleanse the temp CEK copy */
if (n < 0) {
lwsl_err("%s: encrypt cek fail\n", __func__);
return -1;
}
jwe->jws.map.len[LJWE_EKEY] = (unsigned int)n; /* update to encrypted EKEY size */
/*
* We end up with IV, ATAG, set, EKEY encrypted and CTXT is ciphertext,
* and b64u version of ATAG in map_b64.
*/
return 0;
}
int
lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe)
{
int n;
struct lws_genrsa_ctx rsactx;
uint8_t enc_cek[512];
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
if (jwe->jws.map.len[LJWE_EKEY] < 40) {
lwsl_err("%s: EKEY length too short %d\n", __func__,
(int)jwe->jws.map.len[LJWE_EKEY]);
return -1;
}
/* Decrypt the JWE Encrypted Key to get the raw MAC || CEK */
if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context,
!strcmp(jwe->jose.alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = lws_genrsa_private_decrypt(&rsactx,
(uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY], enc_cek,
sizeof(enc_cek));
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: decrypt cek fail: \n", __func__);
return -1;
}
n = lws_jwe_auth_and_decrypt_cbc_hs(jwe, enc_cek,
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n",
__func__);
return -1;
}
#if defined(LWS_WITH_MBEDTLS) && defined(LWS_PLAT_OPTEE)
/* strip padding */
n = jwe->jws.map.buf[LJWE_CTXT][jwe->jws.map.len[LJWE_CTXT] - 1];
if (n > 16) {
lwsl_err("%s: n == %d, plen %d\n", __func__, n,
(int)jwe->jws.map.len[LJWE_CTXT]);
return -1;
}
jwe->jws.map.len[LJWE_CTXT] -= n;
#endif
return (int)jwe->jws.map.len[LJWE_CTXT];
}

View File

@ -0,0 +1,183 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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-jwe.h"
#define LWS_AESGCM_IV 12
int
lws_jwe_encrypt_rsa_aes_gcm(struct lws_jwe *jwe, char *temp, int *temp_len)
{
int ekbytes = jwe->jose.enc_alg->keybits_fixed / 8;
struct lws_genrsa_ctx rsactx;
int n, ret = -1, ot = *temp_len;
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: wrong kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
/* create the IV + CEK */
if (lws_jws_randomize_element(jwe->jws.context, &jwe->jws.map, LJWE_IV,
temp, temp_len,
LWS_AESGCM_IV, 0))
return -1;
if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG,
temp + (ot - *temp_len),
temp_len, LWS_AESGCM_TAG, 0))
return -1;
/* create a b64 version of the JOSE header, needed as aad */
if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
temp + (ot - *temp_len), temp_len,
jwe->jws.map.buf[LJWE_JOSE],
jwe->jws.map.len[LJWE_JOSE]))
return -1;
/*
* If none already, create a new, random CEK in the JWE (so it can be
* reused for other recipients on same payload). If it already exists,
* just reuse it. It will be cleansed in the JWE destroy.
*/
if (!jwe->cek_valid) {
if (lws_get_random(jwe->jws.context, jwe->cek, (unsigned int)ekbytes) !=
(size_t)ekbytes) {
lwsl_err("%s: Problem getting random\n", __func__);
return -1;
}
jwe->cek_valid = 1;
}
if (lws_jws_dup_element(&jwe->jws.map, LJWE_EKEY,
temp + (ot - *temp_len), temp_len,
jwe->cek, (unsigned int)ekbytes, 0))
return -1;
/* encrypt the payload */
n = lws_jwe_encrypt_gcm(jwe, (uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt_gcm failed\n",
__func__);
goto bail;
}
/* Encrypt the CEK into EKEY to make the JWE Encrypted Key */
if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context,
!strcmp(jwe->jose.alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_SHA1 /* !!! */)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
goto bail;
}
n = lws_genrsa_public_encrypt(&rsactx, jwe->cek, (unsigned int)ekbytes,
(uint8_t *)jwe->jws.map.buf[LJWE_EKEY]);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: encrypt cek fail: \n", __func__);
goto bail;
}
/* set the EKEY length to the actual enciphered length */
jwe->jws.map.len[LJWE_EKEY] = (unsigned int)n;
ret = (int32_t)jwe->jws.map.len[LJWE_CTXT];
bail:
return ret;
}
int
lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jwe *jwe)
{
int n;
struct lws_genrsa_ctx rsactx;
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
return -1;
}
if (jwe->jws.map.len[LJWE_EKEY] < 32) {
lwsl_err("%s: EKEY length too short %d\n", __func__,
(int)jwe->jws.map.len[LJWE_EKEY]);
return -1;
}
/* Decrypt the JWE Encrypted Key to get the direct CEK */
if (lws_genrsa_create(&rsactx, jwe->jws.jwk->e, jwe->jws.context,
!strcmp(jwe->jose.alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_SHA1 /* !!! */)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = lws_genrsa_private_decrypt(&rsactx,
(uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
jwe->jws.map.len[LJWE_EKEY], enc_cek,
sizeof(enc_cek));
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: decrypt cek fail: \n", __func__);
return -1;
}
n = lws_jwe_auth_and_decrypt_gcm(jwe, enc_cek,
(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
(int)jwe->jws.map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_gcm_hs failed\n",
__func__);
return -1;
}
#if defined(LWS_WITH_MBEDTLS) && defined(LWS_PLAT_OPTEE)
/* strip padding */
n = jwe->jws.map.buf[LJWE_CTXT][jwe->jws.map.len[LJWE_CTXT] - 1];
if (n > 16)
return -1;
jwe->jws.map.len[LJWE_CTXT] -= n;
#endif
return (int)jwe->jws.map.len[LJWE_CTXT];
}

View File

@ -0,0 +1,791 @@
/*
* 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;
}

View File

@ -0,0 +1,88 @@
/*
* 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.
*/
#define LWS_AESGCM_IV 12
#define LWS_AESGCM_TAG 16
/* jwe-rsa-aescbc.c */
int
lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe);
int
lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jwe *jwe,
char *temp, int *temp_len);
int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
/* jws-rsa-aesgcm.c */
int
lws_jwe_auth_and_decrypt_gcm(struct lws_jwe *jwe, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
int
lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jwe *jwe);
int
lws_jwe_encrypt_gcm(struct lws_jwe *jwe,
uint8_t *enc_cek, uint8_t *aad, int aad_len);
int
lws_jwe_encrypt_rsa_aes_gcm(struct lws_jwe *jwe,
char *temp, int *temp_len);
/* jwe-rsa-aeskw.c */
int
lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jwe *jwe,
char *temp, int *temp_len);
int
lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jwe *jwe);
/* aescbc.c */
int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
int
lws_jwe_encrypt_cbc_hs(struct lws_jwe *jwe,
uint8_t *cek, uint8_t *aad, int aad_len);
int
lws_jwe_auth_and_decrypt_ecdh_cbc_hs(struct lws_jwe *jwe,
char *temp, int *temp_len);
int
lws_jwe_encrypt_ecdh_cbc_hs(struct lws_jwe *jwe,
char *temp, int *temp_len);