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