Coverage Report

Created: 2026-10-19 07:16

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