Coverage Report

Created: 2025-07-12 06:21

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