Coverage Report

Created: 2025-07-02 06:56

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