Coverage Report

Created: 2025-07-01 07:01

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