diff options
Diffstat (limited to 'ssh-rsa.c')
-rw-r--r-- | ssh-rsa.c | 260 |
1 files changed, 133 insertions, 127 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-rsa.c,v 1.51 2014/02/02 03:44:31 djm Exp $ */ | 1 | /* $OpenBSD: ssh-rsa.c,v 1.52 2014/06/24 01:13:21 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> | 3 | * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> |
4 | * | 4 | * |
@@ -25,163 +25,167 @@ | |||
25 | #include <stdarg.h> | 25 | #include <stdarg.h> |
26 | #include <string.h> | 26 | #include <string.h> |
27 | 27 | ||
28 | #include "xmalloc.h" | 28 | #include "sshbuf.h" |
29 | #include "log.h" | ||
30 | #include "buffer.h" | ||
31 | #include "key.h" | ||
32 | #include "compat.h" | 29 | #include "compat.h" |
33 | #include "misc.h" | 30 | #include "ssherr.h" |
34 | #include "ssh.h" | 31 | #define SSHKEY_INTERNAL |
32 | #include "sshkey.h" | ||
35 | #include "digest.h" | 33 | #include "digest.h" |
36 | 34 | ||
37 | static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); | 35 | static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); |
38 | 36 | ||
39 | /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ | 37 | /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ |
40 | int | 38 | int |
41 | ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, | 39 | ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, |
42 | const u_char *data, u_int datalen) | 40 | const u_char *data, size_t datalen, u_int compat) |
43 | { | 41 | { |
44 | int hash_alg; | 42 | int hash_alg; |
45 | u_char digest[SSH_DIGEST_MAX_LENGTH], *sig; | 43 | u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; |
46 | u_int slen, dlen, len; | 44 | size_t slen; |
47 | int ok, nid; | 45 | u_int dlen, len; |
48 | Buffer b; | 46 | int nid, ret = SSH_ERR_INTERNAL_ERROR; |
47 | struct sshbuf *b = NULL; | ||
49 | 48 | ||
50 | if (key == NULL || key_type_plain(key->type) != KEY_RSA || | 49 | if (lenp != NULL) |
51 | key->rsa == NULL) { | 50 | *lenp = 0; |
52 | error("%s: no RSA key", __func__); | 51 | if (sigp != NULL) |
53 | return -1; | 52 | *sigp = NULL; |
54 | } | 53 | |
54 | if (key == NULL || key->rsa == NULL || | ||
55 | sshkey_type_plain(key->type) != KEY_RSA) | ||
56 | return SSH_ERR_INVALID_ARGUMENT; | ||
57 | slen = RSA_size(key->rsa); | ||
58 | if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) | ||
59 | return SSH_ERR_INVALID_ARGUMENT; | ||
55 | 60 | ||
56 | /* hash the data */ | 61 | /* hash the data */ |
57 | hash_alg = SSH_DIGEST_SHA1; | 62 | hash_alg = SSH_DIGEST_SHA1; |
58 | nid = NID_sha1; | 63 | nid = NID_sha1; |
59 | if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { | 64 | if ((dlen = ssh_digest_bytes(hash_alg)) == 0) |
60 | error("%s: bad hash algorithm %d", __func__, hash_alg); | 65 | return SSH_ERR_INTERNAL_ERROR; |
61 | return -1; | 66 | if ((ret = ssh_digest_memory(hash_alg, data, datalen, |
62 | } | 67 | digest, sizeof(digest))) != 0) |
63 | if (ssh_digest_memory(hash_alg, data, datalen, | 68 | goto out; |
64 | digest, sizeof(digest)) != 0) { | ||
65 | error("%s: ssh_digest_memory failed", __func__); | ||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | slen = RSA_size(key->rsa); | ||
70 | sig = xmalloc(slen); | ||
71 | |||
72 | ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); | ||
73 | explicit_bzero(digest, sizeof(digest)); | ||
74 | 69 | ||
75 | if (ok != 1) { | 70 | if ((sig = malloc(slen)) == NULL) { |
76 | int ecode = ERR_get_error(); | 71 | ret = SSH_ERR_ALLOC_FAIL; |
72 | goto out; | ||
73 | } | ||
77 | 74 | ||
78 | error("%s: RSA_sign failed: %s", __func__, | 75 | if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { |
79 | ERR_error_string(ecode, NULL)); | 76 | ret = SSH_ERR_LIBCRYPTO_ERROR; |
80 | free(sig); | 77 | goto out; |
81 | return -1; | ||
82 | } | 78 | } |
83 | if (len < slen) { | 79 | if (len < slen) { |
84 | u_int diff = slen - len; | 80 | size_t diff = slen - len; |
85 | debug("slen %u > len %u", slen, len); | ||
86 | memmove(sig + diff, sig, len); | 81 | memmove(sig + diff, sig, len); |
87 | explicit_bzero(sig, diff); | 82 | explicit_bzero(sig, diff); |
88 | } else if (len > slen) { | 83 | } else if (len > slen) { |
89 | error("%s: slen %u slen2 %u", __func__, slen, len); | 84 | ret = SSH_ERR_INTERNAL_ERROR; |
90 | free(sig); | 85 | goto out; |
91 | return -1; | ||
92 | } | 86 | } |
93 | /* encode signature */ | 87 | /* encode signature */ |
94 | buffer_init(&b); | 88 | if ((b = sshbuf_new()) == NULL) { |
95 | buffer_put_cstring(&b, "ssh-rsa"); | 89 | ret = SSH_ERR_ALLOC_FAIL; |
96 | buffer_put_string(&b, sig, slen); | 90 | goto out; |
97 | len = buffer_len(&b); | 91 | } |
92 | if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 || | ||
93 | (ret = sshbuf_put_string(b, sig, slen)) != 0) | ||
94 | goto out; | ||
95 | len = sshbuf_len(b); | ||
96 | if (sigp != NULL) { | ||
97 | if ((*sigp = malloc(len)) == NULL) { | ||
98 | ret = SSH_ERR_ALLOC_FAIL; | ||
99 | goto out; | ||
100 | } | ||
101 | memcpy(*sigp, sshbuf_ptr(b), len); | ||
102 | } | ||
98 | if (lenp != NULL) | 103 | if (lenp != NULL) |
99 | *lenp = len; | 104 | *lenp = len; |
100 | if (sigp != NULL) { | 105 | ret = 0; |
101 | *sigp = xmalloc(len); | 106 | out: |
102 | memcpy(*sigp, buffer_ptr(&b), len); | 107 | explicit_bzero(digest, sizeof(digest)); |
108 | if (sig != NULL) { | ||
109 | explicit_bzero(sig, slen); | ||
110 | free(sig); | ||
103 | } | 111 | } |
104 | buffer_free(&b); | 112 | if (b != NULL) |
105 | explicit_bzero(sig, slen); | 113 | sshbuf_free(b); |
106 | free(sig); | ||
107 | |||
108 | return 0; | 114 | return 0; |
109 | } | 115 | } |
110 | 116 | ||
111 | int | 117 | int |
112 | ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, | 118 | ssh_rsa_verify(const struct sshkey *key, |
113 | const u_char *data, u_int datalen) | 119 | const u_char *signature, size_t signaturelen, |
120 | const u_char *data, size_t datalen, u_int compat) | ||
114 | { | 121 | { |
115 | Buffer b; | 122 | char *ktype = NULL; |
116 | int hash_alg; | 123 | int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; |
117 | char *ktype; | 124 | size_t len, diff, modlen, dlen; |
118 | u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob; | 125 | struct sshbuf *b = NULL; |
119 | u_int len, dlen, modlen; | 126 | u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; |
120 | int rlen, ret; | ||
121 | 127 | ||
122 | if (key == NULL || key_type_plain(key->type) != KEY_RSA || | 128 | if (key == NULL || key->rsa == NULL || |
123 | key->rsa == NULL) { | 129 | sshkey_type_plain(key->type) != KEY_RSA || |
124 | error("%s: no RSA key", __func__); | 130 | BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) |
125 | return -1; | 131 | return SSH_ERR_INVALID_ARGUMENT; |
126 | } | ||
127 | 132 | ||
128 | if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { | 133 | if ((b = sshbuf_from(signature, signaturelen)) == NULL) |
129 | error("%s: RSA modulus too small: %d < minimum %d bits", | 134 | return SSH_ERR_ALLOC_FAIL; |
130 | __func__, BN_num_bits(key->rsa->n), | 135 | if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { |
131 | SSH_RSA_MINIMUM_MODULUS_SIZE); | 136 | ret = SSH_ERR_INVALID_FORMAT; |
132 | return -1; | 137 | goto out; |
133 | } | 138 | } |
134 | buffer_init(&b); | ||
135 | buffer_append(&b, signature, signaturelen); | ||
136 | ktype = buffer_get_cstring(&b, NULL); | ||
137 | if (strcmp("ssh-rsa", ktype) != 0) { | 139 | if (strcmp("ssh-rsa", ktype) != 0) { |
138 | error("%s: cannot handle type %s", __func__, ktype); | 140 | ret = SSH_ERR_KEY_TYPE_MISMATCH; |
139 | buffer_free(&b); | 141 | goto out; |
140 | free(ktype); | ||
141 | return -1; | ||
142 | } | 142 | } |
143 | free(ktype); | 143 | if (sshbuf_get_string(b, &sigblob, &len) != 0) { |
144 | sigblob = buffer_get_string(&b, &len); | 144 | ret = SSH_ERR_INVALID_FORMAT; |
145 | rlen = buffer_len(&b); | 145 | goto out; |
146 | buffer_free(&b); | 146 | } |
147 | if (rlen != 0) { | 147 | if (sshbuf_len(b) != 0) { |
148 | error("%s: remaining bytes in signature %d", __func__, rlen); | 148 | ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; |
149 | free(sigblob); | 149 | goto out; |
150 | return -1; | ||
151 | } | 150 | } |
152 | /* RSA_verify expects a signature of RSA_size */ | 151 | /* RSA_verify expects a signature of RSA_size */ |
153 | modlen = RSA_size(key->rsa); | 152 | modlen = RSA_size(key->rsa); |
154 | if (len > modlen) { | 153 | if (len > modlen) { |
155 | error("%s: len %u > modlen %u", __func__, len, modlen); | 154 | ret = SSH_ERR_KEY_BITS_MISMATCH; |
156 | free(sigblob); | 155 | goto out; |
157 | return -1; | ||
158 | } else if (len < modlen) { | 156 | } else if (len < modlen) { |
159 | u_int diff = modlen - len; | 157 | diff = modlen - len; |
160 | debug("%s: add padding: modlen %u > len %u", __func__, | 158 | osigblob = sigblob; |
161 | modlen, len); | 159 | if ((sigblob = realloc(sigblob, modlen)) == NULL) { |
162 | sigblob = xrealloc(sigblob, 1, modlen); | 160 | sigblob = osigblob; /* put it back for clear/free */ |
161 | ret = SSH_ERR_ALLOC_FAIL; | ||
162 | goto out; | ||
163 | } | ||
163 | memmove(sigblob + diff, sigblob, len); | 164 | memmove(sigblob + diff, sigblob, len); |
164 | explicit_bzero(sigblob, diff); | 165 | explicit_bzero(sigblob, diff); |
165 | len = modlen; | 166 | len = modlen; |
166 | } | 167 | } |
167 | /* hash the data */ | ||
168 | hash_alg = SSH_DIGEST_SHA1; | 168 | hash_alg = SSH_DIGEST_SHA1; |
169 | if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { | 169 | if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { |
170 | error("%s: bad hash algorithm %d", __func__, hash_alg); | 170 | ret = SSH_ERR_INTERNAL_ERROR; |
171 | return -1; | 171 | goto out; |
172 | } | ||
173 | if (ssh_digest_memory(hash_alg, data, datalen, | ||
174 | digest, sizeof(digest)) != 0) { | ||
175 | error("%s: ssh_digest_memory failed", __func__); | ||
176 | return -1; | ||
177 | } | 172 | } |
173 | if ((ret = ssh_digest_memory(hash_alg, data, datalen, | ||
174 | digest, sizeof(digest))) != 0) | ||
175 | goto out; | ||
178 | 176 | ||
179 | ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, | 177 | ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, |
180 | key->rsa); | 178 | key->rsa); |
179 | out: | ||
180 | if (sigblob != NULL) { | ||
181 | explicit_bzero(sigblob, len); | ||
182 | free(sigblob); | ||
183 | } | ||
184 | if (ktype != NULL) | ||
185 | free(ktype); | ||
186 | if (b != NULL) | ||
187 | sshbuf_free(b); | ||
181 | explicit_bzero(digest, sizeof(digest)); | 188 | explicit_bzero(digest, sizeof(digest)); |
182 | explicit_bzero(sigblob, len); | ||
183 | free(sigblob); | ||
184 | debug("%s: signature %scorrect", __func__, (ret == 0) ? "in" : ""); | ||
185 | return ret; | 189 | return ret; |
186 | } | 190 | } |
187 | 191 | ||
@@ -204,15 +208,15 @@ static const u_char id_sha1[] = { | |||
204 | }; | 208 | }; |
205 | 209 | ||
206 | static int | 210 | static int |
207 | openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen, | 211 | openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, |
208 | u_char *sigbuf, u_int siglen, RSA *rsa) | 212 | u_char *sigbuf, size_t siglen, RSA *rsa) |
209 | { | 213 | { |
210 | u_int ret, rsasize, oidlen = 0, hlen = 0; | 214 | size_t ret, rsasize = 0, oidlen = 0, hlen = 0; |
211 | int len, oidmatch, hashmatch; | 215 | int len, oidmatch, hashmatch; |
212 | const u_char *oid = NULL; | 216 | const u_char *oid = NULL; |
213 | u_char *decrypted = NULL; | 217 | u_char *decrypted = NULL; |
214 | 218 | ||
215 | ret = 0; | 219 | ret = SSH_ERR_INTERNAL_ERROR; |
216 | switch (hash_alg) { | 220 | switch (hash_alg) { |
217 | case SSH_DIGEST_SHA1: | 221 | case SSH_DIGEST_SHA1: |
218 | oid = id_sha1; | 222 | oid = id_sha1; |
@@ -223,37 +227,39 @@ openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen, | |||
223 | goto done; | 227 | goto done; |
224 | } | 228 | } |
225 | if (hashlen != hlen) { | 229 | if (hashlen != hlen) { |
226 | error("bad hashlen"); | 230 | ret = SSH_ERR_INVALID_ARGUMENT; |
227 | goto done; | 231 | goto done; |
228 | } | 232 | } |
229 | rsasize = RSA_size(rsa); | 233 | rsasize = RSA_size(rsa); |
230 | if (siglen == 0 || siglen > rsasize) { | 234 | if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || |
231 | error("bad siglen"); | 235 | siglen == 0 || siglen > rsasize) { |
236 | ret = SSH_ERR_INVALID_ARGUMENT; | ||
237 | goto done; | ||
238 | } | ||
239 | if ((decrypted = malloc(rsasize)) == NULL) { | ||
240 | ret = SSH_ERR_ALLOC_FAIL; | ||
232 | goto done; | 241 | goto done; |
233 | } | 242 | } |
234 | decrypted = xmalloc(rsasize); | ||
235 | if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, | 243 | if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, |
236 | RSA_PKCS1_PADDING)) < 0) { | 244 | RSA_PKCS1_PADDING)) < 0) { |
237 | error("RSA_public_decrypt failed: %s", | 245 | ret = SSH_ERR_LIBCRYPTO_ERROR; |
238 | ERR_error_string(ERR_get_error(), NULL)); | ||
239 | goto done; | 246 | goto done; |
240 | } | 247 | } |
241 | if (len < 0 || (u_int)len != hlen + oidlen) { | 248 | if (len < 0 || (size_t)len != hlen + oidlen) { |
242 | error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); | 249 | ret = SSH_ERR_INVALID_FORMAT; |
243 | goto done; | 250 | goto done; |
244 | } | 251 | } |
245 | oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; | 252 | oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; |
246 | hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; | 253 | hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; |
247 | if (!oidmatch) { | 254 | if (!oidmatch || !hashmatch) { |
248 | error("oid mismatch"); | 255 | ret = SSH_ERR_SIGNATURE_INVALID; |
249 | goto done; | ||
250 | } | ||
251 | if (!hashmatch) { | ||
252 | error("hash mismatch"); | ||
253 | goto done; | 256 | goto done; |
254 | } | 257 | } |
255 | ret = 1; | 258 | ret = 0; |
256 | done: | 259 | done: |
257 | free(decrypted); | 260 | if (decrypted) { |
261 | explicit_bzero(decrypted, rsasize); | ||
262 | free(decrypted); | ||
263 | } | ||
258 | return ret; | 264 | return ret; |
259 | } | 265 | } |