Mercurial > dropbear
comparison keyimport.c @ 4:fe6bca95afa7
Makefile.in contains updated files required
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 01 Jun 2004 02:46:09 +0000 |
parents | |
children | 0bf5cebe622c |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 4:fe6bca95afa7 |
---|---|
1 /* | |
2 * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com | |
3 * keyfiles. | |
4 * | |
5 * The horribleness of the code is probably mine (matt). | |
6 * | |
7 * Modifications copyright 2003 Matt Johnston | |
8 * | |
9 * PuTTY is copyright 1997-2003 Simon Tatham. | |
10 * | |
11 * Portions copyright Robert de Bath, Joris van Rantwijk, Delian | |
12 * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, | |
13 * Justin Bradford, and CORE SDI S.A. | |
14 * | |
15 * Permission is hereby granted, free of charge, to any person | |
16 * obtaining a copy of this software and associated documentation files | |
17 * (the "Software"), to deal in the Software without restriction, | |
18 * including without limitation the rights to use, copy, modify, merge, | |
19 * publish, distribute, sublicense, and/or sell copies of the Software, | |
20 * and to permit persons to whom the Software is furnished to do so, | |
21 * subject to the following conditions: | |
22 * | |
23 * The above copyright notice and this permission notice shall be | |
24 * included in all copies or substantial portions of the Software. | |
25 * | |
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE | |
30 * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
31 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
32 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
33 */ | |
34 | |
35 #include "keyimport.h" | |
36 #include "bignum.h" | |
37 #include "buffer.h" | |
38 #include "dbutil.h" | |
39 | |
40 #define PUT_32BIT(cp, value) do { \ | |
41 (cp)[3] = (unsigned char)(value); \ | |
42 (cp)[2] = (unsigned char)((value) >> 8); \ | |
43 (cp)[1] = (unsigned char)((value) >> 16); \ | |
44 (cp)[0] = (unsigned char)((value) >> 24); } while (0) | |
45 | |
46 #define GET_32BIT(cp) \ | |
47 (((unsigned long)(unsigned char)(cp)[0] << 24) | \ | |
48 ((unsigned long)(unsigned char)(cp)[1] << 16) | \ | |
49 ((unsigned long)(unsigned char)(cp)[2] << 8) | \ | |
50 ((unsigned long)(unsigned char)(cp)[3])) | |
51 | |
52 static int openssh_encrypted(const char *filename); | |
53 static sign_key *openssh_read(const char *filename, char *passphrase); | |
54 static int openssh_write(const char *filename, sign_key *key, | |
55 char *passphrase); | |
56 | |
57 static int dropbear_write(const char*filename, sign_key * key); | |
58 static sign_key *dropbear_read(const char* filename); | |
59 | |
60 #if 0 | |
61 static int sshcom_encrypted(const char *filename, char **comment); | |
62 static struct ssh2_userkey *sshcom_read(const char *filename, char *passphrase); | |
63 static int sshcom_write(const char *filename, struct ssh2_userkey *key, | |
64 char *passphrase); | |
65 #endif | |
66 | |
67 int import_encrypted(const char* filename, int filetype) { | |
68 | |
69 if (filetype == KEYFILE_OPENSSH) { | |
70 return openssh_encrypted(filename); | |
71 #if 0 | |
72 } else if (filetype == KEYFILE_SSHCOM) { | |
73 return sshcom_encrypted(filename, NULL); | |
74 #endif | |
75 } | |
76 return 0; | |
77 } | |
78 | |
79 sign_key *import_read(const char *filename, char *passphrase, int filetype) { | |
80 | |
81 if (filetype == KEYFILE_OPENSSH) { | |
82 return openssh_read(filename, passphrase); | |
83 } else if (filetype == KEYFILE_DROPBEAR) { | |
84 return dropbear_read(filename); | |
85 #if 0 | |
86 } else if (filetype == KEYFILE_SSHCOM) { | |
87 return sshcom_read(filename, passphrase); | |
88 #endif | |
89 } | |
90 return NULL; | |
91 } | |
92 | |
93 int import_write(const char *filename, sign_key *key, char *passphrase, | |
94 int filetype) { | |
95 | |
96 if (filetype == KEYFILE_OPENSSH) { | |
97 return openssh_write(filename, key, passphrase); | |
98 } else if (filetype == KEYFILE_DROPBEAR) { | |
99 return dropbear_write(filename, key); | |
100 #if 0 | |
101 } else if (filetype == KEYFILE_SSHCOM) { | |
102 return sshcom_write(filename, key, passphrase); | |
103 #endif | |
104 } | |
105 return 0; | |
106 } | |
107 | |
108 static sign_key *dropbear_read(const char* filename) { | |
109 | |
110 buffer * buf = NULL; | |
111 int len, maxlen; | |
112 FILE *fp; | |
113 sign_key *ret = NULL; | |
114 int type; | |
115 | |
116 buf = buf_new(2000); | |
117 /* can't use buf_readfile since we might have "-" as filename */ | |
118 if (strlen(filename) == 1 && filename[0] == '-') { | |
119 fp = stdin; | |
120 } else { | |
121 fp = fopen(filename, "r"); | |
122 } | |
123 if (!fp) { | |
124 goto error; | |
125 } | |
126 | |
127 do { | |
128 maxlen = buf->size - buf->pos; | |
129 len = fread(buf_getwriteptr(buf, maxlen), 1, maxlen, fp); | |
130 buf_incrwritepos(buf, len); | |
131 } while (len != maxlen && len > 0); | |
132 | |
133 fclose(fp); | |
134 | |
135 buf_setpos(buf, 0); | |
136 ret = new_sign_key(); | |
137 | |
138 type = DROPBEAR_SIGNKEY_ANY; | |
139 if (buf_get_priv_key(buf, ret, &type) == DROPBEAR_FAILURE){ | |
140 goto error; | |
141 } | |
142 buf_free(buf); | |
143 | |
144 return ret; | |
145 | |
146 error: | |
147 if (buf) { | |
148 buf_free(buf); | |
149 } | |
150 if (ret) { | |
151 sign_key_free(ret); | |
152 } | |
153 return NULL; | |
154 } | |
155 | |
156 /* returns 0 on fail, 1 on success */ | |
157 static int dropbear_write(const char*filename, sign_key * key) { | |
158 | |
159 int keytype = -1; | |
160 buffer * buf; | |
161 FILE*fp; | |
162 int len; | |
163 int ret; | |
164 | |
165 #ifdef DROPBEAR_RSA | |
166 if (key->rsakey != NULL) { | |
167 keytype = DROPBEAR_SIGNKEY_RSA; | |
168 } | |
169 #endif | |
170 #ifdef DROPBEAR_DSS | |
171 if (key->dsskey != NULL) { | |
172 keytype = DROPBEAR_SIGNKEY_DSS; | |
173 } | |
174 #endif | |
175 | |
176 buf = buf_new(2000); | |
177 buf_put_priv_key(buf, key, keytype); | |
178 | |
179 if (strlen(filename) == 1 && filename[0] == '-') { | |
180 fp = stdout; | |
181 } else { | |
182 fp = fopen(filename, "w"); | |
183 } | |
184 if (!fp) { | |
185 ret = 0; | |
186 goto out; | |
187 } | |
188 | |
189 buf_setpos(buf, 0); | |
190 do { | |
191 len = fwrite(buf_getptr(buf, buf->len - buf->pos), | |
192 1, buf->len - buf->pos, fp); | |
193 buf_incrpos(buf, len); | |
194 } while (len > 0 && buf->len != buf->pos); | |
195 | |
196 if (buf->pos != buf->len) { | |
197 ret = 0; | |
198 } else { | |
199 ret = 1; | |
200 } | |
201 out: | |
202 buf_free(buf); | |
203 return ret; | |
204 } | |
205 | |
206 | |
207 /* ---------------------------------------------------------------------- | |
208 * Helper routines. (The base64 ones are defined in sshpubk.c.) | |
209 */ | |
210 | |
211 #define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \ | |
212 ((c) >= 'a' && (c) <= 'z') || \ | |
213 ((c) >= '0' && (c) <= '9') || \ | |
214 (c) == '+' || (c) == '/' || (c) == '=' \ | |
215 ) | |
216 | |
217 /* cpl has to be less than 100 */ | |
218 static void base64_encode_fp(FILE * fp, unsigned char *data, | |
219 int datalen, int cpl) | |
220 { | |
221 char out[100]; | |
222 int n; | |
223 unsigned long outlen; | |
224 int rawcpl; | |
225 rawcpl = cpl * 3 / 4; | |
226 assert((unsigned int)cpl < sizeof(out)); | |
227 | |
228 while (datalen > 0) { | |
229 n = (datalen < rawcpl ? datalen : rawcpl); | |
230 outlen = sizeof(out); | |
231 base64_encode(data, n, out, &outlen); | |
232 data += n; | |
233 datalen -= n; | |
234 fwrite(out, 1, outlen, fp); | |
235 fputc('\n', fp); | |
236 } | |
237 } | |
238 /* | |
239 * Read an ASN.1/BER identifier and length pair. | |
240 * | |
241 * Flags are a combination of the #defines listed below. | |
242 * | |
243 * Returns -1 if unsuccessful; otherwise returns the number of | |
244 * bytes used out of the source data. | |
245 */ | |
246 | |
247 /* ASN.1 tag classes. */ | |
248 #define ASN1_CLASS_UNIVERSAL (0 << 6) | |
249 #define ASN1_CLASS_APPLICATION (1 << 6) | |
250 #define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6) | |
251 #define ASN1_CLASS_PRIVATE (3 << 6) | |
252 #define ASN1_CLASS_MASK (3 << 6) | |
253 | |
254 /* Primitive versus constructed bit. */ | |
255 #define ASN1_CONSTRUCTED (1 << 5) | |
256 | |
257 static int ber_read_id_len(void *source, int sourcelen, | |
258 int *id, int *length, int *flags) | |
259 { | |
260 unsigned char *p = (unsigned char *) source; | |
261 | |
262 if (sourcelen == 0) | |
263 return -1; | |
264 | |
265 *flags = (*p & 0xE0); | |
266 if ((*p & 0x1F) == 0x1F) { | |
267 *id = 0; | |
268 while (*p & 0x80) { | |
269 *id = (*id << 7) | (*p & 0x7F); | |
270 p++, sourcelen--; | |
271 if (sourcelen == 0) | |
272 return -1; | |
273 } | |
274 *id = (*id << 7) | (*p & 0x7F); | |
275 p++, sourcelen--; | |
276 } else { | |
277 *id = *p & 0x1F; | |
278 p++, sourcelen--; | |
279 } | |
280 | |
281 if (sourcelen == 0) | |
282 return -1; | |
283 | |
284 if (*p & 0x80) { | |
285 int n = *p & 0x7F; | |
286 p++, sourcelen--; | |
287 if (sourcelen < n) | |
288 return -1; | |
289 *length = 0; | |
290 while (n--) | |
291 *length = (*length << 8) | (*p++); | |
292 sourcelen -= n; | |
293 } else { | |
294 *length = *p; | |
295 p++, sourcelen--; | |
296 } | |
297 | |
298 return p - (unsigned char *) source; | |
299 } | |
300 | |
301 /* | |
302 * Write an ASN.1/BER identifier and length pair. Returns the | |
303 * number of bytes consumed. Assumes dest contains enough space. | |
304 * Will avoid writing anything if dest is NULL, but still return | |
305 * amount of space required. | |
306 */ | |
307 static int ber_write_id_len(void *dest, int id, int length, int flags) | |
308 { | |
309 unsigned char *d = (unsigned char *)dest; | |
310 int len = 0; | |
311 | |
312 if (id <= 30) { | |
313 /* | |
314 * Identifier is one byte. | |
315 */ | |
316 len++; | |
317 if (d) *d++ = id | flags; | |
318 } else { | |
319 int n; | |
320 /* | |
321 * Identifier is multiple bytes: the first byte is 11111 | |
322 * plus the flags, and subsequent bytes encode the value of | |
323 * the identifier, 7 bits at a time, with the top bit of | |
324 * each byte 1 except the last one which is 0. | |
325 */ | |
326 len++; | |
327 if (d) *d++ = 0x1F | flags; | |
328 for (n = 1; (id >> (7*n)) > 0; n++) | |
329 continue; /* count the bytes */ | |
330 while (n--) { | |
331 len++; | |
332 if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F); | |
333 } | |
334 } | |
335 | |
336 if (length < 128) { | |
337 /* | |
338 * Length is one byte. | |
339 */ | |
340 len++; | |
341 if (d) *d++ = length; | |
342 } else { | |
343 int n; | |
344 /* | |
345 * Length is multiple bytes. The first is 0x80 plus the | |
346 * number of subsequent bytes, and the subsequent bytes | |
347 * encode the actual length. | |
348 */ | |
349 for (n = 1; (length >> (8*n)) > 0; n++) | |
350 continue; /* count the bytes */ | |
351 len++; | |
352 if (d) *d++ = 0x80 | n; | |
353 while (n--) { | |
354 len++; | |
355 if (d) *d++ = (length >> (8*n)) & 0xFF; | |
356 } | |
357 } | |
358 | |
359 return len; | |
360 } | |
361 | |
362 | |
363 /* Simple structure to point to an mp-int within a blob. */ | |
364 struct mpint_pos { void *start; int bytes; }; | |
365 | |
366 /* ---------------------------------------------------------------------- | |
367 * Code to read and write OpenSSH private keys. | |
368 */ | |
369 | |
370 enum { OSSH_DSA, OSSH_RSA }; | |
371 struct openssh_key { | |
372 int type; | |
373 int encrypted; | |
374 char iv[32]; | |
375 unsigned char *keyblob; | |
376 unsigned int keyblob_len, keyblob_size; | |
377 }; | |
378 | |
379 static struct openssh_key *load_openssh_key(const char *filename) | |
380 { | |
381 struct openssh_key *ret; | |
382 FILE *fp; | |
383 char buffer[256]; | |
384 char *errmsg = NULL, *p = NULL; | |
385 int headers_done; | |
386 unsigned long len, outlen; | |
387 | |
388 ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key)); | |
389 ret->keyblob = NULL; | |
390 ret->keyblob_len = ret->keyblob_size = 0; | |
391 ret->encrypted = 0; | |
392 memset(ret->iv, 0, sizeof(ret->iv)); | |
393 | |
394 if (strlen(filename) == 1 && filename[0] == '-') { | |
395 fp = stdin; | |
396 } else { | |
397 fp = fopen(filename, "r"); | |
398 } | |
399 if (!fp) { | |
400 errmsg = "Unable to open key file"; | |
401 goto error; | |
402 } | |
403 if (!fgets(buffer, sizeof(buffer), fp) || | |
404 0 != strncmp(buffer, "-----BEGIN ", 11) || | |
405 0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) { | |
406 errmsg = "File does not begin with OpenSSH key header"; | |
407 goto error; | |
408 } | |
409 if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n")) | |
410 ret->type = OSSH_RSA; | |
411 else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) | |
412 ret->type = OSSH_DSA; | |
413 else { | |
414 errmsg = "Unrecognised key type"; | |
415 goto error; | |
416 } | |
417 | |
418 headers_done = 0; | |
419 while (1) { | |
420 if (!fgets(buffer, sizeof(buffer), fp)) { | |
421 errmsg = "Unexpected end of file"; | |
422 goto error; | |
423 } | |
424 if (0 == strncmp(buffer, "-----END ", 9) && | |
425 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) | |
426 break; /* done */ | |
427 if ((p = strchr(buffer, ':')) != NULL) { | |
428 if (headers_done) { | |
429 errmsg = "Header found in body of key data"; | |
430 goto error; | |
431 } | |
432 *p++ = '\0'; | |
433 while (*p && isspace((unsigned char)*p)) p++; | |
434 if (!strcmp(buffer, "Proc-Type")) { | |
435 if (p[0] != '4' || p[1] != ',') { | |
436 errmsg = "Proc-Type is not 4 (only 4 is supported)"; | |
437 goto error; | |
438 } | |
439 p += 2; | |
440 if (!strcmp(p, "ENCRYPTED\n")) | |
441 ret->encrypted = 1; | |
442 } else if (!strcmp(buffer, "DEK-Info")) { | |
443 int i, j; | |
444 | |
445 if (strncmp(p, "DES-EDE3-CBC,", 13)) { | |
446 errmsg = "Ciphers other than DES-EDE3-CBC not supported"; | |
447 goto error; | |
448 } | |
449 p += 13; | |
450 for (i = 0; i < 8; i++) { | |
451 if (1 != sscanf(p, "%2x", &j)) | |
452 break; | |
453 ret->iv[i] = j; | |
454 p += 2; | |
455 } | |
456 if (i < 8) { | |
457 errmsg = "Expected 16-digit iv in DEK-Info"; | |
458 goto error; | |
459 } | |
460 } | |
461 } else { | |
462 headers_done = 1; | |
463 len = strlen(buffer); | |
464 outlen = len*4/3; | |
465 if (ret->keyblob_len + outlen > ret->keyblob_size) { | |
466 ret->keyblob_size = ret->keyblob_len + outlen + 256; | |
467 ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, | |
468 ret->keyblob_size); | |
469 } | |
470 outlen = ret->keyblob_size - ret->keyblob_len; | |
471 if (base64_decode(buffer, len, | |
472 ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){ | |
473 errmsg = "Error decoding base64"; | |
474 goto error; | |
475 } | |
476 ret->keyblob_len += outlen; | |
477 } | |
478 } | |
479 | |
480 if (ret->keyblob_len == 0 || !ret->keyblob) { | |
481 errmsg = "Key body not present"; | |
482 goto error; | |
483 } | |
484 | |
485 if (ret->encrypted && ret->keyblob_len % 8 != 0) { | |
486 errmsg = "Encrypted key blob is not a multiple of cipher block size"; | |
487 goto error; | |
488 } | |
489 | |
490 memset(buffer, 0, sizeof(buffer)); | |
491 return ret; | |
492 | |
493 error: | |
494 memset(buffer, 0, sizeof(buffer)); | |
495 if (ret) { | |
496 if (ret->keyblob) { | |
497 memset(ret->keyblob, 0, ret->keyblob_size); | |
498 m_free(ret->keyblob); | |
499 } | |
500 memset(&ret, 0, sizeof(ret)); | |
501 m_free(ret); | |
502 } | |
503 if (errmsg) { | |
504 fprintf(stderr, "Error: %s\n", errmsg); | |
505 } | |
506 return NULL; | |
507 } | |
508 | |
509 static int openssh_encrypted(const char *filename) | |
510 { | |
511 struct openssh_key *key = load_openssh_key(filename); | |
512 int ret; | |
513 | |
514 if (!key) | |
515 return 0; | |
516 ret = key->encrypted; | |
517 memset(key->keyblob, 0, key->keyblob_size); | |
518 m_free(key->keyblob); | |
519 memset(&key, 0, sizeof(key)); | |
520 m_free(key); | |
521 return ret; | |
522 } | |
523 | |
524 static sign_key *openssh_read(const char *filename, char *passphrase) | |
525 { | |
526 struct openssh_key *key; | |
527 unsigned char *p; | |
528 int ret, id, len, flags; | |
529 int i, num_integers = 0; | |
530 sign_key *retval = NULL; | |
531 char *errmsg; | |
532 char *modptr = NULL; | |
533 int modlen = -9999; | |
534 int type; | |
535 | |
536 sign_key *retkey; | |
537 buffer * blobbuf = NULL; | |
538 | |
539 key = load_openssh_key(filename); | |
540 | |
541 if (!key) | |
542 return NULL; | |
543 | |
544 if (key->encrypted) { | |
545 errmsg = "encrypted keys not supported currently"; | |
546 goto error; | |
547 #if 0 | |
548 /* matt TODO */ | |
549 /* | |
550 * Derive encryption key from passphrase and iv/salt: | |
551 * | |
552 * - let block A equal MD5(passphrase || iv) | |
553 * - let block B equal MD5(A || passphrase || iv) | |
554 * - block C would be MD5(B || passphrase || iv) and so on | |
555 * - encryption key is the first N bytes of A || B | |
556 */ | |
557 struct MD5Context md5c; | |
558 unsigned char keybuf[32]; | |
559 | |
560 MD5Init(&md5c); | |
561 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
562 MD5Update(&md5c, (unsigned char *)key->iv, 8); | |
563 MD5Final(keybuf, &md5c); | |
564 | |
565 MD5Init(&md5c); | |
566 MD5Update(&md5c, keybuf, 16); | |
567 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
568 MD5Update(&md5c, (unsigned char *)key->iv, 8); | |
569 MD5Final(keybuf+16, &md5c); | |
570 | |
571 /* | |
572 * Now decrypt the key blob. | |
573 */ | |
574 des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, | |
575 key->keyblob, key->keyblob_len); | |
576 | |
577 memset(&md5c, 0, sizeof(md5c)); | |
578 memset(keybuf, 0, sizeof(keybuf)); | |
579 #endif | |
580 } | |
581 | |
582 /* | |
583 * Now we have a decrypted key blob, which contains an ASN.1 | |
584 * encoded private key. We must now untangle the ASN.1. | |
585 * | |
586 * We expect the whole key blob to be formatted as a SEQUENCE | |
587 * (0x30 followed by a length code indicating that the rest of | |
588 * the blob is part of the sequence). Within that SEQUENCE we | |
589 * expect to see a bunch of INTEGERs. What those integers mean | |
590 * depends on the key type: | |
591 * | |
592 * - For RSA, we expect the integers to be 0, n, e, d, p, q, | |
593 * dmp1, dmq1, iqmp in that order. (The last three are d mod | |
594 * (p-1), d mod (q-1), inverse of q mod p respectively.) | |
595 * | |
596 * - For DSA, we expect them to be 0, p, q, g, y, x in that | |
597 * order. | |
598 */ | |
599 | |
600 p = key->keyblob; | |
601 | |
602 /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ | |
603 ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); | |
604 p += ret; | |
605 if (ret < 0 || id != 16) { | |
606 errmsg = "ASN.1 decoding failure - wrong password?"; | |
607 goto error; | |
608 } | |
609 | |
610 /* Expect a load of INTEGERs. */ | |
611 if (key->type == OSSH_RSA) | |
612 num_integers = 9; | |
613 else if (key->type == OSSH_DSA) | |
614 num_integers = 6; | |
615 | |
616 /* | |
617 * Space to create key blob in. | |
618 */ | |
619 blobbuf = buf_new(3000); | |
620 | |
621 if (key->type == OSSH_DSA) { | |
622 buf_putstring(blobbuf, "ssh-dss", 7); | |
623 } else if (key->type == OSSH_RSA) { | |
624 buf_putstring(blobbuf, "ssh-rsa", 7); | |
625 } | |
626 | |
627 for (i = 0; i < num_integers; i++) { | |
628 ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, | |
629 &id, &len, &flags); | |
630 p += ret; | |
631 if (ret < 0 || id != 2 || | |
632 key->keyblob+key->keyblob_len-p < len) { | |
633 errmsg = "ASN.1 decoding failure"; | |
634 goto error; | |
635 } | |
636 | |
637 if (i == 0) { | |
638 /* | |
639 * The first integer should be zero always (I think | |
640 * this is some sort of version indication). | |
641 */ | |
642 if (len != 1 || p[0] != 0) { | |
643 errmsg = "Version number mismatch"; | |
644 goto error; | |
645 } | |
646 } else if (key->type == OSSH_RSA) { | |
647 /* | |
648 * OpenSSH key order is n, e, d, p, q, dmp1, dmq1, iqmp | |
649 * but we want e, n, d, p, q | |
650 */ | |
651 if (i == 1) { | |
652 /* Save the details for after we deal with number 2. */ | |
653 modptr = (char *)p; | |
654 modlen = len; | |
655 } else if (i >= 2 && i <= 5) { | |
656 buf_putstring(blobbuf, p, len); | |
657 if (i == 2) { | |
658 buf_putstring(blobbuf, modptr, modlen); | |
659 } | |
660 } | |
661 } else if (key->type == OSSH_DSA) { | |
662 /* | |
663 * OpenSSH key order is p, q, g, y, x, | |
664 * we want the same. | |
665 */ | |
666 buf_putstring(blobbuf, p, len); | |
667 } | |
668 | |
669 /* Skip past the number. */ | |
670 p += len; | |
671 } | |
672 | |
673 /* | |
674 * Now put together the actual key. Simplest way to do this is | |
675 * to assemble our own key blobs and feed them to the createkey | |
676 * functions; this is a bit faffy but it does mean we get all | |
677 * the sanity checks for free. | |
678 */ | |
679 retkey = new_sign_key(); | |
680 buf_setpos(blobbuf, 0); | |
681 type = DROPBEAR_SIGNKEY_ANY; | |
682 if (buf_get_priv_key(blobbuf, retkey, &type) | |
683 != DROPBEAR_SUCCESS) { | |
684 errmsg = "unable to create key structure"; | |
685 sign_key_free(retkey); | |
686 retkey = NULL; | |
687 goto error; | |
688 } | |
689 | |
690 errmsg = NULL; /* no error */ | |
691 retval = retkey; | |
692 | |
693 error: | |
694 if (blobbuf) { | |
695 buf_burn(blobbuf); | |
696 buf_free(blobbuf); | |
697 } | |
698 m_burn(key->keyblob, key->keyblob_size); | |
699 m_free(key->keyblob); | |
700 m_burn(key, sizeof(key)); | |
701 m_free(key); | |
702 if (errmsg) { | |
703 fprintf(stderr, "Error: %s\n", errmsg); | |
704 } | |
705 return retval; | |
706 } | |
707 | |
708 static int openssh_write(const char *filename, sign_key *key, | |
709 char *passphrase) | |
710 { | |
711 buffer * keyblob = NULL; | |
712 buffer * extrablob = NULL; /* used for calculated values to write */ | |
713 unsigned char *outblob = NULL; | |
714 int outlen = -9999; | |
715 struct mpint_pos numbers[9]; | |
716 int nnumbers = -1, pos, len, seqlen, i; | |
717 char *header = NULL, *footer = NULL; | |
718 char zero[1]; | |
719 unsigned char iv[8]; | |
720 int ret = 0; | |
721 FILE *fp; | |
722 int keytype = -1; | |
723 | |
724 #ifdef DROPBEAR_RSA | |
725 mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ | |
726 | |
727 if (key->rsakey != NULL) { | |
728 keytype = DROPBEAR_SIGNKEY_RSA; | |
729 } | |
730 #endif | |
731 #ifdef DROPBEAR_DSS | |
732 if (key->dsskey != NULL) { | |
733 keytype = DROPBEAR_SIGNKEY_DSS; | |
734 } | |
735 #endif | |
736 | |
737 assert(keytype != -1); | |
738 | |
739 /* | |
740 * Fetch the key blobs. | |
741 */ | |
742 keyblob = buf_new(3000); | |
743 buf_put_priv_key(keyblob, key, keytype); | |
744 | |
745 buf_setpos(keyblob, 0); | |
746 /* skip the "ssh-rsa" or "ssh-dss" header */ | |
747 buf_incrpos(keyblob, buf_getint(keyblob)); | |
748 | |
749 /* | |
750 * Find the sequence of integers to be encoded into the OpenSSH | |
751 * key blob, and also decide on the header line. | |
752 */ | |
753 numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; | |
754 | |
755 #ifdef DROPBEAR_RSA | |
756 if (keytype == DROPBEAR_SIGNKEY_RSA) { | |
757 | |
758 if (key->rsakey->p == NULL || key->rsakey->q == NULL) { | |
759 fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n"); | |
760 goto error; | |
761 } | |
762 | |
763 /* e */ | |
764 numbers[2].bytes = buf_getint(keyblob); | |
765 numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); | |
766 buf_incrpos(keyblob, numbers[2].bytes); | |
767 | |
768 /* n */ | |
769 numbers[1].bytes = buf_getint(keyblob); | |
770 numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); | |
771 buf_incrpos(keyblob, numbers[1].bytes); | |
772 | |
773 /* d */ | |
774 numbers[3].bytes = buf_getint(keyblob); | |
775 numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); | |
776 buf_incrpos(keyblob, numbers[3].bytes); | |
777 | |
778 /* p */ | |
779 numbers[4].bytes = buf_getint(keyblob); | |
780 numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); | |
781 buf_incrpos(keyblob, numbers[4].bytes); | |
782 | |
783 /* q */ | |
784 numbers[5].bytes = buf_getint(keyblob); | |
785 numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); | |
786 buf_incrpos(keyblob, numbers[5].bytes); | |
787 | |
788 /* now calculate some extra parameters: */ | |
789 m_mp_init(&tmpval); | |
790 m_mp_init(&dmp1); | |
791 m_mp_init(&dmq1); | |
792 m_mp_init(&iqmp); | |
793 | |
794 /* dmp1 = d mod (p-1) */ | |
795 if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) { | |
796 fprintf(stderr, "Bignum error for p-1\n"); | |
797 goto error; | |
798 } | |
799 if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) { | |
800 fprintf(stderr, "Bignum error for dmp1\n"); | |
801 goto error; | |
802 } | |
803 | |
804 /* dmq1 = d mod (q-1) */ | |
805 if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) { | |
806 fprintf(stderr, "Bignum error for q-1\n"); | |
807 goto error; | |
808 } | |
809 if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) { | |
810 fprintf(stderr, "Bignum error for dmq1\n"); | |
811 goto error; | |
812 } | |
813 | |
814 /* iqmp = (q^-1) mod p */ | |
815 if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) { | |
816 fprintf(stderr, "Bignum error for iqmp\n"); | |
817 goto error; | |
818 } | |
819 | |
820 extrablob = buf_new(2000); | |
821 buf_putmpint(extrablob, &dmp1); | |
822 buf_putmpint(extrablob, &dmq1); | |
823 buf_putmpint(extrablob, &iqmp); | |
824 buf_setpos(extrablob, 0); | |
825 mp_clear(&dmp1); | |
826 mp_clear(&dmq1); | |
827 mp_clear(&iqmp); | |
828 mp_clear(&tmpval); | |
829 | |
830 /* dmp1 */ | |
831 numbers[6].bytes = buf_getint(extrablob); | |
832 numbers[6].start = buf_getptr(extrablob, numbers[6].bytes); | |
833 buf_incrpos(extrablob, numbers[6].bytes); | |
834 | |
835 /* dmq1 */ | |
836 numbers[7].bytes = buf_getint(extrablob); | |
837 numbers[7].start = buf_getptr(extrablob, numbers[7].bytes); | |
838 buf_incrpos(extrablob, numbers[7].bytes); | |
839 | |
840 /* iqmp */ | |
841 numbers[8].bytes = buf_getint(extrablob); | |
842 numbers[8].start = buf_getptr(extrablob, numbers[8].bytes); | |
843 buf_incrpos(extrablob, numbers[8].bytes); | |
844 | |
845 nnumbers = 9; | |
846 header = "-----BEGIN RSA PRIVATE KEY-----\n"; | |
847 footer = "-----END RSA PRIVATE KEY-----\n"; | |
848 } | |
849 #endif /* DROPBEAR_RSA */ | |
850 | |
851 #ifdef DROPBEAR_DSS | |
852 if (keytype == DROPBEAR_SIGNKEY_DSS) { | |
853 | |
854 /* p */ | |
855 numbers[1].bytes = buf_getint(keyblob); | |
856 numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); | |
857 buf_incrpos(keyblob, numbers[1].bytes); | |
858 | |
859 /* q */ | |
860 numbers[2].bytes = buf_getint(keyblob); | |
861 numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); | |
862 buf_incrpos(keyblob, numbers[2].bytes); | |
863 | |
864 /* g */ | |
865 numbers[3].bytes = buf_getint(keyblob); | |
866 numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); | |
867 buf_incrpos(keyblob, numbers[3].bytes); | |
868 | |
869 /* y */ | |
870 numbers[4].bytes = buf_getint(keyblob); | |
871 numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); | |
872 buf_incrpos(keyblob, numbers[4].bytes); | |
873 | |
874 /* x */ | |
875 numbers[5].bytes = buf_getint(keyblob); | |
876 numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); | |
877 buf_incrpos(keyblob, numbers[5].bytes); | |
878 | |
879 nnumbers = 6; | |
880 header = "-----BEGIN DSA PRIVATE KEY-----\n"; | |
881 footer = "-----END DSA PRIVATE KEY-----\n"; | |
882 } | |
883 #endif /* DROPBEAR_DSS */ | |
884 | |
885 /* | |
886 * Now count up the total size of the ASN.1 encoded integers, | |
887 * so as to determine the length of the containing SEQUENCE. | |
888 */ | |
889 len = 0; | |
890 for (i = 0; i < nnumbers; i++) { | |
891 len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); | |
892 len += numbers[i].bytes; | |
893 } | |
894 seqlen = len; | |
895 /* Now add on the SEQUENCE header. */ | |
896 len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); | |
897 /* Round up to the cipher block size, ensuring we have at least one | |
898 * byte of padding (see below). */ | |
899 outlen = len; | |
900 if (passphrase) | |
901 outlen = (outlen+8) &~ 7; | |
902 | |
903 /* | |
904 * Now we know how big outblob needs to be. Allocate it. | |
905 */ | |
906 outblob = (unsigned char*)m_malloc(outlen); | |
907 | |
908 /* | |
909 * And write the data into it. | |
910 */ | |
911 pos = 0; | |
912 pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); | |
913 for (i = 0; i < nnumbers; i++) { | |
914 pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); | |
915 memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); | |
916 pos += numbers[i].bytes; | |
917 } | |
918 | |
919 /* | |
920 * Padding on OpenSSH keys is deterministic. The number of | |
921 * padding bytes is always more than zero, and always at most | |
922 * the cipher block length. The value of each padding byte is | |
923 * equal to the number of padding bytes. So a plaintext that's | |
924 * an exact multiple of the block size will be padded with 08 | |
925 * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a | |
926 * plaintext one byte less than a multiple of the block size | |
927 * will be padded with just 01. | |
928 * | |
929 * This enables the OpenSSL key decryption function to strip | |
930 * off the padding algorithmically and return the unpadded | |
931 * plaintext to the next layer: it looks at the final byte, and | |
932 * then expects to find that many bytes at the end of the data | |
933 * with the same value. Those are all removed and the rest is | |
934 * returned. | |
935 */ | |
936 assert(pos == len); | |
937 while (pos < outlen) { | |
938 outblob[pos++] = outlen - len; | |
939 } | |
940 | |
941 /* | |
942 * Encrypt the key. | |
943 */ | |
944 if (passphrase) { | |
945 fprintf(stderr, "Encrypted keys aren't supported currently\n"); | |
946 goto error; | |
947 #if 0 | |
948 /* | |
949 * Invent an iv. Then derive encryption key from passphrase | |
950 * and iv/salt: | |
951 * | |
952 * - let block A equal MD5(passphrase || iv) | |
953 * - let block B equal MD5(A || passphrase || iv) | |
954 * - block C would be MD5(B || passphrase || iv) and so on | |
955 * - encryption key is the first N bytes of A || B | |
956 */ | |
957 struct MD5Context md5c; | |
958 unsigned char keybuf[32]; | |
959 | |
960 for (i = 0; i < 8; i++) iv[i] = random_byte(); | |
961 | |
962 MD5Init(&md5c); | |
963 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
964 MD5Update(&md5c, iv, 8); | |
965 MD5Final(keybuf, &md5c); | |
966 | |
967 MD5Init(&md5c); | |
968 MD5Update(&md5c, keybuf, 16); | |
969 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
970 MD5Update(&md5c, iv, 8); | |
971 MD5Final(keybuf+16, &md5c); | |
972 | |
973 /* | |
974 * Now encrypt the key blob. | |
975 */ | |
976 des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); | |
977 | |
978 memset(&md5c, 0, sizeof(md5c)); | |
979 memset(keybuf, 0, sizeof(keybuf)); | |
980 #endif | |
981 } | |
982 | |
983 /* | |
984 * And save it. We'll use Unix line endings just in case it's | |
985 * subsequently transferred in binary mode. | |
986 */ | |
987 if (strlen(filename) == 1 && filename[0] == '-') { | |
988 fp = stdout; | |
989 } else { | |
990 fp = fopen(filename, "wb"); /* ensure Unix line endings */ | |
991 } | |
992 if (!fp) { | |
993 fprintf(stderr, "Failed opening output file\n"); | |
994 goto error; | |
995 } | |
996 fputs(header, fp); | |
997 if (passphrase) { | |
998 fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,"); | |
999 for (i = 0; i < 8; i++) | |
1000 fprintf(fp, "%02X", iv[i]); | |
1001 fprintf(fp, "\n\n"); | |
1002 } | |
1003 base64_encode_fp(fp, outblob, outlen, 64); | |
1004 fputs(footer, fp); | |
1005 fclose(fp); | |
1006 ret = 1; | |
1007 | |
1008 error: | |
1009 if (outblob) { | |
1010 memset(outblob, 0, outlen); | |
1011 m_free(outblob); | |
1012 } | |
1013 if (keyblob) { | |
1014 buf_burn(keyblob); | |
1015 buf_free(keyblob); | |
1016 } | |
1017 if (extrablob) { | |
1018 buf_burn(extrablob); | |
1019 buf_free(extrablob); | |
1020 } | |
1021 return ret; | |
1022 } | |
1023 | |
1024 #if 0 | |
1025 /* XXX TODO ssh.com stuff isn't going yet */ | |
1026 | |
1027 /* ---------------------------------------------------------------------- | |
1028 * Code to read ssh.com private keys. | |
1029 */ | |
1030 | |
1031 /* | |
1032 * The format of the base64 blob is largely ssh2-packet-formatted, | |
1033 * except that mpints are a bit different: they're more like the | |
1034 * old ssh1 mpint. You have a 32-bit bit count N, followed by | |
1035 * (N+7)/8 bytes of data. | |
1036 * | |
1037 * So. The blob contains: | |
1038 * | |
1039 * - uint32 0x3f6ff9eb (magic number) | |
1040 * - uint32 size (total blob size) | |
1041 * - string key-type (see below) | |
1042 * - string cipher-type (tells you if key is encrypted) | |
1043 * - string encrypted-blob | |
1044 * | |
1045 * (The first size field includes the size field itself and the | |
1046 * magic number before it. All other size fields are ordinary ssh2 | |
1047 * strings, so the size field indicates how much data is to | |
1048 * _follow_.) | |
1049 * | |
1050 * The encrypted blob, once decrypted, contains a single string | |
1051 * which in turn contains the payload. (This allows padding to be | |
1052 * added after that string while still making it clear where the | |
1053 * real payload ends. Also it probably makes for a reasonable | |
1054 * decryption check.) | |
1055 * | |
1056 * The payload blob, for an RSA key, contains: | |
1057 * - mpint e | |
1058 * - mpint d | |
1059 * - mpint n (yes, the public and private stuff is intermixed) | |
1060 * - mpint u (presumably inverse of p mod q) | |
1061 * - mpint p (p is the smaller prime) | |
1062 * - mpint q (q is the larger) | |
1063 * | |
1064 * For a DSA key, the payload blob contains: | |
1065 * - uint32 0 | |
1066 * - mpint p | |
1067 * - mpint g | |
1068 * - mpint q | |
1069 * - mpint y | |
1070 * - mpint x | |
1071 * | |
1072 * Alternatively, if the parameters are `predefined', that | |
1073 * (0,p,g,q) sequence can be replaced by a uint32 1 and a string | |
1074 * containing some predefined parameter specification. *shudder*, | |
1075 * but I doubt we'll encounter this in real life. | |
1076 * | |
1077 * The key type strings are ghastly. The RSA key I looked at had a | |
1078 * type string of | |
1079 * | |
1080 * `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}' | |
1081 * | |
1082 * and the DSA key wasn't much better: | |
1083 * | |
1084 * `dl-modp{sign{dsa-nist-sha1},dh{plain}}' | |
1085 * | |
1086 * It isn't clear that these will always be the same. I think it | |
1087 * might be wise just to look at the `if-modn{sign{rsa' and | |
1088 * `dl-modp{sign{dsa' prefixes. | |
1089 * | |
1090 * Finally, the encryption. The cipher-type string appears to be | |
1091 * either `none' or `3des-cbc'. Looks as if this is SSH2-style | |
1092 * 3des-cbc (i.e. outer cbc rather than inner). The key is created | |
1093 * from the passphrase by means of yet another hashing faff: | |
1094 * | |
1095 * - first 16 bytes are MD5(passphrase) | |
1096 * - next 16 bytes are MD5(passphrase || first 16 bytes) | |
1097 * - if there were more, they'd be MD5(passphrase || first 32), | |
1098 * and so on. | |
1099 */ | |
1100 | |
1101 #define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb | |
1102 | |
1103 struct sshcom_key { | |
1104 char comment[256]; /* allowing any length is overkill */ | |
1105 unsigned char *keyblob; | |
1106 int keyblob_len, keyblob_size; | |
1107 }; | |
1108 | |
1109 static struct sshcom_key *load_sshcom_key(const char *filename) | |
1110 { | |
1111 struct sshcom_key *ret; | |
1112 FILE *fp; | |
1113 char buffer[256]; | |
1114 int len; | |
1115 char *errmsg, *p; | |
1116 int headers_done; | |
1117 char base64_bit[4]; | |
1118 int base64_chars = 0; | |
1119 | |
1120 ret = snew(struct sshcom_key); | |
1121 ret->comment[0] = '\0'; | |
1122 ret->keyblob = NULL; | |
1123 ret->keyblob_len = ret->keyblob_size = 0; | |
1124 | |
1125 fp = fopen(filename, "r"); | |
1126 if (!fp) { | |
1127 errmsg = "Unable to open key file"; | |
1128 goto error; | |
1129 } | |
1130 if (!fgets(buffer, sizeof(buffer), fp) || | |
1131 0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) { | |
1132 errmsg = "File does not begin with ssh.com key header"; | |
1133 goto error; | |
1134 } | |
1135 | |
1136 headers_done = 0; | |
1137 while (1) { | |
1138 if (!fgets(buffer, sizeof(buffer), fp)) { | |
1139 errmsg = "Unexpected end of file"; | |
1140 goto error; | |
1141 } | |
1142 if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n")) | |
1143 break; /* done */ | |
1144 if ((p = strchr(buffer, ':')) != NULL) { | |
1145 if (headers_done) { | |
1146 errmsg = "Header found in body of key data"; | |
1147 goto error; | |
1148 } | |
1149 *p++ = '\0'; | |
1150 while (*p && isspace((unsigned char)*p)) p++; | |
1151 /* | |
1152 * Header lines can end in a trailing backslash for | |
1153 * continuation. | |
1154 */ | |
1155 while ((len = strlen(p)) > (int)(sizeof(buffer) - (p-buffer) -1) || | |
1156 p[len-1] != '\n' || p[len-2] == '\\') { | |
1157 if (len > (int)((p-buffer) + sizeof(buffer)-2)) { | |
1158 errmsg = "Header line too long to deal with"; | |
1159 goto error; | |
1160 } | |
1161 if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) { | |
1162 errmsg = "Unexpected end of file"; | |
1163 goto error; | |
1164 } | |
1165 } | |
1166 p[strcspn(p, "\n")] = '\0'; | |
1167 if (!strcmp(buffer, "Comment")) { | |
1168 /* Strip quotes in comment if present. */ | |
1169 if (p[0] == '"' && p[strlen(p)-1] == '"') { | |
1170 p++; | |
1171 p[strlen(p)-1] = '\0'; | |
1172 } | |
1173 strncpy(ret->comment, p, sizeof(ret->comment)); | |
1174 ret->comment[sizeof(ret->comment)-1] = '\0'; | |
1175 } | |
1176 } else { | |
1177 headers_done = 1; | |
1178 | |
1179 p = buffer; | |
1180 while (isbase64(*p)) { | |
1181 base64_bit[base64_chars++] = *p; | |
1182 if (base64_chars == 4) { | |
1183 unsigned char out[3]; | |
1184 | |
1185 base64_chars = 0; | |
1186 | |
1187 len = base64_decode_atom(base64_bit, out); | |
1188 | |
1189 if (len <= 0) { | |
1190 errmsg = "Invalid base64 encoding"; | |
1191 goto error; | |
1192 } | |
1193 | |
1194 if (ret->keyblob_len + len > ret->keyblob_size) { | |
1195 ret->keyblob_size = ret->keyblob_len + len + 256; | |
1196 ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, | |
1197 unsigned char); | |
1198 } | |
1199 | |
1200 memcpy(ret->keyblob + ret->keyblob_len, out, len); | |
1201 ret->keyblob_len += len; | |
1202 } | |
1203 | |
1204 p++; | |
1205 } | |
1206 } | |
1207 } | |
1208 | |
1209 if (ret->keyblob_len == 0 || !ret->keyblob) { | |
1210 errmsg = "Key body not present"; | |
1211 goto error; | |
1212 } | |
1213 | |
1214 return ret; | |
1215 | |
1216 error: | |
1217 if (ret) { | |
1218 if (ret->keyblob) { | |
1219 memset(ret->keyblob, 0, ret->keyblob_size); | |
1220 m_free(ret->keyblob); | |
1221 } | |
1222 memset(&ret, 0, sizeof(ret)); | |
1223 m_free(ret); | |
1224 } | |
1225 return NULL; | |
1226 } | |
1227 | |
1228 int sshcom_encrypted(const char *filename, char **comment) | |
1229 { | |
1230 struct sshcom_key *key = load_sshcom_key(filename); | |
1231 int pos, len, answer; | |
1232 | |
1233 *comment = NULL; | |
1234 if (!key) | |
1235 return 0; | |
1236 | |
1237 /* | |
1238 * Check magic number. | |
1239 */ | |
1240 if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) | |
1241 return 0; /* key is invalid */ | |
1242 | |
1243 /* | |
1244 * Find the cipher-type string. | |
1245 */ | |
1246 answer = 0; | |
1247 pos = 8; | |
1248 if (key->keyblob_len < pos+4) | |
1249 goto done; /* key is far too short */ | |
1250 pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */ | |
1251 if (key->keyblob_len < pos+4) | |
1252 goto done; /* key is far too short */ | |
1253 len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */ | |
1254 if (key->keyblob_len < pos+4+len) | |
1255 goto done; /* cipher type string is incomplete */ | |
1256 if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) | |
1257 answer = 1; | |
1258 | |
1259 done: | |
1260 *comment = dupstr(key->comment); | |
1261 memset(key->keyblob, 0, key->keyblob_size); | |
1262 m_free(key->keyblob); | |
1263 memset(&key, 0, sizeof(key)); | |
1264 m_free(key); | |
1265 return answer; | |
1266 } | |
1267 | |
1268 static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) | |
1269 { | |
1270 int bits; | |
1271 int bytes; | |
1272 unsigned char *d = (unsigned char *) data; | |
1273 | |
1274 if (len < 4) | |
1275 goto error; | |
1276 bits = GET_32BIT(d); | |
1277 | |
1278 bytes = (bits + 7) / 8; | |
1279 if (len < 4+bytes) | |
1280 goto error; | |
1281 | |
1282 ret->start = d + 4; | |
1283 ret->bytes = bytes; | |
1284 return bytes+4; | |
1285 | |
1286 error: | |
1287 ret->start = NULL; | |
1288 ret->bytes = -1; | |
1289 return len; /* ensure further calls fail as well */ | |
1290 } | |
1291 | |
1292 static int sshcom_put_mpint(void *target, void *data, int len) | |
1293 { | |
1294 unsigned char *d = (unsigned char *)target; | |
1295 unsigned char *i = (unsigned char *)data; | |
1296 int bits = len * 8 - 1; | |
1297 | |
1298 while (bits > 0) { | |
1299 if (*i & (1 << (bits & 7))) | |
1300 break; | |
1301 if (!(bits-- & 7)) | |
1302 i++, len--; | |
1303 } | |
1304 | |
1305 PUT_32BIT(d, bits+1); | |
1306 memcpy(d+4, i, len); | |
1307 return len+4; | |
1308 } | |
1309 | |
1310 sign_key *sshcom_read(const char *filename, char *passphrase) | |
1311 { | |
1312 struct sshcom_key *key = load_sshcom_key(filename); | |
1313 char *errmsg; | |
1314 int pos, len; | |
1315 const char prefix_rsa[] = "if-modn{sign{rsa"; | |
1316 const char prefix_dsa[] = "dl-modp{sign{dsa"; | |
1317 enum { RSA, DSA } type; | |
1318 int encrypted; | |
1319 char *ciphertext; | |
1320 int cipherlen; | |
1321 struct ssh2_userkey *ret = NULL, *retkey; | |
1322 const struct ssh_signkey *alg; | |
1323 unsigned char *blob = NULL; | |
1324 int blobsize, publen, privlen; | |
1325 | |
1326 if (!key) | |
1327 return NULL; | |
1328 | |
1329 /* | |
1330 * Check magic number. | |
1331 */ | |
1332 if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) { | |
1333 errmsg = "Key does not begin with magic number"; | |
1334 goto error; | |
1335 } | |
1336 | |
1337 /* | |
1338 * Determine the key type. | |
1339 */ | |
1340 pos = 8; | |
1341 if (key->keyblob_len < pos+4 || | |
1342 (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { | |
1343 errmsg = "Key blob does not contain a key type string"; | |
1344 goto error; | |
1345 } | |
1346 if (len > sizeof(prefix_rsa) - 1 && | |
1347 !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) { | |
1348 type = RSA; | |
1349 } else if (len > sizeof(prefix_dsa) - 1 && | |
1350 !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) { | |
1351 type = DSA; | |
1352 } else { | |
1353 errmsg = "Key is of unknown type"; | |
1354 goto error; | |
1355 } | |
1356 pos += 4+len; | |
1357 | |
1358 /* | |
1359 * Determine the cipher type. | |
1360 */ | |
1361 if (key->keyblob_len < pos+4 || | |
1362 (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { | |
1363 errmsg = "Key blob does not contain a cipher type string"; | |
1364 goto error; | |
1365 } | |
1366 if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4)) | |
1367 encrypted = 0; | |
1368 else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8)) | |
1369 encrypted = 1; | |
1370 else { | |
1371 errmsg = "Key encryption is of unknown type"; | |
1372 goto error; | |
1373 } | |
1374 pos += 4+len; | |
1375 | |
1376 /* | |
1377 * Get hold of the encrypted part of the key. | |
1378 */ | |
1379 if (key->keyblob_len < pos+4 || | |
1380 (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { | |
1381 errmsg = "Key blob does not contain actual key data"; | |
1382 goto error; | |
1383 } | |
1384 ciphertext = (char *)key->keyblob + pos + 4; | |
1385 cipherlen = len; | |
1386 if (cipherlen == 0) { | |
1387 errmsg = "Length of key data is zero"; | |
1388 goto error; | |
1389 } | |
1390 | |
1391 /* | |
1392 * Decrypt it if necessary. | |
1393 */ | |
1394 if (encrypted) { | |
1395 /* | |
1396 * Derive encryption key from passphrase and iv/salt: | |
1397 * | |
1398 * - let block A equal MD5(passphrase) | |
1399 * - let block B equal MD5(passphrase || A) | |
1400 * - block C would be MD5(passphrase || A || B) and so on | |
1401 * - encryption key is the first N bytes of A || B | |
1402 */ | |
1403 struct MD5Context md5c; | |
1404 unsigned char keybuf[32], iv[8]; | |
1405 | |
1406 if (cipherlen % 8 != 0) { | |
1407 errmsg = "Encrypted part of key is not a multiple of cipher block" | |
1408 " size"; | |
1409 goto error; | |
1410 } | |
1411 | |
1412 MD5Init(&md5c); | |
1413 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1414 MD5Final(keybuf, &md5c); | |
1415 | |
1416 MD5Init(&md5c); | |
1417 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1418 MD5Update(&md5c, keybuf, 16); | |
1419 MD5Final(keybuf+16, &md5c); | |
1420 | |
1421 /* | |
1422 * Now decrypt the key blob. | |
1423 */ | |
1424 memset(iv, 0, sizeof(iv)); | |
1425 des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, | |
1426 cipherlen); | |
1427 | |
1428 memset(&md5c, 0, sizeof(md5c)); | |
1429 memset(keybuf, 0, sizeof(keybuf)); | |
1430 | |
1431 /* | |
1432 * Hereafter we return WRONG_PASSPHRASE for any parsing | |
1433 * error. (But only if we've just tried to decrypt it! | |
1434 * Returning WRONG_PASSPHRASE for an unencrypted key is | |
1435 * automatic doom.) | |
1436 */ | |
1437 if (encrypted) | |
1438 ret = SSH2_WRONG_PASSPHRASE; | |
1439 } | |
1440 | |
1441 /* | |
1442 * Strip away the containing string to get to the real meat. | |
1443 */ | |
1444 len = GET_32BIT(ciphertext); | |
1445 if (len > cipherlen-4) { | |
1446 errmsg = "containing string was ill-formed"; | |
1447 goto error; | |
1448 } | |
1449 ciphertext += 4; | |
1450 cipherlen = len; | |
1451 | |
1452 /* | |
1453 * Now we break down into RSA versus DSA. In either case we'll | |
1454 * construct public and private blobs in our own format, and | |
1455 * end up feeding them to alg->createkey(). | |
1456 */ | |
1457 blobsize = cipherlen + 256; | |
1458 blob = snewn(blobsize, unsigned char); | |
1459 privlen = 0; | |
1460 if (type == RSA) { | |
1461 struct mpint_pos n, e, d, u, p, q; | |
1462 int pos = 0; | |
1463 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e); | |
1464 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d); | |
1465 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n); | |
1466 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u); | |
1467 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); | |
1468 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); | |
1469 if (!q.start) { | |
1470 errmsg = "key data did not contain six integers"; | |
1471 goto error; | |
1472 } | |
1473 | |
1474 alg = &ssh_rsa; | |
1475 pos = 0; | |
1476 pos += put_string(blob+pos, "ssh-rsa", 7); | |
1477 pos += put_mp(blob+pos, e.start, e.bytes); | |
1478 pos += put_mp(blob+pos, n.start, n.bytes); | |
1479 publen = pos; | |
1480 pos += put_string(blob+pos, d.start, d.bytes); | |
1481 pos += put_mp(blob+pos, q.start, q.bytes); | |
1482 pos += put_mp(blob+pos, p.start, p.bytes); | |
1483 pos += put_mp(blob+pos, u.start, u.bytes); | |
1484 privlen = pos - publen; | |
1485 } else if (type == DSA) { | |
1486 struct mpint_pos p, q, g, x, y; | |
1487 int pos = 4; | |
1488 if (GET_32BIT(ciphertext) != 0) { | |
1489 errmsg = "predefined DSA parameters not supported"; | |
1490 goto error; | |
1491 } | |
1492 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); | |
1493 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g); | |
1494 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); | |
1495 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y); | |
1496 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x); | |
1497 if (!x.start) { | |
1498 errmsg = "key data did not contain five integers"; | |
1499 goto error; | |
1500 } | |
1501 | |
1502 alg = &ssh_dss; | |
1503 pos = 0; | |
1504 pos += put_string(blob+pos, "ssh-dss", 7); | |
1505 pos += put_mp(blob+pos, p.start, p.bytes); | |
1506 pos += put_mp(blob+pos, q.start, q.bytes); | |
1507 pos += put_mp(blob+pos, g.start, g.bytes); | |
1508 pos += put_mp(blob+pos, y.start, y.bytes); | |
1509 publen = pos; | |
1510 pos += put_mp(blob+pos, x.start, x.bytes); | |
1511 privlen = pos - publen; | |
1512 } | |
1513 | |
1514 assert(privlen > 0); /* should have bombed by now if not */ | |
1515 | |
1516 retkey = snew(struct ssh2_userkey); | |
1517 retkey->alg = alg; | |
1518 retkey->data = alg->createkey(blob, publen, blob+publen, privlen); | |
1519 if (!retkey->data) { | |
1520 m_free(retkey); | |
1521 errmsg = "unable to create key data structure"; | |
1522 goto error; | |
1523 } | |
1524 retkey->comment = dupstr(key->comment); | |
1525 | |
1526 errmsg = NULL; /* no error */ | |
1527 ret = retkey; | |
1528 | |
1529 error: | |
1530 if (blob) { | |
1531 memset(blob, 0, blobsize); | |
1532 m_free(blob); | |
1533 } | |
1534 memset(key->keyblob, 0, key->keyblob_size); | |
1535 m_free(key->keyblob); | |
1536 memset(&key, 0, sizeof(key)); | |
1537 m_free(key); | |
1538 return ret; | |
1539 } | |
1540 | |
1541 int sshcom_write(const char *filename, sign_key *key, | |
1542 char *passphrase) | |
1543 { | |
1544 unsigned char *pubblob, *privblob; | |
1545 int publen, privlen; | |
1546 unsigned char *outblob; | |
1547 int outlen; | |
1548 struct mpint_pos numbers[6]; | |
1549 int nnumbers, initial_zero, pos, lenpos, i; | |
1550 char *type; | |
1551 char *ciphertext; | |
1552 int cipherlen; | |
1553 int ret = 0; | |
1554 FILE *fp; | |
1555 | |
1556 /* | |
1557 * Fetch the key blobs. | |
1558 */ | |
1559 pubblob = key->alg->public_blob(key->data, &publen); | |
1560 privblob = key->alg->private_blob(key->data, &privlen); | |
1561 outblob = NULL; | |
1562 | |
1563 /* | |
1564 * Find the sequence of integers to be encoded into the OpenSSH | |
1565 * key blob, and also decide on the header line. | |
1566 */ | |
1567 if (key->alg == &ssh_rsa) { | |
1568 int pos; | |
1569 struct mpint_pos n, e, d, p, q, iqmp; | |
1570 | |
1571 pos = 4 + GET_32BIT(pubblob); | |
1572 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); | |
1573 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); | |
1574 pos = 0; | |
1575 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); | |
1576 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); | |
1577 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); | |
1578 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); | |
1579 | |
1580 assert(e.start && iqmp.start); /* can't go wrong */ | |
1581 | |
1582 numbers[0] = e; | |
1583 numbers[1] = d; | |
1584 numbers[2] = n; | |
1585 numbers[3] = iqmp; | |
1586 numbers[4] = q; | |
1587 numbers[5] = p; | |
1588 | |
1589 nnumbers = 6; | |
1590 initial_zero = 0; | |
1591 type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; | |
1592 } else if (key->alg == &ssh_dss) { | |
1593 int pos; | |
1594 struct mpint_pos p, q, g, y, x; | |
1595 | |
1596 pos = 4 + GET_32BIT(pubblob); | |
1597 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); | |
1598 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); | |
1599 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); | |
1600 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); | |
1601 pos = 0; | |
1602 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); | |
1603 | |
1604 assert(y.start && x.start); /* can't go wrong */ | |
1605 | |
1606 numbers[0] = p; | |
1607 numbers[1] = g; | |
1608 numbers[2] = q; | |
1609 numbers[3] = y; | |
1610 numbers[4] = x; | |
1611 | |
1612 nnumbers = 5; | |
1613 initial_zero = 1; | |
1614 type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}"; | |
1615 } else { | |
1616 assert(0); /* zoinks! */ | |
1617 } | |
1618 | |
1619 /* | |
1620 * Total size of key blob will be somewhere under 512 plus | |
1621 * combined length of integers. We'll calculate the more | |
1622 * precise size as we construct the blob. | |
1623 */ | |
1624 outlen = 512; | |
1625 for (i = 0; i < nnumbers; i++) | |
1626 outlen += 4 + numbers[i].bytes; | |
1627 outblob = snewn(outlen, unsigned char); | |
1628 | |
1629 /* | |
1630 * Create the unencrypted key blob. | |
1631 */ | |
1632 pos = 0; | |
1633 PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4; | |
1634 pos += 4; /* length field, fill in later */ | |
1635 pos += put_string(outblob+pos, type, strlen(type)); | |
1636 { | |
1637 char *ciphertype = passphrase ? "3des-cbc" : "none"; | |
1638 pos += put_string(outblob+pos, ciphertype, strlen(ciphertype)); | |
1639 } | |
1640 lenpos = pos; /* remember this position */ | |
1641 pos += 4; /* encrypted-blob size */ | |
1642 pos += 4; /* encrypted-payload size */ | |
1643 if (initial_zero) { | |
1644 PUT_32BIT(outblob+pos, 0); | |
1645 pos += 4; | |
1646 } | |
1647 for (i = 0; i < nnumbers; i++) | |
1648 pos += sshcom_put_mpint(outblob+pos, | |
1649 numbers[i].start, numbers[i].bytes); | |
1650 /* Now wrap up the encrypted payload. */ | |
1651 PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8)); | |
1652 /* Pad encrypted blob to a multiple of cipher block size. */ | |
1653 if (passphrase) { | |
1654 int padding = -(pos - (lenpos+4)) & 7; | |
1655 while (padding--) | |
1656 outblob[pos++] = random_byte(); | |
1657 } | |
1658 ciphertext = (char *)outblob+lenpos+4; | |
1659 cipherlen = pos - (lenpos+4); | |
1660 assert(!passphrase || cipherlen % 8 == 0); | |
1661 /* Wrap up the encrypted blob string. */ | |
1662 PUT_32BIT(outblob+lenpos, cipherlen); | |
1663 /* And finally fill in the total length field. */ | |
1664 PUT_32BIT(outblob+4, pos); | |
1665 | |
1666 assert(pos < outlen); | |
1667 | |
1668 /* | |
1669 * Encrypt the key. | |
1670 */ | |
1671 if (passphrase) { | |
1672 /* | |
1673 * Derive encryption key from passphrase and iv/salt: | |
1674 * | |
1675 * - let block A equal MD5(passphrase) | |
1676 * - let block B equal MD5(passphrase || A) | |
1677 * - block C would be MD5(passphrase || A || B) and so on | |
1678 * - encryption key is the first N bytes of A || B | |
1679 */ | |
1680 struct MD5Context md5c; | |
1681 unsigned char keybuf[32], iv[8]; | |
1682 | |
1683 MD5Init(&md5c); | |
1684 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1685 MD5Final(keybuf, &md5c); | |
1686 | |
1687 MD5Init(&md5c); | |
1688 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1689 MD5Update(&md5c, keybuf, 16); | |
1690 MD5Final(keybuf+16, &md5c); | |
1691 | |
1692 /* | |
1693 * Now decrypt the key blob. | |
1694 */ | |
1695 memset(iv, 0, sizeof(iv)); | |
1696 des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, | |
1697 cipherlen); | |
1698 | |
1699 memset(&md5c, 0, sizeof(md5c)); | |
1700 memset(keybuf, 0, sizeof(keybuf)); | |
1701 } | |
1702 | |
1703 /* | |
1704 * And save it. We'll use Unix line endings just in case it's | |
1705 * subsequently transferred in binary mode. | |
1706 */ | |
1707 fp = fopen(filename, "wb"); /* ensure Unix line endings */ | |
1708 if (!fp) | |
1709 goto error; | |
1710 fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); | |
1711 fprintf(fp, "Comment: \""); | |
1712 /* | |
1713 * Comment header is broken with backslash-newline if it goes | |
1714 * over 70 chars. Although it's surrounded by quotes, it | |
1715 * _doesn't_ escape backslashes or quotes within the string. | |
1716 * Don't ask me, I didn't design it. | |
1717 */ | |
1718 { | |
1719 int slen = 60; /* starts at 60 due to "Comment: " */ | |
1720 char *c = key->comment; | |
1721 while ((int)strlen(c) > slen) { | |
1722 fprintf(fp, "%.*s\\\n", slen, c); | |
1723 c += slen; | |
1724 slen = 70; /* allow 70 chars on subsequent lines */ | |
1725 } | |
1726 fprintf(fp, "%s\"\n", c); | |
1727 } | |
1728 base64_encode_fp(fp, outblob, pos, 70); | |
1729 fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); | |
1730 fclose(fp); | |
1731 ret = 1; | |
1732 | |
1733 error: | |
1734 if (outblob) { | |
1735 memset(outblob, 0, outlen); | |
1736 m_free(outblob); | |
1737 } | |
1738 if (privblob) { | |
1739 memset(privblob, 0, privlen); | |
1740 m_free(privblob); | |
1741 } | |
1742 if (pubblob) { | |
1743 memset(pubblob, 0, publen); | |
1744 m_free(pubblob); | |
1745 } | |
1746 return ret; | |
1747 } | |
1748 #endif /* ssh.com stuff disabled */ |