1*ba1276acSMatthew Dillon /* $OpenBSD: ssh-ecdsa-sk.c,v 1.18 2023/03/08 04:43:12 guenther Exp $ */
20cbfa66cSDaniel Fojt /*
30cbfa66cSDaniel Fojt * Copyright (c) 2000 Markus Friedl. All rights reserved.
40cbfa66cSDaniel Fojt * Copyright (c) 2010 Damien Miller. All rights reserved.
50cbfa66cSDaniel Fojt * Copyright (c) 2019 Google Inc. All rights reserved.
60cbfa66cSDaniel Fojt *
70cbfa66cSDaniel Fojt * Redistribution and use in source and binary forms, with or without
80cbfa66cSDaniel Fojt * modification, are permitted provided that the following conditions
90cbfa66cSDaniel Fojt * are met:
100cbfa66cSDaniel Fojt * 1. Redistributions of source code must retain the above copyright
110cbfa66cSDaniel Fojt * notice, this list of conditions and the following disclaimer.
120cbfa66cSDaniel Fojt * 2. Redistributions in binary form must reproduce the above copyright
130cbfa66cSDaniel Fojt * notice, this list of conditions and the following disclaimer in the
140cbfa66cSDaniel Fojt * documentation and/or other materials provided with the distribution.
150cbfa66cSDaniel Fojt *
160cbfa66cSDaniel Fojt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
170cbfa66cSDaniel Fojt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
180cbfa66cSDaniel Fojt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
190cbfa66cSDaniel Fojt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
200cbfa66cSDaniel Fojt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
210cbfa66cSDaniel Fojt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
220cbfa66cSDaniel Fojt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
230cbfa66cSDaniel Fojt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
240cbfa66cSDaniel Fojt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
250cbfa66cSDaniel Fojt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
260cbfa66cSDaniel Fojt */
270cbfa66cSDaniel Fojt
280cbfa66cSDaniel Fojt /* #define DEBUG_SK 1 */
290cbfa66cSDaniel Fojt
300cbfa66cSDaniel Fojt #include "includes.h"
310cbfa66cSDaniel Fojt
320cbfa66cSDaniel Fojt #include <sys/types.h>
330cbfa66cSDaniel Fojt
340cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
350cbfa66cSDaniel Fojt #include <openssl/bn.h>
360cbfa66cSDaniel Fojt #include <openssl/ec.h>
370cbfa66cSDaniel Fojt #include <openssl/ecdsa.h>
380cbfa66cSDaniel Fojt #include <openssl/evp.h>
390cbfa66cSDaniel Fojt #endif
400cbfa66cSDaniel Fojt
410cbfa66cSDaniel Fojt #include <string.h>
420cbfa66cSDaniel Fojt #include <stdio.h> /* needed for DEBUG_SK only */
430cbfa66cSDaniel Fojt
440cbfa66cSDaniel Fojt #include "openbsd-compat/openssl-compat.h"
450cbfa66cSDaniel Fojt
460cbfa66cSDaniel Fojt #include "sshbuf.h"
470cbfa66cSDaniel Fojt #include "ssherr.h"
480cbfa66cSDaniel Fojt #include "digest.h"
490cbfa66cSDaniel Fojt #define SSHKEY_INTERNAL
500cbfa66cSDaniel Fojt #include "sshkey.h"
510cbfa66cSDaniel Fojt
5250a69bb5SSascha Wildner #ifndef OPENSSL_HAS_ECC
530cbfa66cSDaniel Fojt /* ARGSUSED */
540cbfa66cSDaniel Fojt int
ssh_ecdsa_sk_verify(const struct sshkey * key,const u_char * signature,size_t signaturelen,const u_char * data,size_t datalen,u_int compat,struct sshkey_sig_details ** detailsp)550cbfa66cSDaniel Fojt ssh_ecdsa_sk_verify(const struct sshkey *key,
560cbfa66cSDaniel Fojt const u_char *signature, size_t signaturelen,
570cbfa66cSDaniel Fojt const u_char *data, size_t datalen, u_int compat,
580cbfa66cSDaniel Fojt struct sshkey_sig_details **detailsp)
590cbfa66cSDaniel Fojt {
6050a69bb5SSascha Wildner return SSH_ERR_FEATURE_UNSUPPORTED;
6150a69bb5SSascha Wildner }
6250a69bb5SSascha Wildner #else /* OPENSSL_HAS_ECC */
6350a69bb5SSascha Wildner
64*ba1276acSMatthew Dillon /* Reuse some ECDSA internals */
65*ba1276acSMatthew Dillon extern struct sshkey_impl_funcs sshkey_ecdsa_funcs;
66*ba1276acSMatthew Dillon
67*ba1276acSMatthew Dillon static void
ssh_ecdsa_sk_cleanup(struct sshkey * k)68*ba1276acSMatthew Dillon ssh_ecdsa_sk_cleanup(struct sshkey *k)
69*ba1276acSMatthew Dillon {
70*ba1276acSMatthew Dillon sshkey_sk_cleanup(k);
71*ba1276acSMatthew Dillon sshkey_ecdsa_funcs.cleanup(k);
72*ba1276acSMatthew Dillon }
73*ba1276acSMatthew Dillon
74*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_equal(const struct sshkey * a,const struct sshkey * b)75*ba1276acSMatthew Dillon ssh_ecdsa_sk_equal(const struct sshkey *a, const struct sshkey *b)
76*ba1276acSMatthew Dillon {
77*ba1276acSMatthew Dillon if (!sshkey_sk_fields_equal(a, b))
78*ba1276acSMatthew Dillon return 0;
79*ba1276acSMatthew Dillon if (!sshkey_ecdsa_funcs.equal(a, b))
80*ba1276acSMatthew Dillon return 0;
81*ba1276acSMatthew Dillon return 1;
82*ba1276acSMatthew Dillon }
83*ba1276acSMatthew Dillon
84*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)85*ba1276acSMatthew Dillon ssh_ecdsa_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
86*ba1276acSMatthew Dillon enum sshkey_serialize_rep opts)
87*ba1276acSMatthew Dillon {
88*ba1276acSMatthew Dillon int r;
89*ba1276acSMatthew Dillon
90*ba1276acSMatthew Dillon if ((r = sshkey_ecdsa_funcs.serialize_public(key, b, opts)) != 0)
91*ba1276acSMatthew Dillon return r;
92*ba1276acSMatthew Dillon if ((r = sshkey_serialize_sk(key, b)) != 0)
93*ba1276acSMatthew Dillon return r;
94*ba1276acSMatthew Dillon
95*ba1276acSMatthew Dillon return 0;
96*ba1276acSMatthew Dillon }
97*ba1276acSMatthew Dillon
98*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)99*ba1276acSMatthew Dillon ssh_ecdsa_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
100*ba1276acSMatthew Dillon enum sshkey_serialize_rep opts)
101*ba1276acSMatthew Dillon {
102*ba1276acSMatthew Dillon int r;
103*ba1276acSMatthew Dillon
104*ba1276acSMatthew Dillon if (!sshkey_is_cert(key)) {
105*ba1276acSMatthew Dillon if ((r = sshkey_ecdsa_funcs.serialize_public(key,
106*ba1276acSMatthew Dillon b, opts)) != 0)
107*ba1276acSMatthew Dillon return r;
108*ba1276acSMatthew Dillon }
109*ba1276acSMatthew Dillon if ((r = sshkey_serialize_private_sk(key, b)) != 0)
110*ba1276acSMatthew Dillon return r;
111*ba1276acSMatthew Dillon
112*ba1276acSMatthew Dillon return 0;
113*ba1276acSMatthew Dillon }
114*ba1276acSMatthew Dillon
115*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_copy_public(const struct sshkey * from,struct sshkey * to)116*ba1276acSMatthew Dillon ssh_ecdsa_sk_copy_public(const struct sshkey *from, struct sshkey *to)
117*ba1276acSMatthew Dillon {
118*ba1276acSMatthew Dillon int r;
119*ba1276acSMatthew Dillon
120*ba1276acSMatthew Dillon if ((r = sshkey_ecdsa_funcs.copy_public(from, to)) != 0)
121*ba1276acSMatthew Dillon return r;
122*ba1276acSMatthew Dillon if ((r = sshkey_copy_public_sk(from, to)) != 0)
123*ba1276acSMatthew Dillon return r;
124*ba1276acSMatthew Dillon return 0;
125*ba1276acSMatthew Dillon }
126*ba1276acSMatthew Dillon
127*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)128*ba1276acSMatthew Dillon ssh_ecdsa_sk_deserialize_public(const char *ktype, struct sshbuf *b,
129*ba1276acSMatthew Dillon struct sshkey *key)
130*ba1276acSMatthew Dillon {
131*ba1276acSMatthew Dillon int r;
132*ba1276acSMatthew Dillon
133*ba1276acSMatthew Dillon if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype, b, key)) != 0)
134*ba1276acSMatthew Dillon return r;
135*ba1276acSMatthew Dillon if ((r = sshkey_deserialize_sk(b, key)) != 0)
136*ba1276acSMatthew Dillon return r;
137*ba1276acSMatthew Dillon return 0;
138*ba1276acSMatthew Dillon }
139*ba1276acSMatthew Dillon
140*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)141*ba1276acSMatthew Dillon ssh_ecdsa_sk_deserialize_private(const char *ktype, struct sshbuf *b,
142*ba1276acSMatthew Dillon struct sshkey *key)
143*ba1276acSMatthew Dillon {
144*ba1276acSMatthew Dillon int r;
145*ba1276acSMatthew Dillon
146*ba1276acSMatthew Dillon if (!sshkey_is_cert(key)) {
147*ba1276acSMatthew Dillon if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype,
148*ba1276acSMatthew Dillon b, key)) != 0)
149*ba1276acSMatthew Dillon return r;
150*ba1276acSMatthew Dillon }
151*ba1276acSMatthew Dillon if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
152*ba1276acSMatthew Dillon return r;
153*ba1276acSMatthew Dillon
154*ba1276acSMatthew Dillon return 0;
155*ba1276acSMatthew Dillon }
156*ba1276acSMatthew Dillon
15750a69bb5SSascha Wildner /*
15850a69bb5SSascha Wildner * Check FIDO/W3C webauthn signatures clientData field against the expected
15950a69bb5SSascha Wildner * format and prepare a hash of it for use in signature verification.
16050a69bb5SSascha Wildner *
16150a69bb5SSascha Wildner * webauthn signatures do not sign the hash of the message directly, but
16250a69bb5SSascha Wildner * instead sign a JSON-like "clientData" wrapper structure that contains the
16350a69bb5SSascha Wildner * message hash along with a other information.
16450a69bb5SSascha Wildner *
16550a69bb5SSascha Wildner * Fortunately this structure has a fixed format so it is possible to verify
16650a69bb5SSascha Wildner * that the hash of the signed message is present within the clientData
16750a69bb5SSascha Wildner * structure without needing to implement any JSON parsing.
16850a69bb5SSascha Wildner */
16950a69bb5SSascha Wildner static int
webauthn_check_prepare_hash(const u_char * data,size_t datalen,const char * origin,const struct sshbuf * wrapper,uint8_t flags,const struct sshbuf * extensions,u_char * msghash,size_t msghashlen)17050a69bb5SSascha Wildner webauthn_check_prepare_hash(const u_char *data, size_t datalen,
17150a69bb5SSascha Wildner const char *origin, const struct sshbuf *wrapper,
17250a69bb5SSascha Wildner uint8_t flags, const struct sshbuf *extensions,
17350a69bb5SSascha Wildner u_char *msghash, size_t msghashlen)
17450a69bb5SSascha Wildner {
17550a69bb5SSascha Wildner int r = SSH_ERR_INTERNAL_ERROR;
17650a69bb5SSascha Wildner struct sshbuf *chall = NULL, *m = NULL;
17750a69bb5SSascha Wildner
17850a69bb5SSascha Wildner if ((m = sshbuf_new()) == NULL ||
17950a69bb5SSascha Wildner (chall = sshbuf_from(data, datalen)) == NULL) {
18050a69bb5SSascha Wildner r = SSH_ERR_ALLOC_FAIL;
18150a69bb5SSascha Wildner goto out;
18250a69bb5SSascha Wildner }
18350a69bb5SSascha Wildner /*
18450a69bb5SSascha Wildner * Ensure origin contains no quote character and that the flags are
18550a69bb5SSascha Wildner * consistent with what we received
18650a69bb5SSascha Wildner */
18750a69bb5SSascha Wildner if (strchr(origin, '\"') != NULL ||
18850a69bb5SSascha Wildner (flags & 0x40) != 0 /* AD */ ||
18950a69bb5SSascha Wildner ((flags & 0x80) == 0 /* ED */) != (sshbuf_len(extensions) == 0)) {
19050a69bb5SSascha Wildner r = SSH_ERR_INVALID_FORMAT;
19150a69bb5SSascha Wildner goto out;
19250a69bb5SSascha Wildner }
19350a69bb5SSascha Wildner
19450a69bb5SSascha Wildner /*
19550a69bb5SSascha Wildner * Prepare the preamble to clientData that we expect, poking the
19650a69bb5SSascha Wildner * challenge and origin into their canonical positions in the
19750a69bb5SSascha Wildner * structure. The crossOrigin flag and any additional extension
19850a69bb5SSascha Wildner * fields present are ignored.
19950a69bb5SSascha Wildner */
20050a69bb5SSascha Wildner #define WEBAUTHN_0 "{\"type\":\"webauthn.get\",\"challenge\":\""
20150a69bb5SSascha Wildner #define WEBAUTHN_1 "\",\"origin\":\""
20250a69bb5SSascha Wildner #define WEBAUTHN_2 "\""
20350a69bb5SSascha Wildner if ((r = sshbuf_put(m, WEBAUTHN_0, sizeof(WEBAUTHN_0) - 1)) != 0 ||
20450a69bb5SSascha Wildner (r = sshbuf_dtourlb64(chall, m, 0)) != 0 ||
20550a69bb5SSascha Wildner (r = sshbuf_put(m, WEBAUTHN_1, sizeof(WEBAUTHN_1) - 1)) != 0 ||
20650a69bb5SSascha Wildner (r = sshbuf_put(m, origin, strlen(origin))) != 0 ||
20750a69bb5SSascha Wildner (r = sshbuf_put(m, WEBAUTHN_2, sizeof(WEBAUTHN_2) - 1)) != 0)
20850a69bb5SSascha Wildner goto out;
20950a69bb5SSascha Wildner #ifdef DEBUG_SK
21050a69bb5SSascha Wildner fprintf(stderr, "%s: received origin: %s\n", __func__, origin);
21150a69bb5SSascha Wildner fprintf(stderr, "%s: received clientData:\n", __func__);
21250a69bb5SSascha Wildner sshbuf_dump(wrapper, stderr);
21350a69bb5SSascha Wildner fprintf(stderr, "%s: expected clientData premable:\n", __func__);
21450a69bb5SSascha Wildner sshbuf_dump(m, stderr);
21550a69bb5SSascha Wildner #endif
21650a69bb5SSascha Wildner /* Check that the supplied clientData has the preamble we expect */
21750a69bb5SSascha Wildner if ((r = sshbuf_cmp(wrapper, 0, sshbuf_ptr(m), sshbuf_len(m))) != 0)
21850a69bb5SSascha Wildner goto out;
21950a69bb5SSascha Wildner
22050a69bb5SSascha Wildner /* Prepare hash of clientData */
22150a69bb5SSascha Wildner if ((r = ssh_digest_buffer(SSH_DIGEST_SHA256, wrapper,
22250a69bb5SSascha Wildner msghash, msghashlen)) != 0)
22350a69bb5SSascha Wildner goto out;
22450a69bb5SSascha Wildner
22550a69bb5SSascha Wildner /* success */
22650a69bb5SSascha Wildner r = 0;
22750a69bb5SSascha Wildner out:
22850a69bb5SSascha Wildner sshbuf_free(chall);
22950a69bb5SSascha Wildner sshbuf_free(m);
23050a69bb5SSascha Wildner return r;
23150a69bb5SSascha Wildner }
23250a69bb5SSascha Wildner
233*ba1276acSMatthew Dillon static int
ssh_ecdsa_sk_verify(const struct sshkey * key,const u_char * sig,size_t siglen,const u_char * data,size_t dlen,const char * alg,u_int compat,struct sshkey_sig_details ** detailsp)23450a69bb5SSascha Wildner ssh_ecdsa_sk_verify(const struct sshkey *key,
235*ba1276acSMatthew Dillon const u_char *sig, size_t siglen,
236*ba1276acSMatthew Dillon const u_char *data, size_t dlen, const char *alg, u_int compat,
23750a69bb5SSascha Wildner struct sshkey_sig_details **detailsp)
23850a69bb5SSascha Wildner {
239*ba1276acSMatthew Dillon ECDSA_SIG *esig = NULL;
2400cbfa66cSDaniel Fojt BIGNUM *sig_r = NULL, *sig_s = NULL;
2410cbfa66cSDaniel Fojt u_char sig_flags;
2420cbfa66cSDaniel Fojt u_char msghash[32], apphash[32], sighash[32];
2430cbfa66cSDaniel Fojt u_int sig_counter;
24450a69bb5SSascha Wildner int is_webauthn = 0, ret = SSH_ERR_INTERNAL_ERROR;
2450cbfa66cSDaniel Fojt struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL;
24650a69bb5SSascha Wildner struct sshbuf *webauthn_wrapper = NULL, *webauthn_exts = NULL;
24750a69bb5SSascha Wildner char *ktype = NULL, *webauthn_origin = NULL;
2480cbfa66cSDaniel Fojt struct sshkey_sig_details *details = NULL;
2490cbfa66cSDaniel Fojt #ifdef DEBUG_SK
2500cbfa66cSDaniel Fojt char *tmp = NULL;
2510cbfa66cSDaniel Fojt #endif
2520cbfa66cSDaniel Fojt
2530cbfa66cSDaniel Fojt if (detailsp != NULL)
2540cbfa66cSDaniel Fojt *detailsp = NULL;
2550cbfa66cSDaniel Fojt if (key == NULL || key->ecdsa == NULL ||
2560cbfa66cSDaniel Fojt sshkey_type_plain(key->type) != KEY_ECDSA_SK ||
257*ba1276acSMatthew Dillon sig == NULL || siglen == 0)
2580cbfa66cSDaniel Fojt return SSH_ERR_INVALID_ARGUMENT;
2590cbfa66cSDaniel Fojt
2600cbfa66cSDaniel Fojt if (key->ecdsa_nid != NID_X9_62_prime256v1)
2610cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR;
2620cbfa66cSDaniel Fojt
2630cbfa66cSDaniel Fojt /* fetch signature */
264*ba1276acSMatthew Dillon if ((b = sshbuf_from(sig, siglen)) == NULL)
2650cbfa66cSDaniel Fojt return SSH_ERR_ALLOC_FAIL;
26650a69bb5SSascha Wildner if ((details = calloc(1, sizeof(*details))) == NULL) {
26750a69bb5SSascha Wildner ret = SSH_ERR_ALLOC_FAIL;
26850a69bb5SSascha Wildner goto out;
26950a69bb5SSascha Wildner }
27050a69bb5SSascha Wildner if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
27150a69bb5SSascha Wildner ret = SSH_ERR_INVALID_FORMAT;
27250a69bb5SSascha Wildner goto out;
27350a69bb5SSascha Wildner }
27450a69bb5SSascha Wildner if (strcmp(ktype, "webauthn-sk-ecdsa-sha2-nistp256@openssh.com") == 0)
27550a69bb5SSascha Wildner is_webauthn = 1;
27650a69bb5SSascha Wildner else if (strcmp(ktype, "sk-ecdsa-sha2-nistp256@openssh.com") != 0) {
27750a69bb5SSascha Wildner ret = SSH_ERR_INVALID_FORMAT;
27850a69bb5SSascha Wildner goto out;
27950a69bb5SSascha Wildner }
28050a69bb5SSascha Wildner if (sshbuf_froms(b, &sigbuf) != 0 ||
2810cbfa66cSDaniel Fojt sshbuf_get_u8(b, &sig_flags) != 0 ||
2820cbfa66cSDaniel Fojt sshbuf_get_u32(b, &sig_counter) != 0) {
2830cbfa66cSDaniel Fojt ret = SSH_ERR_INVALID_FORMAT;
2840cbfa66cSDaniel Fojt goto out;
2850cbfa66cSDaniel Fojt }
28650a69bb5SSascha Wildner if (is_webauthn) {
28750a69bb5SSascha Wildner if (sshbuf_get_cstring(b, &webauthn_origin, NULL) != 0 ||
28850a69bb5SSascha Wildner sshbuf_froms(b, &webauthn_wrapper) != 0 ||
28950a69bb5SSascha Wildner sshbuf_froms(b, &webauthn_exts) != 0) {
29050a69bb5SSascha Wildner ret = SSH_ERR_INVALID_FORMAT;
2910cbfa66cSDaniel Fojt goto out;
2920cbfa66cSDaniel Fojt }
29350a69bb5SSascha Wildner }
2940cbfa66cSDaniel Fojt if (sshbuf_len(b) != 0) {
2950cbfa66cSDaniel Fojt ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
2960cbfa66cSDaniel Fojt goto out;
2970cbfa66cSDaniel Fojt }
2980cbfa66cSDaniel Fojt
2990cbfa66cSDaniel Fojt /* parse signature */
3000cbfa66cSDaniel Fojt if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
3010cbfa66cSDaniel Fojt sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
3020cbfa66cSDaniel Fojt ret = SSH_ERR_INVALID_FORMAT;
3030cbfa66cSDaniel Fojt goto out;
3040cbfa66cSDaniel Fojt }
30550a69bb5SSascha Wildner if (sshbuf_len(sigbuf) != 0) {
30650a69bb5SSascha Wildner ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
3070cbfa66cSDaniel Fojt goto out;
3080cbfa66cSDaniel Fojt }
30950a69bb5SSascha Wildner
3100cbfa66cSDaniel Fojt #ifdef DEBUG_SK
3110cbfa66cSDaniel Fojt fprintf(stderr, "%s: data: (len %zu)\n", __func__, datalen);
3120cbfa66cSDaniel Fojt /* sshbuf_dump_data(data, datalen, stderr); */
3130cbfa66cSDaniel Fojt fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r)));
3140cbfa66cSDaniel Fojt free(tmp);
3150cbfa66cSDaniel Fojt fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s)));
3160cbfa66cSDaniel Fojt free(tmp);
3170cbfa66cSDaniel Fojt fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
3180cbfa66cSDaniel Fojt __func__, sig_flags, sig_counter);
31950a69bb5SSascha Wildner if (is_webauthn) {
32050a69bb5SSascha Wildner fprintf(stderr, "%s: webauthn origin: %s\n", __func__,
32150a69bb5SSascha Wildner webauthn_origin);
32250a69bb5SSascha Wildner fprintf(stderr, "%s: webauthn_wrapper:\n", __func__);
32350a69bb5SSascha Wildner sshbuf_dump(webauthn_wrapper, stderr);
32450a69bb5SSascha Wildner }
3250cbfa66cSDaniel Fojt #endif
326*ba1276acSMatthew Dillon if ((esig = ECDSA_SIG_new()) == NULL) {
32750a69bb5SSascha Wildner ret = SSH_ERR_ALLOC_FAIL;
3280cbfa66cSDaniel Fojt goto out;
3290cbfa66cSDaniel Fojt }
330*ba1276acSMatthew Dillon if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
33150a69bb5SSascha Wildner ret = SSH_ERR_LIBCRYPTO_ERROR;
33250a69bb5SSascha Wildner goto out;
33350a69bb5SSascha Wildner }
33450a69bb5SSascha Wildner sig_r = sig_s = NULL; /* transferred */
3350cbfa66cSDaniel Fojt
3360cbfa66cSDaniel Fojt /* Reconstruct data that was supposedly signed */
3370cbfa66cSDaniel Fojt if ((original_signed = sshbuf_new()) == NULL) {
3380cbfa66cSDaniel Fojt ret = SSH_ERR_ALLOC_FAIL;
3390cbfa66cSDaniel Fojt goto out;
3400cbfa66cSDaniel Fojt }
34150a69bb5SSascha Wildner if (is_webauthn) {
342*ba1276acSMatthew Dillon if ((ret = webauthn_check_prepare_hash(data, dlen,
34350a69bb5SSascha Wildner webauthn_origin, webauthn_wrapper, sig_flags, webauthn_exts,
34450a69bb5SSascha Wildner msghash, sizeof(msghash))) != 0)
34550a69bb5SSascha Wildner goto out;
346*ba1276acSMatthew Dillon } else if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
3470cbfa66cSDaniel Fojt msghash, sizeof(msghash))) != 0)
3480cbfa66cSDaniel Fojt goto out;
3490cbfa66cSDaniel Fojt /* Application value is hashed before signature */
3500cbfa66cSDaniel Fojt if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
3510cbfa66cSDaniel Fojt strlen(key->sk_application), apphash, sizeof(apphash))) != 0)
3520cbfa66cSDaniel Fojt goto out;
3530cbfa66cSDaniel Fojt #ifdef DEBUG_SK
3540cbfa66cSDaniel Fojt fprintf(stderr, "%s: hashed application:\n", __func__);
3550cbfa66cSDaniel Fojt sshbuf_dump_data(apphash, sizeof(apphash), stderr);
3560cbfa66cSDaniel Fojt fprintf(stderr, "%s: hashed message:\n", __func__);
3570cbfa66cSDaniel Fojt sshbuf_dump_data(msghash, sizeof(msghash), stderr);
3580cbfa66cSDaniel Fojt #endif
3590cbfa66cSDaniel Fojt if ((ret = sshbuf_put(original_signed,
3600cbfa66cSDaniel Fojt apphash, sizeof(apphash))) != 0 ||
3610cbfa66cSDaniel Fojt (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 ||
3620cbfa66cSDaniel Fojt (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 ||
36350a69bb5SSascha Wildner (ret = sshbuf_putb(original_signed, webauthn_exts)) != 0 ||
3640cbfa66cSDaniel Fojt (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0)
3650cbfa66cSDaniel Fojt goto out;
3660cbfa66cSDaniel Fojt /* Signature is over H(original_signed) */
3670cbfa66cSDaniel Fojt if ((ret = ssh_digest_buffer(SSH_DIGEST_SHA256, original_signed,
3680cbfa66cSDaniel Fojt sighash, sizeof(sighash))) != 0)
3690cbfa66cSDaniel Fojt goto out;
3700cbfa66cSDaniel Fojt details->sk_counter = sig_counter;
3710cbfa66cSDaniel Fojt details->sk_flags = sig_flags;
3720cbfa66cSDaniel Fojt #ifdef DEBUG_SK
3730cbfa66cSDaniel Fojt fprintf(stderr, "%s: signed buf:\n", __func__);
3740cbfa66cSDaniel Fojt sshbuf_dump(original_signed, stderr);
3750cbfa66cSDaniel Fojt fprintf(stderr, "%s: signed hash:\n", __func__);
3760cbfa66cSDaniel Fojt sshbuf_dump_data(sighash, sizeof(sighash), stderr);
3770cbfa66cSDaniel Fojt #endif
3780cbfa66cSDaniel Fojt
3790cbfa66cSDaniel Fojt /* Verify it */
380*ba1276acSMatthew Dillon switch (ECDSA_do_verify(sighash, sizeof(sighash), esig, key->ecdsa)) {
3810cbfa66cSDaniel Fojt case 1:
3820cbfa66cSDaniel Fojt ret = 0;
3830cbfa66cSDaniel Fojt break;
3840cbfa66cSDaniel Fojt case 0:
3850cbfa66cSDaniel Fojt ret = SSH_ERR_SIGNATURE_INVALID;
3860cbfa66cSDaniel Fojt goto out;
3870cbfa66cSDaniel Fojt default:
3880cbfa66cSDaniel Fojt ret = SSH_ERR_LIBCRYPTO_ERROR;
3890cbfa66cSDaniel Fojt goto out;
3900cbfa66cSDaniel Fojt }
3910cbfa66cSDaniel Fojt /* success */
3920cbfa66cSDaniel Fojt if (detailsp != NULL) {
3930cbfa66cSDaniel Fojt *detailsp = details;
3940cbfa66cSDaniel Fojt details = NULL;
3950cbfa66cSDaniel Fojt }
3960cbfa66cSDaniel Fojt out:
3970cbfa66cSDaniel Fojt explicit_bzero(&sig_flags, sizeof(sig_flags));
3980cbfa66cSDaniel Fojt explicit_bzero(&sig_counter, sizeof(sig_counter));
3990cbfa66cSDaniel Fojt explicit_bzero(msghash, sizeof(msghash));
4000cbfa66cSDaniel Fojt explicit_bzero(sighash, sizeof(msghash));
4010cbfa66cSDaniel Fojt explicit_bzero(apphash, sizeof(apphash));
4020cbfa66cSDaniel Fojt sshkey_sig_details_free(details);
40350a69bb5SSascha Wildner sshbuf_free(webauthn_wrapper);
40450a69bb5SSascha Wildner sshbuf_free(webauthn_exts);
40550a69bb5SSascha Wildner free(webauthn_origin);
4060cbfa66cSDaniel Fojt sshbuf_free(original_signed);
4070cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
4080cbfa66cSDaniel Fojt sshbuf_free(b);
409*ba1276acSMatthew Dillon ECDSA_SIG_free(esig);
4100cbfa66cSDaniel Fojt BN_clear_free(sig_r);
4110cbfa66cSDaniel Fojt BN_clear_free(sig_s);
4120cbfa66cSDaniel Fojt free(ktype);
4130cbfa66cSDaniel Fojt return ret;
4140cbfa66cSDaniel Fojt }
41550a69bb5SSascha Wildner
416*ba1276acSMatthew Dillon static const struct sshkey_impl_funcs sshkey_ecdsa_sk_funcs = {
417*ba1276acSMatthew Dillon /* .size = */ NULL,
418*ba1276acSMatthew Dillon /* .alloc = */ NULL,
419*ba1276acSMatthew Dillon /* .cleanup = */ ssh_ecdsa_sk_cleanup,
420*ba1276acSMatthew Dillon /* .equal = */ ssh_ecdsa_sk_equal,
421*ba1276acSMatthew Dillon /* .ssh_serialize_public = */ ssh_ecdsa_sk_serialize_public,
422*ba1276acSMatthew Dillon /* .ssh_deserialize_public = */ ssh_ecdsa_sk_deserialize_public,
423*ba1276acSMatthew Dillon /* .ssh_serialize_private = */ ssh_ecdsa_sk_serialize_private,
424*ba1276acSMatthew Dillon /* .ssh_deserialize_private = */ ssh_ecdsa_sk_deserialize_private,
425*ba1276acSMatthew Dillon /* .generate = */ NULL,
426*ba1276acSMatthew Dillon /* .copy_public = */ ssh_ecdsa_sk_copy_public,
427*ba1276acSMatthew Dillon /* .sign = */ NULL,
428*ba1276acSMatthew Dillon /* .verify = */ ssh_ecdsa_sk_verify,
429*ba1276acSMatthew Dillon };
430*ba1276acSMatthew Dillon
431*ba1276acSMatthew Dillon const struct sshkey_impl sshkey_ecdsa_sk_impl = {
432*ba1276acSMatthew Dillon /* .name = */ "sk-ecdsa-sha2-nistp256@openssh.com",
433*ba1276acSMatthew Dillon /* .shortname = */ "ECDSA-SK",
434*ba1276acSMatthew Dillon /* .sigalg = */ NULL,
435*ba1276acSMatthew Dillon /* .type = */ KEY_ECDSA_SK,
436*ba1276acSMatthew Dillon /* .nid = */ NID_X9_62_prime256v1,
437*ba1276acSMatthew Dillon /* .cert = */ 0,
438*ba1276acSMatthew Dillon /* .sigonly = */ 0,
439*ba1276acSMatthew Dillon /* .keybits = */ 256,
440*ba1276acSMatthew Dillon /* .funcs = */ &sshkey_ecdsa_sk_funcs,
441*ba1276acSMatthew Dillon };
442*ba1276acSMatthew Dillon
443*ba1276acSMatthew Dillon const struct sshkey_impl sshkey_ecdsa_sk_cert_impl = {
444*ba1276acSMatthew Dillon /* .name = */ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com",
445*ba1276acSMatthew Dillon /* .shortname = */ "ECDSA-SK-CERT",
446*ba1276acSMatthew Dillon /* .sigalg = */ NULL,
447*ba1276acSMatthew Dillon /* .type = */ KEY_ECDSA_SK_CERT,
448*ba1276acSMatthew Dillon /* .nid = */ NID_X9_62_prime256v1,
449*ba1276acSMatthew Dillon /* .cert = */ 1,
450*ba1276acSMatthew Dillon /* .sigonly = */ 0,
451*ba1276acSMatthew Dillon /* .keybits = */ 256,
452*ba1276acSMatthew Dillon /* .funcs = */ &sshkey_ecdsa_sk_funcs,
453*ba1276acSMatthew Dillon };
454*ba1276acSMatthew Dillon
455*ba1276acSMatthew Dillon const struct sshkey_impl sshkey_ecdsa_sk_webauthn_impl = {
456*ba1276acSMatthew Dillon /* .name = */ "webauthn-sk-ecdsa-sha2-nistp256@openssh.com",
457*ba1276acSMatthew Dillon /* .shortname = */ "ECDSA-SK",
458*ba1276acSMatthew Dillon /* .sigalg = */ NULL,
459*ba1276acSMatthew Dillon /* .type = */ KEY_ECDSA_SK,
460*ba1276acSMatthew Dillon /* .nid = */ NID_X9_62_prime256v1,
461*ba1276acSMatthew Dillon /* .cert = */ 0,
462*ba1276acSMatthew Dillon /* .sigonly = */ 1,
463*ba1276acSMatthew Dillon /* .keybits = */ 256,
464*ba1276acSMatthew Dillon /* .funcs = */ &sshkey_ecdsa_sk_funcs,
465*ba1276acSMatthew Dillon };
466*ba1276acSMatthew Dillon
46750a69bb5SSascha Wildner #endif /* OPENSSL_HAS_ECC */
468