Coverage Report

Created: 2025-10-13 06:57

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