diff options
Diffstat (limited to 'ssh-ecdsa.c')
-rw-r--r-- | ssh-ecdsa.c | 232 |
1 files changed, 123 insertions, 109 deletions
diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c index 551c9c460..1119db045 100644 --- a/ssh-ecdsa.c +++ b/ssh-ecdsa.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-ecdsa.c,v 1.10 2014/02/03 23:28:00 djm Exp $ */ | 1 | /* $OpenBSD: ssh-ecdsa.c,v 1.11 2014/06/24 01:13:21 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2010 Damien Miller. All rights reserved. | 4 | * Copyright (c) 2010 Damien Miller. All rights reserved. |
@@ -37,141 +37,155 @@ | |||
37 | 37 | ||
38 | #include <string.h> | 38 | #include <string.h> |
39 | 39 | ||
40 | #include "xmalloc.h" | 40 | #include "sshbuf.h" |
41 | #include "buffer.h" | 41 | #include "ssherr.h" |
42 | #include "compat.h" | ||
43 | #include "log.h" | ||
44 | #include "key.h" | ||
45 | #include "digest.h" | 42 | #include "digest.h" |
43 | #define SSHKEY_INTERNAL | ||
44 | #include "sshkey.h" | ||
46 | 45 | ||
46 | /* ARGSUSED */ | ||
47 | int | 47 | int |
48 | ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp, | 48 | ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, |
49 | const u_char *data, u_int datalen) | 49 | const u_char *data, size_t datalen, u_int compat) |
50 | { | 50 | { |
51 | ECDSA_SIG *sig; | 51 | ECDSA_SIG *sig = NULL; |
52 | int hash_alg; | 52 | int hash_alg; |
53 | u_char digest[SSH_DIGEST_MAX_LENGTH]; | 53 | u_char digest[SSH_DIGEST_MAX_LENGTH]; |
54 | u_int len, dlen; | 54 | size_t len, dlen; |
55 | Buffer b, bb; | 55 | struct sshbuf *b = NULL, *bb = NULL; |
56 | int ret = SSH_ERR_INTERNAL_ERROR; | ||
56 | 57 | ||
57 | if (key == NULL || key_type_plain(key->type) != KEY_ECDSA || | 58 | if (lenp != NULL) |
58 | key->ecdsa == NULL) { | 59 | *lenp = 0; |
59 | error("%s: no ECDSA key", __func__); | 60 | if (sigp != NULL) |
60 | return -1; | 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; | ||
61 | } | 77 | } |
62 | 78 | ||
63 | hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid); | 79 | if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { |
64 | if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { | 80 | ret = SSH_ERR_ALLOC_FAIL; |
65 | error("%s: bad hash algorithm %d", __func__, hash_alg); | 81 | goto out; |
66 | return -1; | ||
67 | } | ||
68 | if (ssh_digest_memory(hash_alg, data, datalen, | ||
69 | digest, sizeof(digest)) != 0) { | ||
70 | error("%s: digest_memory failed", __func__); | ||
71 | return -1; | ||
72 | } | 82 | } |
73 | 83 | if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || | |
74 | sig = ECDSA_do_sign(digest, dlen, key->ecdsa); | 84 | (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) |
75 | explicit_bzero(digest, sizeof(digest)); | 85 | goto out; |
76 | 86 | if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || | |
77 | if (sig == NULL) { | 87 | (ret = sshbuf_put_stringb(b, bb)) != 0) |
78 | error("%s: sign failed", __func__); | 88 | goto out; |
79 | return -1; | 89 | len = sshbuf_len(b); |
90 | if (sigp != NULL) { | ||
91 | if ((*sigp = malloc(len)) == NULL) { | ||
92 | ret = SSH_ERR_ALLOC_FAIL; | ||
93 | goto out; | ||
94 | } | ||
95 | memcpy(*sigp, sshbuf_ptr(b), len); | ||
80 | } | 96 | } |
81 | |||
82 | buffer_init(&bb); | ||
83 | buffer_put_bignum2(&bb, sig->r); | ||
84 | buffer_put_bignum2(&bb, sig->s); | ||
85 | ECDSA_SIG_free(sig); | ||
86 | |||
87 | buffer_init(&b); | ||
88 | buffer_put_cstring(&b, key_ssh_name_plain(key)); | ||
89 | buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb)); | ||
90 | buffer_free(&bb); | ||
91 | len = buffer_len(&b); | ||
92 | if (lenp != NULL) | 97 | if (lenp != NULL) |
93 | *lenp = len; | 98 | *lenp = len; |
94 | if (sigp != NULL) { | 99 | ret = 0; |
95 | *sigp = xmalloc(len); | 100 | out: |
96 | memcpy(*sigp, buffer_ptr(&b), len); | 101 | explicit_bzero(digest, sizeof(digest)); |
97 | } | 102 | if (b != NULL) |
98 | buffer_free(&b); | 103 | sshbuf_free(b); |
99 | 104 | if (bb != NULL) | |
100 | return 0; | 105 | sshbuf_free(bb); |
106 | if (sig != NULL) | ||
107 | ECDSA_SIG_free(sig); | ||
108 | return ret; | ||
101 | } | 109 | } |
110 | |||
111 | /* ARGSUSED */ | ||
102 | int | 112 | int |
103 | ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen, | 113 | ssh_ecdsa_verify(const struct sshkey *key, |
104 | const u_char *data, u_int datalen) | 114 | const u_char *signature, size_t signaturelen, |
115 | const u_char *data, size_t datalen, u_int compat) | ||
105 | { | 116 | { |
106 | ECDSA_SIG *sig; | 117 | ECDSA_SIG *sig = NULL; |
107 | int hash_alg; | 118 | int hash_alg; |
108 | u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob; | 119 | u_char digest[SSH_DIGEST_MAX_LENGTH]; |
109 | u_int len, dlen; | 120 | size_t dlen; |
110 | int rlen, ret; | 121 | int ret = SSH_ERR_INTERNAL_ERROR; |
111 | Buffer b, bb; | 122 | struct sshbuf *b = NULL, *sigbuf = NULL; |
112 | char *ktype; | 123 | char *ktype = NULL; |
113 | 124 | ||
114 | if (key == NULL || key_type_plain(key->type) != KEY_ECDSA || | 125 | if (key == NULL || key->ecdsa == NULL || |
115 | key->ecdsa == NULL) { | 126 | sshkey_type_plain(key->type) != KEY_ECDSA) |
116 | error("%s: no ECDSA key", __func__); | 127 | return SSH_ERR_INVALID_ARGUMENT; |
117 | return -1; | 128 | |
118 | } | 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; | ||
119 | 132 | ||
120 | /* fetch signature */ | 133 | /* fetch signature */ |
121 | buffer_init(&b); | 134 | if ((b = sshbuf_from(signature, signaturelen)) == NULL) |
122 | buffer_append(&b, signature, signaturelen); | 135 | return SSH_ERR_ALLOC_FAIL; |
123 | ktype = buffer_get_string(&b, NULL); | 136 | if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || |
124 | if (strcmp(key_ssh_name_plain(key), ktype) != 0) { | 137 | sshbuf_froms(b, &sigbuf) != 0) { |
125 | error("%s: cannot handle type %s", __func__, ktype); | 138 | ret = SSH_ERR_INVALID_FORMAT; |
126 | buffer_free(&b); | 139 | goto out; |
127 | free(ktype); | ||
128 | return -1; | ||
129 | } | 140 | } |
130 | free(ktype); | 141 | if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { |
131 | sigblob = buffer_get_string(&b, &len); | 142 | ret = SSH_ERR_KEY_TYPE_MISMATCH; |
132 | rlen = buffer_len(&b); | 143 | goto out; |
133 | buffer_free(&b); | 144 | } |
134 | if (rlen != 0) { | 145 | if (sshbuf_len(b) != 0) { |
135 | error("%s: remaining bytes in signature %d", __func__, rlen); | 146 | ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; |
136 | free(sigblob); | 147 | goto out; |
137 | return -1; | ||
138 | } | 148 | } |
139 | 149 | ||
140 | /* parse signature */ | 150 | /* parse signature */ |
141 | if ((sig = ECDSA_SIG_new()) == NULL) | 151 | if ((sig = ECDSA_SIG_new()) == NULL) { |
142 | fatal("%s: ECDSA_SIG_new failed", __func__); | 152 | ret = SSH_ERR_ALLOC_FAIL; |
143 | 153 | goto out; | |
144 | buffer_init(&bb); | 154 | } |
145 | buffer_append(&bb, sigblob, len); | 155 | if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || |
146 | buffer_get_bignum2(&bb, sig->r); | 156 | sshbuf_get_bignum2(sigbuf, sig->s) != 0) { |
147 | buffer_get_bignum2(&bb, sig->s); | 157 | ret = SSH_ERR_INVALID_FORMAT; |
148 | if (buffer_len(&bb) != 0) | 158 | goto out; |
149 | fatal("%s: remaining bytes in inner sigblob", __func__); | 159 | } |
150 | buffer_free(&bb); | 160 | if (sshbuf_len(sigbuf) != 0) { |
151 | 161 | ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; | |
152 | /* clean up */ | 162 | goto out; |
153 | explicit_bzero(sigblob, len); | ||
154 | free(sigblob); | ||
155 | |||
156 | /* hash the data */ | ||
157 | hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid); | ||
158 | if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { | ||
159 | error("%s: bad hash algorithm %d", __func__, hash_alg); | ||
160 | return -1; | ||
161 | } | 163 | } |
162 | if (ssh_digest_memory(hash_alg, data, datalen, | 164 | if ((ret = ssh_digest_memory(hash_alg, data, datalen, |
163 | digest, sizeof(digest)) != 0) { | 165 | digest, sizeof(digest))) != 0) |
164 | error("%s: digest_memory failed", __func__); | 166 | goto out; |
165 | return -1; | 167 | |
168 | switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { | ||
169 | case 1: | ||
170 | ret = 0; | ||
171 | break; | ||
172 | case 0: | ||
173 | ret = SSH_ERR_SIGNATURE_INVALID; | ||
174 | goto out; | ||
175 | default: | ||
176 | ret = SSH_ERR_LIBCRYPTO_ERROR; | ||
177 | goto out; | ||
166 | } | 178 | } |
167 | 179 | ||
168 | ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa); | 180 | out: |
169 | explicit_bzero(digest, sizeof(digest)); | 181 | explicit_bzero(digest, sizeof(digest)); |
170 | 182 | if (sigbuf != NULL) | |
171 | ECDSA_SIG_free(sig); | 183 | sshbuf_free(sigbuf); |
172 | 184 | if (b != NULL) | |
173 | debug("%s: signature %s", __func__, | 185 | sshbuf_free(b); |
174 | ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); | 186 | if (sig != NULL) |
187 | ECDSA_SIG_free(sig); | ||
188 | free(ktype); | ||
175 | return ret; | 189 | return ret; |
176 | } | 190 | } |
177 | 191 | ||