• 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 			(int)args->jose->e[ctx->path_match - 1].len,
368 			(char *)args->jose->e[ctx->path_match - 1].buf,
369 			(int)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 -= (int)args->jose->e[ctx->path_match - 1].len - n - 1;
376 		*args->temp_len +=
377 			(int)args->jose->e[ctx->path_match - 1].len - n - 1;
378 
379 		args->jose->e[ctx->path_match - 1].len = (uint32_t)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 static int
lws_jose_parse(struct lws_jose * jose,const uint8_t * buf,int n,char * temp,int * temp_len,int is_jwe)408 lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n,
409 	       char *temp, int *temp_len, int is_jwe)
410 {
411 	struct lejp_ctx jctx;
412 	struct jose_cb_args args;
413 	int m;
414 
415 	if (is_jwe) {
416 		/* prepare a context for JOSE epk ephemeral jwk parsing */
417 		lws_jwk_init_jps(&args.jps,
418 				 &jose->recipient[jose->recipients].jwk_ephemeral,
419 				 NULL, NULL);
420 		lejp_construct(&args.jwk_jctx, cb_jwk, &args.jps,
421 				jwk_tok, LWS_ARRAY_SIZE(jwk_tok));
422 	}
423 
424 	args.is_jwe		= (unsigned int)is_jwe;
425 	args.temp		= temp;
426 	args.temp_len		= temp_len;
427 	args.jose		= jose;
428 	args.recip		= 0;
429 	args.recipients_array	= 0;
430 	jose->recipients	= 0;
431 
432 	lejp_construct(&jctx, lws_jws_jose_cb, &args, jws_jose,
433 		       LWS_ARRAY_SIZE(jws_jose));
434 
435 	m = lejp_parse(&jctx, (uint8_t *)buf, n);
436 	lejp_destruct(&jctx);
437 	if (m < 0) {
438 		lwsl_notice("%s: parse returned %d\n", __func__, m);
439 		return -1;
440 	}
441 
442 	if (!args.recipients_array && jose->recipient[0].unprot[LJJHI_ALG].buf)
443 		/* if no explicit recipients[], we got one */
444 		jose->recipients++;
445 
446 	return 0;
447 }
448 
449 int
lws_jws_parse_jose(struct lws_jose * jose,const char * buf,int len,char * temp,int * temp_len)450 lws_jws_parse_jose(struct lws_jose *jose,
451 		   const char *buf, int len, char *temp, int *temp_len)
452 {
453 	return lws_jose_parse(jose, (const uint8_t *)buf, len,
454 			temp, temp_len, 0);
455 }
456 
457 int
lws_jwe_parse_jose(struct lws_jose * jose,const char * buf,int len,char * temp,int * temp_len)458 lws_jwe_parse_jose(struct lws_jose *jose,
459 		   const char *buf, int len, char *temp, int *temp_len)
460 {
461 	return lws_jose_parse(jose,
462 			      (const uint8_t *)buf, len, temp, temp_len, 1);
463 }
464 
465 int
lws_jose_render(struct lws_jose * jose,struct lws_jwk * aux_jwk,char * out,size_t out_len)466 lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
467 		char *out, size_t out_len)
468 {
469 	struct lws_jwk *jwk;
470 	char *end = out + out_len - 1;
471 	int n, m, f, sub = 0, vl;
472 
473 	/* JOSE requires an alg */
474 	if (!jose->alg || !jose->alg->alg)
475 		goto bail;
476 
477 	*out++ = '{';
478 
479 	for (n = 0; n < LWS_COUNT_JOSE_HDR_ELEMENTS; n++) {
480 		switch (n) {
481 
482 		/* strings */
483 
484 		case LJJHI_ALG:	/* REQUIRED */
485 		case LJJHI_JKU:	/* Optional: string */
486 		case LJJHI_KID:	/* Optional: string */
487 		case LJJHI_TYP:	/* Optional: string: media type */
488 		case LJJHI_CTY:	/* Optional: string: content media type */
489 		case LJJHI_X5U:	/* Optional: string: pubkey cert / chain URL */
490 		case LJJHI_ENC:	/* JWE only: Optional: string */
491 		case LJJHI_ZIP:	/* JWE only: Optional: string ("DEF"=deflate) */
492 			if (jose->e[n].buf) {
493 				out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
494 					"%s\"%s\":\"%s\"", sub ? ",\n" : "",
495 					jws_jose[n], jose->e[n].buf);
496 				sub = 1;
497 			}
498 			break;
499 
500 		case LJJHI_X5T:	/* Optional: base64url: SHA-1 of actual cert */
501 		case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of cert */
502 		case LJJHI_APU:	/* Additional arg for JWE ECDH:  b64url */
503 		case LJJHI_APV:	/* Additional arg for JWE ECDH:  b64url */
504 		case LJJHI_IV:	/* Additional arg for JWE AES:   b64url */
505 		case LJJHI_TAG:	/* Additional arg for JWE AES:   b64url */
506 		case LJJHI_P2S:	/* Additional arg for JWE PBES2: b64url: salt */
507 			if (jose->e[n].buf) {
508 				out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
509 					"%s\"%s\":\"", sub ? ",\n" : "",
510 						jws_jose[n]);
511 				sub = 1;
512 				m = lws_b64_encode_string_url((const char *)
513 						jose->e[n].buf, (int)jose->e[n].len,
514 						out, lws_ptr_diff(end, out));
515 				if (m < 0)
516 					return -1;
517 				out += m;
518 				out += lws_snprintf(out, lws_ptr_diff_size_t(end, out), "\"");
519 			}
520 			break;
521 
522 		case LJJHI_P2C: /* Additional arg for JWE PBES2: int: count */
523 			break; /* don't support atm */
524 
525 		case LJJHI_X5C:	/* Optional: base64 (NOT -url): actual cert */
526 			if (jose->e[n].buf) {
527 				out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
528 					"%s\"%s\":\"", sub ? ",\n" : "",
529 							jws_jose[n]);
530 				sub = 1;
531 				m = lws_b64_encode_string((const char *)
532 						jose->e[n].buf, (int)jose->e[n].len,
533 						out, lws_ptr_diff(end, out));
534 				if (m < 0)
535 					return -1;
536 				out += m;
537 				out += lws_snprintf(out, lws_ptr_diff_size_t(end, out), "\"");
538 			}
539 			break;
540 
541 		case LJJHI_EPK:	/* Additional arg for JWE ECDH:  eph pubkey */
542 		case LJJHI_JWK:	/* Optional: jwk JSON object: public key: */
543 
544 			jwk = n == LJJHI_EPK ? &jose->recipient[0].jwk_ephemeral : aux_jwk;
545 			if (!jwk || !jwk->kty)
546 				break;
547 
548 			out += lws_snprintf(out, lws_ptr_diff_size_t(end, out), "%s\"%s\":",
549 					    sub ? ",\n" : "", jws_jose[n]);
550 			sub = 1;
551 			vl = lws_ptr_diff(end, out);
552 			m = lws_jwk_export(jwk, 0, out, &vl);
553 			if (m < 0) {
554 				lwsl_notice("%s: failed to export key\n",
555 						__func__);
556 
557 				return -1;
558 			}
559 			out += m;
560 			break;
561 
562 		case LJJHI_CRIT:/* Optional for send, REQUIRED: array of strings:
563 				 * mustn't contain standardized strings or null set */
564 			if (!jose->e[n].buf)
565 				break;
566 
567 			out += lws_snprintf(out, lws_ptr_diff_size_t(end, out),
568 				"%s\"%s\":[", sub ? ",\n" : "", jws_jose[n]);
569 			sub = 1;
570 
571 			m = 0;
572 			f = 1;
573 			while ((unsigned int)m < jose->e[n].len && (end - out) > 1) {
574 				if (jose->e[n].buf[m] == ' ') {
575 					if (!f)
576 						*out++ = '\"';
577 
578 					m++;
579 					f = 1;
580 					continue;
581 				}
582 
583 				if (f) {
584 					if (m)
585 						*out++ = ',';
586 					*out++ = '\"';
587 					f = 0;
588 				}
589 
590 				*out++ = (char)jose->e[n].buf[m];
591 				m++;
592 			}
593 
594 			break;
595 		}
596 	}
597 
598 	*out++ = '}';
599 
600 	if (out > end - 2)
601 		return -1;
602 
603 	return lws_ptr_diff(out_len, (end - out)) - 1;
604 
605 bail:
606 	return -1;
607 }
608