OSDN Git Service

Normalize half / full width characters
[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 = GET_32BIT(d);\r
293     if (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;\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     memset(line, 0, 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             break;                     /* done */\r
375         if ((p = strchr(line, ':')) != NULL) {\r
376             if (headers_done) {\r
377                 errmsg = "header found in body of key data";\r
378                 goto error;\r
379             }\r
380             *p++ = '\0';\r
381             while (*p && isspace((unsigned char)*p)) p++;\r
382             if (!strcmp(line, "Proc-Type")) {\r
383                 if (p[0] != '4' || p[1] != ',') {\r
384                     errmsg = "Proc-Type is not 4 (only 4 is supported)";\r
385                     goto error;\r
386                 }\r
387                 p += 2;\r
388                 if (!strcmp(p, "ENCRYPTED"))\r
389                     ret->encrypted = 1;\r
390             } else if (!strcmp(line, "DEK-Info")) {\r
391                 int i, j, ivlen;\r
392 \r
393                 if (!strncmp(p, "DES-EDE3-CBC,", 13)) {\r
394                     ret->encryption = OSSH_ENC_3DES;\r
395                     ivlen = 8;\r
396                 } else if (!strncmp(p, "AES-128-CBC,", 12)) {\r
397                     ret->encryption = OSSH_ENC_AES;\r
398                     ivlen = 16;\r
399                 } else {\r
400                     errmsg = "unsupported cipher";\r
401                     goto error;\r
402                 }\r
403                 p = strchr(p, ',') + 1;/* always non-NULL, by above checks */\r
404                 for (i = 0; i < ivlen; i++) {\r
405                     if (1 != sscanf(p, "%2x", &j)) {\r
406                         errmsg = "expected more iv data in DEK-Info";\r
407                         goto error;\r
408                     }\r
409                     ret->iv[i] = j;\r
410                     p += 2;\r
411                 }\r
412                 if (*p) {\r
413                     errmsg = "more iv data than expected in DEK-Info";\r
414                     goto error;\r
415                 }\r
416             }\r
417         } else {\r
418             headers_done = 1;\r
419 \r
420             p = line;\r
421             while (isbase64(*p)) {\r
422                 base64_bit[base64_chars++] = *p;\r
423                 if (base64_chars == 4) {\r
424                     unsigned char out[3];\r
425                     int len;\r
426 \r
427                     base64_chars = 0;\r
428 \r
429                     len = base64_decode_atom(base64_bit, out);\r
430 \r
431                     if (len <= 0) {\r
432                         errmsg = "invalid base64 encoding";\r
433                         goto error;\r
434                     }\r
435 \r
436                     if (ret->keyblob_len + len > ret->keyblob_size) {\r
437                         ret->keyblob_size = ret->keyblob_len + len + 256;\r
438                         ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,\r
439                                                unsigned char);\r
440                     }\r
441 \r
442                     memcpy(ret->keyblob + ret->keyblob_len, out, len);\r
443                     ret->keyblob_len += len;\r
444 \r
445                     memset(out, 0, sizeof(out));\r
446                 }\r
447 \r
448                 p++;\r
449             }\r
450         }\r
451         memset(line, 0, strlen(line));\r
452         sfree(line);\r
453         line = NULL;\r
454     }\r
455 \r
456     if (ret->keyblob_len == 0 || !ret->keyblob) {\r
457         errmsg = "key body not present";\r
458         goto error;\r
459     }\r
460 \r
461     if (ret->encrypted && ret->keyblob_len % 8 != 0) {\r
462         errmsg = "encrypted key blob is not a multiple of cipher block size";\r
463         goto error;\r
464     }\r
465 \r
466     memset(base64_bit, 0, sizeof(base64_bit));\r
467     if (errmsg_p) *errmsg_p = NULL;\r
468     return ret;\r
469 \r
470     error:\r
471     if (line) {\r
472         memset(line, 0, strlen(line));\r
473         sfree(line);\r
474         line = NULL;\r
475     }\r
476     memset(base64_bit, 0, sizeof(base64_bit));\r
477     if (ret) {\r
478         if (ret->keyblob) {\r
479             memset(ret->keyblob, 0, ret->keyblob_size);\r
480             sfree(ret->keyblob);\r
481         }\r
482         memset(ret, 0, sizeof(*ret));\r
483         sfree(ret);\r
484     }\r
485     if (errmsg_p) *errmsg_p = errmsg;\r
486     return NULL;\r
487 }\r
488 \r
489 int openssh_encrypted(const Filename *filename)\r
490 {\r
491     struct openssh_key *key = load_openssh_key(filename, NULL);\r
492     int ret;\r
493 \r
494     if (!key)\r
495         return 0;\r
496     ret = key->encrypted;\r
497     memset(key->keyblob, 0, key->keyblob_size);\r
498     sfree(key->keyblob);\r
499     memset(key, 0, sizeof(*key));\r
500     sfree(key);\r
501     return ret;\r
502 }\r
503 \r
504 struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,\r
505                                   const char **errmsg_p)\r
506 {\r
507     struct openssh_key *key = load_openssh_key(filename, errmsg_p);\r
508     struct ssh2_userkey *retkey;\r
509     unsigned char *p;\r
510     int ret, id, len, flags;\r
511     int i, num_integers;\r
512     struct ssh2_userkey *retval = NULL;\r
513     char *errmsg;\r
514     unsigned char *blob;\r
515     int blobsize = 0, blobptr, privptr;\r
516     char *modptr = NULL;\r
517     int modlen = 0;\r
518 \r
519     blob = NULL;\r
520 \r
521     if (!key)\r
522         return NULL;\r
523 \r
524     if (key->encrypted) {\r
525         /*\r
526          * Derive encryption key from passphrase and iv/salt:\r
527          * \r
528          *  - let block A equal MD5(passphrase || iv)\r
529          *  - let block B equal MD5(A || passphrase || iv)\r
530          *  - block C would be MD5(B || passphrase || iv) and so on\r
531          *  - encryption key is the first N bytes of A || B\r
532          *\r
533          * (Note that only 8 bytes of the iv are used for key\r
534          * derivation, even when the key is encrypted with AES and\r
535          * hence there are 16 bytes available.)\r
536          */\r
537         struct MD5Context md5c;\r
538         unsigned char keybuf[32];\r
539 \r
540         MD5Init(&md5c);\r
541         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
542         MD5Update(&md5c, (unsigned char *)key->iv, 8);\r
543         MD5Final(keybuf, &md5c);\r
544 \r
545         MD5Init(&md5c);\r
546         MD5Update(&md5c, keybuf, 16);\r
547         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
548         MD5Update(&md5c, (unsigned char *)key->iv, 8);\r
549         MD5Final(keybuf+16, &md5c);\r
550 \r
551         /*\r
552          * Now decrypt the key blob.\r
553          */\r
554         if (key->encryption == OSSH_ENC_3DES)\r
555             des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv,\r
556                                      key->keyblob, key->keyblob_len);\r
557         else {\r
558             void *ctx;\r
559             assert(key->encryption == OSSH_ENC_AES);\r
560             ctx = aes_make_context();\r
561             aes128_key(ctx, keybuf);\r
562             aes_iv(ctx, (unsigned char *)key->iv);\r
563             aes_ssh2_decrypt_blk(ctx, key->keyblob, key->keyblob_len);\r
564             aes_free_context(ctx);\r
565         }\r
566 \r
567         memset(&md5c, 0, sizeof(md5c));\r
568         memset(keybuf, 0, sizeof(keybuf));\r
569     }\r
570 \r
571     /*\r
572      * Now we have a decrypted key blob, which contains an ASN.1\r
573      * encoded private key. We must now untangle the ASN.1.\r
574      *\r
575      * We expect the whole key blob to be formatted as a SEQUENCE\r
576      * (0x30 followed by a length code indicating that the rest of\r
577      * the blob is part of the sequence). Within that SEQUENCE we\r
578      * expect to see a bunch of INTEGERs. What those integers mean\r
579      * depends on the key type:\r
580      *\r
581      *  - For RSA, we expect the integers to be 0, n, e, d, p, q,\r
582      *    dmp1, dmq1, iqmp in that order. (The last three are d mod\r
583      *    (p-1), d mod (q-1), inverse of q mod p respectively.)\r
584      *\r
585      *  - For DSA, we expect them to be 0, p, q, g, y, x in that\r
586      *    order.\r
587      */\r
588     \r
589     p = key->keyblob;\r
590 \r
591     /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */\r
592     ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);\r
593     p += ret;\r
594     if (ret < 0 || id != 16) {\r
595         errmsg = "ASN.1 decoding failure";\r
596         retval = SSH2_WRONG_PASSPHRASE;\r
597         goto error;\r
598     }\r
599 \r
600     /* Expect a load of INTEGERs. */\r
601     if (key->type == OSSH_RSA)\r
602         num_integers = 9;\r
603     else if (key->type == OSSH_DSA)\r
604         num_integers = 6;\r
605     else\r
606         num_integers = 0;              /* placate compiler warnings */\r
607 \r
608     /*\r
609      * Space to create key blob in.\r
610      */\r
611     blobsize = 256+key->keyblob_len;\r
612     blob = snewn(blobsize, unsigned char);\r
613     PUT_32BIT(blob, 7);\r
614     if (key->type == OSSH_DSA)\r
615         memcpy(blob+4, "ssh-dss", 7);\r
616     else if (key->type == OSSH_RSA)\r
617         memcpy(blob+4, "ssh-rsa", 7);\r
618     blobptr = 4+7;\r
619     privptr = -1;\r
620 \r
621     for (i = 0; i < num_integers; i++) {\r
622         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,\r
623                               &id, &len, &flags);\r
624         p += ret;\r
625         if (ret < 0 || id != 2 ||\r
626             key->keyblob+key->keyblob_len-p < len) {\r
627             errmsg = "ASN.1 decoding failure";\r
628             retval = SSH2_WRONG_PASSPHRASE;\r
629             goto error;\r
630         }\r
631 \r
632         if (i == 0) {\r
633             /*\r
634              * The first integer should be zero always (I think\r
635              * this is some sort of version indication).\r
636              */\r
637             if (len != 1 || p[0] != 0) {\r
638                 errmsg = "version number mismatch";\r
639                 goto error;\r
640             }\r
641         } else if (key->type == OSSH_RSA) {\r
642             /*\r
643              * Integers 1 and 2 go into the public blob but in the\r
644              * opposite order; integers 3, 4, 5 and 8 go into the\r
645              * private blob. The other two (6 and 7) are ignored.\r
646              */\r
647             if (i == 1) {\r
648                 /* Save the details for after we deal with number 2. */\r
649                 modptr = (char *)p;\r
650                 modlen = len;\r
651             } else if (i != 6 && i != 7) {\r
652                 PUT_32BIT(blob+blobptr, len);\r
653                 memcpy(blob+blobptr+4, p, len);\r
654                 blobptr += 4+len;\r
655                 if (i == 2) {\r
656                     PUT_32BIT(blob+blobptr, modlen);\r
657                     memcpy(blob+blobptr+4, modptr, modlen);\r
658                     blobptr += 4+modlen;\r
659                     privptr = blobptr;\r
660                 }\r
661             }\r
662         } else if (key->type == OSSH_DSA) {\r
663             /*\r
664              * Integers 1-4 go into the public blob; integer 5 goes\r
665              * into the private blob.\r
666              */\r
667             PUT_32BIT(blob+blobptr, len);\r
668             memcpy(blob+blobptr+4, p, len);\r
669             blobptr += 4+len;\r
670             if (i == 4)\r
671                 privptr = blobptr;\r
672         }\r
673 \r
674         /* Skip past the number. */\r
675         p += len;\r
676     }\r
677 \r
678     /*\r
679      * Now put together the actual key. Simplest way to do this is\r
680      * to assemble our own key blobs and feed them to the createkey\r
681      * functions; this is a bit faffy but it does mean we get all\r
682      * the sanity checks for free.\r
683      */\r
684     assert(privptr > 0);               /* should have bombed by now if not */\r
685     retkey = snew(struct ssh2_userkey);\r
686     retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);\r
687     retkey->data = retkey->alg->createkey(blob, privptr,\r
688                                           blob+privptr, blobptr-privptr);\r
689     if (!retkey->data) {\r
690         sfree(retkey);\r
691         errmsg = "unable to create key data structure";\r
692         goto error;\r
693     }\r
694 \r
695     retkey->comment = dupstr("imported-openssh-key");\r
696     errmsg = NULL;                     /* no error */\r
697     retval = retkey;\r
698 \r
699     error:\r
700     if (blob) {\r
701         memset(blob, 0, blobsize);\r
702         sfree(blob);\r
703     }\r
704     memset(key->keyblob, 0, key->keyblob_size);\r
705     sfree(key->keyblob);\r
706     memset(key, 0, sizeof(*key));\r
707     sfree(key);\r
708     if (errmsg_p) *errmsg_p = errmsg;\r
709     return retval;\r
710 }\r
711 \r
712 int openssh_write(const Filename *filename, struct ssh2_userkey *key,\r
713                   char *passphrase)\r
714 {\r
715     unsigned char *pubblob, *privblob, *spareblob;\r
716     int publen, privlen, sparelen = 0;\r
717     unsigned char *outblob;\r
718     int outlen;\r
719     struct mpint_pos numbers[9];\r
720     int nnumbers, pos, len, seqlen, i;\r
721     char *header, *footer;\r
722     char zero[1];\r
723     unsigned char iv[8];\r
724     int ret = 0;\r
725     FILE *fp;\r
726 \r
727     /*\r
728      * Fetch the key blobs.\r
729      */\r
730     pubblob = key->alg->public_blob(key->data, &publen);\r
731     privblob = key->alg->private_blob(key->data, &privlen);\r
732     spareblob = outblob = NULL;\r
733 \r
734     /*\r
735      * Find the sequence of integers to be encoded into the OpenSSH\r
736      * key blob, and also decide on the header line.\r
737      */\r
738     if (key->alg == &ssh_rsa) {\r
739         int pos;\r
740         struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;\r
741         Bignum bd, bp, bq, bdmp1, bdmq1;\r
742 \r
743         pos = 4 + GET_32BIT(pubblob);\r
744         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);\r
745         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);\r
746         pos = 0;\r
747         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);\r
748         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);\r
749         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);\r
750         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);\r
751 \r
752         assert(e.start && iqmp.start); /* can't go wrong */\r
753 \r
754         /* We also need d mod (p-1) and d mod (q-1). */\r
755         bd = bignum_from_bytes(d.start, d.bytes);\r
756         bp = bignum_from_bytes(p.start, p.bytes);\r
757         bq = bignum_from_bytes(q.start, q.bytes);\r
758         decbn(bp);\r
759         decbn(bq);\r
760         bdmp1 = bigmod(bd, bp);\r
761         bdmq1 = bigmod(bd, bq);\r
762         freebn(bd);\r
763         freebn(bp);\r
764         freebn(bq);\r
765 \r
766         dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;\r
767         dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;\r
768         sparelen = dmp1.bytes + dmq1.bytes;\r
769         spareblob = snewn(sparelen, unsigned char);\r
770         dmp1.start = spareblob;\r
771         dmq1.start = spareblob + dmp1.bytes;\r
772         for (i = 0; i < dmp1.bytes; i++)\r
773             spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);\r
774         for (i = 0; i < dmq1.bytes; i++)\r
775             spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);\r
776         freebn(bdmp1);\r
777         freebn(bdmq1);\r
778 \r
779         numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';\r
780         numbers[1] = n;\r
781         numbers[2] = e;\r
782         numbers[3] = d;\r
783         numbers[4] = p;\r
784         numbers[5] = q;\r
785         numbers[6] = dmp1;\r
786         numbers[7] = dmq1;\r
787         numbers[8] = iqmp;\r
788 \r
789         nnumbers = 9;\r
790         header = "-----BEGIN RSA PRIVATE KEY-----\n";\r
791         footer = "-----END RSA PRIVATE KEY-----\n";\r
792     } else if (key->alg == &ssh_dss) {\r
793         int pos;\r
794         struct mpint_pos p, q, g, y, x;\r
795 \r
796         pos = 4 + GET_32BIT(pubblob);\r
797         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);\r
798         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);\r
799         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);\r
800         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);\r
801         pos = 0;\r
802         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);\r
803 \r
804         assert(y.start && x.start); /* can't go wrong */\r
805 \r
806         numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; \r
807         numbers[1] = p;\r
808         numbers[2] = q;\r
809         numbers[3] = g;\r
810         numbers[4] = y;\r
811         numbers[5] = x;\r
812 \r
813         nnumbers = 6;\r
814         header = "-----BEGIN DSA PRIVATE KEY-----\n";\r
815         footer = "-----END DSA PRIVATE KEY-----\n";\r
816     } else {\r
817         assert(0);                     /* zoinks! */\r
818         exit(1); /* XXX: GCC doesn't understand assert() on some systems. */\r
819     }\r
820 \r
821     /*\r
822      * Now count up the total size of the ASN.1 encoded integers,\r
823      * so as to determine the length of the containing SEQUENCE.\r
824      */\r
825     len = 0;\r
826     for (i = 0; i < nnumbers; i++) {\r
827         len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);\r
828         len += numbers[i].bytes;\r
829     }\r
830     seqlen = len;\r
831     /* Now add on the SEQUENCE header. */\r
832     len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);\r
833     /* Round up to the cipher block size, ensuring we have at least one\r
834      * byte of padding (see below). */\r
835     outlen = len;\r
836     if (passphrase)\r
837         outlen = (outlen+8) &~ 7;\r
838 \r
839     /*\r
840      * Now we know how big outblob needs to be. Allocate it.\r
841      */\r
842     outblob = snewn(outlen, unsigned char);\r
843 \r
844     /*\r
845      * And write the data into it.\r
846      */\r
847     pos = 0;\r
848     pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);\r
849     for (i = 0; i < nnumbers; i++) {\r
850         pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);\r
851         memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);\r
852         pos += numbers[i].bytes;\r
853     }\r
854 \r
855     /*\r
856      * Padding on OpenSSH keys is deterministic. The number of\r
857      * padding bytes is always more than zero, and always at most\r
858      * the cipher block length. The value of each padding byte is\r
859      * equal to the number of padding bytes. So a plaintext that's\r
860      * an exact multiple of the block size will be padded with 08\r
861      * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a\r
862      * plaintext one byte less than a multiple of the block size\r
863      * will be padded with just 01.\r
864      * \r
865      * This enables the OpenSSL key decryption function to strip\r
866      * off the padding algorithmically and return the unpadded\r
867      * plaintext to the next layer: it looks at the final byte, and\r
868      * then expects to find that many bytes at the end of the data\r
869      * with the same value. Those are all removed and the rest is\r
870      * returned.\r
871      */\r
872     assert(pos == len);\r
873     while (pos < outlen) {\r
874         outblob[pos++] = outlen - len;\r
875     }\r
876 \r
877     /*\r
878      * Encrypt the key.\r
879      *\r
880      * For the moment, we still encrypt our OpenSSH keys using\r
881      * old-style 3DES.\r
882      */\r
883     if (passphrase) {\r
884         /*\r
885          * Invent an iv. Then derive encryption key from passphrase\r
886          * and iv/salt:\r
887          * \r
888          *  - let block A equal MD5(passphrase || iv)\r
889          *  - let block B equal MD5(A || passphrase || iv)\r
890          *  - block C would be MD5(B || passphrase || iv) and so on\r
891          *  - encryption key is the first N bytes of A || B\r
892          */\r
893         struct MD5Context md5c;\r
894         unsigned char keybuf[32];\r
895 \r
896         for (i = 0; i < 8; i++) iv[i] = random_byte();\r
897 \r
898         MD5Init(&md5c);\r
899         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
900         MD5Update(&md5c, iv, 8);\r
901         MD5Final(keybuf, &md5c);\r
902 \r
903         MD5Init(&md5c);\r
904         MD5Update(&md5c, keybuf, 16);\r
905         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
906         MD5Update(&md5c, iv, 8);\r
907         MD5Final(keybuf+16, &md5c);\r
908 \r
909         /*\r
910          * Now encrypt the key blob.\r
911          */\r
912         des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen);\r
913 \r
914         memset(&md5c, 0, sizeof(md5c));\r
915         memset(keybuf, 0, sizeof(keybuf));\r
916     }\r
917 \r
918     /*\r
919      * And save it. We'll use Unix line endings just in case it's\r
920      * subsequently transferred in binary mode.\r
921      */\r
922     fp = f_open(*filename, "wb", TRUE);      /* ensure Unix line endings */\r
923     if (!fp)\r
924         goto error;\r
925     fputs(header, fp);\r
926     if (passphrase) {\r
927         fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,");\r
928         for (i = 0; i < 8; i++)\r
929             fprintf(fp, "%02X", iv[i]);\r
930         fprintf(fp, "\n\n");\r
931     }\r
932     base64_encode(fp, outblob, outlen, 64);\r
933     fputs(footer, fp);\r
934     fclose(fp);\r
935     ret = 1;\r
936 \r
937     error:\r
938     if (outblob) {\r
939         memset(outblob, 0, outlen);\r
940         sfree(outblob);\r
941     }\r
942     if (spareblob) {\r
943         memset(spareblob, 0, sparelen);\r
944         sfree(spareblob);\r
945     }\r
946     if (privblob) {\r
947         memset(privblob, 0, privlen);\r
948         sfree(privblob);\r
949     }\r
950     if (pubblob) {\r
951         memset(pubblob, 0, publen);\r
952         sfree(pubblob);\r
953     }\r
954     return ret;\r
955 }\r
956 \r
957 /* ----------------------------------------------------------------------\r
958  * Code to read ssh.com private keys.\r
959  */\r
960 \r
961 /*\r
962  * The format of the base64 blob is largely SSH-2-packet-formatted,\r
963  * except that mpints are a bit different: they're more like the\r
964  * old SSH-1 mpint. You have a 32-bit bit count N, followed by\r
965  * (N+7)/8 bytes of data.\r
966  * \r
967  * So. The blob contains:\r
968  * \r
969  *  - uint32 0x3f6ff9eb       (magic number)\r
970  *  - uint32 size             (total blob size)\r
971  *  - string key-type         (see below)\r
972  *  - string cipher-type      (tells you if key is encrypted)\r
973  *  - string encrypted-blob\r
974  * \r
975  * (The first size field includes the size field itself and the\r
976  * magic number before it. All other size fields are ordinary SSH-2\r
977  * strings, so the size field indicates how much data is to\r
978  * _follow_.)\r
979  * \r
980  * The encrypted blob, once decrypted, contains a single string\r
981  * which in turn contains the payload. (This allows padding to be\r
982  * added after that string while still making it clear where the\r
983  * real payload ends. Also it probably makes for a reasonable\r
984  * decryption check.)\r
985  * \r
986  * The payload blob, for an RSA key, contains:\r
987  *  - mpint e\r
988  *  - mpint d\r
989  *  - mpint n  (yes, the public and private stuff is intermixed)\r
990  *  - mpint u  (presumably inverse of p mod q)\r
991  *  - mpint p  (p is the smaller prime)\r
992  *  - mpint q  (q is the larger)\r
993  * \r
994  * For a DSA key, the payload blob contains:\r
995  *  - uint32 0\r
996  *  - mpint p\r
997  *  - mpint g\r
998  *  - mpint q\r
999  *  - mpint y\r
1000  *  - mpint x\r
1001  * \r
1002  * Alternatively, if the parameters are `predefined', that\r
1003  * (0,p,g,q) sequence can be replaced by a uint32 1 and a string\r
1004  * containing some predefined parameter specification. *shudder*,\r
1005  * but I doubt we'll encounter this in real life.\r
1006  * \r
1007  * The key type strings are ghastly. The RSA key I looked at had a\r
1008  * type string of\r
1009  * \r
1010  *   `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}'\r
1011  * \r
1012  * and the DSA key wasn't much better:\r
1013  * \r
1014  *   `dl-modp{sign{dsa-nist-sha1},dh{plain}}'\r
1015  * \r
1016  * It isn't clear that these will always be the same. I think it\r
1017  * might be wise just to look at the `if-modn{sign{rsa' and\r
1018  * `dl-modp{sign{dsa' prefixes.\r
1019  * \r
1020  * Finally, the encryption. The cipher-type string appears to be\r
1021  * either `none' or `3des-cbc'. Looks as if this is SSH-2-style\r
1022  * 3des-cbc (i.e. outer cbc rather than inner). The key is created\r
1023  * from the passphrase by means of yet another hashing faff:\r
1024  * \r
1025  *  - first 16 bytes are MD5(passphrase)\r
1026  *  - next 16 bytes are MD5(passphrase || first 16 bytes)\r
1027  *  - if there were more, they'd be MD5(passphrase || first 32),\r
1028  *    and so on.\r
1029  */\r
1030 \r
1031 #define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb\r
1032 \r
1033 struct sshcom_key {\r
1034     char comment[256];                 /* allowing any length is overkill */\r
1035     unsigned char *keyblob;\r
1036     int keyblob_len, keyblob_size;\r
1037 };\r
1038 \r
1039 static struct sshcom_key *load_sshcom_key(const Filename *filename,\r
1040                                           const char **errmsg_p)\r
1041 {\r
1042     struct sshcom_key *ret;\r
1043     FILE *fp;\r
1044     char *line = NULL;\r
1045     int hdrstart, len;\r
1046     char *errmsg, *p;\r
1047     int headers_done;\r
1048     char base64_bit[4];\r
1049     int base64_chars = 0;\r
1050 \r
1051     ret = snew(struct sshcom_key);\r
1052     ret->comment[0] = '\0';\r
1053     ret->keyblob = NULL;\r
1054     ret->keyblob_len = ret->keyblob_size = 0;\r
1055 \r
1056     fp = f_open(*filename, "r", FALSE);\r
1057     if (!fp) {\r
1058         errmsg = "unable to open key file";\r
1059         goto error;\r
1060     }\r
1061     if (!(line = fgetline(fp))) {\r
1062         errmsg = "unexpected end of file";\r
1063         goto error;\r
1064     }\r
1065     strip_crlf(line);\r
1066     if (0 != strcmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) {\r
1067         errmsg = "file does not begin with ssh.com key header";\r
1068         goto error;\r
1069     }\r
1070     memset(line, 0, strlen(line));\r
1071     sfree(line);\r
1072     line = NULL;\r
1073 \r
1074     headers_done = 0;\r
1075     while (1) {\r
1076         if (!(line = fgetline(fp))) {\r
1077             errmsg = "unexpected end of file";\r
1078             goto error;\r
1079         }\r
1080         strip_crlf(line);\r
1081         if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----"))\r
1082             break;                     /* done */\r
1083         if ((p = strchr(line, ':')) != NULL) {\r
1084             if (headers_done) {\r
1085                 errmsg = "header found in body of key data";\r
1086                 goto error;\r
1087             }\r
1088             *p++ = '\0';\r
1089             while (*p && isspace((unsigned char)*p)) p++;\r
1090             hdrstart = p - line;\r
1091 \r
1092             /*\r
1093              * Header lines can end in a trailing backslash for\r
1094              * continuation.\r
1095              */\r
1096             len = hdrstart + strlen(line+hdrstart);\r
1097             assert(!line[len]);\r
1098             while (line[len-1] == '\\') {\r
1099                 char *line2;\r
1100                 int line2len;\r
1101 \r
1102                 line2 = fgetline(fp);\r
1103                 if (!line2) {\r
1104                     errmsg = "unexpected end of file";\r
1105                     goto error;\r
1106                 }\r
1107                 strip_crlf(line2);\r
1108 \r
1109                 line2len = strlen(line2);\r
1110                 line = sresize(line, len + line2len + 1, char);\r
1111                 strcpy(line + len - 1, line2);\r
1112                 len += line2len - 1;\r
1113                 assert(!line[len]);\r
1114 \r
1115                 memset(line2, 0, strlen(line2));\r
1116                 sfree(line2);\r
1117                 line2 = NULL;\r
1118             }\r
1119             p = line + hdrstart;\r
1120             strip_crlf(p);\r
1121             if (!strcmp(line, "Comment")) {\r
1122                 /* Strip quotes in comment if present. */\r
1123                 if (p[0] == '"' && p[strlen(p)-1] == '"') {\r
1124                     p++;\r
1125                     p[strlen(p)-1] = '\0';\r
1126                 }\r
1127                 strncpy(ret->comment, p, sizeof(ret->comment));\r
1128                 ret->comment[sizeof(ret->comment)-1] = '\0';\r
1129             }\r
1130         } else {\r
1131             headers_done = 1;\r
1132 \r
1133             p = line;\r
1134             while (isbase64(*p)) {\r
1135                 base64_bit[base64_chars++] = *p;\r
1136                 if (base64_chars == 4) {\r
1137                     unsigned char out[3];\r
1138 \r
1139                     base64_chars = 0;\r
1140 \r
1141                     len = base64_decode_atom(base64_bit, out);\r
1142 \r
1143                     if (len <= 0) {\r
1144                         errmsg = "invalid base64 encoding";\r
1145                         goto error;\r
1146                     }\r
1147 \r
1148                     if (ret->keyblob_len + len > ret->keyblob_size) {\r
1149                         ret->keyblob_size = ret->keyblob_len + len + 256;\r
1150                         ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,\r
1151                                                unsigned char);\r
1152                     }\r
1153 \r
1154                     memcpy(ret->keyblob + ret->keyblob_len, out, len);\r
1155                     ret->keyblob_len += len;\r
1156                 }\r
1157 \r
1158                 p++;\r
1159             }\r
1160         }\r
1161         memset(line, 0, strlen(line));\r
1162         sfree(line);\r
1163         line = NULL;\r
1164     }\r
1165 \r
1166     if (ret->keyblob_len == 0 || !ret->keyblob) {\r
1167         errmsg = "key body not present";\r
1168         goto error;\r
1169     }\r
1170 \r
1171     if (errmsg_p) *errmsg_p = NULL;\r
1172     return ret;\r
1173 \r
1174     error:\r
1175     if (line) {\r
1176         memset(line, 0, strlen(line));\r
1177         sfree(line);\r
1178         line = NULL;\r
1179     }\r
1180     if (ret) {\r
1181         if (ret->keyblob) {\r
1182             memset(ret->keyblob, 0, ret->keyblob_size);\r
1183             sfree(ret->keyblob);\r
1184         }\r
1185         memset(ret, 0, sizeof(*ret));\r
1186         sfree(ret);\r
1187     }\r
1188     if (errmsg_p) *errmsg_p = errmsg;\r
1189     return NULL;\r
1190 }\r
1191 \r
1192 int sshcom_encrypted(const Filename *filename, char **comment)\r
1193 {\r
1194     struct sshcom_key *key = load_sshcom_key(filename, NULL);\r
1195     int pos, len, answer;\r
1196 \r
1197     *comment = NULL;\r
1198     if (!key)\r
1199         return 0;\r
1200 \r
1201     /*\r
1202      * Check magic number.\r
1203      */\r
1204     if (GET_32BIT(key->keyblob) != 0x3f6ff9eb)\r
1205         return 0;                      /* key is invalid */\r
1206 \r
1207     /*\r
1208      * Find the cipher-type string.\r
1209      */\r
1210     answer = 0;\r
1211     pos = 8;\r
1212     if (key->keyblob_len < pos+4)\r
1213         goto done;                     /* key is far too short */\r
1214     pos += 4 + GET_32BIT(key->keyblob + pos);   /* skip key type */\r
1215     if (key->keyblob_len < pos+4)\r
1216         goto done;                     /* key is far too short */\r
1217     len = GET_32BIT(key->keyblob + pos);   /* find cipher-type length */\r
1218     if (key->keyblob_len < pos+4+len)\r
1219         goto done;                     /* cipher type string is incomplete */\r
1220     if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))\r
1221         answer = 1;\r
1222 \r
1223     done:\r
1224     *comment = dupstr(key->comment);\r
1225     memset(key->keyblob, 0, key->keyblob_size);\r
1226     sfree(key->keyblob);\r
1227     memset(key, 0, sizeof(*key));\r
1228     sfree(key);\r
1229     return answer;\r
1230 }\r
1231 \r
1232 static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)\r
1233 {\r
1234     int bits;\r
1235     int bytes;\r
1236     unsigned char *d = (unsigned char *) data;\r
1237 \r
1238     if (len < 4)\r
1239         goto error;\r
1240     bits = GET_32BIT(d);\r
1241 \r
1242     bytes = (bits + 7) / 8;\r
1243     if (len < 4+bytes)\r
1244         goto error;\r
1245 \r
1246     ret->start = d + 4;\r
1247     ret->bytes = bytes;\r
1248     return bytes+4;\r
1249 \r
1250     error:\r
1251     ret->start = NULL;\r
1252     ret->bytes = -1;\r
1253     return len;                        /* ensure further calls fail as well */\r
1254 }\r
1255 \r
1256 static int sshcom_put_mpint(void *target, void *data, int len)\r
1257 {\r
1258     unsigned char *d = (unsigned char *)target;\r
1259     unsigned char *i = (unsigned char *)data;\r
1260     int bits = len * 8 - 1;\r
1261 \r
1262     while (bits > 0) {\r
1263         if (*i & (1 << (bits & 7)))\r
1264             break;\r
1265         if (!(bits-- & 7))\r
1266             i++, len--;\r
1267     }\r
1268 \r
1269     PUT_32BIT(d, bits+1);\r
1270     memcpy(d+4, i, len);\r
1271     return len+4;\r
1272 }\r
1273 \r
1274 struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,\r
1275                                  const char **errmsg_p)\r
1276 {\r
1277     struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);\r
1278     char *errmsg;\r
1279     int pos, len;\r
1280     const char prefix_rsa[] = "if-modn{sign{rsa";\r
1281     const char prefix_dsa[] = "dl-modp{sign{dsa";\r
1282     enum { RSA, DSA } type;\r
1283     int encrypted;\r
1284     char *ciphertext;\r
1285     int cipherlen;\r
1286     struct ssh2_userkey *ret = NULL, *retkey;\r
1287     const struct ssh_signkey *alg;\r
1288     unsigned char *blob = NULL;\r
1289     int blobsize = 0, publen, privlen;\r
1290 \r
1291     if (!key)\r
1292         return NULL;\r
1293 \r
1294     /*\r
1295      * Check magic number.\r
1296      */\r
1297     if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) {\r
1298         errmsg = "key does not begin with magic number";\r
1299         goto error;\r
1300     }\r
1301 \r
1302     /*\r
1303      * Determine the key type.\r
1304      */\r
1305     pos = 8;\r
1306     if (key->keyblob_len < pos+4 ||\r
1307         (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {\r
1308         errmsg = "key blob does not contain a key type string";\r
1309         goto error;\r
1310     }\r
1311     if (len > sizeof(prefix_rsa) - 1 &&\r
1312         !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) {\r
1313         type = RSA;\r
1314     } else if (len > sizeof(prefix_dsa) - 1 &&\r
1315         !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) {\r
1316         type = DSA;\r
1317     } else {\r
1318         errmsg = "key is of unknown type";\r
1319         goto error;\r
1320     }\r
1321     pos += 4+len;\r
1322 \r
1323     /*\r
1324      * Determine the cipher type.\r
1325      */\r
1326     if (key->keyblob_len < pos+4 ||\r
1327         (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {\r
1328         errmsg = "key blob does not contain a cipher type string";\r
1329         goto error;\r
1330     }\r
1331     if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4))\r
1332         encrypted = 0;\r
1333     else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8))\r
1334         encrypted = 1;\r
1335     else {\r
1336         errmsg = "key encryption is of unknown type";\r
1337         goto error;\r
1338     }\r
1339     pos += 4+len;\r
1340 \r
1341     /*\r
1342      * Get hold of the encrypted part of the key.\r
1343      */\r
1344     if (key->keyblob_len < pos+4 ||\r
1345         (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {\r
1346         errmsg = "key blob does not contain actual key data";\r
1347         goto error;\r
1348     }\r
1349     ciphertext = (char *)key->keyblob + pos + 4;\r
1350     cipherlen = len;\r
1351     if (cipherlen == 0) {\r
1352         errmsg = "length of key data is zero";\r
1353         goto error;\r
1354     }\r
1355 \r
1356     /*\r
1357      * Decrypt it if necessary.\r
1358      */\r
1359     if (encrypted) {\r
1360         /*\r
1361          * Derive encryption key from passphrase and iv/salt:\r
1362          * \r
1363          *  - let block A equal MD5(passphrase)\r
1364          *  - let block B equal MD5(passphrase || A)\r
1365          *  - block C would be MD5(passphrase || A || B) and so on\r
1366          *  - encryption key is the first N bytes of A || B\r
1367          */\r
1368         struct MD5Context md5c;\r
1369         unsigned char keybuf[32], iv[8];\r
1370 \r
1371         if (cipherlen % 8 != 0) {\r
1372             errmsg = "encrypted part of key is not a multiple of cipher block"\r
1373                 " size";\r
1374             goto error;\r
1375         }\r
1376 \r
1377         MD5Init(&md5c);\r
1378         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
1379         MD5Final(keybuf, &md5c);\r
1380 \r
1381         MD5Init(&md5c);\r
1382         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
1383         MD5Update(&md5c, keybuf, 16);\r
1384         MD5Final(keybuf+16, &md5c);\r
1385 \r
1386         /*\r
1387          * Now decrypt the key blob.\r
1388          */\r
1389         memset(iv, 0, sizeof(iv));\r
1390         des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,\r
1391                                  cipherlen);\r
1392 \r
1393         memset(&md5c, 0, sizeof(md5c));\r
1394         memset(keybuf, 0, sizeof(keybuf));\r
1395 \r
1396         /*\r
1397          * Hereafter we return WRONG_PASSPHRASE for any parsing\r
1398          * error. (But only if we've just tried to decrypt it!\r
1399          * Returning WRONG_PASSPHRASE for an unencrypted key is\r
1400          * automatic doom.)\r
1401          */\r
1402         if (encrypted)\r
1403             ret = SSH2_WRONG_PASSPHRASE;\r
1404     }\r
1405 \r
1406     /*\r
1407      * Strip away the containing string to get to the real meat.\r
1408      */\r
1409     len = GET_32BIT(ciphertext);\r
1410     if (len < 0 || len > cipherlen-4) {\r
1411         errmsg = "containing string was ill-formed";\r
1412         goto error;\r
1413     }\r
1414     ciphertext += 4;\r
1415     cipherlen = len;\r
1416 \r
1417     /*\r
1418      * Now we break down into RSA versus DSA. In either case we'll\r
1419      * construct public and private blobs in our own format, and\r
1420      * end up feeding them to alg->createkey().\r
1421      */\r
1422     blobsize = cipherlen + 256;\r
1423     blob = snewn(blobsize, unsigned char);\r
1424     privlen = 0;\r
1425     if (type == RSA) {\r
1426         struct mpint_pos n, e, d, u, p, q;\r
1427         int pos = 0;\r
1428         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e);\r
1429         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d);\r
1430         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n);\r
1431         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u);\r
1432         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);\r
1433         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);\r
1434         if (!q.start) {\r
1435             errmsg = "key data did not contain six integers";\r
1436             goto error;\r
1437         }\r
1438 \r
1439         alg = &ssh_rsa;\r
1440         pos = 0;\r
1441         pos += put_string(blob+pos, "ssh-rsa", 7);\r
1442         pos += put_mp(blob+pos, e.start, e.bytes);\r
1443         pos += put_mp(blob+pos, n.start, n.bytes);\r
1444         publen = pos;\r
1445         pos += put_string(blob+pos, d.start, d.bytes);\r
1446         pos += put_mp(blob+pos, q.start, q.bytes);\r
1447         pos += put_mp(blob+pos, p.start, p.bytes);\r
1448         pos += put_mp(blob+pos, u.start, u.bytes);\r
1449         privlen = pos - publen;\r
1450     } else if (type == DSA) {\r
1451         struct mpint_pos p, q, g, x, y;\r
1452         int pos = 4;\r
1453         if (GET_32BIT(ciphertext) != 0) {\r
1454             errmsg = "predefined DSA parameters not supported";\r
1455             goto error;\r
1456         }\r
1457         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);\r
1458         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g);\r
1459         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);\r
1460         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y);\r
1461         pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x);\r
1462         if (!x.start) {\r
1463             errmsg = "key data did not contain five integers";\r
1464             goto error;\r
1465         }\r
1466 \r
1467         alg = &ssh_dss;\r
1468         pos = 0;\r
1469         pos += put_string(blob+pos, "ssh-dss", 7);\r
1470         pos += put_mp(blob+pos, p.start, p.bytes);\r
1471         pos += put_mp(blob+pos, q.start, q.bytes);\r
1472         pos += put_mp(blob+pos, g.start, g.bytes);\r
1473         pos += put_mp(blob+pos, y.start, y.bytes);\r
1474         publen = pos;\r
1475         pos += put_mp(blob+pos, x.start, x.bytes);\r
1476         privlen = pos - publen;\r
1477     } else\r
1478         return NULL;\r
1479 \r
1480     assert(privlen > 0);               /* should have bombed by now if not */\r
1481 \r
1482     retkey = snew(struct ssh2_userkey);\r
1483     retkey->alg = alg;\r
1484     retkey->data = alg->createkey(blob, publen, blob+publen, privlen);\r
1485     if (!retkey->data) {\r
1486         sfree(retkey);\r
1487         errmsg = "unable to create key data structure";\r
1488         goto error;\r
1489     }\r
1490     retkey->comment = dupstr(key->comment);\r
1491 \r
1492     errmsg = NULL; /* no error */\r
1493     ret = retkey;\r
1494 \r
1495     error:\r
1496     if (blob) {\r
1497         memset(blob, 0, blobsize);\r
1498         sfree(blob);\r
1499     }\r
1500     memset(key->keyblob, 0, key->keyblob_size);\r
1501     sfree(key->keyblob);\r
1502     memset(key, 0, sizeof(*key));\r
1503     sfree(key);\r
1504     if (errmsg_p) *errmsg_p = errmsg;\r
1505     return ret;\r
1506 }\r
1507 \r
1508 int sshcom_write(const Filename *filename, struct ssh2_userkey *key,\r
1509                  char *passphrase)\r
1510 {\r
1511     unsigned char *pubblob, *privblob;\r
1512     int publen, privlen;\r
1513     unsigned char *outblob;\r
1514     int outlen;\r
1515     struct mpint_pos numbers[6];\r
1516     int nnumbers, initial_zero, pos, lenpos, i;\r
1517     char *type;\r
1518     char *ciphertext;\r
1519     int cipherlen;\r
1520     int ret = 0;\r
1521     FILE *fp;\r
1522 \r
1523     /*\r
1524      * Fetch the key blobs.\r
1525      */\r
1526     pubblob = key->alg->public_blob(key->data, &publen);\r
1527     privblob = key->alg->private_blob(key->data, &privlen);\r
1528     outblob = NULL;\r
1529 \r
1530     /*\r
1531      * Find the sequence of integers to be encoded into the OpenSSH\r
1532      * key blob, and also decide on the header line.\r
1533      */\r
1534     if (key->alg == &ssh_rsa) {\r
1535         int pos;\r
1536         struct mpint_pos n, e, d, p, q, iqmp;\r
1537 \r
1538         pos = 4 + GET_32BIT(pubblob);\r
1539         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);\r
1540         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);\r
1541         pos = 0;\r
1542         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);\r
1543         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);\r
1544         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);\r
1545         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);\r
1546 \r
1547         assert(e.start && iqmp.start); /* can't go wrong */\r
1548 \r
1549         numbers[0] = e;\r
1550         numbers[1] = d;\r
1551         numbers[2] = n;\r
1552         numbers[3] = iqmp;\r
1553         numbers[4] = q;\r
1554         numbers[5] = p;\r
1555 \r
1556         nnumbers = 6;\r
1557         initial_zero = 0;\r
1558         type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";\r
1559     } else if (key->alg == &ssh_dss) {\r
1560         int pos;\r
1561         struct mpint_pos p, q, g, y, x;\r
1562 \r
1563         pos = 4 + GET_32BIT(pubblob);\r
1564         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);\r
1565         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);\r
1566         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);\r
1567         pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);\r
1568         pos = 0;\r
1569         pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);\r
1570 \r
1571         assert(y.start && x.start); /* can't go wrong */\r
1572 \r
1573         numbers[0] = p;\r
1574         numbers[1] = g;\r
1575         numbers[2] = q;\r
1576         numbers[3] = y;\r
1577         numbers[4] = x;\r
1578 \r
1579         nnumbers = 5;\r
1580         initial_zero = 1;\r
1581         type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";\r
1582     } else {\r
1583         assert(0);                     /* zoinks! */\r
1584         exit(1); /* XXX: GCC doesn't understand assert() on some systems. */\r
1585     }\r
1586 \r
1587     /*\r
1588      * Total size of key blob will be somewhere under 512 plus\r
1589      * combined length of integers. We'll calculate the more\r
1590      * precise size as we construct the blob.\r
1591      */\r
1592     outlen = 512;\r
1593     for (i = 0; i < nnumbers; i++)\r
1594         outlen += 4 + numbers[i].bytes;\r
1595     outblob = snewn(outlen, unsigned char);\r
1596 \r
1597     /*\r
1598      * Create the unencrypted key blob.\r
1599      */\r
1600     pos = 0;\r
1601     PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4;\r
1602     pos += 4;                          /* length field, fill in later */\r
1603     pos += put_string(outblob+pos, type, strlen(type));\r
1604     {\r
1605         char *ciphertype = passphrase ? "3des-cbc" : "none";\r
1606         pos += put_string(outblob+pos, ciphertype, strlen(ciphertype));\r
1607     }\r
1608     lenpos = pos;                      /* remember this position */\r
1609     pos += 4;                          /* encrypted-blob size */\r
1610     pos += 4;                          /* encrypted-payload size */\r
1611     if (initial_zero) {\r
1612         PUT_32BIT(outblob+pos, 0);\r
1613         pos += 4;\r
1614     }\r
1615     for (i = 0; i < nnumbers; i++)\r
1616         pos += sshcom_put_mpint(outblob+pos,\r
1617                                 numbers[i].start, numbers[i].bytes);\r
1618     /* Now wrap up the encrypted payload. */\r
1619     PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8));\r
1620     /* Pad encrypted blob to a multiple of cipher block size. */\r
1621     if (passphrase) {\r
1622         int padding = -(pos - (lenpos+4)) & 7;\r
1623         while (padding--)\r
1624             outblob[pos++] = random_byte();\r
1625     }\r
1626     ciphertext = (char *)outblob+lenpos+4;\r
1627     cipherlen = pos - (lenpos+4);\r
1628     assert(!passphrase || cipherlen % 8 == 0);\r
1629     /* Wrap up the encrypted blob string. */\r
1630     PUT_32BIT(outblob+lenpos, cipherlen);\r
1631     /* And finally fill in the total length field. */\r
1632     PUT_32BIT(outblob+4, pos);\r
1633 \r
1634     assert(pos < outlen);\r
1635 \r
1636     /*\r
1637      * Encrypt the key.\r
1638      */\r
1639     if (passphrase) {\r
1640         /*\r
1641          * Derive encryption key from passphrase and iv/salt:\r
1642          * \r
1643          *  - let block A equal MD5(passphrase)\r
1644          *  - let block B equal MD5(passphrase || A)\r
1645          *  - block C would be MD5(passphrase || A || B) and so on\r
1646          *  - encryption key is the first N bytes of A || B\r
1647          */\r
1648         struct MD5Context md5c;\r
1649         unsigned char keybuf[32], iv[8];\r
1650 \r
1651         MD5Init(&md5c);\r
1652         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
1653         MD5Final(keybuf, &md5c);\r
1654 \r
1655         MD5Init(&md5c);\r
1656         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
1657         MD5Update(&md5c, keybuf, 16);\r
1658         MD5Final(keybuf+16, &md5c);\r
1659 \r
1660         /*\r
1661          * Now decrypt the key blob.\r
1662          */\r
1663         memset(iv, 0, sizeof(iv));\r
1664         des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,\r
1665                                  cipherlen);\r
1666 \r
1667         memset(&md5c, 0, sizeof(md5c));\r
1668         memset(keybuf, 0, sizeof(keybuf));\r
1669     }\r
1670 \r
1671     /*\r
1672      * And save it. We'll use Unix line endings just in case it's\r
1673      * subsequently transferred in binary mode.\r
1674      */\r
1675     fp = f_open(*filename, "wb", TRUE);      /* ensure Unix line endings */\r
1676     if (!fp)\r
1677         goto error;\r
1678     fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);\r
1679     fprintf(fp, "Comment: \"");\r
1680     /*\r
1681      * Comment header is broken with backslash-newline if it goes\r
1682      * over 70 chars. Although it's surrounded by quotes, it\r
1683      * _doesn't_ escape backslashes or quotes within the string.\r
1684      * Don't ask me, I didn't design it.\r
1685      */\r
1686     {\r
1687         int slen = 60;                 /* starts at 60 due to "Comment: " */\r
1688         char *c = key->comment;\r
1689         while ((int)strlen(c) > slen) {\r
1690             fprintf(fp, "%.*s\\\n", slen, c);\r
1691             c += slen;\r
1692             slen = 70;                 /* allow 70 chars on subsequent lines */\r
1693         }\r
1694         fprintf(fp, "%s\"\n", c);\r
1695     }\r
1696     base64_encode(fp, outblob, pos, 70);\r
1697     fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);\r
1698     fclose(fp);\r
1699     ret = 1;\r
1700 \r
1701     error:\r
1702     if (outblob) {\r
1703         memset(outblob, 0, outlen);\r
1704         sfree(outblob);\r
1705     }\r
1706     if (privblob) {\r
1707         memset(privblob, 0, privlen);\r
1708         sfree(privblob);\r
1709     }\r
1710     if (pubblob) {\r
1711         memset(pubblob, 0, publen);\r
1712         sfree(pubblob);\r
1713     }\r
1714     return ret;\r
1715 }\r