• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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