1 |
/* $OpenBSD: ssh-rsa.c,v 1.45 2010/08/31 09:58:37 djm Exp $ */ |
2 |
/* |
3 |
* Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> |
4 |
* |
5 |
* Permission to use, copy, modify, and distribute this software for any |
6 |
* purpose with or without fee is hereby granted, provided that the above |
7 |
* copyright notice and this permission notice appear in all copies. |
8 |
* |
9 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 |
*/ |
17 |
|
18 |
#include "includes.h" |
19 |
|
20 |
#include <sys/types.h> |
21 |
|
22 |
#include <openssl/evp.h> |
23 |
#include <openssl/err.h> |
24 |
|
25 |
#include <stdarg.h> |
26 |
#include <string.h> |
27 |
|
28 |
#include "xmalloc.h" |
29 |
#include "log.h" |
30 |
#include "buffer.h" |
31 |
#include "key.h" |
32 |
#include "compat.h" |
33 |
#include "misc.h" |
34 |
#include "ssh.h" |
35 |
|
36 |
static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); |
37 |
|
38 |
/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ |
39 |
int |
40 |
ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, |
41 |
const u_char *data, u_int datalen) |
42 |
{ |
43 |
const EVP_MD *evp_md; |
44 |
EVP_MD_CTX md; |
45 |
u_char digest[EVP_MAX_MD_SIZE], *sig; |
46 |
u_int slen, dlen, len; |
47 |
int ok, nid; |
48 |
Buffer b; |
49 |
|
50 |
if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && |
51 |
key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { |
52 |
error("ssh_rsa_sign: no RSA key"); |
53 |
return -1; |
54 |
} |
55 |
nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; |
56 |
if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { |
57 |
error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); |
58 |
return -1; |
59 |
} |
60 |
EVP_DigestInit(&md, evp_md); |
61 |
EVP_DigestUpdate(&md, data, datalen); |
62 |
EVP_DigestFinal(&md, digest, &dlen); |
63 |
|
64 |
slen = RSA_size(key->rsa); |
65 |
sig = xmalloc(slen); |
66 |
|
67 |
ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); |
68 |
memset(digest, 'd', sizeof(digest)); |
69 |
|
70 |
if (ok != 1) { |
71 |
int ecode = ERR_get_error(); |
72 |
|
73 |
error("ssh_rsa_sign: RSA_sign failed: %s", |
74 |
ERR_error_string(ecode, NULL)); |
75 |
xfree(sig); |
76 |
return -1; |
77 |
} |
78 |
if (len < slen) { |
79 |
u_int diff = slen - len; |
80 |
debug("slen %u > len %u", slen, len); |
81 |
memmove(sig + diff, sig, len); |
82 |
memset(sig, 0, diff); |
83 |
} else if (len > slen) { |
84 |
error("ssh_rsa_sign: slen %u slen2 %u", slen, len); |
85 |
xfree(sig); |
86 |
return -1; |
87 |
} |
88 |
/* encode signature */ |
89 |
buffer_init(&b); |
90 |
buffer_put_cstring(&b, "ssh-rsa"); |
91 |
buffer_put_string(&b, sig, slen); |
92 |
len = buffer_len(&b); |
93 |
if (lenp != NULL) |
94 |
*lenp = len; |
95 |
if (sigp != NULL) { |
96 |
*sigp = xmalloc(len); |
97 |
memcpy(*sigp, buffer_ptr(&b), len); |
98 |
} |
99 |
buffer_free(&b); |
100 |
memset(sig, 's', slen); |
101 |
xfree(sig); |
102 |
|
103 |
return 0; |
104 |
} |
105 |
|
106 |
int |
107 |
ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, |
108 |
const u_char *data, u_int datalen) |
109 |
{ |
110 |
Buffer b; |
111 |
const EVP_MD *evp_md; |
112 |
EVP_MD_CTX md; |
113 |
char *ktype; |
114 |
u_char digest[EVP_MAX_MD_SIZE], *sigblob; |
115 |
u_int len, dlen, modlen; |
116 |
int rlen, ret, nid; |
117 |
|
118 |
if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && |
119 |
key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { |
120 |
error("ssh_rsa_verify: no RSA key"); |
121 |
return -1; |
122 |
} |
123 |
if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { |
124 |
error("ssh_rsa_verify: RSA modulus too small: %d < minimum %d bits", |
125 |
BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); |
126 |
return -1; |
127 |
} |
128 |
buffer_init(&b); |
129 |
buffer_append(&b, signature, signaturelen); |
130 |
ktype = buffer_get_cstring(&b, NULL); |
131 |
if (strcmp("ssh-rsa", ktype) != 0) { |
132 |
error("ssh_rsa_verify: cannot handle type %s", ktype); |
133 |
buffer_free(&b); |
134 |
xfree(ktype); |
135 |
return -1; |
136 |
} |
137 |
xfree(ktype); |
138 |
sigblob = buffer_get_string(&b, &len); |
139 |
rlen = buffer_len(&b); |
140 |
buffer_free(&b); |
141 |
if (rlen != 0) { |
142 |
error("ssh_rsa_verify: remaining bytes in signature %d", rlen); |
143 |
xfree(sigblob); |
144 |
return -1; |
145 |
} |
146 |
/* RSA_verify expects a signature of RSA_size */ |
147 |
modlen = RSA_size(key->rsa); |
148 |
if (len > modlen) { |
149 |
error("ssh_rsa_verify: len %u > modlen %u", len, modlen); |
150 |
xfree(sigblob); |
151 |
return -1; |
152 |
} else if (len < modlen) { |
153 |
u_int diff = modlen - len; |
154 |
debug("ssh_rsa_verify: add padding: modlen %u > len %u", |
155 |
modlen, len); |
156 |
sigblob = xrealloc(sigblob, 1, modlen); |
157 |
memmove(sigblob + diff, sigblob, len); |
158 |
memset(sigblob, 0, diff); |
159 |
len = modlen; |
160 |
} |
161 |
nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; |
162 |
if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { |
163 |
error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid); |
164 |
xfree(sigblob); |
165 |
return -1; |
166 |
} |
167 |
EVP_DigestInit(&md, evp_md); |
168 |
EVP_DigestUpdate(&md, data, datalen); |
169 |
EVP_DigestFinal(&md, digest, &dlen); |
170 |
|
171 |
ret = openssh_RSA_verify(nid, digest, dlen, sigblob, len, key->rsa); |
172 |
memset(digest, 'd', sizeof(digest)); |
173 |
memset(sigblob, 's', len); |
174 |
xfree(sigblob); |
175 |
debug("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : ""); |
176 |
return ret; |
177 |
} |
178 |
|
179 |
/* |
180 |
* See: |
181 |
* http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ |
182 |
* ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn |
183 |
*/ |
184 |
/* |
185 |
* id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) |
186 |
* oiw(14) secsig(3) algorithms(2) 26 } |
187 |
*/ |
188 |
static const u_char id_sha1[] = { |
189 |
0x30, 0x21, /* type Sequence, length 0x21 (33) */ |
190 |
0x30, 0x09, /* type Sequence, length 0x09 */ |
191 |
0x06, 0x05, /* type OID, length 0x05 */ |
192 |
0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ |
193 |
0x05, 0x00, /* NULL */ |
194 |
0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ |
195 |
}; |
196 |
/* |
197 |
* id-md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) |
198 |
* rsadsi(113549) digestAlgorithm(2) 5 } |
199 |
*/ |
200 |
static const u_char id_md5[] = { |
201 |
0x30, 0x20, /* type Sequence, length 0x20 (32) */ |
202 |
0x30, 0x0c, /* type Sequence, length 0x09 */ |
203 |
0x06, 0x08, /* type OID, length 0x05 */ |
204 |
0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* id-md5 */ |
205 |
0x05, 0x00, /* NULL */ |
206 |
0x04, 0x10 /* Octet string, length 0x10 (16), followed by md5 hash */ |
207 |
}; |
208 |
|
209 |
static int |
210 |
openssh_RSA_verify(int type, u_char *hash, u_int hashlen, |
211 |
u_char *sigbuf, u_int siglen, RSA *rsa) |
212 |
{ |
213 |
u_int ret, rsasize, oidlen = 0, hlen = 0; |
214 |
int len, oidmatch, hashmatch; |
215 |
const u_char *oid = NULL; |
216 |
u_char *decrypted = NULL; |
217 |
|
218 |
ret = 0; |
219 |
switch (type) { |
220 |
case NID_sha1: |
221 |
oid = id_sha1; |
222 |
oidlen = sizeof(id_sha1); |
223 |
hlen = 20; |
224 |
break; |
225 |
case NID_md5: |
226 |
oid = id_md5; |
227 |
oidlen = sizeof(id_md5); |
228 |
hlen = 16; |
229 |
break; |
230 |
default: |
231 |
goto done; |
232 |
} |
233 |
if (hashlen != hlen) { |
234 |
error("bad hashlen"); |
235 |
goto done; |
236 |
} |
237 |
rsasize = RSA_size(rsa); |
238 |
if (siglen == 0 || siglen > rsasize) { |
239 |
error("bad siglen"); |
240 |
goto done; |
241 |
} |
242 |
decrypted = xmalloc(rsasize); |
243 |
if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, |
244 |
RSA_PKCS1_PADDING)) < 0) { |
245 |
error("RSA_public_decrypt failed: %s", |
246 |
ERR_error_string(ERR_get_error(), NULL)); |
247 |
goto done; |
248 |
} |
249 |
if (len < 0 || (u_int)len != hlen + oidlen) { |
250 |
error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); |
251 |
goto done; |
252 |
} |
253 |
oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; |
254 |
hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; |
255 |
if (!oidmatch) { |
256 |
error("oid mismatch"); |
257 |
goto done; |
258 |
} |
259 |
if (!hashmatch) { |
260 |
error("hash mismatch"); |
261 |
goto done; |
262 |
} |
263 |
ret = 1; |
264 |
done: |
265 |
if (decrypted) |
266 |
xfree(decrypted); |
267 |
return ret; |
268 |
} |