Update Files
This commit is contained in:
47
Kinc/Sources/kinc/libs/core/jose/CMakeLists.txt
Normal file
47
Kinc/Sources/kinc/libs/core/jose/CMakeLists.txt
Normal 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()
|
79
Kinc/Sources/kinc/libs/core/jose/README.md
Normal file
79
Kinc/Sources/kinc/libs/core/jose/README.md
Normal 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.
|
||||
|
271
Kinc/Sources/kinc/libs/core/jose/jwe/enc/aescbc.c
Executable file
271
Kinc/Sources/kinc/libs/core/jose/jwe/enc/aescbc.c
Executable 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];
|
||||
}
|
||||
|
173
Kinc/Sources/kinc/libs/core/jose/jwe/enc/aesgcm.c
Normal file
173
Kinc/Sources/kinc/libs/core/jose/jwe/enc/aesgcm.c
Normal 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];
|
||||
}
|
177
Kinc/Sources/kinc/libs/core/jose/jwe/enc/aeskw.c
Normal file
177
Kinc/Sources/kinc/libs/core/jose/jwe/enc/aeskw.c
Normal 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];
|
||||
}
|
||||
|
||||
|
616
Kinc/Sources/kinc/libs/core/jose/jwe/jwe-ecdh-es-aeskw.c
Normal file
616
Kinc/Sources/kinc/libs/core/jose/jwe/jwe-ecdh-es-aeskw.c
Normal 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);
|
||||
}
|
196
Kinc/Sources/kinc/libs/core/jose/jwe/jwe-rsa-aescbc.c
Normal file
196
Kinc/Sources/kinc/libs/core/jose/jwe/jwe-rsa-aescbc.c
Normal 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];
|
||||
}
|
183
Kinc/Sources/kinc/libs/core/jose/jwe/jwe-rsa-aesgcm.c
Normal file
183
Kinc/Sources/kinc/libs/core/jose/jwe/jwe-rsa-aesgcm.c
Normal 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];
|
||||
}
|
791
Kinc/Sources/kinc/libs/core/jose/jwe/jwe.c
Executable file
791
Kinc/Sources/kinc/libs/core/jose/jwe/jwe.c
Executable 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;
|
||||
}
|
88
Kinc/Sources/kinc/libs/core/jose/jwe/private-lib-jose-jwe.h
Normal file
88
Kinc/Sources/kinc/libs/core/jose/jwe/private-lib-jose-jwe.h
Normal 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);
|
649
Kinc/Sources/kinc/libs/core/jose/jwk/jose_key.c
Normal file
649
Kinc/Sources/kinc/libs/core/jose/jwk/jose_key.c
Normal 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;
|
||||
}
|
297
Kinc/Sources/kinc/libs/core/jose/jwk/jwk.c
Normal file
297
Kinc/Sources/kinc/libs/core/jose/jwk/jwk.c
Normal 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;
|
||||
}
|
||||
|
611
Kinc/Sources/kinc/libs/core/jose/jws/jose.c
Normal file
611
Kinc/Sources/kinc/libs/core/jose/jws/jose.c
Normal 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;
|
||||
}
|
1314
Kinc/Sources/kinc/libs/core/jose/jws/jws.c
Normal file
1314
Kinc/Sources/kinc/libs/core/jose/jws/jws.c
Normal file
File diff suppressed because it is too large
Load Diff
27
Kinc/Sources/kinc/libs/core/jose/jws/private-lib-jose-jws.h
Normal file
27
Kinc/Sources/kinc/libs/core/jose/jws/private-lib-jose-jws.h
Normal 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.
|
||||
*/
|
||||
|
53
Kinc/Sources/kinc/libs/core/jose/private-lib-jose.h
Normal file
53
Kinc/Sources/kinc/libs/core/jose/private-lib-jose.h
Normal 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];
|
Reference in New Issue
Block a user