• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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