xref: /dragonfly/crypto/openssh/ssh-ecdsa.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
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
ssh_ecdsa_size(const struct sshkey * key)49 ssh_ecdsa_size(const struct sshkey *key)
50 {
51           switch (key->ecdsa_nid) {
52           case NID_X9_62_prime256v1:
53                     return 256;
54           case NID_secp384r1:
55                     return 384;
56 #ifdef OPENSSL_HAS_NISTP521
57           case NID_secp521r1:
58                     return 521;
59 #endif
60           default:
61                     return 0;
62           }
63 }
64 
65 static void
ssh_ecdsa_cleanup(struct sshkey * k)66 ssh_ecdsa_cleanup(struct sshkey *k)
67 {
68           EC_KEY_free(k->ecdsa);
69           k->ecdsa = NULL;
70 }
71 
72 static int
ssh_ecdsa_equal(const struct sshkey * a,const struct sshkey * b)73 ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
74 {
75           const EC_GROUP *grp_a, *grp_b;
76           const EC_POINT *pub_a, *pub_b;
77 
78           if (a->ecdsa == NULL || b->ecdsa == NULL)
79                     return 0;
80           if ((grp_a = EC_KEY_get0_group(a->ecdsa)) == NULL ||
81               (grp_b = EC_KEY_get0_group(b->ecdsa)) == NULL)
82                     return 0;
83           if ((pub_a = EC_KEY_get0_public_key(a->ecdsa)) == NULL ||
84               (pub_b = EC_KEY_get0_public_key(b->ecdsa)) == NULL)
85                     return 0;
86           if (EC_GROUP_cmp(grp_a, grp_b, NULL) != 0)
87                     return 0;
88           if (EC_POINT_cmp(grp_a, pub_a, pub_b, NULL) != 0)
89                     return 0;
90 
91           return 1;
92 }
93 
94 static int
ssh_ecdsa_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)95 ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
96     enum sshkey_serialize_rep opts)
97 {
98           int r;
99 
100           if (key->ecdsa == NULL)
101                     return SSH_ERR_INVALID_ARGUMENT;
102           if ((r = sshbuf_put_cstring(b,
103               sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
104               (r = sshbuf_put_eckey(b, key->ecdsa)) != 0)
105                     return r;
106 
107           return 0;
108 }
109 
110 static int
ssh_ecdsa_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)111 ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
112     enum sshkey_serialize_rep opts)
113 {
114           int r;
115 
116           if (!sshkey_is_cert(key)) {
117                     if ((r = ssh_ecdsa_serialize_public(key, b, opts)) != 0)
118                               return r;
119           }
120           if ((r = sshbuf_put_bignum2(b,
121               EC_KEY_get0_private_key(key->ecdsa))) != 0)
122                     return r;
123           return 0;
124 }
125 
126 static int
ssh_ecdsa_generate(struct sshkey * k,int bits)127 ssh_ecdsa_generate(struct sshkey *k, int bits)
128 {
129           EC_KEY *private;
130 
131           if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
132                     return SSH_ERR_KEY_LENGTH;
133           if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
134                     return SSH_ERR_ALLOC_FAIL;
135           if (EC_KEY_generate_key(private) != 1) {
136                     EC_KEY_free(private);
137                     return SSH_ERR_LIBCRYPTO_ERROR;
138           }
139           EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
140           k->ecdsa = private;
141           return 0;
142 }
143 
144 static int
ssh_ecdsa_copy_public(const struct sshkey * from,struct sshkey * to)145 ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
146 {
147           to->ecdsa_nid = from->ecdsa_nid;
148           if ((to->ecdsa = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
149                     return SSH_ERR_ALLOC_FAIL;
150           if (EC_KEY_set_public_key(to->ecdsa,
151               EC_KEY_get0_public_key(from->ecdsa)) != 1)
152                     return SSH_ERR_LIBCRYPTO_ERROR; /* caller will free k->ecdsa */
153           return 0;
154 }
155 
156 static int
ssh_ecdsa_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)157 ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
158     struct sshkey *key)
159 {
160           int r;
161           char *curve = NULL;
162 
163           if ((key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype)) == -1)
164                     return SSH_ERR_INVALID_ARGUMENT;
165           if ((r = sshbuf_get_cstring(b, &curve, NULL)) != 0)
166                     goto out;
167           if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
168                     r = SSH_ERR_EC_CURVE_MISMATCH;
169                     goto out;
170           }
171           EC_KEY_free(key->ecdsa);
172           key->ecdsa = NULL;
173           if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
174                     r = SSH_ERR_LIBCRYPTO_ERROR;
175                     goto out;
176           }
177           if ((r = sshbuf_get_eckey(b, key->ecdsa)) != 0)
178                     goto out;
179           if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
180               EC_KEY_get0_public_key(key->ecdsa)) != 0) {
181                     r = SSH_ERR_KEY_INVALID_EC_VALUE;
182                     goto out;
183           }
184           /* success */
185           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  out:
191           free(curve);
192           if (r != 0) {
193                     EC_KEY_free(key->ecdsa);
194                     key->ecdsa = NULL;
195           }
196           return r;
197 }
198 
199 static int
ssh_ecdsa_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)200 ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b,
201     struct sshkey *key)
202 {
203           int r;
204           BIGNUM *exponent = NULL;
205 
206           if (!sshkey_is_cert(key)) {
207                     if ((r = ssh_ecdsa_deserialize_public(ktype, b, key)) != 0)
208                               return r;
209           }
210           if ((r = sshbuf_get_bignum2(b, &exponent)) != 0)
211                     goto out;
212           if (EC_KEY_set_private_key(key->ecdsa, exponent) != 1) {
213                     r = SSH_ERR_LIBCRYPTO_ERROR;
214                     goto out;
215           }
216           if ((r = sshkey_ec_validate_private(key->ecdsa)) != 0)
217                     goto out;
218           /* success */
219           r = 0;
220  out:
221           BN_clear_free(exponent);
222           return r;
223 }
224 
225 static int
ssh_ecdsa_sign(struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t dlen,const char * alg,const char * sk_provider,const char * sk_pin,u_int compat)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 {
231           ECDSA_SIG *esig = NULL;
232           const BIGNUM *sig_r, *sig_s;
233           int hash_alg;
234           u_char digest[SSH_DIGEST_MAX_LENGTH];
235           size_t len, hlen;
236           struct sshbuf *b = NULL, *bb = NULL;
237           int ret = SSH_ERR_INTERNAL_ERROR;
238 
239           if (lenp != NULL)
240                     *lenp = 0;
241           if (sigp != NULL)
242                     *sigp = NULL;
243 
244           if (key == NULL || key->ecdsa == NULL ||
245               sshkey_type_plain(key->type) != KEY_ECDSA)
246                     return SSH_ERR_INVALID_ARGUMENT;
247 
248           if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
249               (hlen = ssh_digest_bytes(hash_alg)) == 0)
250                     return SSH_ERR_INTERNAL_ERROR;
251           if ((ret = ssh_digest_memory(hash_alg, data, dlen,
252               digest, sizeof(digest))) != 0)
253                     goto out;
254 
255           if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) {
256                     ret = SSH_ERR_LIBCRYPTO_ERROR;
257                     goto out;
258           }
259 
260           if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
261                     ret = SSH_ERR_ALLOC_FAIL;
262                     goto out;
263           }
264           ECDSA_SIG_get0(esig, &sig_r, &sig_s);
265           if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
266               (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
267                     goto out;
268           if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
269               (ret = sshbuf_put_stringb(b, bb)) != 0)
270                     goto out;
271           len = sshbuf_len(b);
272           if (sigp != NULL) {
273                     if ((*sigp = malloc(len)) == NULL) {
274                               ret = SSH_ERR_ALLOC_FAIL;
275                               goto out;
276                     }
277                     memcpy(*sigp, sshbuf_ptr(b), len);
278           }
279           if (lenp != NULL)
280                     *lenp = len;
281           ret = 0;
282  out:
283           explicit_bzero(digest, sizeof(digest));
284           sshbuf_free(b);
285           sshbuf_free(bb);
286           ECDSA_SIG_free(esig);
287           return ret;
288 }
289 
290 static int
ssh_ecdsa_verify(const struct sshkey * key,const u_char * sig,size_t siglen,const u_char * data,size_t dlen,const char * alg,u_int compat,struct sshkey_sig_details ** detailsp)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 {
296           ECDSA_SIG *esig = NULL;
297           BIGNUM *sig_r = NULL, *sig_s = NULL;
298           int hash_alg;
299           u_char digest[SSH_DIGEST_MAX_LENGTH];
300           size_t hlen;
301           int ret = SSH_ERR_INTERNAL_ERROR;
302           struct sshbuf *b = NULL, *sigbuf = NULL;
303           char *ktype = NULL;
304 
305           if (key == NULL || key->ecdsa == NULL ||
306               sshkey_type_plain(key->type) != KEY_ECDSA ||
307               sig == NULL || siglen == 0)
308                     return SSH_ERR_INVALID_ARGUMENT;
309 
310           if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
311               (hlen = ssh_digest_bytes(hash_alg)) == 0)
312                     return SSH_ERR_INTERNAL_ERROR;
313 
314           /* fetch signature */
315           if ((b = sshbuf_from(sig, siglen)) == NULL)
316                     return SSH_ERR_ALLOC_FAIL;
317           if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
318               sshbuf_froms(b, &sigbuf) != 0) {
319                     ret = SSH_ERR_INVALID_FORMAT;
320                     goto out;
321           }
322           if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
323                     ret = SSH_ERR_KEY_TYPE_MISMATCH;
324                     goto out;
325           }
326           if (sshbuf_len(b) != 0) {
327                     ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
328                     goto out;
329           }
330 
331           /* parse signature */
332           if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
333               sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
334                     ret = SSH_ERR_INVALID_FORMAT;
335                     goto out;
336           }
337           if ((esig = ECDSA_SIG_new()) == NULL) {
338                     ret = SSH_ERR_ALLOC_FAIL;
339                     goto out;
340           }
341           if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
342                     ret = SSH_ERR_LIBCRYPTO_ERROR;
343                     goto out;
344           }
345           sig_r = sig_s = NULL; /* transferred */
346 
347           if (sshbuf_len(sigbuf) != 0) {
348                     ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
349                     goto out;
350           }
351           if ((ret = ssh_digest_memory(hash_alg, data, dlen,
352               digest, sizeof(digest))) != 0)
353                     goto out;
354 
355           switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) {
356           case 1:
357                     ret = 0;
358                     break;
359           case 0:
360                     ret = SSH_ERR_SIGNATURE_INVALID;
361                     goto out;
362           default:
363                     ret = SSH_ERR_LIBCRYPTO_ERROR;
364                     goto out;
365           }
366 
367  out:
368           explicit_bzero(digest, sizeof(digest));
369           sshbuf_free(sigbuf);
370           sshbuf_free(b);
371           ECDSA_SIG_free(esig);
372           BN_clear_free(sig_r);
373           BN_clear_free(sig_s);
374           free(ktype);
375           return ret;
376 }
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 */
469