1/*
2 * lws-api-test-jose - RFC7515 jws tests
3 *
4 * Written in 2010-2020 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 */
9
10#include <libwebsockets.h>
11
12/*
13 * JSON Web Signature is defined in RFC7515
14 *
15 * https://tools.ietf.org/html/rfc7515
16 *
17 * It's basically a way to wrap some JSON with a JSON "header" describing the
18 * crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
19 *
20 * The signature stays with the content, it serves a different purpose than eg
21 * a TLS tunnel to transfer it.
22 *
23 */
24
25/* for none, the compact serialization format is b64u(jose hdr).b64u(payload) */
26
27static const char *none_cser =
28	  "eyJhbGciOiJub25lIn0"
29	  "."
30	  "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
31	  "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
32	  *none_jose = "{\"alg\":\"none\"}",
33	  *none_payload	= "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
34			  " \"http://example.com/is_root\":true}";
35
36int
37test_jws_none(struct lws_context *context)
38{
39	struct lws_jws_map map;
40	struct lws_jose jose;
41	char temp[2048];
42	int n, temp_len = sizeof(temp), ret = -1;
43
44	lws_jose_init(&jose);
45
46	/* A.5 Unsecured JSON "none" RFC7515 worked example */
47
48	/* decode the b64.b64[.b64] compact serialization blocks */
49	n = lws_jws_compact_decode(none_cser, (int)strlen(none_cser), &map, NULL,
50				   temp, &temp_len);
51	if (n != 2) {
52		lwsl_err("%s: concat_map failed\n", __func__);
53		goto bail;
54	}
55
56		/* confirm the decoded JOSE header is exactly what we expect */
57		if (strncmp(none_jose, map.buf[LJWS_JOSE], map.len[LJWS_JOSE])) {
58			lwsl_err("%s: jose b64 decode wrong\n", __func__);
59			goto bail;
60		}
61
62	/* parse the JOSE header */
63	if (lws_jws_parse_jose(&jose, map.buf[LJWS_JOSE],
64			       (int)map.len[LJWS_JOSE],
65			       (char *)lws_concat_temp(temp, temp_len),
66			       &temp_len) < 0 || !jose.alg) {
67		lwsl_err("%s: JOSE parse failed\n", __func__);
68		goto bail;
69	}
70
71		/* confirm we used the "none" alg as expected from JOSE hdr */
72		if (strcmp(jose.alg->alg, "none")) {
73			lwsl_err("%s: JOSE header has wrong alg\n", __func__);
74			goto bail;
75		}
76
77		/* confirm the payload is literally what we expect */
78		if (strncmp(none_payload, map.buf[LJWS_PYLD],
79					  map.len[LJWS_PYLD])) {
80			lwsl_err("%s: payload b64 decode wrong\n", __func__);
81			goto bail;
82		}
83
84	/* end */
85
86	ret = 0;
87
88bail:
89	lws_jose_destroy(&jose);
90
91	if (ret)
92		lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
93	else
94		lwsl_notice("%s: selftest OK\n", __func__);
95
96	return ret;
97}
98
99
100
101static const char
102	   *test1	= "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
103	   *test1_enc	= "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
104	   *test2	= "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
105			  " \"http://example.com/is_root\":true}",
106	   *test2_enc	= "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
107			  "ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
108	   *key_jwk	= "{\"kty\":\"oct\",\r\n"
109			  " \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
110			  "Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
111	   *hash_enc	= "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
112;
113
114int
115test_jws_HS256(struct lws_context *context)
116{
117	char buf[2048], temp[256], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr;
118	uint8_t digest[LWS_GENHASH_LARGEST];
119	struct lws_jws_map map;
120	int temp_len = sizeof(temp);
121	struct lws_genhmac_ctx ctx;
122	struct lws_jose jose;
123	struct lws_jwk jwk;
124	struct lws_jws jws;
125	int n;
126
127	lws_jose_init(&jose);
128	lws_jws_init(&jws, &jwk, context);
129
130	/* Test 1: SHA256 on RFC7515 worked example */
131
132	/* parse the JOSE header */
133
134	if (lws_jws_parse_jose(&jose, test1, (int)strlen(test1), temp,
135			       &temp_len) < 0 || !jose.alg) {
136		lwsl_err("%s: JOSE parse failed\n", __func__);
137		goto bail;
138	}
139
140		/* confirm we used the "none" alg as expected from JOSE hdr */
141		if (strcmp(jose.alg->alg, "HS256")) {
142			lwsl_err("%s: JOSE header has wrong alg\n", __func__);
143			goto bail;
144		}
145
146	/* 1.1: import the JWK oct key */
147
148	if (lws_jwk_import(&jwk, NULL, NULL, key_jwk, strlen(key_jwk)) < 0) {
149		lwsl_notice("Failed to decode JWK test key\n");
150		return -1;
151	}
152		if (jwk.kty != LWS_GENCRYPTO_KTY_OCT) {
153			lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
154
155			return -1;
156		}
157
158	/* 1.2: create JWS known hdr + known payload */
159
160	n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
161	if (n < 0) {
162		goto bail;
163	}
164
165		if (strcmp(buf, test1_enc))
166			goto bail;
167
168	enc_ptr = p + 1; /* + 1 skips the . */
169	n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
170	if (n < 0) {
171		goto bail;
172	}
173
174		if (strcmp(enc_ptr, test2_enc))
175			goto bail;
176
177	/* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
178
179	if (lws_genhmac_init(&ctx, jose.alg->hmac_type,
180			     jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
181			     jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].len))
182		goto bail;
183	if (lws_genhmac_update(&ctx, (uint8_t *)buf, lws_ptr_diff_size_t(p, buf)))
184		goto bail_destroy_hmac;
185	lws_genhmac_destroy(&ctx, digest);
186
187	/* 1.4: append a base64 encode of the computed HMAC digest */
188
189	enc_ptr = p + 1; /* + 1 skips the . */
190	n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
191	if (n < 0)
192		goto bail;
193	if (strcmp(enc_ptr, hash_enc)) { /* check against known B64URL hash */
194		lwsl_err("%s: b64 enc of computed HMAC mismatches '%s' '%s'\n",
195			 __func__, enc_ptr, hash_enc);
196		goto bail;
197	}
198
199	/* 1.5: Check we can agree the signature matches the payload */
200
201	if (lws_jws_sig_confirm_compact_b64(buf, lws_ptr_diff_size_t(p, buf), &map, &jwk, context,
202			lws_concat_temp(temp, temp_len), &temp_len) < 0) {
203		lwsl_notice("%s: confirm sig failed\n", __func__);
204		goto bail;
205	}
206
207	lws_jws_destroy(&jws);
208	lws_jwk_destroy(&jwk);
209	lws_jose_destroy(&jose);
210
211	/* end */
212
213	lwsl_notice("%s: selftest OK\n", __func__);
214
215	return 0;
216
217bail_destroy_hmac:
218	lws_genhmac_destroy(&ctx, NULL);
219
220bail:
221	lws_jws_destroy(&jws);
222	lws_jwk_destroy(&jwk);
223	lws_jose_destroy(&jose);
224	lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
225
226	return 1;
227}
228
229
230static const char
231	/* the key from worked example in RFC7515 A-2, as a JWK */
232	*rfc7515_rsa_key =
233	"{\"kty\":\"RSA\","
234	" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
235		 "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
236		 "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
237		 "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
238		 "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
239		 "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
240	"\"e\":\"AQAB\","
241	"\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
242		"jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
243		"BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
244		"439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
245		"CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
246		"BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
247	"\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
248		"YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
249		"BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
250	"\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
251		"ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
252		"-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
253	"\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
254		"CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
255		"34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
256	"\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
257		"7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
258		"NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
259	"\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
260		"y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
261		"W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
262	"}",
263	*rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
264	"eyJhbGciOiJSUzI1NiJ9"
265	".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
266	"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
267	".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
268	"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
269	"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
270	"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
271	"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
272	"p0igcN_IoypGlUPQGe77Rw"
273;
274
275int
276test_jws_RS256(struct lws_context *context)
277{
278	struct lws_jws_map map;
279	struct lws_jose jose;
280	struct lws_jwk jwk;
281	struct lws_jws jws;
282	char temp[2048], *in;
283	int n, l, temp_len = sizeof(temp);
284
285	lws_jose_init(&jose);
286	lws_jws_init(&jws, &jwk, context);
287
288	/* Test 2: RS256 on RFC7515 worked example */
289
290	if (lws_gencrypto_jws_alg_to_definition("RS256", &jose.alg)) {
291		lwsl_err("%s: RS256 not supported\n", __func__);
292		goto bail;
293	}
294
295	/* 2.1: import the jwk */
296
297	if (lws_jwk_import(&jwk, NULL, NULL,
298			   rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
299		lwsl_notice("%s: 2.2: Failed to read JWK key\n", __func__);
300		goto bail2;
301	}
302
303	if (jwk.kty != LWS_GENCRYPTO_KTY_RSA) {
304		lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty);
305		goto bail;
306	}
307
308	/* 2.2: check the signature on the test packet from RFC7515 A-1 */
309
310	if (lws_jws_sig_confirm_compact_b64(rfc7515_rsa_a1,
311					    strlen(rfc7515_rsa_a1), &map,
312					    &jwk, context, temp, &temp_len) < 0) {
313		lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
314		goto bail;
315	}
316
317	if (lws_jws_b64_compact_map(rfc7515_rsa_a1, (int)strlen(rfc7515_rsa_a1),
318				   &jws.map_b64) != 3) {
319		lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__);
320		goto bail;
321	}
322
323	/* 2.3: generate our own signature for a copy of the test packet */
324
325	in = lws_concat_temp(temp, temp_len);
326	l = (int)strlen(rfc7515_rsa_a1);
327	if (temp_len < l + 1)
328		goto bail;
329	memcpy(in, rfc7515_rsa_a1, (unsigned int)l + 1);
330	temp_len -= l + 1;
331
332	if (lws_jws_b64_compact_map(in, l, &jws.map_b64) != 3) {
333		lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__);
334		goto bail;
335	}
336
337	/* overwrite the copy of the known b64 sig (it's all placed inside temp) */
338	n = lws_jws_sign_from_b64(&jose, &jws,
339				  (char *)jws.map_b64.buf[LJWS_SIG],
340				  jws.map_b64.len[LJWS_SIG] + 8);
341	if (n < 0) {
342		lwsl_err("%s: failed signing test packet\n", __func__);
343		goto bail;
344	}
345	jws.map_b64.len[LJWS_SIG] = (unsigned int)n;
346
347	/* 2.4: confirm our signature can be verified */
348
349	in[l] = '\0';
350	if (lws_jws_sig_confirm_compact_b64(in, (unsigned int)l, &map, &jwk,
351			context, lws_concat_temp(temp, temp_len), &temp_len) < 0) {
352		lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
353		goto bail;
354	}
355
356	lws_jwk_destroy(&jwk);
357
358	/* end */
359
360	lwsl_notice("%s: selftest OK\n", __func__);
361
362	return 0;
363
364bail:
365	lws_jwk_destroy(&jwk);
366bail2:
367	lws_jws_destroy(&jws);
368	lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
369
370	return 1;
371}
372
373static const char
374	*es256_jose = "{\"alg\":\"ES256\"}",
375	*es256_payload	= "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
376			  " \"http://example.com/is_root\":true}",
377	*es256_cser =
378	    "eyJhbGciOiJFUzI1NiJ9"
379	    "."
380	    "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
381	    "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
382	    "."
383	    "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA"
384	    "pmWQxfKTUJqPP3-Kg6NU1Q",
385	*es256_jwk =
386	"{"
387		"\"kty\":\"EC\","
388		"\"crv\":\"P-256\","
389		"\"x\":\"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU\","
390		"\"y\":\"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0\","
391		"\"d\":\"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI\""
392	"}"
393#if 0
394			,
395	rfc7515_ec_a3_R[] = {
396		 14, 209,  33,  83, 121,  99, 108,  72,  60,  47, 127,  21,  88,
397		  7, 212,   2, 163, 178,  40,   3,  58, 249, 124, 126,  23, 129,
398		154, 195,  22, 158, 166, 101
399	},
400	rfc7515_ec_a3_S[] = {
401		197,  10,   7, 211, 140,  60, 112, 229, 216, 241,  45, 175,
402		  8,  74,  84, 128, 166, 101, 144, 197, 242, 147,  80, 154,
403		143,  63, 127, 138, 131, 163,  84, 213
404	}
405#endif
406;
407
408int
409test_jws_ES256(struct lws_context *context)
410{
411	uint8_t digest[LWS_GENHASH_LARGEST];
412	struct lws_genhash_ctx hash_ctx;
413	struct lws_jws_map map;
414	struct lws_jose jose;
415	struct lws_jwk jwk;
416	struct lws_jws jws;
417	char temp[2048], *p;
418	int ret = -1, l, n, temp_len = sizeof(temp);
419
420	/* A.3 "ES256" RFC7515 worked example - verify */
421
422	lws_jose_init(&jose);
423
424	/* decode the b64.b64[.b64] compact serialization blocks */
425	if (lws_jws_compact_decode(es256_cser, (int)strlen(es256_cser),
426				   &jws.map, &jws.map_b64,
427				   temp, &temp_len) != 3) {
428		lwsl_err("%s: concat_map failed\n", __func__);
429		goto bail;
430	}
431
432		/* confirm the decoded JOSE header is exactly what we expect */
433		if (jws.map.len[LJWS_JOSE] != strlen(es256_jose) ||
434		    strncmp(es256_jose, jws.map.buf[LJWS_JOSE],
435				    jws.map.len[LJWS_JOSE])) {
436			lwsl_err("%s: jose b64 decode wrong\n", __func__);
437			goto bail;
438		}
439
440		/* confirm the decoded payload is exactly what we expect */
441		if (jws.map.len[LJWS_PYLD] != strlen(es256_payload) ||
442		    strncmp(es256_payload, jws.map.buf[LJWS_PYLD],
443					    jws.map.len[LJWS_PYLD])) {
444			lwsl_err("%s: payload b64 decode wrong\n", __func__);
445			goto bail;
446		}
447
448	/* parse the JOSE header */
449	if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
450			       (int)jws.map.len[LJWS_JOSE],
451			       (char *)lws_concat_temp(temp, temp_len), &temp_len) < 0) {
452		lwsl_err("%s: JOSE parse failed\n", __func__);
453		goto bail;
454	}
455
456		/* confirm we used "ES256" alg we expect from the JOSE hdr */
457		if (strcmp(jose.alg->alg, "ES256")) {
458			lwsl_err("%s: JOSE header has wrong alg\n", __func__);
459			goto bail;
460		}
461
462	jws.jwk = &jwk;
463	jws.context = context;
464
465	/* import the ES256 jwk */
466	if (lws_jwk_import(&jwk, NULL, NULL, es256_jwk, strlen(es256_jwk))) {
467		lwsl_notice("%s: Failed to read JWK key\n", __func__);
468		goto bail;
469	}
470
471		/* sanity */
472		if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
473			lwsl_err("%s: kty: %d instead of EC\n",
474					__func__, jwk.kty);
475			goto bail1;
476		}
477
478	if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) {
479		lwsl_notice("%s: confirm EC sig failed\n", __func__);
480		goto bail1;
481	}
482
483	/* A.3 "ES256" RFC7515 worked example - sign */
484
485	l = (int)strlen(es256_cser);
486	if (temp_len < l + 1)
487		goto bail1;
488	p = lws_concat_temp(temp, temp_len);
489	memcpy(p, es256_cser, (unsigned int)l + 1);
490	temp_len -= l + 1;
491
492	/* scan the b64 compact serialization string to map the blocks */
493	if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3)
494		goto bail1;
495
496	/* create the hash of the protected b64 part */
497	if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
498	    lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE],
499			    jws.map_b64.len[LJWS_JOSE]) ||
500	    lws_genhash_update(&hash_ctx, ".", 1) ||
501	    lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD],
502			    jws.map_b64.len[LJWS_PYLD]) ||
503	    lws_genhash_destroy(&hash_ctx, digest)) {
504		lws_genhash_destroy(&hash_ctx, NULL);
505
506		goto bail1;
507	}
508
509	lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
510
511	/* overwrite the copy of the known b64 sig (it's placed inside buf) */
512	n = lws_jws_sign_from_b64(&jose, &jws,
513				  (char *)jws.map_b64.buf[LJWS_SIG],
514				  jws.map_b64.len[LJWS_SIG] + 8);
515	if (n < 0) {
516		lwsl_err("%s: failed signing test packet\n", __func__);
517		goto bail1;
518	}
519	jws.map_b64.len[LJWS_SIG] = (unsigned int)n;
520
521	lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
522
523	/* 2.4: confirm our generated signature can be verified */
524
525//	lwsl_err("p %p, l %d\n", p, (int)l);
526	p[l] = '\0';
527	if (lws_jws_sig_confirm_compact_b64(p, (unsigned int)l, &map, &jwk,
528			context, lws_concat_temp(temp, temp_len), &temp_len) < 0) {
529		lwsl_notice("%s: confirm our EC sig failed\n", __func__);
530		goto bail1;
531	}
532
533	/* end */
534	ret =  0;
535
536bail1:
537	lws_jwk_destroy(&jwk);
538	lws_jose_destroy(&jose);
539
540bail:
541	lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK");
542
543	return ret;
544}
545
546static const char
547	*es512_jose = "{\"alg\":\"ES512\"}",
548	*es512_payload	= "Payload",
549	*es512_cser =
550	     "eyJhbGciOiJFUzUxMiJ9"
551	     "."
552	     "UGF5bG9hZA"
553	     "."
554	     "AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZq"
555	     "wqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8Kp"
556	     "EHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn",
557	*es512_jwk =
558	   "{"
559	      "\"kty\":\"EC\","
560	      "\"crv\":\"P-521\","
561	      "\"x\":\"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_"
562	           "NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk\","
563	      "\"y\":\"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl"
564	           "y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2\","
565	      "\"d\":\"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA"
566	           "xerEzgdRhajnu0ferB0d53vM9mE15j2C\""
567	   "}"
568;
569
570int
571test_jws_ES512(struct lws_context *context)
572{
573	uint8_t digest[LWS_GENHASH_LARGEST];
574	struct lws_genhash_ctx hash_ctx;
575	struct lws_jws_map map;
576	struct lws_jose jose;
577	struct lws_jwk jwk;
578	struct lws_jws jws;
579	char temp[2048], *p;
580	int ret = -1, l, n, temp_len = sizeof(temp);
581
582	/* A.4 "ES512" RFC7515 worked example - verify */
583
584	lws_jose_init(&jose);
585
586	/* decode the b64.b64[.b64] compact serialization blocks */
587	if (lws_jws_compact_decode(es512_cser, (int)strlen(es512_cser),
588				   &jws.map, &jws.map_b64, temp,
589				   &temp_len) != 3) {
590		lwsl_err("%s: concat_map failed\n", __func__);
591		goto bail;
592	}
593
594		/* confirm the decoded JOSE header is exactly what we expect */
595		if (jws.map.len[LJWS_JOSE] != strlen(es512_jose) ||
596		    strncmp(es512_jose, jws.map.buf[LJWS_JOSE],
597				        jws.map.len[LJWS_JOSE])) {
598			lwsl_err("%s: jose b64 decode wrong\n", __func__);
599			goto bail;
600		}
601
602		/* confirm the decoded payload is exactly what we expect */
603		if (jws.map.len[LJWS_PYLD] != strlen(es512_payload) ||
604		    strncmp(es512_payload, jws.map.buf[LJWS_PYLD],
605					   jws.map.len[LJWS_PYLD])) {
606			lwsl_err("%s: payload b64 decode wrong\n", __func__);
607			goto bail;
608		}
609
610	/* parse the JOSE header */
611	if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
612			      (int)jws.map.len[LJWS_JOSE],
613			      lws_concat_temp(temp, temp_len), &temp_len) < 0) {
614		lwsl_err("%s: JOSE parse failed\n", __func__);
615		goto bail;
616	}
617
618		/* confirm we used "es512" alg we expect from the JOSE hdr */
619		if (strcmp(jose.alg->alg, "ES512")) {
620			lwsl_err("%s: JOSE header has wrong alg\n", __func__);
621			goto bail;
622		}
623
624	jws.jwk = &jwk;
625	jws.context = context;
626
627	/* import the es512 jwk */
628	if (lws_jwk_import(&jwk, NULL, NULL, es512_jwk, strlen(es512_jwk))) {
629		lwsl_notice("%s: Failed to read JWK key\n", __func__);
630		goto bail;
631	}
632
633		/* sanity */
634		if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
635			lwsl_err("%s: kty: %d instead of EC\n",
636					__func__, jwk.kty);
637			goto bail1;
638		}
639
640	if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) {
641		lwsl_notice("%s: confirm EC sig failed\n", __func__);
642		goto bail1;
643	}
644
645	/* A.3 "es512" RFC7515 worked example - sign */
646
647	l = (int)strlen(es512_cser);
648	if (temp_len < l)
649		goto bail1;
650	p = lws_concat_temp(temp, temp_len);
651	memcpy(p, es512_cser, (unsigned int)l + 1);
652	temp_len -= (l + 1);
653
654	/* scan the b64 compact serialization string to map the blocks */
655	if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3)
656		goto bail1;
657
658	/* create the hash of the protected b64 part */
659	if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
660	    lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE],
661			       jws.map_b64.len[LJWS_JOSE]) ||
662	    lws_genhash_update(&hash_ctx, ".", 1) ||
663	    lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD],
664			       jws.map_b64.len[LJWS_PYLD]) ||
665	    lws_genhash_destroy(&hash_ctx, digest)) {
666		lws_genhash_destroy(&hash_ctx, NULL);
667
668		goto bail1;
669	}
670
671	/* overwrite the copy of the known b64 sig (it's placed inside buf) */
672	n = lws_jws_sign_from_b64(&jose, &jws,
673				  (char *)jws.map_b64.buf[LJWS_SIG], 1024);
674	if (n < 0) {
675		lwsl_err("%s: failed signing test packet\n", __func__);
676		goto bail1;
677	}
678	jws.map_b64.len[LJWS_SIG] = (unsigned int)n;
679
680	/* 2.4: confirm our generated signature can be verified */
681
682	p[l] = '\0';
683
684	if (lws_jws_sig_confirm_compact_b64(p, (unsigned int)l, &map, &jwk, context,
685			lws_concat_temp(temp, temp_len), &temp_len) < 0) {
686		lwsl_notice("%s: confirm our ECDSA sig failed\n", __func__);
687		goto bail1;
688	}
689
690	/* jwt test */
691
692	{
693		unsigned long long ull = lws_now_secs();
694		char buf[8192];
695		size_t cml = 2048, cml2 = 2048;
696
697		if (lws_jwt_sign_compact(context, &jwk, "ES512",
698					(char *)buf, &cml2,
699					(char *)buf + 2048, 4096,
700					"{\"iss\":\"warmcat.com\",\"aud\":"
701					"\"https://libwebsockets.org/sai\","
702					"\"iat\":%llu,"
703					"\"nbf\":%llu,"
704					"\"exp\":%llu,"
705					"\"sub\":\"manage\"}", ull,
706					ull - 60, ull + (30 * 24 * 3600)
707				     )) {
708			lwsl_err("%s: failed to create JWT\n", __func__);
709			goto bail1;
710		}
711
712		lwsl_notice("%s: jwt test '%s'\n", __func__, buf);
713
714		if (lws_jwt_signed_validate(context, &jwk, "ES512",
715					     (const char *)buf, cml2,
716					     (char *)buf + 2048, 2048,
717					     (char *)buf + 4096, &cml)) {
718			lwsl_err("%s: failed to parse JWT\n", __func__);
719
720			goto bail1;
721		}
722
723		lwsl_notice("%s: jwt valid, payload '%s'\n",
724				__func__, buf + 4096);
725	}
726
727	/* end */
728	ret =  0;
729
730bail1:
731	lws_jwk_destroy(&jwk);
732	lws_jose_destroy(&jose);
733
734bail:
735	lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK");
736
737	return ret;
738}
739
740static char
741	rsa_cert[] = "-----BEGIN CERTIFICATE-----\n"
742	     "MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD\n"
743	     "VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb\n"
744	     "MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx\n"
745	     "HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3\n"
746	     "WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl\n"
747	     "d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0\n"
748	     "cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA\n"
749	     "aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW\n"
750	     "aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8\n"
751	     "Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek\n"
752	     "LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH\n"
753	     "KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6\n"
754	     "jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ\n"
755	     "Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz\n"
756	     "TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK\n"
757	     "Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0\n"
758	     "nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo\n"
759	     "GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p\n"
760	     "sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU\n"
761	     "9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar\n"
762	     "jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow\n"
763	     "YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA\n"
764	     "xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P\n"
765	     "wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34\n"
766	     "H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv\n"
767	     "xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk\n"
768	     "ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g\n"
769	     "1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA\n"
770	     "AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg\n"
771	     "mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s\n"
772	     "8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX\n"
773	     "e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=\n"
774	     "-----END CERTIFICATE-----\n",
775	rsa_key[] = "-----BEGIN PRIVATE KEY-----\n"
776	    "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJ\n"
777	    "PubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHK\n"
778	    "nSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZ\n"
779	    "toGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU\n"
780	    "0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBT\n"
781	    "J1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pS\n"
782	    "Np7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHN\n"
783	    "uC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9\n"
784	    "fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSn\n"
785	    "zXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/Au\n"
786	    "ehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaB\n"
787	    "QLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8f\n"
788	    "qokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+\n"
789	    "vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9\n"
790	    "fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5A\n"
791	    "Kqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMT\n"
792	    "G+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/\n"
793	    "HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8\n"
794	    "YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXgl\n"
795	    "xBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvs\n"
796	    "esnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqw\n"
797	    "zFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVz\n"
798	    "mgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCw\n"
799	    "au9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN77\n"
800	    "40QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5\n"
801	    "YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFH\n"
802	    "PgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXj\n"
803	    "W7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuR\n"
804	    "naVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr6\n"
805	    "2ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m\n"
806	    "39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79\n"
807	    "J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDC\n"
808	    "R1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMp\n"
809	    "Y+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaCh\n"
810	    "BVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCE\n"
811	    "fXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQ\n"
812	    "x1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHI\n"
813	    "UlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RM\n"
814	    "OMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L\n"
815	    "65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/A\n"
816	    "aJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5\n"
817	    "SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6S\n"
818	    "me/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+I\n"
819	    "G4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iK\n"
820	    "TncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY\n"
821	    "56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2\n"
822	    "gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMr\n"
823	    "Ziw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3E\n"
824	    "NqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrs\n"
825	    "fBrpEY1IATtPq1taBZZogRqI3rOkkPk=\n"
826	    "-----END PRIVATE KEY-----\n";
827
828int
829test_jwt_RS256(struct lws_context *context)
830{
831	struct lws_jwk jwk;
832	struct lws_x509_cert *pub = NULL;
833	int ret = -1;
834	int ret_encode;
835	char sha1_fingerprint[30];
836	uint8_t sha1sum[20];
837	char der_buf[LWS_ARRAY_SIZE(rsa_cert)];
838	union lws_tls_cert_info_results *der_info =
839			(union lws_tls_cert_info_results *)der_buf;
840
841	if (lws_x509_create(&pub)) {
842		lwsl_err("%s: failed to create x509 public key\n", __func__);
843		goto bail;
844	}
845
846	if (lws_x509_parse_from_pem(pub, rsa_cert, LWS_ARRAY_SIZE(rsa_cert))) {
847		lwsl_err("%s: failed to parse x509 public key\n", __func__);
848		goto bail;
849	}
850
851	if (lws_x509_public_to_jwk(&jwk, pub, NULL, 2048)) {
852		lwsl_err("%s: failed to copy public key to jwk\n", __func__);
853		goto bail;
854	}
855
856	if (lws_x509_jwk_privkey_pem(context, &jwk, (char *)rsa_key,
857				     LWS_ARRAY_SIZE(rsa_key), NULL)) {
858		lwsl_err("%s: failed to copy private key to jwk\n", __func__);
859		goto bail;
860	}
861
862	if (lws_x509_info(pub, LWS_TLS_CERT_INFO_DER_RAW, der_info,
863			  LWS_ARRAY_SIZE(der_buf) - sizeof(*der_info) +
864			  sizeof(der_info->ns.name)) ||
865	    der_info->ns.len <= 0) {
866		lwsl_err("%s: failed to parse x509 public key\n", __func__);
867		goto bail;
868	}
869
870	if (!lws_SHA1((unsigned char *)der_info->ns.name,
871		      (size_t)der_info->ns.len, sha1sum)) {
872		lwsl_err("%s: sha1sum of public key failed\n", __func__);
873		goto bail;
874	}
875
876	ret_encode = lws_b64_encode_string_url((char *)sha1sum,
877				LWS_ARRAY_SIZE(sha1sum), sha1_fingerprint,
878				LWS_ARRAY_SIZE(sha1_fingerprint));
879	if (ret_encode < 0) {
880		lwsl_err("%s: failed to encode sha1sum to base64url\n", __func__);
881		goto bail;
882	}
883
884	while (sha1_fingerprint[--ret_encode] == '=')
885		sha1_fingerprint[ret_encode] = '\0';
886
887	lwsl_notice("%s: cert fingerprint '%s'\n", __func__, sha1_fingerprint);
888
889	/* now produce jwt with some additional header fields */
890	{
891		unsigned long long ull = lws_now_secs();
892		char buf[8192];
893		size_t cml = 2048, cml2 = 2048;
894		const char hdr_fmt[] = "{\"alg\":\"RS256\", \"typ\":\"JWT\", \"x5t\":\"%s\"}";
895		char jose_hdr[LWS_ARRAY_SIZE(hdr_fmt) + LWS_ARRAY_SIZE(sha1_fingerprint)];
896
897		struct lws_jwt_sign_info info = {
898			.alg = NULL,
899			.jose_hdr = jose_hdr,
900			.jose_hdr_len = (size_t)lws_snprintf(jose_hdr, LWS_ARRAY_SIZE(jose_hdr), hdr_fmt, sha1_fingerprint),
901			.out = buf,
902			.out_len = &cml2,
903			.temp = buf + cml2,
904			.tl = 4096
905		};
906
907		lwsl_notice("%s: jose_hdr of len %zu: '%s'\n", __func__, info.jose_hdr_len, info.jose_hdr);
908		if (lws_jwt_sign_via_info(context, &jwk, &info,
909					"{\"iss\":\"warmcat.com\",\"aud\":"
910					"\"https://libwebsockets.org/sai\","
911					"\"iat\":%llu,"
912					"\"nbf\":%llu,"
913					"\"exp\":%llu,"
914					"\"sub\":\"manage\"}", ull,
915					ull - 60, ull + (30 * 24 * 3600)
916						 )) {
917			lwsl_err("%s: failed to create JWT\n", __func__);
918			goto bail1;
919		}
920
921		lwsl_notice("%s: jwt test '%s'\n", __func__, buf);
922
923		if (lws_jwt_signed_validate(context, &jwk, "RS256",
924							 (const char *)buf, cml2,
925							 (char *)buf + 2048, 2048,
926							 (char *)buf + 4096, &cml)) {
927			lwsl_err("%s: failed to parse JWT\n", __func__);
928
929			goto bail1;
930		}
931
932		lwsl_notice("%s: jwt valid, payload '%s'\n",
933				__func__, buf + 4096);
934	}
935
936	/* end */
937	ret =	0;
938
939bail1:
940	lws_jwk_destroy(&jwk);
941	lws_x509_destroy(&pub);
942
943bail:
944	lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK");
945
946	return ret;
947}
948
949int
950test_jws(struct lws_context *context)
951{
952	int n = 0;
953
954	n |= test_jws_none(context);
955	n |= test_jws_HS256(context);
956	n |= test_jws_RS256(context);
957	n |= test_jws_ES256(context);
958	n |= test_jws_ES512(context);
959	n |= test_jwt_RS256(context);
960
961	return n;
962}
963