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