Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Dropbear - a SSH2 server |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * All rights reserved. |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in |
15 | | * all copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. */ |
24 | | |
25 | | /* Perform RSA operations on data, including reading keys, signing and |
26 | | * verification. |
27 | | * |
28 | | * The format is specified in rfc2437, Applied Cryptography or The Handbook of |
29 | | * Applied Cryptography detail the general algorithm. */ |
30 | | |
31 | | #include "includes.h" |
32 | | #include "dbutil.h" |
33 | | #include "bignum.h" |
34 | | #include "rsa.h" |
35 | | #include "buffer.h" |
36 | | #include "ssh.h" |
37 | | #include "dbrandom.h" |
38 | | #include "signkey.h" |
39 | | |
40 | | #if DROPBEAR_RSA |
41 | | |
42 | | #if !(DROPBEAR_RSA_SHA1 || DROPBEAR_RSA_SHA256) |
43 | | #error Somehow RSA was enabled with neither DROPBEAR_RSA_SHA1 nor DROPBEAR_RSA_SHA256 |
44 | | #endif |
45 | | |
46 | | static void rsa_pad_em(const dropbear_rsa_key * key, |
47 | | const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype); |
48 | | |
49 | | /* Load a public rsa key from a buffer, initialising the values. |
50 | | * The key will have the same format as buf_put_rsa_key. |
51 | | * These should be freed with rsa_key_free. |
52 | | * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
53 | 2 | int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) { |
54 | | |
55 | 2 | int ret = DROPBEAR_FAILURE; |
56 | 2 | TRACE(("enter buf_get_rsa_pub_key")) |
57 | 2 | dropbear_assert(key != NULL); |
58 | 2 | m_mp_alloc_init_multi(&key->e, &key->n, NULL); |
59 | 2 | key->d = NULL; |
60 | 2 | key->p = NULL; |
61 | 2 | key->q = NULL; |
62 | | |
63 | 2 | buf_incrpos(buf, 4+SSH_SIGNKEY_RSA_LEN); /* int + "ssh-rsa" */ |
64 | | |
65 | 2 | if (buf_getmpint(buf, key->e) == DROPBEAR_FAILURE |
66 | 2 | || buf_getmpint(buf, key->n) == DROPBEAR_FAILURE) { |
67 | 0 | TRACE(("leave buf_get_rsa_pub_key: failure")) |
68 | 0 | goto out; |
69 | 0 | } |
70 | | |
71 | 2 | if (mp_count_bits(key->n) < MIN_RSA_KEYLEN) { |
72 | 0 | dropbear_log(LOG_WARNING, "RSA key too short"); |
73 | 0 | goto out; |
74 | 0 | } |
75 | | |
76 | | /* 64 bit is limit used by openssl, so we won't block any keys in the wild */ |
77 | 2 | if (mp_count_bits(key->e) > 64) { |
78 | 0 | dropbear_log(LOG_WARNING, "RSA key bad e"); |
79 | 0 | goto out; |
80 | 0 | } |
81 | | |
82 | 2 | TRACE(("leave buf_get_rsa_pub_key: success")) |
83 | 2 | ret = DROPBEAR_SUCCESS; |
84 | 2 | out: |
85 | 2 | if (ret == DROPBEAR_FAILURE) { |
86 | 0 | m_mp_free_multi(&key->e, &key->n, NULL); |
87 | 0 | } |
88 | 2 | return ret; |
89 | 2 | } |
90 | | |
91 | | /* Same as buf_get_rsa_pub_key, but reads private bits at the end. |
92 | | * Loads a private rsa key from a buffer |
93 | | * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
94 | 2 | int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key) { |
95 | 2 | int ret = DROPBEAR_FAILURE; |
96 | | |
97 | 2 | TRACE(("enter buf_get_rsa_priv_key")) |
98 | 2 | dropbear_assert(key != NULL); |
99 | | |
100 | 2 | if (buf_get_rsa_pub_key(buf, key) == DROPBEAR_FAILURE) { |
101 | 0 | TRACE(("leave buf_get_rsa_priv_key: pub: ret == DROPBEAR_FAILURE")) |
102 | 0 | return DROPBEAR_FAILURE; |
103 | 0 | } |
104 | | |
105 | 2 | key->d = NULL; |
106 | 2 | key->p = NULL; |
107 | 2 | key->q = NULL; |
108 | | |
109 | 2 | m_mp_alloc_init_multi(&key->d, NULL); |
110 | 2 | if (buf_getmpint(buf, key->d) == DROPBEAR_FAILURE) { |
111 | 0 | TRACE(("leave buf_get_rsa_priv_key: d: ret == DROPBEAR_FAILURE")) |
112 | 0 | goto out; |
113 | 0 | } |
114 | | |
115 | 2 | if (buf->pos == buf->len) { |
116 | | /* old Dropbear private keys didn't keep p and q, so we will ignore them*/ |
117 | 2 | } else { |
118 | 2 | m_mp_alloc_init_multi(&key->p, &key->q, NULL); |
119 | | |
120 | 2 | if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE) { |
121 | 0 | TRACE(("leave buf_get_rsa_priv_key: p: ret == DROPBEAR_FAILURE")) |
122 | 0 | goto out; |
123 | 0 | } |
124 | | |
125 | 2 | if (buf_getmpint(buf, key->q) == DROPBEAR_FAILURE) { |
126 | 0 | TRACE(("leave buf_get_rsa_priv_key: q: ret == DROPBEAR_FAILURE")) |
127 | 0 | goto out; |
128 | 0 | } |
129 | 2 | } |
130 | | |
131 | 2 | ret = DROPBEAR_SUCCESS; |
132 | 2 | out: |
133 | 2 | if (ret == DROPBEAR_FAILURE) { |
134 | 0 | m_mp_free_multi(&key->d, &key->p, &key->q, NULL); |
135 | 0 | } |
136 | 2 | TRACE(("leave buf_get_rsa_priv_key")) |
137 | 2 | return ret; |
138 | 2 | } |
139 | | |
140 | | |
141 | | /* Clear and free the memory used by a public or private key */ |
142 | 2 | void rsa_key_free(dropbear_rsa_key *key) { |
143 | | |
144 | 2 | TRACE2(("enter rsa_key_free")) |
145 | | |
146 | 2 | if (key == NULL) { |
147 | 2 | TRACE2(("leave rsa_key_free: key == NULL")) |
148 | 2 | return; |
149 | 2 | } |
150 | 0 | m_mp_free_multi(&key->d, &key->e, &key->p, &key->q, &key->n, NULL); |
151 | 0 | m_free(key); |
152 | 0 | TRACE2(("leave rsa_key_free")) |
153 | 0 | } |
154 | | |
155 | | /* Put the public rsa key into the buffer in the required format: |
156 | | * |
157 | | * string "ssh-rsa" |
158 | | * mp_int e |
159 | | * mp_int n |
160 | | */ |
161 | 0 | void buf_put_rsa_pub_key(buffer* buf, const dropbear_rsa_key *key) { |
162 | |
|
163 | 0 | TRACE(("enter buf_put_rsa_pub_key")) |
164 | 0 | dropbear_assert(key != NULL); |
165 | | |
166 | 0 | buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN); |
167 | 0 | buf_putmpint(buf, key->e); |
168 | 0 | buf_putmpint(buf, key->n); |
169 | |
|
170 | 0 | TRACE(("leave buf_put_rsa_pub_key")) |
171 | |
|
172 | 0 | } |
173 | | |
174 | | /* Same as buf_put_rsa_pub_key, but with the private "x" key appended */ |
175 | 0 | void buf_put_rsa_priv_key(buffer* buf, const dropbear_rsa_key *key) { |
176 | |
|
177 | 0 | TRACE(("enter buf_put_rsa_priv_key")) |
178 | |
|
179 | 0 | dropbear_assert(key != NULL); |
180 | 0 | buf_put_rsa_pub_key(buf, key); |
181 | 0 | buf_putmpint(buf, key->d); |
182 | | |
183 | | /* new versions have p and q, old versions don't */ |
184 | 0 | if (key->p) { |
185 | 0 | buf_putmpint(buf, key->p); |
186 | 0 | } |
187 | 0 | if (key->q) { |
188 | 0 | buf_putmpint(buf, key->q); |
189 | 0 | } |
190 | | |
191 | |
|
192 | 0 | TRACE(("leave buf_put_rsa_priv_key")) |
193 | |
|
194 | 0 | } |
195 | | |
196 | | #if DROPBEAR_SIGNKEY_VERIFY |
197 | | /* Verify a signature in buf, made on data by the key given. |
198 | | * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
199 | | int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, |
200 | 0 | enum signature_type sigtype, const buffer *data_buf) { |
201 | 0 | unsigned int slen; |
202 | 0 | DEF_MP_INT(rsa_s); |
203 | 0 | DEF_MP_INT(rsa_mdash); |
204 | 0 | DEF_MP_INT(rsa_em); |
205 | 0 | int ret = DROPBEAR_FAILURE; |
206 | |
|
207 | 0 | TRACE(("enter buf_rsa_verify")) |
208 | |
|
209 | 0 | dropbear_assert(key != NULL); |
210 | | |
211 | 0 | m_mp_init_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL); |
212 | |
|
213 | 0 | slen = buf_getint(buf); |
214 | 0 | if (slen != (unsigned int)mp_ubin_size(key->n)) { |
215 | 0 | TRACE(("bad size")) |
216 | 0 | goto out; |
217 | 0 | } |
218 | | |
219 | 0 | if (mp_from_ubin(&rsa_s, buf_getptr(buf, buf->len - buf->pos), |
220 | 0 | buf->len - buf->pos) != MP_OKAY) { |
221 | 0 | TRACE(("failed reading rsa_s")) |
222 | 0 | goto out; |
223 | 0 | } |
224 | | |
225 | | /* check that s <= n-1 */ |
226 | 0 | if (mp_cmp(&rsa_s, key->n) != MP_LT) { |
227 | 0 | TRACE(("s > n-1")) |
228 | 0 | goto out; |
229 | 0 | } |
230 | | |
231 | | /* create the magic PKCS padded value */ |
232 | 0 | rsa_pad_em(key, data_buf, &rsa_em, sigtype); |
233 | |
|
234 | 0 | if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) { |
235 | 0 | TRACE(("failed exptmod rsa_s")) |
236 | 0 | goto out; |
237 | 0 | } |
238 | | |
239 | 0 | if (mp_cmp(&rsa_em, &rsa_mdash) == MP_EQ) { |
240 | | /* signature is valid */ |
241 | 0 | TRACE(("success!")) |
242 | 0 | ret = DROPBEAR_SUCCESS; |
243 | 0 | } |
244 | |
|
245 | 0 | out: |
246 | 0 | mp_clear_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL); |
247 | 0 | TRACE(("leave buf_rsa_verify: ret %d", ret)) |
248 | 0 | return ret; |
249 | 0 | } |
250 | | |
251 | | #endif /* DROPBEAR_SIGNKEY_VERIFY */ |
252 | | |
253 | | /* Sign the data presented with key, writing the signature contents |
254 | | * to the buffer */ |
255 | | void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, |
256 | 0 | enum signature_type sigtype, const buffer *data_buf) { |
257 | 0 | const char *name = NULL; |
258 | 0 | unsigned int nsize, ssize, namelen = 0; |
259 | 0 | unsigned int i; |
260 | 0 | size_t written; |
261 | 0 | DEF_MP_INT(rsa_s); |
262 | 0 | DEF_MP_INT(rsa_tmp1); |
263 | 0 | DEF_MP_INT(rsa_tmp2); |
264 | 0 | DEF_MP_INT(rsa_tmp3); |
265 | | |
266 | 0 | TRACE(("enter buf_put_rsa_sign")) |
267 | 0 | dropbear_assert(key != NULL); |
268 | | |
269 | 0 | m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); |
270 | |
|
271 | 0 | rsa_pad_em(key, data_buf, &rsa_tmp1, sigtype); |
272 | | |
273 | | /* the actual signing of the padded data */ |
274 | |
|
275 | 0 | #if DROPBEAR_RSA_BLINDING |
276 | | |
277 | | /* With blinding, s = (r^(-1))((em)*r^e)^d mod n */ |
278 | | |
279 | | /* generate the r blinding value */ |
280 | | /* rsa_tmp2 is r */ |
281 | 0 | gen_random_mpint(key->n, &rsa_tmp2); |
282 | | |
283 | | /* rsa_tmp1 is em */ |
284 | | /* em' = em * r^e mod n */ |
285 | | |
286 | | /* rsa_s used as a temp var*/ |
287 | 0 | if (mp_exptmod(&rsa_tmp2, key->e, key->n, &rsa_s) != MP_OKAY) { |
288 | 0 | dropbear_exit("RSA error"); |
289 | 0 | } |
290 | 0 | if (mp_invmod(&rsa_tmp2, key->n, &rsa_tmp3) != MP_OKAY) { |
291 | 0 | dropbear_exit("RSA error"); |
292 | 0 | } |
293 | 0 | if (mp_mulmod(&rsa_tmp1, &rsa_s, key->n, &rsa_tmp2) != MP_OKAY) { |
294 | 0 | dropbear_exit("RSA error"); |
295 | 0 | } |
296 | | |
297 | | /* rsa_tmp2 is em' */ |
298 | | /* s' = (em')^d mod n */ |
299 | 0 | if (mp_exptmod(&rsa_tmp2, key->d, key->n, &rsa_tmp1) != MP_OKAY) { |
300 | 0 | dropbear_exit("RSA error"); |
301 | 0 | } |
302 | | |
303 | | /* rsa_tmp1 is s' */ |
304 | | /* rsa_tmp3 is r^(-1) mod n */ |
305 | | /* s = (s')r^(-1) mod n */ |
306 | 0 | if (mp_mulmod(&rsa_tmp1, &rsa_tmp3, key->n, &rsa_s) != MP_OKAY) { |
307 | 0 | dropbear_exit("RSA error"); |
308 | 0 | } |
309 | | |
310 | | #else |
311 | | |
312 | | /* s = em^d mod n */ |
313 | | /* rsa_tmp1 is em */ |
314 | | if (mp_exptmod(&rsa_tmp1, key->d, key->n, &rsa_s) != MP_OKAY) { |
315 | | dropbear_exit("RSA error"); |
316 | | } |
317 | | |
318 | | #endif /* DROPBEAR_RSA_BLINDING */ |
319 | | |
320 | 0 | mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); |
321 | | |
322 | | /* create the signature to return */ |
323 | 0 | name = signature_name_from_type(sigtype, &namelen); |
324 | 0 | buf_putstring(buf, name, namelen); |
325 | |
|
326 | 0 | nsize = mp_ubin_size(key->n); |
327 | | |
328 | | /* string rsa_signature_blob length */ |
329 | 0 | buf_putint(buf, nsize); |
330 | | /* pad out s to same length as n */ |
331 | 0 | ssize = mp_ubin_size(&rsa_s); |
332 | 0 | dropbear_assert(ssize <= nsize); |
333 | 0 | for (i = 0; i < nsize-ssize; i++) { |
334 | 0 | buf_putbyte(buf, 0x00); |
335 | 0 | } |
336 | |
|
337 | 0 | if (mp_to_ubin(&rsa_s, buf_getwriteptr(buf, ssize), ssize, &written) != MP_OKAY) { |
338 | 0 | dropbear_exit("RSA error"); |
339 | 0 | } |
340 | 0 | buf_incrwritepos(buf, written); |
341 | 0 | mp_clear(&rsa_s); |
342 | |
|
343 | | #if defined(DEBUG_RSA) && DEBUG_TRACE |
344 | | if (!debug_trace) { |
345 | | printhex("RSA sig", buf->data, buf->len); |
346 | | } |
347 | | #endif |
348 | | |
349 | |
|
350 | 0 | TRACE(("leave buf_put_rsa_sign")) |
351 | 0 | } |
352 | | |
353 | | /* Creates the message value as expected by PKCS, |
354 | | see rfc8017 section 9.2 */ |
355 | | static void rsa_pad_em(const dropbear_rsa_key * key, |
356 | 0 | const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype) { |
357 | | /* EM = 0x00 || 0x01 || PS || 0x00 || T |
358 | | PS is padding of 0xff to make EM the size of key->n |
359 | | |
360 | | T is the DER encoding of the hash alg (sha1 or sha256) |
361 | | */ |
362 | | |
363 | | /* From rfc8017 page 46 */ |
364 | 0 | #if DROPBEAR_RSA_SHA1 |
365 | 0 | const unsigned char T_sha1[] = |
366 | 0 | {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, |
367 | 0 | 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}; |
368 | 0 | #endif |
369 | 0 | #if DROPBEAR_RSA_SHA256 |
370 | 0 | const unsigned char T_sha256[] = |
371 | 0 | {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, |
372 | 0 | 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; |
373 | 0 | #endif |
374 | |
|
375 | 0 | int Tlen = 0; |
376 | 0 | const unsigned char *T = NULL; |
377 | 0 | const struct ltc_hash_descriptor *hash_desc = NULL; |
378 | 0 | buffer * rsa_EM = NULL; |
379 | 0 | hash_state hs; |
380 | 0 | unsigned int nsize; |
381 | |
|
382 | 0 | switch (sigtype) { |
383 | 0 | #if DROPBEAR_RSA_SHA1 |
384 | 0 | case DROPBEAR_SIGNATURE_RSA_SHA1: |
385 | 0 | Tlen = sizeof(T_sha1); |
386 | 0 | T = T_sha1; |
387 | 0 | hash_desc = &sha1_desc; |
388 | 0 | break; |
389 | 0 | #endif |
390 | 0 | #if DROPBEAR_RSA_SHA256 |
391 | 0 | case DROPBEAR_SIGNATURE_RSA_SHA256: |
392 | 0 | Tlen = sizeof(T_sha256); |
393 | 0 | T = T_sha256; |
394 | 0 | hash_desc = &sha256_desc; |
395 | 0 | break; |
396 | 0 | #endif |
397 | 0 | default: |
398 | 0 | assert(0); |
399 | 0 | } |
400 | | |
401 | | |
402 | 0 | nsize = mp_ubin_size(key->n); |
403 | |
|
404 | 0 | rsa_EM = buf_new(nsize); |
405 | | /* type byte */ |
406 | 0 | buf_putbyte(rsa_EM, 0x00); |
407 | 0 | buf_putbyte(rsa_EM, 0x01); |
408 | | /* Padding with PS 0xFF bytes */ |
409 | 0 | while(rsa_EM->pos != rsa_EM->size - (1 + Tlen + hash_desc->hashsize)) { |
410 | 0 | buf_putbyte(rsa_EM, 0xff); |
411 | 0 | } |
412 | 0 | buf_putbyte(rsa_EM, 0x00); |
413 | | /* Magic ASN1 stuff */ |
414 | 0 | buf_putbytes(rsa_EM, T, Tlen); |
415 | | |
416 | | /* The hash of the data */ |
417 | 0 | hash_desc->init(&hs); |
418 | 0 | hash_desc->process(&hs, data_buf->data, data_buf->len); |
419 | 0 | hash_desc->done(&hs, buf_getwriteptr(rsa_EM, hash_desc->hashsize)); |
420 | 0 | buf_incrwritepos(rsa_EM, hash_desc->hashsize); |
421 | |
|
422 | 0 | dropbear_assert(rsa_EM->pos == rsa_EM->size); |
423 | | |
424 | | /* Create the mp_int from the encoded bytes */ |
425 | 0 | buf_setpos(rsa_EM, 0); |
426 | 0 | bytes_to_mp(rsa_em, buf_getptr(rsa_EM, rsa_EM->size), |
427 | 0 | rsa_EM->size); |
428 | 0 | buf_free(rsa_EM); |
429 | 0 | } |
430 | | |
431 | | #endif /* DROPBEAR_RSA */ |