1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * lws-crypto-jwe
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Written in 2010-2020 by Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0
7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication.
8d4afb5ceSopenharmony_ci */
9d4afb5ceSopenharmony_ci
10d4afb5ceSopenharmony_ci#include <libwebsockets.h>
11d4afb5ceSopenharmony_ci#include <sys/types.h>
12d4afb5ceSopenharmony_ci#include <fcntl.h>
13d4afb5ceSopenharmony_ci
14d4afb5ceSopenharmony_ci/*
15d4afb5ceSopenharmony_ci * handles escapes and line wrapping suitable for use
16d4afb5ceSopenharmony_ci * defining a C char array ( -c option )
17d4afb5ceSopenharmony_ci */
18d4afb5ceSopenharmony_ci
19d4afb5ceSopenharmony_cistatic void
20d4afb5ceSopenharmony_ciformat_c(const char *key)
21d4afb5ceSopenharmony_ci{
22d4afb5ceSopenharmony_ci	const char *k = key;
23d4afb5ceSopenharmony_ci	int seq = 0;
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci	while (*k) {
26d4afb5ceSopenharmony_ci		if (*k == '{') {
27d4afb5ceSopenharmony_ci			putchar('\"');
28d4afb5ceSopenharmony_ci			putchar('{');
29d4afb5ceSopenharmony_ci			putchar('\"');
30d4afb5ceSopenharmony_ci			putchar('\n');
31d4afb5ceSopenharmony_ci			putchar('\t');
32d4afb5ceSopenharmony_ci			putchar('\"');
33d4afb5ceSopenharmony_ci			k++;
34d4afb5ceSopenharmony_ci			seq = 0;
35d4afb5ceSopenharmony_ci			continue;
36d4afb5ceSopenharmony_ci		}
37d4afb5ceSopenharmony_ci		if (*k == '}') {
38d4afb5ceSopenharmony_ci			putchar('\"');
39d4afb5ceSopenharmony_ci			putchar('\n');
40d4afb5ceSopenharmony_ci			putchar('\"');
41d4afb5ceSopenharmony_ci			putchar('}');
42d4afb5ceSopenharmony_ci			putchar('\"');
43d4afb5ceSopenharmony_ci			putchar('\n');
44d4afb5ceSopenharmony_ci			k++;
45d4afb5ceSopenharmony_ci			seq = 0;
46d4afb5ceSopenharmony_ci			continue;
47d4afb5ceSopenharmony_ci		}
48d4afb5ceSopenharmony_ci		if (*k == '\"') {
49d4afb5ceSopenharmony_ci			putchar('\\');
50d4afb5ceSopenharmony_ci			putchar('\"');
51d4afb5ceSopenharmony_ci			seq += 2;
52d4afb5ceSopenharmony_ci			k++;
53d4afb5ceSopenharmony_ci			continue;
54d4afb5ceSopenharmony_ci		}
55d4afb5ceSopenharmony_ci		if (*k == ',') {
56d4afb5ceSopenharmony_ci			putchar(',');
57d4afb5ceSopenharmony_ci			putchar('\"');
58d4afb5ceSopenharmony_ci			putchar('\n');
59d4afb5ceSopenharmony_ci			putchar('\t');
60d4afb5ceSopenharmony_ci			putchar('\"');
61d4afb5ceSopenharmony_ci			k++;
62d4afb5ceSopenharmony_ci			seq = 0;
63d4afb5ceSopenharmony_ci			continue;
64d4afb5ceSopenharmony_ci		}
65d4afb5ceSopenharmony_ci		putchar(*k);
66d4afb5ceSopenharmony_ci		seq++;
67d4afb5ceSopenharmony_ci		if (seq >= 60) {
68d4afb5ceSopenharmony_ci			putchar('\"');
69d4afb5ceSopenharmony_ci			putchar('\n');
70d4afb5ceSopenharmony_ci			putchar('\t');
71d4afb5ceSopenharmony_ci			putchar(' ');
72d4afb5ceSopenharmony_ci			putchar('\"');
73d4afb5ceSopenharmony_ci			seq = 1;
74d4afb5ceSopenharmony_ci		}
75d4afb5ceSopenharmony_ci		k++;
76d4afb5ceSopenharmony_ci	}
77d4afb5ceSopenharmony_ci}
78d4afb5ceSopenharmony_ci
79d4afb5ceSopenharmony_ci#define MAX_SIZE (4 * 1024 * 1024)
80d4afb5ceSopenharmony_ci	char temp[MAX_SIZE], compact[MAX_SIZE];
81d4afb5ceSopenharmony_ci
82d4afb5ceSopenharmony_ciint main(int argc, const char **argv)
83d4afb5ceSopenharmony_ci{
84d4afb5ceSopenharmony_ci	int n, enc = 0, result = 0,
85d4afb5ceSopenharmony_ci	    logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
86d4afb5ceSopenharmony_ci	char *in;
87d4afb5ceSopenharmony_ci	struct lws_context_creation_info info;
88d4afb5ceSopenharmony_ci	int temp_len = sizeof(temp);
89d4afb5ceSopenharmony_ci	struct lws_context *context;
90d4afb5ceSopenharmony_ci	struct lws_jwe jwe;
91d4afb5ceSopenharmony_ci	const char *p;
92d4afb5ceSopenharmony_ci
93d4afb5ceSopenharmony_ci	if ((p = lws_cmdline_option(argc, argv, "-d")))
94d4afb5ceSopenharmony_ci		logs = atoi(p);
95d4afb5ceSopenharmony_ci
96d4afb5ceSopenharmony_ci	lws_set_log_level(logs, NULL);
97d4afb5ceSopenharmony_ci	lwsl_user("LWS JWE example tool\n");
98d4afb5ceSopenharmony_ci
99d4afb5ceSopenharmony_ci	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
100d4afb5ceSopenharmony_ci#if defined(LWS_WITH_NETWORK)
101d4afb5ceSopenharmony_ci	info.port = CONTEXT_PORT_NO_LISTEN;
102d4afb5ceSopenharmony_ci#endif
103d4afb5ceSopenharmony_ci	info.options = 0;
104d4afb5ceSopenharmony_ci
105d4afb5ceSopenharmony_ci	context = lws_create_context(&info);
106d4afb5ceSopenharmony_ci	if (!context) {
107d4afb5ceSopenharmony_ci		lwsl_err("lws init failed\n");
108d4afb5ceSopenharmony_ci		return 1;
109d4afb5ceSopenharmony_ci	}
110d4afb5ceSopenharmony_ci
111d4afb5ceSopenharmony_ci	lws_jwe_init(&jwe, context);
112d4afb5ceSopenharmony_ci
113d4afb5ceSopenharmony_ci	/* if encrypting, set the ciphers */
114d4afb5ceSopenharmony_ci
115d4afb5ceSopenharmony_ci	if ((p = lws_cmdline_option(argc, argv, "-e"))) {
116d4afb5ceSopenharmony_ci		char *sp = strchr(p, ' ');
117d4afb5ceSopenharmony_ci
118d4afb5ceSopenharmony_ci		if (!sp) {
119d4afb5ceSopenharmony_ci			lwsl_err("format: -e \"<cek cipher alg> "
120d4afb5ceSopenharmony_ci				 "<payload enc alg>\", eg, "
121d4afb5ceSopenharmony_ci				 "-e \"RSA1_5 A128CBC-HS256\"\n");
122d4afb5ceSopenharmony_ci
123d4afb5ceSopenharmony_ci			return 1;
124d4afb5ceSopenharmony_ci		}
125d4afb5ceSopenharmony_ci		*sp = '\0';
126d4afb5ceSopenharmony_ci		if (lws_gencrypto_jwe_alg_to_definition(p, &jwe.jose.alg)) {
127d4afb5ceSopenharmony_ci			lwsl_err("Unknown cipher alg %s\n", p);
128d4afb5ceSopenharmony_ci			return 1;
129d4afb5ceSopenharmony_ci		}
130d4afb5ceSopenharmony_ci		if (lws_gencrypto_jwe_enc_to_definition(sp + 1, &jwe.jose.enc_alg)) {
131d4afb5ceSopenharmony_ci			lwsl_err("Unknown payload enc alg %s\n", sp + 1);
132d4afb5ceSopenharmony_ci			return 1;
133d4afb5ceSopenharmony_ci		}
134d4afb5ceSopenharmony_ci
135d4afb5ceSopenharmony_ci		/* create JOSE header, also needed for output */
136d4afb5ceSopenharmony_ci
137d4afb5ceSopenharmony_ci		if (lws_jws_alloc_element(&jwe.jws.map, LJWS_JOSE,
138d4afb5ceSopenharmony_ci					  lws_concat_temp(temp, temp_len),
139d4afb5ceSopenharmony_ci					  &temp_len, strlen(p) +
140d4afb5ceSopenharmony_ci					  strlen(sp + 1) + 32, 0)) {
141d4afb5ceSopenharmony_ci			lwsl_err("%s: temp space too small\n", __func__);
142d4afb5ceSopenharmony_ci			return 1;
143d4afb5ceSopenharmony_ci		}
144d4afb5ceSopenharmony_ci
145d4afb5ceSopenharmony_ci		jwe.jws.map.len[LJWS_JOSE] = (uint32_t)lws_snprintf(
146d4afb5ceSopenharmony_ci				(char *)jwe.jws.map.buf[LJWS_JOSE], (unsigned int)temp_len,
147d4afb5ceSopenharmony_ci				"{\"alg\":\"%s\",\"enc\":\"%s\"}", p, sp + 1);
148d4afb5ceSopenharmony_ci
149d4afb5ceSopenharmony_ci		enc = 1;
150d4afb5ceSopenharmony_ci	}
151d4afb5ceSopenharmony_ci
152d4afb5ceSopenharmony_ci	in = lws_concat_temp(temp, temp_len);
153d4afb5ceSopenharmony_ci	n = (int)read(0, in, (unsigned int)temp_len);
154d4afb5ceSopenharmony_ci	if (n < 0) {
155d4afb5ceSopenharmony_ci		lwsl_err("Problem reading from stdin\n");
156d4afb5ceSopenharmony_ci		return 1;
157d4afb5ceSopenharmony_ci	}
158d4afb5ceSopenharmony_ci
159d4afb5ceSopenharmony_ci	/* account for padding as well */
160d4afb5ceSopenharmony_ci
161d4afb5ceSopenharmony_ci	temp_len -= (int)lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, (unsigned int)n);
162d4afb5ceSopenharmony_ci
163d4afb5ceSopenharmony_ci	/* grab the key */
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ci	if ((p = lws_cmdline_option(argc, argv, "-k"))) {
166d4afb5ceSopenharmony_ci		if (lws_jwk_load(&jwe.jwk, p, NULL, NULL)) {
167d4afb5ceSopenharmony_ci			lwsl_err("%s: problem loading JWK %s\n", __func__, p);
168d4afb5ceSopenharmony_ci
169d4afb5ceSopenharmony_ci			return 1;
170d4afb5ceSopenharmony_ci		}
171d4afb5ceSopenharmony_ci	} else {
172d4afb5ceSopenharmony_ci		lwsl_err("-k <jwk file> is required\n");
173d4afb5ceSopenharmony_ci
174d4afb5ceSopenharmony_ci		return 1;
175d4afb5ceSopenharmony_ci	}
176d4afb5ceSopenharmony_ci
177d4afb5ceSopenharmony_ci	if (enc) {
178d4afb5ceSopenharmony_ci
179d4afb5ceSopenharmony_ci		/* point CTXT to the plaintext we read from stdin */
180d4afb5ceSopenharmony_ci
181d4afb5ceSopenharmony_ci		jwe.jws.map.buf[LJWE_CTXT] = in;
182d4afb5ceSopenharmony_ci		jwe.jws.map.len[LJWE_CTXT] = (uint32_t)n;
183d4afb5ceSopenharmony_ci
184d4afb5ceSopenharmony_ci		/*
185d4afb5ceSopenharmony_ci		 * Create a random CEK and set EKEY to it
186d4afb5ceSopenharmony_ci		 * CEK size is determined by hash / hmac size
187d4afb5ceSopenharmony_ci		 */
188d4afb5ceSopenharmony_ci
189d4afb5ceSopenharmony_ci		n = lws_gencrypto_bits_to_bytes(jwe.jose.enc_alg->keybits_fixed);
190d4afb5ceSopenharmony_ci		if (lws_jws_randomize_element(context, &jwe.jws.map, LJWE_EKEY,
191d4afb5ceSopenharmony_ci					      lws_concat_temp(temp, temp_len),
192d4afb5ceSopenharmony_ci					      &temp_len, (unsigned int)n,
193d4afb5ceSopenharmony_ci					      LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
194d4afb5ceSopenharmony_ci			lwsl_err("Problem getting random\n");
195d4afb5ceSopenharmony_ci			goto bail1;
196d4afb5ceSopenharmony_ci		}
197d4afb5ceSopenharmony_ci
198d4afb5ceSopenharmony_ci		/* perform the encryption of the CEK and the plaintext */
199d4afb5ceSopenharmony_ci
200d4afb5ceSopenharmony_ci		n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len),
201d4afb5ceSopenharmony_ci				    &temp_len);
202d4afb5ceSopenharmony_ci		if (n < 0) {
203d4afb5ceSopenharmony_ci			lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
204d4afb5ceSopenharmony_ci			goto bail1;
205d4afb5ceSopenharmony_ci		}
206d4afb5ceSopenharmony_ci		if (lws_cmdline_option(argc, argv, "-f"))
207d4afb5ceSopenharmony_ci			/* output the JWE in flattened form */
208d4afb5ceSopenharmony_ci			n = lws_jwe_render_flattened(&jwe, compact,
209d4afb5ceSopenharmony_ci						     sizeof(compact));
210d4afb5ceSopenharmony_ci		else
211d4afb5ceSopenharmony_ci			/* output the JWE in compact form */
212d4afb5ceSopenharmony_ci			n = lws_jwe_render_compact(&jwe, compact,
213d4afb5ceSopenharmony_ci						   sizeof(compact));
214d4afb5ceSopenharmony_ci
215d4afb5ceSopenharmony_ci		if (n < 0) {
216d4afb5ceSopenharmony_ci			lwsl_err("%s: lws_jwe_render failed: %d\n",
217d4afb5ceSopenharmony_ci				 __func__, n);
218d4afb5ceSopenharmony_ci			goto bail1;
219d4afb5ceSopenharmony_ci		}
220d4afb5ceSopenharmony_ci
221d4afb5ceSopenharmony_ci		if (lws_cmdline_option(argc, argv, "-c"))
222d4afb5ceSopenharmony_ci			format_c(compact);
223d4afb5ceSopenharmony_ci		else
224d4afb5ceSopenharmony_ci			if (write(1, compact,
225d4afb5ceSopenharmony_ci#if defined(WIN32)
226d4afb5ceSopenharmony_ci					(unsigned int)
227d4afb5ceSopenharmony_ci#endif
228d4afb5ceSopenharmony_ci					strlen(compact)) < 0) {
229d4afb5ceSopenharmony_ci				lwsl_err("Write stdout failed\n");
230d4afb5ceSopenharmony_ci				goto bail1;
231d4afb5ceSopenharmony_ci			}
232d4afb5ceSopenharmony_ci	} else {
233d4afb5ceSopenharmony_ci		if (lws_cmdline_option(argc, argv, "-f")) {
234d4afb5ceSopenharmony_ci			if (lws_jwe_json_parse(&jwe, (uint8_t *)in, n,
235d4afb5ceSopenharmony_ci					       lws_concat_temp(temp, temp_len),
236d4afb5ceSopenharmony_ci					       &temp_len)) {
237d4afb5ceSopenharmony_ci				lwsl_err("%s: lws_jwe_json_parse failed\n",
238d4afb5ceSopenharmony_ci								 __func__);
239d4afb5ceSopenharmony_ci				goto bail1;
240d4afb5ceSopenharmony_ci			}
241d4afb5ceSopenharmony_ci		} else
242d4afb5ceSopenharmony_ci			/*
243d4afb5ceSopenharmony_ci			 * converts a compact serialization to b64 + decoded maps
244d4afb5ceSopenharmony_ci			 * held in jws
245d4afb5ceSopenharmony_ci			 */
246d4afb5ceSopenharmony_ci			if (lws_jws_compact_decode(in, n, &jwe.jws.map,
247d4afb5ceSopenharmony_ci						   &jwe.jws.map_b64,
248d4afb5ceSopenharmony_ci						   lws_concat_temp(temp, temp_len),
249d4afb5ceSopenharmony_ci						   &temp_len) != 5) {
250d4afb5ceSopenharmony_ci				lwsl_err("%s: lws_jws_compact_decode failed\n",
251d4afb5ceSopenharmony_ci					 __func__);
252d4afb5ceSopenharmony_ci				goto bail1;
253d4afb5ceSopenharmony_ci			}
254d4afb5ceSopenharmony_ci
255d4afb5ceSopenharmony_ci		/*
256d4afb5ceSopenharmony_ci		 * Do the crypto according to what we parsed into the jose
257d4afb5ceSopenharmony_ci		 * (information on the ciphers) and the jws (plaintext and
258d4afb5ceSopenharmony_ci		 * signature info)
259d4afb5ceSopenharmony_ci		 */
260d4afb5ceSopenharmony_ci
261d4afb5ceSopenharmony_ci		n = lws_jwe_auth_and_decrypt(&jwe,
262d4afb5ceSopenharmony_ci					     lws_concat_temp(temp, temp_len),
263d4afb5ceSopenharmony_ci					     &temp_len);
264d4afb5ceSopenharmony_ci		if (n < 0) {
265d4afb5ceSopenharmony_ci			lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
266d4afb5ceSopenharmony_ci				 __func__);
267d4afb5ceSopenharmony_ci			goto bail1;
268d4afb5ceSopenharmony_ci		}
269d4afb5ceSopenharmony_ci
270d4afb5ceSopenharmony_ci		/* if it's valid, dump the plaintext and return 0 */
271d4afb5ceSopenharmony_ci
272d4afb5ceSopenharmony_ci		if (write(1, jwe.jws.map.buf[LJWE_CTXT],
273d4afb5ceSopenharmony_ci			     jwe.jws.map.len[LJWE_CTXT]) < 0) {
274d4afb5ceSopenharmony_ci			lwsl_err("Write stdout failed\n");
275d4afb5ceSopenharmony_ci			goto bail1;
276d4afb5ceSopenharmony_ci		}
277d4afb5ceSopenharmony_ci	}
278d4afb5ceSopenharmony_ci
279d4afb5ceSopenharmony_ci	result = 0;
280d4afb5ceSopenharmony_ci
281d4afb5ceSopenharmony_cibail1:
282d4afb5ceSopenharmony_ci
283d4afb5ceSopenharmony_ci	lws_jwe_destroy(&jwe);
284d4afb5ceSopenharmony_ci
285d4afb5ceSopenharmony_ci	lws_context_destroy(context);
286d4afb5ceSopenharmony_ci
287d4afb5ceSopenharmony_ci	return result;
288d4afb5ceSopenharmony_ci}
289