Coverage Report

Created: 2026-01-09 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssh/regress/misc/sk-dummy/sk-dummy.c
Line
Count
Source
1
/* $OpenBSD: sk-dummy.c,v 1.16 2025/06/17 01:24:32 djm Exp $ */
2
/*
3
 * Copyright (c) 2019 Markus Friedl
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
#include "includes.h"
19
20
#include <stdint.h>
21
#include <stdlib.h>
22
#include <string.h>
23
#include <stdio.h>
24
#include <stddef.h>
25
#include <stdarg.h>
26
27
#include "crypto_api.h"
28
#include "sk-api.h"
29
30
#ifdef WITH_OPENSSL
31
#include <openssl/opensslv.h>
32
#include <openssl/sha.h>
33
#include <openssl/crypto.h>
34
#include <openssl/evp.h>
35
#include <openssl/bn.h>
36
#include <openssl/ec.h>
37
#include <openssl/ecdsa.h>
38
#include <openssl/pem.h>
39
40
/* Use OpenSSL SHA256 instead of libc */
41
312
#define SHA256Init(x)   SHA256_Init(x)
42
624
#define SHA256Update(x, y, z) SHA256_Update(x, y, z)
43
312
#define SHA256Final(x, y) SHA256_Final(x, y)
44
208
#define SHA2_CTX    SHA256_CTX
45
46
#elif defined(HAVE_SHA2_H)
47
#include <sha2.h>
48
#endif /* WITH_OPENSSL */
49
50
/* #define SK_DEBUG 1 */
51
52
#if SSH_SK_VERSION_MAJOR != 0x000a0000
53
# error SK API has changed, sk-dummy.c needs an update
54
#endif
55
56
#ifdef SK_DUMMY_INTEGRATE
57
# define sk_api_version   ssh_sk_api_version
58
# define sk_enroll    ssh_sk_enroll
59
# define sk_sign    ssh_sk_sign
60
# define sk_load_resident_keys  ssh_sk_load_resident_keys
61
#endif /* !SK_STANDALONE */
62
63
static void skdebug(const char *func, const char *fmt, ...)
64
    __attribute__((__format__ (printf, 2, 3)));
65
66
static void
67
skdebug(const char *func, const char *fmt, ...)
68
0
{
69
#if defined(SK_DEBUG)
70
  va_list ap;
71
72
  va_start(ap, fmt);
73
  fprintf(stderr, "sk-dummy %s: ", func);
74
  vfprintf(stderr, fmt, ap);
75
  fputc('\n', stderr);
76
  va_end(ap);
77
#else
78
0
  (void)func; /* XXX */
79
0
  (void)fmt; /* XXX */
80
0
#endif
81
0
}
82
83
uint32_t
84
sk_api_version(void)
85
0
{
86
0
  return SSH_SK_VERSION_MAJOR;
87
0
}
88
89
static int
90
pack_key_ecdsa(struct sk_enroll_response *response)
91
0
{
92
0
#ifdef OPENSSL_HAS_ECC
93
0
  EC_KEY *key = NULL;
94
0
  const EC_GROUP *g;
95
0
  const EC_POINT *q;
96
0
  int ret = -1;
97
0
  long privlen;
98
0
  BIO *bio = NULL;
99
0
  char *privptr;
100
101
0
  response->public_key = NULL;
102
0
  response->public_key_len = 0;
103
0
  response->key_handle = NULL;
104
0
  response->key_handle_len = 0;
105
106
0
  if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
107
0
    skdebug(__func__, "EC_KEY_new_by_curve_name");
108
0
    goto out;
109
0
  }
110
0
  if (EC_KEY_generate_key(key) != 1) {
111
0
    skdebug(__func__, "EC_KEY_generate_key");
112
0
    goto out;
113
0
  }
114
0
  EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
115
0
  if ((bio = BIO_new(BIO_s_mem())) == NULL ||
116
0
      (g = EC_KEY_get0_group(key)) == NULL ||
117
0
      (q = EC_KEY_get0_public_key(key)) == NULL) {
118
0
    skdebug(__func__, "couldn't get key parameters");
119
0
    goto out;
120
0
  }
121
0
  response->public_key_len = EC_POINT_point2oct(g, q,
122
0
      POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
123
0
  if (response->public_key_len == 0 || response->public_key_len > 2048) {
124
0
    skdebug(__func__, "bad pubkey length %zu",
125
0
        response->public_key_len);
126
0
    goto out;
127
0
  }
128
0
  if ((response->public_key = malloc(response->public_key_len)) == NULL) {
129
0
    skdebug(__func__, "malloc pubkey failed");
130
0
    goto out;
131
0
  }
132
0
  if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
133
0
      response->public_key, response->public_key_len, NULL) == 0) {
134
0
    skdebug(__func__, "EC_POINT_point2oct failed");
135
0
    goto out;
136
0
  }
137
  /* Key handle contains PEM encoded private key */
138
0
  if (!PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) {
139
0
    skdebug(__func__, "PEM_write_bio_ECPrivateKey failed");
140
0
    goto out;
141
0
  }
142
0
  if ((privlen = BIO_get_mem_data(bio, &privptr)) <= 0) {
143
0
    skdebug(__func__, "BIO_get_mem_data failed");
144
0
    goto out;
145
0
  }
146
0
  if ((response->key_handle = malloc(privlen)) == NULL) {
147
0
    skdebug(__func__, "malloc key_handle failed");
148
0
    goto out;
149
0
  }
150
0
  response->key_handle_len = (size_t)privlen;
151
0
  memcpy(response->key_handle, privptr, response->key_handle_len);
152
  /* success */
153
0
  ret = 0;
154
0
 out:
155
0
  if (ret != 0) {
156
0
    if (response->public_key != NULL) {
157
0
      memset(response->public_key, 0,
158
0
          response->public_key_len);
159
0
      free(response->public_key);
160
0
      response->public_key = NULL;
161
0
    }
162
0
    if (response->key_handle != NULL) {
163
0
      memset(response->key_handle, 0,
164
0
          response->key_handle_len);
165
0
      free(response->key_handle);
166
0
      response->key_handle = NULL;
167
0
    }
168
0
  }
169
0
  BIO_free(bio);
170
0
  EC_KEY_free(key);
171
0
  return ret;
172
#else
173
  return -1;
174
#endif
175
0
}
176
177
static int
178
pack_key_ed25519(struct sk_enroll_response *response)
179
0
{
180
0
  int ret = -1;
181
0
  u_char pk[crypto_sign_ed25519_PUBLICKEYBYTES];
182
0
  u_char sk[crypto_sign_ed25519_SECRETKEYBYTES];
183
184
0
  response->public_key = NULL;
185
0
  response->public_key_len = 0;
186
0
  response->key_handle = NULL;
187
0
  response->key_handle_len = 0;
188
189
0
  memset(pk, 0, sizeof(pk));
190
0
  memset(sk, 0, sizeof(sk));
191
0
  crypto_sign_ed25519_keypair(pk, sk);
192
193
0
  response->public_key_len = sizeof(pk);
194
0
  if ((response->public_key = malloc(response->public_key_len)) == NULL) {
195
0
    skdebug(__func__, "malloc pubkey failed");
196
0
    goto out;
197
0
  }
198
0
  memcpy(response->public_key, pk, sizeof(pk));
199
  /* Key handle contains sk */
200
0
  response->key_handle_len = sizeof(sk);
201
0
  if ((response->key_handle = malloc(response->key_handle_len)) == NULL) {
202
0
    skdebug(__func__, "malloc key_handle failed");
203
0
    goto out;
204
0
  }
205
0
  memcpy(response->key_handle, sk, sizeof(sk));
206
  /* success */
207
0
  ret = 0;
208
0
 out:
209
0
  if (ret != 0)
210
0
    free(response->public_key);
211
0
  return ret;
212
0
}
213
214
static int
215
check_options(struct sk_option **options)
216
104
{
217
104
  size_t i;
218
219
104
  if (options == NULL)
220
104
    return 0;
221
0
  for (i = 0; options[i] != NULL; i++) {
222
0
    skdebug(__func__, "requested unsupported option %s",
223
0
        options[i]->name);
224
0
    if (options[i]->required) {
225
0
      skdebug(__func__, "unknown required option");
226
0
      return -1;
227
0
    }
228
0
  }
229
0
  return 0;
230
0
}
231
232
int
233
sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
234
    const char *application, uint8_t flags, const char *pin,
235
    struct sk_option **options, struct sk_enroll_response **enroll_response)
236
0
{
237
0
  struct sk_enroll_response *response = NULL;
238
0
  int ret = SSH_SK_ERR_GENERAL;
239
240
0
  (void)flags; /* XXX; unused */
241
242
0
  if (enroll_response == NULL) {
243
0
    skdebug(__func__, "enroll_response == NULL");
244
0
    goto out;
245
0
  }
246
0
  *enroll_response = NULL;
247
0
  if (check_options(options) != 0)
248
0
    goto out; /* error already logged */
249
0
  if ((response = calloc(1, sizeof(*response))) == NULL) {
250
0
    skdebug(__func__, "calloc response failed");
251
0
    goto out;
252
0
  }
253
0
  response->flags = flags;
254
0
  switch(alg) {
255
0
  case SSH_SK_ECDSA:
256
0
    if (pack_key_ecdsa(response) != 0)
257
0
      goto out;
258
0
    break;
259
0
  case SSH_SK_ED25519:
260
0
    if (pack_key_ed25519(response) != 0)
261
0
      goto out;
262
0
    break;
263
0
  default:
264
0
    skdebug(__func__, "unsupported key type %d", alg);
265
0
    goto out;
266
0
  }
267
  /* Have to return something here */
268
0
  if ((response->signature = calloc(1, 1)) == NULL) {
269
0
    skdebug(__func__, "calloc signature failed");
270
0
    goto out;
271
0
  }
272
0
  response->signature_len = 0;
273
274
0
  *enroll_response = response;
275
0
  response = NULL;
276
0
  ret = 0;
277
0
 out:
278
0
  if (response != NULL) {
279
0
    free(response->public_key);
280
0
    free(response->key_handle);
281
0
    free(response->signature);
282
0
    free(response->attestation_cert);
283
0
    free(response);
284
0
  }
285
0
  return ret;
286
0
}
287
288
static void
289
dump(const char *preamble, const void *sv, size_t l)
290
520
{
291
#ifdef SK_DEBUG
292
  const u_char *s = (const u_char *)sv;
293
  size_t i;
294
295
  fprintf(stderr, "%s (len %zu):\n", preamble, l);
296
  for (i = 0; i < l; i++) {
297
    if (i % 16 == 0)
298
      fprintf(stderr, "%04zu: ", i);
299
    fprintf(stderr, "%02x", s[i]);
300
    if (i % 16 == 15 || i == l - 1)
301
      fprintf(stderr, "\n");
302
  }
303
#endif
304
520
}
305
306
static int
307
sig_ecdsa(const uint8_t *message, size_t message_len,
308
    const char *application, uint32_t counter, uint8_t flags,
309
    const uint8_t *key_handle, size_t key_handle_len,
310
    struct sk_sign_response *response)
311
104
{
312
104
#ifdef OPENSSL_HAS_ECC
313
104
  ECDSA_SIG *sig = NULL;
314
104
  const BIGNUM *sig_r, *sig_s;
315
104
  int ret = -1;
316
104
  BIO *bio = NULL;
317
104
  EVP_PKEY *pk = NULL;
318
104
  EC_KEY *ec = NULL;
319
104
  SHA2_CTX ctx;
320
104
  uint8_t apphash[SHA256_DIGEST_LENGTH];
321
104
  uint8_t sighash[SHA256_DIGEST_LENGTH];
322
104
  uint8_t countbuf[4];
323
324
  /* Decode EC_KEY from key handle */
325
104
  if ((bio = BIO_new(BIO_s_mem())) == NULL ||
326
104
      BIO_write(bio, key_handle, key_handle_len) != (int)key_handle_len) {
327
0
    skdebug(__func__, "BIO setup failed");
328
0
    goto out;
329
0
  }
330
104
  if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, "")) == NULL) {
331
0
    skdebug(__func__, "PEM_read_bio_PrivateKey failed");
332
0
    goto out;
333
0
  }
334
104
  if (EVP_PKEY_base_id(pk) != EVP_PKEY_EC) {
335
0
    skdebug(__func__, "Not an EC key: %d", EVP_PKEY_base_id(pk));
336
0
    goto out;
337
0
  }
338
104
  if ((ec = EVP_PKEY_get1_EC_KEY(pk)) == NULL) {
339
0
    skdebug(__func__, "EVP_PKEY_get1_EC_KEY failed");
340
0
    goto out;
341
0
  }
342
  /* Expect message to be pre-hashed */
343
104
  if (message_len != SHA256_DIGEST_LENGTH) {
344
0
    skdebug(__func__, "bad message len %zu", message_len);
345
0
    goto out;
346
0
  }
347
  /* Prepare data to be signed */
348
104
  dump("message", message, message_len);
349
104
  SHA256Init(&ctx);
350
104
  SHA256Update(&ctx, (const u_char *)application, strlen(application));
351
104
  SHA256Final(apphash, &ctx);
352
104
  dump("apphash", apphash, sizeof(apphash));
353
104
  countbuf[0] = (counter >> 24) & 0xff;
354
104
  countbuf[1] = (counter >> 16) & 0xff;
355
104
  countbuf[2] = (counter >> 8) & 0xff;
356
104
  countbuf[3] = counter & 0xff;
357
104
  dump("countbuf", countbuf, sizeof(countbuf));
358
104
  dump("flags", &flags, sizeof(flags));
359
104
  SHA256Init(&ctx);
360
104
  SHA256Update(&ctx, apphash, sizeof(apphash));
361
104
  SHA256Update(&ctx, &flags, sizeof(flags));
362
104
  SHA256Update(&ctx, countbuf, sizeof(countbuf));
363
104
  SHA256Update(&ctx, message, message_len);
364
104
  SHA256Final(sighash, &ctx);
365
104
  dump("sighash", sighash, sizeof(sighash));
366
  /* create and encode signature */
367
104
  if ((sig = ECDSA_do_sign(sighash, sizeof(sighash), ec)) == NULL) {
368
0
    skdebug(__func__, "ECDSA_do_sign failed");
369
0
    goto out;
370
0
  }
371
104
  ECDSA_SIG_get0(sig, &sig_r, &sig_s);
372
104
  response->sig_r_len = BN_num_bytes(sig_r);
373
104
  response->sig_s_len = BN_num_bytes(sig_s);
374
104
  if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
375
104
      (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
376
0
    skdebug(__func__, "calloc signature failed");
377
0
    goto out;
378
0
  }
379
104
  BN_bn2bin(sig_r, response->sig_r);
380
104
  BN_bn2bin(sig_s, response->sig_s);
381
104
  ret = 0;
382
104
 out:
383
104
  explicit_bzero(&ctx, sizeof(ctx));
384
104
  explicit_bzero(&apphash, sizeof(apphash));
385
104
  explicit_bzero(&sighash, sizeof(sighash));
386
104
  ECDSA_SIG_free(sig);
387
104
  if (ret != 0) {
388
0
    free(response->sig_r);
389
0
    free(response->sig_s);
390
0
    response->sig_r = NULL;
391
0
    response->sig_s = NULL;
392
0
  }
393
104
  BIO_free(bio);
394
104
  EC_KEY_free(ec);
395
104
  EVP_PKEY_free(pk);
396
104
  return ret;
397
#else
398
  return -1;
399
#endif
400
104
}
401
402
static int
403
sig_ed25519(const uint8_t *message, size_t message_len,
404
    const char *application, uint32_t counter, uint8_t flags,
405
    const uint8_t *key_handle, size_t key_handle_len,
406
    struct sk_sign_response *response)
407
0
{
408
0
  size_t o;
409
0
  int ret = -1;
410
0
  SHA2_CTX ctx;
411
0
  uint8_t apphash[SHA256_DIGEST_LENGTH];
412
0
  uint8_t signbuf[sizeof(apphash) + sizeof(flags) +
413
0
      sizeof(counter) + SHA256_DIGEST_LENGTH];
414
0
  uint8_t sig[crypto_sign_ed25519_BYTES + sizeof(signbuf)];
415
0
  unsigned long long smlen;
416
417
0
  if (key_handle_len != crypto_sign_ed25519_SECRETKEYBYTES) {
418
0
    skdebug(__func__, "bad key handle length %zu", key_handle_len);
419
0
    goto out;
420
0
  }
421
  /* Expect message to be pre-hashed */
422
0
  if (message_len != SHA256_DIGEST_LENGTH) {
423
0
    skdebug(__func__, "bad message len %zu", message_len);
424
0
    goto out;
425
0
  }
426
  /* Prepare data to be signed */
427
0
  dump("message", message, message_len);
428
0
  SHA256Init(&ctx);
429
0
  SHA256Update(&ctx, (const u_char *)application, strlen(application));
430
0
  SHA256Final(apphash, &ctx);
431
0
  dump("apphash", apphash, sizeof(apphash));
432
433
0
  memcpy(signbuf, apphash, sizeof(apphash));
434
0
  o = sizeof(apphash);
435
0
  signbuf[o++] = flags;
436
0
  signbuf[o++] = (counter >> 24) & 0xff;
437
0
  signbuf[o++] = (counter >> 16) & 0xff;
438
0
  signbuf[o++] = (counter >> 8) & 0xff;
439
0
  signbuf[o++] = counter & 0xff;
440
0
  memcpy(signbuf + o, message, message_len);
441
0
  o += message_len;
442
0
  if (o != sizeof(signbuf)) {
443
0
    skdebug(__func__, "bad sign buf len %zu, expected %zu",
444
0
        o, sizeof(signbuf));
445
0
    goto out;
446
0
  }
447
0
  dump("signbuf", signbuf, sizeof(signbuf));
448
  /* create and encode signature */
449
0
  smlen = sizeof(signbuf);
450
0
  if (crypto_sign_ed25519(sig, &smlen, signbuf, sizeof(signbuf),
451
0
      key_handle) != 0) {
452
0
    skdebug(__func__, "crypto_sign_ed25519 failed");
453
0
    goto out;
454
0
  }
455
0
  if (smlen <= sizeof(signbuf)) {
456
0
    skdebug(__func__, "bad sign smlen %llu, expected min %zu",
457
0
        smlen, sizeof(signbuf) + 1);
458
0
    goto out;
459
0
  }
460
0
  response->sig_r_len = (size_t)(smlen - sizeof(signbuf));
461
0
  if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
462
0
    skdebug(__func__, "calloc signature failed");
463
0
    goto out;
464
0
  }
465
0
  memcpy(response->sig_r, sig, response->sig_r_len);
466
0
  dump("sig_r", response->sig_r, response->sig_r_len);
467
0
  ret = 0;
468
0
 out:
469
0
  explicit_bzero(&ctx, sizeof(ctx));
470
0
  explicit_bzero(&apphash, sizeof(apphash));
471
0
  explicit_bzero(&signbuf, sizeof(signbuf));
472
0
  explicit_bzero(&sig, sizeof(sig));
473
0
  if (ret != 0) {
474
0
    free(response->sig_r);
475
0
    response->sig_r = NULL;
476
0
  }
477
0
  return ret;
478
0
}
479
480
int
481
sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
482
    const char *application, const uint8_t *key_handle, size_t key_handle_len,
483
    uint8_t flags, const char *pin, struct sk_option **options,
484
    struct sk_sign_response **sign_response)
485
104
{
486
104
  struct sk_sign_response *response = NULL;
487
104
  int ret = SSH_SK_ERR_GENERAL;
488
104
  SHA2_CTX ctx;
489
104
  uint8_t message[32];
490
491
104
  if (sign_response == NULL) {
492
0
    skdebug(__func__, "sign_response == NULL");
493
0
    goto out;
494
0
  }
495
104
  *sign_response = NULL;
496
104
  if (check_options(options) != 0)
497
0
    goto out; /* error already logged */
498
104
  if ((response = calloc(1, sizeof(*response))) == NULL) {
499
0
    skdebug(__func__, "calloc response failed");
500
0
    goto out;
501
0
  }
502
104
  SHA256Init(&ctx);
503
104
  SHA256Update(&ctx, data, datalen);
504
104
  SHA256Final(message, &ctx);
505
104
  response->flags = flags;
506
104
  response->counter = 0x12345678;
507
104
  switch(alg) {
508
104
  case SSH_SK_ECDSA:
509
104
    if (sig_ecdsa(message, sizeof(message), application,
510
104
        response->counter, flags, key_handle, key_handle_len,
511
104
        response) != 0)
512
0
      goto out;
513
104
    break;
514
104
  case SSH_SK_ED25519:
515
0
    if (sig_ed25519(message, sizeof(message), application,
516
0
        response->counter, flags, key_handle, key_handle_len,
517
0
        response) != 0)
518
0
      goto out;
519
0
    break;
520
0
  default:
521
0
    skdebug(__func__, "unsupported key type %d", alg);
522
0
    goto out;
523
104
  }
524
104
  *sign_response = response;
525
104
  response = NULL;
526
104
  ret = 0;
527
104
 out:
528
104
  explicit_bzero(message, sizeof(message));
529
104
  if (response != NULL) {
530
0
    free(response->sig_r);
531
0
    free(response->sig_s);
532
0
    free(response);
533
0
  }
534
104
  return ret;
535
104
}
536
537
int
538
sk_load_resident_keys(const char *pin, struct sk_option **options,
539
    struct sk_resident_key ***rks, size_t *nrks)
540
0
{
541
0
  return SSH_SK_ERR_UNSUPPORTED;
542
0
}