xref: /openbsd-src/usr.bin/ssh/ssh-ecdsa-sk.c (revision 5411e7691687441b2e28282ca3a1c531c6b949c4)
1*5411e769Sdjm /* $OpenBSD: ssh-ecdsa-sk.c,v 1.19 2024/08/15 00:51:51 djm Exp $ */
24f5eb3ebSdjm /*
34f5eb3ebSdjm  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
44f5eb3ebSdjm  * Copyright (c) 2010 Damien Miller.  All rights reserved.
54f5eb3ebSdjm  * Copyright (c) 2019 Google Inc.  All rights reserved.
64f5eb3ebSdjm  *
74f5eb3ebSdjm  * Redistribution and use in source and binary forms, with or without
84f5eb3ebSdjm  * modification, are permitted provided that the following conditions
94f5eb3ebSdjm  * are met:
104f5eb3ebSdjm  * 1. Redistributions of source code must retain the above copyright
114f5eb3ebSdjm  *    notice, this list of conditions and the following disclaimer.
124f5eb3ebSdjm  * 2. Redistributions in binary form must reproduce the above copyright
134f5eb3ebSdjm  *    notice, this list of conditions and the following disclaimer in the
144f5eb3ebSdjm  *    documentation and/or other materials provided with the distribution.
154f5eb3ebSdjm  *
164f5eb3ebSdjm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
174f5eb3ebSdjm  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
184f5eb3ebSdjm  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
194f5eb3ebSdjm  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
204f5eb3ebSdjm  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
214f5eb3ebSdjm  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
224f5eb3ebSdjm  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
234f5eb3ebSdjm  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
244f5eb3ebSdjm  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
254f5eb3ebSdjm  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
264f5eb3ebSdjm  */
274f5eb3ebSdjm 
284f5eb3ebSdjm /* #define DEBUG_SK 1 */
294f5eb3ebSdjm 
304f5eb3ebSdjm #include <sys/types.h>
314f5eb3ebSdjm 
324f5eb3ebSdjm #include <openssl/bn.h>
334f5eb3ebSdjm #include <openssl/ec.h>
344f5eb3ebSdjm #include <openssl/ecdsa.h>
354f5eb3ebSdjm #include <openssl/evp.h>
364f5eb3ebSdjm 
374f5eb3ebSdjm #include <string.h>
384f5eb3ebSdjm #include <stdio.h> /* needed for DEBUG_SK only */
394f5eb3ebSdjm 
404f5eb3ebSdjm #include "sshbuf.h"
414f5eb3ebSdjm #include "ssherr.h"
424f5eb3ebSdjm #include "digest.h"
434f5eb3ebSdjm #define SSHKEY_INTERNAL
444f5eb3ebSdjm #include "sshkey.h"
454f5eb3ebSdjm 
46712f5ecfSdjm /* Reuse some ECDSA internals */
47712f5ecfSdjm extern struct sshkey_impl_funcs sshkey_ecdsa_funcs;
48712f5ecfSdjm 
499c1667dbSdjm static void
509c1667dbSdjm ssh_ecdsa_sk_cleanup(struct sshkey *k)
519c1667dbSdjm {
52712f5ecfSdjm 	sshkey_sk_cleanup(k);
53712f5ecfSdjm 	sshkey_ecdsa_funcs.cleanup(k);
54712f5ecfSdjm }
55712f5ecfSdjm 
56712f5ecfSdjm static int
57712f5ecfSdjm ssh_ecdsa_sk_equal(const struct sshkey *a, const struct sshkey *b)
58712f5ecfSdjm {
59712f5ecfSdjm 	if (!sshkey_sk_fields_equal(a, b))
60712f5ecfSdjm 		return 0;
61712f5ecfSdjm 	if (!sshkey_ecdsa_funcs.equal(a, b))
62712f5ecfSdjm 		return 0;
63712f5ecfSdjm 	return 1;
649c1667dbSdjm }
659c1667dbSdjm 
66eefcf659Sdjm static int
67eefcf659Sdjm ssh_ecdsa_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
68c8d92406Sdjm     enum sshkey_serialize_rep opts)
69eefcf659Sdjm {
70eefcf659Sdjm 	int r;
71eefcf659Sdjm 
72c8d92406Sdjm 	if ((r = sshkey_ecdsa_funcs.serialize_public(key, b, opts)) != 0)
73eefcf659Sdjm 		return r;
74eefcf659Sdjm 	if ((r = sshkey_serialize_sk(key, b)) != 0)
75eefcf659Sdjm 		return r;
76eefcf659Sdjm 
77eefcf659Sdjm 	return 0;
78eefcf659Sdjm }
79eefcf659Sdjm 
800d39f001Sdjm static int
81d03db38bSdjm ssh_ecdsa_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
82d03db38bSdjm     enum sshkey_serialize_rep opts)
83d03db38bSdjm {
84d03db38bSdjm 	int r;
85d03db38bSdjm 
86d03db38bSdjm 	if (!sshkey_is_cert(key)) {
87d03db38bSdjm 		if ((r = sshkey_ecdsa_funcs.serialize_public(key,
88d03db38bSdjm 		    b, opts)) != 0)
89d03db38bSdjm 			return r;
90d03db38bSdjm 	}
91d03db38bSdjm 	if ((r = sshkey_serialize_private_sk(key, b)) != 0)
92d03db38bSdjm 		return r;
93d03db38bSdjm 
94d03db38bSdjm 	return 0;
95d03db38bSdjm }
96d03db38bSdjm 
97d03db38bSdjm static int
980d39f001Sdjm ssh_ecdsa_sk_copy_public(const struct sshkey *from, struct sshkey *to)
990d39f001Sdjm {
1000d39f001Sdjm 	int r;
1010d39f001Sdjm 
1020d39f001Sdjm 	if ((r = sshkey_ecdsa_funcs.copy_public(from, to)) != 0)
1030d39f001Sdjm 		return r;
1040d39f001Sdjm 	if ((r = sshkey_copy_public_sk(from, to)) != 0)
1050d39f001Sdjm 		return r;
1060d39f001Sdjm 	return 0;
1070d39f001Sdjm }
1080d39f001Sdjm 
109c8d92406Sdjm static int
110c8d92406Sdjm ssh_ecdsa_sk_deserialize_public(const char *ktype, struct sshbuf *b,
111c8d92406Sdjm     struct sshkey *key)
112c8d92406Sdjm {
113c8d92406Sdjm 	int r;
114c8d92406Sdjm 
115c8d92406Sdjm 	if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype, b, key)) != 0)
116c8d92406Sdjm 		return r;
117c8d92406Sdjm 	if ((r = sshkey_deserialize_sk(b, key)) != 0)
118c8d92406Sdjm 		return r;
119c8d92406Sdjm 	return 0;
120c8d92406Sdjm }
121c8d92406Sdjm 
122a2c931d9Sdjm static int
123a2c931d9Sdjm ssh_ecdsa_sk_deserialize_private(const char *ktype, struct sshbuf *b,
124a2c931d9Sdjm     struct sshkey *key)
125a2c931d9Sdjm {
126a2c931d9Sdjm 	int r;
127a2c931d9Sdjm 
128a2c931d9Sdjm 	if (!sshkey_is_cert(key)) {
129a2c931d9Sdjm 		if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype,
130a2c931d9Sdjm 		    b, key)) != 0)
131a2c931d9Sdjm 			return r;
132a2c931d9Sdjm 	}
133a2c931d9Sdjm 	if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
134a2c931d9Sdjm 		return r;
135a2c931d9Sdjm 
136a2c931d9Sdjm 	return 0;
137a2c931d9Sdjm }
138a2c931d9Sdjm 
139c25c45acSdjm /*
140c25c45acSdjm  * Check FIDO/W3C webauthn signatures clientData field against the expected
141c25c45acSdjm  * format and prepare a hash of it for use in signature verification.
142c25c45acSdjm  *
143c25c45acSdjm  * webauthn signatures do not sign the hash of the message directly, but
144c25c45acSdjm  * instead sign a JSON-like "clientData" wrapper structure that contains the
145c25c45acSdjm  * message hash along with a other information.
146c25c45acSdjm  *
147c25c45acSdjm  * Fortunately this structure has a fixed format so it is possible to verify
148c25c45acSdjm  * that the hash of the signed message is present within the clientData
149c25c45acSdjm  * structure without needing to implement any JSON parsing.
150c25c45acSdjm  */
151c25c45acSdjm static int
152c25c45acSdjm webauthn_check_prepare_hash(const u_char *data, size_t datalen,
153c25c45acSdjm     const char *origin, const struct sshbuf *wrapper,
154c25c45acSdjm     uint8_t flags, const struct sshbuf *extensions,
155c25c45acSdjm     u_char *msghash, size_t msghashlen)
156c25c45acSdjm {
157c25c45acSdjm 	int r = SSH_ERR_INTERNAL_ERROR;
158c25c45acSdjm 	struct sshbuf *chall = NULL, *m = NULL;
159c25c45acSdjm 
160c25c45acSdjm 	if ((m = sshbuf_new()) == NULL ||
161c25c45acSdjm 	    (chall = sshbuf_from(data, datalen)) == NULL) {
162c25c45acSdjm 		r = SSH_ERR_ALLOC_FAIL;
163c25c45acSdjm 		goto out;
164c25c45acSdjm 	}
165c25c45acSdjm 	/*
166c25c45acSdjm 	 * Ensure origin contains no quote character and that the flags are
167c25c45acSdjm 	 * consistent with what we received
168c25c45acSdjm 	 */
169c25c45acSdjm 	if (strchr(origin, '\"') != NULL ||
170c25c45acSdjm 	    (flags & 0x40) != 0 /* AD */ ||
171c25c45acSdjm 	    ((flags & 0x80) == 0 /* ED */) != (sshbuf_len(extensions) == 0)) {
172c25c45acSdjm 		r = SSH_ERR_INVALID_FORMAT;
173c25c45acSdjm 		goto out;
174c25c45acSdjm 	}
175385c31a1Sdjm 
176385c31a1Sdjm 	/*
177385c31a1Sdjm 	 * Prepare the preamble to clientData that we expect, poking the
178385c31a1Sdjm 	 * challenge and origin into their canonical positions in the
179385c31a1Sdjm 	 * structure. The crossOrigin flag and any additional extension
180385c31a1Sdjm 	 * fields present are ignored.
181385c31a1Sdjm 	 */
182c25c45acSdjm #define WEBAUTHN_0	"{\"type\":\"webauthn.get\",\"challenge\":\""
183c25c45acSdjm #define WEBAUTHN_1	"\",\"origin\":\""
184c25c45acSdjm #define WEBAUTHN_2	"\""
185c25c45acSdjm 	if ((r = sshbuf_put(m, WEBAUTHN_0, sizeof(WEBAUTHN_0) - 1)) != 0 ||
186c25c45acSdjm 	    (r = sshbuf_dtourlb64(chall, m, 0)) != 0 ||
187c25c45acSdjm 	    (r = sshbuf_put(m, WEBAUTHN_1, sizeof(WEBAUTHN_1) - 1)) != 0 ||
188c25c45acSdjm 	    (r = sshbuf_put(m, origin, strlen(origin))) != 0 ||
189c25c45acSdjm 	    (r = sshbuf_put(m, WEBAUTHN_2, sizeof(WEBAUTHN_2) - 1)) != 0)
190c25c45acSdjm 		goto out;
191c25c45acSdjm #ifdef DEBUG_SK
192c25c45acSdjm 	fprintf(stderr, "%s: received origin: %s\n", __func__, origin);
193c25c45acSdjm 	fprintf(stderr, "%s: received clientData:\n", __func__);
194c25c45acSdjm 	sshbuf_dump(wrapper, stderr);
195c25c45acSdjm 	fprintf(stderr, "%s: expected clientData premable:\n", __func__);
196c25c45acSdjm 	sshbuf_dump(m, stderr);
197c25c45acSdjm #endif
198385c31a1Sdjm 	/* Check that the supplied clientData has the preamble we expect */
199c25c45acSdjm 	if ((r = sshbuf_cmp(wrapper, 0, sshbuf_ptr(m), sshbuf_len(m))) != 0)
200c25c45acSdjm 		goto out;
201c25c45acSdjm 
202c25c45acSdjm 	/* Prepare hash of clientData */
203c25c45acSdjm 	if ((r = ssh_digest_buffer(SSH_DIGEST_SHA256, wrapper,
204c25c45acSdjm 	    msghash, msghashlen)) != 0)
205c25c45acSdjm 		goto out;
206c25c45acSdjm 
207c25c45acSdjm 	/* success */
208c25c45acSdjm 	r = 0;
209c25c45acSdjm  out:
210c25c45acSdjm 	sshbuf_free(chall);
211c25c45acSdjm 	sshbuf_free(m);
212c25c45acSdjm 	return r;
213c25c45acSdjm }
214c25c45acSdjm 
215c5c174faSdjm static int
2164f5eb3ebSdjm ssh_ecdsa_sk_verify(const struct sshkey *key,
217c5c174faSdjm     const u_char *sig, size_t siglen,
218c5c174faSdjm     const u_char *data, size_t dlen, const char *alg, u_int compat,
219493ad5b0Sdjm     struct sshkey_sig_details **detailsp)
2204f5eb3ebSdjm {
221c5c174faSdjm 	ECDSA_SIG *esig = NULL;
222*5411e769Sdjm 	EVP_MD_CTX *md_ctx = NULL;
2234f5eb3ebSdjm 	BIGNUM *sig_r = NULL, *sig_s = NULL;
2244f5eb3ebSdjm 	u_char sig_flags;
225*5411e769Sdjm 	u_char msghash[32], apphash[32];
2264f5eb3ebSdjm 	u_int sig_counter;
227*5411e769Sdjm 	u_char *sigb = NULL, *cp;
228*5411e769Sdjm 	int is_webauthn = 0, ret = SSH_ERR_INTERNAL_ERROR, len = 0;
2294f5eb3ebSdjm 	struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL;
230c25c45acSdjm 	struct sshbuf *webauthn_wrapper = NULL, *webauthn_exts = NULL;
231c25c45acSdjm 	char *ktype = NULL, *webauthn_origin = NULL;
232493ad5b0Sdjm 	struct sshkey_sig_details *details = NULL;
2334f5eb3ebSdjm #ifdef DEBUG_SK
2344f5eb3ebSdjm 	char *tmp = NULL;
2354f5eb3ebSdjm #endif
2364f5eb3ebSdjm 
237493ad5b0Sdjm 	if (detailsp != NULL)
238493ad5b0Sdjm 		*detailsp = NULL;
239*5411e769Sdjm 	if (key == NULL || key->pkey == NULL ||
2404f5eb3ebSdjm 	    sshkey_type_plain(key->type) != KEY_ECDSA_SK ||
241c5c174faSdjm 	    sig == NULL || siglen == 0)
2424f5eb3ebSdjm 		return SSH_ERR_INVALID_ARGUMENT;
2434f5eb3ebSdjm 
2444f5eb3ebSdjm 	if (key->ecdsa_nid != NID_X9_62_prime256v1)
2454f5eb3ebSdjm 		return SSH_ERR_INTERNAL_ERROR;
2464f5eb3ebSdjm 
2474f5eb3ebSdjm 	/* fetch signature */
248c5c174faSdjm 	if ((b = sshbuf_from(sig, siglen)) == NULL)
2494f5eb3ebSdjm 		return SSH_ERR_ALLOC_FAIL;
2504d740d45Sdjm 	if ((details = calloc(1, sizeof(*details))) == NULL) {
2514d740d45Sdjm 		ret = SSH_ERR_ALLOC_FAIL;
2524d740d45Sdjm 		goto out;
2534d740d45Sdjm 	}
2544d740d45Sdjm 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
2554f5eb3ebSdjm 		ret = SSH_ERR_INVALID_FORMAT;
2564f5eb3ebSdjm 		goto out;
2574f5eb3ebSdjm 	}
258c25c45acSdjm 	if (strcmp(ktype, "webauthn-sk-ecdsa-sha2-nistp256@openssh.com") == 0)
259c25c45acSdjm 		is_webauthn = 1;
260c25c45acSdjm 	else if (strcmp(ktype, "sk-ecdsa-sha2-nistp256@openssh.com") != 0) {
2614d740d45Sdjm 		ret = SSH_ERR_INVALID_FORMAT;
2624d740d45Sdjm 		goto out;
2634d740d45Sdjm 	}
2644d740d45Sdjm 	if (sshbuf_froms(b, &sigbuf) != 0 ||
2654d740d45Sdjm 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
2664d740d45Sdjm 	    sshbuf_get_u32(b, &sig_counter) != 0) {
2674d740d45Sdjm 		ret = SSH_ERR_INVALID_FORMAT;
2684f5eb3ebSdjm 		goto out;
2694f5eb3ebSdjm 	}
270c25c45acSdjm 	if (is_webauthn) {
271c25c45acSdjm 		if (sshbuf_get_cstring(b, &webauthn_origin, NULL) != 0 ||
272c25c45acSdjm 		    sshbuf_froms(b, &webauthn_wrapper) != 0 ||
273c25c45acSdjm 		    sshbuf_froms(b, &webauthn_exts) != 0) {
274c25c45acSdjm 			ret = SSH_ERR_INVALID_FORMAT;
275c25c45acSdjm 			goto out;
276c25c45acSdjm 		}
277c25c45acSdjm 	}
2784f5eb3ebSdjm 	if (sshbuf_len(b) != 0) {
2794f5eb3ebSdjm 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
2804f5eb3ebSdjm 		goto out;
2814f5eb3ebSdjm 	}
2824f5eb3ebSdjm 
2834f5eb3ebSdjm 	/* parse signature */
2844f5eb3ebSdjm 	if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
285039d6aaeSdjm 	    sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
2864f5eb3ebSdjm 		ret = SSH_ERR_INVALID_FORMAT;
2874f5eb3ebSdjm 		goto out;
2884f5eb3ebSdjm 	}
2894d740d45Sdjm 	if (sshbuf_len(sigbuf) != 0) {
2904d740d45Sdjm 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
2914f5eb3ebSdjm 		goto out;
2924f5eb3ebSdjm 	}
293c25c45acSdjm 
2944f5eb3ebSdjm #ifdef DEBUG_SK
295584bea6cSdjm 	fprintf(stderr, "%s: data: (len %zu)\n", __func__, datalen);
296584bea6cSdjm 	/* sshbuf_dump_data(data, datalen, stderr); */
2974f5eb3ebSdjm 	fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r)));
2984f5eb3ebSdjm 	free(tmp);
2994f5eb3ebSdjm 	fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s)));
3004f5eb3ebSdjm 	free(tmp);
3014f5eb3ebSdjm 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
3024f5eb3ebSdjm 	    __func__, sig_flags, sig_counter);
303c25c45acSdjm 	if (is_webauthn) {
304c25c45acSdjm 		fprintf(stderr, "%s: webauthn origin: %s\n", __func__,
305c25c45acSdjm 		    webauthn_origin);
306c25c45acSdjm 		fprintf(stderr, "%s: webauthn_wrapper:\n", __func__);
307c25c45acSdjm 		sshbuf_dump(webauthn_wrapper, stderr);
308c25c45acSdjm 	}
3094f5eb3ebSdjm #endif
310c5c174faSdjm 	if ((esig = ECDSA_SIG_new()) == NULL) {
3114d740d45Sdjm 		ret = SSH_ERR_ALLOC_FAIL;
3124f5eb3ebSdjm 		goto out;
3134f5eb3ebSdjm 	}
314c5c174faSdjm 	if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
3154d740d45Sdjm 		ret = SSH_ERR_LIBCRYPTO_ERROR;
3164d740d45Sdjm 		goto out;
3174d740d45Sdjm 	}
3184d740d45Sdjm 	sig_r = sig_s = NULL; /* transferred */
3194f5eb3ebSdjm 
3204f5eb3ebSdjm 	/* Reconstruct data that was supposedly signed */
32111c8e95aSdjm 	if ((original_signed = sshbuf_new()) == NULL) {
32211c8e95aSdjm 		ret = SSH_ERR_ALLOC_FAIL;
32311c8e95aSdjm 		goto out;
32411c8e95aSdjm 	}
325c25c45acSdjm 	if (is_webauthn) {
326c5c174faSdjm 		if ((ret = webauthn_check_prepare_hash(data, dlen,
327c25c45acSdjm 		    webauthn_origin, webauthn_wrapper, sig_flags, webauthn_exts,
328c25c45acSdjm 		    msghash, sizeof(msghash))) != 0)
329c25c45acSdjm 			goto out;
330c5c174faSdjm 	} else if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
3314f5eb3ebSdjm 	    msghash, sizeof(msghash))) != 0)
3324f5eb3ebSdjm 		goto out;
3334f5eb3ebSdjm 	/* Application value is hashed before signature */
3344f5eb3ebSdjm 	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
3354f5eb3ebSdjm 	    strlen(key->sk_application), apphash, sizeof(apphash))) != 0)
3364f5eb3ebSdjm 		goto out;
3374f5eb3ebSdjm #ifdef DEBUG_SK
338584bea6cSdjm 	fprintf(stderr, "%s: hashed application:\n", __func__);
339584bea6cSdjm 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
3404f5eb3ebSdjm 	fprintf(stderr, "%s: hashed message:\n", __func__);
3414f5eb3ebSdjm 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
3424f5eb3ebSdjm #endif
3434f5eb3ebSdjm 	if ((ret = sshbuf_put(original_signed,
3444f5eb3ebSdjm 	    apphash, sizeof(apphash))) != 0 ||
3454f5eb3ebSdjm 	    (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 ||
3464f5eb3ebSdjm 	    (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 ||
347c25c45acSdjm 	    (ret = sshbuf_putb(original_signed, webauthn_exts)) != 0 ||
3484f5eb3ebSdjm 	    (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0)
3494f5eb3ebSdjm 		goto out;
350493ad5b0Sdjm 	details->sk_counter = sig_counter;
351493ad5b0Sdjm 	details->sk_flags = sig_flags;
3524f5eb3ebSdjm #ifdef DEBUG_SK
3534f5eb3ebSdjm 	fprintf(stderr, "%s: signed buf:\n", __func__);
3544f5eb3ebSdjm 	sshbuf_dump(original_signed, stderr);
3554f5eb3ebSdjm #endif
3564f5eb3ebSdjm 
357*5411e769Sdjm 	if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
358*5411e769Sdjm 		ret = SSH_ERR_ALLOC_FAIL;
359*5411e769Sdjm 		goto out;
360*5411e769Sdjm 	}
361*5411e769Sdjm 	if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
362*5411e769Sdjm 		len = 0;
363*5411e769Sdjm 		ret = SSH_ERR_LIBCRYPTO_ERROR;
364*5411e769Sdjm 		goto out;
365*5411e769Sdjm 	}
366*5411e769Sdjm 	if ((sigb = calloc(1, len)) == NULL) {
367*5411e769Sdjm 		ret = SSH_ERR_ALLOC_FAIL;
368*5411e769Sdjm 		goto out;
369*5411e769Sdjm 	}
370*5411e769Sdjm 	cp = sigb; /* ASN1_item_i2d increments the pointer past the object */
371*5411e769Sdjm 	if (i2d_ECDSA_SIG(esig, &cp) != len) {
372*5411e769Sdjm 		ret = SSH_ERR_LIBCRYPTO_ERROR;
373*5411e769Sdjm 		goto out;
374*5411e769Sdjm 	}
375*5411e769Sdjm #ifdef DEBUG_SK
376*5411e769Sdjm 	fprintf(stderr, "%s: signed hash:\n", __func__);
377*5411e769Sdjm 	sshbuf_dump_data(sigb, len, stderr);
378*5411e769Sdjm #endif
3794f5eb3ebSdjm 	/* Verify it */
380*5411e769Sdjm 	if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL,
381*5411e769Sdjm 	    key->pkey) != 1) {
382*5411e769Sdjm 		ret = SSH_ERR_LIBCRYPTO_ERROR;
383*5411e769Sdjm 		goto out;
384*5411e769Sdjm 	}
385*5411e769Sdjm 	switch (EVP_DigestVerify(md_ctx, sigb, len,
386*5411e769Sdjm 	    sshbuf_ptr(original_signed), sshbuf_len(original_signed))) {
3874f5eb3ebSdjm 	case 1:
3884f5eb3ebSdjm 		ret = 0;
3894f5eb3ebSdjm 		break;
3904f5eb3ebSdjm 	case 0:
3914f5eb3ebSdjm 		ret = SSH_ERR_SIGNATURE_INVALID;
3924f5eb3ebSdjm 		goto out;
3934f5eb3ebSdjm 	default:
3944f5eb3ebSdjm 		ret = SSH_ERR_LIBCRYPTO_ERROR;
3954f5eb3ebSdjm 		goto out;
3964f5eb3ebSdjm 	}
397493ad5b0Sdjm 	/* success */
398493ad5b0Sdjm 	if (detailsp != NULL) {
399493ad5b0Sdjm 		*detailsp = details;
400493ad5b0Sdjm 		details = NULL;
401493ad5b0Sdjm 	}
4024f5eb3ebSdjm  out:
4034f5eb3ebSdjm 	explicit_bzero(&sig_flags, sizeof(sig_flags));
4044f5eb3ebSdjm 	explicit_bzero(&sig_counter, sizeof(sig_counter));
4054f5eb3ebSdjm 	explicit_bzero(msghash, sizeof(msghash));
4064f5eb3ebSdjm 	explicit_bzero(apphash, sizeof(apphash));
407493ad5b0Sdjm 	sshkey_sig_details_free(details);
408c25c45acSdjm 	sshbuf_free(webauthn_wrapper);
409c25c45acSdjm 	sshbuf_free(webauthn_exts);
410c25c45acSdjm 	free(webauthn_origin);
4114f5eb3ebSdjm 	sshbuf_free(original_signed);
4124f5eb3ebSdjm 	sshbuf_free(sigbuf);
4134f5eb3ebSdjm 	sshbuf_free(b);
414c5c174faSdjm 	ECDSA_SIG_free(esig);
4154f5eb3ebSdjm 	BN_clear_free(sig_r);
4164f5eb3ebSdjm 	BN_clear_free(sig_s);
4174f5eb3ebSdjm 	free(ktype);
418*5411e769Sdjm 	freezero(sigb, len);
419*5411e769Sdjm 	EVP_MD_CTX_free(md_ctx);
4204f5eb3ebSdjm 	return ret;
4214f5eb3ebSdjm }
4229c1667dbSdjm 
4239c1667dbSdjm static const struct sshkey_impl_funcs sshkey_ecdsa_sk_funcs = {
4249c1667dbSdjm 	/* .size = */		NULL,
4259c1667dbSdjm 	/* .alloc = */		NULL,
4269c1667dbSdjm 	/* .cleanup = */	ssh_ecdsa_sk_cleanup,
427712f5ecfSdjm 	/* .equal = */		ssh_ecdsa_sk_equal,
428eefcf659Sdjm 	/* .ssh_serialize_public = */ ssh_ecdsa_sk_serialize_public,
429c8d92406Sdjm 	/* .ssh_deserialize_public = */ ssh_ecdsa_sk_deserialize_public,
430d03db38bSdjm 	/* .ssh_serialize_private = */ ssh_ecdsa_sk_serialize_private,
431a2c931d9Sdjm 	/* .ssh_deserialize_private = */ ssh_ecdsa_sk_deserialize_private,
432b6025febSdjm 	/* .generate = */	NULL,
4330d39f001Sdjm 	/* .copy_public = */	ssh_ecdsa_sk_copy_public,
434c5c174faSdjm 	/* .sign = */		NULL,
435c5c174faSdjm 	/* .verify = */		ssh_ecdsa_sk_verify,
4369c1667dbSdjm };
4379c1667dbSdjm 
4389c1667dbSdjm const struct sshkey_impl sshkey_ecdsa_sk_impl = {
4399c1667dbSdjm 	/* .name = */		"sk-ecdsa-sha2-nistp256@openssh.com",
4409c1667dbSdjm 	/* .shortname = */	"ECDSA-SK",
4419c1667dbSdjm 	/* .sigalg = */		NULL,
4429c1667dbSdjm 	/* .type = */		KEY_ECDSA_SK,
4439c1667dbSdjm 	/* .nid = */		NID_X9_62_prime256v1,
4449c1667dbSdjm 	/* .cert = */		0,
4459c1667dbSdjm 	/* .sigonly = */	0,
4469c1667dbSdjm 	/* .keybits = */	256,
4479c1667dbSdjm 	/* .funcs = */		&sshkey_ecdsa_sk_funcs,
4489c1667dbSdjm };
4499c1667dbSdjm 
4509c1667dbSdjm const struct sshkey_impl sshkey_ecdsa_sk_cert_impl = {
4519c1667dbSdjm 	/* .name = */		"sk-ecdsa-sha2-nistp256-cert-v01@openssh.com",
4529c1667dbSdjm 	/* .shortname = */	"ECDSA-SK-CERT",
4539c1667dbSdjm 	/* .sigalg = */		NULL,
4549c1667dbSdjm 	/* .type = */		KEY_ECDSA_SK_CERT,
4559c1667dbSdjm 	/* .nid = */		NID_X9_62_prime256v1,
4569c1667dbSdjm 	/* .cert = */		1,
4579c1667dbSdjm 	/* .sigonly = */	0,
4589c1667dbSdjm 	/* .keybits = */	256,
4599c1667dbSdjm 	/* .funcs = */		&sshkey_ecdsa_sk_funcs,
4609c1667dbSdjm };
4619c1667dbSdjm 
4629c1667dbSdjm const struct sshkey_impl sshkey_ecdsa_sk_webauthn_impl = {
4639c1667dbSdjm 	/* .name = */		"webauthn-sk-ecdsa-sha2-nistp256@openssh.com",
4649c1667dbSdjm 	/* .shortname = */	"ECDSA-SK",
4659c1667dbSdjm 	/* .sigalg = */		NULL,
4669c1667dbSdjm 	/* .type = */		KEY_ECDSA_SK,
4679c1667dbSdjm 	/* .nid = */		NID_X9_62_prime256v1,
4689c1667dbSdjm 	/* .cert = */		0,
4699c1667dbSdjm 	/* .sigonly = */	1,
4709c1667dbSdjm 	/* .keybits = */	256,
4719c1667dbSdjm 	/* .funcs = */		&sshkey_ecdsa_sk_funcs,
4729c1667dbSdjm };
473