xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-ecdsa.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: ssh-ecdsa.c,v 1.14 2019/04/20 17:16:40 christos Exp $	*/
2 /* $OpenBSD: ssh-ecdsa.c,v 1.16 2019/01/21 09:54:11 djm Exp $ */
3 /*
4  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
5  * Copyright (c) 2010 Damien Miller.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "includes.h"
29 __RCSID("$NetBSD: ssh-ecdsa.c,v 1.14 2019/04/20 17:16:40 christos Exp $");
30 #include <sys/types.h>
31 
32 #include <openssl/bn.h>
33 #include <openssl/ec.h>
34 #include <openssl/ecdsa.h>
35 #include <openssl/evp.h>
36 
37 #include <string.h>
38 
39 #include "sshbuf.h"
40 #include "ssherr.h"
41 #include "digest.h"
42 #define SSHKEY_INTERNAL
43 #include "sshkey.h"
44 
45 /* ARGSUSED */
46 int
47 ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
48     const u_char *data, size_t datalen, u_int compat)
49 {
50 	ECDSA_SIG *sig = NULL;
51 	const BIGNUM *sig_r, *sig_s;
52 	int hash_alg;
53 	u_char digest[SSH_DIGEST_MAX_LENGTH];
54 	size_t len, dlen;
55 	struct sshbuf *b = NULL, *bb = NULL;
56 	int ret = SSH_ERR_INTERNAL_ERROR;
57 
58 	if (lenp != NULL)
59 		*lenp = 0;
60 	if (sigp != NULL)
61 		*sigp = NULL;
62 
63 	if (key == NULL || key->ecdsa == NULL ||
64 	    sshkey_type_plain(key->type) != KEY_ECDSA)
65 		return SSH_ERR_INVALID_ARGUMENT;
66 
67 	if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
68 	    (dlen = ssh_digest_bytes(hash_alg)) == 0)
69 		return SSH_ERR_INTERNAL_ERROR;
70 	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
71 	    digest, sizeof(digest))) != 0)
72 		goto out;
73 
74 	if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) {
75 		ret = SSH_ERR_LIBCRYPTO_ERROR;
76 		goto out;
77 	}
78 
79 	if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
80 		ret = SSH_ERR_ALLOC_FAIL;
81 		goto out;
82 	}
83 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
84 	if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
85 	    (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
86 		goto out;
87 	if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
88 	    (ret = sshbuf_put_stringb(b, bb)) != 0)
89 		goto out;
90 	len = sshbuf_len(b);
91 	if (sigp != NULL) {
92 		if ((*sigp = malloc(len)) == NULL) {
93 			ret = SSH_ERR_ALLOC_FAIL;
94 			goto out;
95 		}
96 		memcpy(*sigp, sshbuf_ptr(b), len);
97 	}
98 	if (lenp != NULL)
99 		*lenp = len;
100 	ret = 0;
101  out:
102 	explicit_bzero(digest, sizeof(digest));
103 	sshbuf_free(b);
104 	sshbuf_free(bb);
105 	ECDSA_SIG_free(sig);
106 	return ret;
107 }
108 
109 /* ARGSUSED */
110 int
111 ssh_ecdsa_verify(const struct sshkey *key,
112     const u_char *signature, size_t signaturelen,
113     const u_char *data, size_t datalen, u_int compat)
114 {
115 	ECDSA_SIG *sig = NULL;
116 	BIGNUM *sig_r = NULL, *sig_s = NULL;
117 	int hash_alg;
118 	u_char digest[SSH_DIGEST_MAX_LENGTH];
119 	size_t dlen;
120 	int ret = SSH_ERR_INTERNAL_ERROR;
121 	struct sshbuf *b = NULL, *sigbuf = NULL;
122 	char *ktype = NULL;
123 
124 	if (key == NULL || key->ecdsa == NULL ||
125 	    sshkey_type_plain(key->type) != KEY_ECDSA ||
126 	    signature == NULL || signaturelen == 0)
127 		return SSH_ERR_INVALID_ARGUMENT;
128 
129 	if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
130 	    (dlen = ssh_digest_bytes(hash_alg)) == 0)
131 		return SSH_ERR_INTERNAL_ERROR;
132 
133 	/* fetch signature */
134 	if ((b = sshbuf_from(signature, signaturelen)) == NULL)
135 		return SSH_ERR_ALLOC_FAIL;
136 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
137 	    sshbuf_froms(b, &sigbuf) != 0) {
138 		ret = SSH_ERR_INVALID_FORMAT;
139 		goto out;
140 	}
141 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
142 		ret = SSH_ERR_KEY_TYPE_MISMATCH;
143 		goto out;
144 	}
145 	if (sshbuf_len(b) != 0) {
146 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
147 		goto out;
148 	}
149 
150 	/* parse signature */
151 	if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
152 	    sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
153 		ret = SSH_ERR_INVALID_FORMAT;
154 		goto out;
155 	}
156 	if ((sig = ECDSA_SIG_new()) == NULL) {
157 		ret = SSH_ERR_ALLOC_FAIL;
158 		goto out;
159 	}
160 	if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) {
161 		ret = SSH_ERR_LIBCRYPTO_ERROR;
162 		goto out;
163 	}
164 	sig_r = sig_s = NULL; /* transferred */
165 
166 	if (sshbuf_len(sigbuf) != 0) {
167 		ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
168 		goto out;
169 	}
170 	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
171 	    digest, sizeof(digest))) != 0)
172 		goto out;
173 
174 	switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) {
175 	case 1:
176 		ret = 0;
177 		break;
178 	case 0:
179 		ret = SSH_ERR_SIGNATURE_INVALID;
180 		goto out;
181 	default:
182 		ret = SSH_ERR_LIBCRYPTO_ERROR;
183 		goto out;
184 	}
185 
186  out:
187 	explicit_bzero(digest, sizeof(digest));
188 	sshbuf_free(sigbuf);
189 	sshbuf_free(b);
190 	ECDSA_SIG_free(sig);
191 	BN_clear_free(sig_r);
192 	BN_clear_free(sig_s);
193 	free(ktype);
194 	return ret;
195 }
196