xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-ecdsa.c (revision 9469f4f13c84743995b7d51c506f9c9849ba30de)
1*9469f4f1Schristos /*	$NetBSD: ssh-ecdsa.c,v 1.16 2024/09/24 21:32:19 christos Exp $	*/
2*9469f4f1Schristos /* $OpenBSD: ssh-ecdsa.c,v 1.27 2024/08/15 00:51:51 djm Exp $ */
3*9469f4f1Schristos 
499214115Schristos /*
599214115Schristos  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
699214115Schristos  * Copyright (c) 2010 Damien Miller.  All rights reserved.
799214115Schristos  *
899214115Schristos  * Redistribution and use in source and binary forms, with or without
999214115Schristos  * modification, are permitted provided that the following conditions
1099214115Schristos  * are met:
1199214115Schristos  * 1. Redistributions of source code must retain the above copyright
1299214115Schristos  *    notice, this list of conditions and the following disclaimer.
1399214115Schristos  * 2. Redistributions in binary form must reproduce the above copyright
1499214115Schristos  *    notice, this list of conditions and the following disclaimer in the
1599214115Schristos  *    documentation and/or other materials provided with the distribution.
1699214115Schristos  *
1799214115Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1899214115Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1999214115Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2099214115Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2199214115Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2299214115Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2399214115Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2499214115Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2599214115Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2699214115Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2799214115Schristos  */
2899214115Schristos 
29185c8f97Schristos #include "includes.h"
30*9469f4f1Schristos __RCSID("$NetBSD: ssh-ecdsa.c,v 1.16 2024/09/24 21:32:19 christos Exp $");
3199214115Schristos #include <sys/types.h>
3299214115Schristos 
3399214115Schristos #include <openssl/bn.h>
3499214115Schristos #include <openssl/ec.h>
3599214115Schristos #include <openssl/ecdsa.h>
3699214115Schristos #include <openssl/evp.h>
3799214115Schristos 
3899214115Schristos #include <string.h>
3999214115Schristos 
408a4530f9Schristos #include "sshbuf.h"
418a4530f9Schristos #include "ssherr.h"
428a4530f9Schristos #include "digest.h"
438a4530f9Schristos #define SSHKEY_INTERNAL
448a4530f9Schristos #include "sshkey.h"
4599214115Schristos 
46*9469f4f1Schristos int
47*9469f4f1Schristos sshkey_ecdsa_fixup_group(EVP_PKEY *k)
48*9469f4f1Schristos {
49*9469f4f1Schristos 	int nids[] = {
50*9469f4f1Schristos 		NID_X9_62_prime256v1,
51*9469f4f1Schristos 		NID_secp384r1,
52*9469f4f1Schristos 		NID_secp521r1,
53*9469f4f1Schristos 		-1
54*9469f4f1Schristos 	};
55*9469f4f1Schristos 	int nid = -1;
56*9469f4f1Schristos 	u_int i;
57*9469f4f1Schristos 	const EC_GROUP *g;
58*9469f4f1Schristos 	EC_KEY *ec = NULL;
59*9469f4f1Schristos 	EC_GROUP *eg = NULL;
60*9469f4f1Schristos 
61*9469f4f1Schristos 	if ((ec = EVP_PKEY_get1_EC_KEY(k)) == NULL ||
62*9469f4f1Schristos 	    (g = EC_KEY_get0_group(ec)) == NULL)
63*9469f4f1Schristos 		goto out;
64*9469f4f1Schristos 	/*
65*9469f4f1Schristos 	 * The group may be stored in a ASN.1 encoded private key in one of two
66*9469f4f1Schristos 	 * ways: as a "named group", which is reconstituted by ASN.1 object ID
67*9469f4f1Schristos 	 * or explicit group parameters encoded into the key blob. Only the
68*9469f4f1Schristos 	 * "named group" case sets the group NID for us, but we can figure
69*9469f4f1Schristos 	 * it out for the other case by comparing against all the groups that
70*9469f4f1Schristos 	 * are supported.
71*9469f4f1Schristos 	 */
72*9469f4f1Schristos 	if ((nid = EC_GROUP_get_curve_name(g)) > 0)
73*9469f4f1Schristos 		goto out;
74*9469f4f1Schristos 	nid = -1;
75*9469f4f1Schristos 	for (i = 0; nids[i] != -1; i++) {
76*9469f4f1Schristos 		if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
77*9469f4f1Schristos 			goto out;
78*9469f4f1Schristos 		if (EC_GROUP_cmp(g, eg, NULL) == 0)
79*9469f4f1Schristos 			break;
80*9469f4f1Schristos 		EC_GROUP_free(eg);
81*9469f4f1Schristos 		eg = NULL;
82*9469f4f1Schristos 	}
83*9469f4f1Schristos 	if (nids[i] == -1)
84*9469f4f1Schristos 		goto out;
85*9469f4f1Schristos 
86*9469f4f1Schristos 	/* Use the group with the NID attached */
87*9469f4f1Schristos 	EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
88*9469f4f1Schristos 	if (EC_KEY_set_group(ec, eg) != 1 ||
89*9469f4f1Schristos 	    EVP_PKEY_set1_EC_KEY(k, ec) != 1)
90*9469f4f1Schristos 		goto out;
91*9469f4f1Schristos 	/* success */
92*9469f4f1Schristos 	nid = nids[i];
93*9469f4f1Schristos  out:
94*9469f4f1Schristos 	EC_KEY_free(ec);
95*9469f4f1Schristos 	EC_GROUP_free(eg);
96*9469f4f1Schristos 	return nid;
97*9469f4f1Schristos }
98*9469f4f1Schristos 
99b1066cf3Schristos static u_int
100b1066cf3Schristos ssh_ecdsa_size(const struct sshkey *key)
10199214115Schristos {
102b1066cf3Schristos 	switch (key->ecdsa_nid) {
103b1066cf3Schristos 	case NID_X9_62_prime256v1:
104b1066cf3Schristos 		return 256;
105b1066cf3Schristos 	case NID_secp384r1:
106b1066cf3Schristos 		return 384;
107b1066cf3Schristos 	case NID_secp521r1:
108b1066cf3Schristos 		return 521;
109b1066cf3Schristos 	default:
110b1066cf3Schristos 		return 0;
111b1066cf3Schristos 	}
112b1066cf3Schristos }
113b1066cf3Schristos 
114b1066cf3Schristos static void
115b1066cf3Schristos ssh_ecdsa_cleanup(struct sshkey *k)
116b1066cf3Schristos {
117*9469f4f1Schristos 	EVP_PKEY_free(k->pkey);
118*9469f4f1Schristos 	k->pkey = NULL;
119b1066cf3Schristos }
120b1066cf3Schristos 
121b1066cf3Schristos static int
122b1066cf3Schristos ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
123b1066cf3Schristos {
124*9469f4f1Schristos 	if (a->pkey == NULL || b->pkey == NULL)
125b1066cf3Schristos 		return 0;
126*9469f4f1Schristos 	return EVP_PKEY_cmp(a->pkey, b->pkey) == 1;
127b1066cf3Schristos }
128b1066cf3Schristos 
129b1066cf3Schristos static int
130b1066cf3Schristos ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
131b1066cf3Schristos     enum sshkey_serialize_rep opts)
132b1066cf3Schristos {
133b1066cf3Schristos 	int r;
134b1066cf3Schristos 
135*9469f4f1Schristos 	if (key->pkey == NULL)
136b1066cf3Schristos 		return SSH_ERR_INVALID_ARGUMENT;
137b1066cf3Schristos 	if ((r = sshbuf_put_cstring(b,
138b1066cf3Schristos 	    sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
139*9469f4f1Schristos 	    (r = sshbuf_put_ec_pkey(b, key->pkey)) != 0)
140b1066cf3Schristos 		return r;
141b1066cf3Schristos 
142b1066cf3Schristos 	return 0;
143b1066cf3Schristos }
144b1066cf3Schristos 
145b1066cf3Schristos static int
146b1066cf3Schristos ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
147b1066cf3Schristos     enum sshkey_serialize_rep opts)
148b1066cf3Schristos {
149b1066cf3Schristos 	int r;
150b1066cf3Schristos 
151b1066cf3Schristos 	if (!sshkey_is_cert(key)) {
152b1066cf3Schristos 		if ((r = ssh_ecdsa_serialize_public(key, b, opts)) != 0)
153b1066cf3Schristos 			return r;
154b1066cf3Schristos 	}
155b1066cf3Schristos 	if ((r = sshbuf_put_bignum2(b,
156*9469f4f1Schristos 	    EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(key->pkey)))) != 0)
157b1066cf3Schristos 		return r;
158b1066cf3Schristos 	return 0;
159b1066cf3Schristos }
160b1066cf3Schristos 
161b1066cf3Schristos static int
162b1066cf3Schristos ssh_ecdsa_generate(struct sshkey *k, int bits)
163b1066cf3Schristos {
164*9469f4f1Schristos 	EVP_PKEY *res = NULL;
165*9469f4f1Schristos 	EVP_PKEY_CTX *ctx = NULL;
166*9469f4f1Schristos 	int ret = SSH_ERR_INTERNAL_ERROR;
167b1066cf3Schristos 
168b1066cf3Schristos 	if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
169b1066cf3Schristos 		return SSH_ERR_KEY_LENGTH;
170*9469f4f1Schristos 
171*9469f4f1Schristos 	if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL)
172b1066cf3Schristos 		return SSH_ERR_ALLOC_FAIL;
173*9469f4f1Schristos 
174*9469f4f1Schristos 	if (EVP_PKEY_keygen_init(ctx) <= 0 ||
175*9469f4f1Schristos 	    EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, k->ecdsa_nid) <= 0 ||
176*9469f4f1Schristos 	    EVP_PKEY_keygen(ctx, &res) <= 0) {
177*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
178*9469f4f1Schristos 		goto out;
179b1066cf3Schristos 	}
180*9469f4f1Schristos 	/* success */
181*9469f4f1Schristos 	k->pkey = res;
182*9469f4f1Schristos 	res = NULL;
183*9469f4f1Schristos 	ret = 0;
184*9469f4f1Schristos  out:
185*9469f4f1Schristos 	EVP_PKEY_free(res);
186*9469f4f1Schristos 	EVP_PKEY_CTX_free(ctx);
187*9469f4f1Schristos 	return ret;
188b1066cf3Schristos }
189b1066cf3Schristos 
190b1066cf3Schristos static int
191b1066cf3Schristos ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
192b1066cf3Schristos {
193*9469f4f1Schristos 	const EC_KEY *ec_from;
194*9469f4f1Schristos 	EC_KEY *ec_to = NULL;
195*9469f4f1Schristos 	int ret = SSH_ERR_INTERNAL_ERROR;
196*9469f4f1Schristos 
197*9469f4f1Schristos 	ec_from = EVP_PKEY_get0_EC_KEY(from->pkey);
198*9469f4f1Schristos 	if (ec_from == NULL)
199*9469f4f1Schristos 		return SSH_ERR_LIBCRYPTO_ERROR;
200*9469f4f1Schristos 
201b1066cf3Schristos 	to->ecdsa_nid = from->ecdsa_nid;
202*9469f4f1Schristos 	if ((ec_to = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
203b1066cf3Schristos 		return SSH_ERR_ALLOC_FAIL;
204*9469f4f1Schristos 	if (EC_KEY_set_public_key(ec_to,
205*9469f4f1Schristos 	    EC_KEY_get0_public_key(ec_from)) != 1) {
206*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
207*9469f4f1Schristos 		goto out;
208*9469f4f1Schristos 	}
209*9469f4f1Schristos 	EVP_PKEY_free(to->pkey);
210*9469f4f1Schristos 	if ((to->pkey = EVP_PKEY_new()) == NULL) {
211*9469f4f1Schristos 		ret = SSH_ERR_ALLOC_FAIL;
212*9469f4f1Schristos 		goto out;
213*9469f4f1Schristos 	}
214*9469f4f1Schristos 	if (EVP_PKEY_set1_EC_KEY(to->pkey, ec_to) != 1) {
215*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
216*9469f4f1Schristos 		goto out;
217*9469f4f1Schristos 	}
218*9469f4f1Schristos 	ret = 0;
219*9469f4f1Schristos  out:
220*9469f4f1Schristos 	EC_KEY_free(ec_to);
221*9469f4f1Schristos 	return ret;
222b1066cf3Schristos }
223b1066cf3Schristos 
224b1066cf3Schristos static int
225b1066cf3Schristos ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
226b1066cf3Schristos     struct sshkey *key)
227b1066cf3Schristos {
228b1066cf3Schristos 	int r;
229b1066cf3Schristos 	char *curve = NULL;
230*9469f4f1Schristos 	EVP_PKEY *pkey = NULL;
231*9469f4f1Schristos 	EC_KEY *ec = NULL;
232b1066cf3Schristos 
233b1066cf3Schristos 	if ((key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype)) == -1)
234b1066cf3Schristos 		return SSH_ERR_INVALID_ARGUMENT;
235b1066cf3Schristos 	if ((r = sshbuf_get_cstring(b, &curve, NULL)) != 0)
236b1066cf3Schristos 		goto out;
237b1066cf3Schristos 	if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
238b1066cf3Schristos 		r = SSH_ERR_EC_CURVE_MISMATCH;
239b1066cf3Schristos 		goto out;
240b1066cf3Schristos 	}
241*9469f4f1Schristos 	if ((ec = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
242b1066cf3Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
243b1066cf3Schristos 		goto out;
244b1066cf3Schristos 	}
245*9469f4f1Schristos 	if ((r = sshbuf_get_eckey(b, ec)) != 0)
246b1066cf3Schristos 		goto out;
247*9469f4f1Schristos 	if (sshkey_ec_validate_public(EC_KEY_get0_group(ec),
248*9469f4f1Schristos 	    EC_KEY_get0_public_key(ec)) != 0) {
249b1066cf3Schristos 		r = SSH_ERR_KEY_INVALID_EC_VALUE;
250b1066cf3Schristos 		goto out;
251b1066cf3Schristos 	}
252*9469f4f1Schristos 	if ((pkey = EVP_PKEY_new()) == NULL) {
253*9469f4f1Schristos 		r = SSH_ERR_ALLOC_FAIL;
254*9469f4f1Schristos 		goto out;
255*9469f4f1Schristos 	}
256*9469f4f1Schristos 	if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) {
257*9469f4f1Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
258*9469f4f1Schristos 		goto out;
259*9469f4f1Schristos 	}
260*9469f4f1Schristos 	EVP_PKEY_free(key->pkey);
261*9469f4f1Schristos 	key->pkey = pkey;
262*9469f4f1Schristos 	pkey = NULL;
263b1066cf3Schristos 	/* success */
264b1066cf3Schristos 	r = 0;
265b1066cf3Schristos #ifdef DEBUG_PK
266*9469f4f1Schristos 	sshkey_dump_ec_point(
267*9469f4f1Schristos 	    EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(key->pkey)),
268*9469f4f1Schristos 	    EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(key->pkey)));
269b1066cf3Schristos #endif
270b1066cf3Schristos  out:
271*9469f4f1Schristos 	EC_KEY_free(ec);
272*9469f4f1Schristos 	EVP_PKEY_free(pkey);
273b1066cf3Schristos 	free(curve);
274b1066cf3Schristos 	return r;
275b1066cf3Schristos }
276b1066cf3Schristos 
277b1066cf3Schristos static int
278b1066cf3Schristos ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b,
279b1066cf3Schristos     struct sshkey *key)
280b1066cf3Schristos {
281b1066cf3Schristos 	int r;
282b1066cf3Schristos 	BIGNUM *exponent = NULL;
283*9469f4f1Schristos 	EC_KEY *ec = NULL;
284b1066cf3Schristos 
285b1066cf3Schristos 	if (!sshkey_is_cert(key)) {
286b1066cf3Schristos 		if ((r = ssh_ecdsa_deserialize_public(ktype, b, key)) != 0)
287b1066cf3Schristos 			return r;
288b1066cf3Schristos 	}
289b1066cf3Schristos 	if ((r = sshbuf_get_bignum2(b, &exponent)) != 0)
290b1066cf3Schristos 		goto out;
291*9469f4f1Schristos 	if ((ec = EVP_PKEY_get1_EC_KEY(key->pkey)) == NULL) {
292b1066cf3Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
293b1066cf3Schristos 		goto out;
294b1066cf3Schristos 	}
295*9469f4f1Schristos 	if (EC_KEY_set_private_key(ec, exponent) != 1) {
296*9469f4f1Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
297b1066cf3Schristos 		goto out;
298*9469f4f1Schristos 	}
299*9469f4f1Schristos 	if ((r = sshkey_ec_validate_private(ec)) != 0)
300*9469f4f1Schristos 		goto out;
301*9469f4f1Schristos 	if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1) {
302*9469f4f1Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
303*9469f4f1Schristos 		goto out;
304*9469f4f1Schristos 	}
305b1066cf3Schristos 	/* success */
306b1066cf3Schristos 	r = 0;
307b1066cf3Schristos  out:
308b1066cf3Schristos 	BN_clear_free(exponent);
309*9469f4f1Schristos 	EC_KEY_free(ec);
310b1066cf3Schristos 	return r;
311b1066cf3Schristos }
312b1066cf3Schristos 
313b1066cf3Schristos static int
314b1066cf3Schristos ssh_ecdsa_sign(struct sshkey *key,
315b1066cf3Schristos     u_char **sigp, size_t *lenp,
316b1066cf3Schristos     const u_char *data, size_t dlen,
317b1066cf3Schristos     const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
318b1066cf3Schristos {
319b1066cf3Schristos 	ECDSA_SIG *esig = NULL;
320*9469f4f1Schristos 	unsigned char *sigb = NULL;
321*9469f4f1Schristos 	const unsigned char *psig;
322aa36fcacSchristos 	const BIGNUM *sig_r, *sig_s;
3238a4530f9Schristos 	int hash_alg;
324*9469f4f1Schristos 	size_t slen = 0;
3258a4530f9Schristos 	struct sshbuf *b = NULL, *bb = NULL;
326*9469f4f1Schristos 	int len = 0, ret = SSH_ERR_INTERNAL_ERROR;
3278a4530f9Schristos 
3288a4530f9Schristos 	if (lenp != NULL)
3298a4530f9Schristos 		*lenp = 0;
3308a4530f9Schristos 	if (sigp != NULL)
3318a4530f9Schristos 		*sigp = NULL;
33299214115Schristos 
333*9469f4f1Schristos 	if (key == NULL || key->pkey == NULL ||
3348a4530f9Schristos 	    sshkey_type_plain(key->type) != KEY_ECDSA)
3358a4530f9Schristos 		return SSH_ERR_INVALID_ARGUMENT;
33699214115Schristos 
337*9469f4f1Schristos 	if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
3388a4530f9Schristos 		return SSH_ERR_INTERNAL_ERROR;
339*9469f4f1Schristos 
340*9469f4f1Schristos 	if ((ret = sshkey_pkey_digest_sign(key->pkey, hash_alg, &sigb, &slen,
341*9469f4f1Schristos 	    data, dlen)) != 0)
3428a4530f9Schristos 		goto out;
34399214115Schristos 
344*9469f4f1Schristos 	psig = sigb;
345*9469f4f1Schristos 	if ((esig = d2i_ECDSA_SIG(NULL, &psig, slen)) == NULL) {
3468a4530f9Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
3478a4530f9Schristos 		goto out;
34899214115Schristos 	}
3498a4530f9Schristos 	if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
3508a4530f9Schristos 		ret = SSH_ERR_ALLOC_FAIL;
3518a4530f9Schristos 		goto out;
3528a4530f9Schristos 	}
353b1066cf3Schristos 	ECDSA_SIG_get0(esig, &sig_r, &sig_s);
354aa36fcacSchristos 	if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
355aa36fcacSchristos 	    (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
3568a4530f9Schristos 		goto out;
3578a4530f9Schristos 	if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
3588a4530f9Schristos 	    (ret = sshbuf_put_stringb(b, bb)) != 0)
3598a4530f9Schristos 		goto out;
3608a4530f9Schristos 	len = sshbuf_len(b);
3618a4530f9Schristos 	if (sigp != NULL) {
3628a4530f9Schristos 		if ((*sigp = malloc(len)) == NULL) {
3638a4530f9Schristos 			ret = SSH_ERR_ALLOC_FAIL;
3648a4530f9Schristos 			goto out;
3658a4530f9Schristos 		}
3668a4530f9Schristos 		memcpy(*sigp, sshbuf_ptr(b), len);
3678a4530f9Schristos 	}
36899214115Schristos 	if (lenp != NULL)
36999214115Schristos 		*lenp = len;
3708a4530f9Schristos 	ret = 0;
3718a4530f9Schristos  out:
372*9469f4f1Schristos 	freezero(sigb, slen);
3738a4530f9Schristos 	sshbuf_free(b);
3748a4530f9Schristos 	sshbuf_free(bb);
375b1066cf3Schristos 	ECDSA_SIG_free(esig);
3768a4530f9Schristos 	return ret;
37799214115Schristos }
37899214115Schristos 
379b1066cf3Schristos static int
3808a4530f9Schristos ssh_ecdsa_verify(const struct sshkey *key,
381b1066cf3Schristos     const u_char *sig, size_t siglen,
382b1066cf3Schristos     const u_char *data, size_t dlen, const char *alg, u_int compat,
383b1066cf3Schristos     struct sshkey_sig_details **detailsp)
38499214115Schristos {
385b1066cf3Schristos 	ECDSA_SIG *esig = NULL;
386aa36fcacSchristos 	BIGNUM *sig_r = NULL, *sig_s = NULL;
387*9469f4f1Schristos 	int hash_alg, len = 0;
3888a4530f9Schristos 	int ret = SSH_ERR_INTERNAL_ERROR;
3898a4530f9Schristos 	struct sshbuf *b = NULL, *sigbuf = NULL;
3908a4530f9Schristos 	char *ktype = NULL;
391*9469f4f1Schristos 	unsigned char *sigb = NULL, *cp;
39299214115Schristos 
393*9469f4f1Schristos 	if (key == NULL || key->pkey == NULL ||
3945101d403Schristos 	    sshkey_type_plain(key->type) != KEY_ECDSA ||
395b1066cf3Schristos 	    sig == NULL || siglen == 0)
3968a4530f9Schristos 		return SSH_ERR_INVALID_ARGUMENT;
3978a4530f9Schristos 
398*9469f4f1Schristos 	if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
3998a4530f9Schristos 		return SSH_ERR_INTERNAL_ERROR;
40099214115Schristos 
40199214115Schristos 	/* fetch signature */
402b1066cf3Schristos 	if ((b = sshbuf_from(sig, siglen)) == NULL)
4038a4530f9Schristos 		return SSH_ERR_ALLOC_FAIL;
4048a4530f9Schristos 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
4058a4530f9Schristos 	    sshbuf_froms(b, &sigbuf) != 0) {
4068a4530f9Schristos 		ret = SSH_ERR_INVALID_FORMAT;
4078a4530f9Schristos 		goto out;
40899214115Schristos 	}
4098a4530f9Schristos 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
4108a4530f9Schristos 		ret = SSH_ERR_KEY_TYPE_MISMATCH;
4118a4530f9Schristos 		goto out;
4128a4530f9Schristos 	}
4138a4530f9Schristos 	if (sshbuf_len(b) != 0) {
4148a4530f9Schristos 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
4158a4530f9Schristos 		goto out;
41699214115Schristos 	}
41799214115Schristos 
41899214115Schristos 	/* parse signature */
419aa36fcacSchristos 	if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
420aa36fcacSchristos 	    sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
421aa36fcacSchristos 		ret = SSH_ERR_INVALID_FORMAT;
422aa36fcacSchristos 		goto out;
423aa36fcacSchristos 	}
424*9469f4f1Schristos 	if (sshbuf_len(sigbuf) != 0) {
425*9469f4f1Schristos 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
426*9469f4f1Schristos 		goto out;
427*9469f4f1Schristos 	}
428*9469f4f1Schristos 
429b1066cf3Schristos 	if ((esig = ECDSA_SIG_new()) == NULL) {
4308a4530f9Schristos 		ret = SSH_ERR_ALLOC_FAIL;
4318a4530f9Schristos 		goto out;
4328a4530f9Schristos 	}
433b1066cf3Schristos 	if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
434b400d007Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
4358a4530f9Schristos 		goto out;
4368a4530f9Schristos 	}
437aa36fcacSchristos 	sig_r = sig_s = NULL; /* transferred */
438aa36fcacSchristos 
439*9469f4f1Schristos 	if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
440*9469f4f1Schristos 		len = 0;
4418a4530f9Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
4428a4530f9Schristos 		goto out;
4438a4530f9Schristos 	}
444*9469f4f1Schristos 	if ((sigb = calloc(1, len)) == NULL) {
445*9469f4f1Schristos 		ret = SSH_ERR_ALLOC_FAIL;
446*9469f4f1Schristos 		goto out;
447*9469f4f1Schristos 	}
448*9469f4f1Schristos 	cp = sigb; /* ASN1_item_i2d increments the pointer past the object */
449*9469f4f1Schristos 	if (i2d_ECDSA_SIG(esig, &cp) != len) {
450*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
451*9469f4f1Schristos 		goto out;
452*9469f4f1Schristos 	}
453*9469f4f1Schristos 	if ((ret = sshkey_pkey_digest_verify(key->pkey, hash_alg,
454*9469f4f1Schristos 	    data, dlen, sigb, len)) != 0)
455*9469f4f1Schristos 		goto out;
456*9469f4f1Schristos 	/* success */
4578a4530f9Schristos  out:
458*9469f4f1Schristos 	freezero(sigb, len);
4598a4530f9Schristos 	sshbuf_free(sigbuf);
4608a4530f9Schristos 	sshbuf_free(b);
461b1066cf3Schristos 	ECDSA_SIG_free(esig);
462aa36fcacSchristos 	BN_clear_free(sig_r);
463aa36fcacSchristos 	BN_clear_free(sig_s);
4648a4530f9Schristos 	free(ktype);
46599214115Schristos 	return ret;
46699214115Schristos }
467b1066cf3Schristos 
468b1066cf3Schristos /* NB. not static; used by ECDSA-SK */
469b1066cf3Schristos const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
470b1066cf3Schristos 	/* .size = */		ssh_ecdsa_size,
471b1066cf3Schristos 	/* .alloc = */		NULL,
472b1066cf3Schristos 	/* .cleanup = */	ssh_ecdsa_cleanup,
473b1066cf3Schristos 	/* .equal = */		ssh_ecdsa_equal,
474b1066cf3Schristos 	/* .ssh_serialize_public = */ ssh_ecdsa_serialize_public,
475b1066cf3Schristos 	/* .ssh_deserialize_public = */ ssh_ecdsa_deserialize_public,
476b1066cf3Schristos 	/* .ssh_serialize_private = */ ssh_ecdsa_serialize_private,
477b1066cf3Schristos 	/* .ssh_deserialize_private = */ ssh_ecdsa_deserialize_private,
478b1066cf3Schristos 	/* .generate = */	ssh_ecdsa_generate,
479b1066cf3Schristos 	/* .copy_public = */	ssh_ecdsa_copy_public,
480b1066cf3Schristos 	/* .sign = */		ssh_ecdsa_sign,
481b1066cf3Schristos 	/* .verify = */		ssh_ecdsa_verify,
482b1066cf3Schristos };
483b1066cf3Schristos 
484b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_nistp256_impl = {
485b1066cf3Schristos 	/* .name = */		"ecdsa-sha2-nistp256",
486b1066cf3Schristos 	/* .shortname = */	"ECDSA",
487b1066cf3Schristos 	/* .sigalg = */		NULL,
488b1066cf3Schristos 	/* .type = */		KEY_ECDSA,
489b1066cf3Schristos 	/* .nid = */		NID_X9_62_prime256v1,
490b1066cf3Schristos 	/* .cert = */		0,
491b1066cf3Schristos 	/* .sigonly = */	0,
492b1066cf3Schristos 	/* .keybits = */	0,
493b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_funcs,
494b1066cf3Schristos };
495b1066cf3Schristos 
496b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = {
497b1066cf3Schristos 	/* .name = */		"ecdsa-sha2-nistp256-cert-v01@openssh.com",
498b1066cf3Schristos 	/* .shortname = */	"ECDSA-CERT",
499b1066cf3Schristos 	/* .sigalg = */		NULL,
500b1066cf3Schristos 	/* .type = */		KEY_ECDSA_CERT,
501b1066cf3Schristos 	/* .nid = */		NID_X9_62_prime256v1,
502b1066cf3Schristos 	/* .cert = */		1,
503b1066cf3Schristos 	/* .sigonly = */	0,
504b1066cf3Schristos 	/* .keybits = */	0,
505b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_funcs,
506b1066cf3Schristos };
507b1066cf3Schristos 
508b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_nistp384_impl = {
509b1066cf3Schristos 	/* .name = */		"ecdsa-sha2-nistp384",
510b1066cf3Schristos 	/* .shortname = */	"ECDSA",
511b1066cf3Schristos 	/* .sigalg = */		NULL,
512b1066cf3Schristos 	/* .type = */		KEY_ECDSA,
513b1066cf3Schristos 	/* .nid = */		NID_secp384r1,
514b1066cf3Schristos 	/* .cert = */		0,
515b1066cf3Schristos 	/* .sigonly = */	0,
516b1066cf3Schristos 	/* .keybits = */	0,
517b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_funcs,
518b1066cf3Schristos };
519b1066cf3Schristos 
520b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = {
521b1066cf3Schristos 	/* .name = */		"ecdsa-sha2-nistp384-cert-v01@openssh.com",
522b1066cf3Schristos 	/* .shortname = */	"ECDSA-CERT",
523b1066cf3Schristos 	/* .sigalg = */		NULL,
524b1066cf3Schristos 	/* .type = */		KEY_ECDSA_CERT,
525b1066cf3Schristos 	/* .nid = */		NID_secp384r1,
526b1066cf3Schristos 	/* .cert = */		1,
527b1066cf3Schristos 	/* .sigonly = */	0,
528b1066cf3Schristos 	/* .keybits = */	0,
529b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_funcs,
530b1066cf3Schristos };
531b1066cf3Schristos 
532b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_nistp521_impl = {
533b1066cf3Schristos 	/* .name = */		"ecdsa-sha2-nistp521",
534b1066cf3Schristos 	/* .shortname = */	"ECDSA",
535b1066cf3Schristos 	/* .sigalg = */		NULL,
536b1066cf3Schristos 	/* .type = */		KEY_ECDSA,
537b1066cf3Schristos 	/* .nid = */		NID_secp521r1,
538b1066cf3Schristos 	/* .cert = */		0,
539b1066cf3Schristos 	/* .sigonly = */	0,
540b1066cf3Schristos 	/* .keybits = */	0,
541b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_funcs,
542b1066cf3Schristos };
543b1066cf3Schristos 
544b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = {
545b1066cf3Schristos 	/* .name = */		"ecdsa-sha2-nistp521-cert-v01@openssh.com",
546b1066cf3Schristos 	/* .shortname = */	"ECDSA-CERT",
547b1066cf3Schristos 	/* .sigalg = */		NULL,
548b1066cf3Schristos 	/* .type = */		KEY_ECDSA_CERT,
549b1066cf3Schristos 	/* .nid = */		NID_secp521r1,
550b1066cf3Schristos 	/* .cert = */		1,
551b1066cf3Schristos 	/* .sigonly = */	0,
552b1066cf3Schristos 	/* .keybits = */	0,
553b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_funcs,
554b1066cf3Schristos };
555