Coverage Report

Created: 2023-06-07 07:10

/src/openssh/ssh-sk.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: ssh-sk.c,v 1.39 2022/07/20 03:29:14 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 "includes.h"
21
22
#ifdef ENABLE_SK
23
24
#include <dlfcn.h>
25
#include <stddef.h>
26
#ifdef HAVE_STDINT_H
27
# include <stdint.h>
28
#endif
29
#include <string.h>
30
#include <stdio.h>
31
32
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
33
#include <openssl/objects.h>
34
#include <openssl/ec.h>
35
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
36
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"
46
#include "crypto_api.h"
47
48
/*
49
 * Almost every use of OpenSSL in this file is for ECDSA-NISTP256.
50
 * This is strictly a larger hammer than necessary, but it reduces changes
51
 * with upstream.
52
 */
53
#ifndef OPENSSL_HAS_ECC
54
# undef WITH_OPENSSL
55
#endif
56
57
struct sshsk_provider {
58
  char *path;
59
  void *dlhandle;
60
61
  /* Return the version of the middleware API */
62
  uint32_t (*sk_api_version)(void);
63
64
  /* Enroll a U2F key (private key generation) */
65
  int (*sk_enroll)(int alg, const uint8_t *challenge,
66
      size_t challenge_len, const char *application, uint8_t flags,
67
      const char *pin, struct sk_option **opts,
68
      struct sk_enroll_response **enroll_response);
69
70
  /* Sign a challenge */
71
  int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
72
      const char *application,
73
      const uint8_t *key_handle, size_t key_handle_len,
74
      uint8_t flags, const char *pin, struct sk_option **opts,
75
      struct sk_sign_response **sign_response);
76
77
  /* Enumerate resident keys */
78
  int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts,
79
      struct sk_resident_key ***rks, size_t *nrks);
80
};
81
82
/* Built-in version */
83
int ssh_sk_enroll(int alg, const uint8_t *challenge,
84
    size_t challenge_len, const char *application, uint8_t flags,
85
    const char *pin, struct sk_option **opts,
86
    struct sk_enroll_response **enroll_response);
87
int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
88
    const char *application,
89
    const uint8_t *key_handle, size_t key_handle_len,
90
    uint8_t flags, const char *pin, struct sk_option **opts,
91
    struct sk_sign_response **sign_response);
92
int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts,
93
    struct sk_resident_key ***rks, size_t *nrks);
94
95
static void
96
sshsk_free(struct sshsk_provider *p)
97
0
{
98
0
  if (p == NULL)
99
0
    return;
100
0
  free(p->path);
101
0
  if (p->dlhandle != NULL)
102
0
    dlclose(p->dlhandle);
103
0
  free(p);
104
0
}
105
106
static struct sshsk_provider *
107
sshsk_open(const char *path)
108
0
{
109
0
  struct sshsk_provider *ret = NULL;
110
0
  uint32_t version;
111
112
0
  if (path == NULL || *path == '\0') {
113
0
    error("No FIDO SecurityKeyProvider specified");
114
0
    return NULL;
115
0
  }
116
0
  if ((ret = calloc(1, sizeof(*ret))) == NULL) {
117
0
    error_f("calloc failed");
118
0
    return NULL;
119
0
  }
120
0
  if ((ret->path = strdup(path)) == NULL) {
121
0
    error_f("strdup failed");
122
0
    goto fail;
123
0
  }
124
  /* Skip the rest if we're using the linked in middleware */
125
0
  if (strcasecmp(ret->path, "internal") == 0) {
126
0
#ifdef ENABLE_SK_INTERNAL
127
0
    ret->sk_enroll = ssh_sk_enroll;
128
0
    ret->sk_sign = ssh_sk_sign;
129
0
    ret->sk_load_resident_keys = ssh_sk_load_resident_keys;
130
0
    return ret;
131
#else
132
    error("internal security key support not enabled");
133
    goto fail;
134
#endif
135
0
  }
136
0
  if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
137
0
    error("Provider \"%s\" dlopen failed: %s", path, dlerror());
138
0
    goto fail;
139
0
  }
140
0
  if ((ret->sk_api_version = dlsym(ret->dlhandle,
141
0
      "sk_api_version")) == NULL) {
142
0
    error("Provider \"%s\" dlsym(sk_api_version) failed: %s",
143
0
        path, dlerror());
144
0
    goto fail;
145
0
  }
146
0
  version = ret->sk_api_version();
147
0
  debug_f("provider %s implements version 0x%08lx", ret->path,
148
0
      (u_long)version);
149
0
  if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
150
0
    error("Provider \"%s\" implements unsupported "
151
0
        "version 0x%08lx (supported: 0x%08lx)",
152
0
        path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR);
153
0
    goto fail;
154
0
  }
155
0
  if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
156
0
    error("Provider %s dlsym(sk_enroll) failed: %s",
157
0
        path, dlerror());
158
0
    goto fail;
159
0
  }
160
0
  if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
161
0
    error("Provider \"%s\" dlsym(sk_sign) failed: %s",
162
0
        path, dlerror());
163
0
    goto fail;
164
0
  }
165
0
  if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
166
0
      "sk_load_resident_keys")) == NULL) {
167
0
    error("Provider \"%s\" dlsym(sk_load_resident_keys) "
168
0
        "failed: %s", path, dlerror());
169
0
    goto fail;
170
0
  }
171
  /* success */
172
0
  return ret;
173
0
fail:
174
0
  sshsk_free(ret);
175
0
  return NULL;
176
0
}
177
178
static void
179
sshsk_free_enroll_response(struct sk_enroll_response *r)
180
0
{
181
0
  if (r == NULL)
182
0
    return;
183
0
  freezero(r->key_handle, r->key_handle_len);
184
0
  freezero(r->public_key, r->public_key_len);
185
0
  freezero(r->signature, r->signature_len);
186
0
  freezero(r->attestation_cert, r->attestation_cert_len);
187
0
  freezero(r->authdata, r->authdata_len);
188
0
  freezero(r, sizeof(*r));
189
0
}
190
191
static void
192
sshsk_free_sign_response(struct sk_sign_response *r)
193
0
{
194
0
  if (r == NULL)
195
0
    return;
196
0
  freezero(r->sig_r, r->sig_r_len);
197
0
  freezero(r->sig_s, r->sig_s_len);
198
0
  freezero(r, sizeof(*r));
199
0
}
200
201
#ifdef WITH_OPENSSL
202
/* Assemble key from response */
203
static int
204
sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
205
0
{
206
0
  struct sshkey *key = NULL;
207
0
  struct sshbuf *b = NULL;
208
0
  EC_POINT *q = NULL;
209
0
  int r;
210
211
0
  *keyp = NULL;
212
0
  if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
213
0
    error_f("sshkey_new failed");
214
0
    r = SSH_ERR_ALLOC_FAIL;
215
0
    goto out;
216
0
  }
217
0
  key->ecdsa_nid = NID_X9_62_prime256v1;
218
0
  if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
219
0
      (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
220
0
      (b = sshbuf_new()) == NULL) {
221
0
    error_f("allocation failed");
222
0
    r = SSH_ERR_ALLOC_FAIL;
223
0
    goto out;
224
0
  }
225
0
  if ((r = sshbuf_put_string(b,
226
0
      resp->public_key, resp->public_key_len)) != 0) {
227
0
    error_fr(r, "sshbuf_put_string");
228
0
    goto out;
229
0
  }
230
0
  if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
231
0
    error_fr(r, "parse");
232
0
    r = SSH_ERR_INVALID_FORMAT;
233
0
    goto out;
234
0
  }
235
0
  if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
236
0
    error("Authenticator returned invalid ECDSA key");
237
0
    r = SSH_ERR_KEY_INVALID_EC_VALUE;
238
0
    goto out;
239
0
  }
240
0
  if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
241
    /* XXX assume it is a allocation error */
242
0
    error_f("allocation failed");
243
0
    r = SSH_ERR_ALLOC_FAIL;
244
0
    goto out;
245
0
  }
246
  /* success */
247
0
  *keyp = key;
248
0
  key = NULL; /* transferred */
249
0
  r = 0;
250
0
 out:
251
0
  EC_POINT_free(q);
252
0
  sshkey_free(key);
253
0
  sshbuf_free(b);
254
0
  return r;
255
0
}
256
#endif /* WITH_OPENSSL */
257
258
static int
259
sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
260
0
{
261
0
  struct sshkey *key = NULL;
262
0
  int r;
263
264
0
  *keyp = NULL;
265
0
  if (resp->public_key_len != ED25519_PK_SZ) {
266
0
    error_f("invalid size: %zu", resp->public_key_len);
267
0
    r = SSH_ERR_INVALID_FORMAT;
268
0
    goto out;
269
0
  }
270
0
  if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
271
0
    error_f("sshkey_new failed");
272
0
    r = SSH_ERR_ALLOC_FAIL;
273
0
    goto out;
274
0
  }
275
0
  if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
276
0
    error_f("malloc failed");
277
0
    r = SSH_ERR_ALLOC_FAIL;
278
0
    goto out;
279
0
  }
280
0
  memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
281
  /* success */
282
0
  *keyp = key;
283
0
  key = NULL; /* transferred */
284
0
  r = 0;
285
0
 out:
286
0
  sshkey_free(key);
287
0
  return r;
288
0
}
289
290
static int
291
sshsk_key_from_response(int alg, const char *application, uint8_t flags,
292
    struct sk_enroll_response *resp, struct sshkey **keyp)
293
0
{
294
0
  struct sshkey *key = NULL;
295
0
  int r = SSH_ERR_INTERNAL_ERROR;
296
297
0
  *keyp = NULL;
298
299
  /* Check response validity */
300
0
  if (resp->public_key == NULL || resp->key_handle == NULL) {
301
0
    error_f("sk_enroll response invalid");
302
0
    r = SSH_ERR_INVALID_FORMAT;
303
0
    goto out;
304
0
  }
305
0
  switch (alg) {
306
0
#ifdef WITH_OPENSSL
307
0
  case SSH_SK_ECDSA:
308
0
    if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
309
0
      goto out;
310
0
    break;
311
0
#endif /* WITH_OPENSSL */
312
0
  case SSH_SK_ED25519:
313
0
    if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
314
0
      goto out;
315
0
    break;
316
0
  default:
317
0
    error_f("unsupported algorithm %d", alg);
318
0
    r = SSH_ERR_INVALID_ARGUMENT;
319
0
    goto out;
320
0
  }
321
0
  key->sk_flags = flags;
322
0
  if ((key->sk_key_handle = sshbuf_new()) == NULL ||
323
0
      (key->sk_reserved = sshbuf_new()) == NULL) {
324
0
    error_f("allocation failed");
325
0
    r = SSH_ERR_ALLOC_FAIL;
326
0
    goto out;
327
0
  }
328
0
  if ((key->sk_application = strdup(application)) == NULL) {
329
0
    error_f("strdup application failed");
330
0
    r = SSH_ERR_ALLOC_FAIL;
331
0
    goto out;
332
0
  }
333
0
  if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
334
0
      resp->key_handle_len)) != 0) {
335
0
    error_fr(r, "put key handle");
336
0
    goto out;
337
0
  }
338
  /* success */
339
0
  r = 0;
340
0
  *keyp = key;
341
0
  key = NULL;
342
0
 out:
343
0
  sshkey_free(key);
344
0
  return r;
345
0
}
346
347
static int
348
skerr_to_ssherr(int skerr)
349
0
{
350
0
  switch (skerr) {
351
0
  case SSH_SK_ERR_UNSUPPORTED:
352
0
    return SSH_ERR_FEATURE_UNSUPPORTED;
353
0
  case SSH_SK_ERR_PIN_REQUIRED:
354
0
    return SSH_ERR_KEY_WRONG_PASSPHRASE;
355
0
  case SSH_SK_ERR_DEVICE_NOT_FOUND:
356
0
    return SSH_ERR_DEVICE_NOT_FOUND;
357
0
  case SSH_SK_ERR_CREDENTIAL_EXISTS:
358
0
    return SSH_ERR_KEY_BAD_PERMISSIONS;
359
0
  case SSH_SK_ERR_GENERAL:
360
0
  default:
361
0
    return SSH_ERR_INVALID_FORMAT;
362
0
  }
363
0
}
364
365
static void
366
sshsk_free_options(struct sk_option **opts)
367
0
{
368
0
  size_t i;
369
370
0
  if (opts == NULL)
371
0
    return;
372
0
  for (i = 0; opts[i] != NULL; i++) {
373
0
    free(opts[i]->name);
374
0
    free(opts[i]->value);
375
0
    free(opts[i]);
376
0
  }
377
0
  free(opts);
378
0
}
379
380
static int
381
sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
382
    const char *name, const char *value, uint8_t required)
383
0
{
384
0
  struct sk_option **opts = *optsp;
385
0
  size_t nopts = *noptsp;
386
387
0
  if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
388
0
      sizeof(*opts))) == NULL) {
389
0
    error_f("array alloc failed");
390
0
    return SSH_ERR_ALLOC_FAIL;
391
0
  }
392
0
  *optsp = opts;
393
0
  *noptsp = nopts + 1;
394
0
  if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
395
0
    error_f("alloc failed");
396
0
    return SSH_ERR_ALLOC_FAIL;
397
0
  }
398
0
  if ((opts[nopts]->name = strdup(name)) == NULL ||
399
0
      (opts[nopts]->value = strdup(value)) == NULL) {
400
0
    error_f("alloc failed");
401
0
    return SSH_ERR_ALLOC_FAIL;
402
0
  }
403
0
  opts[nopts]->required = required;
404
0
  return 0;
405
0
}
406
407
static int
408
make_options(const char *device, const char *user_id,
409
    struct sk_option ***optsp)
410
0
{
411
0
  struct sk_option **opts = NULL;
412
0
  size_t nopts = 0;
413
0
  int r, ret = SSH_ERR_INTERNAL_ERROR;
414
415
0
  if (device != NULL &&
416
0
      (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
417
0
    ret = r;
418
0
    goto out;
419
0
  }
420
0
  if (user_id != NULL &&
421
0
      (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
422
0
    ret = r;
423
0
    goto out;
424
0
  }
425
  /* success */
426
0
  *optsp = opts;
427
0
  opts = NULL;
428
0
  nopts = 0;
429
0
  ret = 0;
430
0
 out:
431
0
  sshsk_free_options(opts);
432
0
  return ret;
433
0
}
434
435
436
static int
437
fill_attestation_blob(const struct sk_enroll_response *resp,
438
    struct sshbuf *attest)
439
0
{
440
0
  int r;
441
442
0
  if (attest == NULL)
443
0
    return 0; /* nothing to do */
444
0
  if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 ||
445
0
      (r = sshbuf_put_string(attest,
446
0
      resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
447
0
      (r = sshbuf_put_string(attest,
448
0
      resp->signature, resp->signature_len)) != 0 ||
449
0
      (r = sshbuf_put_string(attest,
450
0
      resp->authdata, resp->authdata_len)) != 0 ||
451
0
      (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */
452
0
      (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) {
453
0
    error_fr(r, "compose");
454
0
    return r;
455
0
  }
456
  /* success */
457
0
  return 0;
458
0
}
459
460
int
461
sshsk_enroll(int type, const char *provider_path, const char *device,
462
    const char *application, const char *userid, uint8_t flags,
463
    const char *pin, struct sshbuf *challenge_buf,
464
    struct sshkey **keyp, struct sshbuf *attest)
465
0
{
466
0
  struct sshsk_provider *skp = NULL;
467
0
  struct sshkey *key = NULL;
468
0
  u_char randchall[32];
469
0
  const u_char *challenge;
470
0
  size_t challenge_len;
471
0
  struct sk_enroll_response *resp = NULL;
472
0
  struct sk_option **opts = NULL;
473
0
  int r = SSH_ERR_INTERNAL_ERROR;
474
0
  int alg;
475
476
0
  debug_f("provider \"%s\", device \"%s\", application \"%s\", "
477
0
      "userid \"%s\", flags 0x%02x, challenge len %zu%s",
478
0
      provider_path, device, application, userid, flags,
479
0
      challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
480
0
      (pin != NULL && *pin != '\0') ? " with-pin" : "");
481
482
0
  *keyp = NULL;
483
0
  if (attest)
484
0
    sshbuf_reset(attest);
485
486
0
  if ((r = make_options(device, userid, &opts)) != 0)
487
0
    goto out;
488
489
0
  switch (type) {
490
0
#ifdef WITH_OPENSSL
491
0
  case KEY_ECDSA_SK:
492
0
    alg = SSH_SK_ECDSA;
493
0
    break;
494
0
#endif /* WITH_OPENSSL */
495
0
  case KEY_ED25519_SK:
496
0
    alg = SSH_SK_ED25519;
497
0
    break;
498
0
  default:
499
0
    error_f("unsupported key type");
500
0
    r = SSH_ERR_INVALID_ARGUMENT;
501
0
    goto out;
502
0
  }
503
0
  if (provider_path == NULL) {
504
0
    error_f("missing provider");
505
0
    r = SSH_ERR_INVALID_ARGUMENT;
506
0
    goto out;
507
0
  }
508
0
  if (application == NULL || *application == '\0') {
509
0
    error_f("missing application");
510
0
    r = SSH_ERR_INVALID_ARGUMENT;
511
0
    goto out;
512
0
  }
513
0
  if (challenge_buf == NULL) {
514
0
    debug_f("using random challenge");
515
0
    arc4random_buf(randchall, sizeof(randchall));
516
0
    challenge = randchall;
517
0
    challenge_len = sizeof(randchall);
518
0
  } else if (sshbuf_len(challenge_buf) == 0) {
519
0
    error("Missing enrollment challenge");
520
0
    r = SSH_ERR_INVALID_ARGUMENT;
521
0
    goto out;
522
0
  } else {
523
0
    challenge = sshbuf_ptr(challenge_buf);
524
0
    challenge_len = sshbuf_len(challenge_buf);
525
0
    debug3_f("using explicit challenge len=%zd", challenge_len);
526
0
  }
527
0
  if ((skp = sshsk_open(provider_path)) == NULL) {
528
0
    r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
529
0
    goto out;
530
0
  }
531
  /* XXX validate flags? */
532
  /* enroll key */
533
0
  if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
534
0
      flags, pin, opts, &resp)) != 0) {
535
0
    debug_f("provider \"%s\" failure %d", provider_path, r);
536
0
    r = skerr_to_ssherr(r);
537
0
    goto out;
538
0
  }
539
540
0
  if ((r = sshsk_key_from_response(alg, application, resp->flags,
541
0
      resp, &key)) != 0)
542
0
    goto out;
543
544
  /* Optionally fill in the attestation information */
545
0
  if ((r = fill_attestation_blob(resp, attest)) != 0)
546
0
    goto out;
547
548
  /* success */
549
0
  *keyp = key;
550
0
  key = NULL; /* transferred */
551
0
  r = 0;
552
0
 out:
553
0
  sshsk_free_options(opts);
554
0
  sshsk_free(skp);
555
0
  sshkey_free(key);
556
0
  sshsk_free_enroll_response(resp);
557
0
  explicit_bzero(randchall, sizeof(randchall));
558
0
  return r;
559
0
}
560
561
#ifdef WITH_OPENSSL
562
static int
563
sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
564
0
{
565
0
  struct sshbuf *inner_sig = NULL;
566
0
  int r = SSH_ERR_INTERNAL_ERROR;
567
568
  /* Check response validity */
569
0
  if (resp->sig_r == NULL || resp->sig_s == NULL) {
570
0
    error_f("sk_sign response invalid");
571
0
    r = SSH_ERR_INVALID_FORMAT;
572
0
    goto out;
573
0
  }
574
0
  if ((inner_sig = sshbuf_new()) == NULL) {
575
0
    r = SSH_ERR_ALLOC_FAIL;
576
0
    goto out;
577
0
  }
578
  /* Prepare and append inner signature object */
579
0
  if ((r = sshbuf_put_bignum2_bytes(inner_sig,
580
0
      resp->sig_r, resp->sig_r_len)) != 0 ||
581
0
      (r = sshbuf_put_bignum2_bytes(inner_sig,
582
0
      resp->sig_s, resp->sig_s_len)) != 0) {
583
0
    error_fr(r, "compose inner");
584
0
    goto out;
585
0
  }
586
0
  if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
587
0
      (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
588
0
      (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
589
0
    error_fr(r, "compose");
590
0
    goto out;
591
0
  }
592
#ifdef DEBUG_SK
593
  fprintf(stderr, "%s: sig_r:\n", __func__);
594
  sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
595
  fprintf(stderr, "%s: sig_s:\n", __func__);
596
  sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
597
  fprintf(stderr, "%s: inner:\n", __func__);
598
  sshbuf_dump(inner_sig, stderr);
599
#endif
600
0
  r = 0;
601
0
 out:
602
0
  sshbuf_free(inner_sig);
603
0
  return r;
604
0
}
605
#endif /* WITH_OPENSSL */
606
607
static int
608
sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
609
0
{
610
0
  int r = SSH_ERR_INTERNAL_ERROR;
611
612
  /* Check response validity */
613
0
  if (resp->sig_r == NULL) {
614
0
    error_f("sk_sign response invalid");
615
0
    r = SSH_ERR_INVALID_FORMAT;
616
0
    goto out;
617
0
  }
618
0
  if ((r = sshbuf_put_string(sig,
619
0
      resp->sig_r, resp->sig_r_len)) != 0 ||
620
0
      (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
621
0
      (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
622
0
    error_fr(r, "compose");
623
0
    goto out;
624
0
  }
625
#ifdef DEBUG_SK
626
  fprintf(stderr, "%s: sig_r:\n", __func__);
627
  sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
628
#endif
629
0
  r = 0;
630
0
 out:
631
0
  return r;
632
0
}
633
634
int
635
sshsk_sign(const char *provider_path, struct sshkey *key,
636
    u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
637
    u_int compat, const char *pin)
638
0
{
639
0
  struct sshsk_provider *skp = NULL;
640
0
  int r = SSH_ERR_INTERNAL_ERROR;
641
0
  int type, alg;
642
0
  struct sk_sign_response *resp = NULL;
643
0
  struct sshbuf *inner_sig = NULL, *sig = NULL;
644
0
  struct sk_option **opts = NULL;
645
646
0
  debug_f("provider \"%s\", key %s, flags 0x%02x%s",
647
0
      provider_path, sshkey_type(key), key->sk_flags,
648
0
      (pin != NULL && *pin != '\0') ? " with-pin" : "");
649
650
0
  if (sigp != NULL)
651
0
    *sigp = NULL;
652
0
  if (lenp != NULL)
653
0
    *lenp = 0;
654
0
  type = sshkey_type_plain(key->type);
655
0
  switch (type) {
656
0
#ifdef WITH_OPENSSL
657
0
  case KEY_ECDSA_SK:
658
0
    alg = SSH_SK_ECDSA;
659
0
    break;
660
0
#endif /* WITH_OPENSSL */
661
0
  case KEY_ED25519_SK:
662
0
    alg = SSH_SK_ED25519;
663
0
    break;
664
0
  default:
665
0
    return SSH_ERR_INVALID_ARGUMENT;
666
0
  }
667
0
  if (provider_path == NULL ||
668
0
      key->sk_key_handle == NULL ||
669
0
      key->sk_application == NULL || *key->sk_application == '\0') {
670
0
    r = SSH_ERR_INVALID_ARGUMENT;
671
0
    goto out;
672
0
  }
673
0
  if ((skp = sshsk_open(provider_path)) == NULL) {
674
0
    r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
675
0
    goto out;
676
0
  }
677
#ifdef DEBUG_SK
678
  fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n",
679
      __func__, key->sk_flags, key->sk_application);
680
  fprintf(stderr, "%s: sk_key_handle:\n", __func__);
681
  sshbuf_dump(key->sk_key_handle, stderr);
682
#endif
683
0
  if ((r = skp->sk_sign(alg, data, datalen, key->sk_application,
684
0
      sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
685
0
      key->sk_flags, pin, opts, &resp)) != 0) {
686
0
    debug_f("sk_sign failed with code %d", r);
687
0
    r = skerr_to_ssherr(r);
688
0
    goto out;
689
0
  }
690
  /* Assemble signature */
691
0
  if ((sig = sshbuf_new()) == NULL) {
692
0
    r = SSH_ERR_ALLOC_FAIL;
693
0
    goto out;
694
0
  }
695
0
  if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
696
0
    error_fr(r, "compose outer");
697
0
    goto out;
698
0
  }
699
0
  switch (type) {
700
0
#ifdef WITH_OPENSSL
701
0
  case KEY_ECDSA_SK:
702
0
    if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
703
0
      goto out;
704
0
    break;
705
0
#endif /* WITH_OPENSSL */
706
0
  case KEY_ED25519_SK:
707
0
    if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
708
0
      goto out;
709
0
    break;
710
0
  }
711
#ifdef DEBUG_SK
712
  fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
713
      __func__, resp->flags, resp->counter);
714
  fprintf(stderr, "%s: data to sign:\n", __func__);
715
  sshbuf_dump_data(data, datalen, stderr);
716
  fprintf(stderr, "%s: sigbuf:\n", __func__);
717
  sshbuf_dump(sig, stderr);
718
#endif
719
0
  if (sigp != NULL) {
720
0
    if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
721
0
      r = SSH_ERR_ALLOC_FAIL;
722
0
      goto out;
723
0
    }
724
0
    memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
725
0
  }
726
0
  if (lenp != NULL)
727
0
    *lenp = sshbuf_len(sig);
728
  /* success */
729
0
  r = 0;
730
0
 out:
731
0
  sshsk_free_options(opts);
732
0
  sshsk_free(skp);
733
0
  sshsk_free_sign_response(resp);
734
0
  sshbuf_free(sig);
735
0
  sshbuf_free(inner_sig);
736
0
  return r;
737
0
}
738
739
static void
740
sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
741
0
{
742
0
  size_t i;
743
744
0
  if (nrks == 0 || rks == NULL)
745
0
    return;
746
0
  for (i = 0; i < nrks; i++) {
747
0
    free(rks[i]->application);
748
0
    freezero(rks[i]->user_id, rks[i]->user_id_len);
749
0
    freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
750
0
    freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
751
0
    freezero(rks[i]->key.signature, rks[i]->key.signature_len);
752
0
    freezero(rks[i]->key.attestation_cert,
753
0
        rks[i]->key.attestation_cert_len);
754
0
    freezero(rks[i], sizeof(**rks));
755
0
  }
756
0
  free(rks);
757
0
}
758
759
static void
760
sshsk_free_resident_key(struct sshsk_resident_key *srk)
761
0
{
762
0
  if (srk == NULL)
763
0
    return;
764
0
  sshkey_free(srk->key);
765
0
  freezero(srk->user_id, srk->user_id_len);
766
0
  free(srk);
767
0
}
768
769
770
void
771
sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
772
0
{
773
0
  size_t i;
774
775
0
  if (srks == NULL || nsrks == 0)
776
0
    return;
777
778
0
  for (i = 0; i < nsrks; i++)
779
0
    sshsk_free_resident_key(srks[i]);
780
0
  free(srks);
781
0
}
782
783
int
784
sshsk_load_resident(const char *provider_path, const char *device,
785
    const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
786
    size_t *nsrksp)
787
0
{
788
0
  struct sshsk_provider *skp = NULL;
789
0
  int r = SSH_ERR_INTERNAL_ERROR;
790
0
  struct sk_resident_key **rks = NULL;
791
0
  size_t i, nrks = 0, nsrks = 0;
792
0
  struct sshkey *key = NULL;
793
0
  struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
794
0
  uint8_t sk_flags;
795
0
  struct sk_option **opts = NULL;
796
797
0
  debug_f("provider \"%s\"%s", provider_path,
798
0
      (pin != NULL && *pin != '\0') ? ", have-pin": "");
799
800
0
  if (srksp == NULL || nsrksp == NULL)
801
0
    return SSH_ERR_INVALID_ARGUMENT;
802
0
  *srksp = NULL;
803
0
  *nsrksp = 0;
804
805
0
  if ((r = make_options(device, NULL, &opts)) != 0)
806
0
    goto out;
807
0
  if ((skp = sshsk_open(provider_path)) == NULL) {
808
0
    r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
809
0
    goto out;
810
0
  }
811
0
  if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
812
0
    error("Provider \"%s\" returned failure %d", provider_path, r);
813
0
    r = skerr_to_ssherr(r);
814
0
    goto out;
815
0
  }
816
0
  for (i = 0; i < nrks; i++) {
817
0
    debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu",
818
0
        i, rks[i]->slot, rks[i]->alg, rks[i]->application,
819
0
        rks[i]->user_id_len);
820
    /* XXX need better filter here */
821
0
    if (strncmp(rks[i]->application, "ssh:", 4) != 0)
822
0
      continue;
823
0
    switch (rks[i]->alg) {
824
0
    case SSH_SK_ECDSA:
825
0
    case SSH_SK_ED25519:
826
0
      break;
827
0
    default:
828
0
      continue;
829
0
    }
830
0
    sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
831
0
    if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD))
832
0
      sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
833
0
    if ((r = sshsk_key_from_response(rks[i]->alg,
834
0
        rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0)
835
0
      goto out;
836
0
    if ((srk = calloc(1, sizeof(*srk))) == NULL) {
837
0
      error_f("calloc failed");
838
0
      r = SSH_ERR_ALLOC_FAIL;
839
0
      goto out;
840
0
    }
841
0
    srk->key = key;
842
0
    key = NULL; /* transferred */
843
0
    if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) {
844
0
      error_f("calloc failed");
845
0
      r = SSH_ERR_ALLOC_FAIL;
846
0
      goto out;
847
0
    }
848
0
    memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len);
849
0
    srk->user_id_len = rks[i]->user_id_len;
850
0
    if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
851
0
        sizeof(*tmp))) == NULL) {
852
0
      error_f("recallocarray failed");
853
0
      r = SSH_ERR_ALLOC_FAIL;
854
0
      goto out;
855
0
    }
856
0
    srks = tmp;
857
0
    srks[nsrks++] = srk;
858
0
    srk = NULL;
859
    /* XXX synthesise comment */
860
0
  }
861
  /* success */
862
0
  *srksp = srks;
863
0
  *nsrksp = nsrks;
864
0
  srks = NULL;
865
0
  nsrks = 0;
866
0
  r = 0;
867
0
 out:
868
0
  sshsk_free_options(opts);
869
0
  sshsk_free(skp);
870
0
  sshsk_free_sk_resident_keys(rks, nrks);
871
0
  sshkey_free(key);
872
0
  sshsk_free_resident_key(srk);
873
0
  sshsk_free_resident_keys(srks, nsrks);
874
0
  return r;
875
0
}
876
877
#endif /* ENABLE_SK */