1/*
2 * Copyright (c) 2019 Markus Friedl
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <stdint.h>
18#include <stdlib.h>
19#include <string.h>
20#include <stdio.h>
21#include <stddef.h>
22#include <stdarg.h>
23#include <sha2.h>
24
25#include "crypto_api.h"
26#include "sk-api.h"
27
28#include <openssl/opensslv.h>
29#include <openssl/crypto.h>
30#include <openssl/evp.h>
31#include <openssl/bn.h>
32#include <openssl/ec.h>
33#include <openssl/ecdsa.h>
34#include <openssl/pem.h>
35
36/* #define SK_DEBUG 1 */
37
38#if SSH_SK_VERSION_MAJOR != 0x000a0000
39# error SK API has changed, sk-dummy.c needs an update
40#endif
41
42#ifdef SK_DUMMY_INTEGRATE
43# define sk_api_version		ssh_sk_api_version
44# define sk_enroll		ssh_sk_enroll
45# define sk_sign		ssh_sk_sign
46# define sk_load_resident_keys	ssh_sk_load_resident_keys
47#endif /* !SK_STANDALONE */
48
49static void skdebug(const char *func, const char *fmt, ...)
50    __attribute__((__format__ (printf, 2, 3)));
51
52static void
53skdebug(const char *func, const char *fmt, ...)
54{
55#if defined(SK_DEBUG)
56	va_list ap;
57
58	va_start(ap, fmt);
59	fprintf(stderr, "sk-dummy %s: ", func);
60	vfprintf(stderr, fmt, ap);
61	fputc('\n', stderr);
62	va_end(ap);
63#else
64	(void)func; /* XXX */
65	(void)fmt; /* XXX */
66#endif
67}
68
69uint32_t
70sk_api_version(void)
71{
72	return SSH_SK_VERSION_MAJOR;
73}
74
75static int
76pack_key_ecdsa(struct sk_enroll_response *response)
77{
78	EC_KEY *key = NULL;
79	const EC_GROUP *g;
80	const EC_POINT *q;
81	int ret = -1;
82	long privlen;
83	BIO *bio = NULL;
84	char *privptr;
85
86	response->public_key = NULL;
87	response->public_key_len = 0;
88	response->key_handle = NULL;
89	response->key_handle_len = 0;
90
91	if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
92		skdebug(__func__, "EC_KEY_new_by_curve_name");
93		goto out;
94	}
95	if (EC_KEY_generate_key(key) != 1) {
96		skdebug(__func__, "EC_KEY_generate_key");
97		goto out;
98	}
99	EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
100	if ((bio = BIO_new(BIO_s_mem())) == NULL ||
101	    (g = EC_KEY_get0_group(key)) == NULL ||
102	    (q = EC_KEY_get0_public_key(key)) == NULL) {
103		skdebug(__func__, "couldn't get key parameters");
104		goto out;
105	}
106	response->public_key_len = EC_POINT_point2oct(g, q,
107	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
108	if (response->public_key_len == 0 || response->public_key_len > 2048) {
109		skdebug(__func__, "bad pubkey length %zu",
110		    response->public_key_len);
111		goto out;
112	}
113	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
114		skdebug(__func__, "malloc pubkey failed");
115		goto out;
116	}
117	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
118	    response->public_key, response->public_key_len, NULL) == 0) {
119		skdebug(__func__, "EC_POINT_point2oct failed");
120		goto out;
121	}
122	/* Key handle contains PEM encoded private key */
123	if (!PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) {
124		skdebug(__func__, "PEM_write_bio_ECPrivateKey failed");
125		goto out;
126	}
127	if ((privlen = BIO_get_mem_data(bio, &privptr)) <= 0) {
128		skdebug(__func__, "BIO_get_mem_data failed");
129		goto out;
130	}
131	if ((response->key_handle = malloc(privlen)) == NULL) {
132		skdebug(__func__, "malloc key_handle failed");
133		goto out;
134	}
135	response->key_handle_len = (size_t)privlen;
136	memcpy(response->key_handle, privptr, response->key_handle_len);
137	/* success */
138	ret = 0;
139 out:
140	if (ret != 0) {
141		if (response->public_key != NULL) {
142			memset(response->public_key, 0,
143			    response->public_key_len);
144			free(response->public_key);
145			response->public_key = NULL;
146		}
147		if (response->key_handle != NULL) {
148			memset(response->key_handle, 0,
149			    response->key_handle_len);
150			free(response->key_handle);
151			response->key_handle = NULL;
152		}
153	}
154	BIO_free(bio);
155	EC_KEY_free(key);
156	return ret;
157}
158
159static int
160pack_key_ed25519(struct sk_enroll_response *response)
161{
162	int ret = -1;
163	u_char pk[crypto_sign_ed25519_PUBLICKEYBYTES];
164	u_char sk[crypto_sign_ed25519_SECRETKEYBYTES];
165
166	response->public_key = NULL;
167	response->public_key_len = 0;
168	response->key_handle = NULL;
169	response->key_handle_len = 0;
170
171	memset(pk, 0, sizeof(pk));
172	memset(sk, 0, sizeof(sk));
173	crypto_sign_ed25519_keypair(pk, sk);
174
175	response->public_key_len = sizeof(pk);
176	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
177		skdebug(__func__, "malloc pubkey failed");
178		goto out;
179	}
180	memcpy(response->public_key, pk, sizeof(pk));
181	/* Key handle contains sk */
182	response->key_handle_len = sizeof(sk);
183	if ((response->key_handle = malloc(response->key_handle_len)) == NULL) {
184		skdebug(__func__, "malloc key_handle failed");
185		goto out;
186	}
187	memcpy(response->key_handle, sk, sizeof(sk));
188	/* success */
189	ret = 0;
190 out:
191	if (ret != 0)
192		free(response->public_key);
193	return ret;
194}
195
196static int
197check_options(struct sk_option **options)
198{
199	size_t i;
200
201	if (options == NULL)
202		return 0;
203	for (i = 0; options[i] != NULL; i++) {
204		skdebug(__func__, "requested unsupported option %s",
205		    options[i]->name);
206		if (options[i]->required) {
207			skdebug(__func__, "unknown required option");
208			return -1;
209		}
210	}
211	return 0;
212}
213
214int
215sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
216    const char *application, uint8_t flags, const char *pin,
217    struct sk_option **options, struct sk_enroll_response **enroll_response)
218{
219	struct sk_enroll_response *response = NULL;
220	int ret = SSH_SK_ERR_GENERAL;
221
222	(void)flags; /* XXX; unused */
223
224	if (enroll_response == NULL) {
225		skdebug(__func__, "enroll_response == NULL");
226		goto out;
227	}
228	*enroll_response = NULL;
229	if (check_options(options) != 0)
230		goto out; /* error already logged */
231	if ((response = calloc(1, sizeof(*response))) == NULL) {
232		skdebug(__func__, "calloc response failed");
233		goto out;
234	}
235	response->flags = flags;
236	switch(alg) {
237	case SSH_SK_ECDSA:
238		if (pack_key_ecdsa(response) != 0)
239			goto out;
240		break;
241	case SSH_SK_ED25519:
242		if (pack_key_ed25519(response) != 0)
243			goto out;
244		break;
245	default:
246		skdebug(__func__, "unsupported key type %d", alg);
247		return -1;
248	}
249	/* Have to return something here */
250	if ((response->signature = calloc(1, 1)) == NULL) {
251		skdebug(__func__, "calloc signature failed");
252		goto out;
253	}
254	response->signature_len = 0;
255
256	*enroll_response = response;
257	response = NULL;
258	ret = 0;
259 out:
260	if (response != NULL) {
261		free(response->public_key);
262		free(response->key_handle);
263		free(response->signature);
264		free(response->attestation_cert);
265		free(response);
266	}
267	return ret;
268}
269
270static void
271dump(const char *preamble, const void *sv, size_t l)
272{
273#ifdef SK_DEBUG
274	const u_char *s = (const u_char *)sv;
275	size_t i;
276
277	fprintf(stderr, "%s (len %zu):\n", preamble, l);
278	for (i = 0; i < l; i++) {
279		if (i % 16 == 0)
280			fprintf(stderr, "%04zu: ", i);
281		fprintf(stderr, "%02x", s[i]);
282		if (i % 16 == 15 || i == l - 1)
283			fprintf(stderr, "\n");
284	}
285#endif
286}
287
288static int
289sig_ecdsa(const uint8_t *message, size_t message_len,
290    const char *application, uint32_t counter, uint8_t flags,
291    const uint8_t *key_handle, size_t key_handle_len,
292    struct sk_sign_response *response)
293{
294	ECDSA_SIG *sig = NULL;
295	const BIGNUM *sig_r, *sig_s;
296	int ret = -1;
297	BIO *bio = NULL;
298	EVP_PKEY *pk = NULL;
299	EC_KEY *ec = NULL;
300	SHA2_CTX ctx;
301	uint8_t	apphash[SHA256_DIGEST_LENGTH];
302	uint8_t	sighash[SHA256_DIGEST_LENGTH];
303	uint8_t countbuf[4];
304
305	/* Decode EC_KEY from key handle */
306	if ((bio = BIO_new(BIO_s_mem())) == NULL ||
307	    BIO_write(bio, key_handle, key_handle_len) != (int)key_handle_len) {
308		skdebug(__func__, "BIO setup failed");
309		goto out;
310	}
311	if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, "")) == NULL) {
312		skdebug(__func__, "PEM_read_bio_PrivateKey failed");
313		goto out;
314	}
315	if (EVP_PKEY_base_id(pk) != EVP_PKEY_EC) {
316		skdebug(__func__, "Not an EC key: %d", EVP_PKEY_base_id(pk));
317		goto out;
318	}
319	if ((ec = EVP_PKEY_get1_EC_KEY(pk)) == NULL) {
320		skdebug(__func__, "EVP_PKEY_get1_EC_KEY failed");
321		goto out;
322	}
323	/* Expect message to be pre-hashed */
324	if (message_len != SHA256_DIGEST_LENGTH) {
325		skdebug(__func__, "bad message len %zu", message_len);
326		goto out;
327	}
328	/* Prepare data to be signed */
329	dump("message", message, message_len);
330	SHA256Init(&ctx);
331	SHA256Update(&ctx, (const u_char *)application, strlen(application));
332	SHA256Final(apphash, &ctx);
333	dump("apphash", apphash, sizeof(apphash));
334	countbuf[0] = (counter >> 24) & 0xff;
335	countbuf[1] = (counter >> 16) & 0xff;
336	countbuf[2] = (counter >> 8) & 0xff;
337	countbuf[3] = counter & 0xff;
338	dump("countbuf", countbuf, sizeof(countbuf));
339	dump("flags", &flags, sizeof(flags));
340	SHA256Init(&ctx);
341	SHA256Update(&ctx, apphash, sizeof(apphash));
342	SHA256Update(&ctx, &flags, sizeof(flags));
343	SHA256Update(&ctx, countbuf, sizeof(countbuf));
344	SHA256Update(&ctx, message, message_len);
345	SHA256Final(sighash, &ctx);
346	dump("sighash", sighash, sizeof(sighash));
347	/* create and encode signature */
348	if ((sig = ECDSA_do_sign(sighash, sizeof(sighash), ec)) == NULL) {
349		skdebug(__func__, "ECDSA_do_sign failed");
350		goto out;
351	}
352	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
353	response->sig_r_len = BN_num_bytes(sig_r);
354	response->sig_s_len = BN_num_bytes(sig_s);
355	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
356	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
357		skdebug(__func__, "calloc signature failed");
358		goto out;
359	}
360	BN_bn2bin(sig_r, response->sig_r);
361	BN_bn2bin(sig_s, response->sig_s);
362	ret = 0;
363 out:
364	explicit_bzero(&ctx, sizeof(ctx));
365	explicit_bzero(&apphash, sizeof(apphash));
366	explicit_bzero(&sighash, sizeof(sighash));
367	ECDSA_SIG_free(sig);
368	if (ret != 0) {
369		free(response->sig_r);
370		free(response->sig_s);
371		response->sig_r = NULL;
372		response->sig_s = NULL;
373	}
374	BIO_free(bio);
375	EC_KEY_free(ec);
376	EVP_PKEY_free(pk);
377	return ret;
378}
379
380static int
381sig_ed25519(const uint8_t *message, size_t message_len,
382    const char *application, uint32_t counter, uint8_t flags,
383    const uint8_t *key_handle, size_t key_handle_len,
384    struct sk_sign_response *response)
385{
386	size_t o;
387	int ret = -1;
388	SHA2_CTX ctx;
389	uint8_t	apphash[SHA256_DIGEST_LENGTH];
390	uint8_t signbuf[sizeof(apphash) + sizeof(flags) +
391	    sizeof(counter) + SHA256_DIGEST_LENGTH];
392	uint8_t sig[crypto_sign_ed25519_BYTES + sizeof(signbuf)];
393	unsigned long long smlen;
394
395	if (key_handle_len != crypto_sign_ed25519_SECRETKEYBYTES) {
396		skdebug(__func__, "bad key handle length %zu", key_handle_len);
397		goto out;
398	}
399	/* Expect message to be pre-hashed */
400	if (message_len != SHA256_DIGEST_LENGTH) {
401		skdebug(__func__, "bad message len %zu", message_len);
402		goto out;
403	}
404	/* Prepare data to be signed */
405	dump("message", message, message_len);
406	SHA256Init(&ctx);
407	SHA256Update(&ctx, (const u_char *)application, strlen(application));
408	SHA256Final(apphash, &ctx);
409	dump("apphash", apphash, sizeof(apphash));
410
411	memcpy(signbuf, apphash, sizeof(apphash));
412	o = sizeof(apphash);
413	signbuf[o++] = flags;
414	signbuf[o++] = (counter >> 24) & 0xff;
415	signbuf[o++] = (counter >> 16) & 0xff;
416	signbuf[o++] = (counter >> 8) & 0xff;
417	signbuf[o++] = counter & 0xff;
418	memcpy(signbuf + o, message, message_len);
419	o += message_len;
420	if (o != sizeof(signbuf)) {
421		skdebug(__func__, "bad sign buf len %zu, expected %zu",
422		    o, sizeof(signbuf));
423		goto out;
424	}
425	dump("signbuf", signbuf, sizeof(signbuf));
426	/* create and encode signature */
427	smlen = sizeof(signbuf);
428	if (crypto_sign_ed25519(sig, &smlen, signbuf, sizeof(signbuf),
429	    key_handle) != 0) {
430		skdebug(__func__, "crypto_sign_ed25519 failed");
431		goto out;
432	}
433	if (smlen <= sizeof(signbuf)) {
434		skdebug(__func__, "bad sign smlen %llu, expected min %zu",
435		    smlen, sizeof(signbuf) + 1);
436		goto out;
437	}
438	response->sig_r_len = (size_t)(smlen - sizeof(signbuf));
439	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
440		skdebug(__func__, "calloc signature failed");
441		goto out;
442	}
443	memcpy(response->sig_r, sig, response->sig_r_len);
444	dump("sig_r", response->sig_r, response->sig_r_len);
445	ret = 0;
446 out:
447	explicit_bzero(&ctx, sizeof(ctx));
448	explicit_bzero(&apphash, sizeof(apphash));
449	explicit_bzero(&signbuf, sizeof(signbuf));
450	explicit_bzero(&sig, sizeof(sig));
451	if (ret != 0) {
452		free(response->sig_r);
453		response->sig_r = NULL;
454	}
455	return ret;
456}
457
458int
459sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
460    const char *application, const uint8_t *key_handle, size_t key_handle_len,
461    uint8_t flags, const char *pin, struct sk_option **options,
462    struct sk_sign_response **sign_response)
463{
464	struct sk_sign_response *response = NULL;
465	int ret = SSH_SK_ERR_GENERAL;
466	SHA2_CTX ctx;
467	uint8_t message[32];
468
469	if (sign_response == NULL) {
470		skdebug(__func__, "sign_response == NULL");
471		goto out;
472	}
473	*sign_response = NULL;
474	if (check_options(options) != 0)
475		goto out; /* error already logged */
476	if ((response = calloc(1, sizeof(*response))) == NULL) {
477		skdebug(__func__, "calloc response failed");
478		goto out;
479	}
480	SHA256Init(&ctx);
481	SHA256Update(&ctx, data, datalen);
482	SHA256Final(message, &ctx);
483	response->flags = flags;
484	response->counter = 0x12345678;
485	switch(alg) {
486	case SSH_SK_ECDSA:
487		if (sig_ecdsa(message, sizeof(message), application,
488		    response->counter, flags, key_handle, key_handle_len,
489		    response) != 0)
490			goto out;
491		break;
492	case SSH_SK_ED25519:
493		if (sig_ed25519(message, sizeof(message), application,
494		    response->counter, flags, key_handle, key_handle_len,
495		    response) != 0)
496			goto out;
497		break;
498	default:
499		skdebug(__func__, "unsupported key type %d", alg);
500		return -1;
501	}
502	*sign_response = response;
503	response = NULL;
504	ret = 0;
505 out:
506	explicit_bzero(message, sizeof(message));
507	if (response != NULL) {
508		free(response->sig_r);
509		free(response->sig_s);
510		free(response);
511	}
512	return ret;
513}
514
515int
516sk_load_resident_keys(const char *pin, struct sk_option **options,
517    struct sk_resident_key ***rks, size_t *nrks)
518{
519	return SSH_SK_ERR_UNSUPPORTED;
520}
521