xref: /openbsd/usr.bin/ssh/ssh-sk.c (revision 274d7c50)
1 /* $OpenBSD: ssh-sk.c,v 1.16 2019/11/19 22:23:19 djm Exp $ */
2 /*
3  * Copyright (c) 2019 Google LLC
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* #define DEBUG_SK 1 */
19 
20 #include <dlfcn.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <stdio.h>
25 
26 #ifdef WITH_OPENSSL
27 #include <openssl/objects.h>
28 #include <openssl/ec.h>
29 #endif /* WITH_OPENSSL */
30 
31 #include "log.h"
32 #include "misc.h"
33 #include "sshbuf.h"
34 #include "sshkey.h"
35 #include "ssherr.h"
36 #include "digest.h"
37 
38 #include "ssh-sk.h"
39 #include "sk-api.h"
40 #include "crypto_api.h"
41 
42 struct sshsk_provider {
43 	char *path;
44 	void *dlhandle;
45 
46 	/* Return the version of the middleware API */
47 	uint32_t (*sk_api_version)(void);
48 
49 	/* Enroll a U2F key (private key generation) */
50 	int (*sk_enroll)(int alg, const uint8_t *challenge,
51 	    size_t challenge_len, const char *application, uint8_t flags,
52 	    struct sk_enroll_response **enroll_response);
53 
54 	/* Sign a challenge */
55 	int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
56 	    const char *application,
57 	    const uint8_t *key_handle, size_t key_handle_len,
58 	    uint8_t flags, struct sk_sign_response **sign_response);
59 };
60 
61 /* Built-in version */
62 int ssh_sk_enroll(int alg, const uint8_t *challenge,
63     size_t challenge_len, const char *application, uint8_t flags,
64     struct sk_enroll_response **enroll_response);
65 int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
66     const char *application,
67     const uint8_t *key_handle, size_t key_handle_len,
68     uint8_t flags, struct sk_sign_response **sign_response);
69 
70 static void
71 sshsk_free(struct sshsk_provider *p)
72 {
73 	if (p == NULL)
74 		return;
75 	free(p->path);
76 	if (p->dlhandle != NULL)
77 		dlclose(p->dlhandle);
78 	free(p);
79 }
80 
81 static struct sshsk_provider *
82 sshsk_open(const char *path)
83 {
84 	struct sshsk_provider *ret = NULL;
85 	uint32_t version;
86 
87 	if ((ret = calloc(1, sizeof(*ret))) == NULL) {
88 		error("%s: calloc failed", __func__);
89 		return NULL;
90 	}
91 	if ((ret->path = strdup(path)) == NULL) {
92 		error("%s: strdup failed", __func__);
93 		goto fail;
94 	}
95 	/* Skip the rest if we're using the linked in middleware */
96 	if (strcasecmp(ret->path, "internal") == 0) {
97 		ret->sk_enroll = ssh_sk_enroll;
98 		ret->sk_sign = ssh_sk_sign;
99 		return ret;
100 	}
101 	if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
102 		error("Security key provider %s dlopen failed: %s",
103 		    path, dlerror());
104 		goto fail;
105 	}
106 	if ((ret->sk_api_version = dlsym(ret->dlhandle,
107 	    "sk_api_version")) == NULL) {
108 		error("Security key provider %s dlsym(sk_api_version) "
109 		    "failed: %s", path, dlerror());
110 		goto fail;
111 	}
112 	version = ret->sk_api_version();
113 	debug("%s: provider %s implements version 0x%08lx", __func__,
114 	    ret->path, (u_long)version);
115 	if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
116 		error("Security key provider %s implements unsupported version "
117 		    "0x%08lx (supported: 0x%08lx)", path, (u_long)version,
118 		    (u_long)SSH_SK_VERSION_MAJOR);
119 		goto fail;
120 	}
121 	if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
122 		error("Security key  provider %s dlsym(sk_enroll) "
123 		    "failed: %s", path, dlerror());
124 		goto fail;
125 	}
126 	if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
127 		error("Security key provider %s dlsym(sk_sign) failed: %s",
128 		    path, dlerror());
129 		goto fail;
130 	}
131 	/* success */
132 	return ret;
133 fail:
134 	sshsk_free(ret);
135 	return NULL;
136 }
137 
138 static void
139 sshsk_free_enroll_response(struct sk_enroll_response *r)
140 {
141 	if (r == NULL)
142 		return;
143 	freezero(r->key_handle, r->key_handle_len);
144 	freezero(r->public_key, r->public_key_len);
145 	freezero(r->signature, r->signature_len);
146 	freezero(r->attestation_cert, r->attestation_cert_len);
147 	freezero(r, sizeof(*r));
148 };
149 
150 static void
151 sshsk_free_sign_response(struct sk_sign_response *r)
152 {
153 	if (r == NULL)
154 		return;
155 	freezero(r->sig_r, r->sig_r_len);
156 	freezero(r->sig_s, r->sig_s_len);
157 	freezero(r, sizeof(*r));
158 };
159 
160 #ifdef WITH_OPENSSL
161 /* Assemble key from response */
162 static int
163 sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
164 {
165 	struct sshkey *key = NULL;
166 	struct sshbuf *b = NULL;
167 	EC_POINT *q = NULL;
168 	int r;
169 
170 	*keyp = NULL;
171 	if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
172 		error("%s: sshkey_new failed", __func__);
173 		r = SSH_ERR_ALLOC_FAIL;
174 		goto out;
175 	}
176 	key->ecdsa_nid = NID_X9_62_prime256v1;
177 	if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
178 	    (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
179 	    (b = sshbuf_new()) == NULL) {
180 		error("%s: allocation failed", __func__);
181 		r = SSH_ERR_ALLOC_FAIL;
182 		goto out;
183 	}
184 	if ((r = sshbuf_put_string(b,
185 	    resp->public_key, resp->public_key_len)) != 0) {
186 		error("%s: buffer error: %s", __func__, ssh_err(r));
187 		goto out;
188 	}
189 	if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
190 		error("%s: parse key: %s", __func__, ssh_err(r));
191 		r = SSH_ERR_INVALID_FORMAT;
192 		goto out;
193 	}
194 	if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
195 		error("Security key returned invalid ECDSA key");
196 		r = SSH_ERR_KEY_INVALID_EC_VALUE;
197 		goto out;
198 	}
199 	if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
200 		/* XXX assume it is a allocation error */
201 		error("%s: allocation failed", __func__);
202 		r = SSH_ERR_ALLOC_FAIL;
203 		goto out;
204 	}
205 	/* success */
206 	*keyp = key;
207 	key = NULL; /* transferred */
208 	r = 0;
209  out:
210 	EC_POINT_free(q);
211 	sshkey_free(key);
212 	sshbuf_free(b);
213 	return r;
214 }
215 #endif /* WITH_OPENSSL */
216 
217 static int
218 sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
219 {
220 	struct sshkey *key = NULL;
221 	int r;
222 
223 	*keyp = NULL;
224 	if (resp->public_key_len != ED25519_PK_SZ) {
225 		error("%s: invalid size: %zu", __func__, resp->public_key_len);
226 		r = SSH_ERR_INVALID_FORMAT;
227 		goto out;
228 	}
229 	if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
230 		error("%s: sshkey_new failed", __func__);
231 		r = SSH_ERR_ALLOC_FAIL;
232 		goto out;
233 	}
234 	if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
235 		error("%s: malloc failed", __func__);
236 		r = SSH_ERR_ALLOC_FAIL;
237 		goto out;
238 	}
239 	memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
240 	/* success */
241 	*keyp = key;
242 	key = NULL; /* transferred */
243 	r = 0;
244  out:
245 	sshkey_free(key);
246 	return r;
247 }
248 
249 int
250 sshsk_enroll(int type, const char *provider_path, const char *application,
251     uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp,
252     struct sshbuf *attest)
253 {
254 	struct sshsk_provider *skp = NULL;
255 	struct sshkey *key = NULL;
256 	u_char randchall[32];
257 	const u_char *challenge;
258 	size_t challenge_len;
259 	struct sk_enroll_response *resp = NULL;
260 	int r = SSH_ERR_INTERNAL_ERROR;
261 	int alg;
262 
263 	debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, "
264 	    "challenge len %zu", __func__, provider_path, application,
265 	    flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf));
266 
267 	*keyp = NULL;
268 	if (attest)
269 		sshbuf_reset(attest);
270 	switch (type) {
271 #ifdef WITH_OPENSSL
272 	case KEY_ECDSA_SK:
273 		alg = SSH_SK_ECDSA;
274 		break;
275 #endif /* WITH_OPENSSL */
276 	case KEY_ED25519_SK:
277 		alg = SSH_SK_ED25519;
278 		break;
279 	default:
280 		error("%s: unsupported key type", __func__);
281 		r = SSH_ERR_INVALID_ARGUMENT;
282 		goto out;
283 	}
284 	if (provider_path == NULL) {
285 		error("%s: missing provider", __func__);
286 		r = SSH_ERR_INVALID_ARGUMENT;
287 		goto out;
288 	}
289 	if (application == NULL || *application == '\0') {
290 		error("%s: missing application", __func__);
291 		r = SSH_ERR_INVALID_ARGUMENT;
292 		goto out;
293 	}
294 	if (challenge_buf == NULL) {
295 		debug("%s: using random challenge", __func__);
296 		arc4random_buf(randchall, sizeof(randchall));
297 		challenge = randchall;
298 		challenge_len = sizeof(randchall);
299 	} else if (sshbuf_len(challenge_buf) == 0) {
300 		error("Missing enrollment challenge");
301 		r = SSH_ERR_INVALID_ARGUMENT;
302 		goto out;
303 	} else {
304 		challenge = sshbuf_ptr(challenge_buf);
305 		challenge_len = sshbuf_len(challenge_buf);
306 		debug3("%s: using explicit challenge len=%zd",
307 		    __func__, challenge_len);
308 	}
309 	if ((skp = sshsk_open(provider_path)) == NULL) {
310 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
311 		goto out;
312 	}
313 	/* XXX validate flags? */
314 	/* enroll key */
315 	if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
316 	    flags, &resp)) != 0) {
317 		error("Security key provider %s returned failure %d",
318 		    provider_path, r);
319 		r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */
320 		goto out;
321 	}
322 	/* Check response validity */
323 	if (resp->public_key == NULL || resp->key_handle == NULL ||
324 	    resp->signature == NULL ||
325 	    (resp->attestation_cert == NULL && resp->attestation_cert_len != 0)) {
326 		error("%s: sk_enroll response invalid", __func__);
327 		r = SSH_ERR_INVALID_FORMAT;
328 		goto out;
329 	}
330 	switch (type) {
331 #ifdef WITH_OPENSSL
332 	case KEY_ECDSA_SK:
333 		if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
334 			goto out;
335 		break;
336 #endif /* WITH_OPENSSL */
337 	case KEY_ED25519_SK:
338 		if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
339 			goto out;
340 		break;
341 	}
342 	key->sk_flags = flags;
343 	if ((key->sk_key_handle = sshbuf_new()) == NULL ||
344 	    (key->sk_reserved = sshbuf_new()) == NULL) {
345 		error("%s: allocation failed", __func__);
346 		r = SSH_ERR_ALLOC_FAIL;
347 		goto out;
348 	}
349 	if ((key->sk_application = strdup(application)) == NULL) {
350 		error("%s: strdup application failed", __func__);
351 		r = SSH_ERR_ALLOC_FAIL;
352 		goto out;
353 	}
354 	if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
355 	    resp->key_handle_len)) != 0) {
356 		error("%s: buffer error: %s", __func__, ssh_err(r));
357 		goto out;
358 	}
359 	/* Optionally fill in the attestation information */
360 	if (attest != NULL) {
361 		if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 ||
362 		    (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */
363 		    (r = sshbuf_put_string(attest,
364 		    resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
365 		    (r = sshbuf_put_string(attest,
366 		    resp->signature, resp->signature_len)) != 0 ||
367 		    (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */
368 		    (r = sshbuf_put_string(attest, NULL, 0)) != 0) {
369 			error("%s: buffer error: %s", __func__, ssh_err(r));
370 			goto out;
371 		}
372 	}
373 	/* success */
374 	*keyp = key;
375 	key = NULL; /* transferred */
376 	r = 0;
377  out:
378 	sshsk_free(skp);
379 	sshkey_free(key);
380 	sshsk_free_enroll_response(resp);
381 	explicit_bzero(randchall, sizeof(randchall));
382 	return r;
383 }
384 
385 #ifdef WITH_OPENSSL
386 static int
387 sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
388 {
389 	struct sshbuf *inner_sig = NULL;
390 	int r = SSH_ERR_INTERNAL_ERROR;
391 
392 	/* Check response validity */
393 	if (resp->sig_r == NULL || resp->sig_s == NULL) {
394 		error("%s: sk_sign response invalid", __func__);
395 		r = SSH_ERR_INVALID_FORMAT;
396 		goto out;
397 	}
398 	if ((inner_sig = sshbuf_new()) == NULL) {
399 		r = SSH_ERR_ALLOC_FAIL;
400 		goto out;
401 	}
402 	/* Prepare and append inner signature object */
403 	if ((r = sshbuf_put_bignum2_bytes(inner_sig,
404 	    resp->sig_r, resp->sig_r_len)) != 0 ||
405 	    (r = sshbuf_put_bignum2_bytes(inner_sig,
406 	    resp->sig_s, resp->sig_s_len)) != 0) {
407 		debug("%s: buffer error: %s", __func__, ssh_err(r));
408 		goto out;
409 	}
410 	if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
411 	    (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
412 	    (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
413 		debug("%s: buffer error: %s", __func__, ssh_err(r));
414 		goto out;
415 	}
416 #ifdef DEBUG_SK
417 	fprintf(stderr, "%s: sig_r:\n", __func__);
418 	sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
419 	fprintf(stderr, "%s: sig_s:\n", __func__);
420 	sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
421 	fprintf(stderr, "%s: inner:\n", __func__);
422 	sshbuf_dump(inner_sig, stderr);
423 #endif
424 	r = 0;
425  out:
426 	sshbuf_free(inner_sig);
427 	return r;
428 }
429 #endif /* WITH_OPENSSL */
430 
431 static int
432 sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
433 {
434 	int r = SSH_ERR_INTERNAL_ERROR;
435 
436 	/* Check response validity */
437 	if (resp->sig_r == NULL) {
438 		error("%s: sk_sign response invalid", __func__);
439 		r = SSH_ERR_INVALID_FORMAT;
440 		goto out;
441 	}
442 	if ((r = sshbuf_put_string(sig,
443 	    resp->sig_r, resp->sig_r_len)) != 0 ||
444 	    (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
445 	    (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
446 		debug("%s: buffer error: %s", __func__, ssh_err(r));
447 		goto out;
448 	}
449 #ifdef DEBUG_SK
450 	fprintf(stderr, "%s: sig_r:\n", __func__);
451 	sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
452 #endif
453 	r = 0;
454  out:
455 	return 0;
456 }
457 
458 int
459 sshsk_sign(const char *provider_path, const struct sshkey *key,
460     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
461     u_int compat)
462 {
463 	struct sshsk_provider *skp = NULL;
464 	int r = SSH_ERR_INTERNAL_ERROR;
465 	int type, alg;
466 	struct sk_sign_response *resp = NULL;
467 	struct sshbuf *inner_sig = NULL, *sig = NULL;
468 	uint8_t message[32];
469 
470 	debug("%s: provider \"%s\", key %s, flags 0x%02x", __func__,
471 	    provider_path, sshkey_type(key), key->sk_flags);
472 
473 	if (sigp != NULL)
474 		*sigp = NULL;
475 	if (lenp != NULL)
476 		*lenp = 0;
477 	type = sshkey_type_plain(key->type);
478 	switch (type) {
479 #ifdef WITH_OPENSSL
480 	case KEY_ECDSA_SK:
481 		alg = SSH_SK_ECDSA;
482 		break;
483 #endif /* WITH_OPENSSL */
484 	case KEY_ED25519_SK:
485 		alg = SSH_SK_ED25519;
486 		break;
487 	default:
488 		return SSH_ERR_INVALID_ARGUMENT;
489 	}
490 	if (provider_path == NULL ||
491 	    key->sk_key_handle == NULL ||
492 	    key->sk_application == NULL || *key->sk_application == '\0') {
493 		r = SSH_ERR_INVALID_ARGUMENT;
494 		goto out;
495 	}
496 	if ((skp = sshsk_open(provider_path)) == NULL) {
497 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
498 		goto out;
499 	}
500 
501 	/* hash data to be signed before it goes to the security key */
502 	if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
503 	    message, sizeof(message))) != 0) {
504 		error("%s: hash application failed: %s", __func__, ssh_err(r));
505 		r = SSH_ERR_INTERNAL_ERROR;
506 		goto out;
507 	}
508 	if ((r = skp->sk_sign(alg, message, sizeof(message),
509 	    key->sk_application,
510 	    sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
511 	    key->sk_flags, &resp)) != 0) {
512 		debug("%s: sk_sign failed with code %d", __func__, r);
513 		goto out;
514 	}
515 	/* Assemble signature */
516 	if ((sig = sshbuf_new()) == NULL) {
517 		r = SSH_ERR_ALLOC_FAIL;
518 		goto out;
519 	}
520 	if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
521 		debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
522 		goto out;
523 	}
524 	switch (type) {
525 #ifdef WITH_OPENSSL
526 	case KEY_ECDSA_SK:
527 		if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
528 			goto out;
529 		break;
530 #endif /* WITH_OPENSSL */
531 	case KEY_ED25519_SK:
532 		if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
533 			goto out;
534 		break;
535 	}
536 #ifdef DEBUG_SK
537 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
538 	    __func__, resp->flags, resp->counter);
539 	fprintf(stderr, "%s: hashed message:\n", __func__);
540 	sshbuf_dump_data(message, sizeof(message), stderr);
541 	fprintf(stderr, "%s: sigbuf:\n", __func__);
542 	sshbuf_dump(sig, stderr);
543 #endif
544 	if (sigp != NULL) {
545 		if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
546 			r = SSH_ERR_ALLOC_FAIL;
547 			goto out;
548 		}
549 		memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
550 	}
551 	if (lenp != NULL)
552 		*lenp = sshbuf_len(sig);
553 	/* success */
554 	r = 0;
555  out:
556 	explicit_bzero(message, sizeof(message));
557 	sshsk_free(skp);
558 	sshsk_free_sign_response(resp);
559 	sshbuf_free(sig);
560 	sshbuf_free(inner_sig);
561 	return r;
562 }
563