Coverage Report

Created: 2024-06-18 06:23

/src/hpn-ssh/ssh-ecdsa.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: ssh-ecdsa.c,v 1.26 2023/03/08 04:43:12 guenther Exp $ */
2
/*
3
 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4
 * Copyright (c) 2010 Damien Miller.  All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 */
26
27
#include "includes.h"
28
29
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
30
31
#include <sys/types.h>
32
33
#include <openssl/bn.h>
34
#include <openssl/ec.h>
35
#include <openssl/ecdsa.h>
36
#include <openssl/evp.h>
37
38
#include <string.h>
39
40
#include "sshbuf.h"
41
#include "ssherr.h"
42
#include "digest.h"
43
#define SSHKEY_INTERNAL
44
#include "sshkey.h"
45
46
#include "openbsd-compat/openssl-compat.h"
47
48
static u_int
49
ssh_ecdsa_size(const struct sshkey *key)
50
0
{
51
0
  switch (key->ecdsa_nid) {
52
0
  case NID_X9_62_prime256v1:
53
0
    return 256;
54
0
  case NID_secp384r1:
55
0
    return 384;
56
0
#ifdef OPENSSL_HAS_NISTP521
57
0
  case NID_secp521r1:
58
0
    return 521;
59
0
#endif
60
0
  default:
61
0
    return 0;
62
0
  }
63
0
}
64
65
static void
66
ssh_ecdsa_cleanup(struct sshkey *k)
67
295
{
68
295
  EC_KEY_free(k->ecdsa);
69
295
  k->ecdsa = NULL;
70
295
}
71
72
static int
73
ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
74
0
{
75
0
  const EC_GROUP *grp_a, *grp_b;
76
0
  const EC_POINT *pub_a, *pub_b;
77
78
0
  if (a->ecdsa == NULL || b->ecdsa == NULL)
79
0
    return 0;
80
0
  if ((grp_a = EC_KEY_get0_group(a->ecdsa)) == NULL ||
81
0
      (grp_b = EC_KEY_get0_group(b->ecdsa)) == NULL)
82
0
    return 0;
83
0
  if ((pub_a = EC_KEY_get0_public_key(a->ecdsa)) == NULL ||
84
0
      (pub_b = EC_KEY_get0_public_key(b->ecdsa)) == NULL)
85
0
    return 0;
86
0
  if (EC_GROUP_cmp(grp_a, grp_b, NULL) != 0)
87
0
    return 0;
88
0
  if (EC_POINT_cmp(grp_a, pub_a, pub_b, NULL) != 0)
89
0
    return 0;
90
91
0
  return 1;
92
0
}
93
94
static int
95
ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
96
    enum sshkey_serialize_rep opts)
97
0
{
98
0
  int r;
99
100
0
  if (key->ecdsa == NULL)
101
0
    return SSH_ERR_INVALID_ARGUMENT;
102
0
  if ((r = sshbuf_put_cstring(b,
103
0
      sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
104
0
      (r = sshbuf_put_eckey(b, key->ecdsa)) != 0)
105
0
    return r;
106
107
0
  return 0;
108
0
}
109
110
static int
111
ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
112
    enum sshkey_serialize_rep opts)
113
0
{
114
0
  int r;
115
116
0
  if (!sshkey_is_cert(key)) {
117
0
    if ((r = ssh_ecdsa_serialize_public(key, b, opts)) != 0)
118
0
      return r;
119
0
  }
120
0
  if ((r = sshbuf_put_bignum2(b,
121
0
      EC_KEY_get0_private_key(key->ecdsa))) != 0)
122
0
    return r;
123
0
  return 0;
124
0
}
125
126
static int
127
ssh_ecdsa_generate(struct sshkey *k, int bits)
128
0
{
129
0
  EC_KEY *private;
130
131
0
  if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
132
0
    return SSH_ERR_KEY_LENGTH;
133
0
  if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
134
0
    return SSH_ERR_ALLOC_FAIL;
135
0
  if (EC_KEY_generate_key(private) != 1) {
136
0
    EC_KEY_free(private);
137
0
    return SSH_ERR_LIBCRYPTO_ERROR;
138
0
  }
139
0
  EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
140
0
  k->ecdsa = private;
141
0
  return 0;
142
0
}
143
144
static int
145
ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
146
0
{
147
0
  to->ecdsa_nid = from->ecdsa_nid;
148
0
  if ((to->ecdsa = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
149
0
    return SSH_ERR_ALLOC_FAIL;
150
0
  if (EC_KEY_set_public_key(to->ecdsa,
151
0
      EC_KEY_get0_public_key(from->ecdsa)) != 1)
152
0
    return SSH_ERR_LIBCRYPTO_ERROR; /* caller will free k->ecdsa */
153
0
  return 0;
154
0
}
155
156
static int
157
ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
158
    struct sshkey *key)
159
295
{
160
295
  int r;
161
295
  char *curve = NULL;
162
163
295
  if ((key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype)) == -1)
164
14
    return SSH_ERR_INVALID_ARGUMENT;
165
281
  if ((r = sshbuf_get_cstring(b, &curve, NULL)) != 0)
166
13
    goto out;
167
268
  if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
168
121
    r = SSH_ERR_EC_CURVE_MISMATCH;
169
121
    goto out;
170
121
  }
171
147
  EC_KEY_free(key->ecdsa);
172
147
  key->ecdsa = NULL;
173
147
  if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
174
0
    r = SSH_ERR_LIBCRYPTO_ERROR;
175
0
    goto out;
176
0
  }
177
147
  if ((r = sshbuf_get_eckey(b, key->ecdsa)) != 0)
178
147
    goto out;
179
0
  if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
180
0
      EC_KEY_get0_public_key(key->ecdsa)) != 0) {
181
0
    r = SSH_ERR_KEY_INVALID_EC_VALUE;
182
0
    goto out;
183
0
  }
184
  /* success */
185
0
  r = 0;
186
#ifdef DEBUG_PK
187
  sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa),
188
      EC_KEY_get0_public_key(key->ecdsa));
189
#endif
190
281
 out:
191
281
  free(curve);
192
281
  if (r != 0) {
193
281
    EC_KEY_free(key->ecdsa);
194
281
    key->ecdsa = NULL;
195
281
  }
196
281
  return r;
197
0
}
198
199
static int
200
ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b,
201
    struct sshkey *key)
202
274
{
203
274
  int r;
204
274
  BIGNUM *exponent = NULL;
205
206
274
  if (!sshkey_is_cert(key)) {
207
274
    if ((r = ssh_ecdsa_deserialize_public(ktype, b, key)) != 0)
208
274
      return r;
209
274
  }
210
0
  if ((r = sshbuf_get_bignum2(b, &exponent)) != 0)
211
0
    goto out;
212
0
  if (EC_KEY_set_private_key(key->ecdsa, exponent) != 1) {
213
0
    r = SSH_ERR_LIBCRYPTO_ERROR;
214
0
    goto out;
215
0
  }
216
0
  if ((r = sshkey_ec_validate_private(key->ecdsa)) != 0)
217
0
    goto out;
218
  /* success */
219
0
  r = 0;
220
0
 out:
221
0
  BN_clear_free(exponent);
222
0
  return r;
223
0
}
224
225
static int
226
ssh_ecdsa_sign(struct sshkey *key,
227
    u_char **sigp, size_t *lenp,
228
    const u_char *data, size_t dlen,
229
    const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
230
0
{
231
0
  ECDSA_SIG *esig = NULL;
232
0
  const BIGNUM *sig_r, *sig_s;
233
0
  int hash_alg;
234
0
  u_char digest[SSH_DIGEST_MAX_LENGTH];
235
0
  size_t len, hlen;
236
0
  struct sshbuf *b = NULL, *bb = NULL;
237
0
  int ret = SSH_ERR_INTERNAL_ERROR;
238
239
0
  if (lenp != NULL)
240
0
    *lenp = 0;
241
0
  if (sigp != NULL)
242
0
    *sigp = NULL;
243
244
0
  if (key == NULL || key->ecdsa == NULL ||
245
0
      sshkey_type_plain(key->type) != KEY_ECDSA)
246
0
    return SSH_ERR_INVALID_ARGUMENT;
247
248
0
  if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
249
0
      (hlen = ssh_digest_bytes(hash_alg)) == 0)
250
0
    return SSH_ERR_INTERNAL_ERROR;
251
0
  if ((ret = ssh_digest_memory(hash_alg, data, dlen,
252
0
      digest, sizeof(digest))) != 0)
253
0
    goto out;
254
255
0
  if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) {
256
0
    ret = SSH_ERR_LIBCRYPTO_ERROR;
257
0
    goto out;
258
0
  }
259
260
0
  if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
261
0
    ret = SSH_ERR_ALLOC_FAIL;
262
0
    goto out;
263
0
  }
264
0
  ECDSA_SIG_get0(esig, &sig_r, &sig_s);
265
0
  if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
266
0
      (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
267
0
    goto out;
268
0
  if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
269
0
      (ret = sshbuf_put_stringb(b, bb)) != 0)
270
0
    goto out;
271
0
  len = sshbuf_len(b);
272
0
  if (sigp != NULL) {
273
0
    if ((*sigp = malloc(len)) == NULL) {
274
0
      ret = SSH_ERR_ALLOC_FAIL;
275
0
      goto out;
276
0
    }
277
0
    memcpy(*sigp, sshbuf_ptr(b), len);
278
0
  }
279
0
  if (lenp != NULL)
280
0
    *lenp = len;
281
0
  ret = 0;
282
0
 out:
283
0
  explicit_bzero(digest, sizeof(digest));
284
0
  sshbuf_free(b);
285
0
  sshbuf_free(bb);
286
0
  ECDSA_SIG_free(esig);
287
0
  return ret;
288
0
}
289
290
static int
291
ssh_ecdsa_verify(const struct sshkey *key,
292
    const u_char *sig, size_t siglen,
293
    const u_char *data, size_t dlen, const char *alg, u_int compat,
294
    struct sshkey_sig_details **detailsp)
295
0
{
296
0
  ECDSA_SIG *esig = NULL;
297
0
  BIGNUM *sig_r = NULL, *sig_s = NULL;
298
0
  int hash_alg;
299
0
  u_char digest[SSH_DIGEST_MAX_LENGTH];
300
0
  size_t hlen;
301
0
  int ret = SSH_ERR_INTERNAL_ERROR;
302
0
  struct sshbuf *b = NULL, *sigbuf = NULL;
303
0
  char *ktype = NULL;
304
305
0
  if (key == NULL || key->ecdsa == NULL ||
306
0
      sshkey_type_plain(key->type) != KEY_ECDSA ||
307
0
      sig == NULL || siglen == 0)
308
0
    return SSH_ERR_INVALID_ARGUMENT;
309
310
0
  if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
311
0
      (hlen = ssh_digest_bytes(hash_alg)) == 0)
312
0
    return SSH_ERR_INTERNAL_ERROR;
313
314
  /* fetch signature */
315
0
  if ((b = sshbuf_from(sig, siglen)) == NULL)
316
0
    return SSH_ERR_ALLOC_FAIL;
317
0
  if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
318
0
      sshbuf_froms(b, &sigbuf) != 0) {
319
0
    ret = SSH_ERR_INVALID_FORMAT;
320
0
    goto out;
321
0
  }
322
0
  if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
323
0
    ret = SSH_ERR_KEY_TYPE_MISMATCH;
324
0
    goto out;
325
0
  }
326
0
  if (sshbuf_len(b) != 0) {
327
0
    ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
328
0
    goto out;
329
0
  }
330
331
  /* parse signature */
332
0
  if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
333
0
      sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
334
0
    ret = SSH_ERR_INVALID_FORMAT;
335
0
    goto out;
336
0
  }
337
0
  if ((esig = ECDSA_SIG_new()) == NULL) {
338
0
    ret = SSH_ERR_ALLOC_FAIL;
339
0
    goto out;
340
0
  }
341
0
  if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
342
0
    ret = SSH_ERR_LIBCRYPTO_ERROR;
343
0
    goto out;
344
0
  }
345
0
  sig_r = sig_s = NULL; /* transferred */
346
347
0
  if (sshbuf_len(sigbuf) != 0) {
348
0
    ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
349
0
    goto out;
350
0
  }
351
0
  if ((ret = ssh_digest_memory(hash_alg, data, dlen,
352
0
      digest, sizeof(digest))) != 0)
353
0
    goto out;
354
355
0
  switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) {
356
0
  case 1:
357
0
    ret = 0;
358
0
    break;
359
0
  case 0:
360
0
    ret = SSH_ERR_SIGNATURE_INVALID;
361
0
    goto out;
362
0
  default:
363
0
    ret = SSH_ERR_LIBCRYPTO_ERROR;
364
0
    goto out;
365
0
  }
366
367
0
 out:
368
0
  explicit_bzero(digest, sizeof(digest));
369
0
  sshbuf_free(sigbuf);
370
0
  sshbuf_free(b);
371
0
  ECDSA_SIG_free(esig);
372
0
  BN_clear_free(sig_r);
373
0
  BN_clear_free(sig_s);
374
0
  free(ktype);
375
0
  return ret;
376
0
}
377
378
/* NB. not static; used by ECDSA-SK */
379
const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
380
  /* .size = */   ssh_ecdsa_size,
381
  /* .alloc = */    NULL,
382
  /* .cleanup = */  ssh_ecdsa_cleanup,
383
  /* .equal = */    ssh_ecdsa_equal,
384
  /* .ssh_serialize_public = */ ssh_ecdsa_serialize_public,
385
  /* .ssh_deserialize_public = */ ssh_ecdsa_deserialize_public,
386
  /* .ssh_serialize_private = */ ssh_ecdsa_serialize_private,
387
  /* .ssh_deserialize_private = */ ssh_ecdsa_deserialize_private,
388
  /* .generate = */ ssh_ecdsa_generate,
389
  /* .copy_public = */  ssh_ecdsa_copy_public,
390
  /* .sign = */   ssh_ecdsa_sign,
391
  /* .verify = */   ssh_ecdsa_verify,
392
};
393
394
const struct sshkey_impl sshkey_ecdsa_nistp256_impl = {
395
  /* .name = */   "ecdsa-sha2-nistp256",
396
  /* .shortname = */  "ECDSA",
397
  /* .sigalg = */   NULL,
398
  /* .type = */   KEY_ECDSA,
399
  /* .nid = */    NID_X9_62_prime256v1,
400
  /* .cert = */   0,
401
  /* .sigonly = */  0,
402
  /* .keybits = */  0,
403
  /* .funcs = */    &sshkey_ecdsa_funcs,
404
};
405
406
const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = {
407
  /* .name = */   "ecdsa-sha2-nistp256-cert-v01@openssh.com",
408
  /* .shortname = */  "ECDSA-CERT",
409
  /* .sigalg = */   NULL,
410
  /* .type = */   KEY_ECDSA_CERT,
411
  /* .nid = */    NID_X9_62_prime256v1,
412
  /* .cert = */   1,
413
  /* .sigonly = */  0,
414
  /* .keybits = */  0,
415
  /* .funcs = */    &sshkey_ecdsa_funcs,
416
};
417
418
const struct sshkey_impl sshkey_ecdsa_nistp384_impl = {
419
  /* .name = */   "ecdsa-sha2-nistp384",
420
  /* .shortname = */  "ECDSA",
421
  /* .sigalg = */   NULL,
422
  /* .type = */   KEY_ECDSA,
423
  /* .nid = */    NID_secp384r1,
424
  /* .cert = */   0,
425
  /* .sigonly = */  0,
426
  /* .keybits = */  0,
427
  /* .funcs = */    &sshkey_ecdsa_funcs,
428
};
429
430
const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = {
431
  /* .name = */   "ecdsa-sha2-nistp384-cert-v01@openssh.com",
432
  /* .shortname = */  "ECDSA-CERT",
433
  /* .sigalg = */   NULL,
434
  /* .type = */   KEY_ECDSA_CERT,
435
  /* .nid = */    NID_secp384r1,
436
  /* .cert = */   1,
437
  /* .sigonly = */  0,
438
  /* .keybits = */  0,
439
  /* .funcs = */    &sshkey_ecdsa_funcs,
440
};
441
442
#ifdef OPENSSL_HAS_NISTP521
443
const struct sshkey_impl sshkey_ecdsa_nistp521_impl = {
444
  /* .name = */   "ecdsa-sha2-nistp521",
445
  /* .shortname = */  "ECDSA",
446
  /* .sigalg = */   NULL,
447
  /* .type = */   KEY_ECDSA,
448
  /* .nid = */    NID_secp521r1,
449
  /* .cert = */   0,
450
  /* .sigonly = */  0,
451
  /* .keybits = */  0,
452
  /* .funcs = */    &sshkey_ecdsa_funcs,
453
};
454
455
const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = {
456
  /* .name = */   "ecdsa-sha2-nistp521-cert-v01@openssh.com",
457
  /* .shortname = */  "ECDSA-CERT",
458
  /* .sigalg = */   NULL,
459
  /* .type = */   KEY_ECDSA_CERT,
460
  /* .nid = */    NID_secp521r1,
461
  /* .cert = */   1,
462
  /* .sigonly = */  0,
463
  /* .keybits = */  0,
464
  /* .funcs = */    &sshkey_ecdsa_funcs,
465
};
466
#endif
467
468
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */