1 /*
2 * lws-minimal-crypto-cose-sign
3 *
4 * Written in 2010-2021 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 static int fdin = 0, fdout = 1;
15 static uint8_t extra[4096];
16 static size_t ext_len;
17
18 int
_alloc_file(struct lws_context * context,const char * filename,uint8_t ** buf,size_t * amount)19 _alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
20 size_t *amount)
21 {
22 FILE *f;
23 size_t s;
24 ssize_t m;
25 int n = 0;
26
27 f = fopen(filename, "rb");
28 if (f == NULL) {
29 n = 1;
30 goto bail;
31 }
32
33 if (fseek(f, 0, SEEK_END) != 0) {
34 n = 1;
35 goto bail;
36 }
37
38 m = ftell(f);
39 if (m == -1l) {
40 n = 1;
41 goto bail;
42 }
43 s = (size_t)m;
44
45 if (fseek(f, 0, SEEK_SET) != 0) {
46 n = 1;
47 goto bail;
48 }
49
50 *buf = malloc(s + 1);
51 if (!*buf) {
52 n = 2;
53 goto bail;
54 }
55
56 if (fread(*buf, s, 1, f) != 1) {
57 free(*buf);
58 n = 1;
59 goto bail;
60 }
61
62 *amount = s;
63
64 bail:
65 if (f)
66 fclose(f);
67
68 return n;
69
70 }
71
72 static int
extra_cb(lws_cose_sig_ext_pay_t * x)73 extra_cb(lws_cose_sig_ext_pay_t *x)
74 {
75 x->ext = extra;
76 x->xl = ext_len;
77
78 // lwsl_hexdump_notice(extra, ext_len);
79
80 return 0;
81 }
82
83 int
pay_cb(struct lws_cose_validate_context * cps,void * opaque,const uint8_t * paychunk,size_t paychunk_len)84 pay_cb(struct lws_cose_validate_context *cps, void *opaque,
85 const uint8_t *paychunk, size_t paychunk_len)
86 {
87 write(fdout, paychunk, paychunk_len);
88
89 return 0;
90 }
91
main(int argc,const char ** argv)92 int main(int argc, const char **argv)
93 {
94 uint8_t *ks, temp[256], *kid = NULL, ktmp[4096], sbuf[512];
95 int n, m, sign = 0, result = 1,
96 logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
97 enum lws_cose_sig_types sigtype = SIGTYPE_UNKNOWN;
98 struct lws_cose_validate_context *cps = NULL;
99 struct lws_cose_sign_context *csc = NULL;
100 const struct lws_gencrypto_keyelem *ke;
101 struct lws_context_creation_info info;
102 lws_cose_validate_create_info_t vi;
103 struct lws_buflist *paybuf = NULL;
104 lws_cose_sign_create_info_t i;
105 struct lws_context *context;
106 size_t ks_len, kid_len = 0;
107 lws_cose_key_t *ck = NULL;
108 lws_dll2_owner_t *o, set;
109 lws_lec_pctx_t lec;
110 cose_param_t alg;
111 const char *p;
112
113 if ((p = lws_cmdline_option(argc, argv, "-d")))
114 logs = atoi(p);
115
116 lws_set_log_level(logs, NULL);
117
118 lwsl_user("LWS cose-sign example tool -k keyset [-s alg-name kid ]\n");
119
120 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
121 #if defined(LWS_WITH_NETWORK)
122 info.port = CONTEXT_PORT_NO_LISTEN;
123 #endif
124
125 context = lws_create_context(&info);
126 if (!context) {
127 lwsl_err("lws init failed\n");
128 return 1;
129 }
130
131 if ((p = lws_cmdline_option(argc, argv, "--stdin"))) {
132 fdin = open(p, LWS_O_RDONLY, 0);
133 if (fdin < 0) {
134 lwsl_err("%s: unable to open stdin file\n", __func__);
135 return 1;
136 }
137 }
138
139 if ((p = lws_cmdline_option(argc, argv, "--stdout"))) {
140 fdout = open(p, LWS_O_WRONLY | LWS_O_CREAT | LWS_O_TRUNC, 0600);
141 if (fdout < 0) {
142 lwsl_err("%s: unable to open stdout file\n", __func__);
143 goto bail_early;
144 }
145 }
146
147 /*
148 * If no tag, you can tell it the signature type, otherwise it will
149 * use the tag to select the right type without these
150 */
151
152 if (lws_cmdline_option(argc, argv, "--cose-sign"))
153 sigtype = SIGTYPE_MULTI;
154
155 if (lws_cmdline_option(argc, argv, "--cose-sign1"))
156 sigtype = SIGTYPE_SINGLE;
157
158 if (lws_cmdline_option(argc, argv, "--cose-mac"))
159 sigtype = SIGTYPE_MAC;
160
161 if (lws_cmdline_option(argc, argv, "--cose-mac0"))
162 sigtype = SIGTYPE_MAC0;
163
164 /* if signing, set the ciphers */
165
166 if (lws_cmdline_option(argc, argv, "-s"))
167 sign = 1;
168
169 if ((p = lws_cmdline_option(argc, argv, "--kid"))) {
170 kid = (uint8_t *)p;
171 kid_len = strlen(p);
172 //lwsl_hexdump_notice(kid, kid_len);
173 }
174
175 if ((p = lws_cmdline_option(argc, argv, "--kid-hex"))) {
176 kid_len = (size_t)lws_hex_to_byte_array(p, ktmp, sizeof(ktmp));
177 kid = (uint8_t *)ktmp;
178 }
179
180 if ((p = lws_cmdline_option(argc, argv, "--extra"))) {
181 ext_len = (size_t)lws_hex_to_byte_array(p, extra, sizeof(extra));
182 lwsl_notice("%llu\n", (unsigned long long)ext_len);
183 if (ext_len == (size_t)-1ll)
184 ext_len = 0;
185 }
186
187 /* grab the key */
188
189 if (!(p = lws_cmdline_option(argc, argv, "-k"))) {
190 lwsl_err("-k <key set file> is required\n");
191 goto bail;
192 }
193
194 if (_alloc_file(context, p, &ks, &ks_len)) {
195 lwsl_err("%s: unable to load %s\n", __func__, p);
196 goto bail;
197 }
198
199 lws_dll2_owner_clear(&set);
200 if (!lws_cose_key_import(&set, NULL, NULL, ks, ks_len)) {
201 lwsl_notice("%s: key import fail\n", __func__);
202 free(ks);
203 goto bail2;
204 }
205
206 free(ks);
207
208 if (!fdin) {
209 struct timeval timeout;
210 fd_set fds;
211
212 FD_ZERO(&fds);
213 FD_SET(0, &fds);
214
215 timeout.tv_sec = 0;
216 timeout.tv_usec = 1000;
217
218 if (select(fdin + 1, &fds, NULL, NULL, &timeout) < 0 ||
219 !FD_ISSET(0, &fds)) {
220 lwsl_err("%s: pass cose_sign or plaintext "
221 "on stdin or --stdin\n", __func__);
222 goto bail2;
223 }
224 }
225
226 if (sign) {
227 uint8_t *ppay;
228 size_t s;
229
230 p = lws_cmdline_option(argc, argv, "--alg");
231 if (!p) {
232 lwsl_err("%s: need to specify alg (eg, ES256) "
233 "when signing\n", __func__);
234 goto bail2;
235 }
236 alg = lws_cose_name_to_alg(p);
237
238 lws_lec_init(&lec, sbuf, sizeof(sbuf));
239 memset(&i, 0, sizeof(i));
240 i.cx = context;
241 i.keyset = &set;
242 i.lec = &lec;
243 i.flags = LCSC_FL_ADD_CBOR_TAG |
244 LCSC_FL_ADD_CBOR_PREFER_MAC0;
245 i.sigtype = sigtype;
246
247 /*
248 * Unfortunately, with COSE we must know the payload length
249 * before we have seen the payload. It's illegal to use
250 * indeterminite lengths inside COSE objects.
251 */
252
253 do {
254 n = (int)read(fdin, temp, sizeof(temp));
255 if (n < 0)
256 goto bail3;
257 if (!n)
258 break;
259
260 s = (size_t)n;
261
262 if (lws_buflist_append_segment(&paybuf, temp, s) < 0)
263 goto bail3;
264 i.inline_payload_len += s;
265
266 } while (1);
267
268 // lwsl_notice("%s: inline_payload_len %llu\n", __func__,
269 // (unsigned long long)i.inline_payload_len);
270
271 csc = lws_cose_sign_create(&i);
272 if (!csc)
273 goto bail2;
274 ck = lws_cose_key_from_set(&set, kid, kid_len);
275 if (!ck)
276 goto bail2;
277
278 if (lws_cose_sign_add(csc, alg, ck))
279 goto bail2;
280
281 do {
282 s = lws_buflist_next_segment_len(&paybuf, &ppay);
283 if (!s)
284 break;
285
286 do {
287 m = (int)lws_cose_sign_payload_chunk(csc,
288 ppay, s);
289 if (lec.used) {
290 // lwsl_hexdump_err(sbuf, lec.used);
291 write(fdout, sbuf, lec.used);
292 lws_lec_setbuf(&lec, sbuf, sizeof(sbuf));
293 }
294 } while (m == LCOSESIGEXTCB_RET_AGAIN);
295
296 if (m == LWS_LECPCTX_RET_FAIL)
297 goto bail2;
298
299 if (lec.used) {
300 write(fdout, sbuf, lec.used);
301 lws_lec_setbuf(&lec, sbuf, sizeof(sbuf));
302 }
303
304 lws_buflist_use_segment(&paybuf, s);
305 } while(1);
306
307 } else {
308 memset(&vi, 0, sizeof(vi));
309
310 vi.cx = context;
311 vi.keyset = &set;
312 vi.sigtype = sigtype;
313 vi.ext_cb = extra_cb;
314 vi.ext_opaque = extra;
315 vi.ext_len = ext_len;
316 vi.pay_cb = pay_cb;
317
318 cps = lws_cose_validate_create(&vi);
319 if (!cps) {
320 lwsl_notice("%s: sign_val_create fail\n", __func__);
321 goto bail;
322 }
323
324 do {
325 n = (int)read(fdin, temp, sizeof(temp));
326 if (n < 0)
327 goto bail3;
328 if (!n)
329 break;
330
331 n = lws_cose_validate_chunk(cps, temp, (size_t)n, NULL);
332 if (n && n != LECP_CONTINUE) {
333 lwsl_err("%s: chunk validation failed: %d\n",
334 __func__, n);
335 goto bail2;
336 }
337 } while (1);
338 }
339
340 bail3:
341
342 result = 0;
343
344 if (!sign) {
345 char buf[2048];
346 int os;
347
348 o = lws_cose_validate_results(cps);
349 if (!o)
350 result = 1;
351 else {
352 os = lws_snprintf(buf, sizeof(buf),
353 "\nresults count %d\n", o->count);
354 write(fdout, buf, (size_t)os);
355
356 if (!o->count)
357 result = 1;
358 }
359
360 lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
361 lws_dll2_get_head(o)) {
362 lws_cose_validate_res_t *res = lws_container_of(p,
363 lws_cose_validate_res_t, list);
364 char khr[256];
365
366 khr[0] = '\0';
367 if (res->cose_key) {
368 ke = &res->cose_key->meta[COSEKEY_META_KID];
369 if (ke && ke->buf)
370 lws_hex_from_byte_array(ke->buf, ke->len,
371 khr, sizeof(khr));
372 }
373 os = lws_snprintf(buf, sizeof(buf),
374 " result: %d (alg %s, kid %s)\n",
375 res->result,
376 lws_cose_alg_to_name(res->cose_alg), khr);
377 write(fdout, buf, (size_t)os);
378 result |= res->result;
379 } lws_end_foreach_dll_safe(p, tp);
380 }
381
382 bail2:
383 if (!sign)
384 lws_cose_validate_destroy(&cps);
385 else {
386 lws_buflist_destroy_all_segments(&paybuf);
387 lws_cose_sign_destroy(&csc);
388 }
389 //bail1:
390 lws_cose_key_set_destroy(&set);
391 bail:
392 lws_context_destroy(context);
393
394 if (result)
395 lwsl_err("%s: FAIL: %d\n", __func__, result);
396 else
397 lwsl_notice("%s: PASS\n", __func__);
398
399 bail_early:
400 if (fdin > 0)
401 close(fdin);
402 if (fdout != 1 && fdout >= 0)
403 close(fdout);
404
405 return result;
406 }
407