diff --git a/CMakeLists.txt b/CMakeLists.txt
index e44fee848..7de8765df 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,8 +106,10 @@ option(LWS_WITH_NO_LOGS "Disable all logging from being compiled in" OFF)
option(LWS_AVOID_SIGPIPE_IGN "Android 7+ reportedly needs this" OFF)
option(LWS_WITH_STATS "Keep statistics of lws internal operations" OFF)
option(LWS_WITH_JWS "JSON Web Signature (RFC7515) API" OFF)
+option(LWS_WITH_JWE "JSON Web Encryption (RFC7516) API" OFF)
option(LWS_WITH_GENHASH "Enable support for Generic Hash (SHA1 + SHA2 with api independent of TLS backend)" OFF)
option(LWS_WITH_GENRSA "Enable support for Generic RSA (RSA with api independent of TLS backend)" OFF)
+option(LWS_WITH_GENEC "Enable support for Generic EC (EC with api independent of TLS backend)" OFF)
option(LWS_WITH_SELFTESTS "Selftests run at context creation" OFF)
option(LWS_WITH_GCOV "Build with gcc gcov coverage instrumentation" OFF)
option(LWS_WITH_EXPORT_LWSTARGETS "Export libwebsockets CMake targets. Disable if they conflict with an outer cmake project." ON)
@@ -156,6 +158,9 @@ if(LWS_WITH_DISTRO_RECOMMENDED)
set(LWS_WITH_LEJP_CONF 1)
set(LWS_WITH_PLUGINS 1)
set(LWS_ROLE_RAW_PROXY 1)
+ set(LWS_WITH_GENHASH 1)
+ set(LWS_WITH_GENRSA 1)
+ set(LWS_WITH_GENEC 1)
endif()
# do you care about this? Then send me a patch where it disables it on travis
@@ -295,10 +300,15 @@ if (LWS_WITH_ACME)
set (LWS_WITH_JWS 1)
endif()
+if (LWS_WITH_JWE)
+ set(LWS_WITH_JWS 1)
+endif()
+
if (LWS_WITH_JWS)
set(LWS_WITH_LEJP 1)
set(LWS_WITH_GENHASH 1)
set(LWS_WITH_GENRSA 1)
+ set(LWS_WITH_GENEC 1)
endif()
if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
@@ -1021,6 +1031,11 @@ if (LWS_WITH_SSL)
lib/tls/mbedtls/lws-genrsa.c
)
endif()
+ if (LWS_WITH_GENEC)
+ list(APPEND SOURCES
+ lib/tls/mbedtls/lws-genec.c
+ )
+ endif()
else()
list(APPEND SOURCES
lib/tls/openssl/ssl.c
@@ -1035,6 +1050,11 @@ if (LWS_WITH_SSL)
lib/tls/openssl/lws-genrsa.c
)
endif()
+ if (LWS_WITH_GENEC)
+ list(APPEND SOURCES
+ lib/tls/openssl/lws-genec.c
+ )
+ endif()
endif()
if (NOT LWS_WITHOUT_SERVER)
@@ -1191,8 +1211,14 @@ endif()
if (LWS_WITH_JWS)
list(APPEND SOURCES
- lib/misc/jws/jwk.c
- lib/misc/jws/jws.c)
+ lib/jose/jwk/jwk.c
+ lib/jose/jws/jose.c
+ lib/jose/jws/jws.c)
+endif()
+
+if (LWS_WITH_JWE)
+ list(APPEND SOURCES
+ lib/jose/jwe/jwe.c)
endif()
# Add helper files for Windows.
diff --git a/READMEs/README.crypto-apis.md b/READMEs/README.crypto-apis.md
new file mode 100644
index 000000000..193c801f6
--- /dev/null
+++ b/READMEs/README.crypto-apis.md
@@ -0,0 +1,40 @@
+# Lws Crypto Apis
+
+## Overview
+
+
+
+Lws provides a "generic" crypto layer on top of both OpenSSL and
+compatible tls library, and mbedtls. Using this layer, your code
+can work without any changes on both types of tls library crypto
+backends... it's as simple as rebuilding lws with `-DLWS_WITH_MBEDTLS=0`
+or `=1` at cmake.
+
+The generic layer can be used directly (as in, eg, the sshd plugin),
+or via another layer on top, which processes JOSE JSON objects using
+JWS (JSON Web Signatures), JWK (JSON Web Keys), and JWE (JSON Web
+Encryption).
+
+## Using the generic layer
+
+All the necessary includes are part of `libwebsockets.h`.
+
+|api|cmake|header|Functionality|
+|---|---|---|---|
+|genhash|`LWS_WITH_GENHASH`|[./include/libwebsockets/lws-genhash.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genhash.h)|Provides SHA1 + SHA2 hashes and hmac|
+|genrsa|`LWS_WITH_GENRSA`|[./include/libwebsockets/lws-genrsa.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genrsa.h)|Provides RSA encryption, decryption, signing, verification, key generation and creation|
+
+Unit tests for these apis, which serve as usage examples, can be found in [./minimal-examples/api-tests/api-test-gencrypto](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-gencrypto)
+
+## Using the JOSE layer
+
+All the necessary includes are part of `libwebsockets.h`.
+
+|api|cmake|header|Functionality|
+|---|---|---|---|
+|JOSE|`LWS_WITH_JWS`|[./include/libwebsockets/jose.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jose.h)|Provides signature and verifcation services for RFC7515 JOSE JSON|
+|JWS|`LWS_WITH_JWS`|[./include/libwebsockets/jws.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jws.h)|Provides signature and verifcation services for RFC7515 JWS JSON|
+|JWK|`LWS_WITH_JWS`|[./include/libwebsockets/jwk.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jwk.h)|Provides signature and verifcation services for RFC7517 JWK JSON, both "keys" arrays and singletons|
+
+Unit tests for these apis, which serve as usage examples, can be found in [./minimal-examples/api-tests/api-test-jose](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose)
+
diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in
index 41c06afcf..ee2f4f800 100644
--- a/cmake/lws_config.h.in
+++ b/cmake/lws_config.h.in
@@ -75,6 +75,7 @@
#cmakedefine LWS_WITH_HTTP_PROXY
#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION
#cmakedefine LWS_WITH_IPV6
+#cmakedefine LWS_WITH_JWE
#cmakedefine LWS_WITH_JWS
#cmakedefine LWS_WITH_LEJP
#cmakedefine LWS_WITH_LIBEV
diff --git a/doc-assets/lws-crypto-overview.svg b/doc-assets/lws-crypto-overview.svg
new file mode 100644
index 000000000..559c8c4a0
--- /dev/null
+++ b/doc-assets/lws-crypto-overview.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/include/libwebsockets.h b/include/libwebsockets.h
index 7cc5c28b7..831904f3a 100644
--- a/include/libwebsockets.h
+++ b/include/libwebsockets.h
@@ -422,9 +422,11 @@ struct lws;
#endif
#include
-#include
#include
+#include
#include
+#include
+#include
#endif
diff --git a/include/libwebsockets/lws-genec.h b/include/libwebsockets/lws-genec.h
new file mode 100644
index 000000000..38bd9c851
--- /dev/null
+++ b/include/libwebsockets/lws-genec.h
@@ -0,0 +1,34 @@
+/*
+ * libwebsockets - Generic Elliptic Curve Encryption
+ *
+ * Copyright (C) 2010-2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * included from libwebsockets.h
+ */
+
+/* include/libwebsockets/lws-jwk.h must be included before this */
+
+struct lws_genec_ctx {
+#if defined(LWS_WITH_MBEDTLS)
+ mbedtls_rsa_context *ctx;
+#else
+ BIGNUM *bn[LWS_COUNT_EC_KEY_ELEMENTS];
+ RSA *rsa;
+#endif
+};
+
diff --git a/include/libwebsockets/lws-genrsa.h b/include/libwebsockets/lws-genrsa.h
index 3e427e292..f9a204ba5 100644
--- a/include/libwebsockets/lws-genrsa.h
+++ b/include/libwebsockets/lws-genrsa.h
@@ -32,44 +32,23 @@
*/
///@{
-enum enum_jwk_tok {
- JWK_KEY_E,
- JWK_KEY_N,
- JWK_KEY_D,
- JWK_KEY_P,
- JWK_KEY_Q,
- JWK_KEY_DP,
- JWK_KEY_DQ,
- JWK_KEY_QI,
- JWK_KTY, /* also serves as count of real elements */
- JWK_KEY,
-};
-
-#define LWS_COUNT_RSA_ELEMENTS JWK_KTY
+/* include/libwebsockets/lws-jwk.h must be included before this */
struct lws_genrsa_ctx {
#if defined(LWS_WITH_MBEDTLS)
mbedtls_rsa_context *ctx;
#else
- BIGNUM *bn[LWS_COUNT_RSA_ELEMENTS];
+ BIGNUM *bn[LWS_COUNT_RSA_KEY_ELEMENTS];
RSA *rsa;
#endif
-};
-
-struct lws_genrsa_element {
- uint8_t *buf;
- uint16_t len;
-};
-
-struct lws_genrsa_elements {
- struct lws_genrsa_element e[LWS_COUNT_RSA_ELEMENTS];
+ struct lws_context *context;
};
/** lws_jwk_destroy_genrsa_elements() - Free allocations in genrsa_elements
*
- * \param el: your struct lws_genrsa_elements
+ * \param el: your struct lws_jwk_elements
*
- * This is a helper for user code making use of struct lws_genrsa_elements
+ * This is a helper for user code making use of struct lws_jwk_elements
* where the elements are allocated on the heap, it frees any non-NULL
* buf element and sets the buf to NULL.
*
@@ -77,7 +56,7 @@ struct lws_genrsa_elements {
* creation and destruction themselves.
*/
LWS_VISIBLE LWS_EXTERN void
-lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el);
+lws_jwk_destroy_genrsa_elements(struct lws_jwk_elements *el);
/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
*
@@ -92,7 +71,8 @@ lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el);
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
-lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el);
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_jwk_elements *el,
+ struct lws_context *context);
/** lws_genrsa_new_keypair() - Create new RSA keypair
*
@@ -110,7 +90,24 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el);
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
- struct lws_genrsa_elements *el, int bits);
+ struct lws_jwk_elements *el, int bits);
+
+/** lws_genrsa_public_encrypt() - Perform RSA public encryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: plaintext input
+ * \param in_len: length of plaintext input
+ * \param out: encrypted output
+ *
+ * Performs PKCS1 v1.5 Encryption
+ *
+ * Returns <0 for error, or length of decrypted data.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out);
/** lws_genrsa_public_decrypt() - Perform RSA public decryption
*
@@ -120,7 +117,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
* \param out: decrypted output
* \param out_max: size of output buffer
*
- * Performs the decryption.
+ * Performs PKCS1 v1.5 Decryption
*
* Returns <0 for error, or length of decrypted data.
*
diff --git a/include/libwebsockets/lws-jose.h b/include/libwebsockets/lws-jose.h
new file mode 100644
index 000000000..a46af795e
--- /dev/null
+++ b/include/libwebsockets/lws-jose.h
@@ -0,0 +1,71 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * included from libwebsockets.h
+ */
+
+enum lws_jws_jose_hdr_indexes {
+ LJJHI_ALG, /* REQUIRED */
+ LJJHI_JKU, /* Optional: string */
+ LJJHI_JWK, /* Optional: jwk JSON object: public key: */
+ LJJHI_KID, /* Optional: string */
+ LJJHI_X5U, /* Optional: string: url of public key cert / chain */
+ LJJHI_X5C, /* Optional: base64 (NOT -url): actual cert */
+ LJJHI_X5T, /* Optional: base64url: SHA-1 of actual cert */
+ LJJHI_X5T_S256, /* Optional: base64url: SHA-256 of actual cert */
+ LJJHI_TYP, /* Optional: string: media type */
+ LJJHI_CTY, /* Optional: string: content media type */
+ LJJHI_CRIT, /* Optional for send, REQUIRED: array of strings:
+ * mustn't contain standardized strings or null set */
+
+ LJJHI_ENC, /* JWE only: Optional: string */
+ LJJHI_ZIP, /* JWE only: Optional: string ("DEF" = deflate) */
+
+ LJJHI_EPK, /* Additional arg for JWE ECDH: ephemeral public key */
+ LJJHI_APU, /* Additional arg for JWE ECDH: base64url */
+ LJJHI_APV, /* Additional arg for JWE ECDH: base64url */
+ LJJHI_IV, /* Additional arg for JWE AES: base64url */
+ LJJHI_TAG, /* Additional arg for JWE AES: base64url */
+ LJJHI_P2S, /* Additional arg for JWE PBES2: base64url: salt */
+ LJJHI_P2C, /* Additional arg for JWE PBES2: integer: count */
+
+ LWS_COUNT_JOSE_HDR_ELEMENTS
+};
+
+struct lws_jose {
+ /* jose header elements */
+ struct lws_jwk_elements e[LWS_COUNT_JOSE_HDR_ELEMENTS];
+};
+
+enum lws_jws_algtype {
+ LWS_JWK_ENCTYPE_NONE,
+ LWS_JWK_ENCTYPE_RSASSA,
+ LWS_JWK_ENCTYPE_EC
+};
+
+struct cb_hdr_s {
+ enum lws_genhash_types hash_type;
+ enum lws_genhmac_types hmac_type;
+ char alg[24]; /* for jwe, the JWA enc alg name, eg "ECDH-ES" */
+ char curve[16];
+ enum lws_jws_algtype algtype; /* for jws, the signing cipher */
+
+ char is_jwe;
+};
diff --git a/include/libwebsockets/lws-jwe.h b/include/libwebsockets/lws-jwe.h
new file mode 100644
index 000000000..d5227c9fc
--- /dev/null
+++ b/include/libwebsockets/lws-jwe.h
@@ -0,0 +1,22 @@
+/*
+ * libwebsockets - JSON Web Encryption
+ *
+ * Copyright (C) 2010-2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * included from libwebsockets.h
+ */
diff --git a/include/libwebsockets/lws-jwk.h b/include/libwebsockets/lws-jwk.h
index 2e2eabd73..d4c846844 100644
--- a/include/libwebsockets/lws-jwk.h
+++ b/include/libwebsockets/lws-jwk.h
@@ -25,35 +25,113 @@
/*! \defgroup jwk JSON Web Keys
* ## JSON Web Keys API
*
- * Lws provides an API to parse JSON Web Keys into a struct lws_genrsa_elements.
+ * Lws provides an API to parse JSON Web Keys into a struct lws_jwk_elements.
*
* "oct" and "RSA" type keys are supported. For "oct" keys, they are held in
- * the "e" member of the struct lws_genrsa_elements.
+ * the "e" member of the struct lws_jwk_elements.
*
* Keys elements are allocated on the heap. You must destroy the allocations
- * in the struct lws_genrsa_elements by calling
+ * in the struct lws_jwk_elements by calling
* lws_jwk_destroy_genrsa_elements() when you are finished with it.
*/
///@{
-struct lws_jwk {
- char keytype[5]; /**< "oct" or "RSA" */
- struct lws_genrsa_elements el; /**< OCTet key is in el.e */
+enum lws_jwk_kyt {
+ LWS_JWK_KYT_UNKNOWN,
+ LWS_JWK_KYT_OCT,
+ LWS_JWK_KYT_RSA,
+ LWS_JWK_KYT_EC
};
+/*
+ * Keytypes where the same element name is reused must all agree to put the
+ * same-named element at the same e[] index. It's because we may not know the
+ * keytype until the end.
+ */
+
+enum enum_jwk_oct_tok {
+ JWK_OCT_KEYEL_K,
+
+ LWS_COUNT_OCT_KEY_ELEMENTS
+};
+
+enum enum_jwk_rsa_tok {
+ JWK_RSA_KEYEL_E,
+ JWK_RSA_KEYEL_N,
+ JWK_RSA_KEYEL_D, /* note... same offset as EC D */
+ JWK_RSA_KEYEL_P,
+ JWK_RSA_KEYEL_Q,
+ JWK_RSA_KEYEL_DP,
+ JWK_RSA_KEYEL_DQ,
+ JWK_RSA_KEYEL_QI,
+
+ LWS_COUNT_RSA_KEY_ELEMENTS
+};
+
+enum enum_jwk_ec_tok {
+ JWK_EC_KEYEL_CRV,
+ JWK_EC_KEYEL_X,
+ JWK_EC_KEYEL_D, /* note... same offset as RSA D */
+ JWK_EC_KEYEL_Y,
+
+ LWS_COUNT_EC_KEY_ELEMENTS
+};
+
+enum enum_jwk_meta_tok {
+ JWK_META_KTY,
+ JWK_META_KID,
+ JWK_META_USE,
+ JWK_META_KEY_OPS,
+ JWK_META_X5C,
+ JWK_META_ALG,
+
+ LWS_COUNT_JWK_ELEMENTS
+};
+
+/* largest number of key elements for any algorithm */
+#define LWS_COUNT_ALG_KEY_ELEMENTS_MAX LWS_COUNT_RSA_KEY_ELEMENTS
+
+struct lws_jwk_elements {
+ uint8_t *buf;
+ uint16_t len;
+};
+
+struct lws_jwk {
+ /* key data elements */
+ struct lws_jwk_elements e[LWS_COUNT_ALG_KEY_ELEMENTS_MAX];
+ /* generic meta key elements, like KID */
+ struct lws_jwk_elements meta[LWS_COUNT_JWK_ELEMENTS];
+ int kty; /**< one of LWS_JWK_ */
+};
+
+typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
+
/** lws_jwk_import() - Create a JSON Web key from the textual representation
*
* \param s: the JWK object to create
+ * \param cb: callback for each jwk-processed key, or NULL if importing a single
+ * key with no parent "keys" JSON
+ * \param user: pointer to be passed to the callback, otherwise ignored by lws.
+ * NULL if importing a single key with no parent "keys" JSON
* \param in: a single JWK JSON stanza in utf-8
* \param len: the length of the JWK JSON stanza in bytes
*
* Creates an lws_jwk struct filled with data from the JSON representation.
- * "oct" and "rsa" key types are supported.
*
- * For "oct" type keys, it is loaded into el.e.
+ * There are two ways to use this... with some protocols a single jwk is
+ * delivered with no parent "keys": [] array. If you call this with cb and
+ * user as NULL, then the input will be interpreted like that and the results
+ * placed in s.
+ *
+ * The second case is that you are dealing with a "keys":[] array with one or
+ * more keys in it. In this case, the function iterates through the keys using
+ * s as a temporary jwk, and calls the user-provided callback for each key in
+ * turn while it return 0 (nonzero return from the callback terminates the
+ * iteration through any further keys).
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_import(struct lws_jwk *s, const char *in, size_t len);
+lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
+ const char *in, size_t len);
/** lws_jwk_destroy() - Destroy a JSON Web key
*
@@ -84,9 +162,21 @@ lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
* \param filename: filename to load from
*
* Returns 0 for OK or -1 for failure
+ *
+ * There are two ways to use this... with some protocols a single jwk is
+ * delivered with no parent "keys": [] array. If you call this with cb and
+ * user as NULL, then the input will be interpreted like that and the results
+ * placed in s.
+ *
+ * The second case is that you are dealing with a "keys":[] array with one or
+ * more keys in it. In this case, the function iterates through the keys using
+ * s as a temporary jwk, and calls the user-provided callback for each key in
+ * turn while it return 0 (nonzero return from the callback terminates the
+ * iteration through any further keys, leaving the last one in s).
*/
LWS_VISIBLE int
-lws_jwk_load(struct lws_jwk *s, const char *filename);
+lws_jwk_load(struct lws_jwk *s, const char *filename,
+ lws_jwk_key_import_callback cb, void *user);
/** lws_jwk_save() - Export a JSON Web key to a file
*
diff --git a/include/libwebsockets/lws-jws.h b/include/libwebsockets/lws-jws.h
index 7112fc3db..3485146d5 100644
--- a/include/libwebsockets/lws-jws.h
+++ b/include/libwebsockets/lws-jws.h
@@ -34,7 +34,8 @@
///@{
LWS_VISIBLE LWS_EXTERN int
-lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
+lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
+ struct lws_context *context);
/**
* lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
@@ -47,6 +48,7 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
* \param sig_len: max bytes we can write at b64_sig
* \param hash_type: one of LWS_GENHASH_TYPE_SHA[256|384|512]
* \param jwk: the struct lws_jwk containing the signing key
+ * \param context: the lws context (used to get random)
*
* This adds a b64-coded JWS signature of the b64-encoded protected header
* and b64-encoded payload, at \p b64_sig. The signature will be as large
@@ -62,7 +64,8 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
LWS_VISIBLE LWS_EXTERN int
lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
size_t pay_len, char *b64_sig, size_t sig_len,
- enum lws_genhash_types hash_type, struct lws_jwk *jwk);
+ enum lws_genhash_types hash_type, struct lws_jwk *jwk,
+ struct lws_context *context);
/**
* lws_jws_create_packet() - add b64 sig to b64 hdr + payload
@@ -73,6 +76,7 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
* \param nonce: Nonse string to include in protected header
* \param out: buffer to take signed packet
* \param out_len: size of \p out buffer
+ * \param conext: lws_context to get random from
*
* This creates a "flattened" JWS packet from the jwk and the plaintext
* payload, and signs it. The packet is written into \p out.
@@ -84,7 +88,8 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
- const char *nonce, char *out, size_t out_len);
+ const char *nonce, char *out, size_t out_len,
+ struct lws_context *context);
/**
* lws_jws_base64_enc() - encode input data into b64url data
@@ -98,4 +103,21 @@ lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
+
+/**
+ * lws_jws_encode_section() - encode input data into b64url data, prepending . if not first
+ *
+ * \param in: the incoming plaintext
+ * \param in_len: the length of the incoming plaintext in bytes
+ * \param first: nonzero if the first section
+ * \param out: the buffer to store the b64url encoded data to
+ * \param out_max: the length of \p out in bytes
+ *
+ * Returns either -1 if problems, or the number of bytes written to \p out.
+ * If the section is not the first one, '.' is prepended.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
+ char *end);
///@}
diff --git a/lib/README.md b/lib/README.md
index d6475b8da..2c0c900eb 100644
--- a/lib/README.md
+++ b/lib/README.md
@@ -6,6 +6,7 @@ Path|Sources
---|---
lib/core|Core lws code related to generic fd and wsi servicing and management
lib/event-libs|Code containing optional event-lib specific adaptations
+lib/jose|JOSE / JWS / JWK / JWE implementations
lib/misc|Code for various mostly optional miscellaneous features
lib/plat|Platform-specific adaptation code
lib/roles|Code for specific optional wsi roles, eg, http/1, h2, ws, raw, etc
diff --git a/lib/core/context.c b/lib/core/context.c
index 9749447a6..e36e942e8 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -1273,10 +1273,6 @@ lws_create_context(const struct lws_context_creation_info *info)
/* expedite post-context init (eg, protocols) */
lws_cancel_service(context);
-#if defined(LWS_WITH_SELFTESTS)
- lws_jws_selftest();
-#endif
-
return context;
bail:
diff --git a/lib/core/private.h b/lib/core/private.h
index 9a8dc8684..43aa14a5f 100644
--- a/lib/core/private.h
+++ b/lib/core/private.h
@@ -1337,7 +1337,7 @@ int
lws_tls_check_cert_lifetime(struct lws_vhost *vhost);
int lws_jws_selftest(void);
-
+int lws_jwe_selftest(void);
#ifndef LWS_NO_CLIENT
LWS_EXTERN int lws_client_socket_service(struct lws *wsi,
diff --git a/lib/jose/README.md b/lib/jose/README.md
new file mode 100644
index 000000000..cf7284f39
--- /dev/null
+++ b/lib/jose/README.md
@@ -0,0 +1,51 @@
+# 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
+
+Symmetric ciphers are not currently supported... symmetric keys and HMAC
+are supported though.
+
+For the required and recommended asymmetric algorithms, support currently
+looks like this
+
+|JWK kty|JWA|lws|
+|---|---|---|
+|EC|Recommended+|no|
+|RSA|Required|yes|
+|oct|Required|yes|
+
+|JWE alg|JWA|lws|
+|---|---|---|
+|RSA1_5|Recommended-|yes (no JWE yet but lws_genrsa supports)|
+|RSA-OAEP|Recommended+|no|
+|ECDH-ES|Recommended+|no|
+
+|JWS alg|JWA|lws|
+|---|---|---|
+|HS256|Required|yes|
+|RS256|Recommended+|yes|
+|ES256|Recommended|no|
+
+## API tests
+
+See `./minimal-examples/api-tests/api-test-jose/` for example test code.
+The tests are built and confirmed during CI.
+
diff --git a/lib/jose/jwe/jwe.c b/lib/jose/jwe/jwe.c
new file mode 100644
index 000000000..cc7c9c82d
--- /dev/null
+++ b/lib/jose/jwe/jwe.c
@@ -0,0 +1,27 @@
+/*
+ * libwebsockets - JSON Web Encryption support
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ *
+ * This supports RFC7516 JSON Web Encryption
+ *
+ *
+ */
+#include "core/private.h"
+
diff --git a/lib/jose/jwk/jwk.c b/lib/jose/jwk/jwk.c
new file mode 100644
index 000000000..a216697f7
--- /dev/null
+++ b/lib/jose/jwk/jwk.c
@@ -0,0 +1,497 @@
+/*
+ * libwebsockets - JSON Web Key support
+ *
+ * Copyright (C) 2017 - 2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+
+#include
+#include
+
+static const char * const kyt_names[] = {
+ "unknown", /* LWS_JWK_KYT_UNKNOWN */
+ "oct", /* LWS_JWK_KYT_OCT */
+ "RSA", /* LWS_JWK_KYT_RSA */
+ "EC" /* LWS_JWK_KYT_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.
+ */
+
+static const char * const jwk_tok[] = {
+ "keys[]", /* dummy */
+ "e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */
+ "kty", /* generic */
+ "k", /* symmetric oct 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"
+};
+
+/* information about each token declared above */
+
+#define FLAG_META (1 << 12)
+#define FLAG_RSA (1 << 13)
+#define FLAG_EC (1 << 14)
+#define FLAG_OCT (1 << 15)
+
+unsigned short tok_map[] = {
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | 0, /* padding */
+ FLAG_RSA | JWK_RSA_KEYEL_E,
+ FLAG_RSA | JWK_RSA_KEYEL_N,
+ FLAG_RSA | FLAG_EC | JWK_RSA_KEYEL_D,
+ FLAG_RSA | JWK_RSA_KEYEL_P,
+ FLAG_RSA | JWK_RSA_KEYEL_Q,
+ FLAG_RSA | JWK_RSA_KEYEL_DP,
+ FLAG_RSA | JWK_RSA_KEYEL_DQ,
+ FLAG_RSA | JWK_RSA_KEYEL_QI,
+
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KTY,
+ FLAG_OCT | JWK_OCT_KEYEL_K,
+
+ FLAG_EC | JWK_EC_KEYEL_CRV,
+ FLAG_EC | JWK_EC_KEYEL_X,
+ FLAG_EC | JWK_EC_KEYEL_Y,
+
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KID,
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_USE,
+
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KEY_OPS,
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_X5C,
+ FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_ALG,
+};
+
+struct cb_lws_jwk {
+ struct lws_jwk *s;
+ char *b64;
+ lws_jwk_key_import_callback per_key_cb;
+ void *user;
+ int b64max;
+ int pos;
+ unsigned short possible;
+};
+
+static int
+_lws_jwk_set_element_jwk(struct lws_jwk_elements *e, char *in, int len)
+{
+ e->buf = lws_malloc(len + 1, "jwk");
+ if (!e->buf)
+ return -1;
+
+ memcpy(e->buf, in, len);
+ e->buf[len] = '\0';
+ e->len = len;
+
+ return 0;
+}
+
+static int
+_lws_jwk_set_element_jwk_b64(struct lws_jwk_elements *e, char *in, int len)
+{
+ int dec_size = ((len * 3) / 4) + 4, n;
+
+ e->buf = lws_malloc(dec_size, "jwk");
+ if (!e->buf)
+ return -1;
+
+ n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
+ if (n < 0)
+ return -1;
+ e->len = n;
+
+ return 0;
+}
+
+void
+lws_jwk_destroy_elements(struct lws_jwk_elements *el, int m)
+{
+ int n;
+
+ for (n = 0; n < m; n++)
+ if (el[n].buf)
+ lws_free_set_NULL(el[n].buf);
+}
+
+LWS_VISIBLE void
+lws_jwk_destroy(struct lws_jwk *s)
+{
+ lws_jwk_destroy_elements(s->e, LWS_ARRAY_SIZE(s->e));
+ lws_jwk_destroy_elements(s->meta, LWS_ARRAY_SIZE(s->meta));
+}
+
+static signed char
+cb_jwk(struct lejp_ctx *ctx, char reason)
+{
+ struct cb_lws_jwk *cbs = (struct cb_lws_jwk *)ctx->user;
+ struct lws_jwk *s = cbs->s;
+ int idx, poss;
+
+ if (reason == LEJPCB_VAL_STR_START)
+ cbs->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.
+ */
+ cbs->possible = FLAG_RSA | FLAG_EC | FLAG_OCT;
+
+ if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) {
+ /* we completed parsing a key */
+ if (cbs->per_key_cb && cbs->possible) {
+ if (cbs->per_key_cb(cbs->s, cbs->user)) {
+
+ lwsl_notice("%s: user cb halts import\n", __func__);
+
+ return -2;
+ }
+
+ /* clear it down */
+ lws_jwk_destroy(cbs->s);
+ cbs->possible = 0;
+ }
+ }
+
+ 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];
+
+ 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's still possible (ie, it's
+ * not trying to tell us that it's RSA when we saw a "crv"
+ * already) and then reduce the possibilities to just the one that
+ * kty told. */
+ case FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KTY:
+
+ if (!strcmp(ctx->buf, "oct")) {
+ if (!(cbs->possible & FLAG_OCT))
+ goto elements_mismatch;
+ s->kty = LWS_JWK_KYT_OCT;
+ cbs->possible = FLAG_OCT;
+ break;
+ }
+ if (!strcmp(ctx->buf, "RSA")) {
+ if (!(cbs->possible & FLAG_RSA))
+ goto elements_mismatch;
+ s->kty = LWS_JWK_KYT_RSA;
+ cbs->possible = FLAG_RSA;
+ break;
+ }
+ if (!strcmp(ctx->buf, "EC")) {
+ if (!(cbs->possible & FLAG_EC))
+ goto elements_mismatch;
+ s->kty = LWS_JWK_KYT_EC;
+ cbs->possible = FLAG_EC;
+ break;
+ }
+ lwsl_err("%s: Unknown KTY '%s'\n", __func__, ctx->buf);
+ return -1;
+
+ default:
+
+ if (cbs->pos + ctx->npos >= cbs->b64max)
+ goto bail;
+
+ memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
+ cbs->pos += ctx->npos;
+
+ if (reason == LEJPCB_VAL_STR_CHUNK)
+ return 0;
+
+ /* chunking has been collated */
+
+ poss = idx & (FLAG_RSA | FLAG_EC | FLAG_OCT);
+ cbs->possible &= poss;
+ if (!cbs->possible)
+ goto elements_mismatch;
+
+ if (idx & FLAG_META) {
+ if (_lws_jwk_set_element_jwk(&s->meta[idx & 0x7f],
+ cbs->b64, cbs->pos) < 0)
+ goto bail;
+
+ break;
+ }
+
+ /* key data... do the base64 decode then */
+
+ if (_lws_jwk_set_element_jwk_b64(&s->e[idx & 0x7f],
+ cbs->b64, cbs->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;
+}
+
+LWS_VISIBLE int
+lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
+ const char *in, size_t len)
+{
+ struct lejp_ctx jctx;
+ struct cb_lws_jwk cbs;
+ const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
+ const char * const *tok = jwk_outer_tok;
+ char b64[b64max];
+ int m;
+
+ memset(s, 0, sizeof(*s));
+ cbs.s = s;
+ cbs.b64 = b64;
+ cbs.b64max = b64max;
+ cbs.pos = 0;
+ cbs.per_key_cb = cb;
+ cbs.user = user;
+ cbs.possible = FLAG_RSA | FLAG_EC | FLAG_OCT;
+
+ if (cb == NULL)
+ tok = jwk_tok;
+
+ lejp_construct(&jctx, cb_jwk, &cbs, tok, LWS_ARRAY_SIZE(jwk_tok));
+ m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
+ lejp_destruct(&jctx);
+
+ if (m < 0) {
+ lwsl_notice("%s: parse got %d\n", __func__, m);
+
+ return -1;
+ }
+
+ if (s->kty == LWS_JWK_KYT_UNKNOWN) {
+ lwsl_notice("%s: missing or unknown kyt\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
+{
+ char *start = p, *end = &p[len - 1];
+ int n, limit = LWS_COUNT_JWK_ELEMENTS;
+
+ /* RFC7638 lexicographic order requires
+ * RSA: e -> kty -> n
+ * oct: k -> kty
+ */
+
+ p += lws_snprintf(p, end - p, "{");
+
+ switch (s->kty) {
+
+ case LWS_JWK_KYT_OCT:
+ if (!s->e[JWK_OCT_KEYEL_K].buf)
+ return -1;
+
+ p += lws_snprintf(p, end - p, "\"k\":\"");
+ n = lws_jws_base64_enc((const char *)s->e[JWK_OCT_KEYEL_K].buf,
+ s->e[JWK_OCT_KEYEL_K].len, p, end - p - 4);
+ if (n < 0) {
+ lwsl_notice("%s: enc failed\n", __func__);
+ return -1;
+ }
+ p += n;
+
+ p += lws_snprintf(p, end - p, "\",\"kty\":\"%s\"}",
+ kyt_names[s->kty]);
+
+ return p - start;
+
+ case LWS_JWK_KYT_RSA:
+ if (!s->e[JWK_RSA_KEYEL_E].buf ||
+ !s->e[JWK_RSA_KEYEL_N].buf ||
+ (private && (!s->e[JWK_RSA_KEYEL_D].buf ||
+ !s->e[JWK_RSA_KEYEL_P].buf ||
+ !s->e[JWK_RSA_KEYEL_Q].buf))
+ ) {
+ lwsl_notice("%s: not enough elements filled\n",
+ __func__);
+ return -1;
+ }
+
+ if (!private)
+ limit = JWK_RSA_KEYEL_N + 1;
+
+ for (n = 0; n < limit; n++) {
+ int m;
+
+ if (!s->e[n].buf)
+ continue;
+ lwsl_info("%d: len %d\n", n, s->e[n].len);
+
+ if (n)
+ p += lws_snprintf(p, end - p, ",");
+ p += lws_snprintf(p, end - p, "\"%s\":\"", jwk_tok[n]);
+ m = lws_jws_base64_enc((const char *)s->e[n].buf,
+ s->e[n].len, p,
+ end - p - 4);
+ if (m < 0) {
+ lwsl_notice("%s: enc fail inlen %d outlen %d\n",
+ __func__, (int)s->e[n].len,
+ lws_ptr_diff(end, p) - 4);
+ return -1;
+ }
+ p += m;
+ *p++ = '\"';
+
+ if (!n) /* RFC7638 lexicographic order */
+ p += lws_snprintf(p, end - p, ",\"kty\":\"%s\"",
+ kyt_names[s->kty]);
+ }
+
+ p += lws_snprintf(p, end - p, "}");
+
+ return p - start;
+
+ case LWS_JWK_KYT_EC:
+ return p - start;
+
+ default:
+ break;
+ }
+
+ lwsl_err("%s: unknown key type %d\n", __func__, s->kty);
+
+ return -1;
+}
+
+LWS_VISIBLE int
+lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
+{
+ struct lws_genhash_ctx hash_ctx;
+ int tmpsize = 2536, n;
+ char *tmp;
+
+ tmp = lws_malloc(tmpsize, "rfc7638 tmp");
+
+ n = lws_jwk_export(s, 0, tmp, tmpsize);
+ if (n < 0)
+ goto bail;
+
+ if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
+ goto bail;
+
+ if (lws_genhash_update(&hash_ctx, tmp, 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;
+}
+
+LWS_VISIBLE int
+lws_jwk_load(struct lws_jwk *s, const char *filename,
+ lws_jwk_key_import_callback cb, void *user)
+{
+ 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(s, cb, user, buf, n);
+ lws_free(buf);
+
+ return n;
+bail:
+ lws_free(buf);
+
+ return -1;
+}
+
+LWS_VISIBLE int
+lws_jwk_save(struct lws_jwk *s, const char *filename)
+{
+ int buflen = 4096;
+ char *buf = lws_malloc(buflen, "jwk-save");
+ int n, m;
+
+ if (!buf)
+ return -1;
+
+ n = lws_jwk_export(s, 1, buf, buflen);
+ if (n < 0)
+ goto bail;
+
+ m = lws_plat_write_file(filename, buf, n);
+
+ lws_free(buf);
+ if (m)
+ return -1;
+
+ return 0;
+
+bail:
+ lws_free(buf);
+
+ return -1;
+}
diff --git a/lib/jose/jws/jose.c b/lib/jose/jws/jose.c
new file mode 100644
index 000000000..6abb96553
--- /dev/null
+++ b/lib/jose/jws/jose.c
@@ -0,0 +1,254 @@
+/*
+ * libwebsockets - JSON Web Signature support
+ *
+ * Copyright (C) 2017 - 2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * 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 "core/private.h"
+
+#include
+
+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 */
+
+ "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 */
+};
+
+static signed char
+lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
+{
+ struct cb_hdr_s *s = (struct cb_hdr_s *)ctx->user;
+
+ if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
+ return 0;
+
+ switch (ctx->path_match - 1) {
+
+ /* strings */
+
+ case LJJHI_ALG: /* REQUIRED */
+
+ lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
+
+ if (s->is_jwe) {
+
+ lwsl_err("%s: JWE alg\n", __func__);
+
+ /* interpret as for JWE... just store the string */
+
+ return 0;
+ }
+
+ /* interpret as for JWS */
+
+ if (!strcmp(ctx->buf, "HS256")) {
+ s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
+ s->algtype = LWS_JWK_ENCTYPE_NONE;
+ break;
+ }
+ if (!strcmp(ctx->buf, "HS384")) {
+ s->hmac_type = LWS_GENHMAC_TYPE_SHA384;
+ s->algtype = LWS_JWK_ENCTYPE_NONE;
+ break;
+ }
+ if (!strcmp(ctx->buf, "HS512")) {
+ s->hmac_type = LWS_GENHMAC_TYPE_SHA512;
+ s->algtype = LWS_JWK_ENCTYPE_NONE;
+ break;
+ }
+
+ if (!strcmp(ctx->buf, "RS256")) {
+ s->hash_type = LWS_GENHASH_TYPE_SHA256;
+ s->algtype = LWS_JWK_ENCTYPE_RSASSA;
+ break;
+ }
+ if (!strcmp(ctx->buf, "RS384")) {
+ s->hash_type = LWS_GENHASH_TYPE_SHA384;
+ s->algtype = LWS_JWK_ENCTYPE_RSASSA;
+ break;
+ }
+ if (!strcmp(ctx->buf, "RS512")) {
+ s->hash_type = LWS_GENHASH_TYPE_SHA512;
+ s->algtype = LWS_JWK_ENCTYPE_RSASSA;
+ break;
+ }
+
+ if (!strcmp(ctx->buf, "ES256")) {
+ s->hash_type = LWS_GENHASH_TYPE_SHA256;
+ s->algtype = LWS_JWK_ENCTYPE_EC;
+ strncpy(s->curve, "P-256", sizeof(s->curve));
+ break;
+ }
+ if (!strcmp(ctx->buf, "ES384")) {
+ s->hash_type = LWS_GENHASH_TYPE_SHA384;
+ s->algtype = LWS_JWK_ENCTYPE_EC;
+ strncpy(s->curve, "P-384", sizeof(s->curve));
+ break;
+ }
+ if (!strcmp(ctx->buf, "ES512")) {
+ s->hash_type = LWS_GENHASH_TYPE_SHA512;
+ s->algtype = LWS_JWK_ENCTYPE_EC;
+ strncpy(s->curve, "P-521", sizeof(s->curve));
+ break;
+ }
+
+ return -1;
+
+ case LJJHI_TYP: /* Optional: string: media type */
+ if (strcmp(ctx->buf, "JWT"))
+ return -1;
+ 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_ENC: /* JWE only: Optional: string */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_EPK: /* Additional arg for JWE ECDH */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_APU: /* Additional arg for JWE ECDH */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_APV: /* Additional arg for JWE ECDH */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_IV: /* Additional arg for JWE AES */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_TAG: /* Additional arg for JWE AES */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ case LJJHI_P2S: /* Additional arg for JWE PBES2 */
+ if (!s->is_jwe)
+ return -1;
+ break;
+ case LJJHI_P2C: /* Additional arg for JWE PBES2 */
+ if (!s->is_jwe)
+ return -1;
+ break;
+
+ /* ignore what we don't understand */
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+lws_jose_parse(struct cb_hdr_s *args, uint8_t *buf, int n, int is_jwe)
+{
+ struct lejp_ctx jctx;
+ int m;
+
+ args->alg[0] = '\0';
+ args->curve[0] = '\0';
+ args->algtype = -1;
+ args->is_jwe = is_jwe;
+
+ lejp_construct(&jctx, lws_jws_jose_cb, args, jws_jose,
+ LWS_ARRAY_SIZE(jws_jose));
+
+ m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
+ lejp_destruct(&jctx);
+ if (m < 0) {
+ lwsl_notice("parse got %d: alg %s\n", m, args->alg);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+lws_jws_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n)
+{
+ return lws_jose_parse(args, buf, n, 0);
+}
+
+int
+lws_jwe_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n)
+{
+ return lws_jose_parse(args, buf, n, 1);
+}
diff --git a/lib/jose/jws/jws.c b/lib/jose/jws/jws.c
new file mode 100644
index 000000000..206551201
--- /dev/null
+++ b/lib/jose/jws/jws.c
@@ -0,0 +1,367 @@
+/*
+ * libwebsockets - JSON Web Signature support
+ *
+ * Copyright (C) 2017 - 2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "core/private.h"
+#include "private.h"
+
+LWS_VISIBLE int
+lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
+{
+ int n;
+
+ n = lws_b64_encode_string_url(in, in_len, out, out_max - 1);
+ if (n < 0)
+ return n; /* too large for output buffer */
+
+ /* trim the terminal = */
+ while (n && out[n - 1] == '=')
+ n--;
+
+ out[n] = '\0';
+
+ return n;
+}
+
+LWS_VISIBLE int
+lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
+ char *end)
+{
+ int n, len = (end - *p) - 1;
+ char *p_entry = *p;
+
+ if (len < 3)
+ return -1;
+
+ if (!first)
+ *(*p)++ = '.';
+
+ n = lws_jws_base64_enc(in, in_len, *p, len - 1);
+ if (n < 0)
+ return -1;
+
+ *p += n;
+
+ return (*p) - p_entry;
+}
+
+static int
+lws_jws_find_sig(const char *in, size_t len)
+{
+ const char *p = in + len - 1;
+
+ while (len--)
+ if (*p == '.')
+ return (p + 1) - in;
+ else
+ p--;
+
+ lwsl_notice("%s failed\n", __func__);
+ return -1;
+}
+
+LWS_VISIBLE int
+lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
+ struct lws_context *context)
+{
+ int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_genhash_ctx hash_ctx;
+ struct lws_genrsa_ctx rsactx;
+ struct lws_genhmac_ctx ctx;
+ struct cb_hdr_s args;
+ char buf[2048];
+
+ /* 1) there has to be a signature */
+
+ if (sig_pos < 0)
+ return -1;
+
+ /* 2) find length of first, hdr, block */
+
+ while (pos < (int)len && in[pos] != '.')
+ pos++;
+ if (pos == (int)len)
+ return -1;
+
+ /* 3) Decode the header block */
+
+ n = lws_b64_decode_string_len(in, pos, buf, sizeof(buf) - 1);
+ if (n < 0)
+ return -1;
+
+ /* 4) Require either:
+ * typ: JWT (if present) and alg: HS256/384/512
+ * typ: JWT (if present) and alg: RS256/384/512
+ * typ: JWT (if present) and alg: ES256/384/512
+ */
+
+ m = lws_jws_parse_jose(&args, (unsigned char *)buf, n);
+ if (m < 0) {
+ lwsl_notice("parse got %d: alg %s\n", m, args.alg);
+ return -1;
+ }
+
+ /* 5) decode the B64URL signature part into buf / m */
+
+ m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
+ buf, sizeof(buf) - 1);
+
+ switch (args.algtype) {
+ case LWS_JWK_ENCTYPE_RSASSA:
+
+ /* RSASSA-PKCS1-v1_5 using SHA-256/384/512 */
+
+ /* 6(RSA): compute the hash of the payload into "digest" */
+
+ if (lws_genhash_init(&hash_ctx, args.hash_type))
+ return -1;
+
+ if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
+ lws_genhash_destroy(&hash_ctx, NULL);
+
+ return -1;
+ }
+ if (lws_genhash_destroy(&hash_ctx, digest))
+ return -1;
+
+ h_len = lws_genhash_size(args.hash_type);
+
+ if (lws_genrsa_create(&rsactx, jwk->e, context)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ return -1;
+ }
+
+ n = lws_genrsa_public_verify(&rsactx, digest, args.hash_type,
+ (uint8_t *)buf, m);
+
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lwsl_notice("decrypt fail\n");
+ return -1;
+ }
+
+ break;
+
+ case LWS_JWK_ENCTYPE_NONE:
+
+ /* SHA256/384/512 HMAC */
+
+ h_len = lws_genhmac_size(args.hmac_type);
+ if (m < 0 || m != h_len)
+ return -1;
+
+ /* 6) compute HMAC over payload */
+
+ if (lws_genhmac_init(&ctx, args.hmac_type,
+ jwk->e[JWK_RSA_KEYEL_E].buf,
+ jwk->e[JWK_RSA_KEYEL_E].len))
+ return -1;
+
+ if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
+ lws_genhmac_destroy(&ctx, NULL);
+
+ return -1;
+ }
+ if (lws_genhmac_destroy(&ctx, digest))
+ return -1;
+
+ /* 7) Compare the computed and decoded hashes */
+
+ if (memcmp(digest, buf, h_len)) {
+ lwsl_notice("digest mismatch\n");
+
+ return -1;
+ }
+
+ break;
+
+ case LWS_JWK_ENCTYPE_EC:
+
+ lwsl_err("%s: EC not supported yet\n", __func__);
+ return -1;
+
+ default:
+ lwsl_err("%s: unknown alg from jose\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
+ size_t pay_len, char *b64_sig, size_t sig_len,
+ enum lws_genhash_types hash_type, struct lws_jwk *jwk,
+ struct lws_context *context)
+{
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_genhash_ctx hash_ctx;
+ struct lws_genrsa_ctx rsactx;
+ uint8_t *buf;
+ int n;
+
+ if (lws_genhash_init(&hash_ctx, hash_type))
+ return -1;
+
+ if (b64_hdr) {
+ if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_hdr, hdr_len))
+ goto hash_fail;
+ if (lws_genhash_update(&hash_ctx, (uint8_t *)".", 1))
+ goto hash_fail;
+ }
+ if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_pay, pay_len))
+ goto hash_fail;
+
+ if (lws_genhash_destroy(&hash_ctx, digest))
+ return -1;
+
+ if (jwk->kty == LWS_JWK_KYT_RSA) {
+ if (lws_genrsa_create(&rsactx, jwk->e, context)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ return -1;
+ }
+
+ n = jwk->e[JWK_RSA_KEYEL_N].len;
+ buf = lws_malloc(n, "jws sign");
+ if (!buf)
+ return -1;
+
+ n = lws_genrsa_public_sign(&rsactx, digest, hash_type, buf, n);
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lws_free(buf);
+
+ return -1;
+ }
+
+ n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
+ lws_free(buf);
+
+ return n;
+ }
+
+ if (jwk->kty == LWS_JWK_KYT_OCT)
+ return lws_jws_base64_enc((char *)digest,
+ lws_genhash_size(hash_type),
+ b64_sig, sig_len);
+
+ /* unknown key type */
+
+ return -1;
+
+hash_fail:
+ lws_genhash_destroy(&hash_ctx, NULL);
+ return -1;
+}
+
+LWS_VISIBLE int
+lws_jws_create_packet(struct lws_jwk *jwk, 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, *b64_hdr, *b64_pay;
+ int n, b64_hdr_len, b64_pay_len;
+
+ /*
+ * This buffer is local to the function, the actual output
+ * is prepared into vhd->buf. Only the plaintext protected header
+ * (which contains the public key, 512 bytes for 4096b) goes in
+ * here temporarily.
+ */
+ n = LWS_PRE + 2048;
+ buf = malloc(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
+ */
+
+ p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
+ n = lws_jwk_export(jwk, 0, p, end - p);
+ if (n < 0) {
+ lwsl_notice("failed to export jwk\n");
+
+ goto bail;
+ }
+ p += n;
+ p += lws_snprintf(p, 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, end1 - p1, "{\"protected\":\"");
+ b64_hdr = p1;
+ n = lws_jws_base64_enc(start, p - start, p1, end1 - p1);
+ if (n < 0) {
+ lwsl_notice("%s: failed to encode protected\n", __func__);
+ goto bail;
+ }
+ b64_hdr_len = n;
+ p1 += n;
+
+ p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
+ b64_pay = p1;
+ n = lws_jws_base64_enc(payload, len, p1, end1 - p1);
+ if (n < 0) {
+ lwsl_notice("%s: failed to encode payload\n", __func__);
+ goto bail;
+ }
+ b64_pay_len = n;
+
+ p1 += n;
+ p1 += lws_snprintf(p1, 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(b64_hdr, b64_hdr_len, b64_pay, b64_pay_len,
+ p1, end1 - p1, LWS_GENHASH_TYPE_SHA256, jwk,
+ context);
+ if (n < 0) {
+ lwsl_notice("sig gen failed\n");
+
+ goto bail;
+ }
+ p1 += n;
+ p1 += lws_snprintf(p1, end1 - p1, "\"}");
+
+ free(buf);
+
+ return p1 - out;
+
+bail:
+ free(buf);
+
+ return -1;
+}
diff --git a/lib/jose/jws/private.h b/lib/jose/jws/private.h
new file mode 100644
index 000000000..b5d39f219
--- /dev/null
+++ b/lib/jose/jws/private.h
@@ -0,0 +1,29 @@
+/*
+ * libwebsockets - JSON Web Signature support
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * 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.
+ */
+
+int
+lws_jws_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n);
+
+int
+lws_jwe_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n);
diff --git a/lib/jose/private.h b/lib/jose/private.h
new file mode 100644
index 000000000..d39f71299
--- /dev/null
+++ b/lib/jose/private.h
@@ -0,0 +1,23 @@
+/*
+ * libwebsockets - jose private header
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+void
+lws_jwk_destroy_elements(struct lws_jwk_elements *el, int m);
diff --git a/lib/misc/jws/jwk.c b/lib/misc/jws/jwk.c
deleted file mode 100644
index fca8346eb..000000000
--- a/lib/misc/jws/jwk.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * libwebsockets - JSON Web Key support
- *
- * Copyright (C) 2017 Andy Green
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "core/private.h"
-
-#include
-#include
-
-static const char * const jwk_tok[] = {
- "e", "n", "d", "p", "q", "dp", "dq", "qi", "kty", "k",
-};
-
-static int
-_lws_jwk_set_element(struct lws_genrsa_element *e, char *in, int len)
-{
- int dec_size = ((len * 3) / 4) + 4, n;
-
- e->buf = lws_malloc(dec_size, "jwk");
- if (!e->buf)
- return -1;
-
- n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
- if (n < 0)
- return -1;
- e->len = n;
-
- return 0;
-}
-
-struct cb_lws_jwk {
- struct lws_jwk *s;
- char *b64;
- int b64max;
- int pos;
-};
-
-static signed char
-cb_jwk(struct lejp_ctx *ctx, char reason)
-{
- struct cb_lws_jwk *cbs = (struct cb_lws_jwk *)ctx->user;
- struct lws_jwk *s = cbs->s;
- int idx;
-
- if (reason == LEJPCB_VAL_STR_START)
- cbs->pos = 0;
-
- if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
- return 0;
-
- switch (ctx->path_match - 1) {
- case JWK_KTY:
- lws_strncpy(s->keytype, ctx->buf, sizeof(s->keytype));
- if (!strcmp(ctx->buf, "oct")) {
- break;
- }
- if (!strcmp(ctx->buf, "RSA")) {
- break;
- }
- return -1;
-
- case JWK_KEY:
-// if (strcmp(s->keytype, "oct"))
-// return -1;
- idx = JWK_KEY_E;
- goto read_element1;
-
- case JWK_KEY_N:
- case JWK_KEY_E:
- case JWK_KEY_D:
- case JWK_KEY_P:
- case JWK_KEY_Q:
- case JWK_KEY_DP:
- case JWK_KEY_DQ:
- case JWK_KEY_QI:
- idx = ctx->path_match - 1;
- goto read_element;
- }
-
- return 0;
-
-read_element:
-/* kty is no longer first in lex order */
-// if (strcmp(s->keytype, "RSA"))
-// return -1;
-
-read_element1:
-
- if (cbs->pos + ctx->npos >= cbs->b64max)
- return -1;
-
- memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
- cbs->pos += ctx->npos;
-
- if (reason == LEJPCB_VAL_STR_CHUNK)
- return 0;
-
- if (_lws_jwk_set_element(&s->el.e[idx], cbs->b64, cbs->pos) < 0) {
- lws_jwk_destroy_genrsa_elements(&s->el);
-
- return -1;
- }
-
- return 0;
-}
-
-LWS_VISIBLE int
-lws_jwk_import(struct lws_jwk *s, const char *in, size_t len)
-{
- struct lejp_ctx jctx;
- struct cb_lws_jwk cbs;
- const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
- char b64[b64max];
- int m;
-
- memset(s, 0, sizeof(*s));
- cbs.s = s;
- cbs.b64 = b64;
- cbs.b64max = b64max;
- cbs.pos = 0;
- lejp_construct(&jctx, cb_jwk, &cbs, jwk_tok, LWS_ARRAY_SIZE(jwk_tok));
- m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
- lejp_destruct(&jctx);
-
- if (m < 0) {
- lwsl_notice("%s: parse got %d\n", __func__, m);
-
- return -1;
- }
-
- return 0;
-}
-
-LWS_VISIBLE void
-lws_jwk_destroy(struct lws_jwk *s)
-{
- lws_jwk_destroy_genrsa_elements(&s->el);
-}
-
-LWS_VISIBLE int
-lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
-{
- char *start = p, *end = &p[len - 1];
- int n, limit = LWS_COUNT_RSA_ELEMENTS;
-
- /* RFC7638 lexicographic order requires
- * RSA: e -> kty -> n
- * oct: k -> kty
- */
-
- p += lws_snprintf(p, end - p, "{");
-
- if (!strcmp(s->keytype, "oct")) {
- if (!s->el.e[JWK_KEY_E].buf)
- return -1;
-
- p += lws_snprintf(p, end - p, "\"k\":\"");
- n = lws_jws_base64_enc((const char *)s->el.e[JWK_KEY_E].buf,
- s->el.e[JWK_KEY_E].len, p,
- end - p - 4);
- if (n < 0) {
- lwsl_notice("%s: enc failed\n", __func__);
- return -1;
- }
- p += n;
-
- p += lws_snprintf(p, end - p, "\",\"kty\":\"%s\"}", s->keytype);
-
- return p - start;
- }
-
- if (!strcmp(s->keytype, "RSA")) {
- if (!s->el.e[JWK_KEY_E].buf ||
- !s->el.e[JWK_KEY_N].buf ||
- (private && (!s->el.e[JWK_KEY_D].buf ||
- !s->el.e[JWK_KEY_P].buf ||
- !s->el.e[JWK_KEY_Q].buf))
- ) {
- lwsl_notice("%s: not enough elements filled\n",
- __func__);
- return -1;
- }
-
- if (!private)
- limit = JWK_KEY_N + 1;
-
- for (n = 0; n < limit; n++) {
- int m;
-
- if (!s->el.e[n].buf)
- continue;
- lwsl_info("%d: len %d\n", n, s->el.e[n].len);
-
- if (n)
- p += lws_snprintf(p, end - p, ",");
- p += lws_snprintf(p, end - p, "\"%s\":\"", jwk_tok[n]);
- m = lws_jws_base64_enc((const char *)s->el.e[n].buf,
- s->el.e[n].len, p,
- end - p - 4);
- if (m < 0) {
- lwsl_notice("%s: enc fail inlen %d outlen %d\n",
- __func__, (int)s->el.e[n].len,
- lws_ptr_diff(end, p) - 4);
- return -1;
- }
- p += m;
- *p++ = '\"';
-
- if (!n) /* RFC7638 lexicographic order */
- p += lws_snprintf(p, end - p, ",\"kty\":\"%s\"",
- s->keytype);
- }
-
- p += lws_snprintf(p, end - p, "}");
-
- return p - start;
- }
-
- lwsl_err("%s: unknown key type %s\n", __func__, s->keytype);
-
- return -1;
-}
-
-LWS_VISIBLE int
-lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
-{
- struct lws_genhash_ctx hash_ctx;
- int tmpsize = 2536, n;
- char *tmp;
-
- tmp = lws_malloc(tmpsize, "rfc7638 tmp");
-
- n = lws_jwk_export(s, 0, tmp, tmpsize);
- if (n < 0)
- goto bail;
-
- if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
- goto bail;
-
- if (lws_genhash_update(&hash_ctx, tmp, 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;
-}
-
-LWS_VISIBLE int
-lws_jwk_load(struct lws_jwk *s, const char *filename)
-{
- 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(s, buf, n);
- lws_free(buf);
-
- return n;
-bail:
- lws_free(buf);
-
- return -1;
-}
-
-LWS_VISIBLE int
-lws_jwk_save(struct lws_jwk *s, const char *filename)
-{
- int buflen = 4096;
- char *buf = lws_malloc(buflen, "jwk-save");
- int n, m;
-
- if (!buf)
- return -1;
-
- n = lws_jwk_export(s, 1, buf, buflen);
- if (n < 0)
- goto bail;
-
- m = lws_plat_write_file(filename, buf, n);
-
- lws_free(buf);
- if (m)
- return -1;
-
- return 0;
-
-bail:
- lws_free(buf);
-
- return -1;
-}
diff --git a/lib/misc/jws/jws.c b/lib/misc/jws/jws.c
deleted file mode 100644
index ae68dc860..000000000
--- a/lib/misc/jws/jws.c
+++ /dev/null
@@ -1,642 +0,0 @@
-/*
- * libwebsockets - JSON Web Signature support
- *
- * Copyright (C) 2017 Andy Green
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include "core/private.h"
-
-/*
- * JSON Web Signature is defined in RFC7515
- *
- * https://tools.ietf.org/html/rfc7515
- *
- * It's basically a way to wrap some JSON with a JSON "header" describing the
- * crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
- *
- * The signature stays with the content, it serves a different purpose than eg
- * a TLS tunnel to transfer it.
- *
- * RFC7518 (JSON Web Algorithms) says for the "alg" names
- *
- * | HS256 | HMAC using SHA-256 | Required |
- * | HS384 | HMAC using SHA-384 | Optional |
- * | HS512 | HMAC using SHA-512 | Optional |
- * | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
- * | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
- * | | SHA-384 | |
- * | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
- * | | SHA-512 | |
- * | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
- * | ES384 | ECDSA using P-384 and SHA-384 | Optional |
- * | ES512 | ECDSA using P-521 and SHA-512 | Optional |
- *
- * Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
- * currently. The "Recommended+" just means it is recommended but will likely
- * be "very recommended" soon.
- *
- * We support HS256/384/512 for symmetric crypto, but the choice for the
- * asymmetric crypto isn't as easy to make.
- *
- * Normally you'd choose the EC option but these are defined to use the
- * "NIST curves" (RFC7518 3.4) which are believed to be insecure.
- *
- * https://safecurves.cr.yp.to/
- *
- * For that reason we implement RS256/384/512 for asymmetric.
- */
-
-#if defined(LWS_WITH_SELFTESTS)
-static const char
- *test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
- *test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
- *test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
- " \"http://example.com/is_root\":true}",
- *test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
- "ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
- *key_jwk = "{\"kty\":\"oct\",\r\n"
- " \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
- "Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
- *hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
- /* the key from worked example in RFC7515 A-1, as a JWK */
- *rfc7515_rsa_key =
- "{\"kty\":\"RSA\","
- " \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
- "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
- "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
- "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
- "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
- "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
- "\"e\":\"AQAB\","
- "\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
- "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
- "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
- "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
- "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
- "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
- "\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
- "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
- "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
- "\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
- "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
- "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
- "\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
- "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
- "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
- "\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
- "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
- "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
- "\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
- "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
- "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
- "}",
- *rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
- "eyJhbGciOiJSUzI1NiJ9"
- ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
- "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
- ".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
- "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
- "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
- "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
- "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
- "p0igcN_IoypGlUPQGe77Rw";
-#endif
-
-LWS_VISIBLE int
-lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
-{
- int n;
-
- n = lws_b64_encode_string_url(in, in_len, out, out_max - 1);
- if (n < 0)
- return n; /* too large for output buffer */
-
- /* trim the terminal = */
- while (n && out[n - 1] == '=')
- n--;
-
- out[n] = '\0';
-
- return n;
-}
-
-LWS_VISIBLE int
-lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
- char *end)
-{
- int n, len = (end - *p) - 1;
- char *p_entry = *p;
-
- if (len < 3)
- return -1;
-
- if (!first)
- *(*p)++ = '.';
-
- n = lws_jws_base64_enc(in, in_len, *p, len - 1);
- if (n < 0)
- return -1;
-
- *p += n;
-
- return (*p) - p_entry;
-}
-
-static int
-lws_jws_find_sig(const char *in, size_t len)
-{
- const char *p = in + len - 1;
-
- while (len--)
- if (*p == '.')
- return (p + 1) - in;
- else
- p--;
-
- lwsl_notice("%s failed\n", __func__);
- return -1;
-}
-
-
-static const char * const jhdr_tok[] = {
- "typ",
- "alg",
-};
-enum enum_jhdr_tok {
- JHP_TYP,
- JHP_ALG
-};
-struct cb_hdr_s {
- enum lws_genhash_types hash_type;
- enum lws_genhmac_types hmac_type;
- char alg[10];
- int is_rsa:1;
-};
-
-static signed char
-cb_hdr(struct lejp_ctx *ctx, char reason)
-{
- struct cb_hdr_s *s = (struct cb_hdr_s *)ctx->user;
-
- if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
- return 0;
-
- switch (ctx->path_match - 1) {
- case JHP_TYP: /* it is optional */
- if (strcmp(ctx->buf, "JWT"))
- return -1;
- break;
- case JHP_ALG:
- lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
- if (!strcmp(ctx->buf, "HS256")) {
- s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
- break;
- }
- if (!strcmp(ctx->buf, "HS384")) {
- s->hmac_type = LWS_GENHMAC_TYPE_SHA384;
- break;
- }
- if (!strcmp(ctx->buf, "HS512")) {
- s->hmac_type = LWS_GENHMAC_TYPE_SHA512;
- break;
- }
- if (!strcmp(ctx->buf, "RS256")) {
- s->hash_type = LWS_GENHASH_TYPE_SHA256;
- s->is_rsa = 1;
- break;
- }
- if (!strcmp(ctx->buf, "RS384")) {
- s->hash_type = LWS_GENHASH_TYPE_SHA384;
- s->is_rsa = 1;
- break;
- }
- if (!strcmp(ctx->buf, "RS512")) {
- s->hash_type = LWS_GENHASH_TYPE_SHA512;
- s->is_rsa = 1;
- break;
- }
- return -1;
- }
-
- return 0;
-}
-
-LWS_VISIBLE int
-lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk)
-{
- int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
- uint8_t digest[LWS_GENHASH_LARGEST];
- struct lws_genhash_ctx hash_ctx;
- struct lws_genrsa_ctx rsactx;
- struct lws_genhmac_ctx ctx;
- struct cb_hdr_s args;
- struct lejp_ctx jctx;
- char buf[2048];
-
- /* 1) there has to be a signature */
-
- if (sig_pos < 0)
- return -1;
-
- /* 2) find length of first, hdr, block */
-
- while (pos < (int)len && in[pos] != '.')
- pos++;
- if (pos == (int)len)
- return -1;
-
- /* 3) Decode the header block */
-
- n = lws_b64_decode_string_len(in, pos, buf, sizeof(buf) - 1);
- if (n < 0)
- return -1;
-
- /* 4) Require either:
- * typ: JWT (if present) and alg: HS256/384/512
- * typ: JWT (if present) and alg: RS256/384/512
- */
-
- args.alg[0] = '\0';
- args.is_rsa = 0;
- lejp_construct(&jctx, cb_hdr, &args, jhdr_tok, LWS_ARRAY_SIZE(jhdr_tok));
- m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
- lejp_destruct(&jctx);
- if (m < 0) {
- lwsl_notice("parse got %d: alg %s\n", m, args.alg);
- return -1;
- }
-
- /* 5) decode the B64URL signature part into buf / m */
-
- m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
- buf, sizeof(buf) - 1);
-
- if (args.is_rsa) {
-
- /* RSASSA-PKCS1-v1_5 using SHA-256/384/512 */
-
- /* 6(RSA): compute the hash of the payload into "digest" */
-
- if (lws_genhash_init(&hash_ctx, args.hash_type))
- return -1;
-
- if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
- lws_genhash_destroy(&hash_ctx, NULL);
-
- return -1;
- }
- if (lws_genhash_destroy(&hash_ctx, digest))
- return -1;
-
- h_len = lws_genhash_size(args.hash_type);
-
- if (lws_genrsa_create(&rsactx, &jwk->el)) {
- lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
- __func__);
- return -1;
- }
-
- n = lws_genrsa_public_verify(&rsactx, digest, args.hash_type,
- (uint8_t *)buf, m);
-
- lws_genrsa_destroy(&rsactx);
- if (n < 0) {
- lwsl_notice("decrypt fail\n");
- return -1;
- }
-
- return 0;
- }
-
- /* SHA256/384/512 HMAC */
-
- h_len = lws_genhmac_size(args.hmac_type);
- if (m < 0 || m != h_len)
- return -1;
-
- /* 6) compute HMAC over payload */
-
- if (lws_genhmac_init(&ctx, args.hmac_type, jwk->el.e[JWK_KEY_E].buf,
- jwk->el.e[JWK_KEY_E].len))
- return -1;
-
- if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
- lws_genhmac_destroy(&ctx, NULL);
-
- return -1;
- }
- if (lws_genhmac_destroy(&ctx, digest))
- return -1;
-
- /* 7) Compare the computed and decoded hashes */
-
- if (memcmp(digest, buf, h_len)) {
- lwsl_notice("digest mismatch\n");
-
- return -1;
- }
-
- return 0;
-}
-
-LWS_VISIBLE int
-lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
- size_t pay_len, char *b64_sig, size_t sig_len,
- enum lws_genhash_types hash_type, struct lws_jwk *jwk)
-{
- uint8_t digest[LWS_GENHASH_LARGEST];
- struct lws_genhash_ctx hash_ctx;
- struct lws_genrsa_ctx rsactx;
- uint8_t *buf;
- int n;
-
- if (lws_genhash_init(&hash_ctx, hash_type))
- return -1;
-
- if (b64_hdr) {
- if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_hdr, hdr_len))
- goto hash_fail;
- if (lws_genhash_update(&hash_ctx, (uint8_t *)".", 1))
- goto hash_fail;
- }
- if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_pay, pay_len))
- goto hash_fail;
-
- if (lws_genhash_destroy(&hash_ctx, digest))
- return -1;
-
- if (!strcmp(jwk->keytype, "RSA")) {
- if (lws_genrsa_create(&rsactx, &jwk->el)) {
- lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
- __func__);
- return -1;
- }
-
- n = jwk->el.e[JWK_KEY_N].len;
- buf = lws_malloc(n, "jws sign");
- if (!buf)
- return -1;
-
- n = lws_genrsa_public_sign(&rsactx, digest, hash_type, buf, n);
- lws_genrsa_destroy(&rsactx);
- if (n < 0) {
- lws_free(buf);
-
- return -1;
- }
-
- n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
- lws_free(buf);
-
- return n;
- }
-
- if (!strcmp(jwk->keytype, "oct"))
- return lws_jws_base64_enc((char *)digest,
- lws_genhash_size(hash_type),
- b64_sig, sig_len);
-
- /* unknown key type */
-
- return -1;
-
-hash_fail:
- lws_genhash_destroy(&hash_ctx, NULL);
- return -1;
-}
-
-LWS_VISIBLE int
-lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
- const char *nonce, char *out, size_t out_len)
-{
- char *buf, *start, *p, *end, *p1, *end1, *b64_hdr, *b64_pay;
- int n, b64_hdr_len, b64_pay_len;
-
- /*
- * This buffer is local to the function, the actual output
- * is prepared into vhd->buf. Only the plaintext protected header
- * (which contains the public key, 512 bytes for 4096b) goes in
- * here temporarily.
- */
- n = LWS_PRE + 2048;
- buf = malloc(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
- */
-
- p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
- n = lws_jwk_export(jwk, 0, p, end - p);
- if (n < 0) {
- lwsl_notice("failed to export jwk\n");
-
- goto bail;
- }
- p += n;
- p += lws_snprintf(p, 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, end1 - p1, "{\"protected\":\"");
- b64_hdr = p1;
- n = lws_jws_base64_enc(start, p - start, p1, end1 - p1);
- if (n < 0) {
- lwsl_notice("%s: failed to encode protected\n", __func__);
- goto bail;
- }
- b64_hdr_len = n;
- p1 += n;
-
- p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
- b64_pay = p1;
- n = lws_jws_base64_enc(payload, len, p1, end1 - p1);
- if (n < 0) {
- lwsl_notice("%s: failed to encode payload\n", __func__);
- goto bail;
- }
- b64_pay_len = n;
-
- p1 += n;
- p1 += lws_snprintf(p1, 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(b64_hdr, b64_hdr_len, b64_pay, b64_pay_len,
- p1, end1 - p1, LWS_GENHASH_TYPE_SHA256, jwk);
- if (n < 0) {
- lwsl_notice("sig gen failed\n");
-
- goto bail;
- }
- p1 += n;
- p1 += lws_snprintf(p1, end1 - p1, "\"}");
-
- free(buf);
-
- return p1 - out;
-
-bail:
- free(buf);
-
- return -1;
-}
-
-
-#if defined(LWS_WITH_SELFTESTS)
-/*
- * These are the inputs and outputs from the worked example in RFC7515
- * Appendix A.1.
- *
- * 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
- * a fixed BASE64URL result.
- *
- * 2) has a fixed header + payload and is signed with a key given in JWK format
- */
-int
-lws_jws_selftest(void)
-{
- struct lws_genhmac_ctx ctx;
- struct lws_jwk jwk;
- char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
- uint8_t digest[LWS_GENHASH_LARGEST];
- int n;
-
- /* Test 1: SHA256 on RFC7515 worked example */
-
- /* 1.1: decode the JWK oct key */
-
- if (lws_jwk_import(&jwk, key_jwk, strlen(key_jwk)) < 0) {
- lwsl_notice("Failed to decode JWK test key\n");
- return -1;
- }
-
- /* 1.2: create JWS known hdr + known payload */
-
- n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
- if (n < 0)
- goto bail;
- if (strcmp(buf, test1_enc))
- goto bail;
-
- enc_ptr = p + 1; /* + 1 skips the . */
- n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
- if (n < 0)
- goto bail;
- if (strcmp(enc_ptr, test2_enc))
- goto bail;
-
- /* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
-
- if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
- jwk.el.e[JWK_KEY_E].buf, jwk.el.e[JWK_KEY_E].len))
- goto bail;
- if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
- goto bail_destroy_hmac;
- lws_genhmac_destroy(&ctx, digest);
-
- /* 1.4: append a base64 encode of the computed HMAC digest */
-
- enc_ptr = p + 1; /* + 1 skips the . */
- n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
- if (n < 0)
- goto bail;
- if (strcmp(enc_ptr, hash_enc)) /* check against known B64URL hash */
- goto bail;
-
- /* 1.5: Check we can agree the signature matches the payload */
-
- if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 0) {
- lwsl_notice("confirm sig failed\n");
- goto bail;
- }
-
- lws_jwk_destroy(&jwk); /* finished with the key from the first test */
-
- /* Test 2: RSA256 on RFC7515 worked example */
-
- /* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
-
- if (lws_jwk_import(&jwk, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
- lwsl_notice("Failed to read JWK key\n");
- goto bail2;
- }
-
- /* 2.2: check the signature on the test packet from RFC7515 A-1 */
-
- if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
- &jwk) < 0) {
- lwsl_notice("confirm rsa sig failed\n");
- goto bail;
- }
-
- /* 2.3: generate our own signature for a copy of the test packet */
-
- memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
-
- /* set p to second . */
- p = strchr(buf + 1, '.');
- p1 = strchr(p + 1, '.');
-
- n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
- p1 + 1, sizeof(buf) - (p1 - buf) - 1,
- LWS_GENHASH_TYPE_SHA256, &jwk);
- if (n < 0)
- goto bail;
-
- puts(buf);
-
- /* 2.4: confirm our signature can be verified */
-
- if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 0) {
- lwsl_notice("confirm rsa sig 2 failed\n");
- goto bail;
- }
-
- lws_jwk_destroy(&jwk);
-
- /* end */
-
- lwsl_notice("%s: selftest OK\n", __func__);
-
- return 0;
-
-bail_destroy_hmac:
- lws_genhmac_destroy(&ctx, NULL);
-
-bail:
- lws_jwk_destroy(&jwk);
-bail2:
- lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
-
- return 1;
-}
-#endif
diff --git a/lib/tls/mbedtls/lws-genec.c b/lib/tls/mbedtls/lws-genec.c
new file mode 100644
index 000000000..e69de29bb
diff --git a/lib/tls/mbedtls/lws-genrsa.c b/lib/tls/mbedtls/lws-genrsa.c
index 99b2e7565..937ed6927 100644
--- a/lib/tls/mbedtls/lws-genrsa.c
+++ b/lib/tls/mbedtls/lws-genrsa.c
@@ -1,7 +1,7 @@
/*
* libwebsockets - generic RSA api hiding the backend
*
- * Copyright (C) 2017 Andy Green
+ * Copyright (C) 2017 - 2018 Andy Green
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,40 +22,39 @@
* same whether you are using openssl or mbedtls hash functions underneath.
*/
#include "core/private.h"
+#include "../../jose/private.h"
LWS_VISIBLE void
-lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
+lws_jwk_destroy_genrsa_elements(struct lws_jwk_elements *el)
{
- int n;
-
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
- if (el->e[n].buf)
- lws_free_set_NULL(el->e[n].buf);
+ lws_jwk_destroy_elements(el, LWS_COUNT_RSA_KEY_ELEMENTS);
}
LWS_VISIBLE int
-lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_jwk_elements *el,
+ struct lws_context *context)
{
memset(ctx, 0, sizeof(*ctx));
ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
if (!ctx->ctx)
return 1;
+ ctx->context = context;
mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
{
int n;
- mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+ mbedtls_mpi *mpi[LWS_COUNT_RSA_KEY_ELEMENTS] = {
&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
&ctx->ctx->QP,
};
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
- if (el->e[n].buf &&
- mbedtls_mpi_read_binary(mpi[n], el->e[n].buf,
- el->e[n].len)) {
+ for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++)
+ if (el[n].buf &&
+ mbedtls_mpi_read_binary(mpi[n], el[n].buf,
+ el[n].len)) {
lwsl_notice("mpi load failed\n");
lws_free_set_NULL(ctx->ctx);
@@ -63,7 +62,7 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
}
}
- ctx->ctx->len = el->e[JWK_KEY_N].len;
+ ctx->ctx->len = el[JWK_RSA_KEYEL_N].len;
return 0;
}
@@ -79,7 +78,7 @@ _rngf(void *context, unsigned char *buf, size_t len)
LWS_VISIBLE int
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
- struct lws_genrsa_elements *el, int bits)
+ struct lws_jwk_elements *el, int bits)
{
int n;
@@ -97,30 +96,30 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
}
{
- mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+ mbedtls_mpi *mpi[LWS_COUNT_RSA_KEY_ELEMENTS] = {
&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
&ctx->ctx->QP,
};
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+ for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++)
if (mbedtls_mpi_size(mpi[n])) {
- el->e[n].buf = lws_malloc(
+ el[n].buf = lws_malloc(
mbedtls_mpi_size(mpi[n]), "genrsakey");
- if (!el->e[n].buf)
+ if (!el[n].buf)
goto cleanup;
- el->e[n].len = mbedtls_mpi_size(mpi[n]);
- mbedtls_mpi_write_binary(mpi[n], el->e[n].buf,
- el->e[n].len);
+ el[n].len = mbedtls_mpi_size(mpi[n]);
+ mbedtls_mpi_write_binary(mpi[n], el[n].buf,
+ el[n].len);
}
}
return 0;
cleanup:
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
- if (el->e[n].buf)
- lws_free_set_NULL(el->e[n].buf);
+ for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++)
+ if (el[n].buf)
+ lws_free_set_NULL(el[n].buf);
cleanup_1:
lws_free(ctx->ctx);
@@ -153,12 +152,14 @@ lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
{
int n;
- ctx->ctx->len = in_len;
- n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, NULL, NULL,
+ //ctx->ctx->len = in_len; // ???
+ ctx->ctx->padding = MBEDTLS_RSA_PKCS_V15;
+ n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf, ctx->context,
MBEDTLS_RSA_PRIVATE,
in_len, in, out);
if (n) {
- lwsl_notice("%s: -0x%x\n", __func__, -n);
+ lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n,
+ (int)in_len);
return -1;
}
@@ -245,7 +246,7 @@ lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
uint8_t *pkey_asn1, size_t pkey_asn1_len)
{
uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1;
- mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+ mbedtls_mpi *mpi[LWS_COUNT_RSA_KEY_ELEMENTS] = {
&ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P,
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
&ctx->ctx->QP,
@@ -277,7 +278,7 @@ lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
*p++ = 0x01;
*p++ = 0x00;
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++) {
+ for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++) {
int m = mbedtls_mpi_size(mpi[n]);
uint8_t *elen;
diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c
index df6ddf2c6..78e308491 100644
--- a/lib/tls/mbedtls/mbedtls-server.c
+++ b/lib/tls/mbedtls/mbedtls-server.c
@@ -453,7 +453,7 @@ lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
int buflen = 0x560;
uint8_t *buf = lws_malloc(buflen, "tmp cert buf"), *p = buf, *pkey_asn1;
struct lws_genrsa_ctx ctx;
- struct lws_genrsa_elements el;
+ struct lws_jwk_elements el;
uint8_t digest[32];
struct lws_genhash_ctx hash_ctx;
int pkey_asn1_len = 3 * 1024;
@@ -498,8 +498,8 @@ lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
/* we need to drop 1 + (keybits / 8) bytes of n in here, 00 + key */
*p++ = 0x00;
- memcpy(p, el.e[JWK_KEY_N].buf, el.e[JWK_KEY_N].len);
- p += el.e[JWK_KEY_N].len;
+ memcpy(p, el.e[JWK_RSA_KEYEL_N].buf, el.e[JWK_RSA_KEYEL_N].len);
+ p += el.e[JWK_RSA_KEYEL_N].len;
memcpy(p, ss_cert_san_leadin, sizeof(ss_cert_san_leadin));
p += sizeof(ss_cert_san_leadin);
diff --git a/lib/tls/openssl/lws-genec.c b/lib/tls/openssl/lws-genec.c
new file mode 100644
index 000000000..e69de29bb
diff --git a/lib/tls/openssl/lws-genrsa.c b/lib/tls/openssl/lws-genrsa.c
index a136327b3..8bb52bf0b 100644
--- a/lib/tls/openssl/lws-genrsa.c
+++ b/lib/tls/openssl/lws-genrsa.c
@@ -22,31 +22,31 @@
* same whether you are using openssl or mbedtls hash functions underneath.
*/
#include "core/private.h"
+#include "../../jose/private.h"
LWS_VISIBLE void
-lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
+lws_jwk_destroy_genrsa_elements(struct lws_jwk_elements *el)
{
- int n;
-
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
- if (el->e[n].buf)
- lws_free_set_NULL(el->e[n].buf);
+ lws_jwk_destroy_elements(el, LWS_COUNT_RSA_KEY_ELEMENTS);
}
LWS_VISIBLE int
-lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_jwk_elements *el,
+ struct lws_context *context)
{
int n;
memset(ctx, 0, sizeof(*ctx));
+ ctx->context = context;
+
/* Step 1:
*
* convert the MPI for e and n to OpenSSL BIGNUMs
*/
for (n = 0; n < 5; n++) {
- ctx->bn[n] = BN_bin2bn(el->e[n].buf, el->e[n].len, NULL);
+ ctx->bn[n] = BN_bin2bn(el[n].buf, el[n].len, NULL);
if (!ctx->bn[n]) {
lwsl_notice("mpi load failed\n");
goto bail;
@@ -65,18 +65,20 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
}
#if defined(LWS_HAVE_RSA_SET0_KEY)
- if (RSA_set0_key(ctx->rsa, ctx->bn[JWK_KEY_N], ctx->bn[JWK_KEY_E],
- ctx->bn[JWK_KEY_D]) != 1) {
+ if (RSA_set0_key(ctx->rsa, ctx->bn[JWK_RSA_KEYEL_N],
+ ctx->bn[JWK_RSA_KEYEL_E],
+ ctx->bn[JWK_RSA_KEYEL_D]) != 1) {
lwsl_notice("RSA_set0_key failed\n");
goto bail;
}
- RSA_set0_factors(ctx->rsa, ctx->bn[JWK_KEY_P], ctx->bn[JWK_KEY_Q]);
+ RSA_set0_factors(ctx->rsa, ctx->bn[JWK_RSA_KEYEL_P],
+ ctx->bn[JWK_RSA_KEYEL_Q]);
#else
- ctx->rsa->e = ctx->bn[JWK_KEY_E];
- ctx->rsa->n = ctx->bn[JWK_KEY_N];
- ctx->rsa->d = ctx->bn[JWK_KEY_D];
- ctx->rsa->p = ctx->bn[JWK_KEY_P];
- ctx->rsa->q = ctx->bn[JWK_KEY_Q];
+ ctx->rsa->e = ctx->bn[JWK_RSA_KEYEL_E];
+ ctx->rsa->n = ctx->bn[JWK_RSA_KEYEL_N];
+ ctx->rsa->d = ctx->bn[JWK_RSA_KEYEL_D];
+ ctx->rsa->p = ctx->bn[JWK_RSA_KEYEL_P];
+ ctx->rsa->q = ctx->bn[JWK_RSA_KEYEL_Q];
#endif
return 0;
@@ -98,7 +100,7 @@ bail:
LWS_VISIBLE int
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
- struct lws_genrsa_elements *el, int bits)
+ struct lws_jwk_elements *el, int bits)
{
BIGNUM *bn;
int n;
@@ -128,9 +130,10 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
{
const BIGNUM *mpi[5];
- RSA_get0_key(ctx->rsa, &mpi[JWK_KEY_N], &mpi[JWK_KEY_E],
- &mpi[JWK_KEY_D]);
- RSA_get0_factors(ctx->rsa, &mpi[JWK_KEY_P], &mpi[JWK_KEY_Q]);
+ RSA_get0_key(ctx->rsa, &mpi[JWK_RSA_KEYEL_N],
+ &mpi[JWK_RSA_KEYEL_E], &mpi[JWK_RSA_KEYEL_D]);
+ RSA_get0_factors(ctx->rsa, &mpi[JWK_RSA_KEYEL_P],
+ &mpi[JWK_RSA_KEYEL_Q]);
#else
{
BIGNUM *mpi[5] = {
@@ -140,41 +143,66 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
#endif
for (n = 0; n < 5; n++)
if (BN_num_bytes(mpi[n])) {
- el->e[n].buf = lws_malloc(
+ el[n].buf = lws_malloc(
BN_num_bytes(mpi[n]), "genrsakey");
- if (!el->e[n].buf)
+ if (!el[n].buf)
goto cleanup;
- el->e[n].len = BN_num_bytes(mpi[n]);
- BN_bn2bin(mpi[n], el->e[n].buf);
+ el[n].len = BN_num_bytes(mpi[n]);
+ BN_bn2bin(mpi[n], el[n].buf);
}
}
return 0;
cleanup:
- for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
- if (el->e[n].buf)
- lws_free_set_NULL(el->e[n].buf);
+ for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++)
+ if (el[n].buf)
+ lws_free_set_NULL(el[n].buf);
cleanup_1:
RSA_free(ctx->rsa);
return -1;
}
+/*
+ * flen must be less than RSA_size(rsa) - 11 for the PKCS #1 v1.5
+ * based padding modes
+ */
+
+LWS_VISIBLE int
+lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out)
+{
+ int m;
+
+ m = RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
+ RSA_PKCS1_PADDING);
+
+ /* the bignums are also freed by freeing the RSA */
+ RSA_free(ctx->rsa);
+ ctx->rsa = NULL;
+
+ if (m != -1)
+ return m;
+
+ return -1;
+}
+
LWS_VISIBLE int
lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max)
{
- uint32_t m;
+ int m;
- m = RSA_public_decrypt((int)in_len, in, out, ctx->rsa, RSA_PKCS1_PADDING);
+ m = RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
+ RSA_PKCS1_PADDING);
/* the bignums are also freed by freeing the RSA */
RSA_free(ctx->rsa);
ctx->rsa = NULL;
- if (m != (uint32_t)-1)
- return (int)m;
+ if (m != -1)
+ return m;
return -1;
}
diff --git a/minimal-examples/api-tests/api-test-jose/CMakeLists.txt b/minimal-examples/api-tests/api-test-jose/CMakeLists.txt
new file mode 100644
index 000000000..49207b7b2
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/CMakeLists.txt
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-api-test-jose)
+set(SRCS main.c jwk.c jws.c jwe.c)
+
+# If we are being built as part of lws, confirm current build config supports
+# reqconfig, else skip building ourselves.
+#
+# If we are being built externally, confirm installed lws was configured to
+# support reqconfig, else error out with a helpful message about the problem.
+#
+MACRO(require_lws_config reqconfig _val result)
+
+ if (DEFINED ${reqconfig})
+ if (${reqconfig})
+ set (rq 1)
+ else()
+ set (rq 0)
+ endif()
+ else()
+ set(rq 0)
+ endif()
+
+ if (${_val} EQUAL ${rq})
+ set(SAME 1)
+ else()
+ set(SAME 0)
+ endif()
+
+ if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
+ if (${_val})
+ message("${SAMP}: skipping as lws being built without ${reqconfig}")
+ else()
+ message("${SAMP}: skipping as lws built with ${reqconfig}")
+ endif()
+ set(${result} 0)
+ else()
+ if (LWS_WITH_MINIMAL_EXAMPLES)
+ set(MET ${SAME})
+ else()
+ CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
+ if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
+ set(HAS_${reqconfig} 0)
+ else()
+ set(HAS_${reqconfig} 1)
+ endif()
+ if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
+ set(MET 1)
+ else()
+ set(MET 0)
+ endif()
+ endif()
+ if (NOT MET)
+ if (${_val})
+ message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
+ else()
+ message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
+ endif()
+ endif()
+ endif()
+ENDMACRO()
+
+
+
+ add_executable(${SAMP} ${SRCS})
+
+ if (websockets_shared)
+ target_link_libraries(${SAMP} websockets_shared)
+ add_dependencies(${SAMP} websockets_shared)
+ else()
+ target_link_libraries(${SAMP} websockets)
+ endif()
diff --git a/minimal-examples/api-tests/api-test-jose/README.md b/minimal-examples/api-tests/api-test-jose/README.md
new file mode 100644
index 000000000..74034c793
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/README.md
@@ -0,0 +1,22 @@
+# lws api test lwsac
+
+Demonstrates how to use and performs selftests for lwsac
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+Commandline option|Meaning
+---|---
+-d |Debug verbosity in decimal, eg, -d15
+
+```
+ $ ./lws-api-test-lwsac
+[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac
+[2018/10/09 09:14:17:4835] USER: Completed: PASS
+```
+
diff --git a/minimal-examples/api-tests/api-test-jose/jwe.c b/minimal-examples/api-tests/api-test-jose/jwe.c
new file mode 100644
index 000000000..77637e36e
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/jwe.c
@@ -0,0 +1,332 @@
+/*
+ * lws-api-test-jose - RFC7516 jwe tests
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+
+
+/* A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 */
+
+/* "Live long and prosper." */
+static
+uint8_t
+
+#if 0
+lws_jwe_ex_a2_plaintext[] = {
+ 76, 105, 118, 101, 32, 108, 111, 110,
+ 103, 32, 97, 110, 100, 32, 112, 114,
+ 111, 115, 112, 101, 114, 46
+},
+#endif
+*lws_jwe_ex_a2_jose_hdr = (uint8_t *)
+ "{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}",
+
+*lws_jwe_ex_a2_jose_hdr_b64utf8 = (unsigned char *)
+ "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
+
+lws_jwe_ex_a2_cek[] = {
+ 4, 211, 31, 197, 84, 157, 252, 254,
+ 11, 100, 157, 250, 63, 170, 106, 206,
+ 107, 124, 212, 45, 111, 107, 9, 219,
+ 200, 177, 0, 240, 143, 156, 44, 207
+},
+
+*lws_jwe_ex_a2_jwk_json = (uint8_t *)
+"{"
+ "\"kty\":\"RSA\","
+ "\"n\":\"sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl"
+ "UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre"
+ "cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_"
+ "7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI"
+ "Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU"
+ "7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq"
+ "1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry"
+ "nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_"
+ "0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj"
+ "-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj"
+ "T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ\","
+ "\"p\":\"9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68"
+ "ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP"
+ "krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM\","
+ "\"q\":\"uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y"
+ "BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN"
+ "-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0\","
+ "\"dp\":\"w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv"
+ "ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra"
+ "Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs\","
+ "\"dq\":\"o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff"
+ "7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_"
+ "odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU\","
+ "\"qi\":\"eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC"
+ "tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ"
+ "B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo\""
+"}",
+
+lws_jwe_ex_a2_jwk_enc_key[] = {
+ 80, 104, 72, 58, 11, 130, 236, 139,
+ 132, 189, 255, 205, 61, 86, 151, 176,
+ 99, 40, 44, 233, 176, 189, 205, 70,
+ 202, 169, 72, 40, 226, 181, 156, 223,
+ 120, 156, 115, 232, 150, 209, 145, 133,
+ 104, 112, 237, 156, 116, 250, 65, 102,
+ 212, 210, 103, 240, 177, 61, 93, 40,
+ 71, 231, 223, 226, 240, 157, 15, 31,
+ 150, 89, 200, 215, 198, 203, 108, 70,
+ 117, 66, 212, 238, 193, 205, 23, 161,
+ 169, 218, 243, 203, 128, 214, 127, 253,
+ 215, 139, 43, 17, 135, 103, 179, 220,
+ 28, 2, 212, 206, 131, 158, 128, 66,
+ 62, 240, 78, 186, 141, 125, 132, 227,
+ 60, 137, 43, 31, 152, 199, 54, 72,
+ 34, 212, 115, 11, 152, 101, 70, 42,
+ 219, 233, 142, 66, 151, 250, 126, 146,
+ 141, 216, 190, 73, 50, 177, 146, 5,
+ 52, 247, 28, 197, 21, 59, 170, 247,
+ 181, 89, 131, 241, 169, 182, 246, 99,
+ 15, 36, 102, 166, 182, 172, 197, 136,
+ 230, 120, 60, 58, 219, 243, 149, 94,
+ 222, 150, 154, 194, 110, 227, 225, 112,
+ 39, 89, 233, 112, 207, 211, 241, 124,
+ 174, 69, 221, 179, 107, 196, 225, 127,
+ 167, 112, 226, 12, 242, 16, 24, 28,
+ 120, 182, 244, 213, 244, 153, 194, 162,
+ 69, 160, 244, 248, 63, 165, 141, 4,
+ 207, 249, 193, 79, 131, 0, 169, 233,
+ 127, 167, 101, 151, 125, 56, 112, 111,
+ 248, 29, 232, 90, 29, 147, 110, 169,
+ 146, 114, 165, 204, 71, 136, 41, 252
+}
+#if 0
+,
+*lws_jwe_ex_a2_jwk_enc_key_b64 = (uint8_t *)
+ "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm"
+ "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc"
+ "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF"
+ "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8"
+ "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv"
+ "-B3oWh2TbqmScqXMR4gp_A",
+
+lws_jwe_ex_a2_iv[] = {
+ 3, 22, 60, 12, 43, 67, 104, 105,
+ 108, 108, 105, 99, 111, 116, 104, 101
+},
+
+*lws_jwe_ex_a2_iv_b64 = (uint8_t *)
+ "AxY8DCtDaGlsbGljb3RoZQ",
+
+lws_jwe_ex_a2_aad[] = {
+ 101, 121, 74, 104, 98, 71, 99, 105,
+ 79, 105, 74, 83, 85, 48, 69, 120,
+ 88, 122, 85, 105, 76, 67, 74, 108,
+ 98, 109, 77, 105, 79, 105, 74, 66,
+ 77, 84, 73, 52, 81, 48, 74, 68,
+ 76, 85, 104, 84, 77, 106, 85, 50,
+ 73, 110, 48
+},
+
+lws_jwe_ex_a2_ciphertext[] = {
+ 40, 57, 83, 181, 119, 33, 133, 148,
+ 198, 185, 243, 24, 152, 230, 6, 75,
+ 129, 223, 127, 19, 210, 82, 183, 230,
+ 168, 33, 215, 104, 143, 112, 56, 102
+},
+
+*lws_jwe_ex_a2_ciphertext_b64 = (uint8_t *)
+ "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
+
+lws_jwe_ex_a2_authtag[] = {
+ 246, 17, 244, 190, 4, 95, 98, 3,
+ 231, 0, 115, 157, 242, 203, 100, 191
+},
+
+*lws_jwe_ex_a2_authtag_b64 = (uint8_t *)
+ "9hH0vgRfYgPnAHOd8stkvw",
+
+*lws_jwe_ex_a2_aggregated = (uint8_t *)
+ "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0."
+ "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm"
+ "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc"
+ "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF"
+ "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8"
+ "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv"
+ "-B3oWh2TbqmScqXMR4gp_A."
+ "AxY8DCtDaGlsbGljb3RoZQ."
+ "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY."
+ "9hH0vgRfYgPnAHOd8stkvw"
+#endif
+;
+
+/*
+ * These are the inputs and outputs from the worked example in RFC7515
+ * Appendix A.1.
+ *
+ * 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
+ * a fixed BASE64URL result.
+ *
+ * 2) has a fixed header + payload and is signed with a key given in JWK format
+ */
+int
+test_jwe(struct lws_context *context)
+{
+ struct lws_genrsa_ctx rsactx;
+ struct lws_jwk jwk;
+ uint8_t enc_cek[sizeof(lws_jwe_ex_a2_jwk_enc_key) + 2048];
+ char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1;
+ int n;
+
+ /* Test 1: A.2 */
+
+ /* Decode the JWK JSON key */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) {
+ lwsl_notice("Failed to decode JWK test key\n");
+ return -1;
+ }
+
+ if (jwk.kty != LWS_JWK_KYT_RSA) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
+
+ return -1;
+ }
+
+ /* A.2.1: encode JOSE header and confirm matches official string */
+
+ n = lws_jws_encode_section((char *)lws_jwe_ex_a2_jose_hdr,
+ strlen((char *)lws_jwe_ex_a2_jose_hdr), 1,
+ &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(buf, (char *)lws_jwe_ex_a2_jose_hdr_b64utf8))
+ goto bail;
+
+ /* A.2.3: Encrypt the CEK with the recipient's public key using the
+ * RSAES-PKCS1-v1_5 algorithm to produce the JWE Encrypted Key.
+ */
+
+ if (lws_genrsa_create(&rsactx, jwk.e, context)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ goto bail;
+ }
+
+ memset(enc_cek, 0, sizeof(enc_cek));
+
+ n = lws_genrsa_public_encrypt(&rsactx, lws_jwe_ex_a2_cek,
+ sizeof(lws_jwe_ex_a2_cek), enc_cek);
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lwsl_err("%s: encrypt cek fail\n", __func__);
+ goto bail;
+ }
+#if 0
+ if (memcmp(enc_cek, lws_jwe_ex_a2_jwk_enc_key, sizeof(enc_cek))) {
+ lwsl_err("%s: encrypt cek wrong output\n", __func__);
+ lwsl_hexdump_notice(enc_cek, sizeof(enc_cek));
+ lwsl_hexdump_notice(lws_jwe_ex_a2_jwk_enc_key,
+ sizeof(lws_jwe_ex_a2_jwk_enc_key));
+ goto bail;
+ }
+
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, test2_enc))
+ goto bail;
+
+ /* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
+
+ if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
+ jwk.el.e[JWK_RSA_KEYEL_E].buf,
+ jwk.el.e[JWK_RSA_KEYEL_E].len))
+ goto bail;
+ if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
+ goto bail_destroy_hmac;
+ lws_genhmac_destroy(&ctx, digest);
+
+ /* 1.4: append a base64 encode of the computed HMAC digest */
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, hash_enc)) /* check against known B64URL hash */
+ goto bail;
+
+ /* 1.5: Check we can agree the signature matches the payload */
+
+ if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 0) {
+ lwsl_notice("confirm sig failed\n");
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk); /* finished with the key from the first test */
+
+ /* Test 2: RSA256 on RFC7515 worked example */
+
+ /* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
+
+ if (lws_jwk_import(&jwk, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
+ lwsl_notice("Failed to read JWK key\n");
+ goto bail2;
+ }
+
+ /* 2.2: check the signature on the test packet from RFC7515 A-1 */
+
+ if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
+ &jwk) < 0) {
+ lwsl_notice("confirm rsa sig failed\n");
+ goto bail;
+ }
+
+ /* 2.3: generate our own signature for a copy of the test packet */
+
+ memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
+
+ /* set p to second . */
+ p = strchr(buf + 1, '.');
+ p1 = strchr(p + 1, '.');
+
+ n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
+ p1 + 1, sizeof(buf) - (p1 - buf) - 1,
+ LWS_GENHASH_TYPE_SHA256, &jwk);
+ if (n < 0)
+ goto bail;
+
+ puts(buf);
+
+ /* 2.4: confirm our signature can be verified */
+
+ if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 0) {
+ lwsl_notice("confirm rsa sig 2 failed\n");
+ goto bail;
+ }
+#endif
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+#if 0
+bail_destroy_hmac:
+ lws_genhmac_destroy(&ctx, NULL);
+#endif
+bail:
+ lws_jwk_destroy(&jwk);
+//bail2:
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+
+}
diff --git a/minimal-examples/api-tests/api-test-jose/jwk.c b/minimal-examples/api-tests/api-test-jose/jwk.c
new file mode 100644
index 000000000..b4fb95aae
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/jwk.c
@@ -0,0 +1,350 @@
+/*
+ * lws-api-test-jose - RFC7517 jwk tests
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+
+static
+uint8_t *lws_jwe_ex_a1_jwk_json = (uint8_t *) /* EC + RSA public keys */
+ "{\"keys\":"
+ "["
+ "{\"kty\":\"EC\","
+ "\"crv\":\"P-256\","
+ "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","
+ "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\","
+ "\"use\":\"enc\","
+ "\"kid\":\"1\"},"
+
+ "{\"kty\":\"RSA\","
+ "\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx"
+ "4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs"
+ "tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2"
+ "QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI"
+ "SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb"
+ "w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
+ "\"e\":\"AQAB\","
+ "\"alg\":\"RS256\","
+ "\"kid\":\"2011-04-29\"}"
+ "]"
+ "}",
+
+*lws_jwe_ex_a2_jwk_json = (uint8_t *) /* EC + RSA private keys */
+ "{\"keys\":"
+ "["
+ "{\"kty\":\"EC\","
+ "\"crv\":\"P-256\","
+ "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","
+ "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\","
+ "\"d\":\"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE\","
+ "\"use\":\"enc\","
+ "\"kid\":\"1\"},"
+
+ "{\"kty\":\"RSA\","
+ "\"n\":\"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4"
+ "cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst"
+ "n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2Q"
+ "vzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbIS"
+ "D08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw"
+ "0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9"
+ "M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqij"
+ "wp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d"
+ "_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBz"
+ "nbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFz"
+ "me1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q\","
+ "\"p\":\"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPV"
+ "nwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqV"
+ "WlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs\","
+ "\"q\":\"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyum"
+ "qjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgx"
+ "kIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk\","
+ "\"dp\":\"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oim"
+ "YwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_Nmtu"
+ "YZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0\","
+ "\"dq\":\"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUU"
+ "vMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9"
+ "GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk\","
+ "\"qi\":\"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzg"
+ "UIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rx"
+ "yR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU\","
+ "\"alg\":\"RS256\","
+ "\"kid\":\"2011-04-29\"}"
+ "]"
+ "}",
+*lws_jwe_ex_a3_jwk_json = (uint8_t *) /* oct symmetric keys */
+ "{\"keys\":"
+ "["
+ "{\"kty\":\"oct\","
+ "\"alg\":\"A128KW\","
+ "\"k\":\"GawgguFyGrWKav7AX4VKUg\"},"
+
+ "{\"kty\":\"oct\","
+ "\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75"
+ "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\","
+ "\"kid\":\"HMAC key used in JWS spec Appendix A.1 example\"}"
+ "]"
+ "}",
+
+*lws_jwe_ex_b_jwk_json = (uint8_t *) /* x5c example (no parent JSON) */
+ "{\"kty\":\"RSA\","
+ "\"use\":\"sig\","
+ "\"kid\":\"1b94c\","
+ "\"n\":\"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08"
+ "PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q"
+ "u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a"
+ "YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH"
+ "MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv"
+ "VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ\","
+ "\"e\":\"AQAB\","
+ "\"x5c\":"
+ "[\"MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB"
+ "gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD"
+ "VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1"
+ "wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg"
+ "NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV"
+ "QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w"
+ "YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH"
+ "YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66"
+ "s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6"
+ "SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn"
+ "fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq"
+ "PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk"
+ "aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA"
+ "QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL"
+ "+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1"
+ "zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL"
+ "2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo"
+ "4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq"
+ "gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==\"]"
+ "}",
+*lws_jwe_ex_c1_jwk_json = (uint8_t *) /* RSA enc private key (no parent JSON) */
+ "{"
+ "\"kty\":\"RSA\","
+ "\"kid\":\"juliet@capulet.lit\","
+ "\"use\":\"enc\","
+ "\"n\":\"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy"
+ "O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP"
+ "8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0"
+ "Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X"
+ "OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1"
+ "_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS"
+ "NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U"
+ "vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu"
+ "ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu"
+ "rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a"
+ "hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ\","
+ "\"p\":\"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf"
+ "QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8"
+ "UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws\","
+ "\"q\":\"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I"
+ "edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK"
+ "rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s\","
+ "\"dp\":\"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3"
+ "tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w"
+ "Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c\","
+ "\"dq\":\"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9"
+ "GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy"
+ "mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots\","
+ "\"qi\":\"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq"
+ "abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o"
+ "Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8\""
+ "}" /*,
+lws_jwe_ex_c1_plaintext[] = {
+ 123, 34, 107, 116, 121, 34, 58, 34, 82, 83, 65, 34, 44, 34, 107,
+ 105, 100, 34, 58, 34, 106, 117, 108, 105, 101, 116, 64, 99, 97, 112,
+ 117, 108, 101, 116, 46, 108, 105, 116, 34, 44, 34, 117, 115, 101, 34,
+ 58, 34, 101, 110, 99, 34, 44, 34, 110, 34, 58, 34, 116, 54, 81, 56,
+ 80, 87, 83, 105, 49, 100, 107, 74, 106, 57, 104, 84, 80, 56, 104, 78,
+ 89, 70, 108, 118, 97, 100, 77, 55, 68, 102, 108, 87, 57, 109, 87,
+ 101, 112, 79, 74, 104, 74, 54, 54, 119, 55, 110, 121, 111, 75, 49,
+ 103, 80, 78, 113, 70, 77, 83, 81, 82, 121, 79, 49, 50, 53, 71, 112,
+ 45, 84, 69, 107, 111, 100, 104, 87, 114, 48, 105, 117, 106, 106, 72,
+ 86, 120, 55, 66, 99, 86, 48, 108, 108, 83, 52, 119, 53, 65, 67, 71,
+ 103, 80, 114, 99, 65, 100, 54, 90, 99, 83, 82, 48, 45, 73, 113, 111,
+ 109, 45, 81, 70, 99, 78, 80, 56, 83, 106, 103, 48, 56, 54, 77, 119,
+ 111, 113, 81, 85, 95, 76, 89, 121, 119, 108, 65, 71, 90, 50, 49, 87,
+ 83, 100, 83, 95, 80, 69, 82, 121, 71, 70, 105, 78, 110, 106, 51, 81,
+ 81, 108, 79, 56, 89, 110, 115, 53, 106, 67, 116, 76, 67, 82, 119, 76,
+ 72, 76, 48, 80, 98, 49, 102, 69, 118, 52, 53, 65, 117, 82, 73, 117,
+ 85, 102, 86, 99, 80, 121, 83, 66, 87, 89, 110, 68, 121, 71, 120, 118,
+ 106, 89, 71, 68, 83, 77, 45, 65, 113, 87, 83, 57, 122, 73, 81, 50,
+ 90, 105, 108, 103, 84, 45, 71, 113, 85, 109, 105, 112, 103, 48, 88,
+ 79, 67, 48, 67, 99, 50, 48, 114, 103, 76, 101, 50, 121, 109, 76, 72,
+ 106, 112, 72, 99, 105, 67, 75, 86, 65, 98, 89, 53, 45, 76, 51, 50,
+ 45, 108, 83, 101, 90, 79, 45, 79, 115, 54, 85, 49, 53, 95, 97, 88,
+ 114, 107, 57, 71, 119, 56, 99, 80, 85, 97, 88, 49, 95, 73, 56, 115,
+ 76, 71, 117, 83, 105, 86, 100, 116, 51, 67, 95, 70, 110, 50, 80, 90,
+ 51, 90, 56, 105, 55, 52, 52, 70, 80, 70, 71, 71, 99, 71, 49, 113,
+ 115, 50, 87, 122, 45, 81, 34, 44, 34, 101, 34, 58, 34, 65, 81, 65,
+ 66, 34, 44, 34, 100, 34, 58, 34, 71, 82, 116, 98, 73, 81, 109, 104,
+ 79, 90, 116, 121, 115, 122, 102, 103, 75, 100, 103, 52, 117, 95, 78,
+ 45, 82, 95, 109, 90, 71, 85, 95, 57, 107, 55, 74, 81, 95, 106, 110,
+ 49, 68, 110, 102, 84, 117, 77, 100, 83, 78, 112, 114, 84, 101, 97,
+ 83, 84, 121, 87, 102, 83, 78, 107, 117, 97, 65, 119, 110, 79, 69, 98,
+ 73, 81, 86, 121, 49, 73, 81, 98, 87, 86, 86, 50, 53, 78, 89, 51, 121,
+ 98, 99, 95, 73, 104, 85, 74, 116, 102, 114, 105, 55, 98, 65, 88, 89,
+ 69, 82, 101, 87, 97, 67, 108, 51, 104, 100, 108, 80, 75, 88, 121, 57,
+ 85, 118, 113, 80, 89, 71, 82, 48, 107, 73, 88, 84, 81, 82, 113, 110,
+ 115, 45, 100, 86, 74, 55, 106, 97, 104, 108, 73, 55, 76, 121, 99,
+ 107, 114, 112, 84, 109, 114, 77, 56, 100, 87, 66, 111, 52, 95, 80,
+ 77, 97, 101, 110, 78, 110, 80, 105, 81, 103, 79, 48, 120, 110, 117,
+ 84, 111, 120, 117, 116, 82, 90, 74, 102, 74, 118, 71, 52, 79, 120,
+ 52, 107, 97, 51, 71, 79, 82, 81, 100, 57, 67, 115, 67, 90, 50, 118,
+ 115, 85, 68, 109, 115, 88, 79, 102, 85, 69, 78, 79, 121, 77, 113, 65,
+ 68, 67, 54, 112, 49, 77, 51, 104, 51, 51, 116, 115, 117, 114, 89, 49,
+ 53, 107, 57, 113, 77, 83, 112, 71, 57, 79, 88, 95, 73, 74, 65, 88,
+ 109, 120, 122, 65, 104, 95, 116, 87, 105, 90, 79, 119, 107, 50, 75,
+ 52, 121, 120, 72, 57, 116, 83, 51, 76, 113, 49, 121, 88, 56, 67, 49,
+ 69, 87, 109, 101, 82, 68, 107, 75, 50, 97, 104, 101, 99, 71, 56, 53,
+ 45, 111, 76, 75, 81, 116, 53, 86, 69, 112, 87, 72, 75, 109, 106, 79,
+ 105, 95, 103, 74, 83, 100, 83, 103, 113, 99, 78, 57, 54, 88, 53, 50,
+ 101, 115, 65, 81, 34, 44, 34, 112, 34, 58, 34, 50, 114, 110, 83, 79,
+ 86, 52, 104, 75, 83, 78, 56, 115, 83, 52, 67, 103, 99, 81, 72, 70,
+ 98, 115, 48, 56, 88, 98, 111, 70, 68, 113, 75, 117, 109, 51, 115, 99,
+ 52, 104, 51, 71, 82, 120, 114, 84, 109, 81, 100, 108, 49, 90, 75, 57,
+ 117, 119, 45, 80, 73, 72, 102, 81, 80, 48, 70, 107, 120, 88, 86, 114,
+ 120, 45, 87, 69, 45, 90, 69, 98, 114, 113, 105, 118, 72, 95, 50, 105,
+ 67, 76, 85, 83, 55, 119, 65, 108, 54, 88, 118, 65, 82, 116, 49, 75,
+ 107, 73, 97, 85, 120, 80, 80, 83, 89, 66, 57, 121, 107, 51, 49, 115,
+ 48, 81, 56, 85, 75, 57, 54, 69, 51, 95, 79, 114, 65, 68, 65, 89, 116,
+ 65, 74, 115, 45, 77, 51, 74, 120, 67, 76, 102, 78, 103, 113, 104, 53,
+ 54, 72, 68, 110, 69, 84, 84, 81, 104, 72, 51, 114, 67, 84, 53, 84,
+ 51, 121, 74, 119, 115, 34, 44, 34, 113, 34, 58, 34, 49, 117, 95, 82,
+ 105, 70, 68, 80, 55, 76, 66, 89, 104, 51, 78, 52, 71, 88, 76, 84, 57,
+ 79, 112, 83, 75, 89, 80, 48, 117, 81, 90, 121, 105, 97, 90, 119, 66,
+ 116, 79, 67, 66, 78, 74, 103, 81, 120, 97, 106, 49, 48, 82, 87, 106,
+ 115, 90, 117, 48, 99, 54, 73, 101, 100, 105, 115, 52, 83, 55, 66, 95,
+ 99, 111, 83, 75, 66, 48, 75, 106, 57, 80, 97, 80, 97, 66, 122, 103,
+ 45, 73, 121, 83, 82, 118, 118, 99, 81, 117, 80, 97, 109, 81, 117, 54,
+ 54, 114, 105, 77, 104, 106, 86, 116, 71, 54, 84, 108, 86, 56, 67, 76,
+ 67, 89, 75, 114, 89, 108, 53, 50, 122, 105, 113, 75, 48, 69, 95, 121,
+ 109, 50, 81, 110, 107, 119, 115, 85, 88, 55, 101, 89, 84, 66, 55, 76,
+ 98, 65, 72, 82, 75, 57, 71, 113, 111, 99, 68, 69, 53, 66, 48, 102,
+ 56, 48, 56, 73, 52, 115, 34, 44, 34, 100, 112, 34, 58, 34, 75, 107,
+ 77, 84, 87, 113, 66, 85, 101, 102, 86, 119, 90, 50, 95, 68, 98, 106,
+ 49, 112, 80, 81, 113, 121, 72, 83, 72, 106, 106, 57, 48, 76, 53, 120,
+ 95, 77, 79, 122, 113, 89, 65, 74, 77, 99, 76, 77, 90, 116, 98, 85,
+ 116, 119, 75, 113, 118, 86, 68, 113, 51, 116, 98, 69, 111, 51, 90,
+ 73, 99, 111, 104, 98, 68, 116, 116, 54, 83, 98, 102, 109, 87, 122,
+ 103, 103, 97, 98, 112, 81, 120, 78, 120, 117, 66, 112, 111, 79, 79,
+ 102, 95, 97, 95, 72, 103, 77, 88, 75, 95, 108, 104, 113, 105, 103,
+ 73, 52, 121, 95, 107, 113, 83, 49, 119, 89, 53, 50, 73, 119, 106, 85,
+ 110, 53, 114, 103, 82, 114, 74, 45, 121, 89, 111, 49, 104, 52, 49,
+ 75, 82, 45, 118, 122, 50, 112, 89, 104, 69, 65, 101, 89, 114, 104,
+ 116, 116, 87, 116, 120, 86, 113, 76, 67, 82, 86, 105, 68, 54, 99, 34,
+ 44, 34, 100, 113, 34, 58, 34, 65, 118, 102, 83, 48, 45, 103, 82, 120,
+ 118, 110, 48, 98, 119, 74, 111, 77, 83, 110, 70, 120, 89, 99, 75, 49,
+ 87, 110, 117, 69, 106, 81, 70, 108, 117, 77, 71, 102, 119, 71, 105,
+ 116, 81, 66, 87, 116, 102, 90, 49, 69, 114, 55, 116, 49, 120, 68,
+ 107, 98, 78, 57, 71, 81, 84, 66, 57, 121, 113, 112, 68, 111, 89, 97,
+ 78, 48, 54, 72, 55, 67, 70, 116, 114, 107, 120, 104, 74, 73, 66, 81,
+ 97, 106, 54, 110, 107, 70, 53, 75, 75, 83, 51, 84, 81, 116, 81, 53,
+ 113, 67, 122, 107, 79, 107, 109, 120, 73, 101, 51, 75, 82, 98, 66,
+ 121, 109, 88, 120, 107, 98, 53, 113, 119, 85, 112, 88, 53, 69, 76,
+ 68, 53, 120, 70, 99, 54, 70, 101, 105, 97, 102, 87, 89, 89, 54, 51,
+ 84, 109, 109, 69, 65, 117, 95, 108, 82, 70, 67, 79, 74, 51, 120, 68,
+ 101, 97, 45, 111, 116, 115, 34, 44, 34, 113, 105, 34, 58, 34, 108,
+ 83, 81, 105, 45, 119, 57, 67, 112, 121, 85, 82, 101, 77, 69, 114, 80,
+ 49, 82, 115, 66, 76, 107, 55, 119, 78, 116, 79, 118, 115, 53, 69, 81,
+ 112, 80, 113, 109, 117, 77, 118, 113, 87, 53, 55, 78, 66, 85, 99,
+ 122, 83, 99, 69, 111, 80, 119, 109, 85, 113, 113, 97, 98, 117, 57,
+ 86, 48, 45, 80, 121, 52, 100, 81, 53, 55, 95, 98, 97, 112, 111, 75,
+ 82, 117, 49, 82, 57, 48, 98, 118, 117, 70, 110, 85, 54, 51, 83, 72,
+ 87, 69, 70, 103, 108, 90, 81, 118, 74, 68, 77, 101, 65, 118, 109,
+ 106, 52, 115, 109, 45, 70, 112, 48, 111, 89, 117, 95, 110, 101, 111,
+ 116, 103, 81, 48, 104, 122, 98, 73, 53, 103, 114, 121, 55, 97, 106,
+ 100, 89, 121, 57, 45, 50, 108, 78, 120, 95, 55, 54, 97, 66, 90, 111,
+ 79, 85, 117, 57, 72, 67, 74, 45, 85, 115, 102, 83, 79, 73, 56, 34,
+ 125 } */
+;
+
+static int
+key_import_callback(struct lws_jwk *s, void *user)
+{
+ lwsl_notice("%s: key type %d\n", __func__, s->kty);
+
+ return 0;
+}
+
+
+int
+test_jwk(struct lws_context *context)
+{
+ struct lws_jwk jwk;
+
+ /* Test 1: A.1: Example public keys */
+
+ if (lws_jwk_import(&jwk, key_import_callback, NULL,
+ (char *)lws_jwe_ex_a1_jwk_json,
+ strlen((char *)lws_jwe_ex_a1_jwk_json)) < 0) {
+ lwsl_notice("Failed to decode JWK test key\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: A.2: Example private keys */
+
+ if (lws_jwk_import(&jwk, key_import_callback, NULL,
+ (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) {
+ lwsl_notice("Failed at A.2\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: A.3: Example symmetric keys */
+
+ if (lws_jwk_import(&jwk, key_import_callback, NULL,
+ (char *)lws_jwe_ex_a3_jwk_json,
+ strlen((char *)lws_jwe_ex_a3_jwk_json)) < 0) {
+ lwsl_notice("Failed at A.3\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: B: Example x509 cert chain (no parent JSON) */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_b_jwk_json,
+ strlen((char *)lws_jwe_ex_b_jwk_json)) < 0) {
+ lwsl_notice("Failed at B\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: C.1: Example private key (no parent JSON) */
+
+ if (lws_jwk_import(&jwk, NULL, NULL,
+ (char *)lws_jwe_ex_c1_jwk_json,
+ strlen((char *)lws_jwe_ex_c1_jwk_json)) < 0) {
+ lwsl_notice("Failed at B\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+
+//bail:
+// lws_jwk_destroy(&jwk);
+bail1:
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+
+}
diff --git a/minimal-examples/api-tests/api-test-jose/jws.c b/minimal-examples/api-tests/api-test-jose/jws.c
new file mode 100644
index 000000000..d82b0ca78
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/jws.c
@@ -0,0 +1,248 @@
+/*
+ * lws-api-test-jose - RFC7515 jws tests
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+
+/*
+ * JSON Web Signature is defined in RFC7515
+ *
+ * https://tools.ietf.org/html/rfc7515
+ *
+ * It's basically a way to wrap some JSON with a JSON "header" describing the
+ * crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
+ *
+ * The signature stays with the content, it serves a different purpose than eg
+ * a TLS tunnel to transfer it.
+ *
+ * RFC7518 (JSON Web Algorithms) says for the "alg" names
+ *
+ * | HS256 | HMAC using SHA-256 | Required |
+ * | HS384 | HMAC using SHA-384 | Optional |
+ * | HS512 | HMAC using SHA-512 | Optional |
+ * | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
+ * | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
+ * | | SHA-384 | |
+ * | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
+ * | | SHA-512 | |
+ * | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
+ * | ES384 | ECDSA using P-384 and SHA-384 | Optional |
+ * | ES512 | ECDSA using P-521 and SHA-512 | Optional |
+ *
+ * Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
+ * currently. The "Recommended+" just means it is recommended but will likely
+ * be "very recommended" soon.
+ *
+ * We support HS256/384/512 for symmetric crypto, but the choice for the
+ * asymmetric crypto isn't as easy to make.
+ *
+ * Normally you'd choose the EC option but these are defined to use the
+ * "NIST curves" (RFC7518 3.4) which are believed to be insecure.
+ *
+ * https://safecurves.cr.yp.to/
+ *
+ * For that reason we implement RS256/384/512 for asymmetric.
+ */
+
+static const char
+ *test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
+ *test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
+ *test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
+ " \"http://example.com/is_root\":true}",
+ *test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
+ "ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
+ *key_jwk = "{\"kty\":\"oct\",\r\n"
+ " \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
+ "Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
+ *hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
+ /* the key from worked example in RFC7515 A-1, as a JWK */
+ *rfc7515_rsa_key =
+ "{\"kty\":\"RSA\","
+ " \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
+ "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
+ "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
+ "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
+ "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
+ "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
+ "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
+ "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
+ "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
+ "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
+ "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
+ "\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
+ "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
+ "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
+ "\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
+ "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
+ "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
+ "\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
+ "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
+ "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
+ "\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
+ "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
+ "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
+ "\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
+ "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
+ "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
+ "}",
+ *rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
+ "eyJhbGciOiJSUzI1NiJ9"
+ ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
+ "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+ ".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
+ "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
+ "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
+ "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
+ "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
+ "p0igcN_IoypGlUPQGe77Rw";
+
+/*
+ * These are the inputs and outputs from the worked example in RFC7515
+ * Appendix A.1.
+ *
+ * 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
+ * a fixed BASE64URL result.
+ *
+ * 2) has a fixed header + payload and is signed with a key given in JWK format
+ */
+int
+test_jws(struct lws_context *context)
+{
+ struct lws_genhmac_ctx ctx;
+ struct lws_jwk jwk;
+ char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ int n;
+
+ /* Test 1: SHA256 on RFC7515 worked example */
+
+ /* 1.1: decode the JWK oct key */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, key_jwk, strlen(key_jwk)) < 0) {
+ lwsl_notice("Failed to decode JWK test key\n");
+ return -1;
+ }
+ if (jwk.kty != LWS_JWK_KYT_OCT) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
+
+ return -1;
+ }
+
+ /* 1.2: create JWS known hdr + known payload */
+
+ n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(buf, test1_enc))
+ goto bail;
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, test2_enc))
+ goto bail;
+
+ /* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
+
+ if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
+ jwk.e[JWK_OCT_KEYEL_K].buf,
+ jwk.e[JWK_OCT_KEYEL_K].len))
+ goto bail;
+ if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
+ goto bail_destroy_hmac;
+ lws_genhmac_destroy(&ctx, digest);
+
+ /* 1.4: append a base64 encode of the computed HMAC digest */
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, hash_enc)) { /* check against known B64URL hash */
+ lwsl_err("%s: b64 enc of computed HMAC mismatches '%s' '%s'\n",
+ __func__, enc_ptr, hash_enc);
+ goto bail;
+ }
+
+ /* 1.5: Check we can agree the signature matches the payload */
+
+ if (lws_jws_confirm_sig(buf, p - buf, &jwk, context) < 0) {
+ lwsl_notice("confirm sig failed\n");
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk); /* finished with the key from the first test */
+
+ /* Test 2: RSA256 on RFC7515 worked example */
+
+ /* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
+
+ if (lws_jwk_import(&jwk, NULL, NULL,
+ rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
+ lwsl_notice("%s: 2.2: Failed to read JWK key\n", __func__);
+ goto bail2;
+ }
+
+ if (jwk.kty != LWS_JWK_KYT_RSA) {
+ lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty);
+ }
+
+ /* 2.2: check the signature on the test packet from RFC7515 A-1 */
+
+ if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
+ &jwk, context) < 0) {
+ lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
+ goto bail;
+ }
+
+ /* 2.3: generate our own signature for a copy of the test packet */
+
+ memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
+
+ /* set p to second . */
+ p = strchr(buf + 1, '.');
+ p1 = strchr(p + 1, '.');
+
+ n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
+ p1 + 1, sizeof(buf) - (p1 - buf) - 1,
+ LWS_GENHASH_TYPE_SHA256, &jwk, context);
+ if (n < 0) {
+ lwsl_err("%s: failed signing test packet\n", __func__);
+ goto bail;
+ }
+
+ // puts(buf);
+
+ /* 2.4: confirm our signature can be verified */
+
+ if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk, context) < 0) {
+ lwsl_notice("confirm rsa sig 2 failed\n");
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+
+bail_destroy_hmac:
+ lws_genhmac_destroy(&ctx, NULL);
+
+bail:
+ lws_jwk_destroy(&jwk);
+bail2:
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+}
diff --git a/minimal-examples/api-tests/api-test-jose/main.c b/minimal-examples/api-tests/api-test-jose/main.c
new file mode 100644
index 000000000..3a5b27b0e
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/main.c
@@ -0,0 +1,51 @@
+/*
+ * lws-api-test-jose
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+
+int
+test_jwk(struct lws_context *context);
+int
+test_jws(struct lws_context *context);
+int
+test_jwe(struct lws_context *context);
+
+int main(int argc, const char **argv)
+{
+ struct lws_context_creation_info info;
+ struct lws_context *context;
+ const char *p;
+ int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS JOSE api tests\n");
+
+ memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+ info.port = CONTEXT_PORT_NO_LISTEN;
+ info.options = 0;
+
+ context = lws_create_context(&info);
+ if (!context) {
+ lwsl_err("lws init failed\n");
+ return 1;
+ }
+
+ result |= test_jwk(context);
+ result |= test_jws(context);
+ result |= test_jwe(context);
+
+ lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS");
+
+ lws_context_destroy(context);
+
+ return result;
+}
diff --git a/minimal-examples/api-tests/api-test-jose/selftest.sh b/minimal-examples/api-tests/api-test-jose/selftest.sh
new file mode 100755
index 000000000..16d1e2e8e
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/selftest.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# $1: path to minimal example binaries...
+# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1
+# that will be ./bin from your build dir
+#
+# $2: path for logs and results. The results will go
+# in a subdir named after the directory this script
+# is in
+#
+# $3: offset for test index count
+#
+# $4: total test count
+#
+# $5: path to ./minimal-examples dir in lws
+#
+# Test return code 0: OK, 254: timed out, other: error indication
+
+. $5/selftests-library.sh
+
+COUNT_TESTS=1
+
+dotest $1 $2 apiselftest
+exit $FAILS
diff --git a/plugins/acme-client/protocol_lws_acme_client.c b/plugins/acme-client/protocol_lws_acme_client.c
index c10b411db..c30056c24 100644
--- a/plugins/acme-client/protocol_lws_acme_client.c
+++ b/plugins/acme-client/protocol_lws_acme_client.c
@@ -435,13 +435,14 @@ lws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd,
{
int n;
- if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH]))
+ if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH],
+ NULL, NULL))
return 0;
- strcpy(vhd->jwk.keytype, "RSA");
+ vhd->jwk.kty = LWS_JWK_KYT_RSA;
lwsl_notice("Generating ACME %d-bit keypair... "
"will take a little while\n", bits);
- n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, &vhd->jwk.el,
+ n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, vhd->jwk.e,
bits);
if (n) {
lwsl_notice("failed to create keypair\n");
@@ -786,7 +787,8 @@ pkt_add_hdrs:
ac->replay_nonce,
&ac->buf[LWS_PRE],
sizeof(ac->buf) -
- LWS_PRE);
+ LWS_PRE,
+ lws_get_context(wsi));
if (ac->len < 0) {
ac->len = 0;
lwsl_notice("lws_jws_create_packet failed\n");
diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c
index 980e4eb3d..baa79e80e 100644
--- a/plugins/ssh-base/sshd.c
+++ b/plugins/ssh-base/sshd.c
@@ -548,7 +548,7 @@ lws_ssh_exec_finish(void *finish_handle, int retcode)
static int
lws_ssh_parse_plaintext(struct per_session_data__sshd *pss, uint8_t *p, size_t len)
{
- struct lws_genrsa_elements el;
+ struct lws_jwk_elements e[LWS_COUNT_RSA_KEY_ELEMENTS];
struct lws_genrsa_ctx ctx;
struct lws_ssh_channel *ch;
struct lws_subprotocol_scp *scp;
@@ -1247,19 +1247,19 @@ again:
* the E and N factors
*/
- memset(&el, 0, sizeof(el));
+ memset(e, 0, sizeof(e));
pp = pss->ua->pubkey;
m = lws_g32(&pp);
pp += m;
m = lws_g32(&pp);
- el.e[JWK_KEY_E].buf = pp;
- el.e[JWK_KEY_E].len = m;
+ e[JWK_RSA_KEYEL_E].buf = pp;
+ e[JWK_RSA_KEYEL_E].len = m;
pp += m;
m = lws_g32(&pp);
- el.e[JWK_KEY_N].buf = pp;
- el.e[JWK_KEY_N].len = m;
+ e[JWK_RSA_KEYEL_N].buf = pp;
+ e[JWK_RSA_KEYEL_N].len = m;
- if (lws_genrsa_create(&ctx, &el))
+ if (lws_genrsa_create(&ctx, e, pss->vhd->context))
goto ua_fail;
/*
diff --git a/scripts/libwebsockets.spec b/scripts/libwebsockets.spec
index 84790b1cb..dd90a4fce 100644
--- a/scripts/libwebsockets.spec
+++ b/scripts/libwebsockets.spec
@@ -110,6 +110,7 @@ rm -rf $RPM_BUILD_ROOT
"/usr/include/libwebsockets/lws-genhash.h"
"/usr/include/libwebsockets/lws-genrsa.h"
"/usr/include/libwebsockets/lws-http.h"
+"/usr/include/libwebsockets/lws-jose.h"
"/usr/include/libwebsockets/lws-jwk.h"
"/usr/include/libwebsockets/lws-jws.h"
"/usr/include/libwebsockets/lws-lejp.h"