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