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