xref: /dflybsd-src/crypto/openssh/ssh-ecdsa-sk.c (revision 0cbfa66cdb87e23928a110d9b02839f403e32c11)
1*0cbfa66cSDaniel Fojt /* $OpenBSD: ssh-ecdsa-sk.c,v 1.5 2019/11/26 03:04:27 djm Exp $ */
2*0cbfa66cSDaniel Fojt /*
3*0cbfa66cSDaniel Fojt  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4*0cbfa66cSDaniel Fojt  * Copyright (c) 2010 Damien Miller.  All rights reserved.
5*0cbfa66cSDaniel Fojt  * Copyright (c) 2019 Google Inc.  All rights reserved.
6*0cbfa66cSDaniel Fojt  *
7*0cbfa66cSDaniel Fojt  * Redistribution and use in source and binary forms, with or without
8*0cbfa66cSDaniel Fojt  * modification, are permitted provided that the following conditions
9*0cbfa66cSDaniel Fojt  * are met:
10*0cbfa66cSDaniel Fojt  * 1. Redistributions of source code must retain the above copyright
11*0cbfa66cSDaniel Fojt  *    notice, this list of conditions and the following disclaimer.
12*0cbfa66cSDaniel Fojt  * 2. Redistributions in binary form must reproduce the above copyright
13*0cbfa66cSDaniel Fojt  *    notice, this list of conditions and the following disclaimer in the
14*0cbfa66cSDaniel Fojt  *    documentation and/or other materials provided with the distribution.
15*0cbfa66cSDaniel Fojt  *
16*0cbfa66cSDaniel Fojt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17*0cbfa66cSDaniel Fojt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*0cbfa66cSDaniel Fojt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19*0cbfa66cSDaniel Fojt  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20*0cbfa66cSDaniel Fojt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21*0cbfa66cSDaniel Fojt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22*0cbfa66cSDaniel Fojt  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23*0cbfa66cSDaniel Fojt  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24*0cbfa66cSDaniel Fojt  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25*0cbfa66cSDaniel Fojt  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*0cbfa66cSDaniel Fojt  */
27*0cbfa66cSDaniel Fojt 
28*0cbfa66cSDaniel Fojt /* #define DEBUG_SK 1 */
29*0cbfa66cSDaniel Fojt 
30*0cbfa66cSDaniel Fojt #include "includes.h"
31*0cbfa66cSDaniel Fojt 
32*0cbfa66cSDaniel Fojt #include <sys/types.h>
33*0cbfa66cSDaniel Fojt 
34*0cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
35*0cbfa66cSDaniel Fojt #include <openssl/bn.h>
36*0cbfa66cSDaniel Fojt #include <openssl/ec.h>
37*0cbfa66cSDaniel Fojt #include <openssl/ecdsa.h>
38*0cbfa66cSDaniel Fojt #include <openssl/evp.h>
39*0cbfa66cSDaniel Fojt #endif
40*0cbfa66cSDaniel Fojt 
41*0cbfa66cSDaniel Fojt #include <string.h>
42*0cbfa66cSDaniel Fojt #include <stdio.h> /* needed for DEBUG_SK only */
43*0cbfa66cSDaniel Fojt 
44*0cbfa66cSDaniel Fojt #include "openbsd-compat/openssl-compat.h"
45*0cbfa66cSDaniel Fojt 
46*0cbfa66cSDaniel Fojt #include "sshbuf.h"
47*0cbfa66cSDaniel Fojt #include "ssherr.h"
48*0cbfa66cSDaniel Fojt #include "digest.h"
49*0cbfa66cSDaniel Fojt #define SSHKEY_INTERNAL
50*0cbfa66cSDaniel Fojt #include "sshkey.h"
51*0cbfa66cSDaniel Fojt 
52*0cbfa66cSDaniel Fojt /* ARGSUSED */
53*0cbfa66cSDaniel Fojt int
54*0cbfa66cSDaniel Fojt ssh_ecdsa_sk_verify(const struct sshkey *key,
55*0cbfa66cSDaniel Fojt     const u_char *signature, size_t signaturelen,
56*0cbfa66cSDaniel Fojt     const u_char *data, size_t datalen, u_int compat,
57*0cbfa66cSDaniel Fojt     struct sshkey_sig_details **detailsp)
58*0cbfa66cSDaniel Fojt {
59*0cbfa66cSDaniel Fojt #ifdef OPENSSL_HAS_ECC
60*0cbfa66cSDaniel Fojt 	ECDSA_SIG *sig = NULL;
61*0cbfa66cSDaniel Fojt 	BIGNUM *sig_r = NULL, *sig_s = NULL;
62*0cbfa66cSDaniel Fojt 	u_char sig_flags;
63*0cbfa66cSDaniel Fojt 	u_char msghash[32], apphash[32], sighash[32];
64*0cbfa66cSDaniel Fojt 	u_int sig_counter;
65*0cbfa66cSDaniel Fojt 	int ret = SSH_ERR_INTERNAL_ERROR;
66*0cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL;
67*0cbfa66cSDaniel Fojt 	char *ktype = NULL;
68*0cbfa66cSDaniel Fojt 	struct sshkey_sig_details *details = NULL;
69*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK
70*0cbfa66cSDaniel Fojt 	char *tmp = NULL;
71*0cbfa66cSDaniel Fojt #endif
72*0cbfa66cSDaniel Fojt 
73*0cbfa66cSDaniel Fojt 	if (detailsp != NULL)
74*0cbfa66cSDaniel Fojt 		*detailsp = NULL;
75*0cbfa66cSDaniel Fojt 	if (key == NULL || key->ecdsa == NULL ||
76*0cbfa66cSDaniel Fojt 	    sshkey_type_plain(key->type) != KEY_ECDSA_SK ||
77*0cbfa66cSDaniel Fojt 	    signature == NULL || signaturelen == 0)
78*0cbfa66cSDaniel Fojt 		return SSH_ERR_INVALID_ARGUMENT;
79*0cbfa66cSDaniel Fojt 
80*0cbfa66cSDaniel Fojt 	if (key->ecdsa_nid != NID_X9_62_prime256v1)
81*0cbfa66cSDaniel Fojt 		return SSH_ERR_INTERNAL_ERROR;
82*0cbfa66cSDaniel Fojt 
83*0cbfa66cSDaniel Fojt 	/* fetch signature */
84*0cbfa66cSDaniel Fojt 	if ((b = sshbuf_from(signature, signaturelen)) == NULL)
85*0cbfa66cSDaniel Fojt 		return SSH_ERR_ALLOC_FAIL;
86*0cbfa66cSDaniel Fojt 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
87*0cbfa66cSDaniel Fojt 	    sshbuf_froms(b, &sigbuf) != 0 ||
88*0cbfa66cSDaniel Fojt 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
89*0cbfa66cSDaniel Fojt 	    sshbuf_get_u32(b, &sig_counter) != 0) {
90*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_INVALID_FORMAT;
91*0cbfa66cSDaniel Fojt 		goto out;
92*0cbfa66cSDaniel Fojt 	}
93*0cbfa66cSDaniel Fojt 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
94*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_KEY_TYPE_MISMATCH;
95*0cbfa66cSDaniel Fojt 		goto out;
96*0cbfa66cSDaniel Fojt 	}
97*0cbfa66cSDaniel Fojt 	if (sshbuf_len(b) != 0) {
98*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
99*0cbfa66cSDaniel Fojt 		goto out;
100*0cbfa66cSDaniel Fojt 	}
101*0cbfa66cSDaniel Fojt 
102*0cbfa66cSDaniel Fojt 	/* parse signature */
103*0cbfa66cSDaniel Fojt 	if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
104*0cbfa66cSDaniel Fojt 	    sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
105*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_INVALID_FORMAT;
106*0cbfa66cSDaniel Fojt 		goto out;
107*0cbfa66cSDaniel Fojt 	}
108*0cbfa66cSDaniel Fojt 	if ((sig = ECDSA_SIG_new()) == NULL) {
109*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_ALLOC_FAIL;
110*0cbfa66cSDaniel Fojt 		goto out;
111*0cbfa66cSDaniel Fojt 	}
112*0cbfa66cSDaniel Fojt 	if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) {
113*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_LIBCRYPTO_ERROR;
114*0cbfa66cSDaniel Fojt 		goto out;
115*0cbfa66cSDaniel Fojt 	}
116*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK
117*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: data: (len %zu)\n", __func__, datalen);
118*0cbfa66cSDaniel Fojt 	/* sshbuf_dump_data(data, datalen, stderr); */
119*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r)));
120*0cbfa66cSDaniel Fojt 	free(tmp);
121*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s)));
122*0cbfa66cSDaniel Fojt 	free(tmp);
123*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
124*0cbfa66cSDaniel Fojt 	    __func__, sig_flags, sig_counter);
125*0cbfa66cSDaniel Fojt #endif
126*0cbfa66cSDaniel Fojt 	sig_r = sig_s = NULL; /* transferred */
127*0cbfa66cSDaniel Fojt 
128*0cbfa66cSDaniel Fojt 	if (sshbuf_len(sigbuf) != 0) {
129*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
130*0cbfa66cSDaniel Fojt 		goto out;
131*0cbfa66cSDaniel Fojt 	}
132*0cbfa66cSDaniel Fojt 
133*0cbfa66cSDaniel Fojt 	/* Reconstruct data that was supposedly signed */
134*0cbfa66cSDaniel Fojt 	if ((original_signed = sshbuf_new()) == NULL) {
135*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_ALLOC_FAIL;
136*0cbfa66cSDaniel Fojt 		goto out;
137*0cbfa66cSDaniel Fojt 	}
138*0cbfa66cSDaniel Fojt 	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
139*0cbfa66cSDaniel Fojt 	    msghash, sizeof(msghash))) != 0)
140*0cbfa66cSDaniel Fojt 		goto out;
141*0cbfa66cSDaniel Fojt 	/* Application value is hashed before signature */
142*0cbfa66cSDaniel Fojt 	if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
143*0cbfa66cSDaniel Fojt 	    strlen(key->sk_application), apphash, sizeof(apphash))) != 0)
144*0cbfa66cSDaniel Fojt 		goto out;
145*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK
146*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: hashed application:\n", __func__);
147*0cbfa66cSDaniel Fojt 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
148*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: hashed message:\n", __func__);
149*0cbfa66cSDaniel Fojt 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
150*0cbfa66cSDaniel Fojt #endif
151*0cbfa66cSDaniel Fojt 	if ((ret = sshbuf_put(original_signed,
152*0cbfa66cSDaniel Fojt 	    apphash, sizeof(apphash))) != 0 ||
153*0cbfa66cSDaniel Fojt 	    (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 ||
154*0cbfa66cSDaniel Fojt 	    (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 ||
155*0cbfa66cSDaniel Fojt 	    (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0)
156*0cbfa66cSDaniel Fojt 		goto out;
157*0cbfa66cSDaniel Fojt 	/* Signature is over H(original_signed) */
158*0cbfa66cSDaniel Fojt 	if ((ret = ssh_digest_buffer(SSH_DIGEST_SHA256, original_signed,
159*0cbfa66cSDaniel Fojt 	    sighash, sizeof(sighash))) != 0)
160*0cbfa66cSDaniel Fojt 		goto out;
161*0cbfa66cSDaniel Fojt 	if ((details = calloc(1, sizeof(*details))) == NULL) {
162*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_ALLOC_FAIL;
163*0cbfa66cSDaniel Fojt 		goto out;
164*0cbfa66cSDaniel Fojt 	}
165*0cbfa66cSDaniel Fojt 	details->sk_counter = sig_counter;
166*0cbfa66cSDaniel Fojt 	details->sk_flags = sig_flags;
167*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK
168*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: signed buf:\n", __func__);
169*0cbfa66cSDaniel Fojt 	sshbuf_dump(original_signed, stderr);
170*0cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: signed hash:\n", __func__);
171*0cbfa66cSDaniel Fojt 	sshbuf_dump_data(sighash, sizeof(sighash), stderr);
172*0cbfa66cSDaniel Fojt #endif
173*0cbfa66cSDaniel Fojt 
174*0cbfa66cSDaniel Fojt 	/* Verify it */
175*0cbfa66cSDaniel Fojt 	switch (ECDSA_do_verify(sighash, sizeof(sighash), sig, key->ecdsa)) {
176*0cbfa66cSDaniel Fojt 	case 1:
177*0cbfa66cSDaniel Fojt 		ret = 0;
178*0cbfa66cSDaniel Fojt 		break;
179*0cbfa66cSDaniel Fojt 	case 0:
180*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_SIGNATURE_INVALID;
181*0cbfa66cSDaniel Fojt 		goto out;
182*0cbfa66cSDaniel Fojt 	default:
183*0cbfa66cSDaniel Fojt 		ret = SSH_ERR_LIBCRYPTO_ERROR;
184*0cbfa66cSDaniel Fojt 		goto out;
185*0cbfa66cSDaniel Fojt 	}
186*0cbfa66cSDaniel Fojt 	/* success */
187*0cbfa66cSDaniel Fojt 	if (detailsp != NULL) {
188*0cbfa66cSDaniel Fojt 		*detailsp = details;
189*0cbfa66cSDaniel Fojt 		details = NULL;
190*0cbfa66cSDaniel Fojt 	}
191*0cbfa66cSDaniel Fojt  out:
192*0cbfa66cSDaniel Fojt 	explicit_bzero(&sig_flags, sizeof(sig_flags));
193*0cbfa66cSDaniel Fojt 	explicit_bzero(&sig_counter, sizeof(sig_counter));
194*0cbfa66cSDaniel Fojt 	explicit_bzero(msghash, sizeof(msghash));
195*0cbfa66cSDaniel Fojt 	explicit_bzero(sighash, sizeof(msghash));
196*0cbfa66cSDaniel Fojt 	explicit_bzero(apphash, sizeof(apphash));
197*0cbfa66cSDaniel Fojt 	sshkey_sig_details_free(details);
198*0cbfa66cSDaniel Fojt 	sshbuf_free(original_signed);
199*0cbfa66cSDaniel Fojt 	sshbuf_free(sigbuf);
200*0cbfa66cSDaniel Fojt 	sshbuf_free(b);
201*0cbfa66cSDaniel Fojt 	ECDSA_SIG_free(sig);
202*0cbfa66cSDaniel Fojt 	BN_clear_free(sig_r);
203*0cbfa66cSDaniel Fojt 	BN_clear_free(sig_s);
204*0cbfa66cSDaniel Fojt 	free(ktype);
205*0cbfa66cSDaniel Fojt 	return ret;
206*0cbfa66cSDaniel Fojt #else
207*0cbfa66cSDaniel Fojt 	return SSH_ERR_INTERNAL_ERROR;
208*0cbfa66cSDaniel Fojt #endif
209*0cbfa66cSDaniel Fojt }
210