xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-ecdsa-sk.c (revision 9469f4f13c84743995b7d51c506f9c9849ba30de)
1*9469f4f1Schristos /*	$NetBSD: ssh-ecdsa-sk.c,v 1.5 2024/09/24 21:32:19 christos Exp $	*/
2*9469f4f1Schristos /* $OpenBSD: ssh-ecdsa-sk.c,v 1.19 2024/08/15 00:51:51 djm Exp $ */
3*9469f4f1Schristos 
418504831Schristos /*
518504831Schristos  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
618504831Schristos  * Copyright (c) 2010 Damien Miller.  All rights reserved.
718504831Schristos  * Copyright (c) 2019 Google Inc.  All rights reserved.
818504831Schristos  *
918504831Schristos  * Redistribution and use in source and binary forms, with or without
1018504831Schristos  * modification, are permitted provided that the following conditions
1118504831Schristos  * are met:
1218504831Schristos  * 1. Redistributions of source code must retain the above copyright
1318504831Schristos  *    notice, this list of conditions and the following disclaimer.
1418504831Schristos  * 2. Redistributions in binary form must reproduce the above copyright
1518504831Schristos  *    notice, this list of conditions and the following disclaimer in the
1618504831Schristos  *    documentation and/or other materials provided with the distribution.
1718504831Schristos  *
1818504831Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1918504831Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2018504831Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2118504831Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2218504831Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2318504831Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2418504831Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2518504831Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2618504831Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2718504831Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2818504831Schristos  */
29ed75d7a8Schristos #include "includes.h"
30*9469f4f1Schristos __RCSID("$NetBSD: ssh-ecdsa-sk.c,v 1.5 2024/09/24 21:32:19 christos Exp $");
3118504831Schristos 
3218504831Schristos /* #define DEBUG_SK 1 */
3318504831Schristos 
3418504831Schristos #include <sys/types.h>
3518504831Schristos 
3618504831Schristos #include <openssl/bn.h>
3718504831Schristos #include <openssl/ec.h>
3818504831Schristos #include <openssl/ecdsa.h>
3918504831Schristos #include <openssl/evp.h>
4018504831Schristos 
4118504831Schristos #include <string.h>
4218504831Schristos #include <stdio.h> /* needed for DEBUG_SK only */
4318504831Schristos 
4418504831Schristos #include "sshbuf.h"
4518504831Schristos #include "ssherr.h"
4618504831Schristos #include "digest.h"
4718504831Schristos #define SSHKEY_INTERNAL
4818504831Schristos #include "sshkey.h"
4918504831Schristos 
50b1066cf3Schristos /* Reuse some ECDSA internals */
51b1066cf3Schristos extern struct sshkey_impl_funcs sshkey_ecdsa_funcs;
52b1066cf3Schristos 
53b1066cf3Schristos static void
54b1066cf3Schristos ssh_ecdsa_sk_cleanup(struct sshkey *k)
55b1066cf3Schristos {
56b1066cf3Schristos 	sshkey_sk_cleanup(k);
57b1066cf3Schristos 	sshkey_ecdsa_funcs.cleanup(k);
58b1066cf3Schristos }
59b1066cf3Schristos 
60b1066cf3Schristos static int
61b1066cf3Schristos ssh_ecdsa_sk_equal(const struct sshkey *a, const struct sshkey *b)
62b1066cf3Schristos {
63b1066cf3Schristos 	if (!sshkey_sk_fields_equal(a, b))
64b1066cf3Schristos 		return 0;
65b1066cf3Schristos 	if (!sshkey_ecdsa_funcs.equal(a, b))
66b1066cf3Schristos 		return 0;
67b1066cf3Schristos 	return 1;
68b1066cf3Schristos }
69b1066cf3Schristos 
70b1066cf3Schristos static int
71b1066cf3Schristos ssh_ecdsa_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
72b1066cf3Schristos     enum sshkey_serialize_rep opts)
73b1066cf3Schristos {
74b1066cf3Schristos 	int r;
75b1066cf3Schristos 
76b1066cf3Schristos 	if ((r = sshkey_ecdsa_funcs.serialize_public(key, b, opts)) != 0)
77b1066cf3Schristos 		return r;
78b1066cf3Schristos 	if ((r = sshkey_serialize_sk(key, b)) != 0)
79b1066cf3Schristos 		return r;
80b1066cf3Schristos 
81b1066cf3Schristos 	return 0;
82b1066cf3Schristos }
83b1066cf3Schristos 
84b1066cf3Schristos static int
85b1066cf3Schristos ssh_ecdsa_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
86b1066cf3Schristos     enum sshkey_serialize_rep opts)
87b1066cf3Schristos {
88b1066cf3Schristos 	int r;
89b1066cf3Schristos 
90b1066cf3Schristos 	if (!sshkey_is_cert(key)) {
91b1066cf3Schristos 		if ((r = sshkey_ecdsa_funcs.serialize_public(key,
92b1066cf3Schristos 		    b, opts)) != 0)
93b1066cf3Schristos 			return r;
94b1066cf3Schristos 	}
95b1066cf3Schristos 	if ((r = sshkey_serialize_private_sk(key, b)) != 0)
96b1066cf3Schristos 		return r;
97b1066cf3Schristos 
98b1066cf3Schristos 	return 0;
99b1066cf3Schristos }
100b1066cf3Schristos 
101b1066cf3Schristos static int
102b1066cf3Schristos ssh_ecdsa_sk_copy_public(const struct sshkey *from, struct sshkey *to)
103b1066cf3Schristos {
104b1066cf3Schristos 	int r;
105b1066cf3Schristos 
106b1066cf3Schristos 	if ((r = sshkey_ecdsa_funcs.copy_public(from, to)) != 0)
107b1066cf3Schristos 		return r;
108b1066cf3Schristos 	if ((r = sshkey_copy_public_sk(from, to)) != 0)
109b1066cf3Schristos 		return r;
110b1066cf3Schristos 	return 0;
111b1066cf3Schristos }
112b1066cf3Schristos 
113b1066cf3Schristos static int
114b1066cf3Schristos ssh_ecdsa_sk_deserialize_public(const char *ktype, struct sshbuf *b,
115b1066cf3Schristos     struct sshkey *key)
116b1066cf3Schristos {
117b1066cf3Schristos 	int r;
118b1066cf3Schristos 
119b1066cf3Schristos 	if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype, b, key)) != 0)
120b1066cf3Schristos 		return r;
121b1066cf3Schristos 	if ((r = sshkey_deserialize_sk(b, key)) != 0)
122b1066cf3Schristos 		return r;
123b1066cf3Schristos 	return 0;
124b1066cf3Schristos }
125b1066cf3Schristos 
126b1066cf3Schristos static int
127b1066cf3Schristos ssh_ecdsa_sk_deserialize_private(const char *ktype, struct sshbuf *b,
128b1066cf3Schristos     struct sshkey *key)
129b1066cf3Schristos {
130b1066cf3Schristos 	int r;
131b1066cf3Schristos 
132b1066cf3Schristos 	if (!sshkey_is_cert(key)) {
133b1066cf3Schristos 		if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype,
134b1066cf3Schristos 		    b, key)) != 0)
135b1066cf3Schristos 			return r;
136b1066cf3Schristos 	}
137b1066cf3Schristos 	if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
138b1066cf3Schristos 		return r;
139b1066cf3Schristos 
140b1066cf3Schristos 	return 0;
141b1066cf3Schristos }
142b1066cf3Schristos 
1432d3b0f52Schristos /*
1442d3b0f52Schristos  * Check FIDO/W3C webauthn signatures clientData field against the expected
1452d3b0f52Schristos  * format and prepare a hash of it for use in signature verification.
1462d3b0f52Schristos  *
1472d3b0f52Schristos  * webauthn signatures do not sign the hash of the message directly, but
1482d3b0f52Schristos  * instead sign a JSON-like "clientData" wrapper structure that contains the
1492d3b0f52Schristos  * message hash along with a other information.
1502d3b0f52Schristos  *
1512d3b0f52Schristos  * Fortunately this structure has a fixed format so it is possible to verify
1522d3b0f52Schristos  * that the hash of the signed message is present within the clientData
1532d3b0f52Schristos  * structure without needing to implement any JSON parsing.
1542d3b0f52Schristos  */
1552d3b0f52Schristos static int
1562d3b0f52Schristos webauthn_check_prepare_hash(const u_char *data, size_t datalen,
1572d3b0f52Schristos     const char *origin, const struct sshbuf *wrapper,
1582d3b0f52Schristos     uint8_t flags, const struct sshbuf *extensions,
1592d3b0f52Schristos     u_char *msghash, size_t msghashlen)
1602d3b0f52Schristos {
1612d3b0f52Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
1622d3b0f52Schristos 	struct sshbuf *chall = NULL, *m = NULL;
1632d3b0f52Schristos 
1642d3b0f52Schristos 	if ((m = sshbuf_new()) == NULL ||
1652d3b0f52Schristos 	    (chall = sshbuf_from(data, datalen)) == NULL) {
1662d3b0f52Schristos 		r = SSH_ERR_ALLOC_FAIL;
1672d3b0f52Schristos 		goto out;
1682d3b0f52Schristos 	}
1692d3b0f52Schristos 	/*
1702d3b0f52Schristos 	 * Ensure origin contains no quote character and that the flags are
1712d3b0f52Schristos 	 * consistent with what we received
1722d3b0f52Schristos 	 */
1732d3b0f52Schristos 	if (strchr(origin, '\"') != NULL ||
1742d3b0f52Schristos 	    (flags & 0x40) != 0 /* AD */ ||
1752d3b0f52Schristos 	    ((flags & 0x80) == 0 /* ED */) != (sshbuf_len(extensions) == 0)) {
1762d3b0f52Schristos 		r = SSH_ERR_INVALID_FORMAT;
1772d3b0f52Schristos 		goto out;
1782d3b0f52Schristos 	}
1792d3b0f52Schristos 
1802d3b0f52Schristos 	/*
1812d3b0f52Schristos 	 * Prepare the preamble to clientData that we expect, poking the
1822d3b0f52Schristos 	 * challenge and origin into their canonical positions in the
1832d3b0f52Schristos 	 * structure. The crossOrigin flag and any additional extension
1842d3b0f52Schristos 	 * fields present are ignored.
1852d3b0f52Schristos 	 */
1862d3b0f52Schristos #define WEBAUTHN_0	"{\"type\":\"webauthn.get\",\"challenge\":\""
1872d3b0f52Schristos #define WEBAUTHN_1	"\",\"origin\":\""
1882d3b0f52Schristos #define WEBAUTHN_2	"\""
1892d3b0f52Schristos 	if ((r = sshbuf_put(m, WEBAUTHN_0, sizeof(WEBAUTHN_0) - 1)) != 0 ||
1902d3b0f52Schristos 	    (r = sshbuf_dtourlb64(chall, m, 0)) != 0 ||
1912d3b0f52Schristos 	    (r = sshbuf_put(m, WEBAUTHN_1, sizeof(WEBAUTHN_1) - 1)) != 0 ||
1922d3b0f52Schristos 	    (r = sshbuf_put(m, origin, strlen(origin))) != 0 ||
1932d3b0f52Schristos 	    (r = sshbuf_put(m, WEBAUTHN_2, sizeof(WEBAUTHN_2) - 1)) != 0)
1942d3b0f52Schristos 		goto out;
1952d3b0f52Schristos #ifdef DEBUG_SK
1962d3b0f52Schristos 	fprintf(stderr, "%s: received origin: %s\n", __func__, origin);
1972d3b0f52Schristos 	fprintf(stderr, "%s: received clientData:\n", __func__);
1982d3b0f52Schristos 	sshbuf_dump(wrapper, stderr);
1992d3b0f52Schristos 	fprintf(stderr, "%s: expected clientData premable:\n", __func__);
2002d3b0f52Schristos 	sshbuf_dump(m, stderr);
2012d3b0f52Schristos #endif
2022d3b0f52Schristos 	/* Check that the supplied clientData has the preamble we expect */
2032d3b0f52Schristos 	if ((r = sshbuf_cmp(wrapper, 0, sshbuf_ptr(m), sshbuf_len(m))) != 0)
2042d3b0f52Schristos 		goto out;
2052d3b0f52Schristos 
2062d3b0f52Schristos 	/* Prepare hash of clientData */
2072d3b0f52Schristos 	if ((r = ssh_digest_buffer(SSH_DIGEST_SHA256, wrapper,
2082d3b0f52Schristos 	    msghash, msghashlen)) != 0)
2092d3b0f52Schristos 		goto out;
2102d3b0f52Schristos 
2112d3b0f52Schristos 	/* success */
2122d3b0f52Schristos 	r = 0;
2132d3b0f52Schristos  out:
2142d3b0f52Schristos 	sshbuf_free(chall);
2152d3b0f52Schristos 	sshbuf_free(m);
2162d3b0f52Schristos 	return r;
2172d3b0f52Schristos }
2182d3b0f52Schristos 
219b1066cf3Schristos static int
22018504831Schristos ssh_ecdsa_sk_verify(const struct sshkey *key,
221b1066cf3Schristos     const u_char *sig, size_t siglen,
222b1066cf3Schristos     const u_char *data, size_t dlen, const char *alg, u_int compat,
22318504831Schristos     struct sshkey_sig_details **detailsp)
22418504831Schristos {
225b1066cf3Schristos 	ECDSA_SIG *esig = NULL;
226*9469f4f1Schristos 	EVP_MD_CTX *md_ctx = NULL;
22718504831Schristos 	BIGNUM *sig_r = NULL, *sig_s = NULL;
22818504831Schristos 	u_char sig_flags;
229*9469f4f1Schristos 	u_char msghash[32], apphash[32];
23018504831Schristos 	u_int sig_counter;
231*9469f4f1Schristos 	u_char *sigb = NULL, *cp;
232*9469f4f1Schristos 	int is_webauthn = 0, ret = SSH_ERR_INTERNAL_ERROR, len = 0;
23318504831Schristos 	struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL;
2342d3b0f52Schristos 	struct sshbuf *webauthn_wrapper = NULL, *webauthn_exts = NULL;
2352d3b0f52Schristos 	char *ktype = NULL, *webauthn_origin = NULL;
23618504831Schristos 	struct sshkey_sig_details *details = NULL;
23718504831Schristos #ifdef DEBUG_SK
23818504831Schristos 	char *tmp = NULL;
23918504831Schristos #endif
24018504831Schristos 
24118504831Schristos 	if (detailsp != NULL)
24218504831Schristos 		*detailsp = NULL;
243*9469f4f1Schristos 	if (key == NULL || key->pkey == NULL ||
24418504831Schristos 	    sshkey_type_plain(key->type) != KEY_ECDSA_SK ||
245b1066cf3Schristos 	    sig == NULL || siglen == 0)
24618504831Schristos 		return SSH_ERR_INVALID_ARGUMENT;
24718504831Schristos 
24818504831Schristos 	if (key->ecdsa_nid != NID_X9_62_prime256v1)
24918504831Schristos 		return SSH_ERR_INTERNAL_ERROR;
25018504831Schristos 
25118504831Schristos 	/* fetch signature */
252b1066cf3Schristos 	if ((b = sshbuf_from(sig, siglen)) == NULL)
25318504831Schristos 		return SSH_ERR_ALLOC_FAIL;
2542d3b0f52Schristos 	if ((details = calloc(1, sizeof(*details))) == NULL) {
2552d3b0f52Schristos 		ret = SSH_ERR_ALLOC_FAIL;
2562d3b0f52Schristos 		goto out;
2572d3b0f52Schristos 	}
2582d3b0f52Schristos 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
2592d3b0f52Schristos 		ret = SSH_ERR_INVALID_FORMAT;
2602d3b0f52Schristos 		goto out;
2612d3b0f52Schristos 	}
2622d3b0f52Schristos 	if (strcmp(ktype, "webauthn-sk-ecdsa-sha2-nistp256@openssh.com") == 0)
2632d3b0f52Schristos 		is_webauthn = 1;
2642d3b0f52Schristos 	else if (strcmp(ktype, "sk-ecdsa-sha2-nistp256@openssh.com") != 0) {
2652d3b0f52Schristos 		ret = SSH_ERR_INVALID_FORMAT;
2662d3b0f52Schristos 		goto out;
2672d3b0f52Schristos 	}
2682d3b0f52Schristos 	if (sshbuf_froms(b, &sigbuf) != 0 ||
26918504831Schristos 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
27018504831Schristos 	    sshbuf_get_u32(b, &sig_counter) != 0) {
27118504831Schristos 		ret = SSH_ERR_INVALID_FORMAT;
27218504831Schristos 		goto out;
27318504831Schristos 	}
2742d3b0f52Schristos 	if (is_webauthn) {
2752d3b0f52Schristos 		if (sshbuf_get_cstring(b, &webauthn_origin, NULL) != 0 ||
2762d3b0f52Schristos 		    sshbuf_froms(b, &webauthn_wrapper) != 0 ||
2772d3b0f52Schristos 		    sshbuf_froms(b, &webauthn_exts) != 0) {
2782d3b0f52Schristos 			ret = SSH_ERR_INVALID_FORMAT;
27918504831Schristos 			goto out;
28018504831Schristos 		}
2812d3b0f52Schristos 	}
28218504831Schristos 	if (sshbuf_len(b) != 0) {
28318504831Schristos 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
28418504831Schristos 		goto out;
28518504831Schristos 	}
28618504831Schristos 
28718504831Schristos 	/* parse signature */
28818504831Schristos 	if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
28918504831Schristos 	    sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
29018504831Schristos 		ret = SSH_ERR_INVALID_FORMAT;
29118504831Schristos 		goto out;
29218504831Schristos 	}
2932d3b0f52Schristos 	if (sshbuf_len(sigbuf) != 0) {
2942d3b0f52Schristos 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
29518504831Schristos 		goto out;
29618504831Schristos 	}
2972d3b0f52Schristos 
29818504831Schristos #ifdef DEBUG_SK
29918504831Schristos 	fprintf(stderr, "%s: data: (len %zu)\n", __func__, datalen);
30018504831Schristos 	/* sshbuf_dump_data(data, datalen, stderr); */
30118504831Schristos 	fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r)));
30218504831Schristos 	free(tmp);
30318504831Schristos 	fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s)));
30418504831Schristos 	free(tmp);
30518504831Schristos 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
30618504831Schristos 	    __func__, sig_flags, sig_counter);
3072d3b0f52Schristos 	if (is_webauthn) {
3082d3b0f52Schristos 		fprintf(stderr, "%s: webauthn origin: %s\n", __func__,
3092d3b0f52Schristos 		    webauthn_origin);
3102d3b0f52Schristos 		fprintf(stderr, "%s: webauthn_wrapper:\n", __func__);
3112d3b0f52Schristos 		sshbuf_dump(webauthn_wrapper, stderr);
3122d3b0f52Schristos 	}
31318504831Schristos #endif
314b1066cf3Schristos 	if ((esig = ECDSA_SIG_new()) == NULL) {
3152d3b0f52Schristos 		ret = SSH_ERR_ALLOC_FAIL;
31618504831Schristos 		goto out;
31718504831Schristos 	}
318b1066cf3Schristos 	if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
3192d3b0f52Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
3202d3b0f52Schristos 		goto out;
3212d3b0f52Schristos 	}
3222d3b0f52Schristos 	sig_r = sig_s = NULL; /* transferred */
32318504831Schristos 
32418504831Schristos 	/* Reconstruct data that was supposedly signed */
32518504831Schristos 	if ((original_signed = sshbuf_new()) == NULL) {
32618504831Schristos 		ret = SSH_ERR_ALLOC_FAIL;
32718504831Schristos 		goto out;
32818504831Schristos 	}
3292d3b0f52Schristos 	if (is_webauthn) {
330b1066cf3Schristos 		if ((ret = webauthn_check_prepare_hash(data, dlen,
3312d3b0f52Schristos 		    webauthn_origin, webauthn_wrapper, sig_flags, webauthn_exts,
3322d3b0f52Schristos 		    msghash, sizeof(msghash))) != 0)
3332d3b0f52Schristos 			goto out;
334b1066cf3Schristos 	} else if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
33518504831Schristos 	    msghash, sizeof(msghash))) != 0)
33618504831Schristos 		goto out;
33718504831Schristos 	/* Application value is hashed before signature */
33818504831Schristos 	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
33918504831Schristos 	    strlen(key->sk_application), apphash, sizeof(apphash))) != 0)
34018504831Schristos 		goto out;
34118504831Schristos #ifdef DEBUG_SK
34218504831Schristos 	fprintf(stderr, "%s: hashed application:\n", __func__);
34318504831Schristos 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
34418504831Schristos 	fprintf(stderr, "%s: hashed message:\n", __func__);
34518504831Schristos 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
34618504831Schristos #endif
34718504831Schristos 	if ((ret = sshbuf_put(original_signed,
34818504831Schristos 	    apphash, sizeof(apphash))) != 0 ||
34918504831Schristos 	    (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 ||
35018504831Schristos 	    (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 ||
3512d3b0f52Schristos 	    (ret = sshbuf_putb(original_signed, webauthn_exts)) != 0 ||
35218504831Schristos 	    (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0)
35318504831Schristos 		goto out;
35418504831Schristos 	details->sk_counter = sig_counter;
35518504831Schristos 	details->sk_flags = sig_flags;
35618504831Schristos #ifdef DEBUG_SK
35718504831Schristos 	fprintf(stderr, "%s: signed buf:\n", __func__);
35818504831Schristos 	sshbuf_dump(original_signed, stderr);
35918504831Schristos #endif
36018504831Schristos 
361*9469f4f1Schristos 	if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
362*9469f4f1Schristos 		ret = SSH_ERR_ALLOC_FAIL;
363*9469f4f1Schristos 		goto out;
364*9469f4f1Schristos 	}
365*9469f4f1Schristos 	if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
366*9469f4f1Schristos 		len = 0;
367*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
368*9469f4f1Schristos 		goto out;
369*9469f4f1Schristos 	}
370*9469f4f1Schristos 	if ((sigb = calloc(1, len)) == NULL) {
371*9469f4f1Schristos 		ret = SSH_ERR_ALLOC_FAIL;
372*9469f4f1Schristos 		goto out;
373*9469f4f1Schristos 	}
374*9469f4f1Schristos 	cp = sigb; /* ASN1_item_i2d increments the pointer past the object */
375*9469f4f1Schristos 	if (i2d_ECDSA_SIG(esig, &cp) != len) {
376*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
377*9469f4f1Schristos 		goto out;
378*9469f4f1Schristos 	}
379*9469f4f1Schristos #ifdef DEBUG_SK
380*9469f4f1Schristos 	fprintf(stderr, "%s: signed hash:\n", __func__);
381*9469f4f1Schristos 	sshbuf_dump_data(sigb, len, stderr);
382*9469f4f1Schristos #endif
38318504831Schristos 	/* Verify it */
384*9469f4f1Schristos 	if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL,
385*9469f4f1Schristos 	    key->pkey) != 1) {
386*9469f4f1Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
387*9469f4f1Schristos 		goto out;
388*9469f4f1Schristos 	}
389*9469f4f1Schristos 	switch (EVP_DigestVerify(md_ctx, sigb, len,
390*9469f4f1Schristos 	    sshbuf_ptr(original_signed), sshbuf_len(original_signed))) {
39118504831Schristos 	case 1:
39218504831Schristos 		ret = 0;
39318504831Schristos 		break;
39418504831Schristos 	case 0:
39518504831Schristos 		ret = SSH_ERR_SIGNATURE_INVALID;
39618504831Schristos 		goto out;
39718504831Schristos 	default:
39818504831Schristos 		ret = SSH_ERR_LIBCRYPTO_ERROR;
39918504831Schristos 		goto out;
40018504831Schristos 	}
40118504831Schristos 	/* success */
40218504831Schristos 	if (detailsp != NULL) {
40318504831Schristos 		*detailsp = details;
40418504831Schristos 		details = NULL;
40518504831Schristos 	}
40618504831Schristos  out:
40718504831Schristos 	explicit_bzero(&sig_flags, sizeof(sig_flags));
40818504831Schristos 	explicit_bzero(&sig_counter, sizeof(sig_counter));
40918504831Schristos 	explicit_bzero(msghash, sizeof(msghash));
41018504831Schristos 	explicit_bzero(apphash, sizeof(apphash));
41118504831Schristos 	sshkey_sig_details_free(details);
4122d3b0f52Schristos 	sshbuf_free(webauthn_wrapper);
4132d3b0f52Schristos 	sshbuf_free(webauthn_exts);
4142d3b0f52Schristos 	free(webauthn_origin);
41518504831Schristos 	sshbuf_free(original_signed);
41618504831Schristos 	sshbuf_free(sigbuf);
41718504831Schristos 	sshbuf_free(b);
418b1066cf3Schristos 	ECDSA_SIG_free(esig);
41918504831Schristos 	BN_clear_free(sig_r);
42018504831Schristos 	BN_clear_free(sig_s);
42118504831Schristos 	free(ktype);
422*9469f4f1Schristos 	freezero(sigb, len);
423*9469f4f1Schristos 	EVP_MD_CTX_free(md_ctx);
42418504831Schristos 	return ret;
42518504831Schristos }
426b1066cf3Schristos 
427b1066cf3Schristos static const struct sshkey_impl_funcs sshkey_ecdsa_sk_funcs = {
428b1066cf3Schristos 	/* .size = */		NULL,
429b1066cf3Schristos 	/* .alloc = */		NULL,
430b1066cf3Schristos 	/* .cleanup = */	ssh_ecdsa_sk_cleanup,
431b1066cf3Schristos 	/* .equal = */		ssh_ecdsa_sk_equal,
432b1066cf3Schristos 	/* .ssh_serialize_public = */ ssh_ecdsa_sk_serialize_public,
433b1066cf3Schristos 	/* .ssh_deserialize_public = */ ssh_ecdsa_sk_deserialize_public,
434b1066cf3Schristos 	/* .ssh_serialize_private = */ ssh_ecdsa_sk_serialize_private,
435b1066cf3Schristos 	/* .ssh_deserialize_private = */ ssh_ecdsa_sk_deserialize_private,
436b1066cf3Schristos 	/* .generate = */	NULL,
437b1066cf3Schristos 	/* .copy_public = */	ssh_ecdsa_sk_copy_public,
438b1066cf3Schristos 	/* .sign = */		NULL,
439b1066cf3Schristos 	/* .verify = */		ssh_ecdsa_sk_verify,
440b1066cf3Schristos };
441b1066cf3Schristos 
442b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_sk_impl = {
443b1066cf3Schristos 	/* .name = */		"sk-ecdsa-sha2-nistp256@openssh.com",
444b1066cf3Schristos 	/* .shortname = */	"ECDSA-SK",
445b1066cf3Schristos 	/* .sigalg = */		NULL,
446b1066cf3Schristos 	/* .type = */		KEY_ECDSA_SK,
447b1066cf3Schristos 	/* .nid = */		NID_X9_62_prime256v1,
448b1066cf3Schristos 	/* .cert = */		0,
449b1066cf3Schristos 	/* .sigonly = */	0,
450b1066cf3Schristos 	/* .keybits = */	256,
451b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_sk_funcs,
452b1066cf3Schristos };
453b1066cf3Schristos 
454b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_sk_cert_impl = {
455b1066cf3Schristos 	/* .name = */		"sk-ecdsa-sha2-nistp256-cert-v01@openssh.com",
456b1066cf3Schristos 	/* .shortname = */	"ECDSA-SK-CERT",
457b1066cf3Schristos 	/* .sigalg = */		NULL,
458b1066cf3Schristos 	/* .type = */		KEY_ECDSA_SK_CERT,
459b1066cf3Schristos 	/* .nid = */		NID_X9_62_prime256v1,
460b1066cf3Schristos 	/* .cert = */		1,
461b1066cf3Schristos 	/* .sigonly = */	0,
462b1066cf3Schristos 	/* .keybits = */	256,
463b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_sk_funcs,
464b1066cf3Schristos };
465b1066cf3Schristos 
466b1066cf3Schristos const struct sshkey_impl sshkey_ecdsa_sk_webauthn_impl = {
467b1066cf3Schristos 	/* .name = */		"webauthn-sk-ecdsa-sha2-nistp256@openssh.com",
468b1066cf3Schristos 	/* .shortname = */	"ECDSA-SK",
469b1066cf3Schristos 	/* .sigalg = */		NULL,
470b1066cf3Schristos 	/* .type = */		KEY_ECDSA_SK,
471b1066cf3Schristos 	/* .nid = */		NID_X9_62_prime256v1,
472b1066cf3Schristos 	/* .cert = */		0,
473b1066cf3Schristos 	/* .sigonly = */	1,
474b1066cf3Schristos 	/* .keybits = */	256,
475b1066cf3Schristos 	/* .funcs = */		&sshkey_ecdsa_sk_funcs,
476b1066cf3Schristos };
477