OSDN Git Service

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