Annotation of src/usr.bin/ssh/ssh-ecdsa.c, Revision 1.21
1.21 ! djm 1: /* $OpenBSD: ssh-ecdsa.c,v 1.20 2022/10/28 00:39:29 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,
88: const char *typename, enum sshkey_serialize_rep opts)
89: {
90: int r;
91:
92: if (key->ecdsa == NULL)
93: return SSH_ERR_INVALID_ARGUMENT;
94: if ((r = sshbuf_put_cstring(b, typename)) != 0 ||
95: (r = sshbuf_put_cstring(b,
96: sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
97: (r = sshbuf_put_eckey(b, key->ecdsa)) != 0)
98: return r;
99:
100: return 0;
101: }
102:
1.20 djm 103: static int
104: ssh_ecdsa_generate(struct sshkey *k, int bits)
105: {
106: EC_KEY *private;
107:
108: if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
109: return SSH_ERR_KEY_LENGTH;
110: if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
111: return SSH_ERR_ALLOC_FAIL;
112: if (EC_KEY_generate_key(private) != 1) {
113: EC_KEY_free(private);
114: return SSH_ERR_LIBCRYPTO_ERROR;
115: }
116: EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
117: k->ecdsa = private;
118: return 0;
119: }
120:
1.21 ! djm 121: static int
! 122: ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
! 123: {
! 124: to->ecdsa_nid = from->ecdsa_nid;
! 125: if ((to->ecdsa = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
! 126: return SSH_ERR_ALLOC_FAIL;
! 127: if (EC_KEY_set_public_key(to->ecdsa,
! 128: EC_KEY_get0_public_key(from->ecdsa)) != 1)
! 129: return SSH_ERR_LIBCRYPTO_ERROR; /* caller will free k->ecdsa */
! 130: return 0;
! 131: }
! 132:
1.11 djm 133: /* ARGSUSED */
1.1 djm 134: int
1.11 djm 135: ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
136: const u_char *data, size_t datalen, u_int compat)
1.1 djm 137: {
1.11 djm 138: ECDSA_SIG *sig = NULL;
1.15 djm 139: const BIGNUM *sig_r, *sig_s;
1.8 djm 140: int hash_alg;
141: u_char digest[SSH_DIGEST_MAX_LENGTH];
1.11 djm 142: size_t len, dlen;
143: struct sshbuf *b = NULL, *bb = NULL;
144: int ret = SSH_ERR_INTERNAL_ERROR;
1.1 djm 145:
1.11 djm 146: if (lenp != NULL)
147: *lenp = 0;
148: if (sigp != NULL)
149: *sigp = NULL;
150:
151: if (key == NULL || key->ecdsa == NULL ||
152: sshkey_type_plain(key->type) != KEY_ECDSA)
153: return SSH_ERR_INVALID_ARGUMENT;
154:
155: if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
156: (dlen = ssh_digest_bytes(hash_alg)) == 0)
157: return SSH_ERR_INTERNAL_ERROR;
158: if ((ret = ssh_digest_memory(hash_alg, data, datalen,
159: digest, sizeof(digest))) != 0)
160: goto out;
161:
162: if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) {
163: ret = SSH_ERR_LIBCRYPTO_ERROR;
164: goto out;
165: }
166:
167: if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
168: ret = SSH_ERR_ALLOC_FAIL;
169: goto out;
170: }
1.15 djm 171: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
172: if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
173: (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
1.11 djm 174: goto out;
175: if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
176: (ret = sshbuf_put_stringb(b, bb)) != 0)
177: goto out;
178: len = sshbuf_len(b);
179: if (sigp != NULL) {
180: if ((*sigp = malloc(len)) == NULL) {
181: ret = SSH_ERR_ALLOC_FAIL;
182: goto out;
183: }
184: memcpy(*sigp, sshbuf_ptr(b), len);
1.1 djm 185: }
186: if (lenp != NULL)
187: *lenp = len;
1.11 djm 188: ret = 0;
189: out:
190: explicit_bzero(digest, sizeof(digest));
1.12 mmcc 191: sshbuf_free(b);
192: sshbuf_free(bb);
1.14 jsing 193: ECDSA_SIG_free(sig);
1.11 djm 194: return ret;
195: }
1.1 djm 196:
1.11 djm 197: /* ARGSUSED */
1.1 djm 198: int
1.11 djm 199: ssh_ecdsa_verify(const struct sshkey *key,
200: const u_char *signature, size_t signaturelen,
201: const u_char *data, size_t datalen, u_int compat)
1.1 djm 202: {
1.11 djm 203: ECDSA_SIG *sig = NULL;
1.15 djm 204: BIGNUM *sig_r = NULL, *sig_s = NULL;
1.8 djm 205: int hash_alg;
1.11 djm 206: u_char digest[SSH_DIGEST_MAX_LENGTH];
207: size_t dlen;
208: int ret = SSH_ERR_INTERNAL_ERROR;
209: struct sshbuf *b = NULL, *sigbuf = NULL;
210: char *ktype = NULL;
211:
212: if (key == NULL || key->ecdsa == NULL ||
1.13 djm 213: sshkey_type_plain(key->type) != KEY_ECDSA ||
214: signature == NULL || signaturelen == 0)
1.11 djm 215: return SSH_ERR_INVALID_ARGUMENT;
216:
217: if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
218: (dlen = ssh_digest_bytes(hash_alg)) == 0)
219: return SSH_ERR_INTERNAL_ERROR;
1.7 djm 220:
1.1 djm 221: /* fetch signature */
1.11 djm 222: if ((b = sshbuf_from(signature, signaturelen)) == NULL)
223: return SSH_ERR_ALLOC_FAIL;
224: if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
225: sshbuf_froms(b, &sigbuf) != 0) {
226: ret = SSH_ERR_INVALID_FORMAT;
227: goto out;
228: }
229: if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
230: ret = SSH_ERR_KEY_TYPE_MISMATCH;
231: goto out;
232: }
233: if (sshbuf_len(b) != 0) {
234: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
235: goto out;
1.1 djm 236: }
237:
238: /* parse signature */
1.16 djm 239: if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
240: sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
241: ret = SSH_ERR_INVALID_FORMAT;
1.11 djm 242: goto out;
243: }
1.16 djm 244: if ((sig = ECDSA_SIG_new()) == NULL) {
245: ret = SSH_ERR_ALLOC_FAIL;
1.11 djm 246: goto out;
247: }
1.15 djm 248: if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) {
249: ret = SSH_ERR_LIBCRYPTO_ERROR;
250: goto out;
251: }
252: sig_r = sig_s = NULL; /* transferred */
253:
1.11 djm 254: if (sshbuf_len(sigbuf) != 0) {
255: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
256: goto out;
257: }
258: if ((ret = ssh_digest_memory(hash_alg, data, datalen,
259: digest, sizeof(digest))) != 0)
260: goto out;
261:
262: switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) {
263: case 1:
264: ret = 0;
265: break;
266: case 0:
267: ret = SSH_ERR_SIGNATURE_INVALID;
268: goto out;
269: default:
270: ret = SSH_ERR_LIBCRYPTO_ERROR;
271: goto out;
1.8 djm 272: }
1.1 djm 273:
1.11 djm 274: out:
1.9 djm 275: explicit_bzero(digest, sizeof(digest));
1.12 mmcc 276: sshbuf_free(sigbuf);
277: sshbuf_free(b);
1.14 jsing 278: ECDSA_SIG_free(sig);
1.15 djm 279: BN_clear_free(sig_r);
280: BN_clear_free(sig_s);
1.11 djm 281: free(ktype);
1.1 djm 282: return ret;
283: }
1.17 djm 284:
1.18 djm 285: /* NB. not static; used by ECDSA-SK */
286: const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
1.17 djm 287: /* .size = */ ssh_ecdsa_size,
288: /* .alloc = */ NULL,
289: /* .cleanup = */ ssh_ecdsa_cleanup,
1.18 djm 290: /* .equal = */ ssh_ecdsa_equal,
1.19 djm 291: /* .ssh_serialize_public = */ ssh_ecdsa_serialize_public,
1.20 djm 292: /* .generate = */ ssh_ecdsa_generate,
1.21 ! djm 293: /* .copy_public = */ ssh_ecdsa_copy_public,
1.17 djm 294: };
295:
296: const struct sshkey_impl sshkey_ecdsa_nistp256_impl = {
297: /* .name = */ "ecdsa-sha2-nistp256",
298: /* .shortname = */ "ECDSA",
299: /* .sigalg = */ NULL,
300: /* .type = */ KEY_ECDSA,
301: /* .nid = */ NID_X9_62_prime256v1,
302: /* .cert = */ 0,
303: /* .sigonly = */ 0,
304: /* .keybits = */ 0,
305: /* .funcs = */ &sshkey_ecdsa_funcs,
306: };
307:
308: const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = {
309: /* .name = */ "ecdsa-sha2-nistp256-cert-v01@openssh.com",
310: /* .shortname = */ "ECDSA-CERT",
311: /* .sigalg = */ NULL,
312: /* .type = */ KEY_ECDSA_CERT,
313: /* .nid = */ NID_X9_62_prime256v1,
314: /* .cert = */ 1,
315: /* .sigonly = */ 0,
316: /* .keybits = */ 0,
317: /* .funcs = */ &sshkey_ecdsa_funcs,
318: };
319:
320: const struct sshkey_impl sshkey_ecdsa_nistp384_impl = {
321: /* .name = */ "ecdsa-sha2-nistp384",
322: /* .shortname = */ "ECDSA",
323: /* .sigalg = */ NULL,
324: /* .type = */ KEY_ECDSA,
325: /* .nid = */ NID_secp384r1,
326: /* .cert = */ 0,
327: /* .sigonly = */ 0,
328: /* .keybits = */ 0,
329: /* .funcs = */ &sshkey_ecdsa_funcs,
330: };
331:
332: const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = {
333: /* .name = */ "ecdsa-sha2-nistp384-cert-v01@openssh.com",
334: /* .shortname = */ "ECDSA-CERT",
335: /* .sigalg = */ NULL,
336: /* .type = */ KEY_ECDSA_CERT,
337: /* .nid = */ NID_secp384r1,
338: /* .cert = */ 1,
339: /* .sigonly = */ 0,
340: /* .keybits = */ 0,
341: /* .funcs = */ &sshkey_ecdsa_funcs,
342: };
343:
344: const struct sshkey_impl sshkey_ecdsa_nistp521_impl = {
345: /* .name = */ "ecdsa-sha2-nistp521",
346: /* .shortname = */ "ECDSA",
347: /* .sigalg = */ NULL,
348: /* .type = */ KEY_ECDSA,
349: /* .nid = */ NID_secp521r1,
350: /* .cert = */ 0,
351: /* .sigonly = */ 0,
352: /* .keybits = */ 0,
353: /* .funcs = */ &sshkey_ecdsa_funcs,
354: };
355:
356: const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = {
357: /* .name = */ "ecdsa-sha2-nistp521-cert-v01@openssh.com",
358: /* .shortname = */ "ECDSA-CERT",
359: /* .sigalg = */ NULL,
360: /* .type = */ KEY_ECDSA_CERT,
361: /* .nid = */ NID_secp521r1,
362: /* .cert = */ 1,
363: /* .sigonly = */ 0,
364: /* .keybits = */ 0,
365: /* .funcs = */ &sshkey_ecdsa_funcs,
366: };