1.13.1
by Colin Watson
Import upstream version 4.6p1 |
1 |
/* $OpenBSD: ssh-rsa.c,v 1.39 2006/08/03 03:34:42 deraadt Exp $ */
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
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 |
*/
|
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
17 |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
18 |
#include "includes.h" |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
19 |
|
20 |
#include <sys/types.h> |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
21 |
|
22 |
#include <openssl/evp.h> |
|
23 |
#include <openssl/err.h> |
|
24 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
25 |
#include <stdarg.h> |
26 |
#include <string.h> |
|
27 |
||
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
28 |
#include "xmalloc.h" |
29 |
#include "log.h" |
|
30 |
#include "buffer.h" |
|
31 |
#include "key.h" |
|
32 |
#include "compat.h" |
|
33 |
#include "ssh.h" |
|
34 |
||
35 |
static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); |
|
36 |
||
37 |
/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
|
|
38 |
int
|
|
39 |
ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, |
|
40 |
const u_char *data, u_int datalen) |
|
41 |
{
|
|
42 |
const EVP_MD *evp_md; |
|
43 |
EVP_MD_CTX md; |
|
44 |
u_char digest[EVP_MAX_MD_SIZE], *sig; |
|
45 |
u_int slen, dlen, len; |
|
46 |
int ok, nid; |
|
47 |
Buffer b; |
|
48 |
||
49 |
if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { |
|
50 |
error("ssh_rsa_sign: no RSA key"); |
|
51 |
return -1; |
|
52 |
}
|
|
53 |
nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; |
|
54 |
if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { |
|
55 |
error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); |
|
56 |
return -1; |
|
57 |
}
|
|
58 |
EVP_DigestInit(&md, evp_md); |
|
59 |
EVP_DigestUpdate(&md, data, datalen); |
|
60 |
EVP_DigestFinal(&md, digest, &dlen); |
|
61 |
||
62 |
slen = RSA_size(key->rsa); |
|
63 |
sig = xmalloc(slen); |
|
64 |
||
65 |
ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); |
|
66 |
memset(digest, 'd', sizeof(digest)); |
|
67 |
||
68 |
if (ok != 1) { |
|
69 |
int ecode = ERR_get_error(); |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
70 |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
71 |
error("ssh_rsa_sign: RSA_sign failed: %s", |
72 |
ERR_error_string(ecode, NULL)); |
|
73 |
xfree(sig); |
|
74 |
return -1; |
|
75 |
}
|
|
76 |
if (len < slen) { |
|
77 |
u_int diff = slen - len; |
|
78 |
debug("slen %u > len %u", slen, len); |
|
79 |
memmove(sig + diff, sig, len); |
|
80 |
memset(sig, 0, diff); |
|
81 |
} else if (len > slen) { |
|
82 |
error("ssh_rsa_sign: slen %u slen2 %u", slen, len); |
|
83 |
xfree(sig); |
|
84 |
return -1; |
|
85 |
}
|
|
86 |
/* encode signature */
|
|
87 |
buffer_init(&b); |
|
88 |
buffer_put_cstring(&b, "ssh-rsa"); |
|
89 |
buffer_put_string(&b, sig, slen); |
|
90 |
len = buffer_len(&b); |
|
91 |
if (lenp != NULL) |
|
92 |
*lenp = len; |
|
93 |
if (sigp != NULL) { |
|
94 |
*sigp = xmalloc(len); |
|
95 |
memcpy(*sigp, buffer_ptr(&b), len); |
|
96 |
}
|
|
97 |
buffer_free(&b); |
|
98 |
memset(sig, 's', slen); |
|
99 |
xfree(sig); |
|
100 |
||
101 |
return 0; |
|
102 |
}
|
|
103 |
||
104 |
int
|
|
105 |
ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, |
|
106 |
const u_char *data, u_int datalen) |
|
107 |
{
|
|
108 |
Buffer b; |
|
109 |
const EVP_MD *evp_md; |
|
110 |
EVP_MD_CTX md; |
|
111 |
char *ktype; |
|
112 |
u_char digest[EVP_MAX_MD_SIZE], *sigblob; |
|
113 |
u_int len, dlen, modlen; |
|
114 |
int rlen, ret, nid; |
|
115 |
||
116 |
if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { |
|
117 |
error("ssh_rsa_verify: no RSA key"); |
|
118 |
return -1; |
|
119 |
}
|
|
120 |
if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { |
|
121 |
error("ssh_rsa_verify: RSA modulus too small: %d < minimum %d bits", |
|
122 |
BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); |
|
123 |
return -1; |
|
124 |
}
|
|
125 |
buffer_init(&b); |
|
126 |
buffer_append(&b, signature, signaturelen); |
|
127 |
ktype = buffer_get_string(&b, NULL); |
|
128 |
if (strcmp("ssh-rsa", ktype) != 0) { |
|
129 |
error("ssh_rsa_verify: cannot handle type %s", ktype); |
|
130 |
buffer_free(&b); |
|
131 |
xfree(ktype); |
|
132 |
return -1; |
|
133 |
}
|
|
134 |
xfree(ktype); |
|
135 |
sigblob = buffer_get_string(&b, &len); |
|
136 |
rlen = buffer_len(&b); |
|
137 |
buffer_free(&b); |
|
138 |
if (rlen != 0) { |
|
139 |
error("ssh_rsa_verify: remaining bytes in signature %d", rlen); |
|
140 |
xfree(sigblob); |
|
141 |
return -1; |
|
142 |
}
|
|
143 |
/* RSA_verify expects a signature of RSA_size */
|
|
144 |
modlen = RSA_size(key->rsa); |
|
145 |
if (len > modlen) { |
|
146 |
error("ssh_rsa_verify: len %u > modlen %u", len, modlen); |
|
147 |
xfree(sigblob); |
|
148 |
return -1; |
|
149 |
} else if (len < modlen) { |
|
150 |
u_int diff = modlen - len; |
|
151 |
debug("ssh_rsa_verify: add padding: modlen %u > len %u", |
|
152 |
modlen, len); |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
153 |
sigblob = xrealloc(sigblob, 1, modlen); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
154 |
memmove(sigblob + diff, sigblob, len); |
155 |
memset(sigblob, 0, diff); |
|
156 |
len = modlen; |
|
157 |
}
|
|
158 |
nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; |
|
159 |
if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { |
|
160 |
error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid); |
|
161 |
xfree(sigblob); |
|
162 |
return -1; |
|
163 |
}
|
|
164 |
EVP_DigestInit(&md, evp_md); |
|
165 |
EVP_DigestUpdate(&md, data, datalen); |
|
166 |
EVP_DigestFinal(&md, digest, &dlen); |
|
167 |
||
168 |
ret = openssh_RSA_verify(nid, digest, dlen, sigblob, len, key->rsa); |
|
169 |
memset(digest, 'd', sizeof(digest)); |
|
170 |
memset(sigblob, 's', len); |
|
171 |
xfree(sigblob); |
|
172 |
debug("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : ""); |
|
173 |
return ret; |
|
174 |
}
|
|
175 |
||
176 |
/*
|
|
177 |
* See:
|
|
178 |
* http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
|
|
179 |
* ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
|
|
180 |
*/
|
|
181 |
/*
|
|
182 |
* id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
|
183 |
* oiw(14) secsig(3) algorithms(2) 26 }
|
|
184 |
*/
|
|
185 |
static const u_char id_sha1[] = { |
|
186 |
0x30, 0x21, /* type Sequence, length 0x21 (33) */ |
|
187 |
0x30, 0x09, /* type Sequence, length 0x09 */ |
|
188 |
0x06, 0x05, /* type OID, length 0x05 */ |
|
189 |
0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ |
|
190 |
0x05, 0x00, /* NULL */ |
|
191 |
0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ |
|
192 |
};
|
|
193 |
/*
|
|
194 |
* id-md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
|
|
195 |
* rsadsi(113549) digestAlgorithm(2) 5 }
|
|
196 |
*/
|
|
197 |
static const u_char id_md5[] = { |
|
198 |
0x30, 0x20, /* type Sequence, length 0x20 (32) */ |
|
199 |
0x30, 0x0c, /* type Sequence, length 0x09 */ |
|
200 |
0x06, 0x08, /* type OID, length 0x05 */ |
|
201 |
0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* id-md5 */ |
|
202 |
0x05, 0x00, /* NULL */ |
|
203 |
0x04, 0x10 /* Octet string, length 0x10 (16), followed by md5 hash */ |
|
204 |
};
|
|
205 |
||
206 |
static int |
|
207 |
openssh_RSA_verify(int type, u_char *hash, u_int hashlen, |
|
208 |
u_char *sigbuf, u_int siglen, RSA *rsa) |
|
209 |
{
|
|
210 |
u_int ret, rsasize, oidlen = 0, hlen = 0; |
|
211 |
int len; |
|
212 |
const u_char *oid = NULL; |
|
213 |
u_char *decrypted = NULL; |
|
214 |
||
215 |
ret = 0; |
|
216 |
switch (type) { |
|
217 |
case NID_sha1: |
|
218 |
oid = id_sha1; |
|
219 |
oidlen = sizeof(id_sha1); |
|
220 |
hlen = 20; |
|
221 |
break; |
|
222 |
case NID_md5: |
|
223 |
oid = id_md5; |
|
224 |
oidlen = sizeof(id_md5); |
|
225 |
hlen = 16; |
|
226 |
break; |
|
227 |
default: |
|
228 |
goto done; |
|
229 |
}
|
|
230 |
if (hashlen != hlen) { |
|
231 |
error("bad hashlen"); |
|
232 |
goto done; |
|
233 |
}
|
|
234 |
rsasize = RSA_size(rsa); |
|
235 |
if (siglen == 0 || siglen > rsasize) { |
|
236 |
error("bad siglen"); |
|
237 |
goto done; |
|
238 |
}
|
|
239 |
decrypted = xmalloc(rsasize); |
|
240 |
if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, |
|
241 |
RSA_PKCS1_PADDING)) < 0) { |
|
242 |
error("RSA_public_decrypt failed: %s", |
|
243 |
ERR_error_string(ERR_get_error(), NULL)); |
|
244 |
goto done; |
|
245 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
246 |
if (len < 0 || (u_int)len != hlen + oidlen) { |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
247 |
error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); |
248 |
goto done; |
|
249 |
}
|
|
250 |
if (memcmp(decrypted, oid, oidlen) != 0) { |
|
251 |
error("oid mismatch"); |
|
252 |
goto done; |
|
253 |
}
|
|
254 |
if (memcmp(decrypted + oidlen, hash, hlen) != 0) { |
|
255 |
error("hash mismatch"); |
|
256 |
goto done; |
|
257 |
}
|
|
258 |
ret = 1; |
|
259 |
done: |
|
260 |
if (decrypted) |
|
261 |
xfree(decrypted); |
|
262 |
return ret; |
|
263 |
}
|