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
27 static 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
36 int
test_jws_none(struct lws_context * context)37 test_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
88 bail:
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
101 static 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
114 int
test_jws_HS256(struct lws_context * context)115 test_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
217 bail_destroy_hmac:
218 lws_genhmac_destroy(&ctx, NULL);
219
220 bail:
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
230 static 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
275 int
test_jws_RS256(struct lws_context * context)276 test_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
364 bail:
365 lws_jwk_destroy(&jwk);
366 bail2:
367 lws_jws_destroy(&jws);
368 lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
369
370 return 1;
371 }
372
373 static 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
408 int
test_jws_ES256(struct lws_context * context)409 test_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
536 bail1:
537 lws_jwk_destroy(&jwk);
538 lws_jose_destroy(&jose);
539
540 bail:
541 lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK");
542
543 return ret;
544 }
545
546 static 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
570 int
test_jws_ES512(struct lws_context * context)571 test_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
730 bail1:
731 lws_jwk_destroy(&jwk);
732 lws_jose_destroy(&jose);
733
734 bail:
735 lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK");
736
737 return ret;
738 }
739
740 static 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
828 int
test_jwt_RS256(struct lws_context * context)829 test_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
939 bail1:
940 lws_jwk_destroy(&jwk);
941 lws_x509_destroy(&pub);
942
943 bail:
944 lwsl_notice("%s: selftest %s\n", __func__, ret ? "FAIL" : "OK");
945
946 return ret;
947 }
948
949 int
test_jws(struct lws_context * context)950 test_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