Added a framework for importing foreign key formats, and implemented
[u/mdw/putty] / import.c
CommitLineData
9dda6459 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
26int openssh_encrypted(char *filename);
27struct ssh2_userkey *openssh_read(char *filename, char *passphrase);
28
29/*
30 * Given a key type, determine whether we know how to import it.
31 */
32int 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 */
44int 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 */
55int 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 */
67int import_ssh1(char *filename, int type, struct RSAKey *key, char *passphrase)
68{
69 return 0;
70}
71
72/*
73 * Import an SSH2 key.
74 */
75struct 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
92extern int base64_decode_atom(char *atom, unsigned char *out);
93extern int base64_lines(int datalen);
94extern void base64_encode_atom(unsigned char *data, int n, char *out);
95extern 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
116int 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
164enum { OSSH_DSA, OSSH_RSA };
165struct openssh_key {
166 int type;
167 int encrypted;
168 char iv[32];
169 unsigned char *keyblob;
170 int keyblob_len, keyblob_size;
171};
172
173struct 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
304int 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
317struct 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}