diff --git a/.gitignore b/.gitignore
index 19199b1b7..5805b27d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,3 +51,4 @@ doc
/build3/
/cov-int/
/.vs/
+/build-mtls/
diff --git a/.travis.yml b/.travis.yml
index d7fa222e2..fb94419c6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,10 +4,10 @@ env:
global:
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
matrix:
- - LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_JWS=1 -DLWS_WITH_GENRSA=1 -DLWS_WITH_GENHASH=1 -DLWS_WITH_GENAES=1 -DLWS_WITH_GENEC=1"
+ - LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1"
- LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/"
- LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
- - LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
+ - LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_JOSE=1 -DCMAKE_BUILD_TYPE=DEBUG"
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 088cb1c26..63bc25cc8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -105,12 +105,8 @@ option(LWS_WITH_SMTP "Provide SMTP support" OFF)
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_GENAES "Enable support for Generic AES (AES 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_JOSE "JSON Web Signature / Encryption / Keys (RFC7515/6/) API" OFF)
+option(LWS_WITH_GENCRYPTO "Enable support for Generic Crypto apis 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)
@@ -159,11 +155,8 @@ 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)
- set(LWS_WITH_JWS 1)
- set(LWS_WITH_JWE 1)
+ set(LWS_WITH_GENCRYPTO 1)
+ set(LWS_WITH_JOSE 1)
endif()
# do you care about this? Then send me a patch where it disables it on travis
@@ -292,6 +285,11 @@ if (LWS_WITH_LWSWS)
set(LWS_ROLE_RAW_PROXY 1)
endif()
+# sshd plugin
+if (LWS_WITH_PLUGINS)
+ set(LWS_WITH_GENCRYPTO 1)
+endif()
+
if (LWS_ROLE_RAW_PROXY)
set (LWS_WITHOUT_CLIENT 0)
set (LWS_WITHOUT_SERVER 0)
@@ -300,19 +298,12 @@ endif()
if (LWS_WITH_ACME)
set (LWS_WITHOUT_CLIENT 0)
set (LWS_WITHOUT_SERVER 0)
- set (LWS_WITH_JWS 1)
- set (LWS_WITH_JWE 1)
+ set (LWS_WITH_JOSE 1)
endif()
-if (LWS_WITH_JWE)
- set(LWS_WITH_JWS 1)
-endif()
-
-if (LWS_WITH_JWS)
+if (LWS_WITH_JOSE)
set(LWS_WITH_LEJP 1)
- set(LWS_WITH_GENHASH 1)
- set(LWS_WITH_GENRSA 1)
- set(LWS_WITH_GENEC 1)
+ set(LWS_WITH_GENCRYPTO 1)
endif()
if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
@@ -327,8 +318,7 @@ endif()
if (LWS_WITH_PLUGINS OR LWS_WITH_CGI)
# sshd plugin
- set(LWS_WITH_GENHASH 1)
- set(LWS_WITH_GENRSA 1)
+ set(LWS_WITH_GENCRYPTO 1)
endif()
if (LWS_WITH_GENERIC_SESSIONS)
@@ -1025,29 +1015,13 @@ if (LWS_WITH_SSL)
list(APPEND SOURCES
lib/tls/mbedtls/ssl.c
)
- if (LWS_WITH_GENHASH)
+ if (LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
lib/tls/mbedtls/lws-genhash.c
- )
- endif()
- if (LWS_WITH_GENRSA)
- list(APPEND SOURCES
lib/tls/mbedtls/lws-genrsa.c
- )
- endif()
- if (LWS_WITH_GENAES)
- list(APPEND SOURCES
lib/tls/mbedtls/lws-genaes.c
- )
- endif()
- if (LWS_WITH_GENEC)
- list(APPEND SOURCES
lib/tls/lws-genec-common.c
lib/tls/mbedtls/lws-genec.c
- )
- endif()
- if (LWS_WITH_GENEC OR LWS_WITH_GENRSA)
- list(APPEND SOURCES
lib/tls/mbedtls/lws-gencrypto.c
)
endif()
@@ -1055,29 +1029,13 @@ if (LWS_WITH_SSL)
list(APPEND SOURCES
lib/tls/openssl/ssl.c
)
- if (LWS_WITH_GENHASH)
+ if (LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
lib/tls/openssl/lws-genhash.c
- )
- endif()
- if (LWS_WITH_GENRSA)
- list(APPEND SOURCES
lib/tls/openssl/lws-genrsa.c
- )
- endif()
- if (LWS_WITH_GENAES)
- list(APPEND SOURCES
lib/tls/openssl/lws-genaes.c
- )
- endif()
- if (LWS_WITH_GENEC)
- list(APPEND SOURCES
lib/tls/lws-genec-common.c
lib/tls/openssl/lws-genec.c
- )
- endif()
- if (LWS_WITH_GENEC OR LWS_WITH_GENRSA)
- list(APPEND SOURCES
lib/tls/openssl/lws-gencrypto.c
)
endif()
@@ -1235,19 +1193,19 @@ if (LWS_WITH_ZIP_FOPS)
endif()
endif()
-if (LWS_WITH_JWS)
+if (LWS_WITH_JOSE)
list(APPEND SOURCES
lib/jose/jwk/jwk.c
lib/jose/jws/jose.c
- lib/jose/jws/jws.c)
+ lib/jose/jws/jws.c
+ lib/jose/jwe/jwe.c
+ lib/jose/jwe/jwe-rsa-aescbc.c
+ lib/jose/jwe/jwe-aeskw.c
+ lib/jose/jwe/jwe-rsa-aesgcm.c
+ )
endif()
-if (LWS_WITH_JWE)
- list(APPEND SOURCES
- lib/jose/jwe/jwe.c)
-endif()
-
-if (LWS_WITH_JWE OR LWS_WITH_JWS OR LWS_WITH_GENHASH)
+if (LWS_WITH_JOSE OR LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
lib/tls/lws-gencrypto-common.c)
endif()
@@ -1685,6 +1643,9 @@ CHECK_FUNCTION_EXISTS(SSL_CTX_set_ciphersuites LWS_HAVE_SSL_CTX_set_ciphersuites
if (LWS_WITH_SSL AND NOT LWS_WITH_MBEDTLS)
CHECK_SYMBOL_EXISTS(SSL_CTX_get_extra_chain_certs_only openssl/ssl.h LWS_HAVE_SSL_EXTRA_CHAIN_CERTS)
CHECK_FUNCTION_EXISTS(EVP_MD_CTX_free LWS_HAVE_EVP_MD_CTX_free)
+CHECK_FUNCTION_EXISTS(ECDSA_SIG_set0 LWS_HAVE_ECDSA_SIG_set0)
+CHECK_FUNCTION_EXISTS(BN_bn2binpad LWS_HAVE_BN_bn2binpad)
+CHECK_FUNCTION_EXISTS(EVP_aes_128_wrap LWS_HAVE_EVP_aes_128_wrap)
endif()
if (LWS_WITH_MBEDTLS)
set(LWS_HAVE_TLS_CLIENT_METHOD 1)
diff --git a/README.md b/README.md
index 958641f70..0fc8526dd 100644
--- a/README.md
+++ b/README.md
@@ -11,14 +11,56 @@ cloud serving.
[50 minimal examples](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples) for
various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to get started quickly.
-
+
News
----
## New features on master
- - **`lws-genec` ECDH + ECDSA** - Work in progress
+ - CMake config simplification for crypto: `-DLWS_WITH_GENCRYPTO=1` for all
+ generic cipher and hash apis built (which work the same on mbedtls and
+ OpenSSL transparently), and `-DLWS_WITH_JOSE=1` for all JOSE, JWK, JWS
+ and JWE support built (which use gencrypto and so also work the same
+ regardless of tls library backend).
+
+ - **`JWE`** - JWE (RFC7516) Work in progress: Working CI tests
+
+|Key Encryption|Payload authentication + crypt|Enc + Dec Support|
+|---|---|---|
+|`RSAES-PKCS1-v1.5` 2048b & 4096b|`AES_128_CBC_HMAC_SHA_256`|Enc + Dec|
+|`RSAES-PKCS1-v1.5` 2048b|`AES_192_CBC_HMAC_SHA_384`|Enc + Dec|
+|`RSAES-PKCS1-v1.5` 2048b|`AES_256_CBC_HMAC_SHA_512`|Enc + Dec|
+|`RSAES-OAEP`|`AES_256_GCM`|Enc + Dec|
+|`AES128KW`, `AES192KW`, `AES256KW`|`AES_128_CBC_HMAC_SHA_256`|Enc + Dec|
+|`AES128KW`, `AES192KW`, `AES256KW`|`AES_192_CBC_HMAC_SHA_384`|Enc + Dec|
+|`AES128KW`, `AES192KW`, `AES256KW`|`AES_256_CBC_HMAC_SHA_512`|Enc + Dec|
+|`ECDH-ES` P-256|`AES_128_GCM`|Dec|
+
+All tests pass on both OpenSSL and mbedTLS backends, using keys generated on
+both OpenSSL and mbedTLS in the tests.
+
+A minimal example tool shows how to encrypt and decrypt compact JWE objects
+from the commandline for all supported algorithms.
+
+ [jwe api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jwe.h),
+ [jwe unit tests](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose/jwe.c),
+ [jwe minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
+
+ - **`lws-genec` ECDSA** - JWS-compatible ECDSA is supported on both OpenSSL and mbedtls... Work in progress: ECDH-ES
+
+ - **`JWS`** - JWS (RFC7515) is now supported for none, HS256/384/512, RS256/384/512, and ES256/384/512, on both OpenSSL and mbedtls. There's a minimal example tool that signs and verifies compact
+ representation JWS from stdin.
+ [jws api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jws.h),
+ [jws unit tests](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose/jws.c),
+ [jws minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
+
+ - **`JWK`** - JWK (RFC7517) now supports oct, RSA and EC keys including JSON key
+ arrays on both OpenSSL and mbedtls. A minimal example tool shows how to create
+ new JSON JWK keys to specified parameters from the commandline for all supported
+ ciphers.
+
+ [jwk minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk)
- **`lws-genrsa` OAEP + PSS support** - in addition to PKCS#1 1.5 padding, OAEP and PSS are
now supported on both mbedtls and openssl backends.
@@ -27,7 +69,7 @@ News
backends. Supports CBC, CFB128, CFB8, CTR, ECB, OFB, XTS and GCM variants. Unit tests in CI.
[genaes api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genaes.h),
[api test](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-gencrypto),
- CMake config: `-DLWS_WITH_GENAES=1`
+ CMake config: `-DLWS_WITH_GENCRYPTO=1`
- **http fallback support** - you can specify a role and protocol to apply if non-http or non-tls
packets arrive at an http(s) listen port. For example, you can specify that the new `raw proxy`
diff --git a/READMEs/README.crypto-apis.md b/READMEs/README.crypto-apis.md
index c8a067756..88e4695f5 100644
--- a/READMEs/README.crypto-apis.md
+++ b/READMEs/README.crypto-apis.md
@@ -15,6 +15,19 @@ 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).
+The `JW` apis use the generic apis (`lws_genrsa_`, etc) to get the crypto tasks
+done, so anything they can do you can also get done using the generic apis.
+The main difference is that with the generic apis, you must instantiate the
+correct types and use type-specfic apis. With the `JW` apis, there is only
+one interface for all operations, with the details hidden in the api and
+controlled by the JSON objects.
+
+Because of this, the `JW` apis are often preferred because they give you
+"crypto agility" cheaply... to change your crypto to another supported algorithm
+once it's working, you literally just change your JSON defining the keys and
+JWE or JWS algorithm. (It's up to you to define your policy for which
+combinations are acceptable by querying the parsed JW structs).
+
## Using the generic layer
All the necessary includes are part of `libwebsockets.h`.
@@ -63,7 +76,7 @@ Unit tests for these apis, which serve as usage examples, can be found in [./min
Keys in the JOSE layer use a `struct lws_jwk`, this contains two arrays of
`struct lws_jwk_elements` sized for the worst case (currently RSA). One
array contains the key elements as described for the generic case, and the
-other contains various key metadata taken from JWK JSON.
+other contains various nonencrypted key metadata taken from JWK JSON.
|metadata index|function|
|---|---|
@@ -74,3 +87,6 @@ other contains various key metadata taken from JWK JSON.
|`JWK_META_X5C`|Optional X.509 cert version of the key|
|`JWK_META_ALG`|Optional overall crypto algorithm the key is intended for use with|
+`lws_jwk_destroy()` should be called when the jwk is going out of scope... this
+takes care to zero down any key element data in the jwk.
+
diff --git a/changelog b/changelog
index 4ce593582..8fd650705 100644
--- a/changelog
+++ b/changelog
@@ -1,6 +1,12 @@
Changelog
---------
+ - CHANGE: REMOVED: LWS_WITH_GENRSA, LWS_WITH_GENHASH, LWS_WITH_GENEC,
+ LWS_WITH_GENAES have all been removed and combined into LWS_WITH_GENCRYPTO
+
+ - CHANGE: REMOVED: LWS_WITH_JWS, LWS_WITH_JWE have been removed and combined
+ into LWS_WITH_JOSE
+
v3.1.0
======
diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in
index 7c50c29d1..7c6cc5240 100644
--- a/cmake/lws_config.h.in
+++ b/cmake/lws_config.h.in
@@ -23,7 +23,10 @@
#cmakedefine LWS_HAS_INTPTR_T
#cmakedefine LWS_HAVE__ATOI64
#cmakedefine LWS_HAVE_ATOLL
+#cmakedefine LWS_HAVE_BN_bn2binpad
+#cmakedefine LWS_HAVE_ECDSA_SIG_set0
#cmakedefine LWS_HAVE_EVP_MD_CTX_free
+#cmakedefine LWS_HAVE_EVP_aes_128_wrap
#cmakedefine LWS_HAVE_LIBCAP
#cmakedefine LWS_HAVE_MALLOC_H
#cmakedefine LWS_HAVE_NEW_UV_VERSION_H
@@ -71,17 +74,13 @@
#cmakedefine LWS_WITH_CGI
#cmakedefine LWS_WITH_ESP32
#cmakedefine LWS_WITH_FTS
-#cmakedefine LWS_WITH_GENRSA
-#cmakedefine LWS_WITH_GENHASH
-#cmakedefine LWS_WITH_GENAES
-#cmakedefine LWS_WITH_GENEC
+#cmakedefine LWS_WITH_GENCRYPTO
#cmakedefine LWS_WITH_HTTP2
#cmakedefine LWS_WITH_HTTP_BROTLI
#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_JOSE
#cmakedefine LWS_WITH_LEJP
#cmakedefine LWS_WITH_LIBEV
#cmakedefine LWS_WITH_LIBEVENT
diff --git a/doc-assets/lws-overview.png b/doc-assets/lws-overview.png
new file mode 100644
index 000000000..7355cdfd1
Binary files /dev/null and b/doc-assets/lws-overview.png differ
diff --git a/doc-assets/lws-overview.svg b/doc-assets/lws-overview.svg
deleted file mode 100644
index 7f8e45bcd..000000000
--- a/doc-assets/lws-overview.svg
+++ /dev/null
@@ -1,335 +0,0 @@
-
-
diff --git a/include/libwebsockets.h b/include/libwebsockets.h
index 78491a76b..e5f234691 100644
--- a/include/libwebsockets.h
+++ b/include/libwebsockets.h
@@ -367,6 +367,9 @@ typedef unsigned long long lws_filepos_t;
typedef long long lws_fileofs_t;
typedef uint32_t lws_fop_flags_t;
+#define lws_concat_temp(_t, _l) (_t + sizeof(_t) - _l)
+#define lws_concat_used(_t, _l) (sizeof(_t) - _l)
+
/** struct lws_pollargs - argument structure for all external poll related calls
* passed in via 'in' */
struct lws_pollargs {
@@ -427,8 +430,8 @@ struct lws;
#include
#include
-#include
#include
+#include
#include
#include
diff --git a/include/libwebsockets/lws-genaes.h b/include/libwebsockets/lws-genaes.h
index 1e7c3cdcd..aecb68d31 100644
--- a/include/libwebsockets/lws-genaes.h
+++ b/include/libwebsockets/lws-genaes.h
@@ -46,6 +46,7 @@ enum enum_aes_modes {
LWS_GAESM_OFB,
LWS_GAESM_XTS, /* care... requires double-length key */
LWS_GAESM_GCM,
+ LWS_GAESM_KW,
};
enum enum_aes_operation {
@@ -53,6 +54,11 @@ enum enum_aes_operation {
LWS_GAESO_DEC
};
+enum enum_aes_padding {
+ LWS_GAESP_NO_PADDING,
+ LWS_GAESP_WITH_PADDING
+};
+
/* include/libwebsockets/lws-jwk.h must be included before this */
#define LWS_AES_BLOCKSIZE 128
@@ -99,7 +105,7 @@ struct lws_genaes_ctx {
LWS_VISIBLE LWS_EXTERN int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
- int padding, void *engine);
+ enum enum_aes_padding padding, void *engine);
/** lws_genaes_destroy() - Destroy genaes AES context
*
@@ -119,21 +125,22 @@ lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen);
/** lws_genaes_crypt() - Encrypt or decrypt
*
* \param ctx: your struct lws_genaes_ctx
+ * \param in: input plaintext or ciphertext
+ * \param len: length of input (which is always length of output)
+ * \param out: output plaintext or ciphertext
* \param op: LWS_GAESO_ENC or LWS_GAESO_DEC
* \param iv_or_nonce_ctr_or_data_unit_16: NULL, iv, nonce_ctr16, or data_unit16
* \param stream_block_16: pointer to 16-byte stream block for CTR mode only
* \param nc_or_iv_off: NULL or pointer to nc, or iv_off
- * \param in: input plaintext or ciphertext
- * \param len: length of input (which is always length of output)
- * \param out: output plaintext or ciphertext
+ * \param taglen: length of tag
*
* Encrypts or decrypts using the AES mode set when the ctx was created.
* The last three arguments have different meanings depending on the mode:
*
- * CBC CFB128 CFB8 CTR ECB OFB XTS
- * iv_or_nonce_ctr_or_data_unit_16 : iv iv iv nonce NULL iv dataunt
- * stream_block_16 : NULL NULL NULL stream NULL NULL NULL
- * nc_or_iv_off : NULL iv_off NULL nc_off NULL iv_off NULL
+ * KW CBC CFB128 CFB8 CTR ECB OFB XTS
+ * iv_or_nonce_ct.._unit_16 : iv iv iv iv nonce NULL iv dataunt
+ * stream_block_16 : NULL NULL NULL NULL stream NULL NULL NULL
+ * nc_or_iv_off : NULL NULL iv_off NULL nc_off NULL iv_off NULL
*
* For GCM:
*
diff --git a/include/libwebsockets/lws-gencrypto.h b/include/libwebsockets/lws-gencrypto.h
index ab47ca15f..7a0b4d2ad 100644
--- a/include/libwebsockets/lws-gencrypto.h
+++ b/include/libwebsockets/lws-gencrypto.h
@@ -27,12 +27,12 @@
* no dependency at all on any JOSE type.
*/
-enum lws_gencrypto_kyt {
- LWS_GENCRYPTO_KYT_UNKNOWN,
+enum lws_gencrypto_kty {
+ LWS_GENCRYPTO_KTY_UNKNOWN,
- LWS_GENCRYPTO_KYT_OCT,
- LWS_GENCRYPTO_KYT_RSA,
- LWS_GENCRYPTO_KYT_EC
+ LWS_GENCRYPTO_KTY_OCT,
+ LWS_GENCRYPTO_KTY_RSA,
+ LWS_GENCRYPTO_KTY_EC
};
/*
@@ -92,3 +92,26 @@ struct lws_gencrypto_keyelem {
uint8_t *buf;
uint16_t len;
};
+
+
+/**
+ * lws_gencrypto_bits_to_bytes() - returns rounded up bytes needed for bits
+ *
+ * \param bits
+ *
+ * Returns the number of bytes needed to store the given number of bits. If
+ * a byte is partially used, the byte count is rounded up.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_gencrypto_bits_to_bytes(int bits);
+
+/**
+ * lws_base64_size() - returns estimated size of base64 encoding
+ *
+ * \param bytes
+ *
+ * Returns a slightly oversize estimate of the size of a base64 encoded version
+ * of the given amount of unencoded data.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_base64_size(int bytes);
diff --git a/include/libwebsockets/lws-genec.h b/include/libwebsockets/lws-genec.h
index cd4fb0ac3..75e9243ec 100644
--- a/include/libwebsockets/lws-genec.h
+++ b/include/libwebsockets/lws-genec.h
@@ -143,11 +143,12 @@ LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
struct lws_gencrypto_keyelem *el);
-/** lws_genecdsa_hash_sig_verify() - Verifies ECDSA signature on a given hash
+/** lws_genecdsa_hash_sig_verify_jws() - Verifies a JWS ECDSA signature on a given hash
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: unencrypted payload (usually a recomputed hash)
* \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param keybits: number of bits in the crypto key
* \param sig: pointer to the signature we received with the payload
* \param sig_len: length of the signature we are checking in bytes
*
@@ -158,31 +159,38 @@ lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
*
* Returns <0 for error, or 0 if signature matches the hash + key..
*
+ * The JWS ECDSA signature verification algorithm differs to generic ECDSA
+ * signatures and they're not interoperable.
+ *
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
-lws_genecdsa_hash_sig_verify(struct lws_genec_ctx *ctx, const uint8_t *in,
- enum lws_genhash_types hash_type,
- const uint8_t *sig, size_t sig_len);
+lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ const uint8_t *sig, size_t sig_len);
-/** lws_genecdsa_hash_sign() - Creates an ECDSA signature for a hash you provide
+/** lws_genecdsa_hash_sign_jws() - Creates a JWS ECDSA signature for a hash you provide
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: precomputed hash
* \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param keybits: number of bits in the crypto key
* \param sig: pointer to buffer to take signature
* \param sig_len: length of the buffer (must be >= length of key N)
*
* Returns <0 for error, or 0 for success.
*
- * This creates an ECDSA signature for a hash you already computed and provide.
+ * This creates a JWS ECDSA signature for a hash you already computed and provide.
+ *
+ * The JWS ECDSA signature generation algorithm differs to generic ECDSA
+ * signatures and they're not interoperable.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
-lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
- enum lws_genhash_types hash_type, uint8_t *sig,
- size_t sig_len);
+lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ uint8_t *sig, size_t sig_len);
/* Apis that apply to both ECDH and ECDSA */
diff --git a/include/libwebsockets/lws-genrsa.h b/include/libwebsockets/lws-genrsa.h
index 0f233791d..243439390 100644
--- a/include/libwebsockets/lws-genrsa.h
+++ b/include/libwebsockets/lws-genrsa.h
@@ -53,26 +53,14 @@ struct lws_genrsa_ctx {
enum enum_genrsa_mode mode;
};
-/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements
- *
- * \param el: your struct lws_gencrypto_keyelem
- *
- * This is a helper for user code making use of struct lws_gencrypto_keyelem
- * where the elements are allocated on the heap, it frees any non-NULL
- * buf element and sets the buf to NULL.
- *
- * NB: lws_genrsa_public_... apis do not need this as they take care of the key
- * creation and destruction themselves.
- */
-LWS_VISIBLE LWS_EXTERN void
-lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
-
/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
*
* \param ctx: your struct lws_genrsa_ctx
* \param el: struct prepared with key element data
* \param context: lws_context for RNG
* \param mode: RSA mode, one of LGRSAM_ constants
+ * \param oaep_hashid: the lws genhash id for the hash used in MFG1 hash
+ * used in OAEP mode - normally, SHA1
*
* Creates an RSA context with a public key associated with it, formed from
* the key elements in \p el.
@@ -86,7 +74,22 @@ lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
- struct lws_context *context, enum enum_genrsa_mode mode);
+ struct lws_context *context, enum enum_genrsa_mode mode,
+ enum lws_genhash_types oaep_hashid);
+
+/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements
+ *
+ * \param el: your struct lws_gencrypto_keyelem
+ *
+ * This is a helper for user code making use of struct lws_gencrypto_keyelem
+ * where the elements are allocated on the heap, it frees any non-NULL
+ * buf element and sets the buf to NULL.
+ *
+ * NB: lws_genrsa_public_... apis do not need this as they take care of the key
+ * creation and destruction themselves.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
/** lws_genrsa_new_keypair() - Create new RSA keypair
*
@@ -111,7 +114,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el,
int bits);
-/** lws_genrsa_public_encrypt() - Perform RSA public encryption
+/** lws_genrsa_public_encrypt() - Perform RSA public key encryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: plaintext input
@@ -128,7 +131,24 @@ 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
+/** lws_genrsa_private_encrypt() - Perform RSA private key 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_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out);
+
+/** lws_genrsa_public_decrypt() - Perform RSA public key decryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: encrypted input
@@ -146,6 +166,24 @@ LWS_VISIBLE LWS_EXTERN 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);
+/** lws_genrsa_private_decrypt() - Perform RSA private key decryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: encrypted input
+ * \param in_len: length of encrypted input
+ * \param out: decrypted output
+ * \param out_max: size of output buffer
+ *
+ * Performs PKCS1 v1.5 Decryption
+ *
+ * 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_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max);
+
/** lws_genrsa_hash_sig_verify() - Verifies RSA signature on a given hash
*
* \param ctx: your struct lws_genrsa_ctx
diff --git a/include/libwebsockets/lws-jose.h b/include/libwebsockets/lws-jose.h
index 370afeae7..d0e6dea94 100644
--- a/include/libwebsockets/lws-jose.h
+++ b/include/libwebsockets/lws-jose.h
@@ -49,11 +49,6 @@ enum lws_jws_jose_hdr_indexes {
LWS_COUNT_JOSE_HDR_ELEMENTS
};
-struct lws_jose {
- /* jose header elements */
- struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS];
-};
-
enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_NONE,
@@ -62,6 +57,7 @@ enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_ECDSA,
+ LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_AES_CBC,
LWS_JOSE_ENCTYPE_AES_CFB128,
@@ -69,10 +65,12 @@ enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_AES_CTR,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_AES_OFB,
- LWS_JOSE_ENCTYPE_AES_XTS, /* care... requires double-length key */
+ LWS_JOSE_ENCTYPE_AES_XTS, /* care: requires double-length key */
LWS_JOSE_ENCTYPE_AES_GCM,
};
+/* there's a table of these defined in lws-gencrypto-common.c */
+
struct lws_jose_jwe_alg {
enum lws_genhash_types hash_type;
enum lws_genhmac_types hmac_type;
@@ -80,16 +78,97 @@ struct lws_jose_jwe_alg {
enum lws_jose_algtype algtype_crypto; /* the encryption cipher */
const char *alg; /* the JWA enc alg name, eg "ES512" */
const char *curve_name; /* NULL, or, eg, "P-256" */
+ unsigned short keybits_min, keybits_fixed;
+ unsigned short ivbits;
};
+struct lws_jose {
+ /* jose header elements */
+ struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS];
+ struct lws_jwk jwk_ephemeral;
+ const struct lws_jose_jwe_alg *alg;
+ const struct lws_jose_jwe_alg *enc_alg;
+};
+
+/**
+ * lws_jose_init() - prepare a struct lws_jose for use
+ *
+ * \param jose: the jose header struct to prepare
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jose_init(struct lws_jose *jose);
+
+/**
+ * lws_jose_destroy() - retire a struct lws_jose from use
+ *
+ * \param jose: the jose header struct to destroy
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jose_destroy(struct lws_jose *jose);
+
+/**
+ * lws_gencrypto_jws_alg_to_definition() - look up a jws alg name
+ *
+ * \param alg: the jws alg name
+ * \param jose: pointer to the pointer to the info struct to set on success
+ *
+ * Returns 0 if *jose set, else nonzero for failure
+ */
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jws_alg_to_definition(const char *alg,
const struct lws_jose_jwe_alg **jose);
+/**
+ * lws_gencrypto_jwe_alg_to_definition() - look up a jwe alg name
+ *
+ * \param alg: the jwe alg name
+ * \param jose: pointer to the pointer to the info struct to set on success
+ *
+ * Returns 0 if *jose set, else nonzero for failure
+ */
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jwe_alg_to_definition(const char *alg,
const struct lws_jose_jwe_alg **jose);
+/**
+ * lws_gencrypto_jwe_enc_to_definition() - look up a jwe enc name
+ *
+ * \param alg: the jwe enc name
+ * \param jose: pointer to the pointer to the info struct to set on success
+ *
+ * Returns 0 if *jose set, else nonzero for failure
+ */
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jwe_enc_to_definition(const char *enc,
const struct lws_jose_jwe_alg **jose);
+
+/**
+ * lws_jws_parse_jose() - parse a JWS JOSE header
+ *
+ * \param jose: the jose struct to set to parsing results
+ * \param buf: the raw JOSE header
+ * \param len: the length of the raw JOSE header
+ * \param temp: parent-owned buffer to "allocate" elements into
+ * \param temp_len: amount of space available in temp
+ *
+ * returns the amount of temp used, or -1 for error
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_parse_jose(struct lws_jose *jose,
+ const char *buf, int len, char *temp, int *temp_len);
+
+/**
+ * lws_jwe_parse_jose() - parse a JWE JOSE header
+ *
+ * \param jose: the jose struct to set to parsing results
+ * \param buf: the raw JOSE header
+ * \param len: the length of the raw JOSE header
+ * \param temp: parent-owned buffer to "allocate" elements into
+ * \param temp_len: amount of space available in temp
+ *
+ * returns the amount of temp used, or -1 for error
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_parse_jose(struct lws_jose *jose,
+ const char *buf, int len, char *temp, int *temp_len);
+
diff --git a/include/libwebsockets/lws-jwe.h b/include/libwebsockets/lws-jwe.h
index 1422a27bd..3e568be8b 100644
--- a/include/libwebsockets/lws-jwe.h
+++ b/include/libwebsockets/lws-jwe.h
@@ -19,8 +19,27 @@
* MA 02110-1301 USA
*
* included from libwebsockets.h
+ *
+ * JWE Compact Serialization consists of
+ *
+ * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
+ * BASE64URL(JWE Encrypted Key) || '.' ||
+ * BASE64URL(JWE Initialization Vector) || '.' ||
+ * BASE64URL(JWE Ciphertext) || '.' ||
+ * BASE64URL(JWE Authentication Tag)
*/
+#define LWS_JWE_RFC3394_OVERHEAD_BYTES 8
+#define LWS_JWE_AES_IV_BYTES 16
+
+#define LWS_JWE_LIMIT_RSA_KEY_BITS 4096
+#define LWS_JWE_LIMIT_AES_KEY_BITS (512 + 64) /* RFC3394 Key Wrap adds 64b */
+#define LWS_JWE_LIMIT_EC_KEY_BITS 528 /* 521 rounded to byte boundary */
+#define LWS_JWE_LIMIT_HASH_BITS (LWS_GENHASH_LARGEST * 8)
+
+/* the largest key element for any cipher */
+#define LWS_JWE_LIMIT_KEY_ELEMENT_BYTES (LWS_JWE_LIMIT_RSA_KEY_BITS / 8)
+
/**
* lws_jwe_create_packet() - add b64 sig to b64 hdr + payload
*
@@ -43,7 +62,78 @@
* Returns the length written to \p out, or -1.
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwe_create_packet(struct lws_jwk *jwk,
- const struct lws_jose_jwe_alg *jose_alg,
+lws_jwe_create_packet(struct lws_jose *jose, struct lws_jwk *jwk,
const char *payload, size_t len, const char *nonce,
char *out, size_t out_len, struct lws_context *context);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_jwe_be64(uint64_t c, uint8_t *p8);
+
+/*
+ * JWE Compact Serialization consists of
+ *
+ * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
+ * BASE64URL(JWE Encrypted Key) || '.' ||
+ * BASE64URL(JWE Initialization Vector) || '.' ||
+ * BASE64URL(JWE Ciphertext) || '.' ||
+ * BASE64URL(JWE Authentication Tag)
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_write_compact(struct lws_jose *jose, struct lws_jws *jws,
+ char *out, size_t out_len);
+
+
+/**
+ * lws_jwe_auth_and_decrypt() - confirm and decrypt JWE
+ *
+ * \param jose: jose context
+ * \param jws: jws / jwe context... .map and .map_b64 must be filled already
+ *
+ * This is a high level JWE decrypt api that takes a jws with the maps
+ * already processed, and if the authentication passes, returns the decrypted
+ * plaintext in jws.map.buf[LJWE_CTXT] and its length in jws.map.len[LJWE_CTXT].
+ *
+ * In the jws, the following fields must have been set by the caller
+ *
+ * .context
+ * .jwk (the key encryption key)
+ * .map
+ * .map_b64
+ *
+ * Having the b64 and decoded maps filled externally makes it flexible where
+ * the data was picked from, eg, from a Complete JWE JSON serialization, a
+ * flattened one, or a Compact Serialization.
+ *
+ * Returns decrypt length, or -1 for failure.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_auth_and_decrypt(struct lws_jose *jose, struct lws_jws *jws);
+
+
+
+/* only exposed because we have test vectors that need it */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws, uint8_t *enc_cek,
+ uint8_t *aad, int aad_len);
+
+/* only exposed because we have test vectors that need it */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwa_concat_kdf(struct lws_jose *jose, struct lws_jws *jws, int direct,
+ uint8_t *out, const uint8_t *shared_secret, int sslen);
+
+
+/**
+ * lws_jwe_encrypt() - perform JWE encryption
+ *
+ * \param jose: the JOSE header information (encryption types, etc)
+ * \param jws: the JWE elements, pointer to jwk etc
+ * \param temp: parent-owned buffer to "allocate" elements into
+ * \param temp_len: amount of space available in temp
+ *
+ * returns the amount of temp used, or -1 for error
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_encrypt(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len);
diff --git a/include/libwebsockets/lws-jwk.h b/include/libwebsockets/lws-jwk.h
index 6cf159647..e2a001ca9 100644
--- a/include/libwebsockets/lws-jwk.h
+++ b/include/libwebsockets/lws-jwk.h
@@ -52,13 +52,23 @@ struct lws_jwk {
/* generic meta key elements, like KID */
struct lws_gencrypto_keyelem meta[LWS_COUNT_JWK_ELEMENTS];
int kty; /**< one of LWS_JWK_ */
+ char private_key; /* nonzero = has private key elements */
};
typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
+struct lws_jwk_parse_state {
+ struct lws_jwk *jwk;
+ char b64[(((8192 / 8) * 4) / 3) + 1]; /* enough for 8Kb key */
+ lws_jwk_key_import_callback per_key_cb;
+ void *user;
+ int pos;
+ unsigned short possible;
+};
+
/** lws_jwk_import() - Create a JSON Web key from the textual representation
*
- * \param s: the JWK object to create
+ * \param jwk: 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.
@@ -80,21 +90,21 @@ typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
* iteration through any further keys).
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
+lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
const char *in, size_t len);
/** lws_jwk_destroy() - Destroy a JSON Web key
*
- * \param s: the JWK object to destroy
+ * \param jwk: the JWK object to destroy
*
* All allocations in the lws_jwk are destroyed
*/
LWS_VISIBLE LWS_EXTERN void
-lws_jwk_destroy(struct lws_jwk *s);
+lws_jwk_destroy(struct lws_jwk *jwk);
/** lws_jwk_export() - Export a JSON Web key to a textual representation
*
- * \param s: the JWK object to export
+ * \param jwk: the JWK object to export
* \param _private: 0 = just export public parts, 1 = export everything
* \param p: the buffer to write the exported JWK to
* \param len: the length of the buffer \p p in bytes
@@ -104,11 +114,11 @@ lws_jwk_destroy(struct lws_jwk *s);
* Serializes the content of the JWK into a char buffer.
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
+lws_jwk_export(struct lws_jwk *jwk, int _private, char *p, size_t len);
/** lws_jwk_load() - Import a JSON Web key from a file
*
- * \param s: the JWK object to load into
+ * \param jwk: the JWK object to load into
* \param filename: filename to load from
*
* Returns 0 for OK or -1 for failure
@@ -125,29 +135,58 @@ lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
* iteration through any further keys, leaving the last one in s).
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_load(struct lws_jwk *s, const char *filename,
+lws_jwk_load(struct lws_jwk *jwk, const char *filename,
lws_jwk_key_import_callback cb, void *user);
/** lws_jwk_save() - Export a JSON Web key to a file
*
- * \param s: the JWK object to save from
+ * \param jwk: the JWK object to save from
* \param filename: filename to save to
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_save(struct lws_jwk *s, const char *filename);
+lws_jwk_save(struct lws_jwk *jwk, const char *filename);
/** lws_jwk_rfc7638_fingerprint() - jwk to RFC7638 compliant fingerprint
*
- * \param s: the JWK object to fingerprint
+ * \param jwk: the JWK object to fingerprint
* \param digest32: buffer to take 32-byte digest
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32);
+lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32);
+
+/** lws_jwk_strdup_meta() - allocate a duplicated string meta element
+ *
+ * \param jwk: the JWK object to fingerprint
+ * \param idx: JWK_META_ element index
+ * \param in: string to copy
+ * \param len: length of string to copy
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
+ const char *in, int len);
+
LWS_VISIBLE LWS_EXTERN int
-lws_jwk_dump(struct lws_jwk *s);
+lws_jwk_dump(struct lws_jwk *jwk);
+
+/** lws_jwk_generate() - create a new key of given type and characteristics
+ *
+ * \param context: the struct lws_context used for RNG
+ * \param jwk: the JWK object to fingerprint
+ * \param kty: One of the LWS_GENCRYPTO_KTY_ key types
+ * \param bits: for OCT and RSA keys, the number of bits
+ * \param curve: for EC keys, the name of the curve
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE int
+lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
+ enum lws_gencrypto_kty kty, int bits, const char *curve);
+
///@}
diff --git a/include/libwebsockets/lws-jws.h b/include/libwebsockets/lws-jws.h
index f7d0c13a3..9e2db5213 100644
--- a/include/libwebsockets/lws-jws.h
+++ b/include/libwebsockets/lws-jws.h
@@ -29,26 +29,144 @@
* SHA256/384/512 HMAC, and RSA 256/384/512 are supported.
*
* The API uses your TLS library crypto, but works exactly the same no matter
- * what you TLS backend is.
+ * what your TLS backend is.
*/
///@{
+/*
+ * The maps are built to work with both JWS (LJWS_) and JWE (LJWE_), and are
+ * sized to the slightly larger JWE case.
+ */
+
+enum enum_jws_sig_elements {
+
+ /* JWS block namespace */
+ LJWS_JOSE,
+ LJWS_PYLD,
+ LJWS_SIG,
+ LJWS_UHDR,
+
+ /* JWE block namespace */
+ LJWE_JOSE = 0,
+ LJWE_EKEY,
+ LJWE_IV,
+ LJWE_CTXT,
+ LJWE_ATAG,
+
+ LWS_JWS_MAX_COMPACT_BLOCKS
+};
+
+struct lws_jws_compact_map {
+ const char *buf[LWS_JWS_MAX_COMPACT_BLOCKS];
+ uint16_t len[LWS_JWS_MAX_COMPACT_BLOCKS];
+};
+
+struct lws_jws {
+ struct lws_jwk *jwk; /* the struct lws_jwk containing the signing key */
+ struct lws_context *context; /* the lws context (used to get random) */
+ struct lws_jws_compact_map map, map_b64;
+};
+
+/* jws EC signatures do not have ASN.1 in them, meaning they're incompatible
+ * with generic signatures.
+ */
+
+/**
+ * lws_jws_init() - initialize a jws for use
+ *
+ * \param jws: pointer to the jws to initialize
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
+ struct lws_context *context);
+
+/**
+ * lws_jws_destroy() - scrub a jws
+ *
+ * \param jws: pointer to the jws to destroy
+ *
+ * Call before the jws goes out of scope.
+ *
+ * Elements defined in the jws are zeroed.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jws_destroy(struct lws_jws *jws);
+
+/**
+ * lws_jws_sig_confirm_compact() - check signature
+ *
+ * \param map: pointers and lengths for each of the unencoded JWS elements
+ * \param jwk: public key
+ * \param content: lws_context
+ *
+ * Confirms the signature on a JWS. Use if you have non-b64 plain JWS elements
+ * in a map... it'll make a temp b64 version needed for comparison. See below
+ * for other variants.
+ *
+ * Returns 0 on match.
+ */
LWS_VISIBLE LWS_EXTERN int
-lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
- struct lws_context *context);
+lws_jws_sig_confirm_compact(struct lws_jws_compact_map *map, struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm_compact_b64_map(struct lws_jws_compact_map *map_b64,
+ struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+/**
+ * lws_jws_sig_confirm_compact_b64() - check signature on b64 compact JWS
+ *
+ * \param in: pointer to b64 jose.payload[.hdr].sig
+ * \param len: bytes available at \p in
+ * \param map: map to take decoded non-b64 content
+ * \param jwk: public key
+ * \param content: lws_context
+ *
+ * Confirms the signature on a JWS. Use if you have you have b64 compact layout
+ * (jose.payload.hdr.sig) as an aggregated string... it'll make a temp plain
+ * version needed for comparison.
+ *
+ * Returns 0 on match.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm_compact_b64(const char *in, size_t len,
+ struct lws_jws_compact_map *map,
+ struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+/**
+ * lws_jws_sig_confirm() - check signature on plain + b64 JWS elements
+ *
+ * \param map_b64: pointers and lengths for each of the b64-encoded JWS elements
+ * \param map: pointers and lengths for each of the unencoded JWS elements
+ * \param jwk: public key
+ * \param content: lws_context
+ *
+ * Confirms the signature on a JWS. Use if you have you already have both b64
+ * compact layout (jose.payload.hdr.sig) and decoded JWS elements in maps.
+ *
+ * If you had the b64 string and called lws_jws_compact_decode() on it, you
+ * will end up with both maps, and can use this api version, saving needlessly
+ * regenerating any temp map.
+ *
+ * Returns 0 on match.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm(struct lws_jws_compact_map *map_b64, /* b64-encoded */
+ struct lws_jws_compact_map *map, /* non-b64 */
+ struct lws_jwk *jwk, struct lws_context *context);
/**
* lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
*
- * \param b64_hdr: protected header encoded in b64, may be NULL
- * \param hdr_len: bytes in b64 coding of protected header
- * \param b64_pay: payload encoded in b64
- * \param pay_len: bytes in b64 coding of payload
- * \param b64_sig: buffer to write the b64 encoded signature into
- * \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)
+ * \param jose: jose header information
+ * \param jws: information to include in the signature
+ * \param b64_sig: output buffer for b64 signature
+ * \param sig_len: size of \p b64_sig output buffer
*
* 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,11 +180,181 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
* Returns the length of the encoded signature written to \p b64_sig, or -1.
*/
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,
- const struct lws_jose_jwe_alg *args,
- struct lws_jwk *jwk,
- struct lws_context *context);
+lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws, char *b64_sig,
+ size_t sig_len);
+
+/**
+ * lws_jws_compact_decode() - converts and maps compact serialization b64 sections
+ *
+ * \param in: the incoming compact serialized b64
+ * \param len: the length of the incoming compact serialized b64
+ * \param map: pointer to the results structure
+ * \param map_b64: NULL, or pointer to a second results structure taking block
+ * information about the undecoded b64
+ * \param out: buffer to hold decoded results
+ * \param out_len: size of out in bytes
+ *
+ * Returns number of sections (2 if "none", else 3), or -1 if illegal.
+ *
+ * map is set to point to the start and hold the length of each decoded block.
+ * If map_b64 is non-NULL, then it's set with information about the input b64
+ * blocks.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_compact_decode(const char *in, int len, struct lws_jws_compact_map *map,
+ struct lws_jws_compact_map *map_b64, char *out, int *out_len);
+
+LWS_VISIBLE int
+lws_jws_compact_encode(struct lws_jws_compact_map *map_b64, /* b64-encoded */
+ const struct lws_jws_compact_map *map, /* non-b64 */
+ char *buf, int *out_len);
+
+/**
+ * lws_jws_write_flattened_json() - create flattened JSON sig
+ *
+ * \param jws: information to include in the signature
+ * \param flattened: output buffer for JSON
+ * \param len: size of \p flattened output buffer
+ *
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len);
+
+/**
+ * lws_jws_write_compact() - create flattened JSON sig
+ *
+ * \param jws: information to include in the signature
+ * \param compact: output buffer for compact format
+ * \param len: size of \p flattened output buffer
+ *
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len);
+
+
+
+/*
+ * below apis are not normally needed if dealing with whole JWS... they're
+ * useful for creating from scratch
+ */
+
+
+/**
+ * lws_jws_dup_element() - allocate space for an element and copy data into it
+ *
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param in: data to duplicate into element
+ * \param in_len: length of data to duplicate
+ * \param actual_alloc: 0 for same as in_len, else actual allocation size
+ *
+ * Copies in_len from in to temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... in_len will be copied into it.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_dup_element(struct lws_jws_compact_map *map, int idx,
+ char *temp, int *temp_len, const void *in, size_t in_len,
+ size_t actual_alloc);
+
+/**
+ * lws_jws_randomize_element() - create an element and fill with random
+ *
+ * \param context: lws_context used for random
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param random_len: length of data to fill with random
+ * \param actual_alloc: 0 for same as random_len, else actual allocation size
+ *
+ * Randomize random_len bytes at temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... the first random_len will be filled with random.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_randomize_element(struct lws_context *context,
+ struct lws_jws_compact_map *map,
+ int idx, char *temp, int *temp_len, size_t random_len,
+ size_t actual_alloc);
+
+/**
+ * lws_jws_alloc_element() - create an element and reserve space for content
+ *
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param len: logical length of element
+ * \param actual_alloc: 0 for same as len, else actual allocation size
+ *
+ * Allocate len bytes at temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... the element logical length will be set to len.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_alloc_element(struct lws_jws_compact_map *map, int idx, char *temp,
+ int *temp_len, size_t len, size_t actual_alloc);
+
+/**
+ * lws_jws_encode_b64_element() - create an b64-encoded element
+ *
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param in: pointer to unencoded input
+ * \param in_len: length of unencoded input
+ *
+ * Allocate len bytes at temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... the element logical length will be set to len.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_encode_b64_element(struct lws_jws_compact_map *map, int idx,
+ char *temp, int *temp_len, const void *in,
+ size_t in_len);
+
+
+/**
+ * lws_jws_b64_compact_map() - find block starts and lengths in compact b64
+ *
+ * \param in: pointer to b64 jose.payload[.hdr].sig
+ * \param len: bytes available at \p in
+ * \param map: output struct with pointers and lengths for each JWS element
+ *
+ * Scans a jose.payload[.hdr].sig b64 string and notes where the blocks start
+ * and their length into \p map.
+ *
+ * Returns number of blocks if OK. May return <0 if malformed.
+ * May not fill all map entries.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_compact_map *map);
+
/**
* lws_jws_base64_enc() - encode input data into b64url data
@@ -82,7 +370,8 @@ 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
+ * 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
diff --git a/include/libwebsockets/lws-lejp.h b/include/libwebsockets/lws-lejp.h
index f3f7e59a6..faa44abcf 100644
--- a/include/libwebsockets/lws-lejp.h
+++ b/include/libwebsockets/lws-lejp.h
@@ -257,6 +257,10 @@ LWS_VISIBLE LWS_EXTERN void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason));
+/* exported for use when reevaluating a path for use with a subcontext */
+LWS_VISIBLE LWS_EXTERN void
+lejp_check_path_match(struct lejp_ctx *ctx);
+
LWS_VISIBLE LWS_EXTERN int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
//@}
diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h
index 6bb446193..9d0729b6b 100644
--- a/include/libwebsockets/lws-misc.h
+++ b/include/libwebsockets/lws-misc.h
@@ -330,6 +330,22 @@ lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
LWS_VISIBLE LWS_EXTERN char *
lws_strncpy(char *dest, const char *src, size_t size);
+/*
+ * lws_timingsafe_bcmp(): constant time memcmp
+ *
+ * \param a: first buffer
+ * \param b: second buffer
+ * \param len: count of bytes to compare
+ *
+ * Return 0 if the two buffers are the same, else nonzero.
+ *
+ * Always compares all of the buffer before returning, so it can't be used as
+ * a timing oracle.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len);
+
/**
* lws_get_random(): fill a buffer with platform random data
*
diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c
index 91dc77a43..e9d85ba2e 100644
--- a/lib/core/libwebsockets.c
+++ b/lib/core/libwebsockets.c
@@ -1064,7 +1064,11 @@ lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf,
/* append at the tail */
while (*head) {
- if (!--sanity || head == &((*head)->next)) {
+ if (!--sanity) {
+ lwsl_err("%s: buflist reached sanity limit\n", __func__);
+ return -1;
+ }
+ if (*head == (*head)->next) {
lwsl_err("%s: corrupt list points to self\n", __func__);
return -1;
}
@@ -1097,7 +1101,7 @@ lws_buflist_destroy_segment(struct lws_buflist **head)
struct lws_buflist *old = *head;
assert(*head);
- *head = (*head)->next;
+ *head = old->next;
old->next = NULL;
lws_free(old);
@@ -3142,6 +3146,18 @@ lws_strncpy(char *dest, const char *src, size_t size)
return dest;
}
+int
+lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
+{
+ const uint8_t *pa = a, *pb = b;
+ uint8_t sum = 0;
+
+ while (len--)
+ sum |= (*pa++ ^ *pb++);
+
+ return sum;
+}
+
typedef enum {
LWS_TOKZS_LEADING_WHITESPACE,
diff --git a/lib/core/output.c b/lib/core/output.c
index e58aa45b2..aef2d7b9d 100644
--- a/lib/core/output.c
+++ b/lib/core/output.c
@@ -70,7 +70,8 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
* the buflist...
*/
- lws_buflist_append_segment(&wsi->buflist_out, buf, len);
+ if (lws_buflist_append_segment(&wsi->buflist_out, buf, len))
+ return -1;
buf = NULL;
len = 0;
diff --git a/lib/jose/README.md b/lib/jose/README.md
index 7473c5b82..b9fd5389b 100644
--- a/lib/jose/README.md
+++ b/lib/jose/README.md
@@ -28,26 +28,33 @@ useful implementations of JWS, JWE and JWK.
### Symmetric ciphers
- - All common AES varaiants: CBC, CFB128, CFB8, CTR, EVB, OFB and XTS
+ - All common AES varaiants: CBC, CFB128, CFB8, CTR, EVB, OFB, KW and XTS
### Asymmetric ciphers
- RSA
- - EC (P-256, P-384 and P521 JWA curves)
+ - EC (P-256, P-384 and P-521 JWA curves)
+
+### Payload auth and crypt
+
+ - AES_128_CBC_HMAC_SHA_256
+ - AES_192_CBC_HMAC_SHA_384
+ - AES_256_CBC_HMAC_SHA_512
+ - AES_128_GCM
For the required and recommended asymmetric algorithms, support currently
looks like this
|JWK kty|JWA|lws|
|---|---|---|
-|EC|Recommended+|no|
+|EC|Recommended+|yes|
|RSA|Required|yes|
|oct|Required|yes|
|JWE alg|JWA|lws|
|---|---|---|
-|RSA1_5|Recommended-|yes (no JWE yet but lws_genrsa supports)|
+|RSA1_5|Recommended-|yes|
|RSA-OAEP|Recommended+|no|
|ECDH-ES|Recommended+|no|
@@ -55,7 +62,15 @@ looks like this
|---|---|---|
|HS256|Required|yes|
|RS256|Recommended+|yes|
-|ES256|Recommended|no|
+|ES256|Recommended|yes|
+
+## Minimal Example tools
+
+[JWK](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk)
+
+[JWS](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jws)
+
+[JWE](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
## API tests
diff --git a/lib/jose/jwe/jwe-aeskw.c b/lib/jose/jwe/jwe-aeskw.c
new file mode 100644
index 000000000..adb0df7ae
--- /dev/null
+++ b/lib/jose/jwe/jwe-aeskw.c
@@ -0,0 +1,192 @@
+/*
+ * 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
+ *
+ *
+ * JWE code related to aeskw cbc
+ *
+ */
+#include "core/private.h"
+#include "jose/jwe/private.h"
+
+
+/*
+ * RFC3394 Key Wrap uses a 128-bit key, and bloats what it is wrapping by
+ * one 8-byte block. So, if you had a 32 byte plaintext CEK to wrap, after
+ * wrapping it becomes a 40 byte wrapped, enciphered, key.
+ *
+ * The CEK comes in from and goes out in LJWE_EKEY. So LJWE_EKEY length
+ * increases by 8 from calling this.
+ */
+
+int
+lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len)
+{
+ struct lws_genaes_ctx aesctx;
+ /* we are wrapping a key, so size for the worst case after wrap */
+ uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES +
+ LWS_JWE_RFC3394_OVERHEAD_BYTES];
+ int n, m, hlen = lws_genhmac_size(jose->enc_alg->hmac_type);
+
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_OCT) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
+
+ return -1;
+ }
+
+ jws->map_b64.len[LJWE_JOSE] = ((jws->map.len[LJWE_JOSE] * 4) / 3) + 10;
+ if (*temp_len < jws->map_b64.len[LJWE_JOSE])
+ return -1;
+ jws->map_b64.buf[LJWE_JOSE] = (char *)temp;
+ temp += jws->map_b64.len[LJWE_JOSE];
+ *temp_len -= jws->map_b64.len[LJWE_JOSE];
+
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_JOSE], jws->map.len[LJWE_JOSE],
+ (char *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_notice("%s: failed to encode JOSE hdr\n", __func__);
+
+ return -1;
+ }
+ jws->map_b64.len[LJWE_JOSE] = n;
+
+ jws->map.buf[LJWE_ATAG] = (char *)temp;
+ jws->map.len[LJWE_ATAG] = hlen / 2;
+ if (*temp_len < jws->map.len[LJWE_ATAG])
+ return -1;
+ temp += hlen / 2;
+ *temp_len -= hlen / 2;
+
+ jws->map.buf[LJWE_IV] = (char *)temp;
+ jws->map.len[LJWE_IV] = LWS_JWE_AES_IV_BYTES;
+ if (*temp_len < jws->map.len[LJWE_IV])
+ return -1;
+ temp += LWS_JWE_AES_IV_BYTES;
+ *temp_len -= LWS_JWE_AES_IV_BYTES;
+
+ /* 1) Encrypt the payload... */
+
+ /* the CEK is 256-bit in the example encrypted with a 128-bit key */
+
+ n = lws_jwe_encrypt_cbc_hs(jose, jws, (uint8_t *)jws->map.buf[LJWE_EKEY],
+ (uint8_t *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
+ return -1;
+ }
+
+ /* 2) Encrypt the JWE Encrypted Key: RFC3394 Key Wrap uses 64 bit blocks
+ * and 128-bit input key*/
+
+ if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW,
+ jws->jwk->e, 1, NULL)) {
+
+ lwsl_notice("%s: lws_genaes_create\n", __func__);
+ return -1;
+ }
+
+ /* tag size is determined by enc cipher key length */
+
+ n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_EKEY],
+ jws->map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL,
+ lws_gencrypto_bits_to_bytes(
+ jose->enc_alg->keybits_fixed));
+ m = lws_genaes_destroy(&aesctx, NULL, 0);
+ if (n < 0) {
+ lwsl_err("%s: encrypt cek fail\n", __func__);
+ return -1;
+ }
+ if (m < 0) {
+ lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
+ return -1;
+ }
+
+ jws->map.len[LJWE_EKEY] += LWS_JWE_RFC3394_OVERHEAD_BYTES;
+ memcpy((uint8_t *)jws->map.buf[LJWE_EKEY], enc_cek,
+ jws->map.len[LJWE_EKEY]);
+
+ return jws->map.len[LJWE_CTXT];
+}
+
+
+int
+lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws)
+{
+ struct lws_genaes_ctx aesctx;
+ uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES +
+ LWS_JWE_RFC3394_OVERHEAD_BYTES];
+ int n, m;
+
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_OCT) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
+
+ return -1;
+ }
+
+ /* the CEK is 256-bit in the example encrypted with a 128-bit key */
+
+ if (jws->map.len[LJWE_EKEY] > sizeof(enc_cek))
+ return -1;
+
+ /* 1) Decrypt the JWE Encrypted Key to get the raw MAC / CEK */
+
+ if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW,
+ jws->jwk->e, 1, NULL)) {
+
+ lwsl_notice("%s: lws_genaes_create\n", __func__);
+ return -1;
+ }
+
+ /*
+ * Decrypt the CEK into enc_cek
+ * tag size is determined by enc cipher key length */
+
+ n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_EKEY],
+ jws->map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL,
+ lws_gencrypto_bits_to_bytes(
+ jose->enc_alg->keybits_fixed));
+ m = lws_genaes_destroy(&aesctx, NULL, 0);
+ if (n < 0) {
+ lwsl_err("%s: decrypt CEK fail\n", __func__);
+ return -1;
+ }
+ if (m < 0) {
+ lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
+ return -1;
+ }
+
+ /* 2) Decrypt the payload */
+
+ n = lws_jwe_auth_and_decrypt_cbc_hs(jose, jws, enc_cek,
+ (uint8_t *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n",
+ __func__);
+ return -1;
+ }
+
+ return jws->map.len[LJWE_CTXT];
+}
+
+
diff --git a/lib/jose/jwe/jwe-rsa-aescbc.c b/lib/jose/jwe/jwe-rsa-aescbc.c
new file mode 100644
index 000000000..dc93b2195
--- /dev/null
+++ b/lib/jose/jwe/jwe-rsa-aescbc.c
@@ -0,0 +1,437 @@
+/*
+ * 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
+ *
+ *
+ * JWE code related to rsa + aescbc
+ *
+ */
+#include "core/private.h"
+#include "jose/jwe/private.h"
+
+int
+lws_jwe_encrypt_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
+ uint8_t *cek, uint8_t *aad, int aad_len)
+{
+ int n, hlen = lws_genhmac_size(jose->enc_alg->hmac_type);
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_gencrypto_keyelem el;
+ struct lws_genhmac_ctx hmacctx;
+ struct lws_genaes_ctx aesctx;
+ uint8_t al[8];
+
+ /* Caller must have prepared space for the results */
+
+ if (jws->map.len[LJWE_ATAG] != hlen / 2) {
+ lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
+ hlen / 2, jws->map.len[LJWE_ATAG]);
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_IV] != 16) {
+ lwsl_notice("expected iv len %d, got %d\n", 16,
+ jws->map.len[LJWE_IV]);
+ return -1;
+ }
+
+ /* first create the authentication hmac */
+
+ /* JWA Section 5.2.2.1
+ *
+ * 1. The secondary keys MAC_KEY and ENC_KEY are generated from the
+ * input key K as follows. Each of these two keys is an octet
+ * string.
+ *
+ * MAC_KEY consists of the initial MAC_KEY_LEN octets of K, in
+ * order.
+ * ENC_KEY consists of the final ENC_KEY_LEN octets of K, in
+ * order.
+ */
+
+ /*
+ * 2. The IV used is a 128-bit value generated randomly or
+ * pseudorandomly for use in the cipher.
+ */
+ lws_get_random(jws->context, (void *)jws->map.buf[LJWE_IV], 16);
+
+ /*
+ * 3. The plaintext is CBC encrypted using PKCS #7 padding using
+ * ENC_KEY as the key and the IV. We denote the ciphertext output
+ * from this step as E.
+ */
+
+ /* second half is the AES ENC_KEY */
+ el.buf = (uint8_t *)jws->map.buf[LJWE_EKEY] + (hlen / 2);
+ el.len = hlen / 2;
+
+ if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_CBC, &el,
+ LWS_GAESP_NO_PADDING, NULL)) {
+ lwsl_err("%s: lws_genaes_create failed\n", __func__);
+
+ return -1;
+ }
+
+ /*
+ * the plaintext gets delivered to us in LJWE_CTXT, this replaces
+ * the plaintext there with the same amount of ciphertext
+ */
+ n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_IV], NULL, NULL, 16);
+ lws_genaes_destroy(&aesctx, NULL, 0);
+ if (n) {
+ lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
+ return -1;
+ }
+
+ /*
+ * 4. The octet string AL is equal to the number of bits in the
+ * Additional Authenticated Data A expressed as a 64-bit unsigned
+ * big-endian integer.
+ */
+ lws_jwe_be64(aad_len * 8, al);
+
+ /* first half of the CEK is the MAC key */
+ if (lws_genhmac_init(&hmacctx, jose->enc_alg->hmac_type,
+ (uint8_t *)jws->map.buf[LJWE_EKEY], hlen / 2))
+ return -1;
+
+ /*
+ * 5. A message Authentication Tag T is computed by applying HMAC
+ * [RFC2104] to the following data, in order:
+ *
+ * - the Additional Authenticated Data A,
+ * - the Initialization Vector IV,
+ * - the ciphertext E computed in the previous step, and
+ * - the octet string AL defined above.
+ *
+ * The string MAC_KEY is used as the MAC key. We denote the output
+ * of the MAC computed in this step as M. The first T_LEN octets of
+ * M are used as T.
+ */
+
+ if (lws_genhmac_update(&hmacctx, aad, aad_len) ||
+ lws_genhmac_update(&hmacctx, jws->map.buf[LJWE_IV],
+ LWS_JWE_AES_IV_BYTES) ||
+ /* since we encrypted it, this is the ciphertext */
+ lws_genhmac_update(&hmacctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT]) ||
+ lws_genhmac_update(&hmacctx, al, 8)) {
+ lwsl_err("%s: hmac computation failed\n", __func__);
+ lws_genhmac_destroy(&hmacctx, NULL);
+ return -1;
+ }
+
+ if (lws_genhmac_destroy(&hmacctx, digest)) {
+ lwsl_err("%s: problem destroying hmac\n", __func__);
+ return -1;
+ }
+
+ /* create tag */
+ memcpy((void *)jws->map.buf[LJWE_ATAG], digest, hlen / 2);
+
+ return jws->map.len[LJWE_CTXT];
+}
+
+/*
+ * Requirements on entry:
+ *
+ * - jws->map LJWE_JOSE contains the ASCII JOSE header
+ * - jws->map LJWE_EKEY contains cek of enc_alg hmac length
+ * - jws->map LJWE_CTXT contains the plaintext
+ *
+ * On successful exit:
+ *
+ * - jws->map LJWE_ATAG contains the tag
+ * - jws->map LJWE_IV contains the new random IV that was used
+ * - jws->map LJWE_EKEY contains the encrypted CEK
+ * - jws->map LJWE_CTXT contains the ciphertext
+ *
+ * Return the amount of temp used, or -1
+ */
+
+int
+lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len)
+{
+ int n, hlen = lws_genhmac_size(jose->enc_alg->hmac_type), want;
+ char ekey[LWS_GENHASH_LARGEST];
+ struct lws_genrsa_ctx rsactx;
+
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
+
+ return -1;
+ }
+
+ /*
+ * Reserve space in caller temp for extra JWE elements and b64 version
+ * of the JOSE hdr needed for computation... notice that the
+ * unencrypted EKEY coming in is smaller than the RSA-encrypted EKEY
+ * going out, which is going to be the RSA key size
+ */
+
+ want = lws_base64_size(jws->map.len[LJWE_JOSE]) +
+ jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len +
+ (hlen / 2) + LWS_JWE_AES_IV_BYTES;
+ if (*temp_len < want) {
+ lwsl_notice("%s: more temp space needed: want %d, got %d\n",
+ __func__, want, *temp_len);
+ return -1;
+ }
+
+ jws->map_b64.buf[LJWE_JOSE] = (char *)temp;
+ jws->map_b64.len[LJWE_JOSE] = lws_base64_size(jws->map.len[LJWE_JOSE]);
+ if (*temp_len < jws->map_b64.len[LJWE_JOSE])
+ return -1;
+ temp += jws->map_b64.len[LJWE_JOSE];
+ *temp_len -= jws->map_b64.len[LJWE_JOSE];
+
+ jws->map.buf[LJWE_ATAG] = (char *)temp;
+ jws->map.len[LJWE_ATAG] = hlen / 2;
+ if (*temp_len < jws->map.len[LJWE_ATAG])
+ return -1;
+ temp += hlen / 2;
+ *temp_len -= hlen / 2;
+
+ jws->map.buf[LJWE_IV] = (char *)temp;
+ jws->map.len[LJWE_IV] = LWS_JWE_AES_IV_BYTES;
+ if (*temp_len < jws->map.len[LJWE_IV])
+ return -1;
+ temp += jws->map.len[LJWE_IV];
+ *temp_len -= jws->map.len[LJWE_IV];
+
+ if (*temp_len < jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len)
+ return -1;
+
+ memcpy(temp, jws->map.buf[LJWE_EKEY], jws->map.len[LJWE_EKEY]);
+ jws->map.buf[LJWE_EKEY] = (char *)temp;
+ /*
+ * don't change jws->map.len[LJWE_EKEY]... it has allocation for up to
+ * jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len bytes now and the length
+ * will be set after the plaintext version is encrypted in-situ
+ */
+ temp += jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
+ *temp_len -= jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
+
+ /* we need a b64u encode of the JOSE header as AAD */
+
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_JOSE], jws->map.len[LJWE_JOSE],
+ (char *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_notice("%s: failed to encode JOSE hdr\n", __func__);
+
+ return -1;
+ }
+ jws->map_b64.len[LJWE_JOSE] = n;
+
+ /* Encrypt using the raw CEK (treated as MAC KEY | ENC KEY) */
+
+ n = lws_jwe_encrypt_cbc_hs(jose, jws,
+ (uint8_t *)jws->map.buf[LJWE_EKEY],
+ (uint8_t *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
+ return -1;
+ }
+
+ if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
+ !strcmp(jose->alg->alg, "RSA-OAEP") ?
+ LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
+ LWS_GENHASH_TYPE_UNKNOWN)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ return -1;
+ }
+
+ /* encrypt the CEK using RSA, mbedtls can't handle both in and out are
+ * the EKEY, so copy the unencrypted ekey out temporarily */
+
+ memcpy(ekey, jws->map.buf[LJWE_EKEY], hlen);
+
+ n = lws_genrsa_public_encrypt(&rsactx, (uint8_t *)ekey, hlen,
+ (uint8_t *)jws->map.buf[LJWE_EKEY]);
+ lws_genrsa_destroy(&rsactx);
+ lws_explicit_bzero(ekey, hlen);
+ if (n < 0) {
+ lwsl_err("%s: decrypt cek fail\n", __func__);
+ return -1;
+ }
+ jws->map.len[LJWE_EKEY] = n; /* update to encrypted EKEY size */
+
+ /*
+ * We end up with IV, ATAG, set, EKEY encrypted and CTXT is ciphertext,
+ * and b64u version of ATAG in map_b64.
+ */
+
+ return 0;
+}
+
+int
+lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws, uint8_t *enc_cek,
+ uint8_t *aad, int aad_len)
+{
+ int n, hlen = lws_genhmac_size(jose->enc_alg->hmac_type);
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_gencrypto_keyelem el;
+ struct lws_genhmac_ctx hmacctx;
+ struct lws_genaes_ctx aesctx;
+ uint8_t al[8];
+
+ /* Some sanity checks on what came in */
+
+ if (jws->map.len[LJWE_ATAG] != hlen / 2) {
+ lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
+ hlen / 2, jws->map.len[LJWE_ATAG]);
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_IV] != 16) {
+ lwsl_notice("expected iv len %d, got %d\n", 16,
+ jws->map.len[LJWE_IV]);
+ return -1;
+ }
+
+ /* Prepare to check authentication
+ *
+ * AAD is the b64 JOSE header.
+ *
+ * The octet string AL, which is the number of bits in AAD expressed as
+ * a big-endian 64-bit unsigned integer is:
+ *
+ * [0, 0, 0, 0, 0, 0, 1, 152]
+ *
+ * Concatenate the AAD, the Initialization Vector, the ciphertext, and
+ * the AL value.
+ *
+ */
+
+ lws_jwe_be64(aad_len * 8, al);
+
+ /* first half of enc_cek is the MAC key */
+ if (lws_genhmac_init(&hmacctx, jose->enc_alg->hmac_type, enc_cek,
+ hlen / 2))
+ return -1;
+
+ if (lws_genhmac_update(&hmacctx, aad, aad_len) ||
+ lws_genhmac_update(&hmacctx, (uint8_t *)jws->map.buf[LJWE_IV],
+ jws->map.len[LJWE_IV]) ||
+ lws_genhmac_update(&hmacctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT]) ||
+ lws_genhmac_update(&hmacctx, al, 8)) {
+ lwsl_err("%s: hmac computation failed\n", __func__);
+ lws_genhmac_destroy(&hmacctx, NULL);
+ return -1;
+ }
+
+ if (lws_genhmac_destroy(&hmacctx, digest)) {
+ lwsl_err("%s: problem destroying hmac\n", __func__);
+ return -1;
+ }
+
+ /* first half of digest is the auth tag */
+
+ if (lws_timingsafe_bcmp(digest, jws->map.buf[LJWE_ATAG], hlen / 2)) {
+ lwsl_err("%s: auth failed: hmac tag != ATAG\n", __func__);
+ lwsl_hexdump_notice(jws->map.buf[LJWE_ATAG], hlen / 2);
+ lwsl_hexdump_notice(digest, 16);
+ return -1;
+ }
+
+ /* second half of enc cek is the CEK KEY */
+ el.buf = enc_cek + (hlen / 2);
+ el.len = hlen / 2;
+
+ if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_CBC,
+ &el, LWS_GAESP_NO_PADDING, NULL)) {
+ lwsl_err("%s: lws_genaes_create failed\n", __func__);
+
+ return -1;
+ }
+
+ n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_IV], NULL, NULL, 16);
+ n |= lws_genaes_destroy(&aesctx, NULL, 0);
+ if (n) {
+ lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
+ return -1;
+ }
+
+ return jws->map.len[LJWE_CTXT];
+}
+
+int
+lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws)
+{
+ int n;
+ struct lws_genrsa_ctx rsactx;
+ uint8_t enc_cek[512];
+
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
+
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_EKEY] < 40) {
+ lwsl_err("%s: EKEY length too short %d\n", __func__,
+ jws->map.len[LJWE_EKEY]);
+
+ return -1;
+ }
+
+ /* Decrypt the JWE Encrypted Key to get the raw MAC || CEK */
+
+ if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
+ !strcmp(jose->alg->alg, "RSA-OAEP") ?
+ LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
+ LWS_GENHASH_TYPE_UNKNOWN)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ return -1;
+ }
+
+ n = lws_genrsa_private_decrypt(&rsactx,
+ (uint8_t *)jws->map.buf[LJWE_EKEY],
+ jws->map.len[LJWE_EKEY], enc_cek,
+ sizeof(enc_cek));
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lwsl_err("%s: decrypt cek fail: \n", __func__);
+ return -1;
+ }
+
+ n = lws_jwe_auth_and_decrypt_cbc_hs(jose, jws, enc_cek,
+ (uint8_t *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n",
+ __func__);
+ return -1;
+ }
+
+ return jws->map.len[LJWE_CTXT];
+}
diff --git a/lib/jose/jwe/jwe-rsa-aesgcm.c b/lib/jose/jwe/jwe-rsa-aesgcm.c
new file mode 100644
index 000000000..ac865f6fa
--- /dev/null
+++ b/lib/jose/jwe/jwe-rsa-aesgcm.c
@@ -0,0 +1,349 @@
+/*
+ * 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
+ *
+ *
+ * JWE code related to aes gcm
+ *
+ */
+#include "core/private.h"
+#include "jose/jwe/private.h"
+
+#define LWS_AESGCM_IV 12
+#define LWS_AESGCM_TAG 16
+
+/*
+ * NOTICE this is AESGCM content encryption, it's not AES GCM key wrapping
+ *
+ *
+ * This section defines the specifics of performing authenticated
+ * encryption with AES in Galois/Counter Mode (GCM) ([AES] and
+ * [NIST.800-38D]).
+ *
+ * The CEK is used as the encryption key.
+ *
+ * Use of an IV of size 96 bits is REQUIRED with this algorithm.
+ *
+ * The requested size of the Authentication Tag output MUST be 128 bits,
+ * regardless of the key size.
+ *
+ * For decrypt: decrypt the KEK, then decrypt the payload
+ *
+ * For encrypt: encrypt the payload, then encrypt the KEK
+ */
+
+/*
+ * encrypting... enc_cek is unencrypted
+ */
+
+int
+lws_jwe_encrypt_gcm(struct lws_jose *jose, struct lws_jws *jws,
+ uint8_t *enc_cek, uint8_t *aad, int aad_len)
+{
+ struct lws_gencrypto_keyelem el;
+ struct lws_genaes_ctx aesctx;
+ size_t ivs = LWS_AESGCM_IV;
+ int n;
+
+ /* Some sanity checks on what came in */
+
+ /* MUST be 128-bit for all sizes */
+ if (jws->map.len[LJWE_ATAG] != LWS_AESGCM_TAG) {
+ lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n",
+ __func__, jws->map.len[LJWE_ATAG]);
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */
+ lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__,
+ jws->map.len[LJWE_IV]);
+ return -1;
+ }
+
+ /* EKEY is directly the CEK KEY */
+ el.buf = enc_cek;
+ el.len = jose->enc_alg->keybits_fixed / 8;
+
+ if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_GCM,
+ &el, LWS_GAESP_NO_PADDING, NULL)) {
+ lwsl_err("%s: lws_genaes_create failed\n", __func__);
+
+ return -1;
+ }
+
+ /* aad */
+
+ n = lws_genaes_crypt(&aesctx, aad, aad_len, NULL,
+ (uint8_t *)jws->map.buf[LJWE_IV],
+ (uint8_t *)jws->map.buf[LJWE_ATAG], &ivs,
+ LWS_AESGCM_TAG);
+ if (n) {
+ lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__);
+ return -1;
+ }
+
+ /* payload */
+ n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_IV],
+ NULL, &ivs,
+ LWS_AESGCM_TAG);
+
+ n |= lws_genaes_destroy(&aesctx, (uint8_t *)jws->map.buf[LJWE_ATAG],
+ LWS_AESGCM_TAG);
+ if (n) {
+ lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
+ return -1;
+ }
+
+ return jws->map.len[LJWE_CTXT];
+}
+
+
+
+int
+lws_jwe_encrypt_rsa_aes_gcm(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len)
+{
+ int n, ret = -1, used = 0;
+ struct lws_genrsa_ctx rsactx;
+ uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
+ int ekbytes = jose->enc_alg->keybits_fixed / 8;
+
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
+
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_EKEY] < 32) {
+ lwsl_err("%s: EKEY length too short %d\n", __func__,
+ jws->map.len[LJWE_EKEY]);
+
+ return -1;
+ }
+
+ /* create the IV + CEK */
+
+ jws->map_b64.len[LJWE_JOSE] = lws_base64_size(jws->map.len[LJWE_JOSE]);
+
+ if (*temp_len < LWS_AESGCM_IV + LWS_AESGCM_TAG + ekbytes +
+ jws->map_b64.len[LJWE_JOSE])
+ return -1;
+
+ *temp_len -= LWS_AESGCM_IV + LWS_AESGCM_TAG +
+ jws->map_b64.len[LJWE_JOSE] +
+ (jose->enc_alg->keybits_fixed / 8);
+
+ if (lws_get_random(jws->context, temp, LWS_AESGCM_IV) != LWS_AESGCM_IV)
+ return -1;
+ jws->map.buf[LJWE_IV] = temp;
+ jws->map.len[LJWE_IV] = LWS_AESGCM_IV;
+ temp += LWS_AESGCM_IV;
+
+ jws->map.buf[LJWE_ATAG] = temp;
+ jws->map.len[LJWE_ATAG] = LWS_AESGCM_TAG;
+ temp += LWS_AESGCM_TAG;
+
+ /* we create the CEK into EKEY, it'll be cleansed by jws destroy */
+
+ if (lws_get_random(jws->context, temp, ekbytes) != ekbytes)
+ return -1;
+ jws->map.buf[LJWE_EKEY] = temp;
+ jws->map.len[LJWE_EKEY] = ekbytes;
+ temp += ekbytes;
+
+ jws->map_b64.buf[LJWE_JOSE] = temp;
+ temp += jws->map_b64.len[LJWE_JOSE];
+ /* we need a b64u encode of the JOSE header as AAD */
+
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_JOSE], jws->map.len[LJWE_JOSE],
+ (char *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_notice("%s: failed to encode JOSE hdr\n", __func__);
+
+ return -1;
+ }
+ jws->map_b64.len[LJWE_JOSE] = n;
+
+ /* we must cleanse enc_cek */
+ used = jws->map.len[LJWE_EKEY];
+ memcpy(enc_cek, jws->map.buf[LJWE_EKEY], jws->map.len[LJWE_EKEY]);
+
+ /* encrypt the payload */
+
+ n = lws_jwe_encrypt_gcm(jose, jws, (uint8_t *)jws->map.buf[LJWE_EKEY],
+ (uint8_t *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt_gcm failed\n",
+ __func__);
+ goto bail;
+ }
+
+ /* Encrypt the CEK to make the JWE Encrypted Key */
+
+ if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
+ !strcmp(jose->alg->alg, "RSA-OAEP") ?
+ LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
+ LWS_GENHASH_TYPE_SHA1 /* !!! */)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ goto bail;
+ }
+
+ n = lws_genrsa_public_encrypt(&rsactx,
+ (uint8_t *)jws->map.buf[LJWE_EKEY],
+ jws->map.len[LJWE_EKEY], enc_cek);
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lwsl_err("%s: encrypt cek fail: \n", __func__);
+ goto bail;
+ }
+ jws->map.len[LJWE_EKEY] = n;
+
+ /* overwrite the CEK in EKEY with the encrypted version */
+
+ memcpy((void *)jws->map.buf[LJWE_EKEY], enc_cek,
+ jws->map.len[LJWE_EKEY]);
+
+ ret = jws->map.len[LJWE_CTXT];
+
+bail:
+ /* cleanse enc_cek on stack that contained the unencrypted CEK */
+ lws_explicit_bzero(enc_cek, used);
+
+ return ret;
+}
+
+int
+lws_jwe_auth_and_decrypt_gcm(struct lws_jose *jose, struct lws_jws *jws,
+ uint8_t *enc_cek, uint8_t *aad, int aad_len)
+{
+ struct lws_gencrypto_keyelem el;
+ struct lws_genaes_ctx aesctx;
+ size_t ivs = LWS_AESGCM_IV;
+ uint8_t tag[LWS_AESGCM_TAG];
+ int n;
+
+ /* Some sanity checks on what came in */
+
+ /* Tag MUST be 128-bit for all sizes */
+ if (jws->map.len[LJWE_ATAG] != LWS_AESGCM_TAG) {
+ lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n",
+ __func__, jws->map.len[LJWE_ATAG]);
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */
+ lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__,
+ jws->map.len[LJWE_IV]);
+ return -1;
+ }
+
+ /* EKEY is directly the CEK KEY */
+ el.buf = enc_cek;
+ el.len = jose->enc_alg->keybits_fixed / 8;
+
+ if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_GCM,
+ &el, LWS_GAESP_NO_PADDING, NULL)) {
+ lwsl_err("%s: lws_genaes_create failed\n", __func__);
+
+ return -1;
+ }
+
+ n = lws_genaes_crypt(&aesctx, aad, aad_len,
+ NULL,
+ (uint8_t *)jws->map.buf[LJWE_IV],
+ (uint8_t *)jws->map.buf[LJWE_ATAG], &ivs, 16);
+ if (n) {
+ lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__);
+ return -1;
+ }
+ n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_CTXT],
+ (uint8_t *)jws->map.buf[LJWE_IV],
+ (uint8_t *)jws->map.buf[LJWE_ATAG], &ivs, 16);
+
+ n |= lws_genaes_destroy(&aesctx, tag, sizeof(tag));
+ if (n) {
+ lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
+ return -1;
+ }
+
+ return jws->map.len[LJWE_CTXT];
+}
+
+
+
+int
+lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jose *jose, struct lws_jws *jws)
+{
+ int n;
+ struct lws_genrsa_ctx rsactx;
+ uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
+
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
+
+ return -1;
+ }
+
+ if (jws->map.len[LJWE_EKEY] < 32) {
+ lwsl_err("%s: EKEY length too short %d\n", __func__,
+ jws->map.len[LJWE_EKEY]);
+
+ return -1;
+ }
+
+ /* Decrypt the JWE Encrypted Key to get the direct CEK */
+
+ if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
+ !strcmp(jose->alg->alg, "RSA-OAEP") ?
+ LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
+ LWS_GENHASH_TYPE_SHA1 /* !!! */)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ return -1;
+ }
+
+ n = lws_genrsa_private_decrypt(&rsactx,
+ (uint8_t *)jws->map.buf[LJWE_EKEY],
+ jws->map.len[LJWE_EKEY], enc_cek,
+ sizeof(enc_cek));
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lwsl_err("%s: decrypt cek fail: \n", __func__);
+ return -1;
+ }
+
+ n = lws_jwe_auth_and_decrypt_gcm(jose, jws, enc_cek,
+ (uint8_t *)jws->map_b64.buf[LJWE_JOSE],
+ jws->map_b64.len[LJWE_JOSE]);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt_gcm_hs failed\n",
+ __func__);
+ return -1;
+ }
+
+ return jws->map.len[LJWE_CTXT];
+}
diff --git a/lib/jose/jwe/jwe.c b/lib/jose/jwe/jwe.c
index ec0510b3b..5cf95a114 100644
--- a/lib/jose/jwe/jwe.c
+++ b/lib/jose/jwe/jwe.c
@@ -21,24 +21,418 @@
*
* This supports RFC7516 JSON Web Encryption
*
- *
*/
#include "core/private.h"
+#include "jose/jwe/private.h"
+#if 0
+static const char * const jwe_complete_tokens[] = {
+ "protected",
+ "recipients[].header",
+ "recipients[].header.alg",
+ "recipients[].header.kid",
+ "recipients[].encrypted_key",
+ "iv",
+ "ciphertext",
+ "tag",
+};
+
+enum enum_jwe_complete_tokens {
+ LWS_EJCT_PROTECTED,
+ LWS_EJCT_HEADER,
+ LWS_EJCT_HEADER_ALG,
+ LWS_EJCT_HEADER_KID,
+ LWS_EJCT_RECIP_ENC_KEY,
+ LWS_EJCT_IV,
+ LWS_EJCT_CIPHERTEXT,
+ LWS_EJCT_TAG,
+};
+
+struct complete_cb_args {
+ struct lws_jws_compact_map *map;
+ struct lws_jws_compact_map *map_b64;
+ char *out;
+ int out_len;
+};
+
+
+static int
+do_map(struct complete_cb_args *args, int index, char *b64, int len)
+{
+ return 0;
+}
+
+static signed char
+lws_jwe_parse_complete_cb(struct lejp_ctx *ctx, char reason)
+{
+ struct complete_cb_args *args = (struct complete_cb_args *)ctx->user;
+
+ if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
+ return 0;
+
+ switch (ctx->path_match - 1) {
+
+ /* strings */
+
+ case LWS_EJCT_PROTECTED:
+ case LWS_EJCT_HEADER:
+ case LWS_EJCT_HEADER_ALG:
+ case LWS_EJCT_HEADER_KID:
+ case LWS_EJCT_RECIP_ENC_KEY:
+ case LWS_EJCT_IV:
+ case LWS_EJCT_CIPHERTEXT:
+ case LWS_EJCT_TAG:
+ }
+
+ return 0;
+}
LWS_VISIBLE int
-lws_jwe_create_packet(struct lws_jwk *jwk,
- const struct lws_jose_jwe_alg *jose_alg,
+lws_jws_complete_decode(const char *json_in, int len,
+ struct lws_jws_compact_map *map,
+ struct lws_jws_compact_map *map_b64, char *out,
+ int out_len)
+{
+
+ struct complete_cb_args args;
+ struct lejp_ctx jctx;
+ int blocks, n, m = 0;
+
+ if (!map_b64)
+ map_b64 = map;
+
+ memset(map_b64, 0, sizeof(*map_b64));
+ memset(map, 0, sizeof(*map));
+
+ args.map = map;
+ args.map_b64 = map_b64;
+ args.out = out;
+ args.out_len = out_len;
+
+ lejp_construct(&jctx, lws_jwe_parse_complete_cb, &args,
+ jwe_complete_tokens,
+ LWS_ARRAY_SIZE(jwe_complete_tokens));
+
+ m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)json_in, len);
+ lejp_destruct(&jctx);
+}
+#endif
+
+static uint8_t *
+be32(uint32_t i, uint32_t *p32)
+{
+ uint8_t *p = (uint8_t *)p32;
+
+ *p++ = (i >> 24) & 0xff;
+ *p++ = (i >> 16) & 0xff;
+ *p++ = (i >> 8) & 0xff;
+ *p++ = i & 0xff;
+
+ return (uint8_t *)p32;
+}
+
+/*
+ * The key derivation process derives the agreed-upon key from the
+ * shared secret Z established through the ECDH algorithm, per
+ * Section 6.2.2.2 of [NIST.800-56A].
+ *
+ * out must be prepared to take at least 32 bytes or the encrypted key size,
+ * whichever is larger.
+ */
+
+int
+lws_jwa_concat_kdf(struct lws_jose *jose, struct lws_jws *jws, int direct,
+ uint8_t *out, const uint8_t *shared_secret, int sslen)
+{
+ int hlen = lws_genhash_size(LWS_GENHASH_TYPE_SHA256), aidlen;
+ struct lws_genhash_ctx hash_ctx;
+ uint32_t ctr = 1, t;
+ const char *aid;
+
+ /*
+ * Hash
+ *
+ * AlgorithmID || PartyUInfo || PartyVInfo
+ * {|| SuppPubInfo }{|| SuppPrivInfo }
+ *
+ * AlgorithmID
+ *
+ * The AlgorithmID value is of the form Datalen || Data, where Data
+ * is a variable-length string of zero or more octets, and Datalen is
+ * a fixed-length, big-endian 32-bit counter that indicates the
+ * length (in octets) of Data. In the Direct Key Agreement case,
+ * Data is set to the octets of the ASCII representation of the "enc"
+ * Header Parameter value. In the Key Agreement with Key Wrapping
+ * case, Data is set to the octets of the ASCII representation of the
+ * "alg" (algorithm) Header Parameter value.
+ */
+
+ aid = direct ? jose->enc_alg->alg : jose->alg->alg;
+ aidlen = strlen(aid);
+
+ /*
+ * PartyUInfo (PartyVInfo is the same deal)
+ *
+ * The PartyUInfo value is of the form Datalen || Data, where Data is
+ * a variable-length string of zero or more octets, and Datalen is a
+ * fixed-length, big-endian 32-bit counter that indicates the length
+ * (in octets) of Data. If an "apu" (agreement PartyUInfo) Header
+ * Parameter is present, Data is set to the result of base64url
+ * decoding the "apu" value and Datalen is set to the number of
+ * octets in Data. Otherwise, Datalen is set to 0 and Data is set to
+ * the empty octet sequence
+ *
+ * SuppPubInfo
+ *
+ * This is set to the keydatalen represented as a 32-bit big-endian
+ * integer.
+ *
+ * keydatalen
+ *
+ * This is set to the number of bits in the desired output key. For
+ * "ECDH-ES", this is length of the key used by the "enc" algorithm.
+ * For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this
+ * is 128, 192, and 256, respectively.
+ *
+ * Compute Hash i = H(counter || Z || OtherInfo).
+ *
+ * We must iteratively hash over key material that's larger than
+ * one hash output size (256b for SHA-256)
+ */
+
+ while (ctr <= (uint32_t)(jose->enc_alg->keybits_fixed / hlen)) {
+
+ /*
+ * Key derivation is performed using the Concat KDF, as defined
+ * in Section 5.8.1 of [NIST.800-56A], where the Digest Method
+ * is SHA-256.
+ */
+
+ if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
+ return -1;
+
+ if (/* counter */
+ lws_genhash_update(&hash_ctx, be32(ctr++, &t), 4) ||
+ /* Z */
+ lws_genhash_update(&hash_ctx, shared_secret, sslen) ||
+ /* other info */
+ lws_genhash_update(&hash_ctx, be32(strlen(aid), &t), 4) ||
+ lws_genhash_update(&hash_ctx, aid, aidlen) ||
+ lws_genhash_update(&hash_ctx,
+ be32(jose->e[LJJHI_APU].len, &t), 4) ||
+ lws_genhash_update(&hash_ctx, jose->e[LJJHI_APU].buf,
+ jose->e[LJJHI_APU].len) ||
+ lws_genhash_update(&hash_ctx,
+ be32(jose->e[LJJHI_APV].len, &t), 4) ||
+ lws_genhash_update(&hash_ctx, jose->e[LJJHI_APV].buf,
+ jose->e[LJJHI_APV].len) ||
+ lws_genhash_update(&hash_ctx,
+ be32(jose->enc_alg->keybits_fixed, &t),
+ 4) ||
+ lws_genhash_destroy(&hash_ctx, out)) {
+ lws_genhash_destroy(&hash_ctx, NULL);
+
+ return -1;
+ }
+
+ out += hlen;
+ }
+
+ return 0;
+}
+
+LWS_VISIBLE void
+lws_jwe_be64(uint64_t c, uint8_t *p8)
+{
+ int n;
+
+ for (n = 56; n >= 0; n -= 8)
+ *p8++ = (uint8_t)((c >> n) & 0xff);
+}
+
+LWS_VISIBLE int
+lws_jwe_auth_and_decrypt(struct lws_jose *jose, struct lws_jws *jws)
+{
+ int valid_aescbc_hmac, valid_aesgcm;
+ char temp[512];
+ int temp_len = sizeof(temp);
+
+ if (lws_jwe_parse_jose(jose, jws->map.buf[LJWS_JOSE],
+ jws->map.len[LJWS_JOSE],
+ temp, &temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+ return -1;
+ }
+
+ valid_aescbc_hmac = jose->enc_alg &&
+ jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
+ (jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
+ jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 ||
+ jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512);
+
+ valid_aesgcm = jose->enc_alg &&
+ jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM;
+
+ /* RSA + AESCBC */
+
+ if ((!strcmp(jose->alg->alg, "RSA1_5") ||
+ !strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aescbc_hmac)
+ return lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(jose, jws);
+
+ /* RSA + AESGCM */
+
+ if ((!strcmp(jose->alg->alg, "RSA1_5") ||
+ !strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aesgcm)
+ return lws_jwe_auth_and_decrypt_rsa_aes_gcm(jose, jws);
+
+ /* AESKW */
+
+ if ((!strcmp(jose->alg->alg, "A128KW") ||
+ !strcmp(jose->alg->alg, "A192KW") ||
+ !strcmp(jose->alg->alg, "A256KW")) && valid_aescbc_hmac)
+ return lws_jwe_auth_and_decrypt_aeskw_cbc_hs(jose, jws);
+
+ lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__,
+ jose->alg->alg, jose->enc_alg ?
+ jose->enc_alg->alg : "NULL");
+
+ return -1;
+}
+LWS_VISIBLE int
+lws_jwe_encrypt(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len)
+{
+ int valid_aescbc_hmac, valid_aesgcm;
+
+ valid_aesgcm = jose->enc_alg &&
+ jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM;
+
+ if (lws_jwe_parse_jose(jose, jws->map.buf[LJWS_JOSE],
+ jws->map.len[LJWS_JOSE], temp, temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+ return -1;
+ }
+
+ valid_aescbc_hmac = jose->enc_alg &&
+ jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
+ (jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
+ jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 ||
+ jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512);
+
+ /* RSA + AESCBC */
+
+ if ((!strcmp(jose->alg->alg, "RSA1_5") ||
+ !strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aescbc_hmac)
+ return lws_jwe_encrypt_rsa_aes_cbc_hs(jose, jws, temp, temp_len);
+
+ /* RSA + AESGCM */
+
+ if ((!strcmp(jose->alg->alg, "RSA1_5") ||
+ !strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aesgcm)
+ return lws_jwe_encrypt_rsa_aes_gcm(jose, jws, temp, temp_len);
+
+ /* AESKW */
+
+ if ((!strcmp(jose->alg->alg, "A128KW") ||
+ !strcmp(jose->alg->alg, "A192KW") ||
+ !strcmp(jose->alg->alg, "A256KW")) && valid_aescbc_hmac)
+ return lws_jwe_encrypt_aeskw_cbc_hs(jose, jws, temp, temp_len);
+
+ lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__,
+ jose->alg->alg, jose->enc_alg ?
+ jose->enc_alg->alg : "NULL");
+
+ return -1;
+}
+
+/*
+ * JWE Compact Serialization consists of
+ *
+ * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
+ * BASE64URL(JWE Encrypted Key) || '.' ||
+ * BASE64URL(JWE Initialization Vector) || '.' ||
+ * BASE64URL(JWE Ciphertext) || '.' ||
+ * BASE64URL(JWE Authentication Tag)
+ */
+
+LWS_VISIBLE int
+lws_jwe_write_compact(struct lws_jose *jose, struct lws_jws *jws,
+ char *out, size_t out_len)
+{
+ size_t orig = out_len;
+ int n;
+
+ n = lws_jws_base64_enc(jws->map.buf[LJWS_JOSE],
+ jws->map.len[LJWS_JOSE], out, out_len);
+ if (n < 0 || (int)out_len == n) {
+ lwsl_info("%s: unable to encode JOSE\n", __func__);
+ return n;
+ }
+
+ out += n;
+ *out++ = '.';
+ out_len -= n + 1;
+
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_EKEY],
+ jws->map.len[LJWE_EKEY], out, out_len);
+ if (n < 0 || (int)out_len == n) {
+ lwsl_info("%s: unable to encode EKEY\n", __func__);
+ return n;
+ }
+
+ out += n;
+ *out++ = '.';
+ out_len -= n + 1;
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_IV],
+ jws->map.len[LJWE_IV], out, out_len);
+ if (n < 0 || (int)out_len == n) {
+ lwsl_info("%s: unable to encode IV\n", __func__);
+ return n;
+ }
+
+ out += n;
+ *out++ = '.';
+ out_len -= n + 1;
+
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_CTXT],
+ jws->map.len[LJWE_CTXT], out, out_len);
+ if (n < 0 || (int)out_len == n) {
+ lwsl_info("%s: unable to encode CTXT\n", __func__);
+ return n;
+ }
+
+ out += n;
+ *out++ = '.';
+ out_len -= n + 1;
+ n = lws_jws_base64_enc(jws->map.buf[LJWE_ATAG],
+ jws->map.len[LJWE_ATAG], out, out_len);
+ if (n < 0 || (int)out_len == n) {
+ lwsl_info("%s: unable to encode ATAG\n", __func__);
+ return n;
+ }
+
+ out += n;
+ *out++ = '\0';
+ out_len -= n;
+
+ return orig - out_len;
+}
+
+LWS_VISIBLE int
+lws_jwe_create_packet(struct lws_jose *jose, 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;
+ char *buf, *start, *p, *end, *p1, *end1;
+ struct lws_jws jws;
+ int n;
+
+ lws_jws_init(&jws, jwk, context);
/*
- * This buffer is local to the function, the actual output
- * is prepared into vhd->buf. Only the plaintext protected header
+ * 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.
*/
@@ -56,7 +450,11 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
* temporary JWS protected header plaintext
*/
- p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
+ if (!jose->alg || !jose->alg->alg)
+ goto bail;
+
+ p += lws_snprintf(p, end - p, "{\"alg\":\"%s\",\"jwk\":",
+ jose->alg->alg);
n = lws_jwk_export(jwk, 0, p, end - p);
if (n < 0) {
lwsl_notice("failed to export jwk\n");
@@ -74,23 +472,33 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
end1 = out + out_len - 1;
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
- b64_hdr = p1;
+ jws.map_b64.buf[LJWS_JOSE] = 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;
+ jws.map_b64.len[LJWS_JOSE] = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
- b64_pay = p1;
+ jws.map_b64.buf[LJWS_PYLD] = 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;
+ jws.map_b64.len[LJWS_PYLD] = n;
+ p1 += n;
+
+ p1 += lws_snprintf(p1, end1 - p1, "\",\"header\":\"");
+ jws.map_b64.buf[LJWS_UHDR] = 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;
+ }
+ jws.map_b64.len[LJWS_UHDR] = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\"");
@@ -99,13 +507,15 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
* 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, jose_alg, jwk, context);
+ n = lws_jws_sign_from_b64(jose, &jws, p1, end1 - p1);
if (n < 0) {
lwsl_notice("sig gen failed\n");
goto bail;
}
+ jws.map_b64.buf[LJWS_SIG] = p1;
+ jws.map_b64.len[LJWS_SIG] = n;
+
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\"}");
@@ -114,6 +524,7 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
return p1 - out;
bail:
+ lws_jws_destroy(&jws);
free(buf);
return -1;
diff --git a/lib/jose/jwe/private.h b/lib/jose/jwe/private.h
new file mode 100644
index 000000000..e4f02b267
--- /dev/null
+++ b/lib/jose/jwe/private.h
@@ -0,0 +1,50 @@
+
+/* jwe-rsa-aescbc.c */
+
+int
+lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len);
+
+int
+lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws, uint8_t *enc_cek,
+ uint8_t *aad, int aad_len);
+
+int
+lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws);
+
+int
+lws_jwe_encrypt_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
+ uint8_t *cek, uint8_t *aad, int aad_len);
+
+
+/* jws-rsa-aesgcm.c */
+
+int
+lws_jwe_auth_and_decrypt_gcm(struct lws_jose *jose,
+ struct lws_jws *jws, uint8_t *enc_cek,
+ uint8_t *aad, int aad_len);
+
+int
+lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jose *jose,
+ struct lws_jws *jws);
+
+int
+lws_jwe_encrypt_gcm(struct lws_jose *jose, struct lws_jws *jws,
+ uint8_t *enc_cek, uint8_t *aad, int aad_len);
+
+int
+lws_jwe_encrypt_rsa_aes_gcm(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len);
+
+
+/* jwe-rsa-aeskw.c */
+
+int
+lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
+ char *temp, int *temp_len);
+
+int
+lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jose *jose,
+ struct lws_jws *jws);
diff --git a/lib/jose/jwk/jwk.c b/lib/jose/jwk/jwk.c
index 8ef7494ee..879cff121 100644
--- a/lib/jose/jwk/jwk.c
+++ b/lib/jose/jwk/jwk.c
@@ -20,15 +20,16 @@
*/
#include "core/private.h"
+#include "jose/private.h"
#include
#include
-static const char * const kyt_names[] = {
- "unknown", /* LWS_GENCRYPTO_KYT_UNKNOWN */
- "oct", /* LWS_GENCRYPTO_KYT_OCT */
- "RSA", /* LWS_GENCRYPTO_KYT_RSA */
- "EC" /* LWS_GENCRYPTO_KYT_EC */
+static const char * const kty_names[] = {
+ "unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */
+ "oct", /* LWS_GENCRYPTO_KTY_OCT */
+ "RSA", /* LWS_GENCRYPTO_KTY_RSA */
+ "EC" /* LWS_GENCRYPTO_KTY_EC */
};
/*
@@ -64,42 +65,84 @@ static const char * const jwk_tok[] = {
/* information about each token declared above */
-#define F_B64 (1 << 10)
-#define F_B64U (1 << 11)
-#define F_META (1 << 12)
-#define F_RSA (1 << 13)
-#define F_EC (1 << 14)
-#define F_OCT (1 << 15)
+#define F_M (1 << 9) /* Mandatory for key type */
+#define F_B64 (1 << 10) /* Base64 coded octets */
+#define F_B64U (1 << 11) /* Base64 Url coded octets */
+#define F_META (1 << 12) /* JWK key metainformation */
+#define F_RSA (1 << 13) /* RSA key */
+#define F_EC (1 << 14) /* Elliptic curve key */
+#define F_OCT (1 << 15) /* octet key */
-unsigned short tok_map[] = {
+static unsigned short tok_map[] = {
F_RSA | F_EC | F_OCT | F_META | 0xff,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_E,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_N,
- F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ,
- F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI,
+ F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E,
+ F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N,
+ F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D,
+ F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P,
+ F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q,
+ F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP,
+ F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ,
+ F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI,
- F_RSA | F_EC | F_OCT | F_META | JWK_META_KTY,
- F_OCT | F_B64U | LWS_GENCRYPTO_OCT_KEYEL_K,
+ F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY,
+ F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K,
- F_EC | LWS_GENCRYPTO_EC_KEYEL_CRV,
- F_EC | F_B64U | LWS_GENCRYPTO_EC_KEYEL_X,
- F_EC | F_B64U | LWS_GENCRYPTO_EC_KEYEL_Y,
+ F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV,
+ F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X,
+ F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y,
- F_RSA | F_EC | F_OCT | F_META | JWK_META_KID,
- F_RSA | F_EC | F_OCT | F_META | JWK_META_USE,
+ F_RSA | F_EC | F_OCT | F_META | JWK_META_KID,
+ F_RSA | F_EC | F_OCT | F_META | JWK_META_USE,
- F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS,
- F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C,
- F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG,
+ F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS,
+ F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C,
+ F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG,
};
static const char *meta_names[] = {
"kty", "kid", "use", "key_ops", "x5c", "alg"
};
+
+struct lexico {
+ const char *name;
+ int idx;
+ char meta;
+} lexico_ec[] = {
+ { "alg", JWK_META_ALG, 1 },
+ { "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 },
+ { "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 },
+ { "key_ops", JWK_META_KEY_OPS, 1 },
+ { "kid", JWK_META_KID, 1 },
+ { "kty", JWK_META_KTY, 1 },
+ { "use", JWK_META_USE, 1 },
+ { "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 },
+ { "x5c", JWK_META_X5C, 1 },
+ { "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 }
+}, lexico_oct[] = {
+ { "alg", JWK_META_ALG, 1 },
+ { "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 },
+ { "key_ops", JWK_META_KEY_OPS, 1 },
+ { "kid", JWK_META_KID, 1 },
+ { "kty", JWK_META_KTY, 1 },
+ { "use", JWK_META_USE, 1 },
+ { "x5c", JWK_META_X5C, 1 }
+}, lexico_rsa[] = {
+ { "alg", JWK_META_ALG, 1 },
+ { "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 },
+ { "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 },
+ { "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 },
+ { "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 },
+ { "key_ops", JWK_META_KEY_OPS, 1 },
+ { "kid", JWK_META_KID, 1 },
+ { "kty", JWK_META_KTY, 1 },
+ { "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 },
+ { "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 },
+ { "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 },
+ { "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 },
+ { "use", JWK_META_USE, 1 },
+ { "x5c", JWK_META_X5C, 1 }
+};
+
static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 };
static const char *oct_names[] = {
@@ -117,72 +160,65 @@ static const char *ec_names[] = {
};
static const char ec_b64[] = { 0, 1, 1, 1 };
-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;
-};
-
LWS_VISIBLE int
-lws_jwk_dump(struct lws_jwk *s)
+lws_jwk_dump(struct lws_jwk *jwk)
{
const char **enames, *b64;
int elems;
int n;
- switch (s->kty) {
+ (void)enames;
+ (void)meta_names;
+
+ switch (jwk->kty) {
default:
- case LWS_GENCRYPTO_KYT_UNKNOWN:
- lwsl_err("%s: jwk %p: unknown type\n", __func__, s);
+ case LWS_GENCRYPTO_KTY_UNKNOWN:
+ lwsl_err("%s: jwk %p: unknown type\n", __func__, jwk);
return 1;
- case LWS_GENCRYPTO_KYT_OCT:
+ case LWS_GENCRYPTO_KTY_OCT:
elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT;
enames = oct_names;
b64 = oct_b64;
break;
- case LWS_GENCRYPTO_KYT_RSA:
+ case LWS_GENCRYPTO_KTY_RSA:
elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT;
enames = rsa_names;
b64 = rsa_b64;
break;
- case LWS_GENCRYPTO_KYT_EC:
+ case LWS_GENCRYPTO_KTY_EC:
elems = LWS_GENCRYPTO_EC_KEYEL_COUNT;
enames = ec_names;
b64 = ec_b64;
break;
}
- lwsl_info("%s: jwk %p\n", __func__, s);
+ lwsl_info("%s: jwk %p\n", __func__, jwk);
for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++) {
- if (s->meta[n].buf && meta_b64[n]) {
+ if (jwk->meta[n].buf && meta_b64[n]) {
lwsl_info(" meta: %s\n", meta_names[n]);
- lwsl_hexdump_info(s->meta[n].buf, s->meta[n].len);
+ lwsl_hexdump_info(jwk->meta[n].buf, jwk->meta[n].len);
}
- if (s->meta[n].buf && !meta_b64[n])
+ if (jwk->meta[n].buf && !meta_b64[n])
lwsl_info(" meta: %s: '%s'\n", meta_names[n],
- s->meta[n].buf);
+ jwk->meta[n].buf);
}
for (n = 0; n < elems; n++) {
- if (s->e[n].buf && b64[n]) {
+ if (jwk->e[n].buf && b64[n]) {
lwsl_info(" e: %s\n", enames[n]);
- lwsl_hexdump_info(s->e[n].buf, s->e[n].len);
+ lwsl_hexdump_info(jwk->e[n].buf, jwk->e[n].len);
}
- if (s->e[n].buf && !b64[n])
- lwsl_info(" e: %s: '%s'\n", enames[n], s->e[n].buf);
+ if (jwk->e[n].buf && !b64[n])
+ lwsl_info(" e: %s: '%s'\n", enames[n], jwk->e[n].buf);
}
return 0;
}
static int
-_lws_jwk_set_element_jwk(struct lws_gencrypto_keyelem *e, char *in, int len)
+_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, int len)
{
e->buf = lws_malloc(len + 1, "jwk");
if (!e->buf)
@@ -196,9 +232,9 @@ _lws_jwk_set_element_jwk(struct lws_gencrypto_keyelem *e, char *in, int len)
}
static int
-_lws_jwk_set_element_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
+_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
{
- int dec_size = ((len * 3) / 4) + 4, n;
+ int dec_size = lws_base64_size(len), n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
@@ -215,9 +251,9 @@ _lws_jwk_set_element_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
}
static int
-_lws_jwk_set_element_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len)
+_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len)
{
- int dec_size = ((len * 3) / 4) + 4, n;
+ int dec_size = lws_base64_size(len), n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
@@ -239,26 +275,29 @@ lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
int n;
for (n = 0; n < m; n++)
- if (el[n].buf)
+ if (el[n].buf) {
+ /* wipe all key material when it goes out of scope */
+ lws_explicit_bzero(el[n].buf, el[n].len);
lws_free_set_NULL(el[n].buf);
+ }
}
LWS_VISIBLE void
-lws_jwk_destroy(struct lws_jwk *s)
+lws_jwk_destroy(struct lws_jwk *jwk)
{
- lws_jwk_destroy_elements(s->e, LWS_ARRAY_SIZE(s->e));
- lws_jwk_destroy_elements(s->meta, LWS_ARRAY_SIZE(s->meta));
+ lws_jwk_destroy_elements(jwk->e, LWS_ARRAY_SIZE(jwk->e));
+ lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->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;
- unsigned int idx, poss;
+ struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user;
+ struct lws_jwk *jwk = jps->jwk;
+ unsigned int idx, poss, n;
if (reason == LEJPCB_VAL_STR_START)
- cbs->pos = 0;
+ jps->pos = 0;
if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1)
/*
@@ -269,12 +308,12 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
* ACME specifies the keys must be ordered in lexographic
* order - where kty is not first.
*/
- cbs->possible = F_RSA | F_EC | F_OCT;
+ jps->possible = F_RSA | F_EC | F_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)) {
+ if (jps->per_key_cb && jps->possible) {
+ if (jps->per_key_cb(jps->jwk, jps->user)) {
lwsl_notice("%s: user cb halts import\n",
__func__);
@@ -283,11 +322,99 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
}
/* clear it down */
- lws_jwk_destroy(cbs->s);
- cbs->possible = 0;
+ lws_jwk_destroy(jps->jwk);
+ jps->possible = 0;
}
}
+ if (reason == LEJPCB_COMPLETE) {
+
+ /*
+ * Now we saw the whole jwk and know the key type, let'jwk insist
+ * that as a whole, it must be consistent and complete.
+ *
+ * The tracking of ->possible bits from even before we know the
+ * kty already makes certain we cannot have key element members
+ * defined that are inconsistent with the key type.
+ */
+
+ for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++)
+ /*
+ * All mandataory elements for the key type
+ * must be present
+ */
+ if ((tok_map[n] & jps->possible) && (
+ ((tok_map[n] & (F_M | F_META)) == (F_M | F_META) &&
+ !jwk->meta[tok_map[n] & 0xff].buf) ||
+ ((tok_map[n] & (F_M | F_META)) == F_M &&
+ !jwk->e[tok_map[n] & 0xff].buf))) {
+ lwsl_notice("%s: missing %s\n", __func__,
+ jwk_tok[n]);
+ return -3;
+ }
+
+ /*
+ * When the key may be public or public + private, ensure the
+ * intra-key members related to that are consistent.
+ *
+ * Only RSA keys need extra care, since EC keys are already
+ * confirmed by making CRV, X and Y mandatory and only D
+ * (the singular private part) optional. For RSA, N and E are
+ * also already known to be present using mandatory checking.
+ */
+
+ /*
+ * If a private key, it must have all D, P and Q. Public key
+ * must have none of them.
+ */
+ if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
+ !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) &&
+ (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) &&
+ (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) ||
+ (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf &&
+ jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf &&
+ jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf))
+ ) {
+ lwsl_notice("%s: RSA requires D, P and Q for private\n",
+ __func__);
+ return -3;
+ }
+
+ /*
+ * If the precomputed private key terms appear, they must all
+ * appear together.
+ */
+ if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
+ !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) &&
+ (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) &&
+ (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) ||
+ (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf &&
+ jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf &&
+ jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf))
+ ) {
+ lwsl_notice("%s: RSA DP, DQ, QI must all appear "
+ "or none\n", __func__);
+ return -3;
+ }
+
+ /*
+ * The precomputed private key terms must not appear without
+ * the private key itself also appearing.
+ */
+ if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
+ !jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf &&
+ jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) {
+ lwsl_notice("%s: RSA DP, DQ, QI can appear only with "
+ "private key\n", __func__);
+ return -3;
+ }
+
+ if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA ||
+ jwk->kty == LWS_GENCRYPTO_KTY_EC) &&
+ jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf)
+ jwk->private_key = 1;
+ }
+
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
@@ -301,43 +428,44 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
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
+ * seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk
+ * not trying to tell us that it'jwk RSA now when we saw a "crv"
+ * earlier) and then reduce the possibilities to just the one that
* kty told. */
- case F_RSA | F_EC | F_OCT | F_META | JWK_META_KTY:
+ case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY:
- if (!strcmp(ctx->buf, "oct")) {
- if (!(cbs->possible & F_OCT))
+ if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) {
+ if (!(jps->possible & F_OCT))
goto elements_mismatch;
- s->kty = LWS_GENCRYPTO_KYT_OCT;
- cbs->possible = F_OCT;
+ jwk->kty = LWS_GENCRYPTO_KTY_OCT;
+ jps->possible = F_OCT;
goto cont;
}
- if (!strcmp(ctx->buf, "RSA")) {
- if (!(cbs->possible & F_RSA))
+ if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) {
+ if (!(jps->possible & F_RSA))
goto elements_mismatch;
- s->kty = LWS_GENCRYPTO_KYT_RSA;
- cbs->possible = F_RSA;
+ jwk->kty = LWS_GENCRYPTO_KTY_RSA;
+ jps->possible = F_RSA;
goto cont;
}
- if (!strcmp(ctx->buf, "EC")) {
- if (!(cbs->possible & F_EC))
+ if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) {
+ if (!(jps->possible & F_EC))
goto elements_mismatch;
- s->kty = LWS_GENCRYPTO_KYT_EC;
- cbs->possible = F_EC;
+ jwk->kty = LWS_GENCRYPTO_KTY_EC;
+ jps->possible = F_EC;
goto cont;
}
- lwsl_err("%s: Unknown KTY '%s'\n", __func__, ctx->buf);
+ lwsl_err("%s: Unknown KTY '%.*s'\n", __func__, ctx->npos,
+ ctx->buf);
return -1;
default:
cont:
- if (cbs->pos + ctx->npos >= cbs->b64max)
+ if (jps->pos + ctx->npos >= (int)sizeof(jps->b64))
goto bail;
- memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
- cbs->pos += ctx->npos;
+ memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos);
+ jps->pos += ctx->npos;
if (reason == LEJPCB_VAL_STR_CHUNK)
return 0;
@@ -345,13 +473,13 @@ cont:
/* chunking has been collated */
poss = idx & (F_RSA | F_EC | F_OCT);
- cbs->possible &= poss;
- if (!cbs->possible)
+ jps->possible &= poss;
+ if (!jps->possible)
goto elements_mismatch;
if (idx & F_META) {
- if (_lws_jwk_set_element_jwk(&s->meta[idx & 0x7f],
- cbs->b64, cbs->pos) < 0)
+ if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f],
+ jps->b64, jps->pos) < 0)
goto bail;
break;
@@ -359,24 +487,30 @@ cont:
if (idx & F_B64U) {
/* key data... do the base64 decode as needed */
- if (_lws_jwk_set_element_jwk_b64u(&s->e[idx & 0x7f],
- cbs->b64, cbs->pos)
- < 0)
+ if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f],
+ jps->b64, jps->pos) < 0)
goto bail;
+
+ if (jwk->e[idx & 0x7f].len >
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) {
+ lwsl_notice("%s: oversize keydata\n", __func__);
+ goto bail;
+ }
+
return 0;
}
if (idx & F_B64) {
+
/* cert data... do non-urlcoded base64 decode */
- if (_lws_jwk_set_element_jwk_b64(&s->e[idx & 0x7f],
- cbs->b64, cbs->pos)
- < 0)
+ if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f],
+ jps->b64, jps->pos) < 0)
goto bail;
return 0;
}
- if (_lws_jwk_set_element_jwk(&s->e[idx & 0x7f],
- cbs->b64, cbs->pos) < 0)
+ if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f],
+ jps->b64, jps->pos) < 0)
goto bail;
break;
}
@@ -392,140 +526,265 @@ bail:
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)
+void
+lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps,
+ struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
+ void *user)
{
- 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;
+ if (jwk)
+ memset(jwk, 0, sizeof(*jwk));
- 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 = F_RSA | F_EC | F_OCT;
+ jps->jwk = jwk;
+ jps->possible = F_RSA | F_EC | F_OCT;
+ jps->per_key_cb = cb;
+ jps->user = user;
+ jps->pos = 0;
- if (cb == NULL)
- tok = jwk_tok;
+ lejp_construct(jctx, cb_jwk, jps, cb ? jwk_outer_tok: jwk_tok,
+ LWS_ARRAY_SIZE(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);
+LWS_VISIBLE int
+lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
+ enum lws_gencrypto_kty kty, int bits, const char *curve)
+{
+ int n;
- if (m < 0) {
- lwsl_notice("%s: parse got %d\n", __func__, m);
+ memset(jwk, 0, sizeof(*jwk));
- return -1;
+ jwk->kty = kty;
+ jwk->private_key = 1;
+
+ switch (kty) {
+ case LWS_GENCRYPTO_KTY_RSA:
+ {
+ struct lws_genrsa_ctx ctx;
+
+ lwsl_notice("%s: generating %d bit RSA key\n", __func__, bits);
+ n = lws_genrsa_new_keypair(context, &ctx, LGRSAM_PKCS1_1_5,
+ jwk->e, bits);
+ lws_genrsa_destroy(&ctx);
+ if (n) {
+ lwsl_err("%s: problem generating RSA key\n", __func__);
+ return 1;
+ }
}
+ break;
+ case LWS_GENCRYPTO_KTY_OCT:
+ n = lws_gencrypto_bits_to_bytes(bits);
+ jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(n, "oct");
+ jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = n;
+ if (lws_get_random(context,
+ jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, n) != n) {
+ lwsl_err("%s: problem getting random\n", __func__);
+ return 1;
+ }
+ break;
+ case LWS_GENCRYPTO_KTY_EC:
+ {
+ struct lws_genec_ctx ctx;
- if (s->kty == LWS_GENCRYPTO_KYT_UNKNOWN) {
- lwsl_notice("%s: missing or unknown kyt\n", __func__);
- return -1;
+ if (!curve) {
+ lwsl_err("%s: must have a named curve\n", __func__);
+
+ return 1;
+ }
+
+ if (lws_genecdsa_create(&ctx, context, NULL))
+ return 1;
+
+ lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__,
+ curve);
+
+ n = lws_genecdsa_new_keypair(&ctx, curve, jwk->e);
+ lws_genec_destroy(&ctx);
+ if (n) {
+ lwsl_err("%s: problem generating ECDSA key\n", __func__);
+ return 1;
+ }
+ }
+ break;
+
+ case LWS_GENCRYPTO_KTY_UNKNOWN:
+ default:
+ lwsl_err("%s: unknown kty\n", __func__);
+ return 1;
}
return 0;
}
LWS_VISIBLE int
-lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
+lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
+ const char *in, size_t len)
{
- char *start = p, *end = &p[len - 1];
- int n, limit = LWS_COUNT_JWK_ELEMENTS;
+ struct lejp_ctx jctx;
+ struct lws_jwk_parse_state jps;
+ int m;
- /* RFC7638 lexicographic order requires
- * RSA: e -> kty -> n
- * oct: k -> kty
- */
+ lws_jwk_init_jps(&jctx, &jps, jwk, cb, user);
- p += lws_snprintf(p, end - p, "{");
+ m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
+ lejp_destruct(&jctx);
- switch (s->kty) {
-
- case LWS_GENCRYPTO_KYT_OCT:
- if (!s->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf)
- return -1;
-
- p += lws_snprintf(p, end - p, "\"k\":\"");
- n = lws_jws_base64_enc((const char *)s->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
- s->e[LWS_GENCRYPTO_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_GENCRYPTO_KYT_RSA:
- if (!s->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf ||
- !s->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf ||
- (private && (!s->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf ||
- !s->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf ||
- !s->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf))
- ) {
- lwsl_notice("%s: not enough elements filled\n",
- __func__);
- return -1;
- }
-
- if (!private)
- limit = LWS_GENCRYPTO_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_GENCRYPTO_KYT_EC:
- return p - start;
+ if (m < 0) {
+ lwsl_notice("%s: parse got %d\n", __func__, m);
+ lws_jwk_destroy(jwk);
+ return -1;
+ }
+ switch (jwk->kty) {
+ case LWS_GENCRYPTO_KTY_UNKNOWN:
+ lwsl_notice("%s: missing or unknown kyt\n", __func__);
+ lws_jwk_destroy(jwk);
+ return -1;
default:
break;
}
- lwsl_err("%s: unknown key type %d\n", __func__, s->kty);
+ return 0;
+}
- return -1;
+
+LWS_VISIBLE int
+lws_jwk_export(struct lws_jwk *jwk, int private, char *p, size_t len)
+{
+ char *start = p, *end = &p[len - 1];
+ int n, m, limit, first = 1, asym = 0;
+ struct lexico *l;
+
+ /* RFC7638 lexicographic order requires
+ * RSA: e -> kty -> n
+ * oct: k -> kty
+ *
+ * ie, meta and key data elements appear interleaved in name alpha order
+ */
+
+ p += lws_snprintf(p, end - p, "{");
+
+ switch (jwk->kty) {
+ case LWS_GENCRYPTO_KTY_OCT:
+ l = lexico_oct;
+ limit = LWS_ARRAY_SIZE(lexico_oct);
+ break;
+ case LWS_GENCRYPTO_KTY_RSA:
+ l = lexico_rsa;
+ limit = LWS_ARRAY_SIZE(lexico_rsa);
+ asym = 1;
+ break;
+ case LWS_GENCRYPTO_KTY_EC:
+ l = lexico_ec;
+ limit = LWS_ARRAY_SIZE(lexico_ec);
+ asym = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ for (n = 0; n < limit; n++) {
+ const char *q, *q_end;
+ char tok[12];
+ int pos = 0, f = 1;
+
+ if ((l->meta & 1) && (jwk->meta[l->idx].buf ||
+ l->idx == (int)JWK_META_KTY)) {
+
+ switch (l->idx) {
+ case JWK_META_KTY:
+ if (!first)
+ *p++ = ',';
+ first = 0;
+ p += lws_snprintf(p, end - p, "\"%s\":\"%s\"",
+ l->name, kty_names[jwk->kty]);
+ break;
+ case JWK_META_KEY_OPS:
+ if (!first)
+ *p++ = ',';
+ first = 0;
+ q = (const char *)jwk->meta[l->idx].buf;
+ q_end = q + jwk->meta[l->idx].len;
+
+ p += lws_snprintf(p, end - p,
+ "\"%s\":[", l->name);
+ /*
+ * For the public version, usages that
+ * require the private part must be
+ * snipped
+ */
+
+ while (q < q_end) {
+ if (*q != ' ' && pos < (int)sizeof(tok) - 1) {
+ tok[pos++] = *q++;
+ if (q != q_end)
+ continue;
+ }
+ tok[pos] = '\0';
+ pos = 0;
+ if (private || !asym ||
+ (strcmp(tok, "sign") &&
+ strcmp(tok, "encrypt"))) {
+ if (!f)
+ *p++ = ',';
+ f = 0;
+ p += lws_snprintf(p, end - p,
+ "\"%s\"", tok);
+ }
+ q++;
+ }
+
+ *p++ = ']';
+
+ break;
+
+ default:
+ /* both sig and enc require asym private key */
+ if (!private && asym && l->idx == (int)JWK_META_USE)
+ break;
+ if (!first)
+ *p++ = ',';
+ first = 0;
+ p += lws_snprintf(p, end - p, "\"%s\":\"%.*s\"",
+ l->name, jwk->meta[l->idx].len,
+ jwk->meta[l->idx].buf);
+ break;
+ }
+ }
+
+ if ((!(l->meta & 1)) && jwk->e[l->idx].buf &&
+ (private || !(l->meta & 2))) {
+ if (!first)
+ *p++ = ',';
+ first = 0;
+
+ p += lws_snprintf(p, end - p, "\"%s\":\"", l->name);
+
+ if (jwk->kty == LWS_GENCRYPTO_KTY_EC &&
+ l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV)
+ m = lws_snprintf(p, end - p, "%.*s",
+ jwk->e[l->idx].len,
+ (const char *)jwk->e[l->idx].buf);
+ else
+ m = lws_jws_base64_enc(
+ (const char *)jwk->e[l->idx].buf,
+ jwk->e[l->idx].len, p, end - p - 4);
+ if (m < 0) {
+ lwsl_notice("%s: enc failed\n", __func__);
+ return -1;
+ }
+ p += m;
+ p += lws_snprintf(p, end - p, "\"");
+ }
+
+ l++;
+ }
+
+ p += lws_snprintf(p, end - p, "}\n");
+
+ return p - start;
}
LWS_VISIBLE int
-lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
+lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32)
{
struct lws_genhash_ctx hash_ctx;
int tmpsize = 2536, n;
@@ -533,7 +792,7 @@ lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
- n = lws_jwk_export(s, 0, tmp, tmpsize);
+ n = lws_jwk_export(jwk, 0, tmp, tmpsize);
if (n < 0)
goto bail;
@@ -559,7 +818,20 @@ bail:
}
LWS_VISIBLE int
-lws_jwk_load(struct lws_jwk *s, const char *filename,
+lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
+ const char *in, int len)
+{
+ jwk->meta[idx].buf = lws_malloc(len, __func__);
+ if (!jwk->meta[idx].buf)
+ return 1;
+ jwk->meta[idx].len = len;
+ memcpy(jwk->meta[idx].buf, in, len);
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_jwk_load(struct lws_jwk *jwk, const char *filename,
lws_jwk_key_import_callback cb, void *user)
{
int buflen = 4096;
@@ -573,7 +845,7 @@ lws_jwk_load(struct lws_jwk *s, const char *filename,
if (n < 0)
goto bail;
- n = lws_jwk_import(s, cb, user, buf, n);
+ n = lws_jwk_import(jwk, cb, user, buf, n);
lws_free(buf);
return n;
@@ -584,7 +856,7 @@ bail:
}
LWS_VISIBLE int
-lws_jwk_save(struct lws_jwk *s, const char *filename)
+lws_jwk_save(struct lws_jwk *jwk, const char *filename)
{
int buflen = 4096;
char *buf = lws_malloc(buflen, "jwk-save");
@@ -593,7 +865,7 @@ lws_jwk_save(struct lws_jwk *s, const char *filename)
if (!buf)
return -1;
- n = lws_jwk_export(s, 1, buf, buflen);
+ n = lws_jwk_export(jwk, 1, buf, buflen);
if (n < 0)
goto bail;
diff --git a/lib/jose/jws/jose.c b/lib/jose/jws/jose.c
index a72014325..5fb51b71a 100644
--- a/lib/jose/jws/jose.c
+++ b/lib/jose/jws/jose.c
@@ -23,6 +23,7 @@
*/
#include "core/private.h"
+#include "jose/private.h"
#include
@@ -54,8 +55,11 @@ static const char * const jws_jose[] = {
};
struct jose_cb_args {
- const struct lws_jose_jwe_alg **args;
- const struct lws_jose_jwe_alg **enc_args; /* null for jws case */
+ struct lws_jose *jose;
+ struct lejp_ctx jwk_jctx; /* fake lejp context used to parse epk */
+ struct lws_jwk_parse_state jps; /* fake jwk parse state */
+ char *temp;
+ int *temp_len;
int is_jwe;
};
@@ -63,6 +67,28 @@ static signed char
lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
{
struct jose_cb_args *args = (struct jose_cb_args *)ctx->user;
+ int n;
+
+ /*
+ * In JOSE JSON, the element "epk" contains a fully-formed JWK.
+ *
+ * For JOSE paths beginning "epk.", we pass them through to a JWK
+ * LEJP subcontext to parse using the JWK parser directly.
+ */
+
+ if (args->is_jwe && !strncmp(ctx->path, "epk.", 4)) {
+ memcpy(args->jwk_jctx.path, ctx->path + 4,
+ sizeof(ctx->path) - 4);
+ memcpy(args->jwk_jctx.buf, ctx->buf, ctx->npos);
+ args->jwk_jctx.npos = ctx->npos;
+
+ if (!ctx->path_match)
+ args->jwk_jctx.path_match = 0;
+ lejp_check_path_match(&args->jwk_jctx);
+
+ if (args->jwk_jctx.path_match)
+ args->jwk_jctx.callback(&args->jwk_jctx, reason);
+ }
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
@@ -79,7 +105,8 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
*/
if (!args->is_jwe &&
- lws_gencrypto_jws_alg_to_definition(ctx->buf, args->args)) {
+ lws_gencrypto_jws_alg_to_definition(ctx->buf,
+ &args->jose->alg)) {
lwsl_notice("%s: unknown alg '%s'\n", __func__,
ctx->buf);
@@ -87,8 +114,9 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
}
if (args->is_jwe &&
- lws_gencrypto_jwe_alg_to_definition(ctx->buf, args->args)) {
- lwsl_notice("%s: unknown alg '%s'\n", __func__,
+ lws_gencrypto_jwe_alg_to_definition(ctx->buf,
+ &args->jose->alg)) {
+ lwsl_notice("%s: unknown JWE alg '%s'\n", __func__,
ctx->buf);
return -1;
@@ -127,11 +155,13 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
/* past here, JWE only */
- case LJJHI_ENC: /* JWE only: Optional: string */
- if (!args->is_jwe)
+ case LJJHI_ENC: /* JWE only: Mandatory: string */
+ if (!args->is_jwe) {
+ lwsl_info("%s: enc in jws\n", __func__);
return -1;
+ }
if (lws_gencrypto_jwe_enc_to_definition(ctx->buf,
- args->enc_args)) {
+ &args->jose->enc_alg)) {
lwsl_notice("%s: unknown enc '%s'\n", __func__,
ctx->buf);
@@ -142,41 +172,45 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */
if (!args->is_jwe)
return -1;
- break;
+ goto append_string;
case LJJHI_EPK: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
+ /* Ephemeral key... this JSON subsection is actually a JWK */
+ lwsl_err("LJJHI_EPK\n");
break;
case LJJHI_APU: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
- break;
+ /* Agreement Party U */
+ goto append_string;
case LJJHI_APV: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
- break;
+ /* Agreement Party V */
+ goto append_string;
case LJJHI_IV: /* Additional arg for JWE AES */
if (!args->is_jwe)
return -1;
- break;
+ goto append_string;
case LJJHI_TAG: /* Additional arg for JWE AES */
if (!args->is_jwe)
return -1;
- break;
+ goto append_string;
case LJJHI_P2S: /* Additional arg for JWE PBES2 */
if (!args->is_jwe)
return -1;
- break;
+ goto append_string;
case LJJHI_P2C: /* Additional arg for JWE PBES2 */
if (!args->is_jwe)
return -1;
- break;
+ goto append_string;
/* ignore what we don't understand */
@@ -185,21 +219,76 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
}
return 0;
+
+append_string:
+
+ if (*args->temp_len < ctx->npos) {
+ lwsl_err("%s: out of parsing space\n", __func__);
+ return -1;
+ }
+
+ if (!args->jose->e[ctx->path_match - 1].buf) {
+ args->jose->e[ctx->path_match - 1].buf = (uint8_t *)args->temp;
+ args->jose->e[ctx->path_match - 1].len = 0;
+ }
+
+ memcpy(args->temp, ctx->buf, ctx->npos);
+ args->temp += ctx->npos;
+ *args->temp_len -= ctx->npos;
+ args->jose->e[ctx->path_match - 1].len += ctx->npos;
+
+ if (reason == LEJPCB_VAL_STR_END) {
+ n = lws_b64_decode_string_len(
+ (const char *)args->jose->e[ctx->path_match - 1].buf,
+ args->jose->e[ctx->path_match - 1].len,
+ (char *)args->jose->e[ctx->path_match - 1].buf,
+ args->jose->e[ctx->path_match - 1].len + 1);
+ if (n < 0) {
+ lwsl_err("%s: b64 decode failed\n", __func__);
+ return -1;
+ }
+
+ args->temp -= args->jose->e[ctx->path_match - 1].len - n - 1;
+ *args->temp_len +=
+ args->jose->e[ctx->path_match - 1].len - n - 1;
+
+ args->jose->e[ctx->path_match - 1].len = n;
+ }
+
+ return 0;
+}
+
+void
+lws_jose_init(struct lws_jose *jose)
+{
+ memset(jose, 0, sizeof(*jose));
+}
+
+void
+lws_jose_destroy(struct lws_jose *jose)
+{
+// lws_gencrypto_destroy_elements(jose->e, LWS_ARRAY_SIZE(jose->e));
+ lws_jwk_destroy(&jose->jwk_ephemeral);
}
static int
-lws_jose_parse(const struct lws_jose_jwe_alg **_args,
- const struct lws_jose_jwe_alg **enc_args,
- uint8_t *buf, int n, int is_jwe)
+lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n,
+ char *temp, int *temp_len, int is_jwe)
{
struct lejp_ctx jctx;
struct jose_cb_args args;
int m;
- args.args = _args;
- args.enc_args = enc_args;
+ if (is_jwe)
+ /* prepare a context for JOSE epk ephemeral jwk parsing */
+ lws_jwk_init_jps(&args.jwk_jctx, &args.jps,
+ &jose->jwk_ephemeral, NULL, NULL);
+
args.is_jwe = is_jwe;
+ args.temp = temp;
+ args.temp_len = temp_len;
+ args.jose = jose;
lejp_construct(&jctx, lws_jws_jose_cb, &args, jws_jose,
LWS_ARRAY_SIZE(jws_jose));
@@ -207,7 +296,7 @@ lws_jose_parse(const struct lws_jose_jwe_alg **_args,
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
lejp_destruct(&jctx);
if (m < 0) {
- lwsl_notice("parse got %d\n", m);
+ lwsl_notice("%s: parse %.*s returned %d\n", __func__, n, buf, m);
return -1;
}
@@ -215,16 +304,17 @@ lws_jose_parse(const struct lws_jose_jwe_alg **_args,
}
int
-lws_jws_parse_jose(const struct lws_jose_jwe_alg **args,
- uint8_t *buf, int n)
+lws_jws_parse_jose(struct lws_jose *jose,
+ const char *buf, int len, char *temp, int *temp_len)
{
- return lws_jose_parse(args, NULL, buf, n, 0);
+ return lws_jose_parse(jose, (const uint8_t *)buf, len,
+ temp, temp_len, 0);
}
int
-lws_jwe_parse_jose(const struct lws_jose_jwe_alg **args,
- const struct lws_jose_jwe_alg **enc_args,
- uint8_t *buf, int n)
+lws_jwe_parse_jose(struct lws_jose *jose,
+ const char *buf, int len, char *temp, int *temp_len)
{
- return lws_jose_parse(args, enc_args, buf, n, 1);
+ return lws_jose_parse(jose,
+ (const uint8_t *)buf, len, temp, temp_len, 1);
}
diff --git a/lib/jose/jws/jws.c b/lib/jose/jws/jws.c
index dae98fd1c..fd4a4cb27 100644
--- a/lib/jose/jws/jws.c
+++ b/lib/jose/jws/jws.c
@@ -22,14 +22,129 @@
#include "core/private.h"
#include "private.h"
+LWS_VISIBLE void
+lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
+ struct lws_context *context)
+{
+ memset(jws, 0, sizeof(*jws));
+ jws->context = context;
+ jws->jwk = jwk;
+}
+
+static void
+lws_jws_compact_map_bzero(struct lws_jws_compact_map *map)
+{
+ int n;
+
+ /* no need to scrub first jose header element (it can be canned then) */
+
+ for (n = 1; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++)
+ if (map->buf[n])
+ lws_explicit_bzero((void *)map->buf[n], map->len[n]);
+}
+
+LWS_VISIBLE void
+lws_jws_destroy(struct lws_jws *jws)
+{
+ lws_jws_compact_map_bzero(&jws->map);
+ jws->jwk = NULL;
+}
+
+LWS_VISIBLE int
+lws_jws_dup_element(struct lws_jws_compact_map *map, int idx,
+ char *temp, int *temp_len, const void *in, size_t in_len,
+ size_t actual_alloc)
+{
+ if (!actual_alloc)
+ actual_alloc = in_len;
+
+ if ((size_t)*temp_len < actual_alloc)
+ return -1;
+
+ map->len[idx] = in_len;
+ map->buf[idx] = temp;
+
+ memcpy((void *)map->buf[idx], in, in_len);
+ *temp_len -= actual_alloc;
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_jws_encode_b64_element(struct lws_jws_compact_map *map, int idx,
+ char *temp, int *temp_len, const void *in,
+ size_t in_len)
+{
+ int n;
+
+ if (*temp_len < lws_base64_size((int)in_len))
+ return -1;
+
+ n = lws_jws_base64_enc(in, in_len, temp, *temp_len);
+ if (n < 0)
+ return -1;
+
+ map->len[idx] = n;
+ map->buf[idx] = temp;
+
+ *temp_len -= n;
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_jws_randomize_element(struct lws_context *context,
+ struct lws_jws_compact_map *map,
+ int idx, char *temp, int *temp_len, size_t random_len,
+ size_t actual_alloc)
+{
+ if (!actual_alloc)
+ actual_alloc = random_len;
+
+ if ((size_t)*temp_len < actual_alloc)
+ return -1;
+
+ map->len[idx] = random_len;
+ map->buf[idx] = temp;
+
+ if (lws_get_random(context, temp, random_len) != (int)random_len) {
+ lwsl_err("Problem getting random\n");
+ return -1;
+ }
+
+ *temp_len -= actual_alloc;
+
+ return 0;
+}
+
+LWS_VISIBLE int
+lws_jws_alloc_element(struct lws_jws_compact_map *map, int idx, char *temp,
+ int *temp_len, size_t len, size_t actual_alloc)
+{
+ if (!actual_alloc)
+ actual_alloc = len;
+
+ if ((size_t)*temp_len < actual_alloc)
+ return -1;
+
+ map->len[idx] = len;
+ map->buf[idx] = temp;
+ *temp_len -= actual_alloc;
+
+ return 0;
+}
+
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)
+ if (n < 0) {
+ lwsl_notice("%s: in len %d too large for %d out buf\n",
+ __func__, (int)in_len, (int)out_max);
return n; /* too large for output buffer */
+ }
/* trim the terminal = */
while (n && out[n - 1] == '=')
@@ -40,6 +155,99 @@ lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
return n;
}
+LWS_VISIBLE int
+lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_compact_map *map)
+{
+ int me = 0;
+
+ memset(map, 0, sizeof(*map));
+
+ map->buf[me] = (char *)in;
+ map->len[me] = 0;
+
+ while (len--) {
+ if (*in++ == '.') {
+ if (++me == LWS_JWS_MAX_COMPACT_BLOCKS)
+ return -1;
+ map->buf[me] = (char *)in;
+ map->len[me] = 0;
+ continue;
+ }
+ map->len[me]++;
+ }
+
+ return me + 1;
+}
+
+/* b64 in, map contains decoded elements, if non-NULL,
+ * map_b64 set to b64 elements
+ */
+
+LWS_VISIBLE int
+lws_jws_compact_decode(const char *in, int len, struct lws_jws_compact_map *map,
+ struct lws_jws_compact_map *map_b64, char *out,
+ int *out_len)
+{
+ int blocks, n, m = 0;
+
+ if (!map_b64)
+ map_b64 = map;
+
+ memset(map_b64, 0, sizeof(*map_b64));
+ memset(map, 0, sizeof(*map));
+
+ blocks = lws_jws_b64_compact_map(in, len, map_b64);
+
+ if (blocks > LWS_JWS_MAX_COMPACT_BLOCKS)
+ return -1;
+
+ while (m < blocks) {
+ n = lws_b64_decode_string_len(map_b64->buf[m],
+ map_b64->len[m], out, *out_len);
+ if (n < 0) {
+ lwsl_err("%s: b64 decode failed\n", __func__);
+ return -1;
+ }
+ /* replace the map entry with the decoded content */
+ map->buf[m] = out;
+ map->len[m++] = n;
+ out += n;
+ *out_len -= n;
+
+ if (*out_len < 1)
+ return -1;
+ }
+
+ return blocks;
+}
+
+static int
+lws_jws_compact_decode_map(struct lws_jws_compact_map *map_b64,
+ struct lws_jws_compact_map *map, char *out,
+ int *out_len)
+{
+ int n, m = 0;
+
+ for (n = 0; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) {
+ n = lws_b64_decode_string_len(map_b64->buf[m],
+ map_b64->len[m], out, *out_len);
+ if (n < 0) {
+ lwsl_err("%s: b64 decode failed\n", __func__);
+ return -1;
+ }
+ /* replace the map entry with the decoded content */
+ map->buf[m] = out;
+ map->len[m++] = n;
+ out += n;
+ *out_len -= n;
+
+ if (*out_len < 1)
+ return -1;
+ }
+
+ return 0;
+}
+
LWS_VISIBLE int
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
char *end)
@@ -62,71 +270,81 @@ lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
return (*p) - p_entry;
}
-static int
-lws_jws_find_sig(const char *in, size_t len)
+LWS_VISIBLE int
+lws_jws_compact_encode(struct lws_jws_compact_map *map_b64, /* b64-encoded */
+ const struct lws_jws_compact_map *map, /* non-b64 */
+ char *buf, int *len)
{
- const char *p = in + len - 1;
+ int n, m;
- while (len--)
- if (*p == '.')
- return (p + 1) - in;
- else
- p--;
+ for (n = 0; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) {
+ if (!map->buf[n]) {
+ map_b64->buf[n] = NULL;
+ map_b64->len[n] = 0;
+ continue;
+ }
+ m = lws_jws_base64_enc(map->buf[n], map->len[n], buf, *len);
+ if (m < 0)
+ return -1;
+ buf += m;
+ *len -= m;
+ if (*len < 1)
+ return -1;
+ }
- lwsl_notice("%s failed\n", __func__);
- return -1;
+ return 0;
}
+/*
+ * This takes both a base64 -encoded map and a plaintext map.
+ *
+ * JWS demands base-64 encoded elements for hash computation and at least for
+ * the JOSE header and signature, decoded versions too.
+ */
+
LWS_VISIBLE int
-lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
- struct lws_context *context)
+lws_jws_sig_confirm(struct lws_jws_compact_map *map_b64, /* b64-encoded */
+ struct lws_jws_compact_map *map, /* non-b64 */
+ struct lws_jwk *jwk, struct lws_context *context)
{
- int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
enum enum_genrsa_mode padding = LGRSAM_PKCS1_1_5;
+ char temp[256];
+ int n, h_len, b = 3, temp_len = sizeof(temp);
uint8_t digest[LWS_GENHASH_LARGEST];
- const struct lws_jose_jwe_alg *args = NULL;
struct lws_genhash_ctx hash_ctx;
struct lws_genec_ctx ecdsactx;
struct lws_genrsa_ctx rsactx;
struct lws_genhmac_ctx ctx;
- char buf[2048];
+ struct lws_jose jose;
- /* 1) there has to be a signature */
+ lws_jose_init(&jose);
- if (sig_pos < 0)
- return -1;
+ /* only valid if no signature or key */
+ if (!map_b64->buf[LJWS_SIG] && !map->buf[LJWS_UHDR])
+ b = 2;
- /* 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);
+ if (lws_jws_parse_jose(&jose, map->buf[LJWS_JOSE], map->len[LJWS_JOSE],
+ temp, &temp_len) < 0) {
+ lwsl_notice("%s: parse failed\n", __func__);
return -1;
}
- /* 5) decode the B64URL signature part into buf / m */
+ if (!strcmp(jose.alg->alg, "none")) {
+ /* "none" compact serialization has 2 blocks: jose.payload */
+ if (b != 2 || jwk)
+ return -1;
- m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
- buf, sizeof(buf) - 1);
+ /* the lack of a key matches the lack of a signature */
+ return 0;
+ }
- switch (args->algtype_signing) {
+ /* all other have 3 blocks: jose.payload.sig */
+ if (b != 3 || !jwk) {
+ lwsl_notice("%s: %d blocks\n", __func__, b);
+ return -1;
+ }
+
+ switch (jose.alg->algtype_signing) {
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS:
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP:
padding = LGRSAM_PKCS1_OAEP_PSS;
@@ -135,67 +353,87 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
/* RSASSA-PKCS1-v1_5 or OAEP using SHA-256/384/512 */
- if (jwk->kty != LWS_GENCRYPTO_KYT_RSA)
+ if (jwk->kty != LWS_GENCRYPTO_KTY_RSA)
return -1;
/* 6(RSA): compute the hash of the payload into "digest" */
- if (lws_genhash_init(&hash_ctx, args->hash_type))
+ if (lws_genhash_init(&hash_ctx, jose.alg->hash_type))
return -1;
- if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
+ /*
+ * JWS Signing Input value:
+ *
+ * BASE64URL(UTF8(JWS Protected Header)) || '.' ||
+ * BASE64URL(JWS Payload)
+ */
+
+ if (lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_JOSE],
+ map_b64->len[LJWS_JOSE]) ||
+ lws_genhash_update(&hash_ctx, ".", 1) ||
+ lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_PYLD],
+ map_b64->len[LJWS_PYLD]) ||
+ lws_genhash_destroy(&hash_ctx, digest)) {
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
- if (lws_genhash_destroy(&hash_ctx, digest))
- return -1;
+ h_len = lws_genhash_size(jose.alg->hash_type);
- h_len = lws_genhash_size(args->hash_type);
-
- if (lws_genrsa_create(&rsactx, jwk->e, context, padding)) {
+ if (lws_genrsa_create(&rsactx, jwk->e, context, padding,
+ LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
- n = lws_genrsa_hash_sig_verify(&rsactx, digest, args->hash_type,
- (uint8_t *)buf, m);
+ n = lws_genrsa_hash_sig_verify(&rsactx, digest,
+ jose.alg->hash_type,
+ (uint8_t *)map->buf[LJWS_SIG],
+ map->len[LJWS_SIG]);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
- lwsl_notice("decrypt fail\n");
+ lwsl_notice("%s: decrypt fail\n", __func__);
return -1;
}
break;
- case LWS_JOSE_ENCTYPE_NONE:
+ case LWS_JOSE_ENCTYPE_NONE: /* HSxxx */
/* SHA256/384/512 HMAC */
- h_len = lws_genhmac_size(args->hmac_type);
- if (m < 0 || m != h_len)
- return -1;
+ h_len = lws_genhmac_size(jose.alg->hmac_type);
/* 6) compute HMAC over payload */
- if (lws_genhmac_init(&ctx, args->hmac_type,
+ if (lws_genhmac_init(&ctx, jose.alg->hmac_type,
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len))
return -1;
- if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
+ /*
+ * JWS Signing Input value:
+ *
+ * BASE64URL(UTF8(JWS Protected Header)) || '.' ||
+ * BASE64URL(JWS Payload)
+ */
+
+ if (lws_genhmac_update(&ctx, map_b64->buf[LJWS_JOSE],
+ map_b64->len[LJWS_JOSE]) ||
+ lws_genhmac_update(&ctx, ".", 1) ||
+ lws_genhmac_update(&ctx, map_b64->buf[LJWS_PYLD],
+ map_b64->len[LJWS_PYLD]) ||
+ lws_genhmac_destroy(&ctx, digest)) {
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)) {
+ if (lws_timingsafe_bcmp(digest, map->buf[2], h_len)) {
lwsl_notice("digest mismatch\n");
return -1;
@@ -207,10 +445,10 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
/* ECDSA using SHA-256/384/512 */
- /* the key coming in with this makes sense, right? */
+ /* Confirm the key coming in with this makes sense */
/* has to be an EC key :-) */
- if (jwk->kty != LWS_GENCRYPTO_KYT_EC)
+ if (jwk->kty != LWS_GENCRYPTO_KTY_EC)
return -1;
/* key must state its curve */
@@ -219,23 +457,41 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
/* key must match the selected alg curve */
if (strcmp((const char *)jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
- args->curve_name))
+ jose.alg->curve_name))
return -1;
- /* compute the hash of the payload into "digest" */
+ /*
+ * JWS Signing Input value:
+ *
+ * BASE64URL(UTF8(JWS Protected Header)) || '.' ||
+ * BASE64URL(JWS Payload)
+ *
+ * Validating the JWS Signature is a bit different from the
+ * previous examples. We need to split the 64 member octet
+ * sequence of the JWS Signature (which is base64url decoded
+ * from the value encoded in the JWS representation) into two
+ * 32 octet sequences, the first representing R and the second
+ * S. We then pass the public key (x, y), the signature (R, S),
+ * and the JWS Signing Input (which is the initial substring of
+ * the JWS Compact Serialization representation up until but not
+ * including the second period character) to an ECDSA signature
+ * verifier that has been configured to use the P-256 curve with
+ * the SHA-256 hash function.
+ */
- if (lws_genhash_init(&hash_ctx, args->hash_type))
- return -1;
-
- if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
+ if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
+ lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_JOSE],
+ map_b64->len[LJWS_JOSE]) ||
+ lws_genhash_update(&hash_ctx, ".", 1) ||
+ lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_PYLD],
+ map_b64->len[LJWS_PYLD]) ||
+ lws_genhash_destroy(&hash_ctx, digest)) {
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);
+ h_len = lws_genhash_size(jose.alg->hash_type);
if (lws_genecdsa_create(&ecdsactx, context, NULL)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
@@ -249,13 +505,14 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
return -1;
}
- n = lws_genecdsa_hash_sig_verify(&ecdsactx, digest,
- args->hash_type,
- (uint8_t *)buf, m);
-
+ n = lws_genecdsa_hash_sig_verify_jws(&ecdsactx, digest,
+ jose.alg->hash_type,
+ jose.alg->keybits_fixed,
+ (uint8_t *)map->buf[LJWS_SIG],
+ map->len[LJWS_SIG]);
lws_genec_destroy(&ecdsactx);
if (n < 0) {
- lwsl_notice("decrypt fail\n");
+ lwsl_notice("%s: verify fail\n", __func__);
return -1;
}
@@ -269,60 +526,121 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
return 0;
}
+/* it's already a b64 map, we will make a temp plain version */
+
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,
- const struct lws_jose_jwe_alg *args,
- struct lws_jwk *jwk,
- struct lws_context *context)
+lws_jws_sig_confirm_compact_b64_map(struct lws_jws_compact_map *map_b64,
+ struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len)
{
- enum enum_genrsa_mode padding = LGRSAM_PKCS1_1_5;
+ struct lws_jws_compact_map map;
+ int n;
+
+ n = lws_jws_compact_decode_map(map_b64, &map, temp, temp_len);
+ if (n > 3 || n < 0)
+ return -1;
+
+ return lws_jws_sig_confirm(map_b64, &map, jwk, context);
+}
+
+/*
+ * it's already a compact / concatenated b64 string, we will make a temp
+ * plain version
+ */
+
+LWS_VISIBLE int
+lws_jws_sig_confirm_compact_b64(const char *in, size_t len,
+ struct lws_jws_compact_map *map,
+ struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len)
+{
+ struct lws_jws_compact_map map_b64;
+ int n;
+
+ if (lws_jws_b64_compact_map(in, len, &map_b64) < 0)
+ return -1;
+
+ n = lws_jws_compact_decode(in, len, map, &map_b64, temp, temp_len);
+ if (n > 3 || n < 0)
+ return -1;
+
+ return lws_jws_sig_confirm(&map_b64, map, jwk, context);
+}
+
+/* it's already plain, we will make a temp b64 version */
+
+LWS_VISIBLE int
+lws_jws_sig_confirm_compact(struct lws_jws_compact_map *map, struct lws_jwk *jwk,
+ struct lws_context *context, char *temp,
+ int *temp_len)
+{
+ struct lws_jws_compact_map map_b64;
+
+ if (lws_jws_compact_encode(&map_b64, map, temp, temp_len) < 0)
+ return -1;
+
+ return lws_jws_sig_confirm(&map_b64, map, jwk, context);
+}
+
+
+LWS_VISIBLE int
+lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws,
+ char *b64_sig, size_t sig_len)
+{
+ enum enum_genrsa_mode pad = LGRSAM_PKCS1_1_5;
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhash_ctx hash_ctx;
struct lws_genec_ctx ecdsactx;
struct lws_genrsa_ctx rsactx;
- int n, m;
uint8_t *buf;
+ int n, m;
+
+ if (jose->alg->hash_type == LWS_GENHASH_TYPE_UNKNOWN &&
+ jose->alg->hmac_type == LWS_GENHMAC_TYPE_UNKNOWN &&
+ !strcmp(jose->alg->alg, "none"))
+ return 0;
+
+ if (lws_genhash_init(&hash_ctx, jose->alg->hash_type) ||
+ lws_genhash_update(&hash_ctx, jws->map_b64.buf[LJWS_JOSE],
+ jws->map_b64.len[LJWS_JOSE]) ||
+ lws_genhash_update(&hash_ctx, ".", 1) ||
+ lws_genhash_update(&hash_ctx, jws->map_b64.buf[LJWS_PYLD],
+ jws->map_b64.len[LJWS_PYLD]) ||
+ lws_genhash_destroy(&hash_ctx, digest)) {
+ lws_genhash_destroy(&hash_ctx, NULL);
- if (lws_genhash_init(&hash_ctx, args->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;
-
- switch (args->algtype_signing) {
+ switch (jose->alg->algtype_signing) {
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS:
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP:
- padding = LGRSAM_PKCS1_OAEP_PSS;
+ pad = LGRSAM_PKCS1_OAEP_PSS;
/* fallthru */
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5:
- if (jwk->kty != LWS_GENCRYPTO_KYT_RSA)
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA)
return -1;
- if (lws_genrsa_create(&rsactx, jwk->e, context, padding)) {
+ if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
+ pad, LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
- n = jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
- buf = lws_malloc(n, "jws sign");
+ n = jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
+ buf = lws_malloc(lws_base64_size(n), "jws sign");
if (!buf)
return -1;
- n = lws_genrsa_hash_sign(&rsactx, digest, args->hash_type, buf, n);
+ n = lws_genrsa_hash_sign(&rsactx, digest, jose->alg->hash_type,
+ buf, n);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
+ lwsl_err("%s: lws_genrsa_hash_sign failed\n", __func__);
lws_free(buf);
return -1;
@@ -330,12 +648,15 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
lws_free(buf);
+ if (n < 0) {
+ lwsl_err("%s: lws_jws_base64_enc failed\n", __func__);
+ }
return n;
case LWS_JOSE_ENCTYPE_NONE:
return lws_jws_base64_enc((char *)digest,
- lws_genhash_size(args->hash_type),
+ lws_genhash_size(jose->alg->hash_type),
b64_sig, sig_len);
case LWS_JOSE_ENCTYPE_ECDSA:
/* ECDSA using SHA-256/384/512 */
@@ -343,47 +664,53 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
/* the key coming in with this makes sense, right? */
/* has to be an EC key :-) */
- if (jwk->kty != LWS_GENCRYPTO_KYT_EC)
+ if (jws->jwk->kty != LWS_GENCRYPTO_KTY_EC)
return -1;
/* key must state its curve */
- if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf)
+ if (!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf)
return -1;
/* must have all his pieces for a private key */
- if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].buf ||
- !jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf ||
- !jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
+ if (!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].buf ||
+ !jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf ||
+ !jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
return -1;
/* key must match the selected alg curve */
- if (strcmp((const char *)jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
- args->curve_name))
+ if (strcmp((const char *)
+ jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
+ jose->alg->curve_name))
return -1;
- if (lws_genecdsa_create(&ecdsactx, context, NULL)) {
+ if (lws_genecdsa_create(&ecdsactx, jws->context, NULL)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
- if (lws_genecdsa_set_key(&ecdsactx, jwk->e)) {
+ if (lws_genecdsa_set_key(&ecdsactx, jws->jwk->e)) {
lws_genec_destroy(&ecdsactx);
lwsl_notice("%s: ec key import fail\n", __func__);
return -1;
}
- m = jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len;
+ m = lws_gencrypto_bits_to_bytes(jose->alg->keybits_fixed) * 2;
buf = lws_malloc(m, "jws sign");
if (!buf)
return -1;
- n = lws_genecdsa_hash_sign(&ecdsactx, digest, args->hash_type,
- (uint8_t *)buf, m);
+ n = lws_genecdsa_hash_sign_jws(&ecdsactx, digest,
+ jose->alg->hash_type,
+ jose->alg->keybits_fixed,
+ (uint8_t *)buf, m);
lws_genec_destroy(&ecdsactx);
if (n < 0) {
- lwsl_notice("decrypt fail\n");
+ lws_free(buf);
+ lwsl_notice("%s: lws_genecdsa_hash_sign_jws fail\n",
+ __func__);
return -1;
}
+
n = lws_jws_base64_enc((char *)buf, m, b64_sig, sig_len);
lws_free(buf);
@@ -396,8 +723,57 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
/* unknown key type */
return -1;
-
-hash_fail:
- lws_genhash_destroy(&hash_ctx, NULL);
- return -1;
+}
+
+/*
+ * Flattened JWS JSON:
+ *
+ * {
+ * "payload": "",
+ * "protected": "",
+ * "header": ,
+ * "signature": ""
+ * }
+ */
+
+LWS_VISIBLE int
+lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len)
+{
+ size_t n = 0;
+
+ if (len < 1)
+ return 1;
+
+ n += lws_snprintf(flattened + n, len - n , "{\"payload\": \"%s\",\n",
+ jws->map_b64.buf[LJWS_PYLD]);
+
+ n += lws_snprintf(flattened + n, len - n , " \"protected\": \"%s\",\n",
+ jws->map_b64.buf[LJWS_JOSE]);
+
+ if (jws->map_b64.buf[LJWS_UHDR])
+ n += lws_snprintf(flattened + n, len - n , " \"header\": %s,\n",
+ jws->map_b64.buf[LJWS_UHDR]);
+
+ n += lws_snprintf(flattened + n, len - n , " \"signature\": \"%s\"}\n",
+ jws->map_b64.buf[LJWS_SIG]);
+
+ return (n >= len - 1);
+}
+
+LWS_VISIBLE int
+lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len)
+{
+ size_t n = 0;
+
+ if (len < 1)
+ return 1;
+
+ n += lws_snprintf(compact + n, len - n , "%.*s",
+ jws->map_b64.len[LJWS_JOSE], jws->map_b64.buf[LJWS_JOSE]);
+ n += lws_snprintf(compact + n, len - n , ".%.*s",
+ jws->map_b64.len[LJWS_PYLD], jws->map_b64.buf[LJWS_PYLD]);
+ n += lws_snprintf(compact + n, len - n , ".%.*s",
+ jws->map_b64.len[LJWS_SIG], jws->map_b64.buf[LJWS_SIG]);
+
+ return n >= len - 1;
}
diff --git a/lib/jose/jws/private.h b/lib/jose/jws/private.h
index c229e5a34..e7113d34a 100644
--- a/lib/jose/jws/private.h
+++ b/lib/jose/jws/private.h
@@ -22,11 +22,3 @@
* to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
*/
-int
-lws_jws_parse_jose(const struct lws_jose_jwe_alg **args,
- uint8_t *buf, int n);
-
-int
-lws_jwe_parse_jose(const struct lws_jose_jwe_alg **args,
- const struct lws_jose_jwe_alg **enc_args,
- uint8_t *buf, int n);
diff --git a/lib/jose/private.h b/lib/jose/private.h
index cfa9c62a2..2985da385 100644
--- a/lib/jose/private.h
+++ b/lib/jose/private.h
@@ -21,3 +21,8 @@
void
lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m);
+
+void
+lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps,
+ struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
+ void *user);
diff --git a/lib/misc/fts/trie-fd.c b/lib/misc/fts/trie-fd.c
index 9e9105e93..56289423c 100644
--- a/lib/misc/fts/trie-fd.c
+++ b/lib/misc/fts/trie-fd.c
@@ -711,10 +711,10 @@ ensure:
if (lseek(ofd, fo, SEEK_SET) < 0)
continue;
- lbuf[sizeof(lbuf) - 1] = '\0';
m = read(ofd, lbuf, sizeof(lbuf) - 1);
if (m < 0)
continue;
+ lbuf[sizeof(lbuf) - 1] = '\0';
p = (unsigned char *)strchr((char *)lbuf, '\n');
if (p)
diff --git a/lib/misc/lejp.c b/lib/misc/lejp.c
index 599a6d37b..677e40aed 100644
--- a/lib/misc/lejp.c
+++ b/lib/misc/lejp.c
@@ -1,7 +1,7 @@
/*
* Lightweight Embedded JSON Parser
*
- * Copyright (C) 2013-2017 Andy Green
+ * Copyright (C) 2013-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
@@ -107,7 +107,7 @@ lejp_change_callback(struct lejp_ctx *ctx,
ctx->callback(ctx, LEJPCB_START);
}
-static void
+void
lejp_check_path_match(struct lejp_ctx *ctx)
{
const char *p, *q;
@@ -644,9 +644,11 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
- ctx->callback(ctx, LEJPCB_COMPLETE);
- /* done, return unused amount */
- return len;
+ if (ctx->callback(ctx, LEJPCB_COMPLETE))
+ goto reject;
+ else
+ /* done, return unused amount */
+ return len;
}
/* pop */
ctx->sp--;
diff --git a/lib/misc/peer-limits.c b/lib/misc/peer-limits.c
index cc4c07d63..53d9421fe 100644
--- a/lib/misc/peer-limits.c
+++ b/lib/misc/peer-limits.c
@@ -77,19 +77,20 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
/* eg, udp doesn't have to have a peer */
return NULL;
- if (af == AF_INET) {
+#ifdef LWS_WITH_IPV6
+ if (af == AF_INET)
+#endif
+ {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
q = &s->sin_addr;
rlen = sizeof(s->sin_addr);
- } else
+ }
#ifdef LWS_WITH_IPV6
- {
+ else {
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
q = &s->sin6_addr;
rlen = sizeof(s->sin6_addr);
}
-#else
- return NULL;
#endif
q8 = q;
diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c
index f43b69f51..4015f4e12 100644
--- a/lib/roles/http/client/client.c
+++ b/lib/roles/http/client/client.c
@@ -127,7 +127,14 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
* need to use that in HANDSHAKE2 to understand
* which wsi to actually write on
*/
- lws_client_socket_service(wfound, pollfd, wsi);
+ if (lws_client_socket_service(wfound, pollfd, wsi) < 0) {
+ /* closed */
+
+ lws_vhost_unlock(wsi->vhost);
+
+ return -1;
+ }
+
lws_callback_on_writable(wsi);
} else
lwsl_debug("%s: didn't find anything in txn q in HS2\n",
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index 063491bd3..dc5e5ff6d 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -1654,6 +1654,7 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
const struct lws_role_ops *role = &role_ops_raw_skt;
const struct lws_protocols *p1, *protocol =
&wsi->vhost->protocols[wsi->vhost->raw_protocol_index];
+ char ipbuf[64];
int n;
if (wsi->vhost->listen_accept_role &&
@@ -1678,9 +1679,14 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)])
n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)];
- lwsl_notice("%s: vh %s, role %s, protocol %s, cb %d, ah %p\n",
- __func__, wsi->vhost->name, role->name, protocol->name, n,
- wsi->http.ah);
+ ipbuf[0] = '\0';
+#if !defined(LWS_PLAT_OPTEE)
+ lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
+#endif
+
+ lwsl_notice("%s: vh %s, peer: %s, role %s, "
+ "protocol %s, cb %d, ah %p\n", __func__, wsi->vhost->name,
+ ipbuf, role->name, protocol->name, n, wsi->http.ah);
if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
return 1;
@@ -2630,8 +2636,7 @@ all_sent:
#else
)
#endif
- )
- {
+ ) {
lwsi_set_state(wsi, LRS_ESTABLISHED);
/* we might be in keepalive, so close it off here */
lws_vfs_file_close(&wsi->http.fop_fd);
@@ -2664,7 +2669,12 @@ all_sent:
return 1; /* >0 indicates completed */
}
- } while (1); //(!lws_send_pipe_choked(wsi));
+ /*
+ * while(1) here causes us to spam the whole file contents into
+ * a hugely bloated output buffer if it ever can't send the
+ * whole chunk...
+ */
+ } while (!lws_send_pipe_choked(wsi));
lws_callback_on_writable(wsi);
diff --git a/lib/tls/lws-gencrypto-common.c b/lib/tls/lws-gencrypto-common.c
index 4151524ca..e77d8497a 100644
--- a/lib/tls/lws-gencrypto-common.c
+++ b/lib/tls/lws-gencrypto-common.c
@@ -21,176 +21,460 @@
#include "core/private.h"
/*
- * Signing algorithms
- *
* These came from RFC7518 (JSON Web Algorithms) Section 3
+ *
+ * Cryptographic Algorithms for Digital Signatures and MACs
*/
static const struct lws_jose_jwe_alg lws_gencrypto_jws_alg_map[] = {
+
+ /*
+ * JWSs MAY also be created that do not provide integrity protection.
+ * Such a JWS is called an Unsecured JWS. An Unsecured JWS uses the
+ * "alg" value "none" and is formatted identically to other JWSs, but
+ * MUST use the empty octet sequence as its JWS Signature value.
+ * Recipients MUST verify that the JWS Signature value is the empty
+ * octet sequence.
+ *
+ * Implementations that support Unsecured JWSs MUST NOT accept such
+ * objects as valid unless the application specifies that it is
+ * acceptable for a specific object to not be integrity protected.
+ * Implementations MUST NOT accept Unsecured JWSs by default. In order
+ * to mitigate downgrade attacks, applications MUST NOT signal
+ * acceptance of Unsecured JWSs at a global level, and SHOULD signal
+ * acceptance on a per-object basis. See Section 8.5 for security
+ * considerations associated with using this algorithm.
+ */
{ /* optional */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_NONE,
+ LWS_JOSE_ENCTYPE_NONE,
+ "none", NULL, 0, 0, 0
+ },
+
+ /*
+ * HMAC with SHA-2 Functions
+ *
+ * The HMAC SHA-256 MAC for a JWS is validated by computing an HMAC
+ * value per RFC 2104, using SHA-256 as the hash algorithm "H", using
+ * the received JWS Signing Input as the "text" value, and using the
+ * shared key. This computed HMAC value is then compared to the result
+ * of base64url decoding the received encoded JWS Signature value. The
+ * comparison of the computed HMAC value to the JWS Signature value MUST
+ * be done in a constant-time manner to thwart timing attacks.
+ *
+ * Alternatively, the computed HMAC value can be base64url encoded and
+ * compared to the received encoded JWS Signature value (also in a
+ * constant-time manner), as this comparison produces the same result as
+ * comparing the unencoded values. In either case, if the values match,
+ * the HMAC has been validated.
+ */
+
+ { /* required: HMAC using SHA-256 */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA256,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
- "none", NULL
+ "HS256", NULL, 0, 0, 0
},
- { /* required */
- LWS_GENHASH_TYPE_UNKNOWN,
- LWS_GENHMAC_TYPE_SHA256,
- LWS_JOSE_ENCTYPE_NONE,
- LWS_JOSE_ENCTYPE_NONE,
- "HS256", NULL
- },
- { /* optional */
+ { /* optional: HMAC using SHA-384 */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA384,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
- "HS384", NULL
+ "HS384", NULL, 0, 0, 0
},
- { /* optional */
+ { /* optional: HMAC using SHA-512 */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA512,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
- "HS512", NULL
+ "HS512", NULL, 0, 0, 0
},
- { /* recommended */
+ /*
+ * Digital Signature with RSASSA-PKCS1-v1_5
+ *
+ * This section defines the use of the RSASSA-PKCS1-v1_5 digital
+ * signature algorithm as defined in Section 8.2 of RFC 3447 [RFC3447]
+ * (commonly known as PKCS #1), using SHA-2 [SHS] hash functions.
+ *
+ * A key of size 2048 bits or larger MUST be used with these algorithms.
+ *
+ * The RSASSA-PKCS1-v1_5 SHA-256 digital signature is generated as
+ * follows: generate a digital signature of the JWS Signing Input using
+ * RSASSA-PKCS1-v1_5-SIGN and the SHA-256 hash function with the desired
+ * private key. This is the JWS Signature value.
+ *
+ * The RSASSA-PKCS1-v1_5 SHA-256 digital signature for a JWS is
+ * validated as follows: submit the JWS Signing Input, the JWS
+ * Signature, and the public key corresponding to the private key used
+ * by the signer to the RSASSA-PKCS1-v1_5-VERIFY algorithm using SHA-256
+ * as the hash function.
+ */
+
+ { /* recommended: RSASSA-PKCS1-v1_5 using SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
- "RS256", NULL
+ "RS256", NULL, 2048, 4096, 0
},
- { /* optional */
+ { /* optional: RSASSA-PKCS1-v1_5 using SHA-384 */
LWS_GENHASH_TYPE_SHA384,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
- "RS384", NULL
+ "RS384", NULL, 2048, 4096, 0
},
- { /* optional */
+ { /* optional: RSASSA-PKCS1-v1_5 using SHA-512 */
LWS_GENHASH_TYPE_SHA512,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
- "RS512", NULL
+ "RS512", NULL, 2048, 4096, 0
},
- { /* Recommended+ */
+ /*
+ * Digital Signature with ECDSA
+ *
+ * The ECDSA P-256 SHA-256 digital signature is generated as follows:
+ *
+ * 1. Generate a digital signature of the JWS Signing Input using ECDSA
+ * P-256 SHA-256 with the desired private key. The output will be
+ * the pair (R, S), where R and S are 256-bit unsigned integers.
+ * 2. Turn R and S into octet sequences in big-endian order, with each
+ * array being be 32 octets long. The octet sequence
+ * representations MUST NOT be shortened to omit any leading zero
+ * octets contained in the values.
+ *
+ * 3. Concatenate the two octet sequences in the order R and then S.
+ * (Note that many ECDSA implementations will directly produce this
+ * concatenation as their output.)
+ *
+ * 4. The resulting 64-octet sequence is the JWS Signature value.
+ *
+ * The ECDSA P-256 SHA-256 digital signature for a JWS is validated as
+ * follows:
+ *
+ * 1. The JWS Signature value MUST be a 64-octet sequence. If it is
+ * not a 64-octet sequence, the validation has failed.
+ *
+ * 2. Split the 64-octet sequence into two 32-octet sequences. The
+ * first octet sequence represents R and the second S. The values R
+ * and S are represented as octet sequences using the Integer-to-
+ * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
+ * (in big-endian octet order).
+ * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
+ * the ECDSA P-256 SHA-256 validator.
+ */
+
+ { /* Recommended+: ECDSA using P-256 and SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_NONE,
- "ES256", "P-256"
+ "ES256", "P-256", 256, 256, 0
},
- { /* optional */
+ { /* optional: ECDSA using P-384 and SHA-384 */
LWS_GENHASH_TYPE_SHA384,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_NONE,
- "ES384", "P-384"
+ "ES384", "P-384", 384, 384, 0
},
- { /* optional */
+ { /* optional: ECDSA using P-521 and SHA-512 */
LWS_GENHASH_TYPE_SHA512,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_NONE,
- "ES512", "P-521"
+ "ES512", "P-521", 521, 521, 0
},
+#if 0
+ Not yet supported
- { /* optional */
+ /*
+ * Digital Signature with RSASSA-PSS
+ *
+ * A key of size 2048 bits or larger MUST be used with this algorithm.
+ *
+ * The RSASSA-PSS SHA-256 digital signature is generated as follows:
+ * generate a digital signature of the JWS Signing Input using RSASSA-
+ * PSS-SIGN, the SHA-256 hash function, and the MGF1 mask generation
+ * function with SHA-256 with the desired private key. This is the JWS
+ * Signature value.
+ *
+ * The RSASSA-PSS SHA-256 digital signature for a JWS is validated as
+ * follows: submit the JWS Signing Input, the JWS Signature, and the
+ * public key corresponding to the private key used by the signer to the
+ * RSASSA-PSS-VERIFY algorithm using SHA-256 as the hash function and
+ * using MGF1 as the mask generation function with SHA-256.
+ *
+ */
+ { /* optional: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_NONE,
- "PS256", NULL
+ "PS256", NULL, 2048, 4096, 0
},
- { /* optional */
+ { /* optional: RSASSA-PSS using SHA-384 and MGF1 with SHA-384 */
LWS_GENHASH_TYPE_SHA384,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_NONE,
- "PS384", NULL
+ "PS384", NULL, 2048, 4096, 0
},
- { /* optional */
+ { /* optional: RSASSA-PSS using SHA-512 and MGF1 with SHA-512*/
LWS_GENHASH_TYPE_SHA512,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_NONE,
- "PS512", NULL
+ "PS512", NULL, 2048, 4096, 0
},
+#endif
};
+/*
+ * These came from RFC7518 (JSON Web Algorithms) Section 4
+ *
+ * Cryptographic Algorithms for Key Management
+ *
+ * JWE uses cryptographic algorithms to encrypt or determine the Content
+ * Encryption Key (CEK).
+ */
+
static const struct lws_jose_jwe_alg lws_gencrypto_jwe_alg_map[] = {
- { /* recommended- */
+
+ /*
+ * This section defines the specifics of encrypting a JWE CEK with
+ * RSAES-PKCS1-v1_5 [RFC3447]. The "alg" (algorithm) Header Parameter
+ * value "RSA1_5" is used for this algorithm.
+ *
+ * A key of size 2048 bits or larger MUST be used with this algorithm.
+ */
+
+ { /* recommended-: RSAES-PKCS1-v1_5 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
- "RSA1_5", NULL
+ "RSA1_5", NULL, 2048, 4096, 0
},
- { /* recommended+ */
+ { /* recommended+: RSAES OAEP using default parameters */
LWS_GENHASH_TYPE_SHA1,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_NONE,
- "RSA-OAEP", NULL
+ "RSA-OAEP", NULL, 2048, 4096, 0
},
-
- { /* recommended */
+ { /* recommended+: RSAES OAEP using SHA-256 and MGF1 SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_NONE,
- "A128KW", NULL
- },
- { /* recommended */
- LWS_GENHASH_TYPE_SHA256,
- LWS_GENHMAC_TYPE_UNKNOWN,
- LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
- LWS_JOSE_ENCTYPE_NONE,
- "A256KW", NULL
+ "RSA-OAEP", NULL, 2048, 4096, 0
},
+ /*
+ * Key Wrapping with AES Key Wrap
+ *
+ * This section defines the specifics of encrypting a JWE CEK with the
+ * Advanced Encryption Standard (AES) Key Wrap Algorithm [RFC3394] using
+ * the default initial value specified in Section 2.2.3.1 of that
+ * document.
+ *
+ *
+ */
+ { /* recommended: AES Key Wrap with AES Key Wrap with defaults
+ using 128-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ LWS_JOSE_ENCTYPE_NONE,
+ "A128KW", NULL, 128, 128, 64
+ },
+
+ { /* optional: AES Key Wrap with AES Key Wrap with defaults
+ using 192-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ LWS_JOSE_ENCTYPE_NONE,
+ "A192KW", NULL, 192, 192, 64
+ },
+
+ { /* recommended: AES Key Wrap with AES Key Wrap with defaults
+ using 256-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ LWS_JOSE_ENCTYPE_NONE,
+ "A256KW", NULL, 256, 256, 64
+ },
+
+ /*
+ * This section defines the specifics of directly performing symmetric
+ * key encryption without performing a key wrapping step. In this case,
+ * the shared symmetric key is used directly as the Content Encryption
+ * Key (CEK) value for the "enc" algorithm. An empty octet sequence is
+ * used as the JWE Encrypted Key value. The "alg" (algorithm) Header
+ * Parameter value "dir" is used in this case.
+ */
{ /* recommended */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_NONE,
- LWS_JOSE_ENCTYPE_AES_GCM,
- "dir", NULL
+ LWS_JOSE_ENCTYPE_NONE,
+ "dir", NULL, 0, 0, 0
},
- { /* recommended+ */
+ /*
+ * Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static
+ * (ECDH-ES)
+ *
+ * This section defines the specifics of key agreement with Elliptic
+ * Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with
+ * the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A]. The
+ * key agreement result can be used in one of two ways:
+ *
+ * 1. directly as the Content Encryption Key (CEK) for the "enc"
+ * algorithm, in the Direct Key Agreement mode, or
+ *
+ * 2. as a symmetric key used to wrap the CEK with the "A128KW",
+ * "A192KW", or "A256KW" algorithms, in the Key Agreement with Key
+ * Wrapping mode.
+ *
+ * A new ephemeral public key value MUST be generated for each key
+ * agreement operation.
+ *
+ * In Direct Key Agreement mode, the output of the Concat KDF MUST be a
+ * key of the same length as that used by the "enc" algorithm. In this
+ * case, the empty octet sequence is used as the JWE Encrypted Key
+ * value. The "alg" (algorithm) Header Parameter value "ECDH-ES" is
+ * used in the Direct Key Agreement mode.
+ *
+ * In Key Agreement with Key Wrapping mode, the output of the Concat KDF
+ * MUST be a key of the length needed for the specified key wrapping
+ * algorithm. In this case, the JWE Encrypted Key is the CEK wrapped
+ * with the agreed-upon key.
+ */
+
+ { /* recommended+: ECDH Ephemeral Static Key agreement Concat KDF */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
- LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
+ LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_NONE,
- "ECDH-ES", NULL
+ "ECDH-ES", NULL, 128, 128, 0
},
- { /* recommended */
+ { /* recommended: ECDH-ES + Concat KDF + wrapped by AES128KW */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
- LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
- LWS_JOSE_ENCTYPE_NONE,
- "ECDH-ES+A128KW", NULL
+ LWS_JOSE_ENCTYPE_ECDHES,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ "ECDH-ES+A128KW", NULL, 128, 128, 0
},
- { /* recommended */
+ { /* optional: ECDH-ES + Concat KDF + wrapped by AES192KW */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
- LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
+ LWS_JOSE_ENCTYPE_ECDHES,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ "ECDH-ES+A192KW", NULL, 192, 192, 0
+ },
+ { /* recommended: ECDH-ES + Concat KDF + wrapped by AES256KW */
+ LWS_GENHASH_TYPE_SHA256,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_ECDHES,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ "ECDH-ES+A256KW", NULL, 256, 256, 0
+ },
+
+ /*
+ * Key Encryption with AES GCM
+ *
+ * This section defines the specifics of encrypting a JWE Content
+ * Encryption Key (CEK) with Advanced Encryption Standard (AES) in
+ * Galois/Counter Mode (GCM) ([AES] and [NIST.800-38D]).
+ *
+ * Use of an Initialization Vector (IV) of size 96 bits is REQUIRED with
+ * this algorithm. The IV is represented in base64url-encoded form as
+ * the "iv" (initialization vector) Header Parameter value.
+ *
+ * The Additional Authenticated Data value used is the empty octet
+ * string.
+ *
+ * The requested size of the Authentication Tag output MUST be 128 bits,
+ * regardless of the key size.
+ *
+ * The JWE Encrypted Key value is the ciphertext output.
+ *
+ * The Authentication Tag output is represented in base64url-encoded
+ * form as the "tag" (authentication tag) Header Parameter value.
+ *
+ *
+ * "iv" (Initialization Vector) Header Parameter
+ *
+ * The "iv" (initialization vector) Header Parameter value is the
+ * base64url-encoded representation of the 96-bit IV value used for the
+ * key encryption operation. This Header Parameter MUST be present and
+ * MUST be understood and processed by implementations when these
+ * algorithms are used.
+ *
+ * "tag" (Authentication Tag) Header Parameter
+ *
+ * The "tag" (authentication tag) Header Parameter value is the
+ * base64url-encoded representation of the 128-bit Authentication Tag
+ * value resulting from the key encryption operation. This Header
+ * Parameter MUST be present and MUST be understood and processed by
+ * implementations when these algorithms are used.
+ */
+ { /* optional: Key wrapping with AES GCM using 128-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
- "ECDH-ES+A256KW", NULL
+ "A128GCMKW", NULL, 128, 128, 96
+ },
+
+ { /* optional: Key wrapping with AES GCM using 192-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ LWS_JOSE_ENCTYPE_NONE,
+ "A192GCMKW", NULL, 192, 192, 96
+ },
+
+ { /* optional: Key wrapping with AES GCM using 256-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ LWS_JOSE_ENCTYPE_NONE,
+ "A256GCMKW", NULL, 256, 256, 96
},
/* list terminator */
{ 0, 0, 0, 0, NULL, NULL }
};
+/*
+ * The "enc" (encryption algorithm) Header Parameter identifies the
+ * content encryption algorithm used to perform authenticated encryption
+ * on the plaintext to produce the ciphertext and the Authentication
+ * Tag. This algorithm MUST be an AEAD algorithm with a specified key
+ * length. The encrypted content is not usable if the "enc" value does
+ * not represent a supported algorithm. "enc" values should either be
+ * registered in the IANA "JSON Web Signature and Encryption Algorithms"
+ * registry established by [JWA] or be a value that contains a
+ * Collision-Resistant Name. The "enc" value is a case-sensitive ASCII
+ * string containing a StringOrURI value. This Header Parameter MUST be
+ * present and MUST be understood and processed by implementations.
+ */
static const struct lws_jose_jwe_alg lws_gencrypto_jwe_enc_map[] = {
/*
+ * AES_128_CBC_HMAC_SHA_256 / 512
+ *
* It uses the HMAC message authentication code [RFC2104] with the
* SHA-256 hash function [SHS] to provide message authentication, with
* the HMAC output truncated to 128 bits, corresponding to the
@@ -213,7 +497,71 @@ static const struct lws_jose_jwe_alg lws_gencrypto_jwe_enc_map[] = {
LWS_GENHMAC_TYPE_SHA256,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_CBC,
- "A128CBC-HS256", NULL
+ "A128CBC-HS256", NULL, 256, 256, 128
+ },
+ /*
+ * AES_192_CBC_HMAC_SHA_384 is based on AES_128_CBC_HMAC_SHA_256, but
+ * with the following differences:
+ *
+ * The input key K is 48 octets long instead of 32.
+ * ENC_KEY_LEN is 24 octets instead of 16.
+ * MAC_KEY_LEN is 24 octets instead of 16.
+ * SHA-384 is used for the HMAC instead of SHA-256.
+ * The HMAC SHA-384 value is truncated to T_LEN=24 octets instead of 16.
+ */
+ { /* required */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_SHA384,
+ LWS_JOSE_ENCTYPE_NONE,
+ LWS_JOSE_ENCTYPE_AES_CBC,
+ "A192CBC-HS384", NULL, 384, 384, 192
+ },
+ /*
+ * AES_256_CBC_HMAC_SHA_512 is based on AES_128_CBC_HMAC_SHA_256, but
+ * with the following differences:
+ *
+ * The input key K is 64 octets long instead of 32.
+ * ENC_KEY_LEN is 32 octets instead of 16.
+ * MAC_KEY_LEN is 32 octets instead of 16.
+ * SHA-512 is used for the HMAC instead of SHA-256.
+ * The HMAC SHA-512 value is truncated to T_LEN=32 octets instead of 16.
+ */
+ { /* required */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_SHA512,
+ LWS_JOSE_ENCTYPE_NONE,
+ LWS_JOSE_ENCTYPE_AES_CBC,
+ "A256CBC-HS512", NULL, 512, 512, 256
+ },
+
+ /*
+ * The CEK is used as the encryption key.
+ *
+ * Use of an IV of size 96 bits is REQUIRED with this algorithm.
+ *
+ * The requested size of the Authentication Tag output MUST be 128 bits,
+ * regardless of the key size.
+ */
+ { /* recommended: AES GCM using 128-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_NONE,
+ LWS_JOSE_ENCTYPE_AES_GCM,
+ "A128GCM", NULL, 128, 128, 96
+ },
+ { /* optional: AES GCM using 192-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_NONE,
+ LWS_JOSE_ENCTYPE_AES_GCM,
+ "A192GCM", NULL, 192, 192, 96
+ },
+ { /* recommended: AES GCM using 256-bit key */
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_JOSE_ENCTYPE_NONE,
+ LWS_JOSE_ENCTYPE_AES_GCM,
+ "A256GCM", NULL, 256, 256, 96
},
};
@@ -306,3 +654,28 @@ lws_genhmac_size(enum lws_genhmac_types type)
return 0;
}
+
+int
+lws_gencrypto_bits_to_bytes(int bits)
+{
+ if (bits & 7)
+ return (bits / 8) + 1;
+
+ return bits / 8;
+}
+
+int
+lws_base64_size(int bytes)
+{
+ return ((bytes * 4) / 3) + 6;
+}
+
+void
+lws_gencrypto_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
+{
+ int n;
+
+ for (n = 0; n < m; n++)
+ if (el[n].buf)
+ lws_free_set_NULL(el[n].buf);
+}
diff --git a/lib/tls/lws-genec-common.c b/lib/tls/lws-genec-common.c
index f9e7b1220..d3e6da225 100644
--- a/lib/tls/lws-genec-common.c
+++ b/lib/tls/lws-genec-common.c
@@ -57,6 +57,8 @@ lws_genec_dump(struct lws_gencrypto_keyelem *el)
{
int n;
+ (void)enames;
+
lwsl_info(" genec %p: crv: '%s'\n", el,
!!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf ?
(char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf: "no curve name");
diff --git a/lib/tls/mbedtls/lws-genaes.c b/lib/tls/mbedtls/lws-genaes.c
index 8f3d87894..0e4da8179 100644
--- a/lib/tls/mbedtls/lws-genaes.c
+++ b/lib/tls/mbedtls/lws-genaes.c
@@ -29,9 +29,9 @@ static int operation_map[] = { MBEDTLS_AES_ENCRYPT, MBEDTLS_AES_DECRYPT };
LWS_VISIBLE int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
- int padding, void *engine)
+ enum enum_aes_padding padding, void *engine)
{
- int n;
+ int n = 0;
ctx->mode = mode;
ctx->k = el;
@@ -40,8 +40,12 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
switch (ctx->mode) {
case LWS_GAESM_XTS:
+#if defined(MBEDTLS_CIPHER_MODE_XTS)
mbedtls_aes_xts_init(&ctx->u.ctx_xts);
break;
+#else
+ return -1;
+#endif
case LWS_GAESM_GCM:
mbedtls_gcm_init(&ctx->u.ctx_gcm);
n = mbedtls_gcm_setkey(&ctx->u.ctx_gcm, MBEDTLS_CIPHER_ID_AES,
@@ -107,23 +111,22 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
LWS_VISIBLE int
lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
{
- int n;
+ int n = 0;
if (ctx->mode == LWS_GAESM_GCM) {
- if (tag) {
- n = mbedtls_gcm_finish(&ctx->u.ctx_gcm, tag, tlen);
- if (n)
- lwsl_notice("%s: mbedtls_gcm_finish: -0x%x\n",
- __func__, -n);
- else
- if (memcmp(ctx->tag, tag, ctx->taglen)) {
- lwsl_err("%s: lws_genaes_crypt tag "
- "mismatch (bad first)\n",
- __func__);
- lwsl_hexdump_notice(tag, tlen);
- lwsl_hexdump_notice(ctx->tag, ctx->taglen);
- n = -1;
- }
+ n = mbedtls_gcm_finish(&ctx->u.ctx_gcm, tag, tlen);
+ if (n)
+ lwsl_notice("%s: mbedtls_gcm_finish: -0x%x\n",
+ __func__, -n);
+ if (tag && ctx->op == MBEDTLS_AES_DECRYPT && !n) {
+ if (lws_timingsafe_bcmp(ctx->tag, tag, ctx->taglen)) {
+ lwsl_err("%s: lws_genaes_crypt tag "
+ "mismatch (bad first)\n",
+ __func__);
+ lwsl_hexdump_notice(tag, tlen);
+ lwsl_hexdump_notice(ctx->tag, ctx->taglen);
+ n = -1;
+ }
}
mbedtls_gcm_free(&ctx->u.ctx_gcm);
return n;
@@ -140,15 +143,134 @@ lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
return 0;
}
+static int
+lws_genaes_rfc3394_wrap(int wrap, int cek_bits, const uint8_t *kek,
+ int kek_bits, const uint8_t *in, uint8_t *out)
+{
+ int n, m, ret = -1, c64 = cek_bits / 64;
+ mbedtls_aes_context ctx;
+ uint8_t a[8], b[16];
+
+ /*
+ * notice the KEK key used to perform the wrapping or unwrapping is
+ * always the size of the AES key used, eg, A128KW == 128 bits. The
+ * key being wrapped or unwrapped may be larger and is set by the
+ * 'bits' parameter.
+ *
+ * If it's larger than the KEK key size bits, we iterate over it
+ */
+
+ mbedtls_aes_init(&ctx);
+
+ if (wrap) {
+ /*
+ * The inputs to the key wrapping process are the KEK and the
+ * plaintext to be wrapped. The plaintext consists of n 64-bit
+ * blocks, containing the key data being wrapped.
+ *
+ * Inputs: Plaintext, n 64-bit values {P1, P2, ..., Pn},
+ * and Key, K (the KEK).
+ * Outputs: Ciphertext, (n+1) 64-bit values
+ * {C0, C1, ..., Cn}.
+ *
+ * The default initial value (IV) is defined to be the
+ * hexadecimal constant:
+ *
+ * A[0] = IV = A6A6A6A6A6A6A6A6
+ */
+ memset(out, 0xa6, 8);
+ memcpy(out + 8, in, 8 * c64);
+ n = mbedtls_aes_setkey_enc(&ctx, kek, kek_bits);
+ } else {
+ /*
+ * 2.2.2 Key Unwrap
+ *
+ * The inputs to the unwrap process are the KEK and (n+1)
+ * 64-bit blocks of ciphertext consisting of previously
+ * wrapped key. It returns n blocks of plaintext consisting
+ * of the n 64-bit blocks of the decrypted key data.
+ *
+ * Inputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn},
+ * and Key, K (the KEK).
+ *
+ * Outputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}.
+ */
+ memcpy(a, in, 8);
+ memcpy(out, in + 8, 8 * c64);
+ n = mbedtls_aes_setkey_dec(&ctx, kek, kek_bits);
+ }
+
+ if (n < 0) {
+ lwsl_err("%s: setkey failed\n", __func__);
+ goto bail;
+ }
+
+ if (wrap) {
+ for (n = 0; n <= 5; n++) {
+ uint8_t *r = out + 8;
+ for (m = 1; m <= c64; m++) {
+ memcpy(b, out, 8);
+ memcpy(b + 8, r, 8);
+ if (mbedtls_internal_aes_encrypt(&ctx, b, b))
+ goto bail;
+
+ memcpy(out, b, 8);
+ out[7] ^= c64 * n + m;
+ memcpy(r, b + 8, 8);
+ r += 8;
+ }
+ }
+ ret = 0;
+ } else {
+ /*
+ *
+ */
+ for (n = 5; n >= 0; n--) {
+ uint8_t *r = out + (c64 - 1) * 8;
+ for (m = c64; m >= 1; m--) {
+ memcpy(b, a, 8);
+ b[7] ^= c64 * n + m;
+ memcpy(b + 8, r, 8);
+ if (mbedtls_internal_aes_decrypt(&ctx, b, b))
+ goto bail;
+
+ memcpy(a, b, 8);
+ memcpy(r, b + 8, 8);
+ r -= 8;
+ }
+ }
+
+ ret = 0;
+ for (n = 0; n < 8; n++)
+ if (a[n] != 0xa6)
+ ret = -1;
+ }
+
+bail:
+ if (ret)
+ lwsl_notice("%s: failed\n", __func__);
+ mbedtls_aes_free(&ctx);
+
+ return ret;
+}
+
LWS_VISIBLE int
lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
uint8_t *out, uint8_t *iv_or_nonce_ctr_or_data_unit_16,
uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen)
{
- uint8_t iv[16], sb[16];
- int n;
+ uint8_t iv[LWS_JWE_AES_IV_BYTES], sb[16];
+ int n = 0;
switch (ctx->mode) {
+ case LWS_GAESM_KW:
+ /* a key of length ctx->k->len is wrapped by a 128-bit KEK */
+ n = lws_genaes_rfc3394_wrap(ctx->op == MBEDTLS_AES_ENCRYPT,
+ ctx->op == MBEDTLS_AES_ENCRYPT ? len * 8 :
+ (len - 8) * 8, ctx->k->buf,
+ ctx->k->len * 8,
+ in, out);
+ break;
case LWS_GAESM_CBC:
memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16);
n = mbedtls_aes_crypt_cbc(&ctx->u.ctx, ctx->op, len, iv,
@@ -181,10 +303,14 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
break;
case LWS_GAESM_OFB:
+#if defined(MBEDTLS_CIPHER_MODE_OFB)
memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16);
n = mbedtls_aes_crypt_ofb(&ctx->u.ctx, len, nc_or_iv_off, iv,
in, out);
break;
+#else
+ return -1;
+#endif
case LWS_GAESM_XTS:
#if defined(MBEDTLS_CIPHER_MODE_XTS)
@@ -233,7 +359,7 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
}
if (n) {
- lwsl_notice("%s: enc: -0x%x, len %d\n", __func__, -n, (int)len);
+ lwsl_notice("%s: failed: -0x%x, len %d\n", __func__, -n, (int)len);
return -1;
}
diff --git a/lib/tls/mbedtls/lws-gencrypto.c b/lib/tls/mbedtls/lws-gencrypto.c
index 8d2619340..abbfd563e 100644
--- a/lib/tls/mbedtls/lws-gencrypto.c
+++ b/lib/tls/mbedtls/lws-gencrypto.c
@@ -52,8 +52,10 @@ lws_gencrypto_mbedtls_hash_to_MD_TYPE(enum lws_genhash_types hash_type)
int
lws_gencrypto_mbedtls_rngf(void *context, unsigned char *buf, size_t len)
{
- if ((size_t)lws_get_random(context, buf, len) == len)
+ if ((size_t)lws_get_random(context, buf, len) == len) {
+ // lwsl_hexdump_err(buf, len);
return 0;
-
+ }
+ lwsl_err("%s: rng failed\n", __func__);
return -1;
}
diff --git a/lib/tls/mbedtls/lws-genec.c b/lib/tls/mbedtls/lws-genec.c
index 0b97ea852..8d0807855 100644
--- a/lib/tls/mbedtls/lws-genec.c
+++ b/lib/tls/mbedtls/lws-genec.c
@@ -86,6 +86,8 @@ lws_genec_keypair_import(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
el[LWS_GENCRYPTO_EC_KEYEL_Y].len))
goto bail1;
+ mbedtls_mpi_lset(&kp.Q.Z, 1);
+
switch (ctx->genec_alg) {
case LEGENEC_ECDH:
if (mbedtls_ecdh_get_params(ctx->u.ctx_ecdh, &kp, side))
@@ -112,6 +114,7 @@ lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
memset(ctx, 0, sizeof(*ctx));
+
ctx->context = context;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDH;
@@ -130,15 +133,16 @@ lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
memset(ctx, 0, sizeof(*ctx));
+
ctx->context = context;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDSA;
- ctx->u.ctx_ecdh = lws_zalloc(sizeof(*ctx->u.ctx_ecdh), "genecdh");
- if (!ctx->u.ctx_ecdh)
+ ctx->u.ctx_ecdsa = lws_zalloc(sizeof(*ctx->u.ctx_ecdsa), "genecdsa");
+ if (!ctx->u.ctx_ecdsa)
return 1;
- mbedtls_ecdh_init(ctx->u.ctx_ecdh);
+ mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
return 0;
}
@@ -210,7 +214,8 @@ lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
}
mbedtls_ecdsa_init(&ecdsa);
- n = mbedtls_ecdsa_genkey(&ecdsa, curve->tls_lib_nid, lws_gencrypto_mbedtls_rngf,
+ n = mbedtls_ecdsa_genkey(&ecdsa, curve->tls_lib_nid,
+ lws_gencrypto_mbedtls_rngf,
ctx->context);
if (n) {
lwsl_err("mbedtls_ecdsa_genkey failed 0x%x\n", -n);
@@ -241,7 +246,8 @@ lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
goto bail1;
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
- for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) {
+ for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT;
+ n++) {
el[n].len = curve->key_bytes;
el[n].buf = lws_malloc(curve->key_bytes, "ec");
if (!el[n].buf)
@@ -288,7 +294,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
return -22;
}
- mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
+ //mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
n = mbedtls_ecdsa_genkey(ctx->u.ctx_ecdsa, curve->tls_lib_nid,
lws_gencrypto_mbedtls_rngf, ctx->context);
if (n) {
@@ -301,7 +307,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
* lws_gencrypto_keyelems, so they can be serialized, used in jwk etc
*/
- kp = (mbedtls_ecp_keypair *)&ctx->u.ctx_ecdsa;
+ kp = (mbedtls_ecp_keypair *)ctx->u.ctx_ecdsa;
mpi[0] = &kp->Q.X;
mpi[1] = &kp->d;
@@ -314,15 +320,18 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
goto bail1;
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
- for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) {
+ for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT;
+ n++) {
el[n].len = curve->key_bytes;
el[n].buf = lws_malloc(curve->key_bytes, "ec");
if (!el[n].buf)
goto bail2;
- if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf,
- curve->key_bytes))
+
+ if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf, el[n].len)) {
+ lwsl_err("%s: mbedtls_mpi_write_binary failed\n", __func__);
goto bail2;
+ }
}
return 0;
@@ -339,65 +348,121 @@ bail1:
}
LWS_VISIBLE LWS_EXTERN int
-lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
- enum lws_genhash_types hash_type,
+lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
uint8_t *sig, size_t sig_len)
{
- mbedtls_md_type_t md_type =
- lws_gencrypto_mbedtls_hash_to_MD_TYPE(hash_type);
+ int n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
+ size_t hlen = lws_genhash_size(hash_type);
+ mbedtls_mpi mpi_r, mpi_s;
size_t slen = sig_len;
- int n;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
- if (md_type < 0)
- return -2;
+ /*
+ * The ECDSA P-256 SHA-256 digital signature is generated as follows:
+ *
+ * 1. Generate a digital signature of the JWS Signing Input using ECDSA
+ * P-256 SHA-256 with the desired private key. The output will be
+ * the pair (R, S), where R and S are 256-bit unsigned integers.
+ *
+ * 2. Turn R and S into octet sequences in big-endian order, with each
+ * array being be 32 octets long. The octet sequence
+ * representations MUST NOT be shortened to omit any leading zero
+ * octets contained in the values.
+ *
+ * 3. Concatenate the two octet sequences in the order R and then S.
+ * (Note that many ECDSA implementations will directly produce this
+ * concatenation as their output.)
+ *
+ * 4. The resulting 64-octet sequence is the JWS Signature value.
+ */
+ mbedtls_mpi_init(&mpi_r);
+ mbedtls_mpi_init(&mpi_s);
- n = mbedtls_ecdsa_write_signature(ctx->u.ctx_ecdsa, md_type, in,
- lws_genhash_size(hash_type), sig,
- &slen, lws_gencrypto_mbedtls_rngf,
- ctx->context);
+ n = mbedtls_ecdsa_sign(&ctx->u.ctx_ecdsa->grp, &mpi_r, &mpi_s,
+ &ctx->u.ctx_ecdsa->d, in, hlen,
+ lws_gencrypto_mbedtls_rngf, ctx->context);
if (n) {
- lwsl_err("%s: mbedtls_ecdsa_write_signature failed: -0x%x\n",
+ lwsl_err("%s: mbedtls_ecdsa_sign failed: -0x%x\n",
__func__, -n);
- goto bail;
+ goto bail2;
}
+ if (mbedtls_mpi_write_binary(&mpi_r, sig, keybytes))
+ goto bail2;
+ mbedtls_mpi_free(&mpi_r);
+ if (mbedtls_mpi_write_binary(&mpi_s, sig + keybytes, keybytes))
+ goto bail1;
+ mbedtls_mpi_free(&mpi_s);
+
return (int)slen;
-bail:
+
+bail2:
+ mbedtls_mpi_free(&mpi_r);
+bail1:
+ mbedtls_mpi_free(&mpi_s);
return -3;
}
LWS_VISIBLE LWS_EXTERN int
-lws_genecdsa_hash_sig_verify(struct lws_genec_ctx *ctx, const uint8_t *in,
- enum lws_genhash_types hash_type,
- const uint8_t *sig, size_t sig_len)
+lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ const uint8_t *sig, size_t sig_len)
{
- mbedtls_md_type_t md_type =
- lws_gencrypto_mbedtls_hash_to_MD_TYPE(hash_type);
- int n;
+ int n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
+ size_t hlen = lws_genhash_size(hash_type);
+ mbedtls_mpi mpi_r, mpi_s;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
- if (md_type < 0)
- return -2;
+ if ((int)sig_len != keybytes * 2)
+ return -1;
+
+ /*
+ * 1. The JWS Signature value MUST be a 64-octet sequence. If it is
+ * not a 64-octet sequence, the validation has failed.
+ *
+ * 2. Split the 64-octet sequence into two 32-octet sequences. The
+ * first octet sequence represents R and the second S. The values R
+ * and S are represented as octet sequences using the Integer-to-
+ * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
+ * (in big-endian octet order).
+ *
+ * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
+ * the ECDSA P-256 SHA-256 validator.
+ */
+
+ mbedtls_mpi_init(&mpi_r);
+ mbedtls_mpi_init(&mpi_s);
+
+ if (mbedtls_mpi_read_binary(&mpi_r, sig, keybytes))
+ return -1;
+ if (mbedtls_mpi_read_binary(&mpi_s, sig + keybytes, keybytes))
+ goto bail1;
+
+ n = mbedtls_ecdsa_verify(&ctx->u.ctx_ecdsa->grp, in, hlen,
+ &ctx->u.ctx_ecdsa->Q, &mpi_r, &mpi_s);
+
+ mbedtls_mpi_free(&mpi_s);
+ mbedtls_mpi_free(&mpi_r);
- n = mbedtls_ecdsa_read_signature(ctx->u.ctx_ecdsa, in,
- lws_genhash_size(hash_type), sig,
- sig_len);
if (n) {
- lwsl_err("%s: mbedtls_ecdsa_write_signature failed: -0x%x\n",
+ lwsl_err("%s: mbedtls_ecdsa_verify failed: -0x%x\n",
__func__, -n);
goto bail;
}
return 0;
+bail1:
+ mbedtls_mpi_free(&mpi_r);
+
bail:
return -3;
diff --git a/lib/tls/mbedtls/lws-genhash.c b/lib/tls/mbedtls/lws-genhash.c
index 01c453212..a5770d62e 100644
--- a/lib/tls/mbedtls/lws-genhash.c
+++ b/lib/tls/mbedtls/lws-genhash.c
@@ -62,6 +62,9 @@ lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
int
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
{
+ if (!len)
+ return 0;
+
switch (ctx->type) {
case LWS_GENHASH_TYPE_SHA1:
MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len);
@@ -147,6 +150,9 @@ lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
int
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
{
+ if (!len)
+ return 0;
+
if (mbedtls_md_hmac_update(&ctx->ctx, in, len))
return -1;
diff --git a/lib/tls/mbedtls/lws-genrsa.c b/lib/tls/mbedtls/lws-genrsa.c
index 4004777f0..5eb6c5b75 100644
--- a/lib/tls/mbedtls/lws-genrsa.c
+++ b/lib/tls/mbedtls/lws-genrsa.c
@@ -23,6 +23,7 @@
*/
#include "core/private.h"
#include "tls/mbedtls/private.h"
+#include
LWS_VISIBLE void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el)
@@ -38,7 +39,8 @@ static int mode_map[] = { MBEDTLS_RSA_PKCS_V15, MBEDTLS_RSA_PKCS_V21 };
LWS_VISIBLE int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
- struct lws_context *context, enum enum_genrsa_mode mode)
+ struct lws_context *context, enum enum_genrsa_mode mode,
+ enum lws_genhash_types oaep_hashid)
{
memset(ctx, 0, sizeof(*ctx));
ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
@@ -53,6 +55,9 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
mbedtls_rsa_init(ctx->ctx, mode_map[mode], 0);
+ ctx->ctx->padding = mode_map[mode];
+ ctx->ctx->hash_id = lws_gencrypto_mbedtls_hash_to_MD_TYPE(oaep_hashid);
+
{
int n;
@@ -71,6 +76,20 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
return -1;
}
+
+ /* mbedtls... compute missing P & Q */
+
+ if ( el[LWS_GENCRYPTO_RSA_KEYEL_D].len &&
+ !el[LWS_GENCRYPTO_RSA_KEYEL_P].len &&
+ !el[LWS_GENCRYPTO_RSA_KEYEL_Q].len) {
+ if (mbedtls_rsa_complete(ctx->ctx)) {
+ lwsl_notice("mbedtls_rsa_complete failed\n");
+ lws_free_set_NULL(ctx->ctx);
+
+ return -1;
+ }
+
+ }
}
ctx->ctx->len = el[LWS_GENCRYPTO_RSA_KEYEL_N].len;
@@ -153,15 +172,19 @@ lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
ctx->ctx->len = in_len;
+ mbedtls_rsa_complete(ctx->ctx);
+
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
- n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, NULL, NULL,
+ n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, _rngf,
+ ctx->context,
MBEDTLS_RSA_PUBLIC,
&olen, in, out,
out_max);
break;
case LGRSAM_PKCS1_OAEP_PSS:
- n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, NULL, NULL,
+ n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, _rngf,
+ ctx->context,
MBEDTLS_RSA_PUBLIC,
NULL, 0,
&olen, in, out, out_max);
@@ -178,13 +201,86 @@ lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
return olen;
}
+LWS_VISIBLE int
+lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max)
+{
+ size_t olen = 0;
+ int n;
+
+ ctx->ctx->len = in_len;
+
+ mbedtls_rsa_complete(ctx->ctx);
+
+ switch(ctx->mode) {
+ case LGRSAM_PKCS1_1_5:
+ n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, _rngf,
+ ctx->context,
+ MBEDTLS_RSA_PRIVATE,
+ &olen, in, out,
+ out_max);
+ break;
+ case LGRSAM_PKCS1_OAEP_PSS:
+ n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, _rngf,
+ ctx->context,
+ MBEDTLS_RSA_PRIVATE,
+ NULL, 0,
+ &olen, in, out, out_max);
+ break;
+ default:
+ return -1;
+ }
+ if (n) {
+ lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+ return -1;
+ }
+
+ return olen;
+}
+
LWS_VISIBLE int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
int n;
- ctx->ctx->padding = mode_map[ctx->mode];
+ mbedtls_rsa_complete(ctx->ctx);
+
+ switch(ctx->mode) {
+ case LGRSAM_PKCS1_1_5:
+ n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf,
+ ctx->context,
+ MBEDTLS_RSA_PUBLIC,
+ in_len, in, out);
+ break;
+ case LGRSAM_PKCS1_OAEP_PSS:
+ n = mbedtls_rsa_rsaes_oaep_encrypt(ctx->ctx, _rngf,
+ ctx->context,
+ MBEDTLS_RSA_PUBLIC,
+ NULL, 0,
+ in_len, in, out);
+ break;
+ default:
+ return -1;
+ }
+ if (n < 0) {
+ lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n,
+ (int)in_len);
+
+ return -1;
+ }
+
+ return mbedtls_mpi_size(&ctx->ctx->N);
+}
+
+LWS_VISIBLE int
+lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out)
+{
+ int n;
+
+ mbedtls_rsa_complete(ctx->ctx);
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
@@ -210,7 +306,7 @@ lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
return -1;
}
- return 0;
+ return mbedtls_mpi_size(&ctx->ctx->N);
}
LWS_VISIBLE int
@@ -223,6 +319,8 @@ lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
if (h < 0)
return -1;
+ mbedtls_rsa_complete(ctx->ctx);
+
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
n = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx->ctx, NULL, NULL,
@@ -256,6 +354,8 @@ lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
if (h < 0)
return -1;
+ mbedtls_rsa_complete(ctx->ctx);
+
/*
* The "sig" buffer must be as large as the size of ctx->N
* (eg. 128 bytes if RSA-1024 is used).
diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c
index fa0eef99c..d75500d77 100644
--- a/lib/tls/mbedtls/mbedtls-server.c
+++ b/lib/tls/mbedtls/mbedtls-server.c
@@ -588,7 +588,7 @@ lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost)
{
}
-#if defined(LWS_WITH_JWS)
+#if defined(LWS_WITH_JOSE)
static int
_rngf(void *context, unsigned char *buf, size_t len)
{
diff --git a/lib/tls/openssl/lws-genaes.c b/lib/tls/openssl/lws-genaes.c
index 76cf5254e..43fc4ae5e 100644
--- a/lib/tls/openssl/lws-genaes.c
+++ b/lib/tls/openssl/lws-genaes.c
@@ -32,9 +32,9 @@
LWS_VISIBLE int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
- int padding, void *engine)
+ enum enum_aes_padding padding, void *engine)
{
- int n;
+ int n = 0;
ctx->ctx = EVP_CIPHER_CTX_new();
if (!ctx->ctx)
@@ -49,6 +49,17 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
switch (ctx->k->len) {
case 128 / 8:
switch (mode) {
+ case LWS_GAESM_KW:
+#if defined(LWS_HAVE_EVP_aes_128_wrap)
+ EVP_CIPHER_CTX_set_flags(ctx->ctx,
+ EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ ctx->cipher = EVP_aes_128_wrap();
+ break;
+#else
+ lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
+ __func__);
+ return -1;
+#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_128_cbc();
break;
@@ -75,12 +86,23 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
ctx->cipher = EVP_aes_128_gcm();
break;
default:
- return -1;
+ goto bail;
}
break;
case 192 / 8:
switch (mode) {
+ case LWS_GAESM_KW:
+#if defined(LWS_HAVE_EVP_aes_128_wrap)
+ EVP_CIPHER_CTX_set_flags(ctx->ctx,
+ EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ ctx->cipher = EVP_aes_192_wrap();
+ break;
+#else
+ lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
+ __func__);
+ return -1;
+#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_192_cbc();
break;
@@ -101,17 +123,28 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
break;
case LWS_GAESM_XTS:
lwsl_err("%s: AES XTS 192 invalid\n", __func__);
- return -1;
+ goto bail;
case LWS_GAESM_GCM:
ctx->cipher = EVP_aes_192_gcm();
break;
default:
- return -1;
+ goto bail;
}
break;
case 256 / 8:
switch (mode) {
+ case LWS_GAESM_KW:
+#if defined(LWS_HAVE_EVP_aes_128_wrap)
+ EVP_CIPHER_CTX_set_flags(ctx->ctx,
+ EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ ctx->cipher = EVP_aes_256_wrap();
+ break;
+#else
+ lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
+ __func__);
+ return -1;
+#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_256_cbc();
break;
@@ -137,7 +170,7 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
ctx->cipher = EVP_aes_256_gcm();
break;
default:
- return -1;
+ goto bail;
}
break;
@@ -147,14 +180,14 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
ctx->cipher = EVP_aes_256_xts();
break;
default:
- return -1;
+ goto bail;
}
break;
default:
lwsl_err("%s: unsupported AES size %d bits\n", __func__,
ctx->k->len * 8);
- return -1;
+ goto bail;
}
switch (ctx->op) {
@@ -172,11 +205,15 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
if (!n) {
lwsl_err("%s: cipher init failed (cipher %p)\n", __func__,
ctx->cipher);
-
- return -1;
+ lws_tls_err_describe();
+ goto bail;
}
return 0;
+bail:
+ EVP_CIPHER_CTX_free(ctx->ctx);
+ ctx->ctx = NULL;
+ return -1;
}
LWS_VISIBLE int
@@ -196,31 +233,24 @@ lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
lwsl_err("%s: enc final failed\n", __func__);
n = -1;
}
+
if (ctx->mode == LWS_GAESM_GCM) {
- memset(tag, 0, tlen);
if (EVP_CIPHER_CTX_ctrl(ctx->ctx,
EVP_CTRL_GCM_GET_TAG,
ctx->taglen, tag) != 1) {
lwsl_err("get tag ctrl failed\n");
//lws_tls_err_describe();
n = 1;
- } else
- if (memcmp(tag, ctx->tag, ctx->taglen)) {
- lwsl_err("%s: tag mismatch "
- "(bad first)\n", __func__);
- //lws_tls_err_describe();
- lwsl_hexdump_notice(tag, tlen);
- lwsl_hexdump_notice(ctx->tag, ctx->taglen);
- n = -1;
}
}
break;
case LWS_GAESO_DEC:
if (EVP_DecryptFinal_ex(ctx->ctx, buf, &outl) != 1) {
lwsl_err("%s: dec final failed\n", __func__);
- //lws_tls_err_describe();
+ lws_tls_err_describe();
n = -1;
}
+
break;
}
if (outl)
@@ -240,7 +270,7 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx,
uint8_t *iv_or_nonce_ctr_or_data_unit_16,
uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen)
{
- int n, outl, olen;
+ int n = 0, outl, olen;
if (!ctx->init) {
@@ -277,19 +307,20 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx,
return -1;
}
ctx->init = 1;
- if (ctx->mode == LWS_GAESM_GCM) {
- /* AAD */
- if (len)
- if (EVP_EncryptUpdate(ctx->ctx, NULL, &olen,
- in, len) != 1) {
- lwsl_err("%s: set aad failed\n",
- __func__);
+ }
- return -1;
- }
+ if (ctx->mode == LWS_GAESM_GCM && !out) {
+ /* AAD */
+ if (len)
+ if (EVP_EncryptUpdate(ctx->ctx, NULL, &olen,
+ in, len) != 1) {
+ lwsl_err("%s: set aad failed\n",
+ __func__);
- return 0;
- }
+ return -1;
+ }
+
+ return 0;
}
switch (ctx->op) {
@@ -299,12 +330,15 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx,
case LWS_GAESO_DEC:
n = EVP_DecryptUpdate(ctx->ctx, out, &outl, in, len);
break;
+ default:
+ return -1;
}
// lwsl_notice("discarding outl %d\n", (int)outl);
if (!n) {
lwsl_notice("%s: update failed\n", __func__);
+ lws_tls_err_describe();
return -1;
}
diff --git a/lib/tls/openssl/lws-gencrypto.c b/lib/tls/openssl/lws-gencrypto.c
index 00c63c810..995e26183 100644
--- a/lib/tls/openssl/lws-gencrypto.c
+++ b/lib/tls/openssl/lws-gencrypto.c
@@ -24,6 +24,11 @@
#include "core/private.h"
#include "tls/openssl/private.h"
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
int
lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type)
{
diff --git a/lib/tls/openssl/lws-genec.c b/lib/tls/openssl/lws-genec.c
index b8205017a..03b36c193 100644
--- a/lib/tls/openssl/lws-genec.c
+++ b/lib/tls/openssl/lws-genec.c
@@ -24,6 +24,56 @@
#include "core/private.h"
#include "tls/openssl/private.h"
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
+#if !defined(LWS_HAVE_ECDSA_SIG_set0)
+static void
+ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
+{
+ if (pr != NULL)
+ *pr = sig->r;
+ if (ps != NULL)
+ *ps = sig->s;
+}
+
+static int
+ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ if (r == NULL || s == NULL)
+ return 0;
+ BN_clear_free(sig->r);
+ BN_clear_free(sig->s);
+ sig->r = r;
+ sig->s = s;
+
+ return 1;
+}
+#endif
+#if !defined(LWS_HAVE_BN_bn2binpad)
+static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
+{
+ int i;
+ BN_ULONG l;
+
+ bn_check_top(a);
+ i = BN_num_bytes(a);
+
+ /* Add leading zeroes if necessary */
+ if (tolen > i) {
+ memset(to, 0, tolen - i);
+ to += tolen - i;
+ }
+ while (i--) {
+ l = a->d[i / BN_BYTES];
+ *(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff;
+ }
+ return tolen;
+}
+#endif
+
const struct lws_ec_curves lws_ec_curves[] = {
/*
* These are the curves we are willing to use by default...
@@ -88,15 +138,16 @@ lws_genec_eckey_import(int nid, EVP_PKEY *pkey, struct lws_gencrypto_keyelem *el
}
n = EC_KEY_set_private_key(ec, bn_d);
- BN_free(bn_d);
+ BN_clear_free(bn_d);
if (n != 1) {
lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__);
goto bail;
}
- if (EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
+ n = EVP_PKEY_assign_EC_KEY(pkey, ec);
+ if (n != 1) {
lwsl_err("%s: EVP_PKEY_set1_EC_KEY failed\n", __func__);
- goto bail;
+ return -1;
}
return 0;
@@ -141,6 +192,7 @@ lws_genec_keypair_import(const struct lws_ec_curves *curve_table,
*pctx = EVP_PKEY_CTX_new(pkey, NULL);
EVP_PKEY_free(pkey);
pkey = NULL;
+
if (!*pctx)
goto bail;
@@ -210,6 +262,11 @@ lws_genec_keypair_destroy(EVP_PKEY_CTX **pctx)
{
if (!*pctx)
return;
+
+// lwsl_err("%p\n", EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx)));
+
+// EC_KEY_free(EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx)));
+
EVP_PKEY_CTX_free(*pctx);
*pctx = NULL;
}
@@ -234,9 +291,6 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
BIGNUM *bn[3];
EC_KEY *ec;
- if (ctx->genec_alg != LEGENEC_ECDH)
- return -1;
-
curve = lws_genec_curve(ctx->curve_table, curve_name);
if (!curve) {
lwsl_err("%s: curve '%s' not supported\n",
@@ -246,11 +300,15 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
}
ec = EC_KEY_new_by_curve_name(curve->tls_lib_nid);
- if (!ec)
+ if (!ec) {
+ lwsl_err("%s: unknown nid %d\n", __func__, curve->tls_lib_nid);
return -23;
+ }
- if (EC_KEY_generate_key(ec) != 1)
+ if (EC_KEY_generate_key(ec) != 1) {
+ lwsl_err("%s: EC_KEY_generate_key failed\n", __func__);
goto bail;
+ }
pkey = EVP_PKEY_new();
if (!pkey)
@@ -273,8 +331,10 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
*/
pubkey = EC_KEY_get0_public_key(ec);
- if (!pubkey)
+ if (!pubkey) {
+ lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__);
goto bail1;
+ }
bn[0] = BN_new();
bn[1] = (BIGNUM *)EC_KEY_get0_private_key(ec);
@@ -290,8 +350,10 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = strlen(curve_name) + 1;
el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf =
lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec");
- if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf)
+ if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) {
+ lwsl_err("%s: OOM\n", __func__);
goto bail2;
+ }
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
@@ -302,7 +364,7 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
if (!el[n].buf)
goto bail2;
- m = BN_bn2bin(bn[n - 1], el[n].buf);
+ m = BN_bn2binpad(bn[n - 1], el[n].buf, el[n].len);
if (m != el[n].len)
goto bail2;
}
@@ -310,8 +372,8 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
ret = 0;
bail2:
- BN_free(bn[0]);
- BN_free(bn[2]);
+ BN_clear_free(bn[0]);
+ BN_clear_free(bn[2]);
bail1:
EVP_PKEY_free(pkey);
bail:
@@ -341,6 +403,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
return lws_genec_new_keypair(ctx, LDHS_OURS, curve_name, el);
}
+#if 0
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
@@ -385,47 +448,161 @@ bail:
return -1;
}
+#endif
LWS_VISIBLE LWS_EXTERN int
-lws_genecdsa_hash_sig_verify(struct lws_genec_ctx *ctx, const uint8_t *in,
- enum lws_genhash_types hash_type,
- const uint8_t *sig, size_t sig_len)
+lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ uint8_t *sig, size_t sig_len)
{
- const EVP_MD *md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type);
- EVP_MD_CTX *mdctx = NULL;
- int ret = -1;
+ int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
+ const BIGNUM *r = NULL, *s = NULL;
+ ECDSA_SIG *ecdsasig;
+ EC_KEY *eckey;
- if (ctx->genec_alg != LEGENEC_ECDSA)
+ if (ctx->genec_alg != LEGENEC_ECDSA) {
+ lwsl_notice("%s: ctx alg %d\n", __func__, ctx->genec_alg);
return -1;
+ }
- if (!md)
+ if ((int)sig_len < keybytes * 2) {
+ lwsl_notice("%s: sig buff %d < %d\n", __func__,
+ (int)sig_len, keybytes * 2);
return -1;
+ }
- mdctx = EVP_MD_CTX_create();
- if (!mdctx)
- goto bail;
+ eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx));
- if (EVP_DigestVerifyInit(mdctx, NULL, md, NULL,
- EVP_PKEY_CTX_get0_pkey(ctx->ctx))) {
- lwsl_err("%s: EVP_DigestSignInit failed\n", __func__);
+ /*
+ * The ECDSA P-256 SHA-256 digital signature is generated as follows:
+ *
+ * 1. Generate a digital signature of the JWS Signing Input using ECDSA
+ * P-256 SHA-256 with the desired private key. The output will be
+ * the pair (R, S), where R and S are 256-bit unsigned integers.
+ *
+ * 2. Turn R and S into octet sequences in big-endian order, with each
+ * array being be 32 octets long. The octet sequence
+ * representations MUST NOT be shortened to omit any leading zero
+ * octets contained in the values.
+ *
+ * 3. Concatenate the two octet sequences in the order R and then S.
+ * (Note that many ECDSA implementations will directly produce this
+ * concatenation as their output.)
+ *
+ * 4. The resulting 64-octet sequence is the JWS Signature value.
+ */
+ ecdsasig = ECDSA_do_sign(in, lws_genhash_size(hash_type), eckey);
+ EC_KEY_free(eckey);
+ if (!ecdsasig) {
+ lwsl_notice("%s: ECDSA_do_sign fail\n", __func__);
goto bail;
}
- if (EVP_DigestVerifyUpdate(mdctx, in, EVP_MD_size(md))) {
- lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__);
+ ECDSA_SIG_get0(ecdsasig, &r, &s);
+
+ /*
+ * in the 521-bit case, we have to pad the last byte as it only
+ * generates 65 bytes
+ */
+
+ n = BN_bn2binpad(r, sig, keybytes);
+ if (n != keybytes) {
+ lwsl_notice("%s: bignum r fail %d %d\n", __func__, n, keybytes);
goto bail;
}
- if (EVP_DigestVerifyFinal(mdctx, sig, sig_len)) {
- lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__);
+ n = BN_bn2binpad(s, sig + keybytes, keybytes);
+ if (n != keybytes) {
+ lwsl_notice("%s: bignum s fail %d %d\n", __func__, n, keybytes);
goto bail;
}
ret = 0;
+
bail:
- if (mdctx)
- EVP_MD_CTX_free(mdctx);
+ if (ecdsasig)
+ ECDSA_SIG_free(ecdsasig);
+
+ return ret;
+}
+
+/* in is the JWS Signing Input hash */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ const uint8_t *sig, size_t sig_len)
+{
+ int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits),
+ hlen = lws_genhash_size(hash_type);
+ ECDSA_SIG *ecsig = ECDSA_SIG_new();
+ BIGNUM *r = NULL, *s = NULL;
+ EC_KEY *eckey;
+
+ if (!ecsig)
+ return -1;
+
+ if (ctx->genec_alg != LEGENEC_ECDSA)
+ goto bail;
+
+ if ((int)sig_len != keybytes * 2) {
+ lwsl_err("%s: sig buf too small %d vs %d\n", __func__,
+ (int)sig_len, keybytes * 2);
+ goto bail;
+ }
+ /*
+ * 1. The JWS Signature value MUST be a 64-octet sequence. If it is
+ * not a 64-octet sequence, the validation has failed.
+ *
+ * 2. Split the 64-octet sequence into two 32-octet sequences. The
+ * first octet sequence represents R and the second S. The values R
+ * and S are represented as octet sequences using the Integer-to-
+ * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
+ * (in big-endian octet order).
+ *
+ * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
+ * the ECDSA P-256 SHA-256 validator.
+ */
+
+ r = BN_bin2bn(sig, keybytes, NULL);
+ if (!r) {
+ lwsl_err("%s: BN_bin2bn (r) fail\n", __func__);
+ goto bail;
+ }
+
+ s = BN_bin2bn(sig + keybytes, keybytes, NULL);
+ if (!s) {
+ lwsl_err("%s: BN_bin2bn (s) fail\n", __func__);
+ goto bail1;
+ }
+
+ if (ECDSA_SIG_set0(ecsig, r, s) != 1) {
+ lwsl_err("%s: ECDSA_SIG_set0 fail\n", __func__);
+ goto bail1;
+ }
+
+ eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx));
+
+ n = ECDSA_do_verify(in, hlen, ecsig, eckey);
+ EC_KEY_free(eckey);
+ if (n != 1) {
+ lwsl_err("%s: ECDSA_do_verify fail\n", __func__);
+ lws_tls_err_describe();
+ goto bail;
+ }
+
+ ret = 0;
+ goto bail;
+
+bail1:
+ if (r)
+ BN_free(r);
+ if (s)
+ BN_free(s);
+
+bail:
+ ECDSA_SIG_free(ecsig);
return ret;
}
diff --git a/lib/tls/openssl/lws-genhash.c b/lib/tls/openssl/lws-genhash.c
index c6ca2c5ec..e9d5125b7 100644
--- a/lib/tls/openssl/lws-genhash.c
+++ b/lib/tls/openssl/lws-genhash.c
@@ -23,6 +23,11 @@
*/
#include "libwebsockets.h"
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
int
lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
{
@@ -60,6 +65,9 @@ lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
int
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
{
+ if (!len)
+ return 0;
+
return EVP_DigestUpdate(ctx->mdctx, in, len) != 1;
}
diff --git a/lib/tls/openssl/lws-genrsa.c b/lib/tls/openssl/lws-genrsa.c
index ca3c2d539..826c31240 100644
--- a/lib/tls/openssl/lws-genrsa.c
+++ b/lib/tls/openssl/lws-genrsa.c
@@ -24,14 +24,15 @@
#include "core/private.h"
#include "tls/openssl/private.h"
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
LWS_VISIBLE void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el)
{
- int n;
-
- for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++)
- if (el[n].buf)
- lws_free_set_NULL(el[n].buf);
+ lws_gencrypto_destroy_elements(el, LWS_GENCRYPTO_RSA_KEYEL_COUNT);
}
static int mode_map_crypt[] = { RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING },
@@ -74,7 +75,8 @@ bail:
LWS_VISIBLE int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
- struct lws_context *context, enum enum_genrsa_mode mode)
+ struct lws_context *context, enum enum_genrsa_mode mode,
+ enum lws_genhash_types oaep_hashid)
{
int n;
@@ -129,7 +131,7 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
bail:
for (n = 0; n < 5; n++)
if (ctx->bn[n]) {
- BN_free(ctx->bn[n]);
+ BN_clear_free(ctx->bn[n]);
ctx->bn[n] = NULL;
}
@@ -168,7 +170,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
}
n = RSA_generate_key_ex(ctx->rsa, bits, bn, NULL);
- BN_free(bn);
+ BN_clear_free(bn);
if (n != 1)
goto cleanup_1;
@@ -219,27 +221,59 @@ LWS_VISIBLE int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
- if (RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
- mode_map_crypt[ctx->mode]) < 0) {
+ int n = RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
+ mode_map_crypt[ctx->mode]);
+ if (n < 0) {
lwsl_err("%s: RSA_public_encrypt failed\n", __func__);
lws_tls_err_describe();
return -1;
}
- return 0;
+ return n;
+}
+
+LWS_VISIBLE int
+lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out)
+{
+ int n = RSA_private_encrypt((int)in_len, in, out, ctx->rsa,
+ mode_map_crypt[ctx->mode]);
+ if (n < 0) {
+ lwsl_err("%s: RSA_private_encrypt failed\n", __func__);
+ lws_tls_err_describe();
+ return -1;
+ }
+
+ return n;
}
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)
{
- if (RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
- mode_map_crypt[ctx->mode]) < 0) {
+ int n = RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
+ mode_map_crypt[ctx->mode]);
+ if (n < 0) {
lwsl_err("%s: RSA_public_decrypt failed\n", __func__);
return -1;
}
- return 0;
+ return n;
+}
+
+LWS_VISIBLE int
+lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max)
+{
+ int n = RSA_private_decrypt((int)in_len, in, out, ctx->rsa,
+ mode_map_crypt[ctx->mode]);
+ if (n < 0) {
+ lwsl_err("%s: RSA_private_decrypt failed\n", __func__);
+ lws_tls_err_describe();
+ return -1;
+ }
+
+ return n;
}
LWS_VISIBLE int
@@ -271,7 +305,8 @@ lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
}
if (n != 1) {
- lwsl_notice("%s: -0x%x\n", __func__, -n);
+ lwsl_notice("%s: fail\n", __func__);
+ lws_tls_err_describe();
return -1;
}
diff --git a/lib/tls/openssl/openssl-client.c b/lib/tls/openssl/openssl-client.c
index 62e250b54..ed2bbdf52 100644
--- a/lib/tls/openssl/openssl-client.c
+++ b/lib/tls/openssl/openssl-client.c
@@ -21,6 +21,11 @@
#include "core/private.h"
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
int lws_openssl_describe_cipher(struct lws *wsi);
extern int openssl_websocket_private_data_index,
diff --git a/lib/tls/openssl/openssl-server.c b/lib/tls/openssl/openssl-server.c
index d680ea837..96d3767cb 100644
--- a/lib/tls/openssl/openssl-server.c
+++ b/lib/tls/openssl/openssl-server.c
@@ -21,6 +21,11 @@
#include "core/private.h"
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c
index db4f7b72f..06859b3ec 100644
--- a/lib/tls/openssl/ssl.c
+++ b/lib/tls/openssl/ssl.c
@@ -25,6 +25,11 @@
int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
+/*
+ * Care: many openssl apis return 1 for success. These are translated to the
+ * lws convention of 0 for success.
+ */
+
int lws_openssl_describe_cipher(struct lws *wsi)
{
#if !defined(LWS_WITH_NO_LOGS)
diff --git a/lib/tls/private.h b/lib/tls/private.h
index 6a1cd0012..a299b5eca 100644
--- a/lib/tls/private.h
+++ b/lib/tls/private.h
@@ -293,6 +293,11 @@ lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
int
lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt);
+int
+lws_gencrypto_bits_to_bytes(int bits);
+
+void
+lws_gencrypto_destroy_elements(struct lws_gencrypto_keyelem *el, int m);
/* genec */
diff --git a/minimal-examples/README.md b/minimal-examples/README.md
index d80dece60..2f271dbc2 100644
--- a/minimal-examples/README.md
+++ b/minimal-examples/README.md
@@ -1,6 +1,7 @@
|name|demonstrates|
---|---
client-server|Minimal examples providing client and server connections simultaneously
+crypto|Minimal examples related to using lws crypto apis
dbus-server|Minimal examples showing how to integrate DBUS into lws event loop
http-client|Minimal examples providing an http client
http-server|Minimal examples providing an http server
diff --git a/minimal-examples/api-tests/README.md b/minimal-examples/api-tests/README.md
index b047af52f..ff8d48ed3 100644
--- a/minimal-examples/api-tests/README.md
+++ b/minimal-examples/api-tests/README.md
@@ -1,5 +1,10 @@
+These are buildable test apps that run in CI to confirm correct api operation.
+
|name|tests|
---|---
-api-test-lwsac|LWS Allocated Chunks
-api-test-lws_tokenize|Generic secure string tokenizer
+api-test-lwsac|LWS Allocated Chunks api
+api-test-lws_tokenize|Generic secure string tokenizer api
+api-test-fts|LWS Full-text Search api
+api-test-gencrypto|LWS Generic Crypto apis
+api-test-jose|LWS JOSE apis
diff --git a/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c b/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c
index b14e08e59..f2f6f20f9 100644
--- a/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c
+++ b/minimal-examples/api-tests/api-test-gencrypto/lws-genaes.c
@@ -64,7 +64,7 @@ test_genaes_cbc(void)
return -1;
}
- if (memcmp(cbc256_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(cbc256_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -88,7 +88,7 @@ test_genaes_cbc(void)
return -1;
}
- if (memcmp(cbc256, res1, 16)) {
+ if (lws_timingsafe_bcmp(cbc256, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -151,7 +151,7 @@ test_genaes_cfb128(void)
return -1;
}
- if (memcmp(cfb128_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(cfb128_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -175,7 +175,7 @@ test_genaes_cfb128(void)
return -1;
}
- if (memcmp(cfb128, res1, 16)) {
+ if (lws_timingsafe_bcmp(cfb128, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res1, 16);
return -1;
@@ -237,7 +237,7 @@ test_genaes_cfb8(void)
return -1;
}
- if (memcmp(cfb8_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(cfb8_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -259,7 +259,7 @@ test_genaes_cfb8(void)
return -1;
}
- if (memcmp(cfb8, res1, 16)) {
+ if (lws_timingsafe_bcmp(cfb8, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res1, 16);
return -1;
@@ -325,7 +325,7 @@ test_genaes_ctr(void)
return -1;
}
- if (memcmp(ctr_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(ctr_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -350,7 +350,7 @@ test_genaes_ctr(void)
return -1;
}
- if (memcmp(ctr, res1, 16)) {
+ if (lws_timingsafe_bcmp(ctr, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res1, 16);
return -1;
@@ -415,7 +415,7 @@ test_genaes_ecb(void)
return -1;
}
- if (memcmp(ecb_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(ecb_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -436,7 +436,7 @@ test_genaes_ecb(void)
return -1;
}
- if (memcmp(ecb, res1, 16)) {
+ if (lws_timingsafe_bcmp(ecb, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -450,6 +450,9 @@ bail:
return -1;
}
+#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_OFB)
+#else
+
static const uint8_t
/*
* produced with (plaintext.txt contains "test plaintext\0\0")
@@ -506,7 +509,7 @@ test_genaes_ofb(void)
return -1;
}
- if (memcmp(ofb_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(ofb_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -530,7 +533,7 @@ test_genaes_ofb(void)
return -1;
}
- if (memcmp(ofb, res1, 16)) {
+ if (lws_timingsafe_bcmp(ofb, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -544,6 +547,8 @@ bail:
return -1;
}
+#endif
+
static const uint8_t
/*
* Fedora openssl tool doesn't support xts... this data produced
@@ -594,7 +599,7 @@ test_genaes_xts(void)
return -1;
}
- if (memcmp(xts_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(xts_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -615,7 +620,7 @@ test_genaes_xts(void)
return -1;
}
- if (memcmp(xts, res1, 16)) {
+ if (lws_timingsafe_bcmp(xts, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@@ -697,7 +702,7 @@ test_genaes_gcm(void)
return -1;
}
- if (memcmp(gcm_ct, res, sizeof(gcm_ct))) {
+ if (lws_timingsafe_bcmp(gcm_ct, res, sizeof(gcm_ct))) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, sizeof(gcm_ct));
return -1;
@@ -730,7 +735,7 @@ test_genaes_gcm(void)
return -1;
}
- if (memcmp(gcm_pt, res, sizeof(gcm_pt))) {
+ if (lws_timingsafe_bcmp(gcm_pt, res, sizeof(gcm_pt))) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, sizeof(gcm_ct));
return -1;
@@ -763,8 +768,11 @@ test_genaes(struct lws_context *context)
if (test_genaes_ecb())
goto bail;
+#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_OFB)
+#else
if (test_genaes_ofb())
goto bail;
+#endif
#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_XTS)
#else
diff --git a/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c b/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c
index d640650dd..db852c9a8 100644
--- a/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c
+++ b/minimal-examples/api-tests/api-test-gencrypto/lws-genec.c
@@ -54,7 +54,7 @@ test_genec1(struct lws_context *context)
lws_jwk_dump(&jwk);
- if (jwk.kty != LWS_GENCRYPTO_KYT_EC) {
+ if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
lws_jwk_destroy(&jwk);
lwsl_err("%s: jwk is not an EC key\n", __func__);
return 1;
@@ -76,7 +76,7 @@ test_genec1(struct lws_context *context)
goto bail;
}
- if (memcmp(cbc256_enc, res, 16)) {
+ if (lws_timingsafe_bcmp(cbc256_enc, res, 16)) {
lwsl_err("%s: lws_genec_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
goto bail;
@@ -95,7 +95,7 @@ test_genec1(struct lws_context *context)
goto bail;
}
- if (memcmp(cbc256, res1, 16)) {
+ if (lws_timingsafe_bcmp(cbc256, res1, 16)) {
lwsl_err("%s: lws_genec_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
goto bail;
diff --git a/minimal-examples/api-tests/api-test-jose/jwe.c b/minimal-examples/api-tests/api-test-jose/jwe.c
index 27b8b9b3d..f619a53a6 100644
--- a/minimal-examples/api-tests/api-test-jose/jwe.c
+++ b/minimal-examples/api-tests/api-test-jose/jwe.c
@@ -9,331 +9,1992 @@
#include
+/*
+ * These are the inputs and outputs from the worked example in RFC7516
+ * Appendix A.1 {"alg":"RSA-OAEP","enc":"A256GCM"}
+ */
-/* A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 */
+
+static char
+
+*ex_a1_ptext =
+ "The true sign of intelligence is not knowledge but imagination.",
+
+*ex_a1_compact =
+ "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ."
+ "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe"
+ "ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb"
+ "Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV"
+ "mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8"
+ "1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi"
+ "6UklfCpIMfIjf7iGdXKHzg."
+ "48V1_ALb6US04U3b."
+ "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji"
+ "SdiwkIr3ajwQzaBtQD_A."
+ "XFBoMYUZodetZdvTiFvSkQ",
+
+ *ex_a1_jwk_json =
+ "{\"kty\":\"RSA\","
+ "\"n\":\"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW"
+ "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S"
+ "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a"
+ "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS"
+ "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj"
+ "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N"
+ "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9"
+ "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk"
+ "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl"
+ "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd"
+ "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ\","
+ "\"p\":\"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-"
+ "SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lf"
+ "fNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0\","
+ "\"q\":\"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBm"
+ "UDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aX"
+ "IWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc\","
+ "\"dp\":\"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KL"
+ "hMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827"
+ "rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE\","
+ "\"dq\":\"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCj"
+ "ywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDB"
+ "UfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis\","
+ "\"qi\":\"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7"
+ "AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3"
+ "eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY\""
+ "}"
+;
+
+static int
+test_jwe_a1(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[2048], compact[2048];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ if (lws_jwk_import(&jwk, NULL, NULL, ex_a1_jwk_json,
+ strlen(ex_a1_jwk_json)) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode(ex_a1_compact, strlen(ex_a1_compact),
+ &jws.map, &jws.map_b64, temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < strlen(ex_a1_ptext) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ex_a1_ptext,
+ strlen(ex_a1_ptext))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ex_a1_ptext, strlen(ex_a1_ptext));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ /*
+ * Canned decrypt worked properly... let's also try encoding the
+ * plaintext ourselves and decoding that...
+ */
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ temp_len = sizeof(temp);
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ if (lws_jwk_import(&jwk, NULL, NULL, ex_a1_jwk_json,
+ strlen(ex_a1_jwk_json)) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ if (lws_gencrypto_jwe_alg_to_definition("RSA-OAEP", &jose.alg)) {
+ lwsl_err("Unknown cipher alg \"RSA-OAEP\"\n");
+ goto bail1;
+ }
+ if (lws_gencrypto_jwe_enc_to_definition("A256GCM", &jose.enc_alg)) {
+ lwsl_err("Unknown payload enc alg \"A256GCM\"\n");
+ goto bail1;
+ }
+
+ /* we require a JOSE-formatted header to do the encryption */
+
+ if (temp_len < 256)
+ goto bail1;
+ jws.map.buf[LJWS_JOSE] = temp;
+ jws.map.len[LJWS_JOSE] = lws_snprintf(temp, temp_len,
+ "{\"alg\":\"%s\",\"enc\":\"%s\"}", "RSA-OAEP", "A256GCM");
+ temp_len -= jws.map.len[LJWS_JOSE];
+
+ /*
+ * dup the plaintext into the ciphertext element, it will be
+ * encrypted in-place to a ciphertext of the same length
+ */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_CTXT,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ ex_a1_ptext, strlen(ex_a1_ptext), 0)) {
+ lwsl_notice("%s: Not enough temp space for ptext\n", __func__);
+ goto bail;
+ }
+
+ /* CEK size is determined by hash / hmac size */
+
+ n = lws_gencrypto_bits_to_bytes(jose.enc_alg->keybits_fixed);
+ if (lws_jws_randomize_element(context, &jws.map, LJWE_EKEY,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, n,
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
+ lwsl_err("Problem getting random\n");
+ goto bail1;
+ }
+
+ n = lws_jwe_encrypt(&jose, &jws, lws_concat_temp(temp, temp_len),
+ &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
+ goto bail1;
+ }
+ n = lws_jwe_write_compact(&jose, &jws, compact, sizeof(compact));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_write_compact failed: %d\n",
+ __func__, n);
+ goto bail1;
+ }
+
+ // puts(compact);
+
+ /*
+ * Okay... what happens when we try to decode what we created?
+ */
+
+ lws_jws_destroy(&jws);
+ lws_jws_init(&jws, &jwk, context);
+ temp_len = sizeof(temp);
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode(compact, strlen(compact),
+ &jws.map, &jws.map_b64, temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: generated lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+bail:
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+
+/* A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256
+ *
+ * This example encrypts the plaintext "Live long and prosper." to the
+ * recipient using RSAES-PKCS1-v1_5 for key encryption and
+ * AES_128_CBC_HMAC_SHA_256 for content encryption.
+ */
/* "Live long and prosper." */
-static
-uint8_t
+static uint8_t
-#if 0
-lws_jwe_ex_a2_plaintext[] = {
+ex_a2_ptext[] = {
+ 76, 105, 118, 101, 32, 108, 111, 110,
+ 103, 32, 97, 110, 100, 32, 112, 114,
+ 111, 115, 112, 101, 114, 46
+}, *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\""
+ "}",
+
+*ex_a2_compact = (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"
+;
+
+static int
+test_jwe_a2(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[2048];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ 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("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode((const char *)ex_a2_compact,
+ strlen((char *)ex_a2_compact),
+ &jws.map, &jws.map_b64,
+ (char *)temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ex_a2_ptext) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ex_a2_ptext,
+ sizeof(ex_a2_ptext))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ex_a2_ptext, sizeof(ex_a2_ptext));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+/* JWE creation using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256
+ *
+ * This example encrypts a different, larger plaintext using the jwk key from
+ * the test above, and AES_128_CBC_HMAC_SHA_256 for content encryption.
+ */
+
+static const char *rsa256a128_jose =
+ "{ \"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}";
+
+static uint8_t
+
+ /* plaintext is 1024 bytes from /dev/urandom */
+
+ra_ptext_1024[] = {
+ 0xfe, 0xc6, 0x4f, 0x3e, 0x4a, 0x19, 0xe9, 0xd7,
+ 0xc2, 0x13, 0xe7, 0xc5, 0x78, 0x6e, 0x71, 0xf6,
+ 0x6e, 0xdd, 0x04, 0xaf, 0xaa, 0x4e, 0xa8, 0xad,
+ 0xd8, 0xe0, 0xb3, 0x32, 0x97, 0x43, 0x7c, 0xd8,
+ 0xd1, 0x5f, 0x56, 0xac, 0x70, 0xaf, 0x7d, 0x0b,
+ 0x40, 0xa1, 0x96, 0x71, 0x7c, 0xc4, 0x4a, 0x37,
+ 0x0b, 0xa6, 0x06, 0xb3, 0x8c, 0x87, 0xee, 0xb6,
+ 0x15, 0xfe, 0xaa, 0x60, 0x7e, 0x7f, 0xdc, 0xb0,
+ 0xff, 0x96, 0x4b, 0x30, 0x60, 0xcf, 0xc6, 0x5d,
+ 0x09, 0x6a, 0x6f, 0x66, 0x0c, 0x5f, 0xb0, 0x6f,
+ 0x61, 0xa6, 0x26, 0x02, 0xbd, 0x46, 0xda, 0xa3,
+ 0x73, 0x19, 0x17, 0xff, 0xe0, 0x5f, 0x30, 0x72,
+ 0x7d, 0x17, 0xd8, 0xb2, 0xbe, 0x84, 0x3e, 0x4d,
+ 0x76, 0xbd, 0x62, 0x5d, 0x63, 0xfe, 0x11, 0x32,
+ 0x11, 0x41, 0xdc, 0xed, 0x96, 0xfd, 0x31, 0x38,
+ 0x6a, 0x84, 0x55, 0x7a, 0x33, 0x3f, 0x37, 0xc3,
+ 0x37, 0x7b, 0xc1, 0xb7, 0x89, 0x00, 0x39, 0xa6,
+ 0x94, 0x91, 0xb7, 0x19, 0x6b, 0x1d, 0x99, 0xeb,
+ 0xf6, 0x10, 0xb9, 0xd2, 0xcd, 0x15, 0x0d, 0xbc,
+ 0x24, 0x34, 0x9a, 0x52, 0x64, 0x21, 0x72, 0x1e,
+ 0x9a, 0x00, 0xf2, 0xcf, 0xf1, 0x7d, 0x1a, 0x12,
+ 0x8d, 0x39, 0xbc, 0xf9, 0x09, 0xfd, 0xd9, 0x22,
+ 0x27, 0x28, 0xe1, 0x3a, 0x0b, 0x82, 0xba, 0x9a,
+ 0xe5, 0x9d, 0xa8, 0x12, 0x6e, 0xf5, 0x4b, 0xc7,
+ 0x2b, 0x9c, 0xdc, 0xfe, 0xf3, 0xe8, 0x74, 0x65,
+ 0x3d, 0xe0, 0xaa, 0x64, 0xf3, 0x43, 0xa4, 0x88,
+ 0xa8, 0xbe, 0x60, 0xdb, 0xfd, 0x2d, 0x3b, 0x84,
+ 0x82, 0x8f, 0x4d, 0xbb, 0xe4, 0xa9, 0x59, 0xe3,
+ 0x6c, 0x52, 0x45, 0xe4, 0x34, 0xdb, 0x28, 0x0e,
+ 0x4a, 0x44, 0xb6, 0x9a, 0x25, 0x9b, 0x3b, 0xae,
+ 0xe1, 0x12, 0x1d, 0x1c, 0x66, 0x7d, 0xb9, 0x5b,
+ 0x5f, 0xc2, 0x4a, 0xaa, 0xd2, 0xe9, 0x65, 0xe2,
+ 0x85, 0x6f, 0xf6, 0x67, 0x66, 0x8e, 0x0b, 0xd2,
+ 0x60, 0xf8, 0x43, 0x60, 0x04, 0x9b, 0xa9, 0x3a,
+ 0x6a, 0x3c, 0x02, 0x3c, 0x08, 0x9d, 0x60, 0x1c,
+ 0xc4, 0x27, 0x3e, 0xff, 0xd0, 0x70, 0x94, 0x43,
+ 0x3e, 0x9e, 0x69, 0x19, 0x22, 0xf0, 0xec, 0x26,
+ 0x2d, 0xa5, 0x71, 0xf3, 0x92, 0x61, 0x95, 0xce,
+ 0xc3, 0xc0, 0xa0, 0xc3, 0x98, 0x22, 0xdd, 0x32,
+ 0x3c, 0x48, 0xcb, 0xd1, 0x61, 0xa0, 0xaa, 0x9a,
+ 0x7e, 0x5a, 0xfa, 0x26, 0x46, 0x49, 0xfc, 0x9c,
+ 0xaa, 0x21, 0x06, 0x45, 0xf1, 0xa0, 0xc9, 0xef,
+ 0x6b, 0x89, 0xf2, 0x01, 0x20, 0x54, 0xfa, 0x0a,
+ 0x23, 0xff, 0xbd, 0x64, 0x35, 0x94, 0xfd, 0x35,
+ 0x70, 0x52, 0x94, 0x66, 0xc5, 0xd0, 0x27, 0xc1,
+ 0x8f, 0x6d, 0xc4, 0xa3, 0x34, 0xc2, 0xea, 0xf0,
+ 0xb3, 0x0d, 0x6c, 0x13, 0xb5, 0xc9, 0x6e, 0x5c,
+ 0xeb, 0x8b, 0x7b, 0xf5, 0x21, 0x4c, 0xe3, 0xb7,
+ 0x73, 0x6d, 0x07, 0xaa, 0x44, 0xc4, 0xba, 0xc5,
+ 0xa5, 0x0e, 0x75, 0x28, 0xb7, 0x50, 0x22, 0x54,
+ 0xa7, 0xe1, 0x2e, 0xfd, 0x20, 0xcd, 0xa4, 0x31,
+ 0xa3, 0xb2, 0x73, 0x98, 0x7c, 0x3c, 0x8f, 0xa3,
+ 0x40, 0x8a, 0xaf, 0x31, 0xfa, 0xf9, 0x70, 0x4d,
+ 0x83, 0x10, 0xc4, 0xa0, 0x9c, 0xd6, 0xa3, 0xd5,
+ 0x07, 0xaf, 0xaf, 0x35, 0x15, 0xd0, 0x84, 0x09,
+ 0x20, 0x36, 0x88, 0xac, 0x6f, 0x16, 0x5e, 0x03,
+ 0xa9, 0xfc, 0xb3, 0x2d, 0x01, 0x57, 0xb3, 0xed,
+ 0x4b, 0x55, 0x2b, 0xbc, 0x92, 0x87, 0x3e, 0x27,
+ 0xc4, 0x2c, 0x44, 0xac, 0x05, 0x5f, 0x26, 0xe7,
+ 0xe9, 0xb0, 0x2d, 0x6b, 0x3c, 0x8c, 0xd2, 0xb4,
+ 0x3c, 0xb4, 0x86, 0xfe, 0x68, 0x99, 0x2a, 0x42,
+ 0xac, 0xa4, 0xb3, 0x89, 0x61, 0xb3, 0xd1, 0xdf,
+ 0x9b, 0x58, 0xc7, 0x81, 0x62, 0x87, 0x26, 0x52,
+ 0x51, 0xe7, 0x7d, 0x7c, 0x37, 0x14, 0xe5, 0x19,
+ 0x28, 0x34, 0x3e, 0x95, 0x17, 0x36, 0x12, 0xf9,
+ 0x5e, 0xc1, 0x3c, 0x9c, 0x28, 0x70, 0x06, 0xdf,
+ 0xc4, 0x6d, 0x25, 0x04, 0x46, 0xe0, 0x95, 0xf0,
+ 0xc8, 0x57, 0x48, 0x27, 0x26, 0xf3, 0xf7, 0x19,
+ 0xbe, 0xea, 0xb4, 0xd4, 0x64, 0xaf, 0x67, 0x7c,
+ 0xf5, 0xa9, 0xfb, 0x85, 0x4a, 0x43, 0x9c, 0x62,
+ 0x06, 0x5e, 0x28, 0x2a, 0x7b, 0x1e, 0xb3, 0x07,
+ 0xe7, 0x19, 0x32, 0xa4, 0x4e, 0xb4, 0xce, 0xe0,
+ 0x92, 0x56, 0xf5, 0x10, 0xcb, 0x56, 0x34, 0x4b,
+ 0x0d, 0xe1, 0xd3, 0x6d, 0xfe, 0xf0, 0x44, 0xf7,
+ 0x22, 0x1d, 0x5e, 0x6b, 0xa7, 0xa5, 0x83, 0x2e,
+ 0xeb, 0x14, 0xf2, 0xd7, 0x27, 0x5a, 0x2a, 0xd2,
+ 0x55, 0x35, 0xe6, 0x7e, 0xd9, 0x3b, 0xac, 0x4e,
+ 0x5a, 0x22, 0x46, 0xd5, 0x7b, 0x57, 0x9c, 0x58,
+ 0xfe, 0xd0, 0xda, 0xbf, 0x7d, 0xe9, 0x8c, 0xb7,
+ 0xba, 0x88, 0xf1, 0xc3, 0x82, 0x53, 0xc3, 0x66,
+ 0x20, 0x51, 0x12, 0xd3, 0xf9, 0xaf, 0xe9, 0xcb,
+ 0xc1, 0x7a, 0xe6, 0x22, 0x44, 0xa5, 0xdf, 0x18,
+ 0xb3, 0x6e, 0x6c, 0xba, 0xf3, 0xc6, 0x24, 0x5a,
+ 0x1c, 0x67, 0xa6, 0xa5, 0xb4, 0xb1, 0x35, 0xdf,
+ 0x5a, 0x60, 0x5c, 0x0b, 0x66, 0xd3, 0x1f, 0x4e,
+ 0x7c, 0xcb, 0x93, 0x7e, 0x2f, 0x6d, 0xbd, 0xce,
+ 0x26, 0x52, 0x44, 0xee, 0xbb, 0xd8, 0x8f, 0xf2,
+ 0x67, 0x38, 0x0d, 0x3b, 0xaa, 0x21, 0x73, 0xf8,
+ 0x3b, 0x54, 0x9d, 0x4e, 0x5e, 0xf1, 0xa2, 0x18,
+ 0x5a, 0xf1, 0x6c, 0x32, 0xbf, 0x0a, 0x73, 0x14,
+ 0x48, 0x4f, 0x56, 0xc0, 0x87, 0x6d, 0x3b, 0x16,
+ 0xcc, 0x3f, 0x44, 0x19, 0x85, 0x22, 0x43, 0x5f,
+ 0x8c, 0x29, 0xbd, 0xa0, 0xce, 0x84, 0xd9, 0x4a,
+ 0xcf, 0x00, 0x6b, 0x37, 0x35, 0xe0, 0xb3, 0xc9,
+ 0xd1, 0x58, 0xd1, 0x1b, 0xc3, 0x6f, 0xe3, 0x50,
+ 0xdb, 0xa6, 0x5e, 0x03, 0x18, 0xe5, 0xe2, 0xc1,
+ 0x97, 0xd5, 0xf8, 0x42, 0x6f, 0xe6, 0x61, 0x80,
+ 0xc9, 0x7c, 0xc6, 0x83, 0xf0, 0xad, 0x70, 0x13,
+ 0x0e, 0x26, 0x75, 0xc0, 0x12, 0x23, 0x14, 0xef,
+ 0x1f, 0xdf, 0xfd, 0x47, 0x99, 0x9f, 0x22, 0xf3,
+ 0x57, 0x21, 0xdc, 0x38, 0xe4, 0x79, 0x87, 0x5b,
+ 0x67, 0x66, 0xdd, 0x0b, 0xe0, 0xae, 0xb5, 0x97,
+ 0xd8, 0xa6, 0x5d, 0x02, 0xcf, 0x6b, 0x84, 0x19,
+ 0xc1, 0xbb, 0x25, 0xd2, 0x10, 0xb9, 0x63, 0xeb,
+ 0x4b, 0x27, 0x8d, 0x05, 0x31, 0xce, 0x3b, 0x0c,
+ 0x5f, 0xd4, 0x83, 0x47, 0xa4, 0x8b, 0xc4, 0x76,
+ 0x33, 0x74, 0x1a, 0x07, 0xf8, 0x18, 0x82, 0x1c,
+ 0x8e, 0x01, 0x75, 0x78, 0xea, 0xd9, 0x72, 0x61,
+ 0x71, 0xa9, 0x09, 0x44, 0x7b, 0x0f, 0x12, 0xcf,
+ 0x4c, 0x76, 0x7b, 0x69, 0xc8, 0x64, 0x98, 0x60,
+ 0x45, 0xb6, 0xc7, 0x6b, 0xd8, 0x43, 0x99, 0x08,
+ 0xc9, 0xd3, 0x6f, 0x01, 0x4f, 0x57, 0x6f, 0x49,
+ 0x4f, 0x4f, 0x72, 0xa4, 0xa2, 0x45, 0xe1, 0x0e,
+ 0xf2, 0x08, 0x3e, 0x67, 0xc3, 0x83, 0x5b, 0xb1,
+ 0x24, 0xc0, 0xe0, 0x3a, 0xf5, 0x1f, 0xf2, 0x06,
+ 0x4b, 0xa7, 0x6f, 0xd2, 0xb2, 0x81, 0x96, 0x91,
+ 0x42, 0xb1, 0x53, 0x65, 0x3a, 0x12, 0xcd, 0x33,
+ 0xb3, 0x7e, 0x79, 0xc0, 0x46, 0xf6, 0xd8, 0x4a,
+ 0x22, 0x35, 0xb8, 0x3f, 0xe4, 0x08, 0x88, 0x49,
+ 0x3c, 0x73, 0x9a, 0x44, 0xe3, 0x3b, 0xcc, 0xc4,
+ 0xae, 0x7c, 0xbe, 0xfd, 0xa6, 0x4a, 0xd4, 0x26,
+ 0x52, 0x58, 0x81, 0x30, 0x66, 0x44, 0x54, 0xc8,
+ 0xe4, 0x7c, 0x5b, 0x63, 0x06, 0x60, 0x94, 0x62,
+ 0xe5, 0x47, 0x45, 0xfb, 0x58, 0xf5, 0x6a, 0x7c,
+ 0xb2, 0x35, 0x08, 0x03, 0x15, 0x68, 0xb3, 0x13,
+ 0xa5, 0xbd, 0xf2, 0x1e, 0x2e, 0x1c, 0x8f, 0xc6,
+ 0xc7, 0xd1, 0xa9, 0x64, 0x37, 0x2b, 0x23, 0xfa,
+ 0x7e, 0x56, 0x22, 0xf0, 0x8a, 0xbd, 0xeb, 0x04
+},
+
+r256a128_cek[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+}
+;
+
+static int
+test_jwe_ra_ptext_1024(struct lws_context *context, char *jwk_txt, int jwk_len)
+{
+ struct lws_jose jose, dec_jose;
+ char temp[4096], compact[4096];
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jose_init(&dec_jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* reuse the rsa private key from the JWE Appendix 2 test above */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, jwk_txt, jwk_len) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* dup the plaintext, it will be replaced in-situ by the ciphertext */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_CTXT,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ ra_ptext_1024, sizeof(ra_ptext_1024), 0)) {
+ lwsl_notice("%s: Not enough temp space for ptext\n", __func__);
+ goto bail;
+ }
+
+ /* dup the cek, since it will be replaced by the encrypted key */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_EKEY,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ r256a128_cek, sizeof(r256a128_cek),
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
+ lwsl_notice("%s: Not enough temp space for EKEY\n", __func__);
+ goto bail;
+ }
+
+ jws.map.buf[LJWE_JOSE] = rsa256a128_jose;
+ jws.map.len[LJWE_JOSE] = strlen(rsa256a128_jose);
+
+ n = lws_jwe_parse_jose(&jose, jws.map.buf[LJWE_JOSE],
+ jws.map.len[LJWE_JOSE],
+ lws_concat_temp(temp, temp_len), &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+
+ goto bail1;
+ }
+
+ n = lws_jwe_encrypt(&jose, &jws, lws_concat_temp(temp, temp_len),
+ &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
+ goto bail1;
+ }
+
+ n = lws_jwe_write_compact(&jose, &jws, compact, sizeof(compact));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_write_compact failed: %d\n", __func__, n);
+ goto bail1;
+ }
+
+ // puts(compact);
+
+ lws_jws_destroy(&jws);
+ lws_jws_init(&jws, &jwk, context);
+ temp_len = sizeof(temp);
+
+ /* now we created the encrypted version, see if we can decrypt it */
+
+ if (lws_jws_compact_decode(compact, n, &jws.map, &jws.map_b64,
+ temp, &temp_len) != 5) {
+ lwsl_err("%s: failed to parse generated compact\n", __func__);
+
+ goto bail1;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&dec_jose, &jws);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail1;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ra_ptext_1024,
+ sizeof(ra_ptext_1024))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail1;
+ }
+
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&dec_jose);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+static const char *rsa256a192_jose =
+ "{ \"alg\":\"RSA1_5\",\"enc\":\"A192CBC-HS384\"}";
+
+static const uint8_t r256a192_cek[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+}
+;
+
+static int
+test_jwe_r256a192_ptext(struct lws_context *context, char *jwk_txt, int jwk_len)
+{
+ struct lws_jose jose, dec_jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[4096], compact[4096];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jose_init(&dec_jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* reuse the rsa private key from the JWE Appendix 2 test above */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, jwk_txt, jwk_len) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /*
+ * dup the plaintext into the ciphertext element, it will be
+ * encrypted in-place to a ciphertext of the same length
+ */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_CTXT,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ ra_ptext_1024, sizeof(ra_ptext_1024), 0)) {
+ lwsl_notice("%s: Not enough temp space for ptext\n", __func__);
+ goto bail;
+ }
+
+ /* copy the cek, since it will be replaced by the encrypted key */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_EKEY,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ r256a192_cek, sizeof(r256a192_cek),
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
+ lwsl_err("Problem getting random\n");
+ goto bail1;
+ }
+
+ jws.map.buf[LJWE_JOSE] = rsa256a192_jose;
+ jws.map.len[LJWE_JOSE] = strlen(rsa256a192_jose);
+
+ n = lws_jwe_parse_jose(&jose, jws.map.buf[LJWE_JOSE],
+ jws.map.len[LJWE_JOSE],
+ lws_concat_temp(temp, temp_len), &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+
+ goto bail1;
+ }
+
+ n = lws_jwe_encrypt(&jose, &jws, lws_concat_temp(temp, temp_len),
+ &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
+ goto bail1;
+ }
+
+ n = lws_jwe_write_compact(&jose, &jws, compact, sizeof(compact));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_write_compact failed: %d\n", __func__, n);
+ goto bail1;
+ }
+
+ // puts(compact);
+
+ /* now we created the encrypted version, see if we can decrypt it */
+
+ n = lws_jwe_auth_and_decrypt(&dec_jose, &jws);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail1;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ra_ptext_1024,
+ sizeof(ra_ptext_1024))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail1;
+ }
+
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&dec_jose);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+
+static const char *rsa256a256_jose =
+ "{ \"alg\":\"RSA1_5\",\"enc\":\"A256CBC-HS512\"}";
+
+static const uint8_t r256a256_cek[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
+}
+;
+
+static int
+test_jwe_r256a256_ptext(struct lws_context *context, char *jwk_txt, int jwk_len)
+{
+ struct lws_jose jose, dec_jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[4096], compact[4096];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jose_init(&dec_jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* reuse the rsa private key from the JWE Appendix 2 test above */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, jwk_txt, jwk_len) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /*
+ * dup the plaintext into the ciphertext element, it will be
+ * encrypted in-place to a ciphertext of the same length
+ */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_CTXT,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ ra_ptext_1024, sizeof(ra_ptext_1024), 0)) {
+ lwsl_notice("%s: Not enough temp space for ptext\n", __func__);
+ goto bail;
+ }
+
+ /* copy the cek, since it will be replaced by the encrypted key */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_EKEY,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ r256a256_cek, sizeof(r256a256_cek),
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
+ lwsl_err("Problem getting random\n");
+ goto bail1;
+ }
+
+ jws.map.buf[LJWE_JOSE] = rsa256a256_jose;
+ jws.map.len[LJWE_JOSE] = strlen(rsa256a256_jose);
+
+ n = lws_jwe_parse_jose(&jose, rsa256a256_jose, strlen(rsa256a256_jose),
+ lws_concat_temp(temp, temp_len), &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+
+ goto bail1;
+ }
+
+ n = lws_jwe_encrypt(&jose, &jws, lws_concat_temp(temp, temp_len),
+ &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
+ goto bail1;
+ }
+
+ n = lws_jwe_write_compact(&jose, &jws, compact, sizeof(compact));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_write_compact failed: %d\n", __func__, n);
+ goto bail1;
+ }
+
+ // puts(compact);
+
+ /* now we created the encrypted version, see if we can decrypt it */
+
+ n = lws_jwe_auth_and_decrypt(&dec_jose, &jws);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail1;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ra_ptext_1024,
+ sizeof(ra_ptext_1024))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail1;
+ }
+
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&dec_jose);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+/* produced by running the minimal example `lws-crypto-jwk -t RSA -b 2048 -c` */
+
+static const char *rsa_key_2048 =
+ "{"
+ "\"e\":\"AQAB\","
+ "\"kty\":\"RSA\","
+ "\"n\":\"lBJdvUq-9_8hlcduIWuBjRb0tGzzAvS4foqoNCO7g-rOXMdeAcmq"
+ "aSzWTbkaGIc3L1I4-Q3TOZtxn2UhuDlShZRIhM6JCQuUVNVAF3TD7oXxHtZ"
+ "LJ7y_BqCUlrAmW31lu-nVmhY2G3xW26yXWUsDbCxz0hfLbVnXRSvVKLzYWm"
+ "_yyrFyEWfxB8peDocvKGh879z_aPCKE3PDOEl2AsgzYfpnWCLytkgnrTeL6"
+ "qY8HXxvvV-Jw-XMaRiwH0VldpIjs4DaoN35Kj1Ex7QOZznTkbYtMIqse8bR"
+ "LoR8Irkxbc5ncUAuX1KSV6lpPtelsA3RtEjJ4NHV-5eEABiYh8_CFQ\","
+ "\"d\":\"DDpguQ9RVQFMoJC5z2hlkvq91kvsXPv2Y9Dcki256xYlg55H7Pre"
+ "p__hahrABR2Jg6QVJhArt5ABjUnDQ_JL69HH6VvLD6RVVBTQ-FRBZ_3HYKY"
+ "Oynx5BA7tJm1BRatF5FkBCvq27i8nAc4vfjAb22o9CFvEW3FLaKAgOCncQ3"
+ "Tnbz9CddH89n7DXw4kBFI8q5ugF_aRIg5-i42W_hQinLaBhZ_zhAuE-nvlt"
+ "ZnhDal8cX3T60lNoUrDOlirqEOXKO3gXCHpm3csZ6nabHYD1UCyHOmi2RsR"
+ "pzjaiqjXdPbwPzQoh2DcYpavNrf1mtHiqTwLZDTJIRHWHufJzHf-sw\","
+ "\"p\":\"ySeC3FtvzduDEL-FX4JqbRN06PdBhUmosCkymmbBjriuLNpkGkG-"
+ "1ex7r-M8neUBZbctmDdih6cpLZ8hjZv3eEDZ4b5Z2LqZnja4QvVoWLUs4Fb"
+ "NN_PxJCR5H28uUfT6ThxqT0Nb2enb8Dyp0Qxvd7eJUeYz6jOt7pEK-ErTB4"
+ "M\","
+ "\"q\":\"vHG2Pd6QUH7vFZjJtXwmlVnrz5tdJvUPQvz7ggeM69cqhf4vLajz"
+ "sqP9GhJr7bEkp6vKVdZGmfEdiFRD8cssIZq651oAO5Wr7zZd2mR_hG9jZx7"
+ "8Davfuxr4SZNN-bmoxO6dbDi-X2c7fvMI2YeJwL4groNKyiosdUYILTrYRI"
+ "c\","
+ "\"dp\":\"h5Gqf2rcokgEQGBjuigCJDtNuskRjoxDNV6-rRL99nt_X9lcR9n"
+ "xjOnRvowOyXeTBoN7JjCFpllBxm6ORYtNMO28KomIsimo6NmGPBJ7XfXVJe"
+ "k6bDBrX-l4_HeJJ1FM9SHvgDYsjGQxh-rKpIqWAYBf-yOD758e5T85vndnX"
+ "JM\","
+ "\"dq\":\"K9LiB-dfdmjenw4mMp-JtYfw8Bn4gtvQzcpZjzbETgB-8iRXwm2"
+ "dJvk-HjcUhHWCyb-I0YeAacKKFK9MEconHDWIq87haPn4vyvMjcJ7aUgiPN"
+ "QW1_MVl8TA4xNvudi0Z__5-jYEB9nRG0fX0gbUQU-19_-uf-9o4WkE88fQj"
+ "bc\","
+ "\"qi\":\"LEkTRqmomn9UiASeRfAKw-Z5q7cye9CSL4luSexFvA3Du7Oin-s"
+ "L9a7F3nJN4CuYzhtNMxQ0hM7k6ExzhDhXDlNRHxnNEDt81-CFRV98v7GVWV"
+ "SH1KnaKf9wgegxSSm-x536ki2SI8EN4k4qkqRF0iLVHZK7CgnWMbtt6tnpp"
+ "3k\""
+ "}";
+/* produced by running the minimal example `lws-crypto-jwk -t RSA -b 4096 -c` */
+
+static const char *rsa_key_4096 =
+ "{"
+ "\"e\":\"AQAB\","
+ "\"kty\":\"RSA\","
+ "\"n\":\"uiLBz1SUgd4eQ0okg6tlPdk9QUhTsqXmiJXygWVFgzT45E5_Rfkq"
+ "vZ2fwAqQ8DvxkDTUWiKpeXMpPRNWG5GxuBuq9n7xdA1vn1eQi8LoekB28dg"
+ "3MwMfozVSKCzyxG1f81xPE5x3EMVhCcx6hshhlMEHkzNNhE07d-oRO87ZC0"
+ "z_5L3Vh03uJBXaDKVlsgHAazoHLhn6G4odqv-ro54T6Nx1eEtyTnMmFY5ND"
+ "V4rN0SjQvSefbZZtsrtby8Z0JmeyvynmDwOINj7FpmPmpFLoWGXntc2yxPP"
+ "8SHnqfT9ESh94fxCMxRhDNohgpegRHyiYwj3M5ZYY6reCZYfOQONSWmc8yp"
+ "NBMJqj4LuJ2bTMGAFS17ZP4ZZWm5RP9ax100Dgk0yxP1UrybG5dCfJRQvHC"
+ "ncxG_aL6cSQu2o4fXqlJsNHxk3FjHtV_CMZ3tqvGTvwrs4yxvKwKv6r3fRh"
+ "KL01bGOePzp9THkHW2-lzVj6kUwnxBdHGZE6fcAnczOdp8ZIEdV1w6ThimC"
+ "m3Bw_TIyl3tkuxRWXpc_d6Q4iiSVKGKCvUvfAlESpTA4tIhQkij-T9FEoj2"
+ "WE2H1D35AKmjcfLCh6yszu8cmDNedn862pwnawE2RvRFAyuI113fLQeCbCz"
+ "tQ1JHuD8cnQt0hpGzReTa5UJ8OEOGIlyXNdWZyTpk\","
+ "\"d\":\"G2ZW582AT-6xvz-IiP5fuJ9EMloygeuEeEo0aMJO3X3cfoUknJkN"
+ "ZtyvYa5cgBSe3la8hKkyD9_5K9WvGP9VLTAbdk4g_m-k5QyXiU9PeAGJ0Nd"
+ "-Zqq4y0Zj2eil8u7Tz0fhFxay-zvG6VGZnsIcBTD2C7_jUwyoaqJA17A_CH"
+ "gU-ifMqS56VgMGdlKZmf7Cg7ZGzM1DoS6vZ9bbfgoczaw4OZVHlg9Cxa0NI"
+ "CDi1S-sJcTLGN_RLISKN5H0J54ZfzF6fUEn5kNykLTZrAvj2XV7g4UUOogn"
+ "1cvjJYRcBVzTzQKcfxbqo2DvymDGFZbQM6pj80rYJ5HFPh2EapjggPN8hXp"
+ "NlTNDEvC84QFv0lo2E-0nVWQqcyHtXd431O1JH2h5X822zKjXxkaztQSCj9"
+ "YP7AdAeoxIaWOa3aO1vcwURH2WWaNV-_KXVkPJNzfo9-bGYwblMw_RIqIkN"
+ "BDayTb8rBuQHTCE_tSEHgoSnkityGpr8j_vgA-Fa-SqmdqUlbklVpwA_Mq_"
+ "UH7RCaqe91dWxRhS_7c85tFMRFCKOcaRXkwxEpP2LD1AYe8yvVQlr0Se8_d"
+ "RefuQcC-BECwMW-TCgR3VxAuL7ExNTYe4bhBD8WYXsHP7wDXWX2Q4v7IRzj"
+ "cfVIdpTNYuWEd69PvXBCuy75hmDniSmS3Xps3ItGU\","
+ "\"p\":\"961BtLSIZkHO7Vu1KfaA3urcwGpISKJiTSB5Nh6npxJr9mSjzv_f"
+ "e8VoxCX6CWGY0SEeQNUQ6ceTnAAxkSHtZJQGed598jBtxIexAWEE7oc9s9d"
+ "b0cWu4QWIVZYXrcOTEWmK1kWN4PXmnnQknrWQF49adn81BaOXqoL-tahe7f"
+ "faXzXe0RXuohK543ZKbuuHQ2TxqFG7CZpXiH_qn1Syao32u0V3iDFpmmCUV"
+ "h9O2JCzfo8sAosTrnQwC0pXz3Nvr_9Cnk6bMluJoMrwB1Ywg_DPQ1WvpYHO"
+ "URezEOqVC8Y3zrko199TMX2COKGNFgutVpnzxs2_h0PyINUmwrY4zQ\","
+ "\"q\":\"wGQRaxy_gBafbrVJy4f32O0a2FQHzmS--WgHhoteDoF6ZAajLcV0"
+ "GEvb-AVmFER1Wii62BFaFJOYQIegELvnBFFzD6oHJRX7bM4m36G8J_TC1o9"
+ "T1IFnxOpaoFDf4JWf2k7DCXClGg_zueyOD8fj8F6j2nqpOfytuLmikHcWMc"
+ "dGTHTCRtQmvOk3pm0uk2qR0cQb5L3Ocv45tCKr55tMc6Zx3DKkMt1kmUwd2"
+ "HFfk_0WM6R7q4LNGIjwl8dwiERppLKA8xao9i3jOOdFEfAD-Zqv8H-32cyH"
+ "Mg6Guo4tPNAYSzcsz8nbEYPtKVVm-PDuM2cx0iaKnS8BIK2XTbzc_Q\","
+ "\"dp\":\"ZXLWIwp_hEMYWyjhP9r0VlqlKTtfeEDrOuQ-Qei0iz6EclwurK8"
+ "p_yyRCSb1D7qmOaLzHWMollllINUDeIsJDdWEAY8cz4L-sy1RV1tCBeHnaC"
+ "6iMX5jb1Aw072y3T3qk4tDjxjWUHroh6bTCR8dckkJqNfaBAFKMlGNuyLIH"
+ "3kSPUV3ivUM1d4NvhnJyz02HmjOgz9W-Uv65rJei_zJR9P2aCbAG00CEHXW"
+ "zJ_uT86VdxV11WTaHu8Abt94sER8Tv6jbuyLrUjJSs9VGew32xNcEhya4ZQ"
+ "VyimG8zri6fu7CDXXgPS8wtzB5ihl_c2ypnJQ4_GKrgEqwEAOrFqvUQ\","
+ "\"dq\":\"uzlmngcm8R6S3qi7fL7_2fG7uyPjSN5P3uR21l8QFCu6kFbJO8S"
+ "4muBP20hds4F_dlLGqXgRYo7TjpCtmztQsKoWv_ql41hGCfeAawa41WViqm"
+ "xmlxmrgzzRHsw1YhgZrNgTAz_E290EQT3Mbd0HnCZtbDMMNisIYAj_A3lwd"
+ "tbHOaYyXb0dSZ_nkSUVO05tQ2aGAo8Xtl5ih0NqaQR_XNhwW2pI0lsTB__D"
+ "15tU-O5FSdJaq2ip8KNrBzmF8IYrDKTNykKWAKRdSEX_uFoLdD8t0mxn3SM"
+ "luffa8vdjXJfh3GiASmHUt3HcPOooQEAufoWBPVJWeGqCvWtRH8yYfQ\","
+ "\"qi\":\"h-e9es5J49OUF48gSXUI8cynZ8ydv5cThXc1deV3mil_7_7Hg8E"
+ "jV3gAErO4l-irHJplFmHFZvU1ud4zs1gtBt5TA-EeeepYOHMSssWDvDK3WI"
+ "zsM6C3vcNTSkT-ihaSFmPWHCVwJ1R3auWfeI2In3at0jd4t-OK-cCcGZXb7"
+ "90-EnyyDcdFTU9WfwVSOJffRGjoUYX8DexavClv7CBzPhpdUzGoeyarNaG4"
+ "z9MI8Q8txHyHgc_D70lZUum1cj0bZwgEj6yDzOPzSgUmICFJiLDDj93oPaI"
+ "v-5CQ_Ckju7icexc_kuuYTKBOLTj_vfaURnV3KCHul2UljUYOxkfeNQ\""
+ "}";
+
+static const char *rsa_key_4096_no_optional =
+ "{"
+ "\"e\":\"AQAB\","
+ "\"kty\":\"RSA\","
+ "\"n\":\"uiLBz1SUgd4eQ0okg6tlPdk9QUhTsqXmiJXygWVFgzT45E5_Rfkq"
+ "vZ2fwAqQ8DvxkDTUWiKpeXMpPRNWG5GxuBuq9n7xdA1vn1eQi8LoekB28dg"
+ "3MwMfozVSKCzyxG1f81xPE5x3EMVhCcx6hshhlMEHkzNNhE07d-oRO87ZC0"
+ "z_5L3Vh03uJBXaDKVlsgHAazoHLhn6G4odqv-ro54T6Nx1eEtyTnMmFY5ND"
+ "V4rN0SjQvSefbZZtsrtby8Z0JmeyvynmDwOINj7FpmPmpFLoWGXntc2yxPP"
+ "8SHnqfT9ESh94fxCMxRhDNohgpegRHyiYwj3M5ZYY6reCZYfOQONSWmc8yp"
+ "NBMJqj4LuJ2bTMGAFS17ZP4ZZWm5RP9ax100Dgk0yxP1UrybG5dCfJRQvHC"
+ "ncxG_aL6cSQu2o4fXqlJsNHxk3FjHtV_CMZ3tqvGTvwrs4yxvKwKv6r3fRh"
+ "KL01bGOePzp9THkHW2-lzVj6kUwnxBdHGZE6fcAnczOdp8ZIEdV1w6ThimC"
+ "m3Bw_TIyl3tkuxRWXpc_d6Q4iiSVKGKCvUvfAlESpTA4tIhQkij-T9FEoj2"
+ "WE2H1D35AKmjcfLCh6yszu8cmDNedn862pwnawE2RvRFAyuI113fLQeCbCz"
+ "tQ1JHuD8cnQt0hpGzReTa5UJ8OEOGIlyXNdWZyTpk\","
+ "\"d\":\"G2ZW582AT-6xvz-IiP5fuJ9EMloygeuEeEo0aMJO3X3cfoUknJkN"
+ "ZtyvYa5cgBSe3la8hKkyD9_5K9WvGP9VLTAbdk4g_m-k5QyXiU9PeAGJ0Nd"
+ "-Zqq4y0Zj2eil8u7Tz0fhFxay-zvG6VGZnsIcBTD2C7_jUwyoaqJA17A_CH"
+ "gU-ifMqS56VgMGdlKZmf7Cg7ZGzM1DoS6vZ9bbfgoczaw4OZVHlg9Cxa0NI"
+ "CDi1S-sJcTLGN_RLISKN5H0J54ZfzF6fUEn5kNykLTZrAvj2XV7g4UUOogn"
+ "1cvjJYRcBVzTzQKcfxbqo2DvymDGFZbQM6pj80rYJ5HFPh2EapjggPN8hXp"
+ "NlTNDEvC84QFv0lo2E-0nVWQqcyHtXd431O1JH2h5X822zKjXxkaztQSCj9"
+ "YP7AdAeoxIaWOa3aO1vcwURH2WWaNV-_KXVkPJNzfo9-bGYwblMw_RIqIkN"
+ "BDayTb8rBuQHTCE_tSEHgoSnkityGpr8j_vgA-Fa-SqmdqUlbklVpwA_Mq_"
+ "UH7RCaqe91dWxRhS_7c85tFMRFCKOcaRXkwxEpP2LD1AYe8yvVQlr0Se8_d"
+ "RefuQcC-BECwMW-TCgR3VxAuL7ExNTYe4bhBD8WYXsHP7wDXWX2Q4v7IRzj"
+ "cfVIdpTNYuWEd69PvXBCuy75hmDniSmS3Xps3ItGU\","
+ "\"p\":\"961BtLSIZkHO7Vu1KfaA3urcwGpISKJiTSB5Nh6npxJr9mSjzv_f"
+ "e8VoxCX6CWGY0SEeQNUQ6ceTnAAxkSHtZJQGed598jBtxIexAWEE7oc9s9d"
+ "b0cWu4QWIVZYXrcOTEWmK1kWN4PXmnnQknrWQF49adn81BaOXqoL-tahe7f"
+ "faXzXe0RXuohK543ZKbuuHQ2TxqFG7CZpXiH_qn1Syao32u0V3iDFpmmCUV"
+ "h9O2JCzfo8sAosTrnQwC0pXz3Nvr_9Cnk6bMluJoMrwB1Ywg_DPQ1WvpYHO"
+ "URezEOqVC8Y3zrko199TMX2COKGNFgutVpnzxs2_h0PyINUmwrY4zQ\","
+ "\"q\":\"wGQRaxy_gBafbrVJy4f32O0a2FQHzmS--WgHhoteDoF6ZAajLcV0"
+ "GEvb-AVmFER1Wii62BFaFJOYQIegELvnBFFzD6oHJRX7bM4m36G8J_TC1o9"
+ "T1IFnxOpaoFDf4JWf2k7DCXClGg_zueyOD8fj8F6j2nqpOfytuLmikHcWMc"
+ "dGTHTCRtQmvOk3pm0uk2qR0cQb5L3Ocv45tCKr55tMc6Zx3DKkMt1kmUwd2"
+ "HFfk_0WM6R7q4LNGIjwl8dwiERppLKA8xao9i3jOOdFEfAD-Zqv8H-32cyH"
+ "Mg6Guo4tPNAYSzcsz8nbEYPtKVVm-PDuM2cx0iaKnS8BIK2XTbzc_Q\""
+ "}";
+
+/* This is a compact JWE containing the plaintext ra_ptext_1024 for the key
+ * lws_jwe_ex_a2_jwk_json... produced by test test above running on OpenSSL.
+ */
+
+static char *jwe_compact_rsa_cbc_openssl =
+ "eyAiYWxnIjoiUlNBMV81IiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9"
+ "."
+ "mWXwMv4hxwgKbUAyMFAuHxiKjg62Z5owkFYLgxho5FNT3Hm5ZGiF8plS5W3NwUTmv8t6C"
+ "I0kV5cOOJXE_PXPaOptsie2aoQR-_Bs6gAFixa7aZNsnsMF4lMAiIy7VkrvP2qh0s04y2"
+ "2poOLfmS93tB9AyWdlnQ6Z-U1wzrM9kncqO9GpPol9M4WnAss1ZtTE-9Tbc7dMHURHbZb"
+ "vHn2h625pBD8oD_s0osRav8YEw7jNeQjW_ch4pI6HRox-hf0dyLtk9yFCtBjxbCvysadW"
+ "SlZPJBj0HYv0BVqCK0fETi7URx4MCJ3zgCJnpAuQo2yq1yQzXwOYcFoLIvY0jIm44A"
+ "."
+ "WINMABhU_GQKJarmmTP_-g"
+ "."
+ "V9kHAh9ajE558EPj_zX6p_C903MevMPJLcMU4MWhfhwe1cFW_0io-LvZfcF_Xj7aNoIZd"
+ "vPXJ0On_jHPFsnwe4dus6kuh8RrSKFFV0sGIv-FFXrKB99FFRY_8BTPsYFrcqt_8EV2Af"
+ "p7toaVOO15WXOEH6Ym81a3aOWCVGdj_akMN46Qx_JrQaql-Xs_fL2HdpaEWHHTV2ac9aY"
+ "ah7o0Ojl9UnzkHyXieRgrjXymvCcT0te3D4OQJhrv7TzH_hfKu621O-Frmkr-NvQGSNcl"
+ "fVgRkte2ks34j5HPqEbJQWWKG3IDfkPRvWmDZzEXW_JTrK_1r1FM-aYtY79tLnir8Zw7I"
+ "WCczD-XmtlOJNYA2Ss5dbjoJDtevbqaZWVl-sDSwO1xdf-DUfiemep7S7IFoFAdl0vXLT"
+ "YtuNBxuFw-cP2Kwi8RyF__uENo4vD003cI4htqSYIYXeyAVqWIkmsP1BFpT7MGixfvhAu"
+ "VCj_ToJmowGY3bOHiMuzyT9M7wtCCiCySEBARVU-EdQBXj8X-quSj-0OnBtxXChUS4QXw"
+ "q2pNn3UKSMsxqvHR25HQq_6U2AbvNHxKhup3luzn0T27uy0l3XeWSz_48SwJZKRnbYPtC"
+ "n5Jd5mRdr5GxihpNwupaO4BWnHZo_fHUTI9-Z18lpj_4QB-c3dzDL15xFN4HEZ5lv2iO5"
+ "zMiRI_NlVVDdA9lqGpn4IyO44osHQieBraUjWF8X5cSXDoqktXDVymAdrxe0fYZQca6Bq"
+ "CsBqFTYae4CG01SpG46ysfwAXmsTEKPzj7uiOguFCRB4hClTd-Q8R2axj9JNT1jU_Vb7U"
+ "GKFBGeDJt5PDXJyvW5rHyiQDewykf0Lpvdp39yITT8qARmJl2SwCrDCPADZ4TwwobT42B"
+ "J_Cq5IKgEOeuS3S7NOdOfXxmAcNfN0yujKbmfiOxnXhwnepQ-TnpgTV0nv8snBRITN7mS"
+ "EgflqQlKAZus_0mDbHmBmw1nY-0q4qMWI03IEwMC57-p4JLshnWgIAupnFCGp9nyi4E_s"
+ "GVyQlGCxzC5VSH1Hba3rvbulQGxx_kGk0j56NGhGsQEzqvSuI4xgIsGMPo1Ii7xUh68dd"
+ "BzJRzaov9oDTgnWM5-hoEQQoazW7hDKAFPYccC6zqX0fnI7vBIIBZsjUsol6-5bdujpb4"
+ "l3LRGCjULXlSPbnNGzyk5R-mIwQC8aM9wcIiZZdcdHdr4meMNr3HmpG_B5xtBmENAJAvU"
+ "K3DO6pro2xhypuNKYtOAdH0Xyl8QBPIJ0EFVH6_1V-H_gHs2MLMIqGfUmFCuRev60APcw"
+ "Pbf-GZxLeXLutPq2DOl1HD0XLNtYL1dB1aw2j4L8OJREOC_N-KpIH3g"
+ "."
+ "n4QRlTzW2urRnNiJlwQkZw"
+;
+
+
+static int
+test_jwe_r256a128_jwe_openssl(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[2048];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ 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("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode((const char *)jwe_compact_rsa_cbc_openssl,
+ strlen((char *)jwe_compact_rsa_cbc_openssl),
+ &jws.map, &jws.map_b64,
+ temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ra_ptext_1024,
+ sizeof(ra_ptext_1024))) {
+ lwsl_err("%s: plaintext RSA/AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+
+/* This is a compact JWE containing the plaintext ra_ptext_1024 for the key
+ * lws_jwe_ex_a2_jwk_json... produced by test test above running on mbedTLS.
+ */
+
+static char
+*jwe_compact_rsa_cbc_mbedtls =
+ "eyAiYWxnIjoiUlNBMV81IiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.oBqKJ06UJs2oryPLWZKyI8743GC0geUt_xaKLMaPtApp__swG2w0IhNtmkIBKA9LeeGyiCWKpGGzOlQUR5YSxrT99PnincHXw_pkCprOvi4j3oxThJ2pFRx-CBc9ZgPJ3Kje1QifOueT3vQt_65iiyXmqyc5PDxzuV0L_KtrA_jEsm2m1JVBMOX--qzXjYyqx_dc87d43TXY_4kuTmAtqVpQe7ixKJlUViPVSzuASyeLEUTIaNlALuEWial1wP-ICF37OQzOcZRH3OVZObrcZi1aWkDOLxF4qO4I_GtpuAgZT732a7gnobR-T2oyBpimcqCVEk88Wa7cYyBXZvAOUA.fNLEFh1mjdlyc3WKw0I2Kg.e8X-11K9yXK0KkK-8ikplEWFViruqduaKPDOA7x6lKpBk8l3RFX1aqC4s0WVc1eN0qd-fB__EoO_AIG1xsfw1ie2IDWV0p18ZaRkQRN9Th5UU-W9C9XyPFQUxcl7ShKRE-yKJU-VdZDk6L2-07FH3s-voVKx0oqLIYqkkXp9a2jvnzrZ0Psujs4PSCHOZEgcS8PNdMmdsjDHLsb0NDMifOSlXk2Mp6V2SizXRIPJtOkVJGKwuBc7FbdO02GnzzVXldiLC7GI0zoRsnSJndF8yc3pMrMQhoVRktkBClAcIujD_OxJwHG-i3OJqUg1uVfci86RoQrnULoygvB7apX_WMxF7eXXJdXbG8sPLLCf0SW4sgvuSclOHL2UXzGi6Tp_l1XjxFQTzVEfUaj7i0gD2wM74Ru79RX8yO0m-5qOOwkySU1lEXqbLTuxjJXD9WLcTQQmF0Nm5myTUyNOl7xKpeDpnNt5A0L8o6SW6iJ3DwZEzhMxk3JWQOYtQP1J2sgwAKEDM6SkGzTy9QXpCEoraKp2UEzunux9S6-roYpzgEFT2RZrq3Hg_JyequTtrcNaoiEKd5szJvE6pUc25WEjDzgg79v_n40gQm688mO62kiVBThVmc88u2JVlNpzVQFUfKt-bu2Xxiqn5lRfEMK93EEPZRd8n12vBq5aJKvvEpPN1AC4HaMepf78Ob0GNTYGR-70zSS0ErecCeIgUJ1CttE2Nn0qEOfbQcO48SjeIltecl9DRzeLT3tPN3Z4BqbzSX8kKU5LStUX5YC-obM_0Ss7swXJM19I1O-QH8VbHZl-9TADR6BLzmrsJQ9_BL_uTB6uPdLhYfqWw6VUf0eMLaqvsY92vV5-JVQqyv7s70FNLT1-8P94k79ZGiLvNdDNZgGsmRQOwA2Vk6snHI0oUYGj7NeEK4O64ZfNRZJgPfWnxtQ-LIhSYCJvxFGL7ZMoA_ijKl9_v_bRqd03_7o8YQisw2luDYqLa87Dh9u9tacOoraGAzcEBIAh-BOcnIrQEt5KoSbly5xNAkfqj7QDvL0vPHArZ5E3Gb_k3VbKjsqCzvisNMEjm887Z-Dc6tW4Y2OceYf-rfUDvJ3EXZ66CWSQ7yKhPVcP1RRtNUFEqLoIAkA4aEAAS2ZPKVHIJQwyMzbbNFAuvY_7piNYprAI5lySFcA1cz_hKl6s9xmqbAkH2XGZZduw5Nv-aY_LMXujjhmblqE2Ocej91xTdgMe74Ftr1b3y9FvPPVSqNjpTSfujCi5L57LOpjT78do8eSrDz6coG0zeRUybjWeTszoiYbif_NlyAcMScO5OMZHNkre6L8u-AVeYSKTGsdpK7em_iLN8cGSEjZABNAr_A9Lfg.6Qb_Qf-ktX0DRHWUHAJxDQ"
+;
+
+static int
+test_jwe_r256a128_jwe_mbedtls(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[2048];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ 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("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode((const char *)jwe_compact_rsa_cbc_mbedtls,
+ strlen((char *)jwe_compact_rsa_cbc_mbedtls),
+ &jws.map, &jws.map_b64, temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ra_ptext_1024) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ra_ptext_1024,
+ sizeof(ra_ptext_1024))) {
+ lwsl_err("%s: plaintext RSA/AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ra_ptext_1024, sizeof(ra_ptext_1024));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+
+
+/* A.3. Example JWE Using AES Key Wrap and AES_128_CBC_HMAC_SHA_256
+ *
+ * This example encrypts the plaintext "Live long and prosper." to the
+ * recipient using AES Key Wrap for key encryption and
+ * AES_128_CBC_HMAC_SHA_256 for content encryption.
+ */
+
+/* "Live long and prosper." */
+static uint8_t
+
+ex_a3_ptext[] = {
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",
+*ex_a3_compact = (uint8_t *)
+ "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0"
+ "."
+ "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"
+ "."
+ "AxY8DCtDaGlsbGljb3RoZQ"
+ "."
+ "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY"
+ "."
+ "U0m_YmjN04DJvceFICbCVQ",
-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\""
-"}"
-
-#if 0
-,
-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
-}
-,
-*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
+*ex_a3_key = (uint8_t *)
+ "{\"kty\":\"oct\","
+ "\"k\":\"GawgguFyGrWKav7AX4VKUg\""
+ "}"
;
-/*
- * These are the inputs and outputs from the worked example in RFC7515
- * Appendix A.1.
+static int
+test_jwe_a3(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[2048];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ if (lws_jwk_import(&jwk, NULL, NULL, (char *)ex_a3_key,
+ strlen((char *)ex_a3_key)) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode((const char *)ex_a3_compact,
+ strlen((char *)ex_a3_compact),
+ &jws.map, &jws.map_b64, temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(ex_a3_ptext) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], ex_a3_ptext,
+ sizeof(ex_a3_ptext))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(ex_a3_ptext, sizeof(ex_a3_ptext));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+/* JWA B.2. Test Cases for AES_192_CBC_HMAC_SHA_384
*
- * 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
+ * Unfortunately JWA just gives this test case as hex literals, not
+ * inside a JWE. So we have to prepare the inputs "by hand".
*/
+
+static uint8_t
+
+jwa_b2_ptext[] = {
+ 0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72,
+ 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
+ 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74,
+ 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,
+ 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,
+ 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69,
+ 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,
+ 0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74,
+ 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65,
+ 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,
+ 0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65
+},
+
+jwa_b2_rawkey[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+},
+
+jwa_b2_iv[] = {
+ 0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd,
+ 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04
+},
+
+jwa_b2_e[] = {
+ 0xea, 0x65, 0xda, 0x6b, 0x59, 0xe6, 0x1e, 0xdb,
+ 0x41, 0x9b, 0xe6, 0x2d, 0x19, 0x71, 0x2a, 0xe5,
+ 0xd3, 0x03, 0xee, 0xb5, 0x00, 0x52, 0xd0, 0xdf,
+ 0xd6, 0x69, 0x7f, 0x77, 0x22, 0x4c, 0x8e, 0xdb,
+ 0x00, 0x0d, 0x27, 0x9b, 0xdc, 0x14, 0xc1, 0x07,
+ 0x26, 0x54, 0xbd, 0x30, 0x94, 0x42, 0x30, 0xc6,
+ 0x57, 0xbe, 0xd4, 0xca, 0x0c, 0x9f, 0x4a, 0x84,
+ 0x66, 0xf2, 0x2b, 0x22, 0x6d, 0x17, 0x46, 0x21,
+ 0x4b, 0xf8, 0xcf, 0xc2, 0x40, 0x0a, 0xdd, 0x9f,
+ 0x51, 0x26, 0xe4, 0x79, 0x66, 0x3f, 0xc9, 0x0b,
+ 0x3b, 0xed, 0x78, 0x7a, 0x2f, 0x0f, 0xfc, 0xbf,
+ 0x39, 0x04, 0xbe, 0x2a, 0x64, 0x1d, 0x5c, 0x21,
+ 0x05, 0xbf, 0xe5, 0x91, 0xba, 0xe2, 0x3b, 0x1d,
+ 0x74, 0x49, 0xe5, 0x32, 0xee, 0xf6, 0x0a, 0x9a,
+ 0xc8, 0xbb, 0x6c, 0x6b, 0x01, 0xd3, 0x5d, 0x49,
+ 0x78, 0x7b, 0xcd, 0x57, 0xef, 0x48, 0x49, 0x27,
+ 0xf2, 0x80, 0xad, 0xc9, 0x1a, 0xc0, 0xc4, 0xe7,
+ 0x9c, 0x7b, 0x11, 0xef, 0xc6, 0x00, 0x54, 0xe3
+},
+
+jwa_b2_a[] = { /* "The second principle of Auguste Kerckhoffs" */
+ 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f,
+ 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,
+ 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,
+ 0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66,
+ 0x66, 0x73
+},
+
+jwa_b2_tag[] = {
+ 0x84, 0x90, 0xac, 0x0e, 0x58, 0x94, 0x9b, 0xfe,
+ 0x51, 0x87, 0x5d, 0x73, 0x3f, 0x93, 0xac, 0x20,
+ 0x75, 0x16, 0x80, 0x39, 0xcc, 0xc7, 0x33, 0xd7
+
+}
+;
+
+static int
+test_jwa_b2(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ int n, ret = -1;
+ char buf[2048];
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /*
+ * normally all this is interpreted from the JWE blob. But we don't
+ * have JWE test vectors for AES_256_CBC_HMAC_SHA_512, just a standalone
+ * one. So we have to create it all by hand.
+ *
+ * See test_jwe_a3 above for a more normal usage pattern.
+ */
+
+ memset(&jwk, 0, sizeof(jwk));
+ jwk.kty = LWS_GENCRYPTO_KTY_OCT;
+ jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = (uint8_t *)jwa_b2_rawkey;
+ jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].len = sizeof(jwa_b2_rawkey);
+
+ memcpy(buf, jwa_b2_e, sizeof(jwa_b2_e));
+
+ jws.map.buf[LJWE_IV] = (char *)jwa_b2_iv;
+ jws.map.len[LJWE_IV] = sizeof(jwa_b2_iv);
+
+ jws.map.buf[LJWE_CTXT] = buf;
+ jws.map.len[LJWE_CTXT] = sizeof(jwa_b2_e);
+
+ jws.map.buf[LJWE_ATAG] = (char *)jwa_b2_tag;
+ jws.map.len[LJWE_ATAG] = sizeof(jwa_b2_tag);
+
+ /*
+ * Normally this comes from the JOSE header. But this test vector
+ * doesn't have one... so...
+ */
+
+ if (lws_gencrypto_jwe_alg_to_definition("A128KW", &jose.alg))
+ goto bail;
+ if (lws_gencrypto_jwe_enc_to_definition("A192CBC-HS384", &jose.enc_alg))
+ goto bail;
+
+ n = lws_jwe_auth_and_decrypt_cbc_hs(&jose, &jws, jwa_b2_rawkey,
+ jwa_b2_a, sizeof(jwa_b2_a));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_a_cbc_hs_decrypt failed\n", __func__);
+
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(jwa_b2_ptext) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT],jwa_b2_ptext,
+ sizeof(jwa_b2_ptext))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(jwa_b2_ptext, sizeof(jwa_b2_ptext));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+
+
+/* JWA B.3. Test Cases for AES_256_CBC_HMAC_SHA_512
+ *
+ * Unfortunately JWA just gives this test case as hex literals, not
+ * inside a JWE. So we have to prepare the inputs "by hand".
+ */
+
+static uint8_t
+
+jwa_b3_ptext[] = {
+ 0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72,
+ 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
+ 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74,
+ 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,
+ 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,
+ 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69,
+ 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,
+ 0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74,
+ 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65,
+ 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,
+ 0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65
+},
+
+
+jwa_b3_rawkey[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
+},
+
+jwa_b3_iv[] = {
+ 0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd,
+ 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04
+},
+
+jwa_b3_e[] = {
+ 0x4a, 0xff, 0xaa, 0xad, 0xb7, 0x8c, 0x31, 0xc5,
+ 0xda, 0x4b, 0x1b, 0x59, 0x0d, 0x10, 0xff, 0xbd,
+ 0x3d, 0xd8, 0xd5, 0xd3, 0x02, 0x42, 0x35, 0x26,
+ 0x91, 0x2d, 0xa0, 0x37, 0xec, 0xbc, 0xc7, 0xbd,
+ 0x82, 0x2c, 0x30, 0x1d, 0xd6, 0x7c, 0x37, 0x3b,
+ 0xcc, 0xb5, 0x84, 0xad, 0x3e, 0x92, 0x79, 0xc2,
+ 0xe6, 0xd1, 0x2a, 0x13, 0x74, 0xb7, 0x7f, 0x07,
+ 0x75, 0x53, 0xdf, 0x82, 0x94, 0x10, 0x44, 0x6b,
+ 0x36, 0xeb, 0xd9, 0x70, 0x66, 0x29, 0x6a, 0xe6,
+ 0x42, 0x7e, 0xa7, 0x5c, 0x2e, 0x08, 0x46, 0xa1,
+ 0x1a, 0x09, 0xcc, 0xf5, 0x37, 0x0d, 0xc8, 0x0b,
+ 0xfe, 0xcb, 0xad, 0x28, 0xc7, 0x3f, 0x09, 0xb3,
+ 0xa3, 0xb7, 0x5e, 0x66, 0x2a, 0x25, 0x94, 0x41,
+ 0x0a, 0xe4, 0x96, 0xb2, 0xe2, 0xe6, 0x60, 0x9e,
+ 0x31, 0xe6, 0xe0, 0x2c, 0xc8, 0x37, 0xf0, 0x53,
+ 0xd2, 0x1f, 0x37, 0xff, 0x4f, 0x51, 0x95, 0x0b,
+ 0xbe, 0x26, 0x38, 0xd0, 0x9d, 0xd7, 0xa4, 0x93,
+ 0x09, 0x30, 0x80, 0x6d, 0x07, 0x03, 0xb1, 0xf6,
+},
+
+jwa_b3_a[] = { /* "The second principle of Auguste Kerckhoffs" */
+ 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f,
+ 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,
+ 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,
+ 0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66,
+ 0x66, 0x73
+},
+
+jws_b3_tag[] = {
+ 0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c,
+ 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf,
+ 0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d,
+ 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5
+}
+;
+
+static int
+test_jwa_b3(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char buf[2048];
+ int n, ret = -1;
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /*
+ * normally all this is interpreted from the JWE blob. But we don't
+ * have JWE test vectors for AES_256_CBC_HMAC_SHA_512, just a standalone
+ * one. So we have to create it all by hand.
+ *
+ * See test_jwe_a3 above for a more normal usage pattern.
+ */
+
+ memset(&jwk, 0, sizeof(jwk));
+ jwk.kty = LWS_GENCRYPTO_KTY_OCT;
+ jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = (uint8_t *)jwa_b3_rawkey;
+ jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].len = sizeof(jwa_b3_rawkey);
+
+ memcpy(buf, jwa_b3_e, sizeof(jwa_b3_e));
+
+ jws.map.buf[LJWE_IV] = (char *)jwa_b3_iv;
+ jws.map.len[LJWE_IV] = sizeof(jwa_b3_iv);
+
+ jws.map.buf[LJWE_CTXT] = buf;
+ jws.map.len[LJWE_CTXT] = sizeof(jwa_b3_e);
+
+ jws.map.buf[LJWE_ATAG] = (char *)jws_b3_tag;
+ jws.map.len[LJWE_ATAG] = sizeof(jws_b3_tag);
+
+ /*
+ * Normally this comes from the JOSE header. But this test vector
+ * doesn't feature one...
+ */
+
+ if (lws_gencrypto_jwe_alg_to_definition("A128KW", &jose.alg))
+ goto bail;
+ if (lws_gencrypto_jwe_enc_to_definition("A256CBC-HS512", &jose.enc_alg))
+ goto bail;
+
+ n = lws_jwe_auth_and_decrypt_cbc_hs(&jose, &jws, jwa_b3_rawkey,
+ jwa_b3_a, sizeof(jwa_b3_a));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_a_cbc_hs_decrypt failed\n", __func__);
+
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < sizeof(jwa_b3_ptext) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT],jwa_b3_ptext,
+ sizeof(jwa_b3_ptext))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(jwa_b3_ptext, sizeof(jwa_b3_ptext));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ if (ret)
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ lws_jws_destroy(&jws);
+
+ return ret;
+}
+
+/* JWA C. Example ECDH-ES Key Agreement Computation
+ *
+ * This example uses ECDH-ES Key Agreement and the Concat KDF to derive
+ * the CEK in the manner described in Section 4.6. In this example, the
+ * ECDH-ES Direct Key Agreement mode ("alg" value "ECDH-ES") is used to
+ * produce an agreed-upon key for AES GCM with a 128-bit key ("enc"
+ * value "A128GCM").
+ *
+ * In this example, a producer Alice is encrypting content to a consumer
+ * Bob. The producer (Alice) generates an ephemeral key for the key
+ * agreement computation.
+ *
+ * JWA Appendix C where this comes from ONLY goes as far as to confirm the
+ * direct derived key, it doesn't do any AES128-GCM.
+ */
+
+static const char
+
+*ex_jwa_c_jose =
+ "{\"alg\":\"ECDH-ES\","
+ "\"enc\":\"A128GCM\","
+ "\"apu\":\"QWxpY2U\"," /* b64u("Alice") */
+ "\"apv\":\"Qm9i\"," /* b64u("Bob") */
+ "\"epk\":" /* public part of A's ephemeral key */
+ "{\"kty\":\"EC\","
+ "\"crv\":\"P-256\","
+ "\"x\":\"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\","
+ "\"y\":\"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\""
+ "}"
+ "}"
+;
+
+static uint8_t
+ex_jwa_c_z[] = {
+ 158, 86, 217, 29, 129, 113, 53, 211,
+ 114, 131, 66, 131, 191, 132, 38, 156,
+ 251, 49, 110, 163, 218, 128, 106, 72,
+ 246, 218, 167, 121, 140, 254, 144, 196
+},
+ex_jwa_c_derived_key[] = {
+ 86, 170, 141, 234, 248, 35, 109, 32,
+ 92, 34, 40, 205, 113, 167, 16, 26
+};
+
+
+static int
+test_jwa_c(struct lws_context *context)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ char temp[2048], *p;
+ int ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, NULL, context);
+
+ /*
+ * again the JWA Appendix C test vectors are not in the form of a
+ * complete JWE, but just the JWE JOSE header, so we must fake up the
+ * pieces and perform just the (normally internal) key agreement step
+ * for this test.
+ *
+ * See test_jwe_a3 above for a more normal usage pattern.
+ */
+
+ if (lws_jwe_parse_jose(&jose, ex_jwa_c_jose, strlen(ex_jwa_c_jose),
+ temp, &temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+
+ goto bail;
+ }
+
+ /*
+ * The ephemeral key has been parsed into a jwk "jose.jwk_ephemeral"
+ *
+ * In this example, the ECDH-ES Direct Key Agreement mode ("alg" value
+ * "ECDH-ES") is used to produce an agreed-upon key for AES GCM with a
+ * 128-bit key ("enc" value "A128GCM").
+ */
+
+ p = lws_concat_temp(temp, temp_len);
+
+ if (lws_jwa_concat_kdf(&jose, &jws, 1, (uint8_t *)p,
+ ex_jwa_c_z, sizeof(ex_jwa_c_z))) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+
+ goto bail;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (lws_timingsafe_bcmp(lws_concat_temp(temp, temp_len), ex_jwa_c_derived_key,
+ sizeof(ex_jwa_c_derived_key))) {
+ lwsl_err("%s: ECDH-ES direct derived key wrong\n", __func__);
+ lwsl_hexdump_notice(ex_jwa_c_derived_key,
+ sizeof(ex_jwa_c_derived_key));
+ lwsl_hexdump_notice(p, sizeof(ex_jwa_c_derived_key));
+ goto bail;
+ }
+
+ ret = 0;
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest failed +++++++++++++++++++\n", __func__);
+ else
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return ret;
+}
+
+
+/* AES Key Wrap and AES_XXX_CBC_HMAC_SHA_YYY variations
+ *
+ * These were created by, eg
+ *
+ * echo -n "plaintext0123456" | \
+ * ./lws-crypto-jwe -e "A192KW A256CBC-HS512" -k aes192.key
+ */
+
+/* "Live long and prosper." */
+static const char
+ *akw_ptext = "plaintext0123456",
+ *akw_ct_128_128 = "eyJhbGciOiJBMTI4S1ciLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.zbTfhhWePf1UrCRDxJD_-8eAQr2AoWAL51_nNOv0L4nV3P0e4_9ARA.qWehIhy4j4_gh_h5MF9ZEw.GD40YH6NeNOEkhhxC9ryZA.PEuU6V3rhYXeoxENrAzDgw",
+ *akw_ct_128_192 = "eyJhbGciOiJBMTI4S1ciLCAiZW5jIjoiQTE5MkNCQy1IUzM4NCJ9.zpkr45xH_kSJ5eTBv5dGo5PN_A6YdC4JoJSOw3_VTqcOeAYyCkCAXeGWugqIVLzMzBKgtXdabO8.O28MVhkgfketu5sxQK4Ffw.j25N7luxh251kQwpAoYURQ.Pm_NOj0KZzUq2fV9ARpHxT3Iach9feLK",
+ *akw_ct_128_256 = "eyJhbGciOiJBMTI4S1ciLCAiZW5jIjoiQTI1NkNCQy1IUzUxMiJ9.VvFmi121jliyh_UKzsBv7HR3TVY7-yALpcdlasHqdzmfISd8LFU5oc2fEhfn3_TKfCbgRycm5M3103NEMbVSiNULZWvJAPFe.7uLHGFO1g-PgD9YkjPbvoA.AlPwQPWSqGaB_em4qEEyjw.0LgTLld5pSffZnzGG6IRWEwXg7HhClmwP4m_p1yKnHw",
+ *akw_ct_192_128 = "eyJhbGciOiJBMTkyS1ciLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.kxlmi-xn0JN-ZlnSfkVDP-fXvricJ-L63WP2bWddWEiVK4m-os2trw.iarAWaeV873kh5s7HjoZ4Q.nFHEpnnIxvbCiYfFfsLj7Q.karz-h-R93dJgwN_YZyPmw",
+ *akw_ct_192_192 = "eyJhbGciOiJBMTkyS1ciLCAiZW5jIjoiQTE5MkNCQy1IUzM4NCJ9.D869MEk-JERZU_4MgFuL_6Pg24LUEbXlTvGj-t_JUnNFsJ0p8fk5L-iOATqPmx2g7AyVWgcUqU0.RrxzDsy6Bne1pzx99PBGsA.C-ZWmMwd1uswYkvhKX2_jg.bIFY0TmGuohI2APxDZyFUYpa6s1Mx2j1",
+ *akw_ct_192_256 = "eyJhbGciOiJBMTkyS1ciLCAiZW5jIjoiQTI1NkNCQy1IUzUxMiJ9.XNOBw0Dy1paAX2_XGkZYm2Zm455i8InAVMqM3aOrVDpXYBAADuZ_Ke_dlo3Fc8J5b9m_KNCUtVUU8f3KV0sY-yESsqyZTSXk.n3wEIV1-tL50JAp4H19Y1w.ODPd-oxmpCai9CzqaO0P3Q.b9z08hJTySSVSOw-4qp5lrTEcUur46L-RRB-SEcqPpk",
+ *akw_ct_256_128 = "eyJhbGciOiJBMjU2S1ciLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.THaIbHUOHkr7McMeiQqIO_gBcm61F0BKx79JXkzQVVSF7m0u7Z6uhA.RAU8Yx_a9rbWeqr_0YyLZA.zzfdv55bM-qblTxaR5pNzQ.cySMIOTOcEoFkcVn0D6RKQ",
+ *akw_ct_256_192 = "eyJhbGciOiJBMjU2S1ciLCAiZW5jIjoiQTE5MkNCQy1IUzM4NCJ9.gFcfX6fVrpmDJWN5jPqSWEvpOOoNuV4Yn2KO47p1wGsdw5qIw3r5AO5U8zOEtoGNVX68IC8vkpo.9w3tBsve4e-77lI-S9cFog.Vj3L009JDipPJlHY0tS4Iw.WYGgCedW4SmxleDF3P6Hx26BUXxnizxl",
+ *akw_ct_256_256 = "eyJhbGciOiJBMjU2S1ciLCAiZW5jIjoiQTI1NkNCQy1IUzUxMiJ9.ldhqlMf2LJrZ7EDl-oZvaqi0b_KPGy4cMRx2QDpKtTg92tTSWF7ALVHPPCyT4qccIybP4rygajKfdC_Q_UE16KFyUvXhBgaj.S9OCmKpY0zDkArLF5XsrJw.zvJ1X-zuHsrwLXGJJbglPA.WaRKb7Le2ZQ30pGQAV3sfp-YY1563KXxPURHQ8ntdPc",
+ *akw_key_128 = "{\"k\":\"JjVJVh8JsXvKf9qgHHWWBA\",\"kty\":\"oct\"}",
+ *akw_key_192 = "{\"k\":\"BYF6urCMDRMKFXXRxXrDSVtW71AUZghj\",\"kty\":\"oct\"}",
+ *akw_key_256 = "{\"k\":\"cSHyZXGEfnlgKud21cM6tAxRyXnK6xbWRTsyLUegTMk\",\"kty\":\"oct\"}"
+;
+
+static int
+test_akw_decrypt(struct lws_context *context, const char *test_name,
+ const char *ciphertext, const char *key)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[2048];
+ int n, ret = -1, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ if (lws_jwk_import(&jwk, NULL, NULL, key, strlen(key)) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ /* converts a compact serialization to jws b64 + decoded maps */
+ if (lws_jws_compact_decode(ciphertext, strlen(ciphertext),
+ &jws.map, &jws.map_b64,
+ temp, &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n", __func__);
+ goto bail1;
+ }
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ lws_jwk_destroy(&jwk);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail1;
+ }
+
+ /* allowing for trailing padding, confirm the plaintext */
+ if (jws.map.len[LJWE_CTXT] < strlen(akw_ptext) ||
+ lws_timingsafe_bcmp(jws.map.buf[LJWE_CTXT], akw_ptext,
+ strlen(akw_ptext))) {
+ lwsl_err("%s: plaintext AES decrypt wrong\n", __func__);
+ lwsl_hexdump_notice(akw_ptext, strlen(akw_ptext));
+ lwsl_hexdump_notice(jws.map.buf[LJWE_CTXT],
+ jws.map.len[LJWE_CTXT]);
+ goto bail1;
+ }
+
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest %s failed +++++++++++++++++++\n",
+ __func__, test_name);
+ else
+ lwsl_notice("%s: selftest %s OK\n", __func__, test_name);
+
+ return ret;
+}
+
+static int
+test_akw_encrypt(struct lws_context *context, const char *test_name,
+ const char *alg, const char *enc,
+ const char *ciphertext, const char *key,
+ char *compact, int compact_len)
+{
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+ char temp[4096];
+ int ret = -1, n, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ if (lws_jwk_import(&jwk, NULL, NULL, key, strlen(key)) < 0) {
+ lwsl_notice("%s: Failed to decode JWK test key\n", __func__);
+ goto bail;
+ }
+
+ if (lws_gencrypto_jwe_alg_to_definition(alg, &jose.alg)) {
+ lwsl_err("Unknown cipher alg %s\n", alg);
+ goto bail1;
+ }
+ if (lws_gencrypto_jwe_enc_to_definition(enc, &jose.enc_alg)) {
+ lwsl_err("Unknown payload enc alg %s\n", enc);
+ goto bail1;
+ }
+
+ /* we require a JOSE-formatted header to do the encryption */
+
+ if (temp_len < 256)
+ goto bail1;
+ jws.map.buf[LJWS_JOSE] = temp;
+ jws.map.len[LJWS_JOSE] = lws_snprintf(temp, temp_len,
+ "{\"alg\":\"%s\", \"enc\":\"%s\"}", alg, enc);
+ temp_len -= jws.map.len[LJWS_JOSE];
+
+ /*
+ * dup the plaintext into the ciphertext element, it will be
+ * encrypted in-place to a ciphertext of the same length
+ */
+
+ if (lws_jws_dup_element(&jws.map, LJWE_CTXT,
+ lws_concat_temp(temp, temp_len), &temp_len,
+ akw_ptext, strlen(akw_ptext), 0)) {
+ lwsl_notice("%s: Not enough temp space for ptext\n", __func__);
+ goto bail;
+ }
+
+ /* CEK size is determined by hash / hmac size */
+
+ n = lws_gencrypto_bits_to_bytes(jose.enc_alg->keybits_fixed);
+ if (lws_jws_randomize_element(context, &jws.map, LJWE_EKEY,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, n,
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
+ lwsl_err("Problem getting random\n");
+ goto bail1;
+ }
+
+ n = lws_jwe_encrypt(&jose, &jws, lws_concat_temp(temp, temp_len),
+ &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
+ goto bail1;
+ }
+
+ n = lws_jwe_write_compact(&jose, &jws, compact, compact_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_write_compact failed: %d\n",
+ __func__, n);
+ goto bail1;
+ }
+
+ ret = 0;
+bail1:
+ lws_jwk_destroy(&jwk);
+bail:
+ lws_jws_destroy(&jws);
+ lws_jose_destroy(&jose);
+ if (ret)
+ lwsl_err("%s: selftest %s failed +++++++++++++++++++\n",
+ __func__, test_name);
+ else
+ lwsl_notice("%s: selftest %s OK\n", __func__, test_name);
+
+ return ret;
+}
+
+#if 0
+static char *complete =
+ "{"
+ "\"protected\":"
+ "\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\","
+ "\"unprotected\":"
+ "{\"jku\":\"https://server.example.com/keys.jwks\"},"
+ "\"recipients\":["
+ "{\"header\":"
+ "{\"alg\":\"RSA1_5\",\"kid\":\"2011-04-29\"},"
+ "\"encrypted_key\":"
+ "\"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-"
+ "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx"
+ "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3"
+ "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh"
+ "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg"
+ "wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A\"},"
+ "{\"header\":"
+ "{\"alg\":\"A128KW\",\"kid\":\"7\"},"
+ "\"encrypted_key\":"
+ "\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"}],"
+ "\"iv\":"
+ "\"AxY8DCtDaGlsbGljb3RoZQ\","
+ "\"ciphertext\":"
+ "\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\","
+ "\"tag\":"
+ "\"Mz-VPPyU4RlcuYv1IwIvzw\""
+ "}\""
+;
+
+
+#endif
+
int
test_jwe(struct lws_context *context)
{
- //const struct lws_jose_jwe_alg *jose_alg;
- struct lws_genrsa_ctx rsactx;
- struct lws_jwk jwk;
- uint8_t enc_cek[/* sizeof(lws_jwe_ex_a2_jwk_enc_key) */ 256 + 2048];
- char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1;
- int n;
+ char compact[4096];
+ int n = 0;
- /* Test 1: A.2 */
+ n |= test_jwe_a1(context);
- /* Decode the JWK JSON key */
+ n |= test_jwe_a2(context);
- 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;
- }
+ n |= test_jwe_ra_ptext_1024(context, (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json));
+ n |= test_jwe_r256a192_ptext(context, (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json));
+ n |= test_jwe_r256a256_ptext(context, (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json));
+ n |= test_jwe_ra_ptext_1024(context, (char *)rsa_key_2048,
+ strlen((char *)rsa_key_2048));
+ n |= test_jwe_r256a192_ptext(context, (char *)rsa_key_2048,
+ strlen((char *)rsa_key_2048));
+ n |= test_jwe_r256a256_ptext(context, (char *)rsa_key_2048,
+ strlen((char *)rsa_key_2048));
+ n |= test_jwe_ra_ptext_1024(context, (char *)rsa_key_4096,
+ strlen((char *)rsa_key_4096));
+ n |= test_jwe_r256a192_ptext(context, (char *)rsa_key_4096,
+ strlen((char *)rsa_key_4096));
+ n |= test_jwe_r256a256_ptext(context, (char *)rsa_key_4096,
+ strlen((char *)rsa_key_4096));
+ n |= test_jwe_ra_ptext_1024(context, (char *)rsa_key_4096_no_optional,
+ strlen((char *)rsa_key_4096_no_optional));
+ n |= test_jwe_r256a192_ptext(context, (char *)rsa_key_4096_no_optional,
+ strlen((char *)rsa_key_4096_no_optional));
+ n |= test_jwe_r256a256_ptext(context, (char *)rsa_key_4096_no_optional,
+ strlen((char *)rsa_key_4096_no_optional));
- if (jwk.kty != LWS_GENCRYPTO_KYT_RSA) {
- lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
+ /* AESKW decrypt all variations */
- return -1;
- }
+ n |= test_akw_decrypt(context, "d-a128kw_128", akw_ct_128_128, akw_key_128);
+ n |= test_akw_decrypt(context, "d-a128kw_192", akw_ct_128_192, akw_key_128);
+ n |= test_akw_decrypt(context, "d-a128kw_256", akw_ct_128_256, akw_key_128);
+ n |= test_akw_decrypt(context, "d-a192kw_128", akw_ct_192_128, akw_key_192);
+ n |= test_akw_decrypt(context, "d-a192kw_192", akw_ct_192_192, akw_key_192);
+ n |= test_akw_decrypt(context, "d-a192kw_256", akw_ct_192_256, akw_key_192);
+ n |= test_akw_decrypt(context, "d-a256kw_128", akw_ct_256_128, akw_key_256);
+ n |= test_akw_decrypt(context, "d-a256kw_192", akw_ct_256_192, akw_key_256);
+ n |= test_akw_decrypt(context, "d-a256kw_256", akw_ct_256_256, akw_key_256);
- /* A.2.1: encode JOSE header and confirm matches official string */
+ /* AESKW encrypt then confirm decrypt */
- 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;
+ if (!test_akw_encrypt(context, "ed-128kw_128", "A128KW", "A128CBC-HS256",
+ akw_ptext, akw_key_128, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-128kw_128", compact, akw_key_128);
+ else
+ n = -1;
+ if (!test_akw_encrypt(context, "ed-128kw_192", "A128KW", "A192CBC-HS384",
+ akw_ptext, akw_key_128, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-128kw_192", compact, akw_key_128);
+ else
+ n = -1;
+ if (!test_akw_encrypt(context, "ed-128kw_256", "A128KW", "A256CBC-HS512",
+ akw_ptext, akw_key_128, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-128kw_256", compact, akw_key_128);
+ else
+ n = -1;
- /* 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 (!test_akw_encrypt(context, "ed-192kw_128", "A192KW", "A128CBC-HS256",
+ akw_ptext, akw_key_192, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-192kw_128", compact, akw_key_192);
+ else
+ n = -1;
+ if (!test_akw_encrypt(context, "ed-192kw_192", "A192KW", "A192CBC-HS384",
+ akw_ptext, akw_key_192, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-192kw_192", compact, akw_key_192);
+ else
+ n = -1;
+ if (!test_akw_encrypt(context, "ed-192kw_256", "A192KW", "A256CBC-HS512",
+ akw_ptext, akw_key_192, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-192kw_256", compact, akw_key_192);
+ else
+ n = -1;
- if (lws_genrsa_create(&rsactx, jwk.e, context, LGRSAM_PKCS1_1_5)) {
- lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
- __func__);
- goto bail;
- }
+ if (!test_akw_encrypt(context, "ed-256kw_128", "A256KW", "A128CBC-HS256",
+ akw_ptext, akw_key_256, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-256kw_128", compact, akw_key_256);
+ else
+ n = -1;
+ if (!test_akw_encrypt(context, "ed-256kw_192", "A256KW", "A192CBC-HS384",
+ akw_ptext, akw_key_256, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-256kw_192", compact, akw_key_256);
+ else
+ n = -1;
+ if (!test_akw_encrypt(context, "ed-256kw_256", "A256KW", "A256CBC-HS512",
+ akw_ptext, akw_key_256, compact, sizeof(compact)))
+ n |= test_akw_decrypt(context, "ed-256kw_256", compact, akw_key_256);
+ else
+ n = -1;
- 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[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
- jwk.el.e[LWS_GENCRYPTO_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, '.');
-
- if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &jose_alg)) {
- lwsl_err("%s: RSA1_5 not supported\n", __func__);
- goto bail;
- }
-
- n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
- p1 + 1, sizeof(buf) - (p1 - buf) - 1,
- jose_alg, &jwk, context);
- 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;
+ n |= test_jwe_r256a128_jwe_openssl(context);
+ n |= test_jwe_r256a128_jwe_mbedtls(context);
+ n |= test_jwe_a3(context);
+ n |= test_jwa_b2(context);
+ n |= test_jwa_b3(context);
+ n |= test_jwa_c(context);
+// n |= test_jwe_json_complete(context);
+ return n;
}
diff --git a/minimal-examples/api-tests/api-test-jose/jws.c b/minimal-examples/api-tests/api-test-jose/jws.c
index d66978e8d..b904f4e13 100644
--- a/minimal-examples/api-tests/api-test-jose/jws.c
+++ b/minimal-examples/api-tests/api-test-jose/jws.c
@@ -22,6 +22,83 @@
*
*/
+/* for none, the compact serialization format is b64u(jose hdr).b64u(payload) */
+
+static const char *none_cser =
+ "eyJhbGciOiJub25lIn0"
+ "."
+ "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
+ "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
+ *none_jose = "{\"alg\":\"none\"}",
+ *none_payload = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
+ " \"http://example.com/is_root\":true}";
+
+int
+test_jws_none(struct lws_context *context)
+{
+ struct lws_jws_compact_map map;
+ struct lws_jose jose;
+ char temp[2048];
+ int n, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+
+ /* A.5 Unsecured JSON "none" RFC7515 worked example */
+
+ /* decode the b64.b64[.b64] compact serialization blocks */
+ n = lws_jws_compact_decode(none_cser, strlen(none_cser), &map, NULL,
+ temp, &temp_len);
+ if (n != 2) {
+ lwsl_err("%s: concat_map failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm the decoded JOSE header is exactly what we expect */
+ if (strncmp(none_jose, map.buf[LJWS_JOSE], map.len[LJWS_JOSE])) {
+ lwsl_err("%s: jose b64 decode wrong\n", __func__);
+ goto bail;
+ }
+
+ /* parse the JOSE header */
+ if (lws_jws_parse_jose(&jose, map.buf[LJWS_JOSE],
+ map.len[LJWS_JOSE],
+ (char *)lws_concat_temp(temp, temp_len),
+ &temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm we used the "none" alg as expected from JOSE hdr */
+ if (strcmp(jose.alg->alg, "none")) {
+ lwsl_err("%s: JOSE header has wrong alg\n", __func__);
+ goto bail;
+ }
+
+ /* confirm the payload is literally what we expect */
+ if (strncmp(none_payload, map.buf[LJWS_PYLD],
+ map.len[LJWS_PYLD])) {
+ lwsl_err("%s: payload b64 decode wrong\n", __func__);
+ goto bail;
+ }
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ lws_jose_destroy(&jose);
+
+ return 0;
+
+bail:
+ lws_jose_destroy(&jose);
+
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+}
+
+
+
static const char
*test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
*test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
@@ -32,9 +109,127 @@ static const char
*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 =
+ *hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
+;
+
+int
+test_jws_HS256(struct lws_context *context)
+{
+ char buf[2048], temp[256], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr;
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_jws_compact_map map;
+ int temp_len = sizeof(temp);
+ struct lws_genhmac_ctx ctx;
+ struct lws_jose jose;
+ struct lws_jwk jwk;
+ struct lws_jws jws;
+ int n;
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* Test 1: SHA256 on RFC7515 worked example */
+
+ /* parse the JOSE header */
+
+ if (lws_jws_parse_jose(&jose, test1, strlen(test1), temp, &temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm we used the "none" alg as expected from JOSE hdr */
+ if (strcmp(jose.alg->alg, "HS256")) {
+ lwsl_err("%s: JOSE header has wrong alg\n", __func__);
+ goto bail;
+ }
+
+ /* 1.1: import 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_GENCRYPTO_KTY_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, jose.alg->hmac_type,
+ jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
+ jwk.e[LWS_GENCRYPTO_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_sig_confirm_compact_b64(buf, p - buf, &map, &jwk, context,
+ lws_concat_temp(temp, temp_len), &temp_len) < 0) {
+ lwsl_notice("%s: confirm sig failed\n", __func__);
+ goto bail;
+ }
+
+ lws_jws_destroy(&jws);
+ lws_jwk_destroy(&jwk);
+ lws_jose_destroy(&jose);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+
+bail_destroy_hmac:
+ lws_genhmac_destroy(&ctx, NULL);
+
+bail:
+ lws_jws_destroy(&jws);
+ lws_jwk_destroy(&jwk);
+ lws_jose_destroy(&jose);
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+}
+
+
+static const char
+ /* the key from worked example in RFC7515 A-2, as a JWK */
+ *rfc7515_rsa_key =
"{\"kty\":\"RSA\","
" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
@@ -65,37 +260,137 @@ static const char
"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"
+ *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"
+;
-#if 0
- ,
- *rfc7515_ec_a3_jose = "{\"alg\":\"ES256\"}",
- /* payload is the same as test2 above */
- *rfc7515_ec_a3_b64_serialization =
- "eyJhbGciOiJFUzI1NiJ9"
- "."
- "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
- "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
- "."
- "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA"
- "pmWQxfKTUJqPP3-Kg6NU1Q",
- *rfc7515_ec_a3_jwk =
- "{"
+int
+test_jws_RS256(struct lws_context *context)
+{
+ struct lws_jws_compact_map map;
+ struct lws_jose jose;
+ struct lws_jwk jwk;
+ struct lws_jws jws;
+ char temp[2048], *in;
+ int n, l, temp_len = sizeof(temp);
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* Test 2: RS256 on RFC7515 worked example */
+
+ if (lws_gencrypto_jws_alg_to_definition("RS256", &jose.alg)) {
+ lwsl_err("%s: RS256 not supported\n", __func__);
+ goto bail;
+ }
+
+ /* 2.1: import the 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_GENCRYPTO_KTY_RSA) {
+ lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty);
+ goto bail;
+ }
+
+ /* 2.2: check the signature on the test packet from RFC7515 A-1 */
+
+ if (lws_jws_sig_confirm_compact_b64(rfc7515_rsa_a1,
+ strlen(rfc7515_rsa_a1), &map,
+ &jwk, context, temp, &temp_len) < 0) {
+ lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
+ goto bail;
+ }
+
+ if (lws_jws_b64_compact_map(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
+ &jws.map_b64) != 3) {
+ lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__);
+ goto bail;
+ }
+
+ /* 2.3: generate our own signature for a copy of the test packet */
+
+ in = lws_concat_temp(temp, temp_len);
+ l = strlen(rfc7515_rsa_a1);
+ if (temp_len < l + 1)
+ goto bail;
+ memcpy(in, rfc7515_rsa_a1, l + 1);
+ temp_len -= l + 1;
+
+ if (lws_jws_b64_compact_map(in, l, &jws.map_b64) != 3) {
+ lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__);
+ goto bail;
+ }
+
+ /* overwrite the copy of the known b64 sig (it's all placed inside temp) */
+ n = lws_jws_sign_from_b64(&jose, &jws,
+ (char *)jws.map_b64.buf[LJWS_SIG],
+ jws.map_b64.len[LJWS_SIG] + 8);
+ if (n < 0) {
+ lwsl_err("%s: failed signing test packet\n", __func__);
+ goto bail;
+ }
+ jws.map_b64.len[LJWS_SIG] = n;
+
+ /* 2.4: confirm our signature can be verified */
+
+ in[l] = '\0';
+ if (lws_jws_sig_confirm_compact_b64(in, l, &map, &jwk, context, lws_concat_temp(temp, temp_len), &temp_len) < 0) {
+ lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+
+bail:
+ lws_jwk_destroy(&jwk);
+bail2:
+ lws_jws_destroy(&jws);
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+}
+
+static const char
+ *es256_jose = "{\"alg\":\"ES256\"}",
+ *es256_payload = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
+ " \"http://example.com/is_root\":true}",
+ *es256_cser =
+ "eyJhbGciOiJFUzI1NiJ9"
+ "."
+ "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
+ "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+ "."
+ "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA"
+ "pmWQxfKTUJqPP3-Kg6NU1Q",
+ *es256_jwk =
+ "{"
"\"kty\":\"EC\","
"\"crv\":\"P-256\","
"\"x\":\"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU\","
"\"y\":\"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0\","
"\"d\":\"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI\""
- "}",
+ "}"
+#if 0
+ ,
rfc7515_ec_a3_R[] = {
14, 209, 33, 83, 121, 99, 108, 72, 60, 47, 127, 21, 88,
7, 212, 2, 163, 178, 40, 3, 58, 249, 124, 126, 23, 129,
@@ -109,152 +404,309 @@ static const char
#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_jws_ES256(struct lws_context *context)
+{
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_genhash_ctx hash_ctx;
+ struct lws_jws_compact_map map;
+ struct lws_jose jose;
+ struct lws_jwk jwk;
+ struct lws_jws jws;
+ char temp[2048], *p;
+ int ret = -1, l, n, temp_len = sizeof(temp);
+
+ /* A.3 "ES256" RFC7515 worked example - verify */
+
+ lws_jose_init(&jose);
+
+ /* decode the b64.b64[.b64] compact serialization blocks */
+ if (lws_jws_compact_decode(es256_cser, strlen(es256_cser),
+ &jws.map, &jws.map_b64,
+ temp, &temp_len) != 3) {
+ lwsl_err("%s: concat_map failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm the decoded JOSE header is exactly what we expect */
+ if (jws.map.len[LJWS_JOSE] != strlen(es256_jose) ||
+ strncmp(es256_jose, jws.map.buf[LJWS_JOSE],
+ jws.map.len[LJWS_JOSE])) {
+ lwsl_err("%s: jose b64 decode wrong\n", __func__);
+ goto bail;
+ }
+
+ /* confirm the decoded payload is exactly what we expect */
+ if (jws.map.len[LJWS_PYLD] != strlen(es256_payload) ||
+ strncmp(es256_payload, jws.map.buf[LJWS_PYLD],
+ jws.map.len[LJWS_PYLD])) {
+ lwsl_err("%s: payload b64 decode wrong\n", __func__);
+ goto bail;
+ }
+
+ /* parse the JOSE header */
+ if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
+ jws.map.len[LJWS_JOSE],
+ (char *)lws_concat_temp(temp, temp_len), &temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm we used "ES256" alg we expect from the JOSE hdr */
+ if (strcmp(jose.alg->alg, "ES256")) {
+ lwsl_err("%s: JOSE header has wrong alg\n", __func__);
+ goto bail;
+ }
+
+ jws.jwk = &jwk;
+ jws.context = context;
+
+ /* import the ES256 jwk */
+ if (lws_jwk_import(&jwk, NULL, NULL, es256_jwk, strlen(es256_jwk))) {
+ lwsl_notice("%s: Failed to read JWK key\n", __func__);
+ goto bail;
+ }
+
+ /* sanity */
+ if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
+ lwsl_err("%s: kty: %d instead of EC\n",
+ __func__, jwk.kty);
+ goto bail1;
+ }
+
+ if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) {
+ lwsl_notice("%s: confirm EC sig failed\n", __func__);
+ goto bail1;
+ }
+
+ /* A.3 "ES256" RFC7515 worked example - sign */
+
+ l = strlen(es256_cser);
+ if (temp_len < l)
+ goto bail1;
+ p = lws_concat_temp(temp, temp_len);
+ memcpy(p, es256_cser, l + 1);
+ temp_len -= l + 1;
+
+ /* scan the b64 compact serialization string to map the blocks */
+ if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3)
+ goto bail1;
+
+ /* create the hash of the protected b64 part */
+ if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
+ lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE],
+ jws.map_b64.len[LJWS_JOSE]) ||
+ lws_genhash_update(&hash_ctx, ".", 1) ||
+ lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD],
+ jws.map_b64.len[LJWS_PYLD]) ||
+ lws_genhash_destroy(&hash_ctx, digest)) {
+ lws_genhash_destroy(&hash_ctx, NULL);
+
+ goto bail1;
+ }
+
+ lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
+
+ /* overwrite the copy of the known b64 sig (it's placed inside buf) */
+ n = lws_jws_sign_from_b64(&jose, &jws,
+ (char *)jws.map_b64.buf[LJWS_SIG],
+ jws.map_b64.len[LJWS_SIG] + 8);
+ if (n < 0) {
+ lwsl_err("%s: failed signing test packet\n", __func__);
+ goto bail1;
+ }
+ jws.map_b64.len[LJWS_SIG] = n;
+
+ lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
+
+ /* 2.4: confirm our generated signature can be verified */
+
+ p[l] = '\0';
+ if (lws_jws_sig_confirm_compact_b64(p, l, &map, &jwk, context, lws_concat_temp(temp, temp_len), &temp_len) < 0) {
+ lwsl_notice("%s: confirm our EC sig failed\n", __func__);
+ goto bail1;
+ }
+
+ /* end */
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+ lws_jose_destroy(&jose);
+
+bail:
+ lwsl_notice("%s: selftest %s\n", __func__, ret < 0 ? "FAIL" : "OK");
+
+ return ret;
+}
+
+static const char
+ *es512_jose = "{\"alg\":\"ES512\"}",
+ *es512_payload = "Payload",
+ *es512_cser =
+ "eyJhbGciOiJFUzUxMiJ9"
+ "."
+ "UGF5bG9hZA"
+ "."
+ "AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZq"
+ "wqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8Kp"
+ "EHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn",
+ *es512_jwk =
+ "{"
+ "\"kty\":\"EC\","
+ "\"crv\":\"P-521\","
+ "\"x\":\"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_"
+ "NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk\","
+ "\"y\":\"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl"
+ "y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2\","
+ "\"d\":\"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA"
+ "xerEzgdRhajnu0ferB0d53vM9mE15j2C\""
+ "}"
+;
+
+int
+test_jws_ES512(struct lws_context *context)
+{
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ struct lws_genhash_ctx hash_ctx;
+ struct lws_jws_compact_map map;
+ struct lws_jose jose;
+ struct lws_jwk jwk;
+ struct lws_jws jws;
+ char temp[2048], *p;
+ int ret = -1, l, n, temp_len = sizeof(temp);
+
+ /* A.4 "ES512" RFC7515 worked example - verify */
+
+ lws_jose_init(&jose);
+
+ /* decode the b64.b64[.b64] compact serialization blocks */
+ if (lws_jws_compact_decode(es512_cser, strlen(es512_cser),
+ &jws.map, &jws.map_b64, temp,
+ &temp_len) != 3) {
+ lwsl_err("%s: concat_map failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm the decoded JOSE header is exactly what we expect */
+ if (jws.map.len[LJWS_JOSE] != strlen(es512_jose) ||
+ strncmp(es512_jose, jws.map.buf[LJWS_JOSE],
+ jws.map.len[LJWS_JOSE])) {
+ lwsl_err("%s: jose b64 decode wrong\n", __func__);
+ goto bail;
+ }
+
+ /* confirm the decoded payload is exactly what we expect */
+ if (jws.map.len[LJWS_PYLD] != strlen(es512_payload) ||
+ strncmp(es512_payload, jws.map.buf[LJWS_PYLD],
+ jws.map.len[LJWS_PYLD])) {
+ lwsl_err("%s: payload b64 decode wrong\n", __func__);
+ goto bail;
+ }
+
+ /* parse the JOSE header */
+ if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
+ jws.map.len[LJWS_JOSE],
+ lws_concat_temp(temp, temp_len), &temp_len) < 0) {
+ lwsl_err("%s: JOSE parse failed\n", __func__);
+ goto bail;
+ }
+
+ /* confirm we used "es512" alg we expect from the JOSE hdr */
+ if (strcmp(jose.alg->alg, "ES512")) {
+ lwsl_err("%s: JOSE header has wrong alg\n", __func__);
+ goto bail;
+ }
+
+ jws.jwk = &jwk;
+ jws.context = context;
+
+ /* import the es512 jwk */
+ if (lws_jwk_import(&jwk, NULL, NULL, es512_jwk, strlen(es512_jwk))) {
+ lwsl_notice("%s: Failed to read JWK key\n", __func__);
+ goto bail;
+ }
+
+ /* sanity */
+ if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
+ lwsl_err("%s: kty: %d instead of EC\n",
+ __func__, jwk.kty);
+ goto bail1;
+ }
+
+ if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) {
+ lwsl_notice("%s: confirm EC sig failed\n", __func__);
+ goto bail1;
+ }
+
+ /* A.3 "es512" RFC7515 worked example - sign */
+
+ l = strlen(es512_cser);
+ if (temp_len < l)
+ goto bail1;
+ p = lws_concat_temp(temp, temp_len);
+ memcpy(p, es512_cser, l + 1);
+ temp_len -= (l + 1);
+
+ /* scan the b64 compact serialization string to map the blocks */
+ if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3)
+ goto bail1;
+
+ /* create the hash of the protected b64 part */
+ if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
+ lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE],
+ jws.map_b64.len[LJWS_JOSE]) ||
+ lws_genhash_update(&hash_ctx, ".", 1) ||
+ lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD],
+ jws.map_b64.len[LJWS_PYLD]) ||
+ lws_genhash_destroy(&hash_ctx, digest)) {
+ lws_genhash_destroy(&hash_ctx, NULL);
+
+ goto bail1;
+ }
+
+ /* overwrite the copy of the known b64 sig (it's placed inside buf) */
+ n = lws_jws_sign_from_b64(&jose, &jws,
+ (char *)jws.map_b64.buf[LJWS_SIG], 1024);
+ if (n < 0) {
+ lwsl_err("%s: failed signing test packet\n", __func__);
+ goto bail1;
+ }
+ jws.map_b64.len[LJWS_SIG] = n;
+
+ /* 2.4: confirm our generated signature can be verified */
+
+ p[l] = '\0';
+
+ if (lws_jws_sig_confirm_compact_b64(p, l, &map, &jwk, context,
+ lws_concat_temp(temp, temp_len), &temp_len) < 0) {
+ lwsl_notice("%s: confirm our ECDSA sig failed\n", __func__);
+ goto bail1;
+ }
+
+ /* end */
+ ret = 0;
+
+bail1:
+ lws_jwk_destroy(&jwk);
+ lws_jose_destroy(&jose);
+
+bail:
+ lwsl_notice("%s: selftest %s\n", __func__, ret < 0 ? "FAIL" : "OK");
+
+ return ret;
+}
+
int
test_jws(struct lws_context *context)
{
- char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
- const struct lws_jose_jwe_alg *jose_alg;
- uint8_t digest[LWS_GENHASH_LARGEST];
- struct lws_genhmac_ctx ctx;
- struct lws_jwk jwk;
- int n;
+ int n = 0;
- /* Test 1: SHA256 on RFC7515 worked example */
+ n |= test_jws_none(context);
+ n |= test_jws_HS256(context);
+ n |= test_jws_RS256(context);
+ n |= test_jws_ES256(context);
+ n |= test_jws_ES512(context);
- /* 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_GENCRYPTO_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[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
- jwk.e[LWS_GENCRYPTO_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_GENCRYPTO_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, '.');
-
- if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &jose_alg)) {
- lwsl_err("%s: RSA1_5 not supported\n", __func__);
- goto bail;
- }
-
- n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
- p1 + 1, sizeof(buf) - (p1 - buf) - 1,
- jose_alg, &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;
+ return n;
}
diff --git a/minimal-examples/api-tests/api-test-lwsac/main.c b/minimal-examples/api-tests/api-test-lwsac/main.c
index 854e0adc2..029d4b2aa 100644
--- a/minimal-examples/api-tests/api-test-lwsac/main.c
+++ b/minimal-examples/api-tests/api-test-lwsac/main.c
@@ -39,6 +39,8 @@ int main(int argc, const char **argv)
for (n = 0; n < 1000; n++) {
m = lwsac_use(&lwsac, sizeof(*m), 0);
+ if (!m)
+ return -1;
m->payload = n;
lws_list_ptr_insert(&list_head, &m->list_next, NULL);
diff --git a/minimal-examples/crypto/README.md b/minimal-examples/crypto/README.md
new file mode 100644
index 000000000..1fc34794c
--- /dev/null
+++ b/minimal-examples/crypto/README.md
@@ -0,0 +1,5 @@
+|name|tests|
+---|---
+minimal-crypto-jwe|Examples for lws RFC7516 JWE apis
+minimal-crypto-jwk|Examples for lws RFC7517 JWK apis
+
diff --git a/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt b/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt
new file mode 100644
index 000000000..05f737669
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-crypto-jwe)
+set(SRCS main.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()
+
+set(requirements 1)
+require_lws_config(LWS_WITH_JOSE 1 requirements)
+
+if (requirements)
+
+ 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()
+endif()
diff --git a/minimal-examples/crypto/minimal-crypto-jwe/README.md b/minimal-examples/crypto/minimal-crypto-jwe/README.md
new file mode 100644
index 000000000..a7dcfb75c
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwe/README.md
@@ -0,0 +1,69 @@
+# lws minimal example for JWE
+
+Demonstrates how to encrypt and decrypt using JWE and JWK, providing a
+commandline tool for creating encrypted JWE and decoding them.
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+Stdin is either the plaintext (if encrypting) or JWE (if decrypting).
+
+Stdout is either the JWE (if encrypting) or plaintext (if decrypting).
+
+You must pass a private or public key JWK file in the -k option if encrypting,
+and must pass a private key JWK file in the -k option if decrypting. To be
+clear, for asymmetric keys the public part of the key is required to encrypt,
+and the private part required to decrypt.
+
+For convenience, a pair of public and private keys are provided,
+`key-rsa-4096.private` and `key-rsa-4096.pub`, these were produced with just
+
+```
+ $ lws-crypto-jwk -t RSA -b 4096 --public key-rsa-4096.pub >key-rsa-4096.private
+```
+
+Similar keys for EC modes may be produced with
+
+```
+ $ lws-crypto-jwk -t EC -v P-256 --public key-ecdh-p-256.pub >key-ecdh-p-256.private
+```
+
+and for AES ("octet") symmetric keys
+
+```
+ $ lws-crypto-jwk -t OCT -b 128 >key-aes-128.private
+```
+
+JWEs produced with openssl and mbedtls backends are completely interchangeable.
+
+Commandline option|Meaning
+---|---
+-d |Debug verbosity in decimal, eg, -d15
+-e " "|Encrypt (default is decrypt), eg, -e "RSA1_5 A128CBC-HS256". For decrypt, the cipher information comes from the input JWE.
+-k |JWK file to encrypt or decrypt with
+-c|Format the JWE as a linebroken C string
+
+```
+ $ echo -n "plaintext0123456" | ./lws-crypto-jwe -k key-rsa-4096.private -e "RSA1_5 A128CBC-HS256"
+[2018/12/19 16:20:25:6519] USER: LWS JWE example tool
+[2018/12/19 16:20:25:6749] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
+eyJhbGciOiJSU0ExXzUiLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.ivFr7qzx-pQ4V_edbjpdvR9OwWL9KmojPE2rXQM52oLtW0BtnxZu2_ezqhsAelyIcaworgfobs3u4bslXHMFbeJJjPb5xD0fBDe64OYXZH1NpUGTMJh9Ka4CrJ2B3xhxe7EByGAuGqmluqE0Yezj7rhSw7vlr5JAwuOJ8FaGa8aZ8ldki5G5h_S2Furlbjdcw3Rrxk7mCoMHcLoqzfZtggMPwGAMFogCqcwUo7oSLbBeGaa6hpMbfSysugseWdr8TzObQKPM52k6iVAlGwRaOg_qdLMgZiYRhHA6nFKTQd7XBbNY6qAS8sPuj7Zz344tF3RSfJ0zX_telG71sOtVv5fMpeDU-eCdpOWlCBfu6J6FQfAFu6SJryM4ajGOif09CwFI5qUQ33SOfQfS_M3nqSyd6Vu5M4lsDrb5wK7_XX5gqUwvI9wicf_8WWR-CQomRF-JvEASnA2SIf8QqYfa8R2rP9q6Md4vwO4EZrtxIsMDPsH-4ZEFu7vDjyy09QfIWWsnEb8-UgpVXensgt2m_2bZ76r1VB8-0nZLMwMyEhaH2wra9vX2FWao5UkmNJ7ht300f4_V6QzMFoePpwCvsufWBW6jcQLB-frCWe6uitWaZHEB4LxmNPKzQSz4QwwTKhpF1jNn8Xh1-w1m-2h0gj-oe-S8QBwPveqhPI1p2fI.snuhUTXHNu5mJ6dEPQqg6g.yl36qC4o0GE4nrquQ2YyCg.Vf0MoT7_kUrZdCNWXhq1DQ
+```
+
+Notice the logging is on stderr, and the output alone on stdout.
+
+You can also pipe the output of the encrypt action directly into the decrypt
+action, eg
+
+```
+ $ echo -n "plaintext0123456" | \
+ ./lws-crypto-jwe -k key-rsa-4096.pub -e "RSA1_5 A128CBC-HS256" | \
+ ./lws-crypto-jwe -k key-rsa-4096.private
+```
+
+prints the plaintext on stdout.
diff --git a/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private b/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private
new file mode 100644
index 000000000..10841439a
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.private
@@ -0,0 +1 @@
+{"d":"XcSl3ulvs4OGomu9thRPVQGOstim0PY7CibP_bnCmzjvmGmzb8J4q5AUmJCnZT5TesOzXuXhyG95CxQWsakd9GWHSAinV1QQSLsahaezPULRG1qmo37JqKb9noKkvXguh5XU5np8HjeoeeEkF_XqtCdEo0wHijEjTL9RZar98jmyAmlizoHIY9NnECavs4DZB27onU61B61vGpw-y4xhC9jlZSIwRqIMDzeTcSv8fRKcVYR80ozm2_KwWMpue27rS2EfTQUtsMXuYmnvMAf_DHqA0tNWyD1gpUWYHvlyBh5xnYrWPuXxQBRNesImQdRQl5VMMsuvdtY-uZfIVUdN5CcsB0acronx4UsmVg-Qz-jd1NVW4koZQM9uA4oWiMZg4FEUTQ-UWelHCldg-PYLAazsItmaHPF9LcAPkLkI8jaVS33v-DhSeXHW3Pg3sibtnPhouiSvD84zMtzu1gjFT7vtapMynBeZouqeWYT-BFeu2wzppJcW1YxTQ_Ai80VJSFY__Huw-9r1MOHmDRcEW7x9W97UezWDjrh5Shhh4C6SMYbaf7ouACzFu1i_r8Q06JqKA7aY8i5izKlKA0We9tQKlTF8Fgsneu9gpxFglvZsd1ersiA-MkuP9qTBQpyAf3kJ6HS9GrQUju6r3DExdWDjdvM5Grt8QD7Zkv-qXeE","dp":"M-LFs3T2GI1JxD5LJt2GgV4cMDKbiPKBddLukfG0duUxNp0-6x2LZ0ptxrlHrhxBMMmvCg4GEaujrZdaYWCar6xCnlnkVlOELz4yZ3JBSpS86thJw03xuE7lyeR7usFY4CpSqUQGI_YveITuFeoh4YjwdKDuqPhOpDI-34ptgU93dlBRS9nnQFTiVoUdP4bhGTKOpULTiLgPXHQxQR5rfiGVD9AIwqHvMdBQ0hxQBKEt37PbRWK_eTzMslHZGWNfbg8ipwJxisvHyUn0c1X3Uelw8BRyvNVCNovNDeCj-R7kFkMvriMd_sqGVy1Go46WZ2wMkUJHkvmYk0gDlhnTGQ","dq":"qO89nQEJfdkaDtGGyD-sQE2Mm8p_PIPSpCmgMfpl8zgSOb4P9iqXBgpHyS7w10uY_UHt8KW6pY7ozy0y4Gu_f4Wk_rcXiYYdbuIhlFl0_nLI2mfFPGxr1xC64zfjjEaBr4zIJr_YzhvTpjZFtIdSAH5VG5Tv-2yUtCC2DnKnU2kzEkgUeSI6LHOEVhXqup7C0Kjiv9FJsLR0hiqwH4oLziqH7EVqVDvJI3yL1lhqoLKjAu1ogTDgH7hzSrqVhttnpwL8rDcgbtY6Q8C2csdN3Jt1ucgtGy-Yzgqf_QIULP3CRlqzDTvHrMe2A9cNAQ4dNsCbNAjW_MxxGKKWuWXAMQ","e":"AQAB","kid":"my kid","kty":"RSA","n":"2_YjG_D1sOWJxs6cohikupHf5WJfWSFfSCrnNZ7WR7AyTLnKZAF4VKyimMeJTLYYwCAXMDD5XmkF8VluI4O-hASUIJ7F9eDg3vO7nPwtkWa9lkqt-QyQZ_PjiOGpwetBLzrsaXsC9PvdVzrKXnjeNPsmmbC_Fx2cUn4H_9H_WfXi01VR75XFTBtxTrDY7hmpZHuFCFUOMCW9siTZRk9339Y6ORBznBs4jFbkGI1Pmc3op0o5f8S1gus9L81W5uyUrxfd-CkmJ6eWE8I36cfzI6irN2bhVhR_NXERUtS0QOEeJYlRJXqfYkxTMVlsXPl6zbYt__ZYLC6ZiUTCc6K2KmfGh7fihWbao4dyQW3Mq4kClhpIT0O01Y0r7sR1j4jTnFNqbmtPSl9lEMrfiUHfOLqRJo3qizQ-b6HLCDty1otFz8Q8gg0rD3copQ_zFrcTGwJGAv2Absraj7kp9EJXBqneCJ3dlRO8rzx7KB9Dsj-ygh3kZaubkPCeT1v4l_VUY2iGnK4vzIGKM7j56DQ97ZAi1Bb0y6GYSbrWB2_z0DKJu0fiU-NscbKplR68vgppUM6_iogrk48JEZg_kkTymniqbT3g7J_WeoZSx1Uu8ZHI3ysIFfUtFscOa2SJGlj1ds-lfk6Oqac_I8ahRqQeyVAEisZPmYIGSJajbJopJ4s","p":"_V4CwEjRd8Hv9-ncqGdB_vtReTIuHSWQzSx4al15J3VxvPFI2kxicNeQKyq3OAVT2kmCmUP3ETgCdwuKIgw_QbEc8qNxtS_KpM_KsuTe9a5jrQKpt8ctYhzELZfr_sy9UzUGJzr8glLjJ1IDX4x6_JAqYB_NhttP6bzgu5Dt-DKtRPNO1qZtfhrLIgmltpC2M6-AlAv-dyHSHck2VJIL84Hwk4FulozEYxop0dKuZdfM5Z1dZM8-ICo62O0zUKzoWxKmQcB9_gDZsxYaO6xZ9BLmaW6-WcPSEI6YDnPk8ptnk_Kbyc4kPW4Z3ASczxjaewBmfl2_lwkqkndFVptAeQ","q":"3j9DR6ZpKC3WrshSrxXFYAuT19Rlf6qQ_9uD_Fq7dIpTjCZdl01695Qx7UmujKoetutL3RMCpeRdZR-gCLVh8aMxpMuIc5fHC6HbhsdF-I7GoqO0DEJ6coS3n5Ey4EXL5uoLh4C3l67wBKfLmPW28bxxG2QAP59jncWXkrBQm_qbS5Qon8r7wj0tejG_tGdsPjhsFc9KdnkkBucT6MiEVpzpdwDlsn7bHpMsyPlNyc0fj5qYmRB-DN7rv5varaisBaVT0mLQdwKjBDVqNVnU2m5azPhY-2txvihHaI5_cLIsLLaqKMbB17UxGumuT_o8S03_h8-1syO3Ay87y9pPIw","qi":"JY2uUek6wPrp4fPcInX_5WdNlhyghcGVEvlqxs9iOEUeCtUc6d42n9tgiImMu605dQaigvNaH5y1pwDpLlmxUk0nOUVxqo9mv0Uw8WNXB88FyDb0fPbewLpn4Fskb8Umv6_OymJ1W814DRG-jq3sI5DsB7AjtqJQ22nP2Vs1bIrx5fUxuScwrMsWSrrjAx4Kr8-5eeSDqE-_c7DPZ_zSPYDoHaMeR2pZfNAq3mEbxp8jMukzh77rYZ3ffQEA6AyxFSCSCrxVozhP4ypQ0jAkXVWOlj4nuV6briIqlL3ZboydwsIolRwaPSgH6-bw03XS6Hb9DA0KHJKLun94N9n5kw","use":"enc"}
diff --git a/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub b/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub
new file mode 100644
index 000000000..e2bd85c22
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwe/key-rsa-4096.pub
@@ -0,0 +1 @@
+{"e":"AQAB","kid":"my kid","kty":"RSA","n":"2_YjG_D1sOWJxs6cohikupHf5WJfWSFfSCrnNZ7WR7AyTLnKZAF4VKyimMeJTLYYwCAXMDD5XmkF8VluI4O-hASUIJ7F9eDg3vO7nPwtkWa9lkqt-QyQZ_PjiOGpwetBLzrsaXsC9PvdVzrKXnjeNPsmmbC_Fx2cUn4H_9H_WfXi01VR75XFTBtxTrDY7hmpZHuFCFUOMCW9siTZRk9339Y6ORBznBs4jFbkGI1Pmc3op0o5f8S1gus9L81W5uyUrxfd-CkmJ6eWE8I36cfzI6irN2bhVhR_NXERUtS0QOEeJYlRJXqfYkxTMVlsXPl6zbYt__ZYLC6ZiUTCc6K2KmfGh7fihWbao4dyQW3Mq4kClhpIT0O01Y0r7sR1j4jTnFNqbmtPSl9lEMrfiUHfOLqRJo3qizQ-b6HLCDty1otFz8Q8gg0rD3copQ_zFrcTGwJGAv2Absraj7kp9EJXBqneCJ3dlRO8rzx7KB9Dsj-ygh3kZaubkPCeT1v4l_VUY2iGnK4vzIGKM7j56DQ97ZAi1Bb0y6GYSbrWB2_z0DKJu0fiU-NscbKplR68vgppUM6_iogrk48JEZg_kkTymniqbT3g7J_WeoZSx1Uu8ZHI3ysIFfUtFscOa2SJGlj1ds-lfk6Oqac_I8ahRqQeyVAEisZPmYIGSJajbJopJ4s"}
diff --git a/minimal-examples/crypto/minimal-crypto-jwe/main.c b/minimal-examples/crypto/minimal-crypto-jwe/main.c
new file mode 100644
index 000000000..e3e28551d
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwe/main.c
@@ -0,0 +1,264 @@
+/*
+ * lws-crypto-jwe
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+#include
+#include
+
+/*
+ * handles escapes and line wrapping suitable for use
+ * defining a C char array ( -c option )
+ */
+
+static void
+format_c(const char *key)
+{
+ const char *k = key;
+ int seq = 0;
+
+ while (*k) {
+ if (*k == '{') {
+ putchar('\"');
+ putchar('{');
+ putchar('\"');
+ putchar('\n');
+ putchar('\t');
+ putchar('\"');
+ k++;
+ seq = 0;
+ continue;
+ }
+ if (*k == '}') {
+ putchar('\"');
+ putchar('\n');
+ putchar('\"');
+ putchar('}');
+ putchar('\"');
+ putchar('\n');
+ k++;
+ seq = 0;
+ continue;
+ }
+ if (*k == '\"') {
+ putchar('\\');
+ putchar('\"');
+ seq += 2;
+ k++;
+ continue;
+ }
+ if (*k == ',') {
+ putchar(',');
+ putchar('\"');
+ putchar('\n');
+ putchar('\t');
+ putchar('\"');
+ k++;
+ seq = 0;
+ continue;
+ }
+ putchar(*k);
+ seq++;
+ if (seq >= 60) {
+ putchar('\"');
+ putchar('\n');
+ putchar('\t');
+ putchar(' ');
+ putchar('\"');
+ seq = 1;
+ }
+ k++;
+ }
+}
+
+int main(int argc, const char **argv)
+{
+ int n, enc = 0, result = 0,
+ logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+ char temp[32768], compact[32768], *in;
+ struct lws_context_creation_info info;
+ int temp_len = sizeof(temp);
+ struct lws_context *context;
+ struct lws_jose jose;
+ struct lws_jwk jwk;
+ struct lws_jws jws;
+ const char *p;
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS JWE example tool\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;
+ }
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* if encrypting, set the ciphers */
+
+ if ((p = lws_cmdline_option(argc, argv, "-e"))) {
+ char *sp = strchr(p, ' ');
+
+ if (!sp) {
+ lwsl_err("format: -e \" "
+ "\", eg, "
+ "-e \"RSA1_5 A128CBC-HS256\"\n");
+
+ return 1;
+ }
+ *sp = '\0';
+ if (lws_gencrypto_jwe_alg_to_definition(p, &jose.alg)) {
+ lwsl_err("Unknown cipher alg %s\n", p);
+ return 1;
+ }
+ if (lws_gencrypto_jwe_enc_to_definition(sp + 1, &jose.enc_alg)) {
+ lwsl_err("Unknown payload enc alg %s\n", sp + 1);
+ return 1;
+ }
+
+ /* create JOSE header, also needed for output */
+
+ if (lws_jws_alloc_element(&jws.map, LJWS_JOSE,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, strlen(p) +
+ strlen(sp + 1) + 16, 0)) {
+ lwsl_err("%s: temp space too small\n", __func__);
+ return 1;
+ }
+
+ jws.map.len[LJWS_JOSE] = lws_snprintf(
+ (char *)jws.map.buf[LJWS_JOSE], temp_len,
+ "{\"alg\":\"%s\",\"enc\":\"%s\"}", p, sp + 1);
+
+ enc = 1;
+ }
+
+ in = lws_concat_temp(temp, temp_len);
+ n = read(0, in, temp_len);
+ if (n < 0) {
+ lwsl_err("Problem reading from stdin\n");
+ return 1;
+ }
+ temp_len -= n;
+
+ /* grab the key */
+
+ if ((p = lws_cmdline_option(argc, argv, "-k"))) {
+ if (lws_jwk_load(&jwk, p, NULL, NULL)) {
+ lwsl_err("%s: problem loading JWK %s\n", __func__, p);
+
+ return 1;
+ }
+ } else {
+ lwsl_err("-k is required\n");
+
+ return 1;
+ }
+
+ if (enc) {
+
+ /* point CTXT to the plaintext we read from stdin */
+
+ jws.map.buf[LJWE_CTXT] = in;
+ jws.map.len[LJWE_CTXT] = n;
+
+ /*
+ * Create a random CEK and set EKEY to it
+ * CEK size is determined by hash / hmac size
+ */
+
+ n = lws_gencrypto_bits_to_bytes(jose.enc_alg->keybits_fixed);
+ if (lws_jws_randomize_element(context, &jws.map, LJWE_EKEY,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, n,
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
+ lwsl_err("Problem getting random\n");
+ goto bail1;
+ }
+
+ /* perform the encryption of the CEK and the plaintext */
+
+ n = lws_jwe_encrypt(&jose, &jws,
+ lws_concat_temp(temp, temp_len),
+ &temp_len);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
+ goto bail1;
+ }
+
+ /* output the JWE in compact form */
+
+ n = lws_jwe_write_compact(&jose, &jws, compact, sizeof(compact));
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_write_compact failed: %d\n",
+ __func__, n);
+ goto bail1;
+ }
+
+ if (lws_cmdline_option(argc, argv, "-c"))
+ format_c(compact);
+ else
+ if (write(1, compact, strlen(compact)) < 0) {
+ lwsl_err("Write stdout failed\n");
+ goto bail1;
+ }
+ } else {
+
+ /*
+ * converts a compact serialization to b64 + decoded maps
+ * held in jws
+ */
+ if (lws_jws_compact_decode(in, n, &jws.map, &jws.map_b64,
+ lws_concat_temp(temp, temp_len),
+ &temp_len) != 5) {
+ lwsl_err("%s: lws_jws_compact_decode failed\n",
+ __func__);
+ goto bail1;
+ }
+
+ /*
+ * Do the crypto according to what we parsed into the jose
+ * (information on the ciphers) and the jws (plaintext and
+ * signature info)
+ */
+
+ n = lws_jwe_auth_and_decrypt(&jose, &jws);
+ if (n < 0) {
+ lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
+ __func__);
+ goto bail1;
+ }
+
+ /* if it's valid, dump the plaintext and return 0 */
+
+ if (write(1, jws.map.buf[LJWE_CTXT], jws.map.len[LJWE_CTXT]) < 0) {
+ lwsl_err("Write stdout failed\n");
+ goto bail1;
+ }
+ }
+
+ result = 0;
+
+bail1:
+
+ lws_jws_destroy(&jws);
+ lws_jwk_destroy(&jwk);
+
+ lws_context_destroy(context);
+
+ return result;
+}
diff --git a/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt b/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt
new file mode 100644
index 000000000..fb8c3e372
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-crypto-jwk)
+set(SRCS main.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()
+
+set(requirements 1)
+require_lws_config(LWS_WITH_JOSE 1 requirements)
+
+if (requirements)
+
+ 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()
+endif()
diff --git a/minimal-examples/crypto/minimal-crypto-jwk/README.md b/minimal-examples/crypto/minimal-crypto-jwk/README.md
new file mode 100644
index 000000000..6807746c2
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwk/README.md
@@ -0,0 +1,51 @@
+# lws minimal example for JWK
+
+Demonstrates how to generate and format any kind of supported new random JWK keys.
+
+The full private key is output to stdout, a version of the key with the private
+part removed and some metadata adapted can be saved to a file at the same time
+using `--public `. In the public form, `key_ops` and `use` elements are
+adjusted to remove activities that require a private key.
+
+Key elements are output in strict RFC7638 lexicographic order as required by
+some applications.
+
+Keys produced with openssl and mbedtls backends are completely interchangeable.
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+Commandline option|Meaning
+---|---
+-d |Debug verbosity in decimal, eg, -d15
+-t |RSA, OCT or EC
+-b |For RSA and OCT, key size in bits
+-v |For EC keys, the curve, eg, "P-384"... this implies the key bits
+--kid "ID string"|Key identity string
+--use "use[ use]"|Key use restriction (mutually exclusive with --key-ops): sig, enc
+--key-ops "op[ op]"|Key valid operations (mutually exclusive with --use): sign, verify, encrypt, decrypt, wrapKey, unwrapKey, deriveKey, deriveBits
+-c|Format the jwk as a linebroken C string
+--public |Only output the full, private key, not the public version first
+
+For legibility the example uses -c, however this
+
+```
+ $ ./lws-crypto-jwk -t EC -v P-256 --key-ops "sign verify" --public mykey.pub
+[2018/12/18 20:19:29:6972] USER: LWS JWK example
+[2018/12/18 20:19:29:7200] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
+[2018/12/18 20:19:29:7251] NOTICE: lws_jwk_generate: generating ECDSA key on curve P-256
+{"crv":"P-256","d":"eMKM_S4BTL2aiebZLqvxglufV2YX4b3_32DesgEUOaM","key_ops":["sign","verify"],"kty":"EC","x":"OWauiGGtJ60ZegtqlwETQlmO1exTZdWbT2VbUs4a1hg","y":"g_eNOlqPecbguVQArL6Fd4T5xZthBgipNCBypXubPos"}
+```
+
+The output in `mykey.pub` is:
+
+```
+{"crv":"P-256","key_ops":["verify"],"kty":"EC","x":"OWauiGGtJ60ZegtqlwETQlmO1exTZdWbT2VbUs4a1hg","y":"g_eNOlqPecbguVQArL6Fd4T5xZthBgipNCBypXubPos"}
+```
+
+Notice the logging goes out on stderr, the key data goes on stdout.
diff --git a/minimal-examples/crypto/minimal-crypto-jwk/main.c b/minimal-examples/crypto/minimal-crypto-jwk/main.c
new file mode 100644
index 000000000..330898f4c
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jwk/main.c
@@ -0,0 +1,187 @@
+/*
+ * lws-crypto-jwk
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+#include
+#include
+
+/*
+ * handles escapes and line wrapping suitable for use
+ * defining a C char array ( -c option )
+ */
+
+static int
+format_c(int fd, const char *key)
+{
+ const char *k = key;
+ int seq = 0;
+
+ while (*k) {
+ if (*k == '{') {
+ if (write(fd, "\"{\"\n\t\"", 6) < 6)
+ return -1;
+ k++;
+ seq = 0;
+ continue;
+ }
+ if (*k == '}') {
+ if (write(fd, "\"\n\"}\"\n", 6) < 6)
+ return -1;
+ k++;
+ seq = 0;
+ continue;
+ }
+ if (*k == '\"') {
+ if (write(fd, "\\\"", 2) < 2)
+ return -1;
+ seq += 2;
+ k++;
+ continue;
+ }
+ if (*k == ',') {
+ if (write(fd, ",\"\n\t\"", 5) < 5)
+ return -1;
+ k++;
+ seq = 0;
+ continue;
+ }
+ if (write(fd, k, 1) < 1)
+ return -1;
+ seq++;
+ if (seq >= 60) {
+ if (write(fd, "\"\n\t \"", 5) < 5)
+ return -1;
+ seq = 1;
+ }
+ k++;
+ }
+
+ return 0;
+}
+
+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;
+ int bits = 4096;
+ enum lws_gencrypto_kty kty = LWS_GENCRYPTO_KTY_RSA;
+ struct lws_jwk jwk;
+ char key[32768];
+ const char *curve = "P-256";
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS JWK example\n");
+
+ if ((p = lws_cmdline_option(argc, argv, "-b")))
+ bits = atoi(p);
+
+ if ((p = lws_cmdline_option(argc, argv, "-t"))) {
+ if (!strcmp(p, "RSA"))
+ kty = LWS_GENCRYPTO_KTY_RSA;
+ else
+ if (!strcmp(p, "OCT"))
+ kty = LWS_GENCRYPTO_KTY_OCT;
+ else
+ if (!strcmp(p, "EC"))
+ kty = LWS_GENCRYPTO_KTY_EC;
+ else {
+ lwsl_err("Unknown key type (must be "
+ "OCT, RSA or EC)\n");
+
+ return 1;
+ }
+ }
+
+ 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;
+ }
+
+ if ((p = lws_cmdline_option(argc, argv, "-v")))
+ curve = p;
+
+ if (lws_jwk_generate(context, &jwk, kty, bits, curve)) {
+ lwsl_err("lws_jwk_generate failed\n");
+
+ return 1;
+ }
+
+ if ((p = lws_cmdline_option(argc, argv, "--kid")))
+ lws_jwk_strdup_meta(&jwk, JWK_META_KID, p, strlen(p));
+
+ if ((p = lws_cmdline_option(argc, argv, "--use")))
+ lws_jwk_strdup_meta(&jwk, JWK_META_USE, p, strlen(p));
+
+ if ((p = lws_cmdline_option(argc, argv, "--key-ops")))
+ lws_jwk_strdup_meta(&jwk, JWK_META_KEY_OPS, p, strlen(p));
+
+ if ((p = lws_cmdline_option(argc, argv, "--public")) &&
+ kty != LWS_GENCRYPTO_KTY_OCT) {
+
+ int fd;
+
+ /* public version */
+
+ if (lws_jwk_export(&jwk, 0, key, sizeof(key)) < 0) {
+ lwsl_err("lws_jwk_export failed\n");
+
+ return 1;
+ }
+
+ fd = open(p, LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600);
+ if (fd < 0) {
+ lwsl_err("Can't open public key file %s\n", p);
+ return 1;
+ }
+
+ if (lws_cmdline_option(argc, argv, "-c"))
+ format_c(fd, key);
+ else {
+ if (write(fd, key, strlen(key)) < 0) {
+ lwsl_err("Write public failed\n");
+ return 1;
+ }
+ }
+
+ close(fd);
+ }
+
+ /* private version */
+
+ if (lws_jwk_export(&jwk, 1, key, sizeof(key)) < 0) {
+ lwsl_err("lws_jwk_export failed\n");
+
+ return 1;
+ }
+
+ if (lws_cmdline_option(argc, argv, "-c")) {
+ if (format_c(1, key) < 0)
+ return 1;
+ } else
+ if (write(1, key, strlen(key)) < 0) {
+ lwsl_err("Write stdout failed\n");
+ return 1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ lws_context_destroy(context);
+
+ return result;
+}
diff --git a/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt b/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt
new file mode 100644
index 000000000..ddf957969
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-crypto-jws)
+set(SRCS main.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()
+
+set(requirements 1)
+require_lws_config(LWS_WITH_JOSE 1 requirements)
+
+if (requirements)
+
+ 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()
+endif()
diff --git a/minimal-examples/crypto/minimal-crypto-jws/README.md b/minimal-examples/crypto/minimal-crypto-jws/README.md
new file mode 100644
index 000000000..d2c426d06
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jws/README.md
@@ -0,0 +1,59 @@
+# lws minimal example for JWS
+
+Demonstrates how to sign and verify using compact JWS and JWK, providing a
+commandline tool for signing and verifying stdin.
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+Stdin is either the plaintext (if signing) or compact JWS (if verifying).
+
+Stdout is either the JWE (if encrypting) or plaintext (if decrypting).
+
+You must pass a private or public key JWK file in the -k option if encrypting,
+and must pass a private key JWK file in the -k option if decrypting. To be
+clear, for asymmetric keys the public part of the key is required to encrypt,
+and the private part required to decrypt.
+
+For convenience, a pair of public and private keys are provided,
+`key-rsa-4096.private` and `key-rsa-4096.pub`, these were produced with just
+
+```
+ $ lws-crypto-jwk -t RSA -b 4096 --public key-rsa-4096.pub >key-rsa-4096.private
+```
+
+Similar keys for EC modes may be produced with
+
+```
+ $ lws-crypto-jwk -t EC -v P-256 --public key-ecdh-p-256.pub >key-ecdh-p-256.private
+```
+
+JWSs produced with openssl and mbedtls backends are completely interchangeable.
+
+Commandline option|Meaning
+---|---
+-d |Debug verbosity in decimal, eg, -d15
+-s ""|Sign (default is verify), eg, -e "ES256". For verify, the cipher information comes from the input JWS.
+-k |JWK file to sign or verify with... sign requires the key has its private part
+-c|Format the JWE as a linebroken C string
+
+```
+ $ echo -n "plaintext0123456" | ./lws-crypto-jws -s "ES256" -k ec-p256.private
+[2018/12/19 16:20:25:6519] USER: LWS JWE example tool
+[2018/12/19 16:20:25:6749] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
+eyJhbGciOiJSU0ExXzUiLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.ivFr7qzx-pQ4V_edbjpdvR9OwWL9KmojPE2rXQM52oLtW0BtnxZu2_ezqhsAelyIcaworgfobs3u4bslXHMFbeJJjPb5xD0fBDe64OYXZH1NpUGTMJh9Ka4CrJ2B3xhxe7EByGAuGqmluqE0Yezj7rhSw7vlr5JAwuOJ8FaGa8aZ8ldki5G5h_S2Furlbjdcw3Rrxk7mCoMHcLoqzfZtggMPwGAMFogCqcwUo7oSLbBeGaa6hpMbfSysugseWdr8TzObQKPM52k6iVAlGwRaOg_qdLMgZiYRhHA6nFKTQd7XBbNY6qAS8sPuj7Zz344tF3RSfJ0zX_telG71sOtVv5fMpeDU-eCdpOWlCBfu6J6FQfAFu6SJryM4ajGOif09CwFI5qUQ33SOfQfS_M3nqSyd6Vu5M4lsDrb5wK7_XX5gqUwvI9wicf_8WWR-CQomRF-JvEASnA2SIf8QqYfa8R2rP9q6Md4vwO4EZrtxIsMDPsH-4ZEFu7vDjyy09QfIWWsnEb8-UgpVXensgt2m_2bZ76r1VB8-0nZLMwMyEhaH2wra9vX2FWao5UkmNJ7ht300f4_V6QzMFoePpwCvsufWBW6jcQLB-frCWe6uitWaZHEB4LxmNPKzQSz4QwwTKhpF1jNn8Xh1-w1m-2h0gj-oe-S8QBwPveqhPI1p2fI.snuhUTXHNu5mJ6dEPQqg6g.yl36qC4o0GE4nrquQ2YyCg.Vf0MoT7_kUrZdCNWXhq1DQ
+```
+
+Notice the logging is on stderr, and the output alone on stdout.
+
+When signing, the compact representation of the JWS is output on stdout.
+
+When verifying, if the signature is valid the plaintext is output on stdout
+and the tool exits with a 0 exit code. Otherwise nothing is output on stdout
+and it exits with a nonzero exit code.
+
diff --git a/minimal-examples/crypto/minimal-crypto-jws/main.c b/minimal-examples/crypto/minimal-crypto-jws/main.c
new file mode 100644
index 000000000..8fd47c225
--- /dev/null
+++ b/minimal-examples/crypto/minimal-crypto-jws/main.c
@@ -0,0 +1,185 @@
+/*
+ * lws-crypto-jws
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include
+#include
+#include
+
+
+int main(int argc, const char **argv)
+{
+ int n, sign = 0, result = 0,
+ logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+ char temp[32768], compact[32768], *in;
+ struct lws_context_creation_info info;
+ struct lws_jws_compact_map map;
+ int temp_len = sizeof(temp);
+ struct lws_context *context;
+ struct lws_jose jose;
+ struct lws_jwk jwk;
+ struct lws_jws jws;
+ const char *p;
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS JWS example tool\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;
+ }
+
+ lws_jose_init(&jose);
+ lws_jws_init(&jws, &jwk, context);
+
+ /* if signing, set the ciphers */
+
+ if ((p = lws_cmdline_option(argc, argv, "-s"))) {
+
+ if (lws_gencrypto_jws_alg_to_definition(p, &jose.alg)) {
+ lwsl_err("format: -s \"\", eg, "
+ "-e \"RS256\"\n");
+
+ return 1;
+ }
+
+ /* create JOSE header, also needed for output */
+
+ if (lws_jws_alloc_element(&jws.map, LJWS_JOSE,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, strlen(p) + 10, 0)) {
+ lwsl_err("%s: temp space too small\n", __func__);
+ return 1;
+ }
+
+ jws.map.len[LJWS_JOSE] =
+ lws_snprintf((char *)jws.map.buf[LJWS_JOSE],
+ temp_len, "{\"alg\":\"%s\"}", p);
+ sign = 1;
+ }
+
+ in = lws_concat_temp(temp, temp_len);
+ n = read(0, in, temp_len);
+ if (n < 0) {
+ lwsl_err("Problem reading from stdin\n");
+ return 1;
+ }
+ temp_len -= n;
+
+ /* grab the key */
+
+ if ((p = lws_cmdline_option(argc, argv, "-k"))) {
+ if (lws_jwk_load(&jwk, p, NULL, NULL)) {
+ lwsl_err("%s: problem loading JWK %s\n", __func__, p);
+
+ return 1;
+ }
+ } else {
+ lwsl_err("-k is required\n");
+
+ return 1;
+ }
+
+ if (sign) {
+
+ /* add the plaintext from stdin to the map and a b64 version */
+
+ jws.map.buf[LJWS_PYLD] = in;
+ jws.map.len[LJWS_PYLD] = n;
+
+ if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_PYLD,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, jws.map.buf[LJWS_PYLD],
+ jws.map.len[LJWS_PYLD]))
+ goto bail1;
+
+ /* add the b64 JOSE header to the b64 map */
+
+ if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_JOSE,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, jws.map.buf[LJWS_JOSE],
+ jws.map.len[LJWS_JOSE]))
+ goto bail1;
+
+ /* prepare the space for the b64 signature in the map */
+
+ if (lws_jws_alloc_element(&jws.map, LJWS_SIG,
+ lws_concat_temp(temp, temp_len),
+ &temp_len, lws_base64_size(
+ LWS_JWE_LIMIT_KEY_ELEMENT_BYTES), 0)) {
+ lwsl_err("%s: temp space too small\n", __func__);
+ goto bail1;
+ }
+
+ /* sign the plaintext */
+
+ n = lws_jws_sign_from_b64(&jose, &jws,
+ (char *)jws.map_b64.buf[LJWS_SIG],
+ jws.map_b64.len[LJWS_SIG]);
+ if (n < 0) {
+ lwsl_err("%s: failed signing test packet\n", __func__);
+ goto bail1;
+ }
+ /* set the actual b64 signature size */
+ jws.map_b64.len[LJWS_SIG] = n;
+
+ /* create the compact JWS representation */
+
+ n = lws_jws_write_compact(&jws, compact, sizeof(compact));
+ if (n < 0) {
+ lwsl_notice("%s: write_compact failed\n", __func__);
+ goto bail1;
+ }
+
+ /* dump the compact JWS representation on stdout */
+
+ if (write(1, compact, strlen(compact)) < 0) {
+ lwsl_err("Write stdout failed\n");
+ goto bail1;
+ }
+
+ } else {
+ /* perform the verify directly on the compact representation */
+
+ if (lws_jws_sig_confirm_compact_b64(in,
+ lws_concat_used(temp, temp_len),
+ &map, &jwk, context,
+ lws_concat_temp(temp, temp_len),
+ &temp_len) < 0) {
+ lwsl_notice("%s: confirm rsa sig failed\n", __func__);
+ goto bail1;
+ }
+
+ lwsl_notice("VALID\n");
+
+ /* dump the verifed plaintext and return 0 */
+
+ if (write(1, jws.map.buf[LJWS_PYLD], jws.map.len[LJWS_PYLD]) < 0) {
+ lwsl_err("Write stdout failed\n");
+ goto bail1;
+ }
+ }
+
+ result = 0;
+
+bail1:
+ lws_jws_destroy(&jws);
+ lws_jwk_destroy(&jwk);
+
+ lws_context_destroy(context);
+
+ return result;
+}
diff --git a/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c b/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c
index 7b687ad96..d6f51b4e5 100644
--- a/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c
+++ b/minimal-examples/http-server/minimal-http-server-smp/minimal-http-server-smp.c
@@ -91,9 +91,11 @@ int main(int argc, const char **argv)
info.mounts = &mount;
info.options =
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
- if ((p = lws_cmdline_option(argc, argv, "-t")))
+ if ((p = lws_cmdline_option(argc, argv, "-t"))) {
info.count_threads = atoi(p);
- else
+ if (info.count_threads < 1 || info.count_threads > LWS_MAX_SMP)
+ return 1;
+ } else
info.count_threads = COUNT_THREADS;
if (lws_cmdline_option(argc, argv, "-s")) {
diff --git a/plugins/acme-client/protocol_lws_acme_client.c b/plugins/acme-client/protocol_lws_acme_client.c
index 2e4695870..0e70ee098 100644
--- a/plugins/acme-client/protocol_lws_acme_client.c
+++ b/plugins/acme-client/protocol_lws_acme_client.c
@@ -439,7 +439,7 @@ lws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd,
NULL, NULL))
return 0;
- vhd->jwk.kty = LWS_GENCRYPTO_KYT_RSA;
+ vhd->jwk.kty = LWS_GENCRYPTO_KTY_RSA;
lwsl_notice("Generating ACME %d-bit keypair... "
"will take a little while\n", bits);
n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, LGRSAM_PKCS1_1_5,
@@ -549,17 +549,19 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
*end = buf + sizeof(buf) - 1, digest[32], *failreason = NULL;
const struct lws_protocol_vhost_options *pvo;
struct lws_acme_cert_aging_args *caa;
- const struct lws_jose_jwe_alg *args;
struct acme_connection *ac = NULL;
struct lws_genhash_ctx hctx;
unsigned char **pp, *pend;
const char *content_type;
+ struct lws_jose jose;
struct lws *cwsi;
int n, m;
if (vhd)
ac = vhd->ac;
+ lws_jose_init(&jose);
+
switch ((int)reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
@@ -783,12 +785,12 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
puts(start);
pkt_add_hdrs:
- if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &args)) {
+ if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &jose.alg)) {
ac->len = 0;
lwsl_notice("%s: no RSA1_5\n", __func__);
goto failed;
}
- ac->len = lws_jwe_create_packet(&vhd->jwk, args,
+ ac->len = lws_jwe_create_packet(&jose, &vhd->jwk,
start, p - start,
ac->replay_nonce,
&ac->buf[LWS_PRE],
diff --git a/plugins/deaddrop/protocol_lws_deaddrop.c b/plugins/deaddrop/protocol_lws_deaddrop.c
index bd12cad68..4d05b1bb5 100644
--- a/plugins/deaddrop/protocol_lws_deaddrop.c
+++ b/plugins/deaddrop/protocol_lws_deaddrop.c
@@ -159,7 +159,7 @@ scan_upload_dir(struct vhd_deaddrop *vhd)
p += lws_snprintf(p, (filepath + sizeof(filepath)) - p,
"%s/", subdir[m]);
- p += lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s",
+ lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s",
de->d_name);
/* ignore temp files */
@@ -304,12 +304,14 @@ file_upload_cb(void *data, const char *name, const char *filename,
if (state == LWS_UFS_CONTENT)
break;
- close((int)(long long)pss->fd);
+ if ((int)(long long)pss->fd >= 0)
+ close((int)(long long)pss->fd);
/* the temp filename without the ~ */
lws_strncpy(filename2, pss->filename, sizeof(filename2));
filename2[strlen(filename2) - 1] = '\0';
- rename(pss->filename, filename2);
+ if (rename(pss->filename, filename2) < 0)
+ lwsl_err("%s: unable to rename\n", __func__);
pss->fd = LWS_INVALID_FILE;
pss->response_code = HTTP_STATUS_OK;
diff --git a/plugins/raw-proxy/protocol_lws_raw_proxy.c b/plugins/raw-proxy/protocol_lws_raw_proxy.c
index 8f52e661b..bfa8d6d9e 100644
--- a/plugins/raw-proxy/protocol_lws_raw_proxy.c
+++ b/plugins/raw-proxy/protocol_lws_raw_proxy.c
@@ -249,7 +249,7 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_CLI_ADOPT:
lwsl_debug("LWS_CALLBACK_RAW_CLI_ADOPT: pss %p\n", pss);
- if (conn)
+ if (conn || !pss)
break;
conn = pss->conn = lws_get_opaque_user_data(wsi);
conn->established[ONW] = 1;
@@ -279,6 +279,9 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_CLI_RX:
lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_RX: %d\n", (int)len);
+ if (!conn)
+ return 0;
+
if (!pss || !conn->wsi[ACC] || conn->closed[ACC]) {
lwsl_info(" pss %p, wsi[ACC] %p, closed[ACC] %d\n",
pss, conn->wsi[ACC], conn->closed[ACC]);
@@ -313,6 +316,9 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE:
lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE\n");
+ if (!conn)
+ break;
+
ppkt = lws_ring_get_element(conn->r[ACC], &conn->t[ACC]);
if (!ppkt) {
lwsl_info("%s: CLI_WRITABLE had nothing in acc ring\n",
@@ -374,7 +380,8 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_SRV_ADOPT:
lwsl_debug("LWS_CALLBACK_RAW_SRV_ADOPT\n");
-
+ if (!pss)
+ return -1;
conn = pss->conn = malloc(sizeof(struct conn));
if (!pss->conn)
return -1;
@@ -463,7 +470,7 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE:
lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE\n");
- if (!conn->established[ONW] || conn->closed[ONW])
+ if (!conn || !conn->established[ONW] || conn->closed[ONW])
break;
ppkt = lws_ring_get_element(conn->r[ONW], &conn->t[ONW]);
diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c
index 68cea380d..638fba455 100644
--- a/plugins/ssh-base/sshd.c
+++ b/plugins/ssh-base/sshd.c
@@ -86,18 +86,6 @@ lws_buf(uint8_t **p, void *s, uint32_t len)
return 0;
}
-int
-lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
-{
- const uint8_t *pa = a, *pb = b;
- uint8_t sum = 0;
-
- while (len--)
- sum |= (*pa++ ^ *pb++);
-
- return sum;
-}
-
void
write_task(struct per_session_data__sshd *pss, struct lws_ssh_channel *ch,
int task)
@@ -1250,7 +1238,8 @@ again:
e[LWS_GENCRYPTO_RSA_KEYEL_N].len = m;
if (lws_genrsa_create(&ctx, e, pss->vhd->context,
- LGRSAM_PKCS1_1_5))
+ LGRSAM_PKCS1_1_5,
+ LWS_GENHASH_TYPE_UNKNOWN))
goto ua_fail;
/*
diff --git a/scripts/autobahn-test-server.sh b/scripts/autobahn-test-server.sh
index d2afd6854..ad0a76ced 100755
--- a/scripts/autobahn-test-server.sh
+++ b/scripts/autobahn-test-server.sh
@@ -10,7 +10,7 @@
set -u
-PARALLEL=8
+PARALLEL=2
N=1
OS=`uname`
diff --git a/test-apps/test-sshd.c b/test-apps/test-sshd.c
index 5eb918799..1550e551f 100644
--- a/test-apps/test-sshd.c
+++ b/test-apps/test-sshd.c
@@ -342,7 +342,7 @@ ssh_ops_is_pubkey_authorized(const char *username, const char *type,
* EN that the peer sends us
*/
- if (memcmp(peer, ps, peer_len)) {
+ if (lws_timingsafe_bcmp(peer, ps, peer_len)) {
lwsl_info("factors mismatch\n");
goto bail;
}