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,47 @@
#
# 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_directories(. ./jwe ./jws ./jwk)
if (LWS_WITH_JOSE)
list(APPEND SOURCES
jose/jws/jose.c
jose/jwk/jwk.c
jose/jwk/jose_key.c
jose/jws/jws.c
jose/jwe/jwe.c
jose/jwe/enc/aescbc.c
jose/jwe/enc/aesgcm.c
jose/jwe/enc/aeskw.c
jose/jwe/jwe-rsa-aescbc.c
jose/jwe/jwe-rsa-aesgcm.c
jose/jwe/jwe-ecdh-es-aeskw.c
)
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,79 @@
# JOSE support
JOSE is a set of web standards aimed at encapsulating crypto
operations flexibly inside JSON objects.
Lws provides lightweight apis to performs operations on JWK, JWS and JWE
independent of the tls backend in use. The JSON parsing is handled by the lws
lejp stream parser.
|Part|RFC|Function|
|---|---|---|
|JWS|[RFC7515](https://tools.ietf.org/html/rfc7515)|JSON Web Signatures|
|JWE|[RFC7516](https://tools.ietf.org/html/rfc7516)|JSON Web Encryption|
|JWK|[RFC7517](https://tools.ietf.org/html/rfc7517)|JSON Web Keys|
|JWA|[RFC7518](https://tools.ietf.org/html/rfc7518)|JSON Web Algorithms|
JWA is a set of recommendations for which combinations of algorithms
are deemed desirable and secure, which implies what must be done for
useful implementations of JWS, JWE and JWK.
## Supported algorithms
### Supported keys
- All RFC7517 / JWK forms: octet, RSA and EC
- singleton and keys[] arrays of keys supported
### Symmetric ciphers
- All common AES varaiants: CBC, CFB128, CFB8, CTR, EVB, OFB, KW and XTS
### Asymmetric ciphers
- RSA
- EC (P-256, P-384 and P-521 JWA curves)
### Payload auth and crypt
- AES_128_CBC_HMAC_SHA_256
- AES_192_CBC_HMAC_SHA_384
- AES_256_CBC_HMAC_SHA_512
- AES_128_GCM
For the required and recommended asymmetric algorithms, support currently
looks like this
|JWK kty|JWA|lws|
|---|---|---|
|EC|Recommended+|yes|
|RSA|Required|yes|
|oct|Required|yes|
|JWE alg|JWA|lws|
|---|---|---|
|RSA1_5|Recommended-|yes|
|RSA-OAEP|Recommended+|no|
|ECDH-ES|Recommended+|no|
|JWS alg|JWA|lws|
|---|---|---|
|HS256|Required|yes|
|RS256|Recommended+|yes|
|ES256|Recommended|yes|
## Minimal Example tools
[JWK](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk)
[JWS](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jws)
[JWE](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
## API tests
See `./minimal-examples/api-tests/api-test-jose/` for example test code.
The tests are built and confirmed during CI.

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);

View File

@ -0,0 +1,649 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* JOSE-specific JWK code
*/
#include "private-lib-core.h"
#include "private-lib-jose.h"
#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
#include <fcntl.h>
#endif
static const char * const kty_names[] = {
"unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */
"oct", /* LWS_GENCRYPTO_KTY_OCT */
"RSA", /* LWS_GENCRYPTO_KTY_RSA */
"EC" /* LWS_GENCRYPTO_KTY_EC */
};
/*
* These are the entire legal token set for names in jwk.
*
* The first version is used to parse a detached single jwk that don't have any
* parent JSON context. The second version is used to parse full jwk objects
* that has a "keys": [ ] array containing the keys.
*/
const char * const jwk_tok[] = {
"keys[]", /* dummy */
"e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */
"kty", /* generic */
"k", /* symmetric key data */
"crv", "x", "y", /* EC (also "D") */
"kid", /* generic */
"use" /* mutually exclusive with "key_ops" */,
"key_ops" /* mutually exclusive with "use" */,
"x5c", /* generic */
"alg" /* generic */
}, * const jwk_outer_tok[] = {
"keys[]",
"keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp",
"keys[].dq", "keys[].qi",
"keys[].kty", "keys[].k", /* generic */
"keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */
"keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */,
"keys[].key_ops", /* mutually exclusive with "use" */
"keys[].x5c", "keys[].alg"
};
static unsigned short tok_map[] = {
F_RSA | F_EC | F_OCT | F_META | 0xff,
F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E,
F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N,
F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI,
F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY,
F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K,
F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV,
F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X,
F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KID,
F_RSA | F_EC | F_OCT | F_META | JWK_META_USE,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS,
F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C,
F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG,
};
struct lexico {
const char *name;
int idx;
char meta;
} lexico_ec[] = {
{ "alg", JWK_META_ALG, 1 },
{ "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 },
{ "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 },
{ "key_ops", JWK_META_KEY_OPS, 1 },
{ "kid", JWK_META_KID, 1 },
{ "kty", JWK_META_KTY, 1 },
{ "use", JWK_META_USE, 1 },
{ "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 },
{ "x5c", JWK_META_X5C, 1 },
{ "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 }
}, lexico_oct[] = {
{ "alg", JWK_META_ALG, 1 },
{ "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 },
{ "key_ops", JWK_META_KEY_OPS, 1 },
{ "kid", JWK_META_KID, 1 },
{ "kty", JWK_META_KTY, 1 },
{ "use", JWK_META_USE, 1 },
{ "x5c", JWK_META_X5C, 1 }
}, lexico_rsa[] = {
{ "alg", JWK_META_ALG, 1 },
{ "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 },
{ "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 },
{ "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 },
{ "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 },
{ "key_ops", JWK_META_KEY_OPS, 1 },
{ "kid", JWK_META_KID, 1 },
{ "kty", JWK_META_KTY, 1 },
{ "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 },
{ "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 },
{ "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 },
{ "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 },
{ "use", JWK_META_USE, 1 },
{ "x5c", JWK_META_X5C, 1 }
};
static int
_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
{
size_t dec_size = (unsigned int)lws_base64_size(len);
int n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
return -1;
/* same decoder accepts both url or original styles */
n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1);
if (n < 0)
return -1;
e->len = (uint32_t)n;
return 0;
}
static int
_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len)
{
size_t dec_size = (size_t)lws_base64_size(len);
int n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
return -1;
/* same decoder accepts both url or original styles */
n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1);
if (n < 0)
return -1;
e->len = (uint32_t)n;
return 0;
}
signed char
cb_jwk(struct lejp_ctx *ctx, char reason)
{
struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user;
struct lws_jwk *jwk = jps->jwk;
unsigned int idx, n;
unsigned short poss;
char dotstar[64];
if (reason == LEJPCB_VAL_STR_START)
jps->pos = 0;
if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1)
/*
* new keys[] member is starting
*
* Until we see some JSON names, it could be anything...
* there is no requirement for kty to be given first and eg,
* ACME specifies the keys must be ordered in lexographic
* order - where kty is not first.
*/
jps->possible = F_RSA | F_EC | F_OCT;
if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) {
/* we completed parsing a key */
if (jps->per_key_cb && jps->possible) {
if (jps->per_key_cb(jps->jwk, jps->user)) {
lwsl_notice("%s: user cb halts import\n",
__func__);
return -2;
}
/* clear it down */
lws_jwk_destroy(jps->jwk);
jps->possible = 0;
}
}
if (reason == LEJPCB_COMPLETE) {
/*
* Now we saw the whole jwk and know the key type, let'jwk insist
* that as a whole, it must be consistent and complete.
*
* The tracking of ->possible bits from even before we know the
* kty already makes certain we cannot have key element members
* defined that are inconsistent with the key type.
*/
for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++)
/*
* All mandataory elements for the key type
* must be present
*/
if ((tok_map[n] & jps->possible) && (
((tok_map[n] & (F_M | F_META)) == (F_M | F_META) &&
!jwk->meta[tok_map[n] & 0xff].buf) ||
((tok_map[n] & (F_M | F_META)) == F_M &&
!jwk->e[tok_map[n] & 0xff].buf))) {
lwsl_notice("%s: missing %s\n", __func__,
jwk_tok[n]);
return -3;
}
/*
* When the key may be public or public + private, ensure the
* intra-key members related to that are consistent.
*
* Only RSA keys need extra care, since EC keys are already
* confirmed by making CRV, X and Y mandatory and only D
* (the singular private part) optional. For RSA, N and E are
* also already known to be present using mandatory checking.
*/
/*
* If a private key, it must have all D, P and Q. Public key
* must have none of them.
*/
if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
!(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) ||
(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf))
) {
lwsl_notice("%s: RSA requires D, P and Q for private\n",
__func__);
return -3;
}
/*
* If the precomputed private key terms appear, they must all
* appear together.
*/
if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
!(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) ||
(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf))
) {
lwsl_notice("%s: RSA DP, DQ, QI must all appear "
"or none\n", __func__);
return -3;
}
/*
* The precomputed private key terms must not appear without
* the private key itself also appearing.
*/
if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) {
lwsl_notice("%s: RSA DP, DQ, QI can appear only with "
"private key\n", __func__);
return -3;
}
if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA ||
jwk->kty == LWS_GENCRYPTO_KTY_EC) &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf)
jwk->private_key = 1;
}
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
if (ctx->path_match == 0 + 1)
return 0;
idx = tok_map[ctx->path_match - 1];
if ((idx & 0xff) == 0xff)
return 0;
switch (idx) {
/* note: kty is not necessarily first... we have to keep track of
* what could match given which element names have already been
* seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk
* not trying to tell us that it'jwk RSA now when we saw a "crv"
* earlier) and then reduce the possibilities to just the one that
* kty told. */
case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY:
if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) {
if (!(jps->possible & F_OCT))
goto elements_mismatch;
jwk->kty = LWS_GENCRYPTO_KTY_OCT;
jps->possible = F_OCT;
goto cont;
}
if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) {
if (!(jps->possible & F_RSA))
goto elements_mismatch;
jwk->kty = LWS_GENCRYPTO_KTY_RSA;
jps->possible = F_RSA;
goto cont;
}
if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) {
if (!(jps->possible & F_EC))
goto elements_mismatch;
jwk->kty = LWS_GENCRYPTO_KTY_EC;
jps->possible = F_EC;
goto cont;
}
lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar));
lwsl_err("%s: Unknown KTY '%s'\n", __func__, dotstar);
return -1;
default:
cont:
if (jps->pos + ctx->npos >= (int)sizeof(jps->b64))
goto bail;
memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos);
jps->pos += ctx->npos;
if (reason == LEJPCB_VAL_STR_CHUNK)
return 0;
/* chunking has been collated */
poss = idx & (F_RSA | F_EC | F_OCT);
jps->possible &= poss;
if (!jps->possible)
goto elements_mismatch;
if (idx & F_META) {
if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f],
jps->b64, (unsigned int)jps->pos) < 0)
goto bail;
break;
}
if (idx & F_B64U) {
/* key data... do the base64 decode as needed */
if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f],
jps->b64, jps->pos) < 0)
goto bail;
if (jwk->e[idx & 0x7f].len >
LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) {
lwsl_notice("%s: oversize keydata\n", __func__);
goto bail;
}
return 0;
}
if (idx & F_B64) {
/* cert data... do non-urlcoded base64 decode */
if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f],
jps->b64, jps->pos) < 0)
goto bail;
return 0;
}
if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f],
jps->b64, (unsigned int)jps->pos) < 0)
goto bail;
break;
}
return 0;
elements_mismatch:
lwsl_err("%s: jwk elements mismatch\n", __func__);
bail:
lwsl_err("%s: element failed\n", __func__);
return -1;
}
int
lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
const char *in, size_t len)
{
struct lejp_ctx jctx;
struct lws_jwk_parse_state jps;
int m;
lws_jwk_init_jps(&jps, jwk, cb, user);
lejp_construct(&jctx, cb_jwk, &jps, cb ? jwk_outer_tok: jwk_tok,
LWS_ARRAY_SIZE(jwk_tok));
m = lejp_parse(&jctx, (uint8_t *)in, (int)len);
lejp_destruct(&jctx);
if (m < 0) {
lwsl_notice("%s: parse got %d\n", __func__, m);
lws_jwk_destroy(jwk);
return -1;
}
switch (jwk->kty) {
case LWS_GENCRYPTO_KTY_UNKNOWN:
lwsl_notice("%s: missing or unknown kty\n", __func__);
lws_jwk_destroy(jwk);
return -1;
default:
break;
}
return 0;
}
int
lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len)
{
char *start = p, *end = &p[*len - 1];
int n, m, limit, first = 1, asym = 0;
struct lexico *l;
/* RFC7638 lexicographic order requires
* RSA: e -> kty -> n
* oct: k -> kty
*
* ie, meta and key data elements appear interleaved in name alpha order
*/
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{");
switch (jwk->kty) {
case LWS_GENCRYPTO_KTY_OCT:
l = lexico_oct;
limit = LWS_ARRAY_SIZE(lexico_oct);
break;
case LWS_GENCRYPTO_KTY_RSA:
l = lexico_rsa;
limit = LWS_ARRAY_SIZE(lexico_rsa);
asym = 1;
break;
case LWS_GENCRYPTO_KTY_EC:
l = lexico_ec;
limit = LWS_ARRAY_SIZE(lexico_ec);
asym = 1;
break;
default:
return -1;
}
for (n = 0; n < limit; n++) {
const char *q, *q_end;
char tok[12];
int pos = 0, f = 1;
if ((l->meta & 1) && (jwk->meta[l->idx].buf ||
l->idx == (int)JWK_META_KTY)) {
switch (l->idx) {
case JWK_META_KTY:
if (!first)
*p++ = ',';
first = 0;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"%s\"",
l->name, kty_names[jwk->kty]);
break;
case JWK_META_KEY_OPS:
if (!first)
*p++ = ',';
first = 0;
q = (const char *)jwk->meta[l->idx].buf;
q_end = q + jwk->meta[l->idx].len;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"\"%s\":[", l->name);
/*
* For the public version, usages that
* require the private part must be
* snipped
*/
while (q < q_end) {
if (*q != ' ' && pos < (int)sizeof(tok) - 1) {
tok[pos++] = *q++;
if (q != q_end)
continue;
}
tok[pos] = '\0';
pos = 0;
if ((flags & LWSJWKF_EXPORT_PRIVATE) ||
!asym || (strcmp(tok, "sign") &&
strcmp(tok, "encrypt"))) {
if (!f)
*p++ = ',';
f = 0;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"\"%s\"", tok);
}
q++;
}
*p++ = ']';
break;
default:
/* both sig and enc require asym private key */
if (!(flags & LWSJWKF_EXPORT_PRIVATE) &&
asym && l->idx == (int)JWK_META_USE)
break;
if (!first)
*p++ = ',';
first = 0;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"",
l->name);
lws_strnncpy(p, (const char *)jwk->meta[l->idx].buf,
jwk->meta[l->idx].len, end - p);
p += strlen(p);
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"");
break;
}
}
if ((!(l->meta & 1)) && jwk->e[l->idx].buf &&
((flags & LWSJWKF_EXPORT_PRIVATE) || !(l->meta & 2))) {
if (!first)
*p++ = ',';
first = 0;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", l->name);
if (jwk->kty == LWS_GENCRYPTO_KTY_EC &&
l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV) {
lws_strnncpy(p,
(const char *)jwk->e[l->idx].buf,
jwk->e[l->idx].len, end - p);
m = (int)strlen(p);
} else
m = lws_jws_base64_enc(
(const char *)jwk->e[l->idx].buf,
jwk->e[l->idx].len, p, lws_ptr_diff_size_t(end, p) - 4);
if (m < 0) {
lwsl_notice("%s: enc failed\n", __func__);
return -1;
}
p += m;
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"");
}
l++;
}
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
(flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n");
*len -= lws_ptr_diff(p, start);
return lws_ptr_diff(p, start);
}
int
lws_jwk_load(struct lws_jwk *jwk, const char *filename,
lws_jwk_key_import_callback cb, void *user)
{
unsigned int buflen = 4096;
char *buf = lws_malloc(buflen, "jwk-load");
int n;
if (!buf)
return -1;
n = lws_plat_read_file(filename, buf, buflen);
if (n < 0)
goto bail;
n = lws_jwk_import(jwk, cb, user, buf, (unsigned int)n);
lws_free(buf);
return n;
bail:
lws_free(buf);
return -1;
}
int
lws_jwk_save(struct lws_jwk *jwk, const char *filename)
{
int buflen = 4096;
char *buf = lws_malloc((unsigned int)buflen, "jwk-save");
int n, m;
if (!buf)
return -1;
n = lws_jwk_export(jwk, LWSJWKF_EXPORT_PRIVATE, buf, &buflen);
if (n < 0)
goto bail;
m = lws_plat_write_file(filename, buf, (size_t)n);
lws_free(buf);
if (m)
return -1;
return 0;
bail:
lws_free(buf);
return -1;
}

View File

@ -0,0 +1,297 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Shared JWK handling that's the same whether JOSE or COSE
*/
#include "private-lib-core.h"
#include "private-lib-jose.h"
static const char *meta_names[] = {
"kty", "kid", "use", "key_ops", "x5c", "alg"
};
static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 };
static const char *oct_names[] = {
"k"
};
static const char oct_b64[] = { 1 };
static const char *rsa_names[] = {
"e", "n", "d", "p", "q", "dp", "dq", "qi"
};
static const char rsa_b64[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
static const char *ec_names[] = {
"crv", "x", "d", "y",
};
static const char ec_b64[] = { 0, 1, 1, 1 };
int
lws_jwk_dump(struct lws_jwk *jwk)
{
const char **enames, *b64;
int elems;
int n;
(void)enames;
(void)meta_names;
switch (jwk->kty) {
default:
case LWS_GENCRYPTO_KTY_UNKNOWN:
lwsl_err("%s: jwk %p: unknown type\n", __func__, jwk);
return 1;
case LWS_GENCRYPTO_KTY_OCT:
elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT;
enames = oct_names;
b64 = oct_b64;
break;
case LWS_GENCRYPTO_KTY_RSA:
elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT;
enames = rsa_names;
b64 = rsa_b64;
break;
case LWS_GENCRYPTO_KTY_EC:
elems = LWS_GENCRYPTO_EC_KEYEL_COUNT;
enames = ec_names;
b64 = ec_b64;
break;
}
lwsl_info("%s: jwk %p\n", __func__, jwk);
for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++) {
if (jwk->meta[n].buf && meta_b64[n]) {
lwsl_info(" meta: %s\n", meta_names[n]);
lwsl_hexdump_info(jwk->meta[n].buf, jwk->meta[n].len);
}
if (jwk->meta[n].buf && !meta_b64[n])
lwsl_info(" meta: %s: '%s'\n", meta_names[n],
jwk->meta[n].buf);
}
for (n = 0; n < elems; n++) {
if (jwk->e[n].buf && b64[n]) {
lwsl_info(" e: %s\n", enames[n]);
lwsl_hexdump_info(jwk->e[n].buf, jwk->e[n].len);
}
if (jwk->e[n].buf && !b64[n])
lwsl_info(" e: %s: '%s'\n", enames[n], jwk->e[n].buf);
}
return 0;
}
int
_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len)
{
e->buf = lws_malloc(len + 1, "jwk");
if (!e->buf)
return -1;
memcpy(e->buf, in, len);
e->buf[len] = '\0';
e->len = (uint32_t)len;
return 0;
}
void
lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
{
int n;
for (n = 0; n < m; n++)
if (el[n].buf) {
/* wipe all key material when it goes out of scope */
lws_explicit_bzero(el[n].buf, el[n].len);
lws_free_set_NULL(el[n].buf);
el[n].len = 0;
}
}
void
lws_jwk_destroy(struct lws_jwk *jwk)
{
lws_jwk_destroy_elements(jwk->e, LWS_ARRAY_SIZE(jwk->e));
lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->meta));
}
void
lws_jwk_init_jps(struct lws_jwk_parse_state *jps,
struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
void *user)
{
if (jwk)
memset(jwk, 0, sizeof(*jwk));
jps->jwk = jwk;
jps->possible = F_RSA | F_EC | F_OCT;
jps->per_key_cb = cb;
jps->user = user;
jps->pos = 0;
jps->seen = 0;
jps->cose_state = 0;
}
int
lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len)
{
unsigned int ulen = (unsigned int)len;
jwk->e[LWS_GENCRYPTO_KTY_OCT].buf = lws_malloc(ulen, __func__);
if (!jwk->e[LWS_GENCRYPTO_KTY_OCT].buf)
return -1;
jwk->kty = LWS_GENCRYPTO_KTY_OCT;
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = ulen;
memcpy(jwk->e[LWS_GENCRYPTO_KTY_OCT].buf, key, ulen);
return 0;
}
int
lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
enum lws_gencrypto_kty kty, int bits, const char *curve)
{
size_t sn;
int n;
memset(jwk, 0, sizeof(*jwk));
jwk->kty = (int)kty;
jwk->private_key = 1;
switch (kty) {
case LWS_GENCRYPTO_KTY_RSA:
{
struct lws_genrsa_ctx ctx;
lwsl_notice("%s: generating %d bit RSA key\n", __func__, bits);
n = lws_genrsa_new_keypair(context, &ctx, LGRSAM_PKCS1_1_5,
jwk->e, bits);
lws_genrsa_destroy(&ctx);
if (n) {
lwsl_err("%s: problem generating RSA key\n", __func__);
return 1;
}
}
break;
case LWS_GENCRYPTO_KTY_OCT:
sn = (unsigned int)lws_gencrypto_bits_to_bytes(bits);
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct");
if (!jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf)
return 1;
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = (uint32_t)sn;
if (lws_get_random(context,
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, sn) != sn) {
lwsl_err("%s: problem getting random\n", __func__);
return 1;
}
break;
case LWS_GENCRYPTO_KTY_EC:
{
struct lws_genec_ctx ctx;
if (!curve) {
lwsl_err("%s: must have a named curve\n", __func__);
return 1;
}
if (lws_genecdsa_create(&ctx, context, NULL))
return 1;
lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__,
curve);
n = lws_genecdsa_new_keypair(&ctx, curve, jwk->e);
lws_genec_destroy(&ctx);
if (n) {
lwsl_err("%s: problem generating ECDSA key\n", __func__);
return 1;
}
}
break;
case LWS_GENCRYPTO_KTY_UNKNOWN:
default:
lwsl_err("%s: unknown kty\n", __func__);
return 1;
}
return 0;
}
int
lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32)
{
struct lws_genhash_ctx hash_ctx;
size_t tmpsize = 2536;
char *tmp;
int n, m = (int)tmpsize;
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
n = lws_jwk_export(jwk, LWSJWKF_EXPORT_NOCRLF, tmp, &m);
if (n < 0)
goto bail;
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
goto bail;
if (lws_genhash_update(&hash_ctx, tmp, (unsigned int)n)) {
lws_genhash_destroy(&hash_ctx, NULL);
goto bail;
}
lws_free(tmp);
if (lws_genhash_destroy(&hash_ctx, digest32))
return -1;
return 0;
bail:
lws_free(tmp);
return -1;
}
int
lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
const char *in, int len)
{
jwk->meta[idx].buf = lws_malloc((unsigned int)len, __func__);
if (!jwk->meta[idx].buf)
return 1;
jwk->meta[idx].len = (uint32_t)(unsigned int)len;
memcpy(jwk->meta[idx].buf, in, (unsigned int)len);
return 0;
}

View File

@ -0,0 +1,611 @@
/*
* 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.
*
* JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515
* to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
*/
#include "private-lib-core.h"
#include "jose/private-lib-jose.h"
#include <stdint.h>
static const char * const jws_jose[] = {
"alg", /* REQUIRED */
"jku",
"jwk",
"kid",
"x5u",
"x5c",
"x5t",
"x5t#S256",
"typ",
"cty",
"crit",
/* valid for JWE only below here */
"recipients[].header",
"recipients[].header.alg",
"recipients[].header.kid",
"recipients[].encrypted_key",
"enc",
"zip", /* ("DEF" = deflate) */
"epk", /* valid for JWE ECDH only */
"apu", /* valid for JWE ECDH only */
"apv", /* valid for JWE ECDH only */
"iv", /* valid for JWE AES only */
"tag", /* valid for JWE AES only */
"p2s", /* valid for JWE PBES2 only */
"p2c" /* valid for JWE PBES2 only */
};
struct jose_cb_args {
struct lws_jose *jose;
struct lejp_ctx jwk_jctx; /* fake lejp context used to parse epk */
struct lws_jwk_parse_state jps; /* fake jwk parse state */
char *temp;
int *temp_len;
unsigned int is_jwe;
unsigned int recipients_array;
int recip;
};
/*
* JWE A.4.7 Complete JWE JSON Serialization example
*
* LEJPCB_CONSTRUCTED
* LEJPCB_START
* LEJPCB_OBJECT_START
*
* protected LEJPCB_PAIR_NAME
* protected LEJPCB_VAL_STR_START
* protected LEJPCB_VAL_STR_END
*
* unprotected LEJPCB_PAIR_NAME
* unprotected LEJPCB_OBJECT_START
* unprotected.jku LEJPCB_PAIR_NAME
* unprotected.jku LEJPCB_VAL_STR_START
* unprotected.jku LEJPCB_VAL_STR_END
* unprotected.jku LEJPCB_OBJECT_END
*
* recipients LEJPCB_PAIR_NAME
* recipients[] LEJPCB_ARRAY_START
*
* recipients[] LEJPCB_OBJECT_START
* recipients[].header LEJPCB_PAIR_NAME
* recipients[].header LEJPCB_OBJECT_START
* recipients[].header.alg LEJPCB_PAIR_NAME
* recipients[].header.alg LEJPCB_VAL_STR_START
* recipients[].header.alg LEJPCB_VAL_STR_END
* recipients[].header.kid LEJPCB_PAIR_NAME
* recipients[].header.kid LEJPCB_VAL_STR_START
* recipients[].header.kid LEJPCB_VAL_STR_END
* recipients[] LEJPCB_OBJECT_END
* recipients[].encrypted_key LEJPCB_PAIR_NAME
* recipients[].encrypted_key LEJPCB_VAL_STR_START
* recipients[].encrypted_key LEJPCB_VAL_STR_CHUNK
* recipients[].encrypted_key LEJPCB_VAL_STR_END
* recipients[] LEJPCB_OBJECT_END (ctx->sp = 1)
*
* recipients[] LEJPCB_OBJECT_START
* recipients[].header LEJPCB_PAIR_NAME
* recipients[].header LEJPCB_OBJECT_START
* recipients[].header.alg LEJPCB_PAIR_NAME
* recipients[].header.alg LEJPCB_VAL_STR_START
* recipients[].header.alg LEJPCB_VAL_STR_END
* recipients[].header.kid LEJPCB_PAIR_NAME
* recipients[].header.kid LEJPCB_VAL_STR_START
* recipients[].header.kid LEJPCB_VAL_STR_END
* recipients[] LEJPCB_OBJECT_END
* recipients[].encrypted_key LEJPCB_PAIR_NAME
* recipients[].encrypted_key LEJPCB_VAL_STR_START
* recipients[].encrypted_key LEJPCB_VAL_STR_END
* recipients[] LEJPCB_OBJECT_END (ctx->sp = 1)
*
* recipients[] LEJPCB_ARRAY_END
*
* iv LEJPCB_PAIR_NAME
* iv LEJPCB_VAL_STR_START
* iv LEJPCB_VAL_STR_END
* ciphertext LEJPCB_PAIR_NAME
* ciphertext LEJPCB_VAL_STR_START
* ciphertext LEJPCB_VAL_STR_END
* tag LEJPCB_PAIR_NAME
* tag LEJPCB_VAL_STR_START
* tag LEJPCB_VAL_STR_END
*
* tag LEJPCB_OBJECT_END
* tag LEJPCB_COMPLETE
* tag LEJPCB_DESTRUCTED
*
*/
/*
* RFC7516 7.2.2
*
* Note that when using the flattened syntax, just as when using the
* general syntax, any unprotected Header Parameter values can reside in
* either the "unprotected" member or the "header" member, or in both.
*/
static signed char
lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
{
struct jose_cb_args *args = (struct jose_cb_args *)ctx->user;
int n; //, dest;
/*
* In JOSE JSON, the element "epk" contains a fully-formed JWK.
*
* For JOSE paths beginning "epk.", we pass them through to a JWK
* LEJP subcontext to parse using the JWK parser directly.
*/
if (args->is_jwe && !strncmp(ctx->path, "epk.", 4)) {
memcpy(args->jwk_jctx.path, ctx->path + 4,
sizeof(ctx->path) - 4);
memcpy(args->jwk_jctx.buf, ctx->buf, ctx->npos);
args->jwk_jctx.npos = ctx->npos;
if (!ctx->path_match)
args->jwk_jctx.path_match = 0;
lejp_check_path_match(&args->jwk_jctx);
if (args->jwk_jctx.path_match)
args->jwk_jctx.pst[args->jwk_jctx.pst_sp].
callback(&args->jwk_jctx, reason);
}
// lwsl_notice("%s: %s %d (%d)\n", __func__, ctx->path, reason, ctx->sp);
/* at the end of each recipients[] entry, bump recipients count */
if (args->is_jwe && reason == LEJPCB_OBJECT_END && ctx->sp == 1 &&
!strcmp(ctx->path, "recipients[]"))
args->jose->recipients++;
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
//dest = ctx->path_match - 1;
switch (ctx->path_match - 1) {
/* strings */
case LJJHI_ALG: /* REQUIRED */
/*
* look up whether we support this alg and point the caller at
* its definition if so
*/
if (!args->is_jwe &&
lws_gencrypto_jws_alg_to_definition(ctx->buf,
&args->jose->alg)) {
lwsl_notice("%s: unknown alg '%s'\n", __func__,
ctx->buf);
return -1;
}
if (args->is_jwe &&
lws_gencrypto_jwe_alg_to_definition(ctx->buf,
&args->jose->alg)) {
lwsl_notice("%s: unknown JWE alg '%s'\n", __func__,
ctx->buf);
return -1;
}
return 0;
case LJJHI_TYP: /* Optional: string: media type */
lws_strnncpy(args->jose->typ, ctx->buf, ctx->npos,
sizeof(args->jose->typ));
break;
case LJJHI_JKU: /* Optional: string */
case LJJHI_KID: /* Optional: string */
case LJJHI_X5U: /* Optional: string: url of public key cert / chain */
case LJJHI_CTY: /* Optional: string: content media type */
/* base64 */
case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */
/* base64-url */
case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */
case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of actual cert */
/* array of strings */
case LJJHI_CRIT: /* Optional for send, REQUIRED: array of strings:
* mustn't contain standardized strings or null set */
break;
/* jwk child */
case LJJHI_JWK: /* Optional: jwk JSON object: public key: */
/* past here, JWE only */
case LJJHI_RECIPS_HDR:
if (!args->is_jwe) {
lwsl_info("%s: recipients in jws\n", __func__);
return -1;
}
args->recipients_array = 1;
break;
case LJJHI_RECIPS_HDR_ALG:
case LJJHI_RECIPS_HDR_KID:
break;
case LJJHI_RECIPS_EKEY:
if (!args->is_jwe) {
lwsl_info("%s: recipients in jws\n", __func__);
return -1;
}
args->recipients_array = 1;
//dest = ;
goto append_string;
case LJJHI_ENC: /* JWE only: Mandatory: string */
if (!args->is_jwe) {
lwsl_info("%s: enc in jws\n", __func__);
return -1;
}
if (lws_gencrypto_jwe_enc_to_definition(ctx->buf,
&args->jose->enc_alg)) {
lwsl_notice("%s: unknown enc '%s'\n", __func__,
ctx->buf);
return -1;
}
break;
case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */
if (!args->is_jwe)
return -1;
goto append_string;
case LJJHI_EPK: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
/* Ephemeral key... this JSON subsection is actually a JWK */
break;
case LJJHI_APU: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
/* Agreement Party U */
goto append_string;
case LJJHI_APV: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
/* Agreement Party V */
goto append_string;
case LJJHI_IV: /* Additional arg for JWE AES */
if (!args->is_jwe)
return -1;
goto append_string;
case LJJHI_TAG: /* Additional arg for JWE AES */
if (!args->is_jwe)
return -1;
goto append_string;
case LJJHI_P2S: /* Additional arg for JWE PBES2 */
if (!args->is_jwe)
return -1;
goto append_string;
case LJJHI_P2C: /* Additional arg for JWE PBES2 */
if (!args->is_jwe)
return -1;
goto append_string;
/* ignore what we don't understand */
default:
return 0;
}
return 0;
append_string:
if (*args->temp_len < ctx->npos) {
lwsl_err("%s: out of parsing space\n", __func__);
return -1;
}
if (!args->jose->e[ctx->path_match - 1].buf) {
args->jose->e[ctx->path_match - 1].buf = (uint8_t *)args->temp;
args->jose->e[ctx->path_match - 1].len = 0;
}
memcpy(args->temp, ctx->buf, ctx->npos);
args->temp += ctx->npos;
*args->temp_len -= ctx->npos;
args->jose->e[ctx->path_match - 1].len += ctx->npos;
if (reason == LEJPCB_VAL_STR_END &&
(int)args->jose->e[ctx->path_match - 1].len &&
!args->jose->edone[ctx->path_match - 1]) {
n = lws_b64_decode_string_len(
(const char *)args->jose->e[ctx->path_match - 1].buf,
(int)args->jose->e[ctx->path_match - 1].len,
(char *)args->jose->e[ctx->path_match - 1].buf,
(int)args->jose->e[ctx->path_match - 1].len + 1);
if (n < 0) {
lwsl_err("%s: b64 decode failed len %d\n", __func__,
(int)args->jose->e[ctx->path_match - 1].len);
return -1;
}
args->jose->edone[ctx->path_match - 1] = 1;
args->temp -= (int)args->jose->e[ctx->path_match - 1].len - n - 1;
*args->temp_len +=
(int)args->jose->e[ctx->path_match - 1].len - n - 1;
args->jose->e[ctx->path_match - 1].len = (uint32_t)n;
}
return 0;
}
void
lws_jose_init(struct lws_jose *jose)
{
memset(jose, 0, sizeof(*jose));
}
static void
lws_jose_recip_destroy(struct lws_jws_recpient *r)
{
lws_jwk_destroy(&r->jwk_ephemeral);
lws_jwk_destroy(&r->jwk);
}
void
lws_jose_destroy(struct lws_jose *jose)
{
int n;
for (n = 0; n < (int)LWS_ARRAY_SIZE(jose->recipient); n++)
lws_jose_recip_destroy(&jose->recipient[n]);
}
static int
lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n,
char *temp, int *temp_len, int is_jwe)
{
struct lejp_ctx jctx;
struct jose_cb_args args;
int m;
if (is_jwe) {
/* prepare a context for JOSE epk ephemeral jwk parsing */
lws_jwk_init_jps(&args.jps,
&jose->recipient[jose->recipients].jwk_ephemeral,
NULL, NULL);
lejp_construct(&args.jwk_jctx, cb_jwk, &args.jps,
jwk_tok, LWS_ARRAY_SIZE(jwk_tok));
}
args.is_jwe = (unsigned int)is_jwe;
args.temp = temp;
args.temp_len = temp_len;
args.jose = jose;
args.recip = 0;
args.recipients_array = 0;
jose->recipients = 0;
lejp_construct(&jctx, lws_jws_jose_cb, &args, jws_jose,
LWS_ARRAY_SIZE(jws_jose));
m = lejp_parse(&jctx, (uint8_t *)buf, n);
lejp_destruct(&jctx);
if (m < 0) {
lwsl_notice("%s: parse returned %d\n", __func__, m);
return -1;
}
if (!args.recipients_array && jose->recipient[0].unprot[LJJHI_ALG].buf)
/* if no explicit recipients[], we got one */
jose->recipients++;
return 0;
}
int
lws_jws_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len)
{
return lws_jose_parse(jose, (const uint8_t *)buf, len,
temp, temp_len, 0);
}
int
lws_jwe_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len)
{
return lws_jose_parse(jose,
(const uint8_t *)buf, len, temp, temp_len, 1);
}
int
lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
char *out, size_t out_len)
{
struct lws_jwk *jwk;
char *end = out + out_len - 1;
int n, m, f, sub = 0, vl;
/* JOSE requires an alg */
if (!jose->alg || !jose->alg->alg)
goto bail;
*out++ = '{';
for (n = 0; n < LWS_COUNT_JOSE_HDR_ELEMENTS; n++) {
switch (n) {
/* strings */
case LJJHI_ALG: /* REQUIRED */
case LJJHI_JKU: /* Optional: string */
case LJJHI_KID: /* Optional: string */
case LJJHI_TYP: /* Optional: string: media type */
case LJJHI_CTY: /* Optional: string: content media type */
case LJJHI_X5U: /* Optional: string: pubkey cert / chain URL */
case LJJHI_ENC: /* JWE only: Optional: string */
case LJJHI_ZIP: /* JWE only: Optional: string ("DEF"=deflate) */
if (jose->e[n].buf) {
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
"%s\"%s\":\"%s\"", sub ? ",\n" : "",
jws_jose[n], jose->e[n].buf);
sub = 1;
}
break;
case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */
case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of cert */
case LJJHI_APU: /* Additional arg for JWE ECDH: b64url */
case LJJHI_APV: /* Additional arg for JWE ECDH: b64url */
case LJJHI_IV: /* Additional arg for JWE AES: b64url */
case LJJHI_TAG: /* Additional arg for JWE AES: b64url */
case LJJHI_P2S: /* Additional arg for JWE PBES2: b64url: salt */
if (jose->e[n].buf) {
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
"%s\"%s\":\"", sub ? ",\n" : "",
jws_jose[n]);
sub = 1;
m = lws_b64_encode_string_url((const char *)
jose->e[n].buf, (int)jose->e[n].len,
out, lws_ptr_diff(end, out));
if (m < 0)
return -1;
out += m;
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out), "\"");
}
break;
case LJJHI_P2C: /* Additional arg for JWE PBES2: int: count */
break; /* don't support atm */
case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */
if (jose->e[n].buf) {
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
"%s\"%s\":\"", sub ? ",\n" : "",
jws_jose[n]);
sub = 1;
m = lws_b64_encode_string((const char *)
jose->e[n].buf, (int)jose->e[n].len,
out, lws_ptr_diff(end, out));
if (m < 0)
return -1;
out += m;
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out), "\"");
}
break;
case LJJHI_EPK: /* Additional arg for JWE ECDH: eph pubkey */
case LJJHI_JWK: /* Optional: jwk JSON object: public key: */
jwk = n == LJJHI_EPK ? &jose->recipient[0].jwk_ephemeral : aux_jwk;
if (!jwk || !jwk->kty)
break;
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out), "%s\"%s\":",
sub ? ",\n" : "", jws_jose[n]);
sub = 1;
vl = lws_ptr_diff(end, out);
m = lws_jwk_export(jwk, 0, out, &vl);
if (m < 0) {
lwsl_notice("%s: failed to export key\n",
__func__);
return -1;
}
out += m;
break;
case LJJHI_CRIT:/* Optional for send, REQUIRED: array of strings:
* mustn't contain standardized strings or null set */
if (!jose->e[n].buf)
break;
out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
"%s\"%s\":[", sub ? ",\n" : "", jws_jose[n]);
sub = 1;
m = 0;
f = 1;
while ((unsigned int)m < jose->e[n].len && (end - out) > 1) {
if (jose->e[n].buf[m] == ' ') {
if (!f)
*out++ = '\"';
m++;
f = 1;
continue;
}
if (f) {
if (m)
*out++ = ',';
*out++ = '\"';
f = 0;
}
*out++ = (char)jose->e[n].buf[m];
m++;
}
break;
}
}
*out++ = '}';
if (out > end - 2)
return -1;
return lws_ptr_diff(out_len, (end - out)) - 1;
bail:
return -1;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
/*
* 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.
*
* JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515
* to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
*/

View File

@ -0,0 +1,53 @@
/*
* 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.
*/
/* information about each token declared above */
#define F_M (1 << 9) /* Mandatory for key type */
#define F_B64 (1 << 10) /* Base64 coded octets */
#define F_B64U (1 << 11) /* Base64 Url coded octets */
#define F_META (1 << 12) /* JWK key metainformation */
#define F_RSA (1 << 13) /* RSA key */
#define F_EC (1 << 14) /* Elliptic curve key */
#define F_OCT (1 << 15) /* octet key */
void
lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m);
int
lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
char *out, size_t out_len);
int
_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len);
void
lws_jwk_init_jps(struct lws_jwk_parse_state *jps,
struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
void *user);
signed char
cb_jwk(struct lejp_ctx *ctx, char reason);
extern const char * const jwk_tok[19], * const jwk_outer_tok[19];