blob: 3f5eed62d22835f782ea213e76b80a0d608e67e6 [file] [log] [blame]
djm@openbsd.orgc312ca02020-01-06 02:00:46 +00001/* $OpenBSD: ssh-sk.c,v 1.24 2020/01/06 02:00:47 djm Exp $ */
djm@openbsd.orged3467c2019-10-31 21:16:20 +00002/*
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 "includes.h"
21
Damien Miller764d51e2019-11-01 13:34:49 +110022#ifdef ENABLE_SK
23
djm@openbsd.orged3467c2019-10-31 21:16:20 +000024#include <dlfcn.h>
25#include <stddef.h>
Darren Tucker429170f2020-01-14 14:41:47 +110026#ifdef HAVE_STDINT_H
27# include <stdint.h>
28#endif
djm@openbsd.orged3467c2019-10-31 21:16:20 +000029#include <string.h>
30#include <stdio.h>
31
naddy@openbsd.org723a5362019-11-18 16:08:57 +000032#ifdef WITH_OPENSSL
djm@openbsd.orged3467c2019-10-31 21:16:20 +000033#include <openssl/objects.h>
34#include <openssl/ec.h>
naddy@openbsd.org723a5362019-11-18 16:08:57 +000035#endif /* WITH_OPENSSL */
djm@openbsd.orged3467c2019-10-31 21:16:20 +000036
37#include "log.h"
38#include "misc.h"
39#include "sshbuf.h"
40#include "sshkey.h"
41#include "ssherr.h"
42#include "digest.h"
43
44#include "ssh-sk.h"
45#include "sk-api.h"
markus@openbsd.org7c32b512019-11-12 19:31:45 +000046#include "crypto_api.h"
djm@openbsd.orged3467c2019-10-31 21:16:20 +000047
48struct sshsk_provider {
49 char *path;
50 void *dlhandle;
51
52 /* Return the version of the middleware API */
53 uint32_t (*sk_api_version)(void);
54
55 /* Enroll a U2F key (private key generation) */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +000056 int (*sk_enroll)(int alg, const uint8_t *challenge,
57 size_t challenge_len, const char *application, uint8_t flags,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000058 const char *pin, struct sk_option **opts,
59 struct sk_enroll_response **enroll_response);
djm@openbsd.orged3467c2019-10-31 21:16:20 +000060
61 /* Sign a challenge */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +000062 int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
djm@openbsd.orged3467c2019-10-31 21:16:20 +000063 const char *application,
64 const uint8_t *key_handle, size_t key_handle_len,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000065 uint8_t flags, const char *pin, struct sk_option **opts,
djm@openbsd.orgc54cd182019-12-30 09:23:28 +000066 struct sk_sign_response **sign_response);
djm@openbsd.org14cea362019-12-30 09:21:16 +000067
68 /* Enumerate resident keys */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000069 int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts,
djm@openbsd.org14cea362019-12-30 09:21:16 +000070 struct sk_resident_key ***rks, size_t *nrks);
djm@openbsd.orged3467c2019-10-31 21:16:20 +000071};
72
djm@openbsd.org6bff9522019-11-14 21:27:29 +000073/* Built-in version */
74int ssh_sk_enroll(int alg, const uint8_t *challenge,
75 size_t challenge_len, const char *application, uint8_t flags,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000076 const char *pin, struct sk_option **opts,
77 struct sk_enroll_response **enroll_response);
djm@openbsd.org6bff9522019-11-14 21:27:29 +000078int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
79 const char *application,
80 const uint8_t *key_handle, size_t key_handle_len,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000081 uint8_t flags, const char *pin, struct sk_option **opts,
82 struct sk_sign_response **sign_response);
83int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts,
djm@openbsd.org14cea362019-12-30 09:21:16 +000084 struct sk_resident_key ***rks, size_t *nrks);
djm@openbsd.org6bff9522019-11-14 21:27:29 +000085
djm@openbsd.orged3467c2019-10-31 21:16:20 +000086static void
87sshsk_free(struct sshsk_provider *p)
88{
89 if (p == NULL)
90 return;
91 free(p->path);
92 if (p->dlhandle != NULL)
93 dlclose(p->dlhandle);
94 free(p);
95}
96
97static struct sshsk_provider *
98sshsk_open(const char *path)
99{
100 struct sshsk_provider *ret = NULL;
101 uint32_t version;
102
103 if ((ret = calloc(1, sizeof(*ret))) == NULL) {
104 error("%s: calloc failed", __func__);
105 return NULL;
106 }
107 if ((ret->path = strdup(path)) == NULL) {
108 error("%s: strdup failed", __func__);
109 goto fail;
110 }
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000111 /* Skip the rest if we're using the linked in middleware */
112 if (strcasecmp(ret->path, "internal") == 0) {
113#ifdef ENABLE_SK_INTERNAL
114 ret->sk_enroll = ssh_sk_enroll;
115 ret->sk_sign = ssh_sk_sign;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000116 ret->sk_load_resident_keys = ssh_sk_load_resident_keys;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000117#else
118 error("internal security key support not enabled");
119#endif
120 return ret;
121 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000122 if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000123 error("Security key provider \"%s\" dlopen failed: %s",
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000124 path, dlerror());
125 goto fail;
126 }
127 if ((ret->sk_api_version = dlsym(ret->dlhandle,
128 "sk_api_version")) == NULL) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000129 error("Security key provider \"%s\" dlsym(sk_api_version) "
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000130 "failed: %s", path, dlerror());
131 goto fail;
132 }
133 version = ret->sk_api_version();
134 debug("%s: provider %s implements version 0x%08lx", __func__,
135 ret->path, (u_long)version);
136 if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000137 error("Security key provider \"%s\" implements unsupported "
138 "version 0x%08lx (supported: 0x%08lx)",
139 path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000140 goto fail;
141 }
142 if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
143 error("Security key provider %s dlsym(sk_enroll) "
144 "failed: %s", path, dlerror());
145 goto fail;
146 }
147 if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000148 error("Security key provider \"%s\" dlsym(sk_sign) failed: %s",
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000149 path, dlerror());
150 goto fail;
151 }
djm@openbsd.org14cea362019-12-30 09:21:16 +0000152 if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
153 "sk_load_resident_keys")) == NULL) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000154 error("Security key provider \"%s\" "
155 "dlsym(sk_load_resident_keys) failed: %s", path, dlerror());
djm@openbsd.org14cea362019-12-30 09:21:16 +0000156 goto fail;
157 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000158 /* success */
159 return ret;
160fail:
161 sshsk_free(ret);
162 return NULL;
163}
164
165static void
166sshsk_free_enroll_response(struct sk_enroll_response *r)
167{
168 if (r == NULL)
169 return;
170 freezero(r->key_handle, r->key_handle_len);
171 freezero(r->public_key, r->public_key_len);
172 freezero(r->signature, r->signature_len);
173 freezero(r->attestation_cert, r->attestation_cert_len);
174 freezero(r, sizeof(*r));
djm@openbsd.orgd8b28382019-11-27 22:32:11 +0000175}
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000176
177static void
178sshsk_free_sign_response(struct sk_sign_response *r)
179{
180 if (r == NULL)
181 return;
182 freezero(r->sig_r, r->sig_r_len);
183 freezero(r->sig_s, r->sig_s_len);
184 freezero(r, sizeof(*r));
djm@openbsd.orgd8b28382019-11-27 22:32:11 +0000185}
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000186
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000187#ifdef WITH_OPENSSL
markus@openbsd.orgcef84a02019-11-12 19:29:54 +0000188/* Assemble key from response */
189static int
190sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
191{
192 struct sshkey *key = NULL;
193 struct sshbuf *b = NULL;
194 EC_POINT *q = NULL;
195 int r;
196
197 *keyp = NULL;
198 if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
199 error("%s: sshkey_new failed", __func__);
200 r = SSH_ERR_ALLOC_FAIL;
201 goto out;
202 }
203 key->ecdsa_nid = NID_X9_62_prime256v1;
204 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
205 (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
206 (b = sshbuf_new()) == NULL) {
207 error("%s: allocation failed", __func__);
208 r = SSH_ERR_ALLOC_FAIL;
209 goto out;
210 }
211 if ((r = sshbuf_put_string(b,
212 resp->public_key, resp->public_key_len)) != 0) {
213 error("%s: buffer error: %s", __func__, ssh_err(r));
214 goto out;
215 }
216 if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
217 error("%s: parse key: %s", __func__, ssh_err(r));
218 r = SSH_ERR_INVALID_FORMAT;
219 goto out;
220 }
221 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
222 error("Security key returned invalid ECDSA key");
223 r = SSH_ERR_KEY_INVALID_EC_VALUE;
224 goto out;
225 }
226 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
227 /* XXX assume it is a allocation error */
228 error("%s: allocation failed", __func__);
229 r = SSH_ERR_ALLOC_FAIL;
230 goto out;
231 }
232 /* success */
233 *keyp = key;
234 key = NULL; /* transferred */
235 r = 0;
236 out:
237 EC_POINT_free(q);
238 sshkey_free(key);
239 sshbuf_free(b);
240 return r;
241}
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000242#endif /* WITH_OPENSSL */
markus@openbsd.orgcef84a02019-11-12 19:29:54 +0000243
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000244static int
245sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
246{
247 struct sshkey *key = NULL;
248 int r;
249
250 *keyp = NULL;
251 if (resp->public_key_len != ED25519_PK_SZ) {
252 error("%s: invalid size: %zu", __func__, resp->public_key_len);
253 r = SSH_ERR_INVALID_FORMAT;
254 goto out;
255 }
256 if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
257 error("%s: sshkey_new failed", __func__);
258 r = SSH_ERR_ALLOC_FAIL;
259 goto out;
260 }
261 if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
262 error("%s: malloc failed", __func__);
263 r = SSH_ERR_ALLOC_FAIL;
264 goto out;
265 }
266 memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
267 /* success */
268 *keyp = key;
269 key = NULL; /* transferred */
270 r = 0;
271 out:
272 sshkey_free(key);
273 return r;
274}
275
djm@openbsd.org2fe05fc2019-12-30 09:20:36 +0000276static int
277sshsk_key_from_response(int alg, const char *application, uint8_t flags,
278 struct sk_enroll_response *resp, struct sshkey **keyp)
279{
280 struct sshkey *key = NULL;
281 int r = SSH_ERR_INTERNAL_ERROR;
282
283 *keyp = NULL;
284
285 /* Check response validity */
djm@openbsd.org14cea362019-12-30 09:21:16 +0000286 if (resp->public_key == NULL || resp->key_handle == NULL) {
djm@openbsd.org2fe05fc2019-12-30 09:20:36 +0000287 error("%s: sk_enroll response invalid", __func__);
288 r = SSH_ERR_INVALID_FORMAT;
289 goto out;
290 }
291 switch (alg) {
292#ifdef WITH_OPENSSL
293 case SSH_SK_ECDSA:
294 if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
295 goto out;
296 break;
297#endif /* WITH_OPENSSL */
298 case SSH_SK_ED25519:
299 if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
300 goto out;
301 break;
302 default:
303 error("%s: unsupported algorithm %d", __func__, alg);
304 r = SSH_ERR_INVALID_ARGUMENT;
305 goto out;
306 }
307 key->sk_flags = flags;
308 if ((key->sk_key_handle = sshbuf_new()) == NULL ||
309 (key->sk_reserved = sshbuf_new()) == NULL) {
310 error("%s: allocation failed", __func__);
311 r = SSH_ERR_ALLOC_FAIL;
312 goto out;
313 }
314 if ((key->sk_application = strdup(application)) == NULL) {
315 error("%s: strdup application failed", __func__);
316 r = SSH_ERR_ALLOC_FAIL;
317 goto out;
318 }
319 if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
320 resp->key_handle_len)) != 0) {
321 error("%s: buffer error: %s", __func__, ssh_err(r));
322 goto out;
323 }
324 /* success */
325 r = 0;
326 *keyp = key;
327 key = NULL;
328 out:
329 sshkey_free(key);
330 return r;
331}
332
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000333static int
334skerr_to_ssherr(int skerr)
335{
336 switch (skerr) {
337 case SSH_SK_ERR_UNSUPPORTED:
338 return SSH_ERR_FEATURE_UNSUPPORTED;
339 case SSH_SK_ERR_PIN_REQUIRED:
340 return SSH_ERR_KEY_WRONG_PASSPHRASE;
341 case SSH_SK_ERR_GENERAL:
342 default:
343 return SSH_ERR_INVALID_FORMAT;
344 }
345}
346
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000347static void
348sshsk_free_options(struct sk_option **opts)
349{
350 size_t i;
351
352 if (opts == NULL)
353 return;
354 for (i = 0; opts[i] != NULL; i++) {
355 free(opts[i]->name);
356 free(opts[i]->value);
357 free(opts[i]);
358 }
359 free(opts);
360}
361
362static int
363sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
364 const char *name, const char *value, uint8_t required)
365{
366 struct sk_option **opts = *optsp;
367 size_t nopts = *noptsp;
368
369 if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
370 sizeof(*opts))) == NULL) {
371 error("%s: array alloc failed", __func__);
372 return SSH_ERR_ALLOC_FAIL;
373 }
374 *optsp = opts;
375 *noptsp = nopts + 1;
376 if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
377 error("%s: alloc failed", __func__);
378 return SSH_ERR_ALLOC_FAIL;
379 }
380 if ((opts[nopts]->name = strdup(name)) == NULL ||
381 (opts[nopts]->value = strdup(value)) == NULL) {
382 error("%s: alloc failed", __func__);
383 return SSH_ERR_ALLOC_FAIL;
384 }
385 opts[nopts]->required = required;
386 return 0;
387}
388
389static int
390make_options(const char *device, const char *user_id,
391 struct sk_option ***optsp)
392{
393 struct sk_option **opts = NULL;
394 size_t nopts = 0;
395 int r, ret = SSH_ERR_INTERNAL_ERROR;
396
397 if (device != NULL &&
398 (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
399 ret = r;
400 goto out;
401 }
402 if (user_id != NULL &&
403 (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
404 ret = r;
405 goto out;
406 }
407 /* success */
408 *optsp = opts;
409 opts = NULL;
410 nopts = 0;
411 ret = 0;
412 out:
413 sshsk_free_options(opts);
414 return ret;
415}
416
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000417int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000418sshsk_enroll(int type, const char *provider_path, const char *device,
419 const char *application, const char *userid, uint8_t flags,
420 const char *pin, struct sshbuf *challenge_buf,
djm@openbsd.orgc54cd182019-12-30 09:23:28 +0000421 struct sshkey **keyp, struct sshbuf *attest)
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000422{
423 struct sshsk_provider *skp = NULL;
424 struct sshkey *key = NULL;
425 u_char randchall[32];
426 const u_char *challenge;
427 size_t challenge_len;
428 struct sk_enroll_response *resp = NULL;
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000429 struct sk_option **opts = NULL;
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000430 int r = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000431 int alg;
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000432
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000433 debug("%s: provider \"%s\", device \"%s\", application \"%s\", "
434 "userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__,
435 provider_path, device, application, userid, flags,
436 challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
djm@openbsd.orgc54cd182019-12-30 09:23:28 +0000437 (pin != NULL && *pin != '\0') ? " with-pin" : "");
djm@openbsd.org4103a3e2019-11-16 22:42:30 +0000438
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000439 *keyp = NULL;
440 if (attest)
441 sshbuf_reset(attest);
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000442
443 if ((r = make_options(device, userid, &opts)) != 0)
444 goto out;
445
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000446 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000447#ifdef WITH_OPENSSL
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000448 case KEY_ECDSA_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000449 alg = SSH_SK_ECDSA;
450 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000451#endif /* WITH_OPENSSL */
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000452 case KEY_ED25519_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000453 alg = SSH_SK_ED25519;
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000454 break;
455 default:
456 error("%s: unsupported key type", __func__);
457 r = SSH_ERR_INVALID_ARGUMENT;
458 goto out;
459 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000460 if (provider_path == NULL) {
461 error("%s: missing provider", __func__);
462 r = SSH_ERR_INVALID_ARGUMENT;
463 goto out;
464 }
465 if (application == NULL || *application == '\0') {
466 error("%s: missing application", __func__);
467 r = SSH_ERR_INVALID_ARGUMENT;
468 goto out;
469 }
470 if (challenge_buf == NULL) {
471 debug("%s: using random challenge", __func__);
472 arc4random_buf(randchall, sizeof(randchall));
473 challenge = randchall;
474 challenge_len = sizeof(randchall);
475 } else if (sshbuf_len(challenge_buf) == 0) {
476 error("Missing enrollment challenge");
477 r = SSH_ERR_INVALID_ARGUMENT;
478 goto out;
479 } else {
480 challenge = sshbuf_ptr(challenge_buf);
481 challenge_len = sshbuf_len(challenge_buf);
482 debug3("%s: using explicit challenge len=%zd",
483 __func__, challenge_len);
484 }
485 if ((skp = sshsk_open(provider_path)) == NULL) {
486 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
487 goto out;
488 }
489 /* XXX validate flags? */
490 /* enroll key */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000491 if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000492 flags, pin, opts, &resp)) != 0) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000493 error("Security key provider \"%s\" returned failure %d",
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000494 provider_path, r);
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000495 r = skerr_to_ssherr(r);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000496 goto out;
497 }
djm@openbsd.org2fe05fc2019-12-30 09:20:36 +0000498
499 if ((r = sshsk_key_from_response(alg, application, flags,
500 resp, &key)) != 0)
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000501 goto out;
djm@openbsd.org2fe05fc2019-12-30 09:20:36 +0000502
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000503 /* Optionally fill in the attestation information */
504 if (attest != NULL) {
505 if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 ||
506 (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */
507 (r = sshbuf_put_string(attest,
508 resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
509 (r = sshbuf_put_string(attest,
510 resp->signature, resp->signature_len)) != 0 ||
511 (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */
512 (r = sshbuf_put_string(attest, NULL, 0)) != 0) {
513 error("%s: buffer error: %s", __func__, ssh_err(r));
514 goto out;
515 }
516 }
517 /* success */
518 *keyp = key;
519 key = NULL; /* transferred */
520 r = 0;
521 out:
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000522 sshsk_free_options(opts);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000523 sshsk_free(skp);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000524 sshkey_free(key);
525 sshsk_free_enroll_response(resp);
526 explicit_bzero(randchall, sizeof(randchall));
527 return r;
528}
529
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000530#ifdef WITH_OPENSSL
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000531static int
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000532sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000533{
534 struct sshbuf *inner_sig = NULL;
535 int r = SSH_ERR_INTERNAL_ERROR;
536
markus@openbsd.org3fcf69a2019-11-12 19:34:00 +0000537 /* Check response validity */
markus@openbsd.orgdffd02e2019-11-13 20:25:45 +0000538 if (resp->sig_r == NULL || resp->sig_s == NULL) {
markus@openbsd.org3fcf69a2019-11-12 19:34:00 +0000539 error("%s: sk_sign response invalid", __func__);
540 r = SSH_ERR_INVALID_FORMAT;
541 goto out;
542 }
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000543 if ((inner_sig = sshbuf_new()) == NULL) {
544 r = SSH_ERR_ALLOC_FAIL;
545 goto out;
546 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000547 /* Prepare and append inner signature object */
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000548 if ((r = sshbuf_put_bignum2_bytes(inner_sig,
549 resp->sig_r, resp->sig_r_len)) != 0 ||
550 (r = sshbuf_put_bignum2_bytes(inner_sig,
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000551 resp->sig_s, resp->sig_s_len)) != 0) {
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000552 debug("%s: buffer error: %s", __func__, ssh_err(r));
553 goto out;
554 }
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000555 if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
556 (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
557 (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000558 debug("%s: buffer error: %s", __func__, ssh_err(r));
559 goto out;
560 }
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000561#ifdef DEBUG_SK
562 fprintf(stderr, "%s: sig_r:\n", __func__);
563 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
564 fprintf(stderr, "%s: sig_s:\n", __func__);
565 sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000566 fprintf(stderr, "%s: inner:\n", __func__);
567 sshbuf_dump(inner_sig, stderr);
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000568#endif
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000569 r = 0;
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000570 out:
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000571 sshbuf_free(inner_sig);
572 return r;
573}
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000574#endif /* WITH_OPENSSL */
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000575
576static int
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000577sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000578{
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000579 int r = SSH_ERR_INTERNAL_ERROR;
580
markus@openbsd.org3fcf69a2019-11-12 19:34:00 +0000581 /* Check response validity */
582 if (resp->sig_r == NULL) {
583 error("%s: sk_sign response invalid", __func__);
584 r = SSH_ERR_INVALID_FORMAT;
585 goto out;
586 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000587 if ((r = sshbuf_put_string(sig,
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000588 resp->sig_r, resp->sig_r_len)) != 0 ||
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000589 (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
590 (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000591 debug("%s: buffer error: %s", __func__, ssh_err(r));
592 goto out;
593 }
594#ifdef DEBUG_SK
595 fprintf(stderr, "%s: sig_r:\n", __func__);
596 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000597#endif
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000598 r = 0;
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000599 out:
600 return 0;
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000601}
602
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000603int
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +0000604sshsk_sign(const char *provider_path, struct sshkey *key,
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000605 u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
djm@openbsd.orgc54cd182019-12-30 09:23:28 +0000606 u_int compat, const char *pin)
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000607{
608 struct sshsk_provider *skp = NULL;
609 int r = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000610 int type, alg;
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000611 struct sk_sign_response *resp = NULL;
612 struct sshbuf *inner_sig = NULL, *sig = NULL;
613 uint8_t message[32];
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000614 struct sk_option **opts = NULL;
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000615
djm@openbsd.orgc54cd182019-12-30 09:23:28 +0000616 debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__,
617 provider_path, sshkey_type(key), key->sk_flags,
618 (pin != NULL && *pin != '\0') ? " with-pin" : "");
djm@openbsd.org4103a3e2019-11-16 22:42:30 +0000619
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000620 if (sigp != NULL)
621 *sigp = NULL;
622 if (lenp != NULL)
623 *lenp = 0;
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000624 type = sshkey_type_plain(key->type);
625 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000626#ifdef WITH_OPENSSL
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000627 case KEY_ECDSA_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000628 alg = SSH_SK_ECDSA;
629 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000630#endif /* WITH_OPENSSL */
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000631 case KEY_ED25519_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000632 alg = SSH_SK_ED25519;
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000633 break;
634 default:
635 return SSH_ERR_INVALID_ARGUMENT;
636 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000637 if (provider_path == NULL ||
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000638 key->sk_key_handle == NULL ||
639 key->sk_application == NULL || *key->sk_application == '\0') {
640 r = SSH_ERR_INVALID_ARGUMENT;
641 goto out;
642 }
643 if ((skp = sshsk_open(provider_path)) == NULL) {
644 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
645 goto out;
646 }
647
648 /* hash data to be signed before it goes to the security key */
649 if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
650 message, sizeof(message))) != 0) {
651 error("%s: hash application failed: %s", __func__, ssh_err(r));
652 r = SSH_ERR_INTERNAL_ERROR;
653 goto out;
654 }
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000655 if ((r = skp->sk_sign(alg, message, sizeof(message),
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000656 key->sk_application,
657 sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000658 key->sk_flags, pin, opts, &resp)) != 0) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000659 debug("%s: sk_sign failed with code %d", __func__, r);
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000660 r = skerr_to_ssherr(r);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000661 goto out;
662 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000663 /* Assemble signature */
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000664 if ((sig = sshbuf_new()) == NULL) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000665 r = SSH_ERR_ALLOC_FAIL;
666 goto out;
667 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000668 if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000669 debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
670 goto out;
671 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000672 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000673#ifdef WITH_OPENSSL
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000674 case KEY_ECDSA_SK:
675 if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
676 goto out;
677 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000678#endif /* WITH_OPENSSL */
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000679 case KEY_ED25519_SK:
680 if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
681 goto out;
682 break;
683 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000684#ifdef DEBUG_SK
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000685 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
686 __func__, resp->flags, resp->counter);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000687 fprintf(stderr, "%s: hashed message:\n", __func__);
688 sshbuf_dump_data(message, sizeof(message), stderr);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000689 fprintf(stderr, "%s: sigbuf:\n", __func__);
690 sshbuf_dump(sig, stderr);
691#endif
692 if (sigp != NULL) {
693 if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
694 r = SSH_ERR_ALLOC_FAIL;
695 goto out;
696 }
697 memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
698 }
699 if (lenp != NULL)
700 *lenp = sshbuf_len(sig);
701 /* success */
702 r = 0;
703 out:
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000704 sshsk_free_options(opts);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000705 explicit_bzero(message, sizeof(message));
706 sshsk_free(skp);
707 sshsk_free_sign_response(resp);
708 sshbuf_free(sig);
709 sshbuf_free(inner_sig);
710 return r;
711}
djm@openbsd.org14cea362019-12-30 09:21:16 +0000712
713static void
714sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
715{
716 size_t i;
717
718 if (nrks == 0 || rks == NULL)
719 return;
720 for (i = 0; i < nrks; i++) {
721 free(rks[i]->application);
722 freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
723 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
724 freezero(rks[i]->key.signature, rks[i]->key.signature_len);
725 freezero(rks[i]->key.attestation_cert,
726 rks[i]->key.attestation_cert_len);
727 freezero(rks[i], sizeof(**rks));
728 }
729 free(rks);
730}
731
732int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000733sshsk_load_resident(const char *provider_path, const char *device,
734 const char *pin, struct sshkey ***keysp, size_t *nkeysp)
djm@openbsd.org14cea362019-12-30 09:21:16 +0000735{
736 struct sshsk_provider *skp = NULL;
737 int r = SSH_ERR_INTERNAL_ERROR;
738 struct sk_resident_key **rks = NULL;
739 size_t i, nrks = 0, nkeys = 0;
740 struct sshkey *key = NULL, **keys = NULL, **tmp;
741 uint8_t flags;
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000742 struct sk_option **opts = NULL;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000743
744 debug("%s: provider \"%s\"%s", __func__, provider_path,
745 (pin != NULL && *pin != '\0') ? ", have-pin": "");
746
747 if (keysp == NULL || nkeysp == NULL)
748 return SSH_ERR_INVALID_ARGUMENT;
749 *keysp = NULL;
750 *nkeysp = 0;
751
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000752 if ((r = make_options(device, NULL, &opts)) != 0)
753 goto out;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000754 if ((skp = sshsk_open(provider_path)) == NULL) {
755 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
756 goto out;
757 }
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000758 if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
djm@openbsd.orgd4335962019-12-30 09:24:03 +0000759 error("Security key provider \"%s\" returned failure %d",
djm@openbsd.org14cea362019-12-30 09:21:16 +0000760 provider_path, r);
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000761 r = skerr_to_ssherr(r);
djm@openbsd.org14cea362019-12-30 09:21:16 +0000762 goto out;
763 }
764 for (i = 0; i < nrks; i++) {
765 debug3("%s: rk %zu: slot = %zu, alg = %d, application = \"%s\"",
766 __func__, i, rks[i]->slot, rks[i]->alg,
767 rks[i]->application);
768 /* XXX need better filter here */
769 if (strncmp(rks[i]->application, "ssh:", 4) != 0)
770 continue;
771 switch (rks[i]->alg) {
772 case SSH_SK_ECDSA:
773 case SSH_SK_ED25519:
774 break;
775 default:
776 continue;
777 }
778 /* XXX where to get flags? */
779 flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
780 if ((r = sshsk_key_from_response(rks[i]->alg,
781 rks[i]->application, flags, &rks[i]->key, &key)) != 0)
782 goto out;
783 if ((tmp = recallocarray(keys, nkeys, nkeys + 1,
784 sizeof(*tmp))) == NULL) {
785 error("%s: recallocarray failed", __func__);
786 r = SSH_ERR_ALLOC_FAIL;
787 goto out;
788 }
789 keys = tmp;
790 keys[nkeys++] = key;
791 key = NULL;
792 /* XXX synthesise comment */
793 }
794 /* success */
795 *keysp = keys;
796 *nkeysp = nkeys;
797 keys = NULL;
798 nkeys = 0;
799 r = 0;
800 out:
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000801 sshsk_free_options(opts);
djm@openbsd.org14cea362019-12-30 09:21:16 +0000802 sshsk_free(skp);
803 sshsk_free_sk_resident_keys(rks, nrks);
804 sshkey_free(key);
805 if (nkeys != 0) {
806 for (i = 0; i < nkeys; i++)
807 sshkey_free(keys[i]);
808 free(keys);
809 }
810 return r;
811}
812
Damien Miller764d51e2019-11-01 13:34:49 +1100813#endif /* ENABLE_SK */