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
45static u_int
46ssh_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
60static void
61ssh_ecdsa_cleanup(struct sshkey *k)
62{
63	EC_KEY_free(k->ecdsa);
64	k->ecdsa = NULL;
65}
66
67static int
68ssh_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
89static int
90ssh_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
105static int
106ssh_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
121static int
122ssh_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
139static int
140ssh_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
151static int
152ssh_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
194static int
195ssh_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
220static int
221ssh_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
285static int
286ssh_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 */
374const 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
389const 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
401const 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
413const 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
425const 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
437const 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
449const 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