]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - import.c
Added a framework for importing foreign key formats, and implemented
[PuTTY.git] / import.c
1 /*
2  * Code for PuTTY to import and export private key files in other
3  * SSH clients' formats.
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <ctype.h>
10
11 #include "ssh.h"
12 #include "misc.h"
13
14 #define PUT_32BIT(cp, value) do { \
15   (cp)[3] = (value); \
16   (cp)[2] = (value) >> 8; \
17   (cp)[1] = (value) >> 16; \
18   (cp)[0] = (value) >> 24; } while (0)
19
20 #define GET_32BIT(cp) \
21     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
22     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
23     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
24     ((unsigned long)(unsigned char)(cp)[3]))
25
26 int openssh_encrypted(char *filename);
27 struct ssh2_userkey *openssh_read(char *filename, char *passphrase);
28
29 /*
30  * Given a key type, determine whether we know how to import it.
31  */
32 int import_possible(int type)
33 {
34     if (type == SSH_KEYTYPE_OPENSSH)
35         return 1;
36     return 0;
37 }
38
39 /*
40  * Given a key type, determine what native key type
41  * (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once
42  * we've imported it.
43  */
44 int import_target_type(int type)
45 {
46     /*
47      * There are no known foreign SSH1 key formats.
48      */
49     return SSH_KEYTYPE_SSH2;
50 }
51
52 /*
53  * Determine whether a foreign key is encrypted.
54  */
55 int import_encrypted(char *filename, int type, char **comment)
56 {
57     if (type == SSH_KEYTYPE_OPENSSH) {
58         *comment = filename;           /* OpenSSH doesn't do key comments */
59         return openssh_encrypted(filename);
60     }
61     return 0;
62 }
63
64 /*
65  * Import an SSH1 key.
66  */
67 int import_ssh1(char *filename, int type, struct RSAKey *key, char *passphrase)
68 {
69     return 0;
70 }
71
72 /*
73  * Import an SSH2 key.
74  */
75 struct ssh2_userkey *import_ssh2(char *filename, int type, char *passphrase)
76 {
77     if (type == SSH_KEYTYPE_OPENSSH)
78         return openssh_read(filename, passphrase);
79     return NULL;
80 }
81
82 /* ----------------------------------------------------------------------
83  * Helper routines. (The base64 ones are defined in sshpubk.c.)
84  */
85
86 #define isbase64(c) (    ((c) >= 'A' && (c) <= 'Z') || \
87                          ((c) >= 'a' && (c) <= 'z') || \
88                          ((c) >= '0' && (c) <= '9') || \
89                          (c) == '+' || (c) == '/' || (c) == '=' \
90                          )
91
92 extern int base64_decode_atom(char *atom, unsigned char *out);
93 extern int base64_lines(int datalen);
94 extern void base64_encode_atom(unsigned char *data, int n, char *out);
95 extern void base64_encode(FILE * fp, unsigned char *data, int datalen);
96
97 /*
98  * Read an ASN.1/BER identifier and length pair.
99  * 
100  * Flags are a combination of the #defines listed below.
101  * 
102  * Returns -1 if unsuccessful; otherwise returns the number of
103  * bytes used out of the source data.
104  */
105
106 /* ASN.1 tag classes. */
107 #define ASN1_CLASS_UNIVERSAL        (0 << 6)
108 #define ASN1_CLASS_APPLICATION      (1 << 6)
109 #define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
110 #define ASN1_CLASS_PRIVATE          (3 << 6)
111 #define ASN1_CLASS_MASK             (3 << 6)
112
113 /* Primitive versus constructed bit. */
114 #define ASN1_CONSTRUCTED            (1 << 5)
115
116 int ber_read_id_len(void *source, int sourcelen,
117                     int *id, int *length, int *flags)
118 {
119     unsigned char *p = (unsigned char *) source;
120
121     if (sourcelen == 0)
122         return -1;
123
124     *flags = (*p & 0xE0);
125     if ((*p & 0x1F) == 0x1F) {
126         *id = 0;
127         while (*p & 0x80) {
128             *id = (*id << 7) | (*p & 0x7F);
129             p++, sourcelen--;
130             if (sourcelen == 0)
131                 return -1;
132         }
133         *id = (*id << 7) | (*p & 0x7F);
134         p++, sourcelen--;
135     } else {
136         *id = *p & 0x1F;
137         p++, sourcelen--;
138     }
139
140     if (sourcelen == 0)
141         return -1;
142
143     if (*p & 0x80) {
144         int n = *p & 0x7F;
145         p++, sourcelen--;
146         if (sourcelen < n)
147             return -1;
148         *length = 0;
149         while (n--)
150             *length = (*length << 8) | (*p++);
151         sourcelen -= n;
152     } else {
153         *length = *p;
154         p++, sourcelen--;
155     }
156
157     return p - (unsigned char *) source;
158 }
159
160 /* ----------------------------------------------------------------------
161  * Code to read OpenSSH private keys.
162  */
163
164 enum { OSSH_DSA, OSSH_RSA };
165 struct openssh_key {
166     int type;
167     int encrypted;
168     char iv[32];
169     unsigned char *keyblob;
170     int keyblob_len, keyblob_size;
171 };
172
173 struct openssh_key *load_openssh_key(char *filename)
174 {
175     struct openssh_key *ret;
176     FILE *fp;
177     char buffer[256];
178     char *errmsg, *p;
179     int headers_done;
180
181     ret = smalloc(sizeof(*ret));
182     ret->keyblob = NULL;
183     ret->keyblob_len = ret->keyblob_size = 0;
184     ret->encrypted = 0;
185     memset(ret->iv, 0, sizeof(ret->iv));
186
187     fp = fopen(filename, "r");
188     if (!fp) {
189         errmsg = "Unable to open key file";
190         goto error;
191     }
192     if (!fgets(buffer, sizeof(buffer), fp) ||
193         0 != strncmp(buffer, "-----BEGIN ", 11) ||
194         0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) {
195         errmsg = "File does not begin with OpenSSH key header";
196         goto error;
197     }
198     if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n"))
199         ret->type = OSSH_RSA;
200     else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n"))
201         ret->type = OSSH_DSA;
202     else {
203         errmsg = "Unrecognised key type";
204         goto error;
205     }
206
207     headers_done = 0;
208     while (1) {
209         if (!fgets(buffer, sizeof(buffer), fp)) {
210             errmsg = "Unexpected end of file";
211             goto error;
212         }
213         if (0 == strncmp(buffer, "-----END ", 9) &&
214             0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n"))
215             break;                     /* done */
216         if ((p = strchr(buffer, ':')) != NULL) {
217             if (headers_done) {
218                 errmsg = "Header found in body of key data";
219                 goto error;
220             }
221             *p++ = '\0';
222             while (*p && isspace((unsigned char)*p)) p++;
223             if (!strcmp(buffer, "Proc-Type")) {
224                 if (p[0] != '4' || p[1] != ',') {
225                     errmsg = "Proc-Type is not 4 (only 4 is supported)";
226                     goto error;
227                 }
228                 p += 2;
229                 if (!strcmp(p, "ENCRYPTED\n"))
230                     ret->encrypted = 1;
231             } else if (!strcmp(buffer, "DEK-Info")) {
232                 int i, j;
233
234                 if (strncmp(p, "DES-EDE3-CBC,", 13)) {
235                     errmsg = "Ciphers other than DES-EDE3-CBC not supported";
236                     goto error;
237                 }
238                 p += 13;
239                 for (i = 0; i < 8; i++) {
240                     if (1 != sscanf(p, "%2x", &j))
241                         break;
242                     ret->iv[i] = j;
243                     p += 2;
244                 }
245                 if (i < 8) {
246                     errmsg = "Expected 16-digit iv in DEK-Info";
247                     goto error;
248                 }
249             }
250         } else {
251             headers_done = 1;
252
253             p = buffer;
254             while (isbase64(p[0]) && isbase64(p[1]) &&
255                    isbase64(p[2]) && isbase64(p[3])) {
256                 int len;
257                 unsigned char out[3];
258
259                 len = base64_decode_atom(p, out);
260
261                 if (len <= 0) {
262                     errmsg = "Invalid base64 encoding";
263                     goto error;
264                 }
265
266                 if (ret->keyblob_len + len > ret->keyblob_size) {
267                     ret->keyblob_size = ret->keyblob_len + len + 256;
268                     ret->keyblob = srealloc(ret->keyblob, ret->keyblob_size);
269                 }
270
271                 memcpy(ret->keyblob + ret->keyblob_len, out, len);
272                 ret->keyblob_len += len;
273
274                 p += 4;
275             }
276
277             if (isbase64(*p)) {
278                 errmsg = "base64 characters left at end of line";
279                 goto error;
280             }
281         }
282     }
283
284     if (ret->keyblob_len == 0 || !ret->keyblob) {
285         errmsg = "Key body not present";
286         goto error;
287     }
288
289     if (ret->encrypted && ret->keyblob_len % 8 != 0) {
290         errmsg = "Encrypted key blob is not a multiple of cipher block size";
291         goto error;
292     }
293
294     return ret;
295
296     error:
297     if (ret) {
298         if (ret->keyblob) sfree(ret->keyblob);
299         sfree(ret);
300     }
301     return NULL;
302 }
303
304 int openssh_encrypted(char *filename)
305 {
306     struct openssh_key *key = load_openssh_key(filename);
307     int ret;
308
309     if (!key)
310         return 0;
311     ret = key->encrypted;
312     sfree(key->keyblob);
313     sfree(key);
314     return ret;
315 }
316
317 struct ssh2_userkey *openssh_read(char *filename, char *passphrase)
318 {
319     struct openssh_key *key = load_openssh_key(filename);
320     struct ssh2_userkey *retkey;
321     unsigned char *p;
322     int ret, id, len, flags;
323     int i, num_integers;
324     struct ssh2_userkey *retval = NULL;
325     char *errmsg;
326     unsigned char *blob;
327     int blobptr, privptr;
328     char *modptr;
329     int modlen;
330
331     if (!key)
332         return NULL;
333
334     if (key->encrypted) {
335         /*
336          * Derive encryption key from passphrase and iv/salt:
337          * 
338          *  - let block A equal MD5(passphrase || iv)
339          *  - let block B equal MD5(A || passphrase || iv)
340          *  - block C would be MD5(B || passphrase || iv) and so on
341          *  - encryption key is the first N bytes of A || B
342          */
343         struct MD5Context md5c;
344         unsigned char keybuf[32];
345
346         MD5Init(&md5c);
347         MD5Update(&md5c, passphrase, strlen(passphrase));
348         MD5Update(&md5c, key->iv, 8);
349         MD5Final(keybuf, &md5c);
350
351         MD5Init(&md5c);
352         MD5Update(&md5c, keybuf, 16);
353         MD5Update(&md5c, passphrase, strlen(passphrase));
354         MD5Update(&md5c, key->iv, 8);
355         MD5Final(keybuf+16, &md5c);
356
357         /*
358          * Now decrypt the key blob.
359          */
360         des3_decrypt_pubkey_ossh(keybuf, key->iv,
361                                  key->keyblob, key->keyblob_len);
362     }
363
364     /*
365      * Now we have a decrypted key blob, which contains an ASN.1
366      * encoded private key. We must now untangle the ASN.1.
367      *
368      * We expect the whole key blob to be formatted as a SEQUENCE
369      * (0x30 followed by a length code indicating that the rest of
370      * the blob is part of the sequence). Within that SEQUENCE we
371      * expect to see a bunch of INTEGERs. What those integers mean
372      * depends on the key type:
373      *
374      *  - For RSA, we expect the integers to be 0, n, e, d, p, q,
375      *    dmp1, dmq1, iqmp in that order. (The last three are d mod
376      *    (p-1), d mod (q-1), inverse of q mod p respectively.)
377      *
378      *  - For DSA, we expect them to be 0, p, q, g, y, x in that
379      *    order.
380      */
381     
382     p = key->keyblob;
383
384     /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */
385     ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
386     p += ret;
387     if (ret < 0 || id != 16) {
388         errmsg = "ASN.1 decoding failure";
389         retval = SSH2_WRONG_PASSPHRASE;
390         goto error;
391     }
392
393     /* Expect a load of INTEGERs. */
394     if (key->type == OSSH_RSA)
395         num_integers = 9;
396     else if (key->type == OSSH_DSA)
397         num_integers = 6;
398
399     /*
400      * Space to create key blob in.
401      */
402     blob = smalloc(256+key->keyblob_len);
403     PUT_32BIT(blob, 7);
404     if (key->type == OSSH_DSA)
405         memcpy(blob+4, "ssh-dss", 7);
406     else if (key->type == OSSH_RSA)
407         memcpy(blob+4, "ssh-rsa", 7);
408     blobptr = 4+7;
409     privptr = -1;
410
411     for (i = 0; i < num_integers; i++) {
412         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
413                               &id, &len, &flags);
414         p += ret;
415         if (ret < 0 || id != 2 ||
416             key->keyblob+key->keyblob_len-p < len) {
417             errmsg = "ASN.1 decoding failure";
418             goto error;
419         }
420
421         if (i == 0) {
422             /*
423              * The first integer should be zero always (I think
424              * this is some sort of version indication).
425              */
426             if (len != 1 || p[0] != 0) {
427                 errmsg = "Version number mismatch";
428                 goto error;
429             }
430         } else if (key->type == OSSH_RSA) {
431             /*
432              * Integers 1 and 2 go into the public blob but in the
433              * opposite order; integers 3, 4, 5 and 8 go into the
434              * private blob. The other two (6 and 7) are ignored.
435              */
436             if (i == 1) {
437                 /* Save the details for after we deal with number 2. */
438                 modptr = p;
439                 modlen = len;
440             } else if (i != 6 && i != 7) {
441                 PUT_32BIT(blob+blobptr, len);
442                 memcpy(blob+blobptr+4, p, len);
443                 blobptr += 4+len;
444                 if (i == 2) {
445                     PUT_32BIT(blob+blobptr, modlen);
446                     memcpy(blob+blobptr+4, modptr, modlen);
447                     blobptr += 4+modlen;
448                     privptr = blobptr;
449                 }
450             }
451         } else if (key->type == OSSH_DSA) {
452             /*
453              * Integers 1-4 go into the public blob; integer 5 goes
454              * into the private blob.
455              */
456             PUT_32BIT(blob+blobptr, len);
457             memcpy(blob+blobptr+4, p, len);
458             blobptr += 4+len;
459             if (i == 4)
460                 privptr = blobptr;
461         }
462
463         /* Skip past the number. */
464         p += len;
465     }
466
467     /*
468      * Now put together the actual key. Simplest way to do this is
469      * to assemble our own key blobs and feed them to the createkey
470      * functions; this is a bit faffy but it does mean we get all
471      * the sanity checks for free.
472      */
473     assert(privptr > 0);               /* should have bombed by now if not */
474     retkey = smalloc(sizeof(struct ssh2_userkey));
475     retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
476     retkey->data = retkey->alg->createkey(blob, privptr,
477                                           blob+privptr, blobptr-privptr);
478     if (!retkey->data) {
479         sfree(retkey);
480         errmsg = "unable to create key data structure";
481         goto error;
482     }
483
484     retkey->comment = dupstr("imported-openssh-key");
485     if (blob) sfree(blob);
486     sfree(key->keyblob);
487     sfree(key);
488     return retkey;
489
490     error:
491     if (blob) sfree(blob);
492     sfree(key->keyblob);
493     sfree(key);
494     return retval;
495 }