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. -![overview](./doc-assets/lws-overview.svg) +![overview](./doc-assets/lws-overview.png) 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - struct lws - - tls - tls ctx - - - - - http1 - - http2 - - - ws - - - - raw - - cgi - - - - - - - - - lws_role - protocol - - - role - - - - - - - - - - struct lws_vhost - struct lws_context - - - - protocol - - - - - - - event loops - - - - - - - - event - uv - poll - ev - - TLS backend - - - - - openSSL - mbedTLS - - - - - - - - - - - - - - - - tcp, udp,unix listensocket(s) - - - - - - - - - - - - - - - tcp, udp,http1,h2, wstls client(s) - - - - - - - - ALPNSNI - ah http headers - - - - - - - event loop - peer trackingbasic auth - logging - - - - - ws ext: pm-def - - - - - - - - ssh - ACME - lws_ringVFSzip_fops - - - - service thread(s) - - - - - - - - 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; }