Annotation of src/usr.bin/ssh/ssh-ecdsa.c, Revision 1.18
1.18 ! djm 1: /* $OpenBSD: ssh-ecdsa.c,v 1.17 2022/10/28 00:35:40 djm Exp $ */
1.1 djm 2: /*
3: * Copyright (c) 2000 Markus Friedl. All rights reserved.
4: * Copyright (c) 2010 Damien Miller. All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: *
15: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/types.h>
28:
29: #include <openssl/bn.h>
30: #include <openssl/ec.h>
31: #include <openssl/ecdsa.h>
32: #include <openssl/evp.h>
33:
34: #include <string.h>
35:
1.11 djm 36: #include "sshbuf.h"
37: #include "ssherr.h"
1.8 djm 38: #include "digest.h"
1.11 djm 39: #define SSHKEY_INTERNAL
40: #include "sshkey.h"
1.1 djm 41:
1.17 djm 42: static u_int
43: ssh_ecdsa_size(const struct sshkey *key)
44: {
45: switch (key->ecdsa_nid) {
46: case NID_X9_62_prime256v1:
47: return 256;
48: case NID_secp384r1:
49: return 384;
50: case NID_secp521r1:
51: return 521;
52: default:
53: return 0;
54: }
55: }
56:
57: static void
58: ssh_ecdsa_cleanup(struct sshkey *k)
59: {
60: EC_KEY_free(k->ecdsa);
61: k->ecdsa = NULL;
62: }
63:
1.18 ! djm 64: static int
! 65: ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
! 66: {
! 67: const EC_GROUP *grp_a, *grp_b;
! 68: const EC_POINT *pub_a, *pub_b;
! 69:
! 70: if (a->ecdsa == NULL || b->ecdsa == NULL)
! 71: return 0;
! 72: if ((grp_a = EC_KEY_get0_group(a->ecdsa)) == NULL ||
! 73: (grp_b = EC_KEY_get0_group(b->ecdsa)) == NULL)
! 74: return 0;
! 75: if ((pub_a = EC_KEY_get0_public_key(a->ecdsa)) == NULL ||
! 76: (pub_b = EC_KEY_get0_public_key(b->ecdsa)) == NULL)
! 77: return 0;
! 78: if (EC_GROUP_cmp(grp_a, grp_b, NULL) != 0)
! 79: return 0;
! 80: if (EC_POINT_cmp(grp_a, pub_a, pub_b, NULL) != 0)
! 81: return 0;
! 82: return 1;
! 83: }
! 84:
1.11 djm 85: /* ARGSUSED */
1.1 djm 86: int
1.11 djm 87: ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
88: const u_char *data, size_t datalen, u_int compat)
1.1 djm 89: {
1.11 djm 90: ECDSA_SIG *sig = NULL;
1.15 djm 91: const BIGNUM *sig_r, *sig_s;
1.8 djm 92: int hash_alg;
93: u_char digest[SSH_DIGEST_MAX_LENGTH];
1.11 djm 94: size_t len, dlen;
95: struct sshbuf *b = NULL, *bb = NULL;
96: int ret = SSH_ERR_INTERNAL_ERROR;
1.1 djm 97:
1.11 djm 98: if (lenp != NULL)
99: *lenp = 0;
100: if (sigp != NULL)
101: *sigp = NULL;
102:
103: if (key == NULL || key->ecdsa == NULL ||
104: sshkey_type_plain(key->type) != KEY_ECDSA)
105: return SSH_ERR_INVALID_ARGUMENT;
106:
107: if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
108: (dlen = ssh_digest_bytes(hash_alg)) == 0)
109: return SSH_ERR_INTERNAL_ERROR;
110: if ((ret = ssh_digest_memory(hash_alg, data, datalen,
111: digest, sizeof(digest))) != 0)
112: goto out;
113:
114: if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) {
115: ret = SSH_ERR_LIBCRYPTO_ERROR;
116: goto out;
117: }
118:
119: if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
120: ret = SSH_ERR_ALLOC_FAIL;
121: goto out;
122: }
1.15 djm 123: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
124: if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
125: (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
1.11 djm 126: goto out;
127: if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
128: (ret = sshbuf_put_stringb(b, bb)) != 0)
129: goto out;
130: len = sshbuf_len(b);
131: if (sigp != NULL) {
132: if ((*sigp = malloc(len)) == NULL) {
133: ret = SSH_ERR_ALLOC_FAIL;
134: goto out;
135: }
136: memcpy(*sigp, sshbuf_ptr(b), len);
1.1 djm 137: }
138: if (lenp != NULL)
139: *lenp = len;
1.11 djm 140: ret = 0;
141: out:
142: explicit_bzero(digest, sizeof(digest));
1.12 mmcc 143: sshbuf_free(b);
144: sshbuf_free(bb);
1.14 jsing 145: ECDSA_SIG_free(sig);
1.11 djm 146: return ret;
147: }
1.1 djm 148:
1.11 djm 149: /* ARGSUSED */
1.1 djm 150: int
1.11 djm 151: ssh_ecdsa_verify(const struct sshkey *key,
152: const u_char *signature, size_t signaturelen,
153: const u_char *data, size_t datalen, u_int compat)
1.1 djm 154: {
1.11 djm 155: ECDSA_SIG *sig = NULL;
1.15 djm 156: BIGNUM *sig_r = NULL, *sig_s = NULL;
1.8 djm 157: int hash_alg;
1.11 djm 158: u_char digest[SSH_DIGEST_MAX_LENGTH];
159: size_t dlen;
160: int ret = SSH_ERR_INTERNAL_ERROR;
161: struct sshbuf *b = NULL, *sigbuf = NULL;
162: char *ktype = NULL;
163:
164: if (key == NULL || key->ecdsa == NULL ||
1.13 djm 165: sshkey_type_plain(key->type) != KEY_ECDSA ||
166: signature == NULL || signaturelen == 0)
1.11 djm 167: return SSH_ERR_INVALID_ARGUMENT;
168:
169: if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
170: (dlen = ssh_digest_bytes(hash_alg)) == 0)
171: return SSH_ERR_INTERNAL_ERROR;
1.7 djm 172:
1.1 djm 173: /* fetch signature */
1.11 djm 174: if ((b = sshbuf_from(signature, signaturelen)) == NULL)
175: return SSH_ERR_ALLOC_FAIL;
176: if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
177: sshbuf_froms(b, &sigbuf) != 0) {
178: ret = SSH_ERR_INVALID_FORMAT;
179: goto out;
180: }
181: if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
182: ret = SSH_ERR_KEY_TYPE_MISMATCH;
183: goto out;
184: }
185: if (sshbuf_len(b) != 0) {
186: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
187: goto out;
1.1 djm 188: }
189:
190: /* parse signature */
1.16 djm 191: if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
192: sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
193: ret = SSH_ERR_INVALID_FORMAT;
1.11 djm 194: goto out;
195: }
1.16 djm 196: if ((sig = ECDSA_SIG_new()) == NULL) {
197: ret = SSH_ERR_ALLOC_FAIL;
1.11 djm 198: goto out;
199: }
1.15 djm 200: if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) {
201: ret = SSH_ERR_LIBCRYPTO_ERROR;
202: goto out;
203: }
204: sig_r = sig_s = NULL; /* transferred */
205:
1.11 djm 206: if (sshbuf_len(sigbuf) != 0) {
207: ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
208: goto out;
209: }
210: if ((ret = ssh_digest_memory(hash_alg, data, datalen,
211: digest, sizeof(digest))) != 0)
212: goto out;
213:
214: switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) {
215: case 1:
216: ret = 0;
217: break;
218: case 0:
219: ret = SSH_ERR_SIGNATURE_INVALID;
220: goto out;
221: default:
222: ret = SSH_ERR_LIBCRYPTO_ERROR;
223: goto out;
1.8 djm 224: }
1.1 djm 225:
1.11 djm 226: out:
1.9 djm 227: explicit_bzero(digest, sizeof(digest));
1.12 mmcc 228: sshbuf_free(sigbuf);
229: sshbuf_free(b);
1.14 jsing 230: ECDSA_SIG_free(sig);
1.15 djm 231: BN_clear_free(sig_r);
232: BN_clear_free(sig_s);
1.11 djm 233: free(ktype);
1.1 djm 234: return ret;
235: }
1.17 djm 236:
1.18 ! djm 237: /* NB. not static; used by ECDSA-SK */
! 238: const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
1.17 djm 239: /* .size = */ ssh_ecdsa_size,
240: /* .alloc = */ NULL,
241: /* .cleanup = */ ssh_ecdsa_cleanup,
1.18 ! djm 242: /* .equal = */ ssh_ecdsa_equal,
1.17 djm 243: };
244:
245: const struct sshkey_impl sshkey_ecdsa_nistp256_impl = {
246: /* .name = */ "ecdsa-sha2-nistp256",
247: /* .shortname = */ "ECDSA",
248: /* .sigalg = */ NULL,
249: /* .type = */ KEY_ECDSA,
250: /* .nid = */ NID_X9_62_prime256v1,
251: /* .cert = */ 0,
252: /* .sigonly = */ 0,
253: /* .keybits = */ 0,
254: /* .funcs = */ &sshkey_ecdsa_funcs,
255: };
256:
257: const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = {
258: /* .name = */ "ecdsa-sha2-nistp256-cert-v01@openssh.com",
259: /* .shortname = */ "ECDSA-CERT",
260: /* .sigalg = */ NULL,
261: /* .type = */ KEY_ECDSA_CERT,
262: /* .nid = */ NID_X9_62_prime256v1,
263: /* .cert = */ 1,
264: /* .sigonly = */ 0,
265: /* .keybits = */ 0,
266: /* .funcs = */ &sshkey_ecdsa_funcs,
267: };
268:
269: const struct sshkey_impl sshkey_ecdsa_nistp384_impl = {
270: /* .name = */ "ecdsa-sha2-nistp384",
271: /* .shortname = */ "ECDSA",
272: /* .sigalg = */ NULL,
273: /* .type = */ KEY_ECDSA,
274: /* .nid = */ NID_secp384r1,
275: /* .cert = */ 0,
276: /* .sigonly = */ 0,
277: /* .keybits = */ 0,
278: /* .funcs = */ &sshkey_ecdsa_funcs,
279: };
280:
281: const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = {
282: /* .name = */ "ecdsa-sha2-nistp384-cert-v01@openssh.com",
283: /* .shortname = */ "ECDSA-CERT",
284: /* .sigalg = */ NULL,
285: /* .type = */ KEY_ECDSA_CERT,
286: /* .nid = */ NID_secp384r1,
287: /* .cert = */ 1,
288: /* .sigonly = */ 0,
289: /* .keybits = */ 0,
290: /* .funcs = */ &sshkey_ecdsa_funcs,
291: };
292:
293: const struct sshkey_impl sshkey_ecdsa_nistp521_impl = {
294: /* .name = */ "ecdsa-sha2-nistp521",
295: /* .shortname = */ "ECDSA",
296: /* .sigalg = */ NULL,
297: /* .type = */ KEY_ECDSA,
298: /* .nid = */ NID_secp521r1,
299: /* .cert = */ 0,
300: /* .sigonly = */ 0,
301: /* .keybits = */ 0,
302: /* .funcs = */ &sshkey_ecdsa_funcs,
303: };
304:
305: const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = {
306: /* .name = */ "ecdsa-sha2-nistp521-cert-v01@openssh.com",
307: /* .shortname = */ "ECDSA-CERT",
308: /* .sigalg = */ NULL,
309: /* .type = */ KEY_ECDSA_CERT,
310: /* .nid = */ NID_secp521r1,
311: /* .cert = */ 1,
312: /* .sigonly = */ 0,
313: /* .keybits = */ 0,
314: /* .funcs = */ &sshkey_ecdsa_funcs,
315: };