aboutsummaryrefslogtreecommitdiff
path: root/minimal-examples/api-tests
diff options
context:
space:
mode:
authorAndy Green <andy@warmcat.com>2018-11-27 08:36:08 +0800
committerAndy Green <andy@warmcat.com>2018-12-27 06:45:31 +0800
commit440dacc9928eedfc6806832f114e8ecd97210362 (patch)
tree78f8ebdc94f1a7f2a7114cc778532a08cbfed634 /minimal-examples/api-tests
parent5976e4b33401e473d929e591caba6e0e8f276940 (diff)
downloadlibwebsockets-440dacc9928eedfc6806832f114e8ecd97210362.tar.gz
JOSE: refactor and prepare for JWE
Until now the JOSE pieces only had enough support for ACME. This patch improves the JWK parsing to prepare for more complete support and for adding JWE, genaes and genec in later patches.
Diffstat (limited to 'minimal-examples/api-tests')
-rw-r--r--minimal-examples/api-tests/api-test-jose/CMakeLists.txt73
-rw-r--r--minimal-examples/api-tests/api-test-jose/README.md22
-rw-r--r--minimal-examples/api-tests/api-test-jose/jwe.c332
-rw-r--r--minimal-examples/api-tests/api-test-jose/jwk.c350
-rw-r--r--minimal-examples/api-tests/api-test-jose/jws.c248
-rw-r--r--minimal-examples/api-tests/api-test-jose/main.c51
-rwxr-xr-xminimal-examples/api-tests/api-test-jose/selftest.sh24
7 files changed, 1100 insertions, 0 deletions
diff --git a/minimal-examples/api-tests/api-test-jose/CMakeLists.txt b/minimal-examples/api-tests/api-test-jose/CMakeLists.txt
new file mode 100644
index 00000000..49207b7b
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/CMakeLists.txt
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-api-test-jose)
+set(SRCS main.c jwk.c jws.c jwe.c)
+
+# If we are being built as part of lws, confirm current build config supports
+# reqconfig, else skip building ourselves.
+#
+# If we are being built externally, confirm installed lws was configured to
+# support reqconfig, else error out with a helpful message about the problem.
+#
+MACRO(require_lws_config reqconfig _val result)
+
+ if (DEFINED ${reqconfig})
+ if (${reqconfig})
+ set (rq 1)
+ else()
+ set (rq 0)
+ endif()
+ else()
+ set(rq 0)
+ endif()
+
+ if (${_val} EQUAL ${rq})
+ set(SAME 1)
+ else()
+ set(SAME 0)
+ endif()
+
+ if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
+ if (${_val})
+ message("${SAMP}: skipping as lws being built without ${reqconfig}")
+ else()
+ message("${SAMP}: skipping as lws built with ${reqconfig}")
+ endif()
+ set(${result} 0)
+ else()
+ if (LWS_WITH_MINIMAL_EXAMPLES)
+ set(MET ${SAME})
+ else()
+ CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
+ if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
+ set(HAS_${reqconfig} 0)
+ else()
+ set(HAS_${reqconfig} 1)
+ endif()
+ if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
+ set(MET 1)
+ else()
+ set(MET 0)
+ endif()
+ endif()
+ if (NOT MET)
+ if (${_val})
+ message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
+ else()
+ message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
+ endif()
+ endif()
+ endif()
+ENDMACRO()
+
+
+
+ add_executable(${SAMP} ${SRCS})
+
+ if (websockets_shared)
+ target_link_libraries(${SAMP} websockets_shared)
+ add_dependencies(${SAMP} websockets_shared)
+ else()
+ target_link_libraries(${SAMP} websockets)
+ endif()
diff --git a/minimal-examples/api-tests/api-test-jose/README.md b/minimal-examples/api-tests/api-test-jose/README.md
new file mode 100644
index 00000000..74034c79
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/README.md
@@ -0,0 +1,22 @@
+# lws api test lwsac
+
+Demonstrates how to use and performs selftests for lwsac
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+Commandline option|Meaning
+---|---
+-d <loglevel>|Debug verbosity in decimal, eg, -d15
+
+```
+ $ ./lws-api-test-lwsac
+[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac
+[2018/10/09 09:14:17:4835] USER: Completed: PASS
+```
+
diff --git a/minimal-examples/api-tests/api-test-jose/jwe.c b/minimal-examples/api-tests/api-test-jose/jwe.c
new file mode 100644
index 00000000..77637e36
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/jwe.c
@@ -0,0 +1,332 @@
+/*
+ * lws-api-test-jose - RFC7516 jwe tests
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include <libwebsockets.h>
+
+
+/* A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 */
+
+/* "Live long and prosper." */
+static
+uint8_t
+
+#if 0
+lws_jwe_ex_a2_plaintext[] = {
+ 76, 105, 118, 101, 32, 108, 111, 110,
+ 103, 32, 97, 110, 100, 32, 112, 114,
+ 111, 115, 112, 101, 114, 46
+},
+#endif
+*lws_jwe_ex_a2_jose_hdr = (uint8_t *)
+ "{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}",
+
+*lws_jwe_ex_a2_jose_hdr_b64utf8 = (unsigned char *)
+ "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
+
+lws_jwe_ex_a2_cek[] = {
+ 4, 211, 31, 197, 84, 157, 252, 254,
+ 11, 100, 157, 250, 63, 170, 106, 206,
+ 107, 124, 212, 45, 111, 107, 9, 219,
+ 200, 177, 0, 240, 143, 156, 44, 207
+},
+
+*lws_jwe_ex_a2_jwk_json = (uint8_t *)
+"{"
+ "\"kty\":\"RSA\","
+ "\"n\":\"sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl"
+ "UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre"
+ "cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_"
+ "7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI"
+ "Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU"
+ "7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq"
+ "1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry"
+ "nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_"
+ "0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj"
+ "-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj"
+ "T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ\","
+ "\"p\":\"9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68"
+ "ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP"
+ "krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM\","
+ "\"q\":\"uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y"
+ "BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN"
+ "-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0\","
+ "\"dp\":\"w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv"
+ "ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra"
+ "Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs\","
+ "\"dq\":\"o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff"
+ "7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_"
+ "odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU\","
+ "\"qi\":\"eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC"
+ "tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ"
+ "B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo\""
+"}",
+
+lws_jwe_ex_a2_jwk_enc_key[] = {
+ 80, 104, 72, 58, 11, 130, 236, 139,
+ 132, 189, 255, 205, 61, 86, 151, 176,
+ 99, 40, 44, 233, 176, 189, 205, 70,
+ 202, 169, 72, 40, 226, 181, 156, 223,
+ 120, 156, 115, 232, 150, 209, 145, 133,
+ 104, 112, 237, 156, 116, 250, 65, 102,
+ 212, 210, 103, 240, 177, 61, 93, 40,
+ 71, 231, 223, 226, 240, 157, 15, 31,
+ 150, 89, 200, 215, 198, 203, 108, 70,
+ 117, 66, 212, 238, 193, 205, 23, 161,
+ 169, 218, 243, 203, 128, 214, 127, 253,
+ 215, 139, 43, 17, 135, 103, 179, 220,
+ 28, 2, 212, 206, 131, 158, 128, 66,
+ 62, 240, 78, 186, 141, 125, 132, 227,
+ 60, 137, 43, 31, 152, 199, 54, 72,
+ 34, 212, 115, 11, 152, 101, 70, 42,
+ 219, 233, 142, 66, 151, 250, 126, 146,
+ 141, 216, 190, 73, 50, 177, 146, 5,
+ 52, 247, 28, 197, 21, 59, 170, 247,
+ 181, 89, 131, 241, 169, 182, 246, 99,
+ 15, 36, 102, 166, 182, 172, 197, 136,
+ 230, 120, 60, 58, 219, 243, 149, 94,
+ 222, 150, 154, 194, 110, 227, 225, 112,
+ 39, 89, 233, 112, 207, 211, 241, 124,
+ 174, 69, 221, 179, 107, 196, 225, 127,
+ 167, 112, 226, 12, 242, 16, 24, 28,
+ 120, 182, 244, 213, 244, 153, 194, 162,
+ 69, 160, 244, 248, 63, 165, 141, 4,
+ 207, 249, 193, 79, 131, 0, 169, 233,
+ 127, 167, 101, 151, 125, 56, 112, 111,
+ 248, 29, 232, 90, 29, 147, 110, 169,
+ 146, 114, 165, 204, 71, 136, 41, 252
+}
+#if 0
+,
+*lws_jwe_ex_a2_jwk_enc_key_b64 = (uint8_t *)
+ "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm"
+ "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc"
+ "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF"
+ "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8"
+ "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv"
+ "-B3oWh2TbqmScqXMR4gp_A",
+
+lws_jwe_ex_a2_iv[] = {
+ 3, 22, 60, 12, 43, 67, 104, 105,
+ 108, 108, 105, 99, 111, 116, 104, 101
+},
+
+*lws_jwe_ex_a2_iv_b64 = (uint8_t *)
+ "AxY8DCtDaGlsbGljb3RoZQ",
+
+lws_jwe_ex_a2_aad[] = {
+ 101, 121, 74, 104, 98, 71, 99, 105,
+ 79, 105, 74, 83, 85, 48, 69, 120,
+ 88, 122, 85, 105, 76, 67, 74, 108,
+ 98, 109, 77, 105, 79, 105, 74, 66,
+ 77, 84, 73, 52, 81, 48, 74, 68,
+ 76, 85, 104, 84, 77, 106, 85, 50,
+ 73, 110, 48
+},
+
+lws_jwe_ex_a2_ciphertext[] = {
+ 40, 57, 83, 181, 119, 33, 133, 148,
+ 198, 185, 243, 24, 152, 230, 6, 75,
+ 129, 223, 127, 19, 210, 82, 183, 230,
+ 168, 33, 215, 104, 143, 112, 56, 102
+},
+
+*lws_jwe_ex_a2_ciphertext_b64 = (uint8_t *)
+ "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
+
+lws_jwe_ex_a2_authtag[] = {
+ 246, 17, 244, 190, 4, 95, 98, 3,
+ 231, 0, 115, 157, 242, 203, 100, 191
+},
+
+*lws_jwe_ex_a2_authtag_b64 = (uint8_t *)
+ "9hH0vgRfYgPnAHOd8stkvw",
+
+*lws_jwe_ex_a2_aggregated = (uint8_t *)
+ "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0."
+ "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm"
+ "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc"
+ "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF"
+ "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8"
+ "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv"
+ "-B3oWh2TbqmScqXMR4gp_A."
+ "AxY8DCtDaGlsbGljb3RoZQ."
+ "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY."
+ "9hH0vgRfYgPnAHOd8stkvw"
+#endif
+;
+
+/*
+ * These are the inputs and outputs from the worked example in RFC7515
+ * Appendix A.1.
+ *
+ * 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
+ * a fixed BASE64URL result.
+ *
+ * 2) has a fixed header + payload and is signed with a key given in JWK format
+ */
+int
+test_jwe(struct lws_context *context)
+{
+ struct lws_genrsa_ctx rsactx;
+ struct lws_jwk jwk;
+ uint8_t enc_cek[sizeof(lws_jwe_ex_a2_jwk_enc_key) + 2048];
+ char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1;
+ int n;
+
+ /* Test 1: A.2 */
+
+ /* Decode the JWK JSON key */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) {
+ lwsl_notice("Failed to decode JWK test key\n");
+ return -1;
+ }
+
+ if (jwk.kty != LWS_JWK_KYT_RSA) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
+
+ return -1;
+ }
+
+ /* A.2.1: encode JOSE header and confirm matches official string */
+
+ n = lws_jws_encode_section((char *)lws_jwe_ex_a2_jose_hdr,
+ strlen((char *)lws_jwe_ex_a2_jose_hdr), 1,
+ &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(buf, (char *)lws_jwe_ex_a2_jose_hdr_b64utf8))
+ goto bail;
+
+ /* A.2.3: Encrypt the CEK with the recipient's public key using the
+ * RSAES-PKCS1-v1_5 algorithm to produce the JWE Encrypted Key.
+ */
+
+ if (lws_genrsa_create(&rsactx, jwk.e, context)) {
+ lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
+ __func__);
+ goto bail;
+ }
+
+ memset(enc_cek, 0, sizeof(enc_cek));
+
+ n = lws_genrsa_public_encrypt(&rsactx, lws_jwe_ex_a2_cek,
+ sizeof(lws_jwe_ex_a2_cek), enc_cek);
+ lws_genrsa_destroy(&rsactx);
+ if (n < 0) {
+ lwsl_err("%s: encrypt cek fail\n", __func__);
+ goto bail;
+ }
+#if 0
+ if (memcmp(enc_cek, lws_jwe_ex_a2_jwk_enc_key, sizeof(enc_cek))) {
+ lwsl_err("%s: encrypt cek wrong output\n", __func__);
+ lwsl_hexdump_notice(enc_cek, sizeof(enc_cek));
+ lwsl_hexdump_notice(lws_jwe_ex_a2_jwk_enc_key,
+ sizeof(lws_jwe_ex_a2_jwk_enc_key));
+ goto bail;
+ }
+
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, test2_enc))
+ goto bail;
+
+ /* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
+
+ if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
+ jwk.el.e[JWK_RSA_KEYEL_E].buf,
+ jwk.el.e[JWK_RSA_KEYEL_E].len))
+ goto bail;
+ if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
+ goto bail_destroy_hmac;
+ lws_genhmac_destroy(&ctx, digest);
+
+ /* 1.4: append a base64 encode of the computed HMAC digest */
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, hash_enc)) /* check against known B64URL hash */
+ goto bail;
+
+ /* 1.5: Check we can agree the signature matches the payload */
+
+ if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 0) {
+ lwsl_notice("confirm sig failed\n");
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk); /* finished with the key from the first test */
+
+ /* Test 2: RSA256 on RFC7515 worked example */
+
+ /* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
+
+ if (lws_jwk_import(&jwk, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
+ lwsl_notice("Failed to read JWK key\n");
+ goto bail2;
+ }
+
+ /* 2.2: check the signature on the test packet from RFC7515 A-1 */
+
+ if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
+ &jwk) < 0) {
+ lwsl_notice("confirm rsa sig failed\n");
+ goto bail;
+ }
+
+ /* 2.3: generate our own signature for a copy of the test packet */
+
+ memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
+
+ /* set p to second . */
+ p = strchr(buf + 1, '.');
+ p1 = strchr(p + 1, '.');
+
+ n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
+ p1 + 1, sizeof(buf) - (p1 - buf) - 1,
+ LWS_GENHASH_TYPE_SHA256, &jwk);
+ if (n < 0)
+ goto bail;
+
+ puts(buf);
+
+ /* 2.4: confirm our signature can be verified */
+
+ if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 0) {
+ lwsl_notice("confirm rsa sig 2 failed\n");
+ goto bail;
+ }
+#endif
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+#if 0
+bail_destroy_hmac:
+ lws_genhmac_destroy(&ctx, NULL);
+#endif
+bail:
+ lws_jwk_destroy(&jwk);
+//bail2:
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+
+}
diff --git a/minimal-examples/api-tests/api-test-jose/jwk.c b/minimal-examples/api-tests/api-test-jose/jwk.c
new file mode 100644
index 00000000..b4fb95aa
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/jwk.c
@@ -0,0 +1,350 @@
+/*
+ * lws-api-test-jose - RFC7517 jwk tests
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include <libwebsockets.h>
+
+static
+uint8_t *lws_jwe_ex_a1_jwk_json = (uint8_t *) /* EC + RSA public keys */
+ "{\"keys\":"
+ "["
+ "{\"kty\":\"EC\","
+ "\"crv\":\"P-256\","
+ "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","
+ "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\","
+ "\"use\":\"enc\","
+ "\"kid\":\"1\"},"
+
+ "{\"kty\":\"RSA\","
+ "\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx"
+ "4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs"
+ "tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2"
+ "QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI"
+ "SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb"
+ "w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
+ "\"e\":\"AQAB\","
+ "\"alg\":\"RS256\","
+ "\"kid\":\"2011-04-29\"}"
+ "]"
+ "}",
+
+*lws_jwe_ex_a2_jwk_json = (uint8_t *) /* EC + RSA private keys */
+ "{\"keys\":"
+ "["
+ "{\"kty\":\"EC\","
+ "\"crv\":\"P-256\","
+ "\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","
+ "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\","
+ "\"d\":\"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE\","
+ "\"use\":\"enc\","
+ "\"kid\":\"1\"},"
+
+ "{\"kty\":\"RSA\","
+ "\"n\":\"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4"
+ "cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst"
+ "n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2Q"
+ "vzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbIS"
+ "D08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw"
+ "0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9"
+ "M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqij"
+ "wp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d"
+ "_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBz"
+ "nbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFz"
+ "me1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q\","
+ "\"p\":\"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPV"
+ "nwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqV"
+ "WlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs\","
+ "\"q\":\"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyum"
+ "qjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgx"
+ "kIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk\","
+ "\"dp\":\"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oim"
+ "YwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_Nmtu"
+ "YZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0\","
+ "\"dq\":\"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUU"
+ "vMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9"
+ "GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk\","
+ "\"qi\":\"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzg"
+ "UIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rx"
+ "yR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU\","
+ "\"alg\":\"RS256\","
+ "\"kid\":\"2011-04-29\"}"
+ "]"
+ "}",
+*lws_jwe_ex_a3_jwk_json = (uint8_t *) /* oct symmetric keys */
+ "{\"keys\":"
+ "["
+ "{\"kty\":\"oct\","
+ "\"alg\":\"A128KW\","
+ "\"k\":\"GawgguFyGrWKav7AX4VKUg\"},"
+
+ "{\"kty\":\"oct\","
+ "\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75"
+ "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\","
+ "\"kid\":\"HMAC key used in JWS spec Appendix A.1 example\"}"
+ "]"
+ "}",
+
+*lws_jwe_ex_b_jwk_json = (uint8_t *) /* x5c example (no parent JSON) */
+ "{\"kty\":\"RSA\","
+ "\"use\":\"sig\","
+ "\"kid\":\"1b94c\","
+ "\"n\":\"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08"
+ "PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q"
+ "u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a"
+ "YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH"
+ "MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv"
+ "VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ\","
+ "\"e\":\"AQAB\","
+ "\"x5c\":"
+ "[\"MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB"
+ "gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD"
+ "VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1"
+ "wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg"
+ "NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV"
+ "QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w"
+ "YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH"
+ "YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66"
+ "s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6"
+ "SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn"
+ "fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq"
+ "PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk"
+ "aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA"
+ "QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL"
+ "+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1"
+ "zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL"
+ "2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo"
+ "4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq"
+ "gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==\"]"
+ "}",
+*lws_jwe_ex_c1_jwk_json = (uint8_t *) /* RSA enc private key (no parent JSON) */
+ "{"
+ "\"kty\":\"RSA\","
+ "\"kid\":\"juliet@capulet.lit\","
+ "\"use\":\"enc\","
+ "\"n\":\"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy"
+ "O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP"
+ "8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0"
+ "Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X"
+ "OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1"
+ "_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS"
+ "NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U"
+ "vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu"
+ "ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu"
+ "rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a"
+ "hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ\","
+ "\"p\":\"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf"
+ "QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8"
+ "UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws\","
+ "\"q\":\"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I"
+ "edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK"
+ "rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s\","
+ "\"dp\":\"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3"
+ "tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w"
+ "Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c\","
+ "\"dq\":\"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9"
+ "GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy"
+ "mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots\","
+ "\"qi\":\"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq"
+ "abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o"
+ "Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8\""
+ "}" /*,
+lws_jwe_ex_c1_plaintext[] = {
+ 123, 34, 107, 116, 121, 34, 58, 34, 82, 83, 65, 34, 44, 34, 107,
+ 105, 100, 34, 58, 34, 106, 117, 108, 105, 101, 116, 64, 99, 97, 112,
+ 117, 108, 101, 116, 46, 108, 105, 116, 34, 44, 34, 117, 115, 101, 34,
+ 58, 34, 101, 110, 99, 34, 44, 34, 110, 34, 58, 34, 116, 54, 81, 56,
+ 80, 87, 83, 105, 49, 100, 107, 74, 106, 57, 104, 84, 80, 56, 104, 78,
+ 89, 70, 108, 118, 97, 100, 77, 55, 68, 102, 108, 87, 57, 109, 87,
+ 101, 112, 79, 74, 104, 74, 54, 54, 119, 55, 110, 121, 111, 75, 49,
+ 103, 80, 78, 113, 70, 77, 83, 81, 82, 121, 79, 49, 50, 53, 71, 112,
+ 45, 84, 69, 107, 111, 100, 104, 87, 114, 48, 105, 117, 106, 106, 72,
+ 86, 120, 55, 66, 99, 86, 48, 108, 108, 83, 52, 119, 53, 65, 67, 71,
+ 103, 80, 114, 99, 65, 100, 54, 90, 99, 83, 82, 48, 45, 73, 113, 111,
+ 109, 45, 81, 70, 99, 78, 80, 56, 83, 106, 103, 48, 56, 54, 77, 119,
+ 111, 113, 81, 85, 95, 76, 89, 121, 119, 108, 65, 71, 90, 50, 49, 87,
+ 83, 100, 83, 95, 80, 69, 82, 121, 71, 70, 105, 78, 110, 106, 51, 81,
+ 81, 108, 79, 56, 89, 110, 115, 53, 106, 67, 116, 76, 67, 82, 119, 76,
+ 72, 76, 48, 80, 98, 49, 102, 69, 118, 52, 53, 65, 117, 82, 73, 117,
+ 85, 102, 86, 99, 80, 121, 83, 66, 87, 89, 110, 68, 121, 71, 120, 118,
+ 106, 89, 71, 68, 83, 77, 45, 65, 113, 87, 83, 57, 122, 73, 81, 50,
+ 90, 105, 108, 103, 84, 45, 71, 113, 85, 109, 105, 112, 103, 48, 88,
+ 79, 67, 48, 67, 99, 50, 48, 114, 103, 76, 101, 50, 121, 109, 76, 72,
+ 106, 112, 72, 99, 105, 67, 75, 86, 65, 98, 89, 53, 45, 76, 51, 50,
+ 45, 108, 83, 101, 90, 79, 45, 79, 115, 54, 85, 49, 53, 95, 97, 88,
+ 114, 107, 57, 71, 119, 56, 99, 80, 85, 97, 88, 49, 95, 73, 56, 115,
+ 76, 71, 117, 83, 105, 86, 100, 116, 51, 67, 95, 70, 110, 50, 80, 90,
+ 51, 90, 56, 105, 55, 52, 52, 70, 80, 70, 71, 71, 99, 71, 49, 113,
+ 115, 50, 87, 122, 45, 81, 34, 44, 34, 101, 34, 58, 34, 65, 81, 65,
+ 66, 34, 44, 34, 100, 34, 58, 34, 71, 82, 116, 98, 73, 81, 109, 104,
+ 79, 90, 116, 121, 115, 122, 102, 103, 75, 100, 103, 52, 117, 95, 78,
+ 45, 82, 95, 109, 90, 71, 85, 95, 57, 107, 55, 74, 81, 95, 106, 110,
+ 49, 68, 110, 102, 84, 117, 77, 100, 83, 78, 112, 114, 84, 101, 97,
+ 83, 84, 121, 87, 102, 83, 78, 107, 117, 97, 65, 119, 110, 79, 69, 98,
+ 73, 81, 86, 121, 49, 73, 81, 98, 87, 86, 86, 50, 53, 78, 89, 51, 121,
+ 98, 99, 95, 73, 104, 85, 74, 116, 102, 114, 105, 55, 98, 65, 88, 89,
+ 69, 82, 101, 87, 97, 67, 108, 51, 104, 100, 108, 80, 75, 88, 121, 57,
+ 85, 118, 113, 80, 89, 71, 82, 48, 107, 73, 88, 84, 81, 82, 113, 110,
+ 115, 45, 100, 86, 74, 55, 106, 97, 104, 108, 73, 55, 76, 121, 99,
+ 107, 114, 112, 84, 109, 114, 77, 56, 100, 87, 66, 111, 52, 95, 80,
+ 77, 97, 101, 110, 78, 110, 80, 105, 81, 103, 79, 48, 120, 110, 117,
+ 84, 111, 120, 117, 116, 82, 90, 74, 102, 74, 118, 71, 52, 79, 120,
+ 52, 107, 97, 51, 71, 79, 82, 81, 100, 57, 67, 115, 67, 90, 50, 118,
+ 115, 85, 68, 109, 115, 88, 79, 102, 85, 69, 78, 79, 121, 77, 113, 65,
+ 68, 67, 54, 112, 49, 77, 51, 104, 51, 51, 116, 115, 117, 114, 89, 49,
+ 53, 107, 57, 113, 77, 83, 112, 71, 57, 79, 88, 95, 73, 74, 65, 88,
+ 109, 120, 122, 65, 104, 95, 116, 87, 105, 90, 79, 119, 107, 50, 75,
+ 52, 121, 120, 72, 57, 116, 83, 51, 76, 113, 49, 121, 88, 56, 67, 49,
+ 69, 87, 109, 101, 82, 68, 107, 75, 50, 97, 104, 101, 99, 71, 56, 53,
+ 45, 111, 76, 75, 81, 116, 53, 86, 69, 112, 87, 72, 75, 109, 106, 79,
+ 105, 95, 103, 74, 83, 100, 83, 103, 113, 99, 78, 57, 54, 88, 53, 50,
+ 101, 115, 65, 81, 34, 44, 34, 112, 34, 58, 34, 50, 114, 110, 83, 79,
+ 86, 52, 104, 75, 83, 78, 56, 115, 83, 52, 67, 103, 99, 81, 72, 70,
+ 98, 115, 48, 56, 88, 98, 111, 70, 68, 113, 75, 117, 109, 51, 115, 99,
+ 52, 104, 51, 71, 82, 120, 114, 84, 109, 81, 100, 108, 49, 90, 75, 57,
+ 117, 119, 45, 80, 73, 72, 102, 81, 80, 48, 70, 107, 120, 88, 86, 114,
+ 120, 45, 87, 69, 45, 90, 69, 98, 114, 113, 105, 118, 72, 95, 50, 105,
+ 67, 76, 85, 83, 55, 119, 65, 108, 54, 88, 118, 65, 82, 116, 49, 75,
+ 107, 73, 97, 85, 120, 80, 80, 83, 89, 66, 57, 121, 107, 51, 49, 115,
+ 48, 81, 56, 85, 75, 57, 54, 69, 51, 95, 79, 114, 65, 68, 65, 89, 116,
+ 65, 74, 115, 45, 77, 51, 74, 120, 67, 76, 102, 78, 103, 113, 104, 53,
+ 54, 72, 68, 110, 69, 84, 84, 81, 104, 72, 51, 114, 67, 84, 53, 84,
+ 51, 121, 74, 119, 115, 34, 44, 34, 113, 34, 58, 34, 49, 117, 95, 82,
+ 105, 70, 68, 80, 55, 76, 66, 89, 104, 51, 78, 52, 71, 88, 76, 84, 57,
+ 79, 112, 83, 75, 89, 80, 48, 117, 81, 90, 121, 105, 97, 90, 119, 66,
+ 116, 79, 67, 66, 78, 74, 103, 81, 120, 97, 106, 49, 48, 82, 87, 106,
+ 115, 90, 117, 48, 99, 54, 73, 101, 100, 105, 115, 52, 83, 55, 66, 95,
+ 99, 111, 83, 75, 66, 48, 75, 106, 57, 80, 97, 80, 97, 66, 122, 103,
+ 45, 73, 121, 83, 82, 118, 118, 99, 81, 117, 80, 97, 109, 81, 117, 54,
+ 54, 114, 105, 77, 104, 106, 86, 116, 71, 54, 84, 108, 86, 56, 67, 76,
+ 67, 89, 75, 114, 89, 108, 53, 50, 122, 105, 113, 75, 48, 69, 95, 121,
+ 109, 50, 81, 110, 107, 119, 115, 85, 88, 55, 101, 89, 84, 66, 55, 76,
+ 98, 65, 72, 82, 75, 57, 71, 113, 111, 99, 68, 69, 53, 66, 48, 102,
+ 56, 48, 56, 73, 52, 115, 34, 44, 34, 100, 112, 34, 58, 34, 75, 107,
+ 77, 84, 87, 113, 66, 85, 101, 102, 86, 119, 90, 50, 95, 68, 98, 106,
+ 49, 112, 80, 81, 113, 121, 72, 83, 72, 106, 106, 57, 48, 76, 53, 120,
+ 95, 77, 79, 122, 113, 89, 65, 74, 77, 99, 76, 77, 90, 116, 98, 85,
+ 116, 119, 75, 113, 118, 86, 68, 113, 51, 116, 98, 69, 111, 51, 90,
+ 73, 99, 111, 104, 98, 68, 116, 116, 54, 83, 98, 102, 109, 87, 122,
+ 103, 103, 97, 98, 112, 81, 120, 78, 120, 117, 66, 112, 111, 79, 79,
+ 102, 95, 97, 95, 72, 103, 77, 88, 75, 95, 108, 104, 113, 105, 103,
+ 73, 52, 121, 95, 107, 113, 83, 49, 119, 89, 53, 50, 73, 119, 106, 85,
+ 110, 53, 114, 103, 82, 114, 74, 45, 121, 89, 111, 49, 104, 52, 49,
+ 75, 82, 45, 118, 122, 50, 112, 89, 104, 69, 65, 101, 89, 114, 104,
+ 116, 116, 87, 116, 120, 86, 113, 76, 67, 82, 86, 105, 68, 54, 99, 34,
+ 44, 34, 100, 113, 34, 58, 34, 65, 118, 102, 83, 48, 45, 103, 82, 120,
+ 118, 110, 48, 98, 119, 74, 111, 77, 83, 110, 70, 120, 89, 99, 75, 49,
+ 87, 110, 117, 69, 106, 81, 70, 108, 117, 77, 71, 102, 119, 71, 105,
+ 116, 81, 66, 87, 116, 102, 90, 49, 69, 114, 55, 116, 49, 120, 68,
+ 107, 98, 78, 57, 71, 81, 84, 66, 57, 121, 113, 112, 68, 111, 89, 97,
+ 78, 48, 54, 72, 55, 67, 70, 116, 114, 107, 120, 104, 74, 73, 66, 81,
+ 97, 106, 54, 110, 107, 70, 53, 75, 75, 83, 51, 84, 81, 116, 81, 53,
+ 113, 67, 122, 107, 79, 107, 109, 120, 73, 101, 51, 75, 82, 98, 66,
+ 121, 109, 88, 120, 107, 98, 53, 113, 119, 85, 112, 88, 53, 69, 76,
+ 68, 53, 120, 70, 99, 54, 70, 101, 105, 97, 102, 87, 89, 89, 54, 51,
+ 84, 109, 109, 69, 65, 117, 95, 108, 82, 70, 67, 79, 74, 51, 120, 68,
+ 101, 97, 45, 111, 116, 115, 34, 44, 34, 113, 105, 34, 58, 34, 108,
+ 83, 81, 105, 45, 119, 57, 67, 112, 121, 85, 82, 101, 77, 69, 114, 80,
+ 49, 82, 115, 66, 76, 107, 55, 119, 78, 116, 79, 118, 115, 53, 69, 81,
+ 112, 80, 113, 109, 117, 77, 118, 113, 87, 53, 55, 78, 66, 85, 99,
+ 122, 83, 99, 69, 111, 80, 119, 109, 85, 113, 113, 97, 98, 117, 57,
+ 86, 48, 45, 80, 121, 52, 100, 81, 53, 55, 95, 98, 97, 112, 111, 75,
+ 82, 117, 49, 82, 57, 48, 98, 118, 117, 70, 110, 85, 54, 51, 83, 72,
+ 87, 69, 70, 103, 108, 90, 81, 118, 74, 68, 77, 101, 65, 118, 109,
+ 106, 52, 115, 109, 45, 70, 112, 48, 111, 89, 117, 95, 110, 101, 111,
+ 116, 103, 81, 48, 104, 122, 98, 73, 53, 103, 114, 121, 55, 97, 106,
+ 100, 89, 121, 57, 45, 50, 108, 78, 120, 95, 55, 54, 97, 66, 90, 111,
+ 79, 85, 117, 57, 72, 67, 74, 45, 85, 115, 102, 83, 79, 73, 56, 34,
+ 125 } */
+;
+
+static int
+key_import_callback(struct lws_jwk *s, void *user)
+{
+ lwsl_notice("%s: key type %d\n", __func__, s->kty);
+
+ return 0;
+}
+
+
+int
+test_jwk(struct lws_context *context)
+{
+ struct lws_jwk jwk;
+
+ /* Test 1: A.1: Example public keys */
+
+ if (lws_jwk_import(&jwk, key_import_callback, NULL,
+ (char *)lws_jwe_ex_a1_jwk_json,
+ strlen((char *)lws_jwe_ex_a1_jwk_json)) < 0) {
+ lwsl_notice("Failed to decode JWK test key\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: A.2: Example private keys */
+
+ if (lws_jwk_import(&jwk, key_import_callback, NULL,
+ (char *)lws_jwe_ex_a2_jwk_json,
+ strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) {
+ lwsl_notice("Failed at A.2\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: A.3: Example symmetric keys */
+
+ if (lws_jwk_import(&jwk, key_import_callback, NULL,
+ (char *)lws_jwe_ex_a3_jwk_json,
+ strlen((char *)lws_jwe_ex_a3_jwk_json)) < 0) {
+ lwsl_notice("Failed at A.3\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: B: Example x509 cert chain (no parent JSON) */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_b_jwk_json,
+ strlen((char *)lws_jwe_ex_b_jwk_json)) < 0) {
+ lwsl_notice("Failed at B\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* Test 1: C.1: Example private key (no parent JSON) */
+
+ if (lws_jwk_import(&jwk, NULL, NULL,
+ (char *)lws_jwe_ex_c1_jwk_json,
+ strlen((char *)lws_jwe_ex_c1_jwk_json)) < 0) {
+ lwsl_notice("Failed at B\n");
+ goto bail1;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+
+//bail:
+// lws_jwk_destroy(&jwk);
+bail1:
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+
+}
diff --git a/minimal-examples/api-tests/api-test-jose/jws.c b/minimal-examples/api-tests/api-test-jose/jws.c
new file mode 100644
index 00000000..d82b0ca7
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/jws.c
@@ -0,0 +1,248 @@
+/*
+ * lws-api-test-jose - RFC7515 jws tests
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include <libwebsockets.h>
+
+/*
+ * JSON Web Signature is defined in RFC7515
+ *
+ * https://tools.ietf.org/html/rfc7515
+ *
+ * It's basically a way to wrap some JSON with a JSON "header" describing the
+ * crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
+ *
+ * The signature stays with the content, it serves a different purpose than eg
+ * a TLS tunnel to transfer it.
+ *
+ * RFC7518 (JSON Web Algorithms) says for the "alg" names
+ *
+ * | HS256 | HMAC using SHA-256 | Required |
+ * | HS384 | HMAC using SHA-384 | Optional |
+ * | HS512 | HMAC using SHA-512 | Optional |
+ * | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
+ * | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
+ * | | SHA-384 | |
+ * | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
+ * | | SHA-512 | |
+ * | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
+ * | ES384 | ECDSA using P-384 and SHA-384 | Optional |
+ * | ES512 | ECDSA using P-521 and SHA-512 | Optional |
+ *
+ * Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
+ * currently. The "Recommended+" just means it is recommended but will likely
+ * be "very recommended" soon.
+ *
+ * We support HS256/384/512 for symmetric crypto, but the choice for the
+ * asymmetric crypto isn't as easy to make.
+ *
+ * Normally you'd choose the EC option but these are defined to use the
+ * "NIST curves" (RFC7518 3.4) which are believed to be insecure.
+ *
+ * https://safecurves.cr.yp.to/
+ *
+ * For that reason we implement RS256/384/512 for asymmetric.
+ */
+
+static const char
+ *test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
+ *test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
+ *test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
+ " \"http://example.com/is_root\":true}",
+ *test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
+ "ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
+ *key_jwk = "{\"kty\":\"oct\",\r\n"
+ " \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
+ "Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
+ *hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
+ /* the key from worked example in RFC7515 A-1, as a JWK */
+ *rfc7515_rsa_key =
+ "{\"kty\":\"RSA\","
+ " \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
+ "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
+ "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
+ "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
+ "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
+ "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
+ "\"e\":\"AQAB\","
+ "\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
+ "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
+ "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
+ "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
+ "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
+ "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
+ "\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
+ "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
+ "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
+ "\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
+ "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
+ "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
+ "\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
+ "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
+ "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
+ "\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
+ "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
+ "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
+ "\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
+ "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
+ "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
+ "}",
+ *rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
+ "eyJhbGciOiJSUzI1NiJ9"
+ ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
+ "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+ ".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
+ "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
+ "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
+ "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
+ "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
+ "p0igcN_IoypGlUPQGe77Rw";
+
+/*
+ * These are the inputs and outputs from the worked example in RFC7515
+ * Appendix A.1.
+ *
+ * 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
+ * a fixed BASE64URL result.
+ *
+ * 2) has a fixed header + payload and is signed with a key given in JWK format
+ */
+int
+test_jws(struct lws_context *context)
+{
+ struct lws_genhmac_ctx ctx;
+ struct lws_jwk jwk;
+ char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
+ uint8_t digest[LWS_GENHASH_LARGEST];
+ int n;
+
+ /* Test 1: SHA256 on RFC7515 worked example */
+
+ /* 1.1: decode the JWK oct key */
+
+ if (lws_jwk_import(&jwk, NULL, NULL, key_jwk, strlen(key_jwk)) < 0) {
+ lwsl_notice("Failed to decode JWK test key\n");
+ return -1;
+ }
+ if (jwk.kty != LWS_JWK_KYT_OCT) {
+ lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
+
+ return -1;
+ }
+
+ /* 1.2: create JWS known hdr + known payload */
+
+ n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(buf, test1_enc))
+ goto bail;
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, test2_enc))
+ goto bail;
+
+ /* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
+
+ if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
+ jwk.e[JWK_OCT_KEYEL_K].buf,
+ jwk.e[JWK_OCT_KEYEL_K].len))
+ goto bail;
+ if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
+ goto bail_destroy_hmac;
+ lws_genhmac_destroy(&ctx, digest);
+
+ /* 1.4: append a base64 encode of the computed HMAC digest */
+
+ enc_ptr = p + 1; /* + 1 skips the . */
+ n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
+ if (n < 0)
+ goto bail;
+ if (strcmp(enc_ptr, hash_enc)) { /* check against known B64URL hash */
+ lwsl_err("%s: b64 enc of computed HMAC mismatches '%s' '%s'\n",
+ __func__, enc_ptr, hash_enc);
+ goto bail;
+ }
+
+ /* 1.5: Check we can agree the signature matches the payload */
+
+ if (lws_jws_confirm_sig(buf, p - buf, &jwk, context) < 0) {
+ lwsl_notice("confirm sig failed\n");
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk); /* finished with the key from the first test */
+
+ /* Test 2: RSA256 on RFC7515 worked example */
+
+ /* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
+
+ if (lws_jwk_import(&jwk, NULL, NULL,
+ rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
+ lwsl_notice("%s: 2.2: Failed to read JWK key\n", __func__);
+ goto bail2;
+ }
+
+ if (jwk.kty != LWS_JWK_KYT_RSA) {
+ lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty);
+ }
+
+ /* 2.2: check the signature on the test packet from RFC7515 A-1 */
+
+ if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
+ &jwk, context) < 0) {
+ lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
+ goto bail;
+ }
+
+ /* 2.3: generate our own signature for a copy of the test packet */
+
+ memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
+
+ /* set p to second . */
+ p = strchr(buf + 1, '.');
+ p1 = strchr(p + 1, '.');
+
+ n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
+ p1 + 1, sizeof(buf) - (p1 - buf) - 1,
+ LWS_GENHASH_TYPE_SHA256, &jwk, context);
+ if (n < 0) {
+ lwsl_err("%s: failed signing test packet\n", __func__);
+ goto bail;
+ }
+
+ // puts(buf);
+
+ /* 2.4: confirm our signature can be verified */
+
+ if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk, context) < 0) {
+ lwsl_notice("confirm rsa sig 2 failed\n");
+ goto bail;
+ }
+
+ lws_jwk_destroy(&jwk);
+
+ /* end */
+
+ lwsl_notice("%s: selftest OK\n", __func__);
+
+ return 0;
+
+bail_destroy_hmac:
+ lws_genhmac_destroy(&ctx, NULL);
+
+bail:
+ lws_jwk_destroy(&jwk);
+bail2:
+ lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
+
+ return 1;
+}
diff --git a/minimal-examples/api-tests/api-test-jose/main.c b/minimal-examples/api-tests/api-test-jose/main.c
new file mode 100644
index 00000000..3a5b27b0
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/main.c
@@ -0,0 +1,51 @@
+/*
+ * lws-api-test-jose
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include <libwebsockets.h>
+
+int
+test_jwk(struct lws_context *context);
+int
+test_jws(struct lws_context *context);
+int
+test_jwe(struct lws_context *context);
+
+int main(int argc, const char **argv)
+{
+ struct lws_context_creation_info info;
+ struct lws_context *context;
+ const char *p;
+ int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS JOSE api tests\n");
+
+ memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+ info.port = CONTEXT_PORT_NO_LISTEN;
+ info.options = 0;
+
+ context = lws_create_context(&info);
+ if (!context) {
+ lwsl_err("lws init failed\n");
+ return 1;
+ }
+
+ result |= test_jwk(context);
+ result |= test_jws(context);
+ result |= test_jwe(context);
+
+ lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS");
+
+ lws_context_destroy(context);
+
+ return result;
+}
diff --git a/minimal-examples/api-tests/api-test-jose/selftest.sh b/minimal-examples/api-tests/api-test-jose/selftest.sh
new file mode 100755
index 00000000..16d1e2e8
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-jose/selftest.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# $1: path to minimal example binaries...
+# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1
+# that will be ./bin from your build dir
+#
+# $2: path for logs and results. The results will go
+# in a subdir named after the directory this script
+# is in
+#
+# $3: offset for test index count
+#
+# $4: total test count
+#
+# $5: path to ./minimal-examples dir in lws
+#
+# Test return code 0: OK, 254: timed out, other: error indication
+
+. $5/selftests-library.sh
+
+COUNT_TESTS=1
+
+dotest $1 $2 apiselftest
+exit $FAILS