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