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