Annotation of src/usr.bin/ssh/ssh-ecdsa.c, Revision 1.23
1.23 ! djm 1: /* $OpenBSD: ssh-ecdsa.c,v 1.22 2022/10/28 00:41:52 djm Exp $ */
1.1 djm 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 <sys/types.h>
28:
29: #include <openssl/bn.h>
30: #include <openssl/ec.h>
31: #include <openssl/ecdsa.h>
32: #include <openssl/evp.h>
33:
34: #include <string.h>
35:
1.11 djm 36: #include "sshbuf.h"
37: #include "ssherr.h"
1.8 djm 38: #include "digest.h"
1.11 djm 39: #define SSHKEY_INTERNAL
40: #include "sshkey.h"
1.1 djm 41:
1.17 djm 42: static u_int
43: ssh_ecdsa_size(const struct sshkey *key)
44: {
45: switch (key->ecdsa_nid) {
46: case NID_X9_62_prime256v1:
47: return 256;
48: case NID_secp384r1:
49: return 384;
50: case NID_secp521r1:
51: return 521;
52: default:
53: return 0;
54: }
55: }
56:
57: static void
58: ssh_ecdsa_cleanup(struct sshkey *k)
59: {
60: EC_KEY_free(k->ecdsa);
61: k->ecdsa = NULL;
62: }
63:
1.18 djm 64: static int
65: ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
66: {
67: const EC_GROUP *grp_a, *grp_b;
68: const EC_POINT *pub_a, *pub_b;
69:
70: if (a->ecdsa == NULL || b->ecdsa == NULL)
71: return 0;
72: if ((grp_a = EC_KEY_get0_group(a->ecdsa)) == NULL ||
73: (grp_b = EC_KEY_get0_group(b->ecdsa)) == NULL)
74: return 0;
75: if ((pub_a = EC_KEY_get0_public_key(a->ecdsa)) == NULL ||
76: (pub_b = EC_KEY_get0_public_key(b->ecdsa)) == NULL)
77: return 0;
78: if (EC_GROUP_cmp(grp_a, grp_b, NULL) != 0)
79: return 0;
80: if (EC_POINT_cmp(grp_a, pub_a, pub_b, NULL) != 0)
81: return 0;
1.19 djm 82:
1.18 djm 83: return 1;
84: }
85:
1.19 djm 86: static int
87: ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
1.22 djm 88: enum sshkey_serialize_rep opts)
1.19 djm 89: {
90: int r;
91:
92: if (key->ecdsa == NULL)
93: return SSH_ERR_INVALID_ARGUMENT;
1.22 djm 94: if ((r = sshbuf_put_cstring(b,
1.19 djm 95: sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
96: (r = sshbuf_put_eckey(b, key->ecdsa)) != 0)
97: return r;
98:
99: return 0;
100: }
101:
1.20 djm 102: static int
103: ssh_ecdsa_generate(struct sshkey *k, int bits)
104: {
105: EC_KEY *private;
106:
107: if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
108: return SSH_ERR_KEY_LENGTH;
109: if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
110: return SSH_ERR_ALLOC_FAIL;
111: if (EC_KEY_generate_key(private) != 1) {
112: EC_KEY_free(private);
113: return SSH_ERR_LIBCRYPTO_ERROR;
114: }
115: EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
116: k->ecdsa = private;
117: return 0;
118: }
119:
1.21 djm 120: static int
121: ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
122: {
123: to->ecdsa_nid = from->ecdsa_nid;
124: if ((to->ecdsa = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
125: return SSH_ERR_ALLOC_FAIL;
126: if (EC_KEY_set_public_key(to->ecdsa,
127: EC_KEY_get0_public_key(from->ecdsa)) != 1)
128: return SSH_ERR_LIBCRYPTO_ERROR; /* caller will free k->ecdsa */
129: return 0;
130: }
131:
1.22 djm 132: static int
133: ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
134: struct sshkey *key)
135: {
136: int ret = SSH_ERR_INTERNAL_ERROR;
137: char *curve = NULL;
138: EC_POINT *q = NULL;
139:
140: key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype);
141: if (sshbuf_get_cstring(b, &curve, NULL) != 0) {
142: ret = SSH_ERR_INVALID_FORMAT;
143: goto out;
144: }
145: if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
146: ret = SSH_ERR_EC_CURVE_MISMATCH;
147: goto out;
148: }
149: EC_KEY_free(key->ecdsa);
150: if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
151: ret = SSH_ERR_EC_CURVE_INVALID;
152: goto out;
153: }
154: if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) {
155: ret = SSH_ERR_ALLOC_FAIL;
156: goto out;
157: }
158: if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) {
159: ret = SSH_ERR_INVALID_FORMAT;
160: goto out;
161: }
162: if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
163: ret = SSH_ERR_KEY_INVALID_EC_VALUE;
164: goto out;
165: }
166: if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
167: /* XXX assume it is a allocation error */
168: ret = SSH_ERR_ALLOC_FAIL;
169: goto out;
170: }
171: #ifdef DEBUG_PK
172: sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
173: #endif
174: /* success */
175: ret = 0;
176: out:
177: free(curve);
178: EC_POINT_free(q);
179: return ret;
180: }
181:
1.11 djm 182: /* ARGSUSED */
1.23 ! djm 183: static int
! 184: ssh_ecdsa_sign(struct sshkey *key,
! 185: u_char **sigp, size_t *lenp,
! 186: const u_char *data, size_t dlen,
! 187: const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
1.1 djm 188: {
1.23 ! djm 189: ECDSA_SIG *esig = NULL;
1.15 djm 190: const BIGNUM *sig_r, *sig_s;
1.8 djm 191: int hash_alg;
192: u_char digest[SSH_DIGEST_MAX_LENGTH];
1.23 ! djm 193: size_t len, hlen;
1.11 djm 194: struct sshbuf *b = NULL, *bb = NULL;
195: int ret = SSH_ERR_INTERNAL_ERROR;
1.1 djm 196:
1.11 djm 197: if (lenp != NULL)
198: *lenp = 0;
199: if (sigp != NULL)
200: *sigp = NULL;
201:
202: if (key == NULL || key->ecdsa == NULL ||
203: sshkey_type_plain(key->type) != KEY_ECDSA)
204: return SSH_ERR_INVALID_ARGUMENT;
205:
206: if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
1.23 ! djm 207: (hlen = ssh_digest_bytes(hash_alg)) == 0)
1.11 djm 208: return SSH_ERR_INTERNAL_ERROR;
1.23 ! djm 209: if ((ret = ssh_digest_memory(hash_alg, data, dlen,
1.11 djm 210: digest, sizeof(digest))) != 0)
211: goto out;
212:
1.23 ! djm 213: if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) {
1.11 djm 214: ret = SSH_ERR_LIBCRYPTO_ERROR;
215: goto out;
216: }
217:
218: if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
219: ret = SSH_ERR_ALLOC_FAIL;
220: goto out;
221: }
1.23 ! djm 222: ECDSA_SIG_get0(esig, &sig_r, &sig_s);
1.15 djm 223: if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
224: (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
1.11 djm 225: goto out;
226: if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
227: (ret = sshbuf_put_stringb(b, bb)) != 0)
228: goto out;
229: len = sshbuf_len(b);
230: if (sigp != NULL) {
231: if ((*sigp = malloc(len)) == NULL) {
232: ret = SSH_ERR_ALLOC_FAIL;
233: goto out;
234: }
235: memcpy(*sigp, sshbuf_ptr(b), len);
1.1 djm 236: }
237: if (lenp != NULL)
238: *lenp = len;
1.11 djm 239: ret = 0;
240: out:
241: explicit_bzero(digest, sizeof(digest));
1.12 mmcc 242: sshbuf_free(b);
243: sshbuf_free(bb);
1.23 ! djm 244: ECDSA_SIG_free(esig);
1.11 djm 245: return ret;
246: }
1.1 djm 247:
1.11 djm 248: /* ARGSUSED */
1.23 ! djm 249: static int
1.11 djm 250: ssh_ecdsa_verify(const struct sshkey *key,
1.23 ! djm 251: const u_char *sig, size_t siglen,
! 252: const u_char *data, size_t dlen, const char *alg, u_int compat,
! 253: struct sshkey_sig_details **detailsp)
1.1 djm 254: {
1.23 ! djm 255: ECDSA_SIG *esig = NULL;
1.15 djm 256: BIGNUM *sig_r = NULL, *sig_s = NULL;
1.8 djm 257: int hash_alg;
1.11 djm 258: u_char digest[SSH_DIGEST_MAX_LENGTH];
1.23 ! djm 259: size_t hlen;
1.11 djm 260: int ret = SSH_ERR_INTERNAL_ERROR;
261: struct sshbuf *b = NULL, *sigbuf = NULL;
262: char *ktype = NULL;
263:
264: if (key == NULL || key->ecdsa == NULL ||
1.13 djm 265: sshkey_type_plain(key->type) != KEY_ECDSA ||
1.23 ! djm 266: sig == NULL || siglen == 0)
1.11 djm 267: return SSH_ERR_INVALID_ARGUMENT;
268:
269: if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
1.23 ! djm 270: (hlen = ssh_digest_bytes(hash_alg)) == 0)
1.11 djm 271: return SSH_ERR_INTERNAL_ERROR;
1.7 djm 272:
1.1 djm 273: /* fetch signature */
1.23 ! djm 274: if ((b = sshbuf_from(sig, siglen)) == NULL)
1.11 djm 275: return SSH_ERR_ALLOC_FAIL;
276: if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
277: sshbuf_froms(b, &sigbuf) != 0) {
278: ret = SSH_ERR_INVALID_FORMAT;
279: goto out;
280: }
281: if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
282: ret = SSH_ERR_KEY_TYPE_MISMATCH;
283: goto out;
284: }
285: if (sshbuf_len(b) != 0) {
286: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
287: goto out;
1.1 djm 288: }
289:
290: /* parse signature */
1.16 djm 291: if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
292: sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
293: ret = SSH_ERR_INVALID_FORMAT;
1.11 djm 294: goto out;
295: }
1.23 ! djm 296: if ((esig = ECDSA_SIG_new()) == NULL) {
1.16 djm 297: ret = SSH_ERR_ALLOC_FAIL;
1.11 djm 298: goto out;
299: }
1.23 ! djm 300: if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
1.15 djm 301: ret = SSH_ERR_LIBCRYPTO_ERROR;
302: goto out;
303: }
304: sig_r = sig_s = NULL; /* transferred */
305:
1.11 djm 306: if (sshbuf_len(sigbuf) != 0) {
307: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
308: goto out;
309: }
1.23 ! djm 310: if ((ret = ssh_digest_memory(hash_alg, data, dlen,
1.11 djm 311: digest, sizeof(digest))) != 0)
312: goto out;
313:
1.23 ! djm 314: switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) {
1.11 djm 315: case 1:
316: ret = 0;
317: break;
318: case 0:
319: ret = SSH_ERR_SIGNATURE_INVALID;
320: goto out;
321: default:
322: ret = SSH_ERR_LIBCRYPTO_ERROR;
323: goto out;
1.8 djm 324: }
1.1 djm 325:
1.11 djm 326: out:
1.9 djm 327: explicit_bzero(digest, sizeof(digest));
1.12 mmcc 328: sshbuf_free(sigbuf);
329: sshbuf_free(b);
1.23 ! djm 330: ECDSA_SIG_free(esig);
1.15 djm 331: BN_clear_free(sig_r);
332: BN_clear_free(sig_s);
1.11 djm 333: free(ktype);
1.1 djm 334: return ret;
335: }
1.17 djm 336:
1.18 djm 337: /* NB. not static; used by ECDSA-SK */
338: const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
1.17 djm 339: /* .size = */ ssh_ecdsa_size,
340: /* .alloc = */ NULL,
341: /* .cleanup = */ ssh_ecdsa_cleanup,
1.18 djm 342: /* .equal = */ ssh_ecdsa_equal,
1.19 djm 343: /* .ssh_serialize_public = */ ssh_ecdsa_serialize_public,
1.22 djm 344: /* .ssh_deserialize_public = */ ssh_ecdsa_deserialize_public,
1.20 djm 345: /* .generate = */ ssh_ecdsa_generate,
1.21 djm 346: /* .copy_public = */ ssh_ecdsa_copy_public,
1.23 ! djm 347: /* .sign = */ ssh_ecdsa_sign,
! 348: /* .verify = */ ssh_ecdsa_verify,
1.17 djm 349: };
350:
351: const struct sshkey_impl sshkey_ecdsa_nistp256_impl = {
352: /* .name = */ "ecdsa-sha2-nistp256",
353: /* .shortname = */ "ECDSA",
354: /* .sigalg = */ NULL,
355: /* .type = */ KEY_ECDSA,
356: /* .nid = */ NID_X9_62_prime256v1,
357: /* .cert = */ 0,
358: /* .sigonly = */ 0,
359: /* .keybits = */ 0,
360: /* .funcs = */ &sshkey_ecdsa_funcs,
361: };
362:
363: const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = {
364: /* .name = */ "ecdsa-sha2-nistp256-cert-v01@openssh.com",
365: /* .shortname = */ "ECDSA-CERT",
366: /* .sigalg = */ NULL,
367: /* .type = */ KEY_ECDSA_CERT,
368: /* .nid = */ NID_X9_62_prime256v1,
369: /* .cert = */ 1,
370: /* .sigonly = */ 0,
371: /* .keybits = */ 0,
372: /* .funcs = */ &sshkey_ecdsa_funcs,
373: };
374:
375: const struct sshkey_impl sshkey_ecdsa_nistp384_impl = {
376: /* .name = */ "ecdsa-sha2-nistp384",
377: /* .shortname = */ "ECDSA",
378: /* .sigalg = */ NULL,
379: /* .type = */ KEY_ECDSA,
380: /* .nid = */ NID_secp384r1,
381: /* .cert = */ 0,
382: /* .sigonly = */ 0,
383: /* .keybits = */ 0,
384: /* .funcs = */ &sshkey_ecdsa_funcs,
385: };
386:
387: const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = {
388: /* .name = */ "ecdsa-sha2-nistp384-cert-v01@openssh.com",
389: /* .shortname = */ "ECDSA-CERT",
390: /* .sigalg = */ NULL,
391: /* .type = */ KEY_ECDSA_CERT,
392: /* .nid = */ NID_secp384r1,
393: /* .cert = */ 1,
394: /* .sigonly = */ 0,
395: /* .keybits = */ 0,
396: /* .funcs = */ &sshkey_ecdsa_funcs,
397: };
398:
399: const struct sshkey_impl sshkey_ecdsa_nistp521_impl = {
400: /* .name = */ "ecdsa-sha2-nistp521",
401: /* .shortname = */ "ECDSA",
402: /* .sigalg = */ NULL,
403: /* .type = */ KEY_ECDSA,
404: /* .nid = */ NID_secp521r1,
405: /* .cert = */ 0,
406: /* .sigonly = */ 0,
407: /* .keybits = */ 0,
408: /* .funcs = */ &sshkey_ecdsa_funcs,
409: };
410:
411: const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = {
412: /* .name = */ "ecdsa-sha2-nistp521-cert-v01@openssh.com",
413: /* .shortname = */ "ECDSA-CERT",
414: /* .sigalg = */ NULL,
415: /* .type = */ KEY_ECDSA_CERT,
416: /* .nid = */ NID_secp521r1,
417: /* .cert = */ 1,
418: /* .sigonly = */ 0,
419: /* .keybits = */ 0,
420: /* .funcs = */ &sshkey_ecdsa_funcs,
421: };