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
48static u_int
49ssh_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
65static void
66ssh_ecdsa_cleanup(struct sshkey *k)
67{
68	EC_KEY_free(k->ecdsa);
69	k->ecdsa = NULL;
70}
71
72static int
73ssh_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
94static int
95ssh_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
110static int
111ssh_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
126static int
127ssh_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
144static int
145ssh_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
156static int
157ssh_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
199static int
200ssh_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
225static int
226ssh_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
290static int
291ssh_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 */
379const 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
394const 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
406const 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
418const 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
430const 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
443const 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
455const 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