1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515
25 * to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
26 */
27
28 #include "private-lib-core.h"
29 #include "jose/private-lib-jose.h"
30
31 #include <stdint.h>
32
33 static const char * const jws_jose[] = {
34 "alg", /* REQUIRED */
35 "jku",
36 "jwk",
37 "kid",
38 "x5u",
39 "x5c",
40 "x5t",
41 "x5t#S256",
42 "typ",
43 "cty",
44 "crit",
45
46 /* valid for JWE only below here */
47
48 "recipients[].header",
49 "recipients[].header.alg",
50 "recipients[].header.kid",
51 "recipients[].encrypted_key",
52
53 "enc",
54 "zip", /* ("DEF" = deflate) */
55
56 "epk", /* valid for JWE ECDH only */
57 "apu", /* valid for JWE ECDH only */
58 "apv", /* valid for JWE ECDH only */
59 "iv", /* valid for JWE AES only */
60 "tag", /* valid for JWE AES only */
61 "p2s", /* valid for JWE PBES2 only */
62 "p2c" /* valid for JWE PBES2 only */
63 };
64
65 struct jose_cb_args {
66 struct lws_jose *jose;
67
68 struct lejp_ctx jwk_jctx; /* fake lejp context used to parse epk */
69 struct lws_jwk_parse_state jps; /* fake jwk parse state */
70
71 char *temp;
72 int *temp_len;
73
74 unsigned int is_jwe;
75 unsigned int recipients_array;
76
77 int recip;
78 };
79
80 /*
81 * JWE A.4.7 Complete JWE JSON Serialization example
82 *
83 * LEJPCB_CONSTRUCTED
84 * LEJPCB_START
85 * LEJPCB_OBJECT_START
86 *
87 * protected LEJPCB_PAIR_NAME
88 * protected LEJPCB_VAL_STR_START
89 * protected LEJPCB_VAL_STR_END
90 *
91 * unprotected LEJPCB_PAIR_NAME
92 * unprotected LEJPCB_OBJECT_START
93 * unprotected.jku LEJPCB_PAIR_NAME
94 * unprotected.jku LEJPCB_VAL_STR_START
95 * unprotected.jku LEJPCB_VAL_STR_END
96 * unprotected.jku LEJPCB_OBJECT_END
97 *
98 * recipients LEJPCB_PAIR_NAME
99 * recipients[] LEJPCB_ARRAY_START
100 *
101 * recipients[] LEJPCB_OBJECT_START
102 * recipients[].header LEJPCB_PAIR_NAME
103 * recipients[].header LEJPCB_OBJECT_START
104 * recipients[].header.alg LEJPCB_PAIR_NAME
105 * recipients[].header.alg LEJPCB_VAL_STR_START
106 * recipients[].header.alg LEJPCB_VAL_STR_END
107 * recipients[].header.kid LEJPCB_PAIR_NAME
108 * recipients[].header.kid LEJPCB_VAL_STR_START
109 * recipients[].header.kid LEJPCB_VAL_STR_END
110 * recipients[] LEJPCB_OBJECT_END
111 * recipients[].encrypted_key LEJPCB_PAIR_NAME
112 * recipients[].encrypted_key LEJPCB_VAL_STR_START
113 * recipients[].encrypted_key LEJPCB_VAL_STR_CHUNK
114 * recipients[].encrypted_key LEJPCB_VAL_STR_END
115 * recipients[] LEJPCB_OBJECT_END (ctx->sp = 1)
116 *
117 * recipients[] LEJPCB_OBJECT_START
118 * recipients[].header LEJPCB_PAIR_NAME
119 * recipients[].header LEJPCB_OBJECT_START
120 * recipients[].header.alg LEJPCB_PAIR_NAME
121 * recipients[].header.alg LEJPCB_VAL_STR_START
122 * recipients[].header.alg LEJPCB_VAL_STR_END
123 * recipients[].header.kid LEJPCB_PAIR_NAME
124 * recipients[].header.kid LEJPCB_VAL_STR_START
125 * recipients[].header.kid LEJPCB_VAL_STR_END
126 * recipients[] LEJPCB_OBJECT_END
127 * recipients[].encrypted_key LEJPCB_PAIR_NAME
128 * recipients[].encrypted_key LEJPCB_VAL_STR_START
129 * recipients[].encrypted_key LEJPCB_VAL_STR_END
130 * recipients[] LEJPCB_OBJECT_END (ctx->sp = 1)
131 *
132 * recipients[] LEJPCB_ARRAY_END
133 *
134 * iv LEJPCB_PAIR_NAME
135 * iv LEJPCB_VAL_STR_START
136 * iv LEJPCB_VAL_STR_END
137 * ciphertext LEJPCB_PAIR_NAME
138 * ciphertext LEJPCB_VAL_STR_START
139 * ciphertext LEJPCB_VAL_STR_END
140 * tag LEJPCB_PAIR_NAME
141 * tag LEJPCB_VAL_STR_START
142 * tag LEJPCB_VAL_STR_END
143 *
144 * tag LEJPCB_OBJECT_END
145 * tag LEJPCB_COMPLETE
146 * tag LEJPCB_DESTRUCTED
147 *
148 */
149
150 /*
151 * RFC7516 7.2.2
152 *
153 * Note that when using the flattened syntax, just as when using the
154 * general syntax, any unprotected Header Parameter values can reside in
155 * either the "unprotected" member or the "header" member, or in both.
156 */
157
158 static signed char
lws_jws_jose_cb(struct lejp_ctx * ctx,char reason)159 lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
160 {
161 struct jose_cb_args *args = (struct jose_cb_args *)ctx->user;
162 int n; //, dest;
163
164 /*
165 * In JOSE JSON, the element "epk" contains a fully-formed JWK.
166 *
167 * For JOSE paths beginning "epk.", we pass them through to a JWK
168 * LEJP subcontext to parse using the JWK parser directly.
169 */
170
171 if (args->is_jwe && !strncmp(ctx->path, "epk.", 4)) {
172 memcpy(args->jwk_jctx.path, ctx->path + 4,
173 sizeof(ctx->path) - 4);
174 memcpy(args->jwk_jctx.buf, ctx->buf, ctx->npos);
175 args->jwk_jctx.npos = ctx->npos;
176
177 if (!ctx->path_match)
178 args->jwk_jctx.path_match = 0;
179 lejp_check_path_match(&args->jwk_jctx);
180
181 if (args->jwk_jctx.path_match)
182 args->jwk_jctx.pst[args->jwk_jctx.pst_sp].
183 callback(&args->jwk_jctx, reason);
184 }
185
186 // lwsl_notice("%s: %s %d (%d)\n", __func__, ctx->path, reason, ctx->sp);
187
188 /* at the end of each recipients[] entry, bump recipients count */
189
190 if (args->is_jwe && reason == LEJPCB_OBJECT_END && ctx->sp == 1 &&
191 !strcmp(ctx->path, "recipients[]"))
192 args->jose->recipients++;
193
194 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
195 return 0;
196
197 //dest = ctx->path_match - 1;
198
199 switch (ctx->path_match - 1) {
200
201 /* strings */
202
203 case LJJHI_ALG: /* REQUIRED */
204
205 /*
206 * look up whether we support this alg and point the caller at
207 * its definition if so
208 */
209
210 if (!args->is_jwe &&
211 lws_gencrypto_jws_alg_to_definition(ctx->buf,
212 &args->jose->alg)) {
213 lwsl_notice("%s: unknown alg '%s'\n", __func__,
214 ctx->buf);
215
216 return -1;
217 }
218
219 if (args->is_jwe &&
220 lws_gencrypto_jwe_alg_to_definition(ctx->buf,
221 &args->jose->alg)) {
222 lwsl_notice("%s: unknown JWE alg '%s'\n", __func__,
223 ctx->buf);
224
225 return -1;
226 }
227
228 return 0;
229
230 case LJJHI_TYP: /* Optional: string: media type */
231 lws_strnncpy(args->jose->typ, ctx->buf, ctx->npos,
232 sizeof(args->jose->typ));
233 break;
234
235 case LJJHI_JKU: /* Optional: string */
236 case LJJHI_KID: /* Optional: string */
237 case LJJHI_X5U: /* Optional: string: url of public key cert / chain */
238 case LJJHI_CTY: /* Optional: string: content media type */
239
240 /* base64 */
241
242 case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */
243
244 /* base64-url */
245
246 case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */
247 case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of actual cert */
248
249 /* array of strings */
250
251 case LJJHI_CRIT: /* Optional for send, REQUIRED: array of strings:
252 * mustn't contain standardized strings or null set */
253 break;
254
255 /* jwk child */
256
257 case LJJHI_JWK: /* Optional: jwk JSON object: public key: */
258
259 /* past here, JWE only */
260
261 case LJJHI_RECIPS_HDR:
262 if (!args->is_jwe) {
263 lwsl_info("%s: recipients in jws\n", __func__);
264 return -1;
265 }
266 args->recipients_array = 1;
267 break;
268
269 case LJJHI_RECIPS_HDR_ALG:
270 case LJJHI_RECIPS_HDR_KID:
271 break;
272
273 case LJJHI_RECIPS_EKEY:
274 if (!args->is_jwe) {
275 lwsl_info("%s: recipients in jws\n", __func__);
276 return -1;
277 }
278 args->recipients_array = 1;
279 //dest = ;
280 goto append_string;
281
282 case LJJHI_ENC: /* JWE only: Mandatory: string */
283 if (!args->is_jwe) {
284 lwsl_info("%s: enc in jws\n", __func__);
285 return -1;
286 }
287 if (lws_gencrypto_jwe_enc_to_definition(ctx->buf,
288 &args->jose->enc_alg)) {
289 lwsl_notice("%s: unknown enc '%s'\n", __func__,
290 ctx->buf);
291
292 return -1;
293 }
294 break;
295
296 case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */
297 if (!args->is_jwe)
298 return -1;
299 goto append_string;
300
301 case LJJHI_EPK: /* Additional arg for JWE ECDH */
302 if (!args->is_jwe)
303 return -1;
304 /* Ephemeral key... this JSON subsection is actually a JWK */
305 lwsl_err("LJJHI_EPK\n");
306 break;
307
308 case LJJHI_APU: /* Additional arg for JWE ECDH */
309 if (!args->is_jwe)
310 return -1;
311 /* Agreement Party U */
312 goto append_string;
313
314 case LJJHI_APV: /* Additional arg for JWE ECDH */
315 if (!args->is_jwe)
316 return -1;
317 /* Agreement Party V */
318 goto append_string;
319
320 case LJJHI_IV: /* Additional arg for JWE AES */
321 if (!args->is_jwe)
322 return -1;
323 goto append_string;
324
325 case LJJHI_TAG: /* Additional arg for JWE AES */
326 if (!args->is_jwe)
327 return -1;
328 goto append_string;
329
330 case LJJHI_P2S: /* Additional arg for JWE PBES2 */
331 if (!args->is_jwe)
332 return -1;
333 goto append_string;
334 case LJJHI_P2C: /* Additional arg for JWE PBES2 */
335 if (!args->is_jwe)
336 return -1;
337 goto append_string;
338
339 /* ignore what we don't understand */
340
341 default:
342 return 0;
343 }
344
345 return 0;
346
347 append_string:
348
349 if (*args->temp_len < ctx->npos) {
350 lwsl_err("%s: out of parsing space\n", __func__);
351 return -1;
352 }
353
354 if (!args->jose->e[ctx->path_match - 1].buf) {
355 args->jose->e[ctx->path_match - 1].buf = (uint8_t *)args->temp;
356 args->jose->e[ctx->path_match - 1].len = 0;
357 }
358
359 memcpy(args->temp, ctx->buf, ctx->npos);
360 args->temp += ctx->npos;
361 *args->temp_len -= ctx->npos;
362 args->jose->e[ctx->path_match - 1].len += ctx->npos;
363
364 if (reason == LEJPCB_VAL_STR_END) {
365 n = lws_b64_decode_string_len(
366 (const char *)args->jose->e[ctx->path_match - 1].buf,
367 args->jose->e[ctx->path_match - 1].len,
368 (char *)args->jose->e[ctx->path_match - 1].buf,
369 args->jose->e[ctx->path_match - 1].len + 1);
370 if (n < 0) {
371 lwsl_err("%s: b64 decode failed\n", __func__);
372 return -1;
373 }
374
375 args->temp -= args->jose->e[ctx->path_match - 1].len - n - 1;
376 *args->temp_len +=
377 args->jose->e[ctx->path_match - 1].len - n - 1;
378
379 args->jose->e[ctx->path_match - 1].len = n;
380 }
381
382 return 0;
383 }
384
385 void
lws_jose_init(struct lws_jose * jose)386 lws_jose_init(struct lws_jose *jose)
387 {
388 memset(jose, 0, sizeof(*jose));
389 }
390
391 static void
lws_jose_recip_destroy(struct lws_jws_recpient * r)392 lws_jose_recip_destroy(struct lws_jws_recpient *r)
393 {
394 lws_jwk_destroy(&r->jwk_ephemeral);
395 lws_jwk_destroy(&r->jwk);
396 }
397
398 void
lws_jose_destroy(struct lws_jose * jose)399 lws_jose_destroy(struct lws_jose *jose)
400 {
401 int n;
402
403 for (n = 0; n < (int)LWS_ARRAY_SIZE(jose->recipient); n++)
404 lws_jose_recip_destroy(&jose->recipient[n]);
405 }
406
407
408 static int
lws_jose_parse(struct lws_jose * jose,const uint8_t * buf,int n,char * temp,int * temp_len,int is_jwe)409 lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n,
410 char *temp, int *temp_len, int is_jwe)
411 {
412 struct lejp_ctx jctx;
413 struct jose_cb_args args;
414 int m;
415
416 if (is_jwe)
417 /* prepare a context for JOSE epk ephemeral jwk parsing */
418 lws_jwk_init_jps(&args.jwk_jctx, &args.jps,
419 &jose->recipient[jose->recipients].jwk_ephemeral,
420 NULL, NULL);
421
422 args.is_jwe = is_jwe;
423 args.temp = temp;
424 args.temp_len = temp_len;
425 args.jose = jose;
426 args.recip = 0;
427 args.recipients_array = 0;
428 jose->recipients = 0;
429
430 lejp_construct(&jctx, lws_jws_jose_cb, &args, jws_jose,
431 LWS_ARRAY_SIZE(jws_jose));
432
433 m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
434 lejp_destruct(&jctx);
435 if (m < 0) {
436 lwsl_notice("%s: parse returned %d\n", __func__, m);
437 return -1;
438 }
439
440 if (!args.recipients_array && jose->recipient[0].unprot[LJJHI_ALG].buf)
441 /* if no explicit recipients[], we got one */
442 jose->recipients++;
443
444 return 0;
445 }
446
447 int
lws_jws_parse_jose(struct lws_jose * jose,const char * buf,int len,char * temp,int * temp_len)448 lws_jws_parse_jose(struct lws_jose *jose,
449 const char *buf, int len, char *temp, int *temp_len)
450 {
451 return lws_jose_parse(jose, (const uint8_t *)buf, len,
452 temp, temp_len, 0);
453 }
454
455 int
lws_jwe_parse_jose(struct lws_jose * jose,const char * buf,int len,char * temp,int * temp_len)456 lws_jwe_parse_jose(struct lws_jose *jose,
457 const char *buf, int len, char *temp, int *temp_len)
458 {
459 return lws_jose_parse(jose,
460 (const uint8_t *)buf, len, temp, temp_len, 1);
461 }
462
463 int
lws_jose_render(struct lws_jose * jose,struct lws_jwk * aux_jwk,char * out,size_t out_len)464 lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
465 char *out, size_t out_len)
466 {
467 struct lws_jwk *jwk;
468 char *end = out + out_len - 1;
469 int n, m, f, sub = 0, vl;
470
471 /* JOSE requires an alg */
472 if (!jose->alg || !jose->alg->alg)
473 goto bail;
474
475 *out++ = '{';
476
477 for (n = 0; n < LWS_COUNT_JOSE_HDR_ELEMENTS; n++) {
478 switch (n) {
479
480 /* strings */
481
482 case LJJHI_ALG: /* REQUIRED */
483 case LJJHI_JKU: /* Optional: string */
484 case LJJHI_KID: /* Optional: string */
485 case LJJHI_TYP: /* Optional: string: media type */
486 case LJJHI_CTY: /* Optional: string: content media type */
487 case LJJHI_X5U: /* Optional: string: pubkey cert / chain URL */
488 case LJJHI_ENC: /* JWE only: Optional: string */
489 case LJJHI_ZIP: /* JWE only: Optional: string ("DEF"=deflate) */
490 if (jose->e[n].buf) {
491 out += lws_snprintf(out, end - out,
492 "%s\"%s\":\"%s\"", sub ? ",\n" : "",
493 jws_jose[n], jose->e[n].buf);
494 sub = 1;
495 }
496 break;
497
498 case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */
499 case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of cert */
500 case LJJHI_APU: /* Additional arg for JWE ECDH: b64url */
501 case LJJHI_APV: /* Additional arg for JWE ECDH: b64url */
502 case LJJHI_IV: /* Additional arg for JWE AES: b64url */
503 case LJJHI_TAG: /* Additional arg for JWE AES: b64url */
504 case LJJHI_P2S: /* Additional arg for JWE PBES2: b64url: salt */
505 if (jose->e[n].buf) {
506 out += lws_snprintf(out, end - out,
507 "%s\"%s\":\"", sub ? ",\n" : "",
508 jws_jose[n]);
509 sub = 1;
510 m = lws_b64_encode_string_url((const char *)
511 jose->e[n].buf, jose->e[n].len,
512 out, end - out);
513 if (m < 0)
514 return -1;
515 out += m;
516 out += lws_snprintf(out, end - out, "\"");
517 }
518 break;
519
520 case LJJHI_P2C: /* Additional arg for JWE PBES2: int: count */
521 break; /* don't support atm */
522
523 case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */
524 if (jose->e[n].buf) {
525 out += lws_snprintf(out, end - out,
526 "%s\"%s\":\"", sub ? ",\n" : "",
527 jws_jose[n]);
528 sub = 1;
529 m = lws_b64_encode_string((const char *)
530 jose->e[n].buf, jose->e[n].len,
531 out, end - out);
532 if (m < 0)
533 return -1;
534 out += m;
535 out += lws_snprintf(out, end - out, "\"");
536 }
537 break;
538
539 case LJJHI_EPK: /* Additional arg for JWE ECDH: eph pubkey */
540 case LJJHI_JWK: /* Optional: jwk JSON object: public key: */
541
542 jwk = n == LJJHI_EPK ? &jose->recipient[0].jwk_ephemeral : aux_jwk;
543 if (!jwk || !jwk->kty)
544 break;
545
546 out += lws_snprintf(out, end - out, "%s\"%s\":",
547 sub ? ",\n" : "", jws_jose[n]);
548 sub = 1;
549 vl = end - out;
550 m = lws_jwk_export(jwk, 0, out, &vl);
551 if (m < 0) {
552 lwsl_notice("%s: failed to export key\n",
553 __func__);
554
555 return -1;
556 }
557 out += m;
558 break;
559
560 case LJJHI_CRIT:/* Optional for send, REQUIRED: array of strings:
561 * mustn't contain standardized strings or null set */
562 if (!jose->e[n].buf)
563 break;
564
565 out += lws_snprintf(out, end - out,
566 "%s\"%s\":[", sub ? ",\n" : "", jws_jose[n]);
567 sub = 1;
568
569 m = 0;
570 f = 1;
571 while ((unsigned int)m < jose->e[n].len && (end - out) > 1) {
572 if (jose->e[n].buf[m] == ' ') {
573 if (!f)
574 *out++ = '\"';
575
576 m++;
577 f = 1;
578 continue;
579 }
580
581 if (f) {
582 if (m)
583 *out++ = ',';
584 *out++ = '\"';
585 f = 0;
586 }
587
588 *out++ = jose->e[n].buf[m];
589 m++;
590 }
591
592 break;
593 }
594 }
595
596 *out++ = '}';
597
598 if (out > end - 2)
599 return -1;
600
601 return out_len - (end - out) - 1;
602
603 bail:
604 return -1;
605 }
606