]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sshpubk.c
a4e4a087ca6caa91467757372041090f1c385d7a
[PuTTY.git] / sshpubk.c
1 /*
2  * Generic SSH public-key handling operations. In particular,
3  * reading of SSH public-key files, and also the generic `sign'
4  * operation for SSH-2 (which checks the type of the key and
5  * dispatches to the appropriate key-type specific function).
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <assert.h>
11
12 #include "putty.h"
13 #include "ssh.h"
14 #include "misc.h"
15
16 #define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n"
17
18 #define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\
19                           (x)-'a'<26 ? (x)-'a'+26 :\
20                           (x)-'0'<10 ? (x)-'0'+52 :\
21                           (x)=='+' ? 62 : \
22                           (x)=='/' ? 63 : 0 )
23
24 static int key_type_fp(FILE *fp);
25
26 static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
27                            char **commentptr, const char *passphrase,
28                            const char **error)
29 {
30     unsigned char buf[16384];
31     unsigned char keybuf[16];
32     int len;
33     int i, j, ciphertype;
34     int ret = 0;
35     struct MD5Context md5c;
36     char *comment;
37
38     *error = NULL;
39
40     /* Slurp the whole file (minus the header) into a buffer. */
41     len = fread(buf, 1, sizeof(buf), fp);
42     fclose(fp);
43     if (len < 0 || len == sizeof(buf)) {
44         *error = "error reading file";
45         goto end;                      /* file too big or not read */
46     }
47
48     i = 0;
49     *error = "file format error";
50
51     /*
52      * A zero byte. (The signature includes a terminating NUL.)
53      */
54     if (len - i < 1 || buf[i] != 0)
55         goto end;
56     i++;
57
58     /* One byte giving encryption type, and one reserved uint32. */
59     if (len - i < 1)
60         goto end;
61     ciphertype = buf[i];
62     if (ciphertype != 0 && ciphertype != SSH_CIPHER_3DES)
63         goto end;
64     i++;
65     if (len - i < 4)
66         goto end;                      /* reserved field not present */
67     if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0
68         || buf[i + 3] != 0) goto end;  /* reserved field nonzero, panic! */
69     i += 4;
70
71     /* Now the serious stuff. An ordinary SSH-1 public key. */
72     j = makekey(buf + i, len - i, key, NULL, 1);
73     if (j < 0)
74         goto end;                      /* overran */
75     i += j;
76
77     /* Next, the comment field. */
78     j = toint(GET_32BIT(buf + i));
79     i += 4;
80     if (j < 0 || len - i < j)
81         goto end;
82     comment = snewn(j + 1, char);
83     if (comment) {
84         memcpy(comment, buf + i, j);
85         comment[j] = '\0';
86     }
87     i += j;
88     if (commentptr)
89         *commentptr = dupstr(comment);
90     if (key)
91         key->comment = comment;
92     else
93         sfree(comment);
94
95     if (pub_only) {
96         ret = 1;
97         goto end;
98     }
99
100     if (!key) {
101         ret = ciphertype != 0;
102         *error = NULL;
103         goto end;
104     }
105
106     /*
107      * Decrypt remainder of buffer.
108      */
109     if (ciphertype) {
110         MD5Init(&md5c);
111         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
112         MD5Final(keybuf, &md5c);
113         des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7);
114         smemclr(keybuf, sizeof(keybuf));        /* burn the evidence */
115     }
116
117     /*
118      * We are now in the secret part of the key. The first four
119      * bytes should be of the form a, b, a, b.
120      */
121     if (len - i < 4)
122         goto end;
123     if (buf[i] != buf[i + 2] || buf[i + 1] != buf[i + 3]) {
124         *error = "wrong passphrase";
125         ret = -1;
126         goto end;
127     }
128     i += 4;
129
130     /*
131      * After that, we have one further bignum which is our
132      * decryption exponent, and then the three auxiliary values
133      * (iqmp, q, p).
134      */
135     j = makeprivate(buf + i, len - i, key);
136     if (j < 0) goto end;
137     i += j;
138     j = ssh1_read_bignum(buf + i, len - i, &key->iqmp);
139     if (j < 0) goto end;
140     i += j;
141     j = ssh1_read_bignum(buf + i, len - i, &key->q);
142     if (j < 0) goto end;
143     i += j;
144     j = ssh1_read_bignum(buf + i, len - i, &key->p);
145     if (j < 0) goto end;
146     i += j;
147
148     if (!rsa_verify(key)) {
149         *error = "rsa_verify failed";
150         freersakey(key);
151         ret = 0;
152     } else
153         ret = 1;
154
155   end:
156     smemclr(buf, sizeof(buf));       /* burn the evidence */
157     return ret;
158 }
159
160 int loadrsakey(const Filename *filename, struct RSAKey *key,
161                const char *passphrase, const char **errorstr)
162 {
163     FILE *fp;
164     char buf[64];
165     int ret = 0;
166     const char *error = NULL;
167
168     fp = f_open(filename, "rb", FALSE);
169     if (!fp) {
170         error = "can't open file";
171         goto end;
172     }
173
174     /*
175      * Read the first line of the file and see if it's a v1 private
176      * key file.
177      */
178     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
179         /*
180          * This routine will take care of calling fclose() for us.
181          */
182         ret = loadrsakey_main(fp, key, FALSE, NULL, passphrase, &error);
183         fp = NULL;
184         goto end;
185     }
186
187     /*
188      * Otherwise, we have nothing. Return empty-handed.
189      */
190     error = "not an SSH-1 RSA file";
191
192   end:
193     if (fp)
194         fclose(fp);
195     if ((ret != 1) && errorstr)
196         *errorstr = error;
197     return ret;
198 }
199
200 /*
201  * See whether an RSA key is encrypted. Return its comment field as
202  * well.
203  */
204 int rsakey_encrypted(const Filename *filename, char **comment)
205 {
206     FILE *fp;
207     char buf[64];
208
209     fp = f_open(filename, "rb", FALSE);
210     if (!fp)
211         return 0;                      /* doesn't even exist */
212
213     /*
214      * Read the first line of the file and see if it's a v1 private
215      * key file.
216      */
217     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
218         const char *dummy;
219         /*
220          * This routine will take care of calling fclose() for us.
221          */
222         return loadrsakey_main(fp, NULL, FALSE, comment, NULL, &dummy);
223     }
224     fclose(fp);
225     return 0;                          /* wasn't the right kind of file */
226 }
227
228 /*
229  * Return a malloc'ed chunk of memory containing the public blob of
230  * an RSA key, as given in the agent protocol (modulus bits,
231  * exponent, modulus).
232  */
233 int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
234                    char **commentptr, const char **errorstr)
235 {
236     FILE *fp;
237     char buf[64];
238     struct RSAKey key;
239     int ret;
240     const char *error = NULL;
241
242     /* Default return if we fail. */
243     *blob = NULL;
244     *bloblen = 0;
245     ret = 0;
246
247     fp = f_open(filename, "rb", FALSE);
248     if (!fp) {
249         error = "can't open file";
250         goto end;
251     }
252
253     /*
254      * Read the first line of the file and see if it's a v1 private
255      * key file.
256      */
257     if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {
258         memset(&key, 0, sizeof(key));
259         if (loadrsakey_main(fp, &key, TRUE, commentptr, NULL, &error)) {
260             *blob = rsa_public_blob(&key, bloblen);
261             freersakey(&key);
262             ret = 1;
263         }
264         fp = NULL; /* loadrsakey_main unconditionally closes fp */
265     } else {
266         /*
267          * Try interpreting the file as an SSH-1 public key.
268          */
269         char *line, *p, *bitsp, *expp, *modp, *commentp;
270
271         rewind(fp);
272         line = chomp(fgetline(fp));
273         p = line;
274
275         bitsp = p;
276         p += strspn(p, "0123456789");
277         if (*p != ' ')
278             goto not_public_either;
279         *p++ = '\0';
280
281         expp = p;
282         p += strspn(p, "0123456789");
283         if (*p != ' ')
284             goto not_public_either;
285         *p++ = '\0';
286
287         modp = p;
288         p += strspn(p, "0123456789");
289         if (*p) {
290             if (*p != ' ')
291                 goto not_public_either;
292             *p++ = '\0';
293             commentp = p;
294         } else {
295             commentp = NULL;
296         }
297
298         memset(&key, 0, sizeof(key));
299         key.exponent = bignum_from_decimal(expp);
300         key.modulus = bignum_from_decimal(modp);
301         if (atoi(bitsp) != bignum_bitcount(key.modulus)) {
302             freebn(key.exponent);
303             freebn(key.modulus);
304             sfree(line);
305             error = "key bit count does not match in SSH-1 public key file";
306             goto end;
307         }
308         if (commentptr)
309             *commentptr = commentp ? dupstr(commentp) : NULL;
310         *blob = rsa_public_blob(&key, bloblen);
311         freersakey(&key);
312         fclose(fp);
313         return 1;
314
315       not_public_either:
316         sfree(line);
317         error = "not an SSH-1 RSA file";
318     }
319
320   end:
321     if (fp)
322         fclose(fp);
323     if ((ret != 1) && errorstr)
324         *errorstr = error;
325     return ret;
326 }
327
328 /*
329  * Save an RSA key file. Return nonzero on success.
330  */
331 int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase)
332 {
333     unsigned char buf[16384];
334     unsigned char keybuf[16];
335     struct MD5Context md5c;
336     unsigned char *p, *estart;
337     FILE *fp;
338
339     /*
340      * Write the initial signature.
341      */
342     p = buf;
343     memcpy(p, rsa_signature, sizeof(rsa_signature));
344     p += sizeof(rsa_signature);
345
346     /*
347      * One byte giving encryption type, and one reserved (zero)
348      * uint32.
349      */
350     *p++ = (passphrase ? SSH_CIPHER_3DES : 0);
351     PUT_32BIT(p, 0);
352     p += 4;
353
354     /*
355      * An ordinary SSH-1 public key consists of: a uint32
356      * containing the bit count, then two bignums containing the
357      * modulus and exponent respectively.
358      */
359     PUT_32BIT(p, bignum_bitcount(key->modulus));
360     p += 4;
361     p += ssh1_write_bignum(p, key->modulus);
362     p += ssh1_write_bignum(p, key->exponent);
363
364     /*
365      * A string containing the comment field.
366      */
367     if (key->comment) {
368         PUT_32BIT(p, strlen(key->comment));
369         p += 4;
370         memcpy(p, key->comment, strlen(key->comment));
371         p += strlen(key->comment);
372     } else {
373         PUT_32BIT(p, 0);
374         p += 4;
375     }
376
377     /*
378      * The encrypted portion starts here.
379      */
380     estart = p;
381
382     /*
383      * Two bytes, then the same two bytes repeated.
384      */
385     *p++ = random_byte();
386     *p++ = random_byte();
387     p[0] = p[-2];
388     p[1] = p[-1];
389     p += 2;
390
391     /*
392      * Four more bignums: the decryption exponent, then iqmp, then
393      * q, then p.
394      */
395     p += ssh1_write_bignum(p, key->private_exponent);
396     p += ssh1_write_bignum(p, key->iqmp);
397     p += ssh1_write_bignum(p, key->q);
398     p += ssh1_write_bignum(p, key->p);
399
400     /*
401      * Now write zeros until the encrypted portion is a multiple of
402      * 8 bytes.
403      */
404     while ((p - estart) % 8)
405         *p++ = '\0';
406
407     /*
408      * Now encrypt the encrypted portion.
409      */
410     if (passphrase) {
411         MD5Init(&md5c);
412         MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
413         MD5Final(keybuf, &md5c);
414         des3_encrypt_pubkey(keybuf, estart, p - estart);
415         smemclr(keybuf, sizeof(keybuf));        /* burn the evidence */
416     }
417
418     /*
419      * Done. Write the result to the file.
420      */
421     fp = f_open(filename, "wb", TRUE);
422     if (fp) {
423         int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf));
424         if (fclose(fp))
425             ret = 0;
426         return ret;
427     } else
428         return 0;
429 }
430
431 /* ----------------------------------------------------------------------
432  * SSH-2 private key load/store functions.
433  */
434
435 /*
436  * PuTTY's own format for SSH-2 keys is as follows:
437  *
438  * The file is text. Lines are terminated by CRLF, although CR-only
439  * and LF-only are tolerated on input.
440  *
441  * The first line says "PuTTY-User-Key-File-2: " plus the name of the
442  * algorithm ("ssh-dss", "ssh-rsa" etc).
443  *
444  * The next line says "Encryption: " plus an encryption type.
445  * Currently the only supported encryption types are "aes256-cbc"
446  * and "none".
447  *
448  * The next line says "Comment: " plus the comment string.
449  *
450  * Next there is a line saying "Public-Lines: " plus a number N.
451  * The following N lines contain a base64 encoding of the public
452  * part of the key. This is encoded as the standard SSH-2 public key
453  * blob (with no initial length): so for RSA, for example, it will
454  * read
455  *
456  *    string "ssh-rsa"
457  *    mpint  exponent
458  *    mpint  modulus
459  *
460  * Next, there is a line saying "Private-Lines: " plus a number N,
461  * and then N lines containing the (potentially encrypted) private
462  * part of the key. For the key type "ssh-rsa", this will be
463  * composed of
464  *
465  *    mpint  private_exponent
466  *    mpint  p                  (the larger of the two primes)
467  *    mpint  q                  (the smaller prime)
468  *    mpint  iqmp               (the inverse of q modulo p)
469  *    data   padding            (to reach a multiple of the cipher block size)
470  *
471  * And for "ssh-dss", it will be composed of
472  *
473  *    mpint  x                  (the private key parameter)
474  *  [ string hash   20-byte hash of mpints p || q || g   only in old format ]
475  * 
476  * Finally, there is a line saying "Private-MAC: " plus a hex
477  * representation of a HMAC-SHA-1 of:
478  *
479  *    string  name of algorithm ("ssh-dss", "ssh-rsa")
480  *    string  encryption type
481  *    string  comment
482  *    string  public-blob
483  *    string  private-plaintext (the plaintext version of the
484  *                               private part, including the final
485  *                               padding)
486  * 
487  * The key to the MAC is itself a SHA-1 hash of:
488  * 
489  *    data    "putty-private-key-file-mac-key"
490  *    data    passphrase
491  *
492  * (An empty passphrase is used for unencrypted keys.)
493  *
494  * If the key is encrypted, the encryption key is derived from the
495  * passphrase by means of a succession of SHA-1 hashes. Each hash
496  * is the hash of:
497  *
498  *    uint32  sequence-number
499  *    data    passphrase
500  *
501  * where the sequence-number increases from zero. As many of these
502  * hashes are used as necessary.
503  *
504  * For backwards compatibility with snapshots between 0.51 and
505  * 0.52, we also support the older key file format, which begins
506  * with "PuTTY-User-Key-File-1" (version number differs). In this
507  * format the Private-MAC: field only covers the private-plaintext
508  * field and nothing else (and without the 4-byte string length on
509  * the front too). Moreover, the Private-MAC: field can be replaced
510  * with a Private-Hash: field which is a plain SHA-1 hash instead of
511  * an HMAC (this was generated for unencrypted keys).
512  */
513
514 static int read_header(FILE * fp, char *header)
515 {
516     int len = 39;
517     int c;
518
519     while (1) {
520         c = fgetc(fp);
521         if (c == '\n' || c == '\r' || c == EOF)
522             return 0;                  /* failure */
523         if (c == ':') {
524             c = fgetc(fp);
525             if (c != ' ')
526                 return 0;
527             *header = '\0';
528             return 1;                  /* success! */
529         }
530         if (len == 0)
531             return 0;                  /* failure */
532         *header++ = c;
533         len--;
534     }
535     return 0;                          /* failure */
536 }
537
538 static char *read_body(FILE * fp)
539 {
540     char *text;
541     int len;
542     int size;
543     int c;
544
545     size = 128;
546     text = snewn(size, char);
547     len = 0;
548     text[len] = '\0';
549
550     while (1) {
551         c = fgetc(fp);
552         if (c == '\r' || c == '\n' || c == EOF) {
553             if (c != EOF) {
554                 c = fgetc(fp);
555                 if (c != '\r' && c != '\n')
556                     ungetc(c, fp);
557             }
558             return text;
559         }
560         if (len + 1 >= size) {
561             size += 128;
562             text = sresize(text, size, char);
563         }
564         text[len++] = c;
565         text[len] = '\0';
566     }
567 }
568
569 static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen)
570 {
571     unsigned char *blob;
572     char *line;
573     int linelen, len;
574     int i, j, k;
575
576     /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */
577     blob = snewn(48 * nlines, unsigned char);
578     len = 0;
579     for (i = 0; i < nlines; i++) {
580         line = read_body(fp);
581         if (!line) {
582             sfree(blob);
583             return NULL;
584         }
585         linelen = strlen(line);
586         if (linelen % 4 != 0 || linelen > 64) {
587             sfree(blob);
588             sfree(line);
589             return NULL;
590         }
591         for (j = 0; j < linelen; j += 4) {
592             k = base64_decode_atom(line + j, blob + len);
593             if (!k) {
594                 sfree(line);
595                 sfree(blob);
596                 return NULL;
597             }
598             len += k;
599         }
600         sfree(line);
601     }
602     *bloblen = len;
603     return blob;
604 }
605
606 /*
607  * Magic error return value for when the passphrase is wrong.
608  */
609 struct ssh2_userkey ssh2_wrong_passphrase = {
610     NULL, NULL, NULL
611 };
612
613 const struct ssh_signkey *find_pubkey_alg_len(int namelen, const char *name)
614 {
615     if (match_ssh_id(namelen, name, "ssh-rsa"))
616         return &ssh_rsa;
617     else if (match_ssh_id(namelen, name, "ssh-dss"))
618         return &ssh_dss;
619     else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp256"))
620         return &ssh_ecdsa_nistp256;
621     else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp384"))
622         return &ssh_ecdsa_nistp384;
623     else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp521"))
624         return &ssh_ecdsa_nistp521;
625     else if (match_ssh_id(namelen, name, "ssh-ed25519"))
626         return &ssh_ecdsa_ed25519;
627     else
628         return NULL;
629 }
630
631 const struct ssh_signkey *find_pubkey_alg(const char *name)
632 {
633     return find_pubkey_alg_len(strlen(name), name);
634 }
635
636 struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
637                                        const char *passphrase,
638                                        const char **errorstr)
639 {
640     FILE *fp;
641     char header[40], *b, *encryption, *comment, *mac;
642     const struct ssh_signkey *alg;
643     struct ssh2_userkey *ret;
644     int cipher, cipherblk;
645     unsigned char *public_blob, *private_blob;
646     int public_blob_len, private_blob_len;
647     int i, is_mac, old_fmt;
648     int passlen = passphrase ? strlen(passphrase) : 0;
649     const char *error = NULL;
650
651     ret = NULL;                        /* return NULL for most errors */
652     encryption = comment = mac = NULL;
653     public_blob = private_blob = NULL;
654
655     fp = f_open(filename, "rb", FALSE);
656     if (!fp) {
657         error = "can't open file";
658         goto error;
659     }
660
661     /* Read the first header line which contains the key type. */
662     if (!read_header(fp, header))
663         goto error;
664     if (0 == strcmp(header, "PuTTY-User-Key-File-2")) {
665         old_fmt = 0;
666     } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) {
667         /* this is an old key file; warn and then continue */
668         old_keyfile_warning();
669         old_fmt = 1;
670     } else if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) {
671         /* this is a key file FROM THE FUTURE; refuse it, but with a
672          * more specific error message than the generic one below */
673         error = "PuTTY key format too new";
674         goto error;
675     } else {
676         error = "not a PuTTY SSH-2 private key";
677         goto error;
678     }
679     error = "file format error";
680     if ((b = read_body(fp)) == NULL)
681         goto error;
682     /* Select key algorithm structure. */
683     alg = find_pubkey_alg(b);
684     if (!alg) {
685         sfree(b);
686         goto error;
687     }
688     sfree(b);
689
690     /* Read the Encryption header line. */
691     if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))
692         goto error;
693     if ((encryption = read_body(fp)) == NULL)
694         goto error;
695     if (!strcmp(encryption, "aes256-cbc")) {
696         cipher = 1;
697         cipherblk = 16;
698     } else if (!strcmp(encryption, "none")) {
699         cipher = 0;
700         cipherblk = 1;
701     } else {
702         goto error;
703     }
704
705     /* Read the Comment header line. */
706     if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))
707         goto error;
708     if ((comment = read_body(fp)) == NULL)
709         goto error;
710
711     /* Read the Public-Lines header line and the public blob. */
712     if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))
713         goto error;
714     if ((b = read_body(fp)) == NULL)
715         goto error;
716     i = atoi(b);
717     sfree(b);
718     if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
719         goto error;
720
721     /* Read the Private-Lines header line and the Private blob. */
722     if (!read_header(fp, header) || 0 != strcmp(header, "Private-Lines"))
723         goto error;
724     if ((b = read_body(fp)) == NULL)
725         goto error;
726     i = atoi(b);
727     sfree(b);
728     if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
729         goto error;
730
731     /* Read the Private-MAC or Private-Hash header line. */
732     if (!read_header(fp, header))
733         goto error;
734     if (0 == strcmp(header, "Private-MAC")) {
735         if ((mac = read_body(fp)) == NULL)
736             goto error;
737         is_mac = 1;
738     } else if (0 == strcmp(header, "Private-Hash") && old_fmt) {
739         if ((mac = read_body(fp)) == NULL)
740             goto error;
741         is_mac = 0;
742     } else
743         goto error;
744
745     fclose(fp);
746     fp = NULL;
747
748     /*
749      * Decrypt the private blob.
750      */
751     if (cipher) {
752         unsigned char key[40];
753         SHA_State s;
754
755         if (!passphrase)
756             goto error;
757         if (private_blob_len % cipherblk)
758             goto error;
759
760         SHA_Init(&s);
761         SHA_Bytes(&s, "\0\0\0\0", 4);
762         SHA_Bytes(&s, passphrase, passlen);
763         SHA_Final(&s, key + 0);
764         SHA_Init(&s);
765         SHA_Bytes(&s, "\0\0\0\1", 4);
766         SHA_Bytes(&s, passphrase, passlen);
767         SHA_Final(&s, key + 20);
768         aes256_decrypt_pubkey(key, private_blob, private_blob_len);
769     }
770
771     /*
772      * Verify the MAC.
773      */
774     {
775         char realmac[41];
776         unsigned char binary[20];
777         unsigned char *macdata;
778         int maclen;
779         int free_macdata;
780
781         if (old_fmt) {
782             /* MAC (or hash) only covers the private blob. */
783             macdata = private_blob;
784             maclen = private_blob_len;
785             free_macdata = 0;
786         } else {
787             unsigned char *p;
788             int namelen = strlen(alg->name);
789             int enclen = strlen(encryption);
790             int commlen = strlen(comment);
791             maclen = (4 + namelen +
792                       4 + enclen +
793                       4 + commlen +
794                       4 + public_blob_len +
795                       4 + private_blob_len);
796             macdata = snewn(maclen, unsigned char);
797             p = macdata;
798 #define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)
799             DO_STR(alg->name, namelen);
800             DO_STR(encryption, enclen);
801             DO_STR(comment, commlen);
802             DO_STR(public_blob, public_blob_len);
803             DO_STR(private_blob, private_blob_len);
804
805             free_macdata = 1;
806         }
807
808         if (is_mac) {
809             SHA_State s;
810             unsigned char mackey[20];
811             char header[] = "putty-private-key-file-mac-key";
812
813             SHA_Init(&s);
814             SHA_Bytes(&s, header, sizeof(header)-1);
815             if (cipher && passphrase)
816                 SHA_Bytes(&s, passphrase, passlen);
817             SHA_Final(&s, mackey);
818
819             hmac_sha1_simple(mackey, 20, macdata, maclen, binary);
820
821             smemclr(mackey, sizeof(mackey));
822             smemclr(&s, sizeof(s));
823         } else {
824             SHA_Simple(macdata, maclen, binary);
825         }
826
827         if (free_macdata) {
828             smemclr(macdata, maclen);
829             sfree(macdata);
830         }
831
832         for (i = 0; i < 20; i++)
833             sprintf(realmac + 2 * i, "%02x", binary[i]);
834
835         if (strcmp(mac, realmac)) {
836             /* An incorrect MAC is an unconditional Error if the key is
837              * unencrypted. Otherwise, it means Wrong Passphrase. */
838             if (cipher) {
839                 error = "wrong passphrase";
840                 ret = SSH2_WRONG_PASSPHRASE;
841             } else {
842                 error = "MAC failed";
843                 ret = NULL;
844             }
845             goto error;
846         }
847     }
848     sfree(mac);
849     mac = NULL;
850
851     /*
852      * Create and return the key.
853      */
854     ret = snew(struct ssh2_userkey);
855     ret->alg = alg;
856     ret->comment = comment;
857     ret->data = alg->createkey(alg, public_blob, public_blob_len,
858                                private_blob, private_blob_len);
859     if (!ret->data) {
860         sfree(ret);
861         ret = NULL;
862         error = "createkey failed";
863         goto error;
864     }
865     sfree(public_blob);
866     smemclr(private_blob, private_blob_len);
867     sfree(private_blob);
868     sfree(encryption);
869     if (errorstr)
870         *errorstr = NULL;
871     return ret;
872
873     /*
874      * Error processing.
875      */
876   error:
877     if (fp)
878         fclose(fp);
879     if (comment)
880         sfree(comment);
881     if (encryption)
882         sfree(encryption);
883     if (mac)
884         sfree(mac);
885     if (public_blob)
886         sfree(public_blob);
887     if (private_blob) {
888         smemclr(private_blob, private_blob_len);
889         sfree(private_blob);
890     }
891     if (errorstr)
892         *errorstr = error;
893     return ret;
894 }
895
896 unsigned char *rfc4716_loadpub(FILE *fp, char **algorithm,
897                                int *pub_blob_len, char **commentptr,
898                                const char **errorstr)
899 {
900     const char *error;
901     char *line, *colon, *value;
902     char *comment = NULL;
903     unsigned char *pubblob = NULL;
904     int pubbloblen, pubblobsize;
905     char base64in[4];
906     unsigned char base64out[3];
907     int base64bytes;
908     int alglen;
909
910     line = chomp(fgetline(fp));
911     if (!line || 0 != strcmp(line, "---- BEGIN SSH2 PUBLIC KEY ----")) {
912         error = "invalid begin line in SSH-2 public key file";
913         goto error;
914     }
915     sfree(line); line = NULL;
916
917     while (1) {
918         line = chomp(fgetline(fp));
919         if (!line) {
920             error = "truncated SSH-2 public key file";
921             goto error;
922         }
923         colon = strstr(line, ": ");
924         if (!colon)
925             break;
926         *colon = '\0';
927         value = colon + 2;
928
929         if (!strcmp(line, "Comment")) {
930             char *p, *q;
931
932             /* Remove containing double quotes, if present */
933             p = value;
934             if (*p == '"' && p[strlen(p)-1] == '"') {
935                 p[strlen(p)-1] = '\0';
936                 p++;
937             }
938
939             /* Remove \-escaping, not in RFC4716 but seen in the wild
940              * in practice. */
941             for (q = line; *p; p++) {
942                 if (*p == '\\' && p[1])
943                     p++;
944                 *q++ = *p;
945             }
946
947             *q = '\0';
948             comment = dupstr(line);
949         } else if (!strcmp(line, "Subject") ||
950                    !strncmp(line, "x-", 2)) {
951             /* Headers we recognise and ignore. Do nothing. */
952         } else {
953             error = "unrecognised header in SSH-2 public key file";
954             goto error;
955         }
956
957         sfree(line); line = NULL;
958     }
959
960     /*
961      * Now line contains the initial line of base64 data. Loop round
962      * while it still does contain base64.
963      */
964     pubblobsize = 4096;
965     pubblob = snewn(pubblobsize, unsigned char);
966     pubbloblen = 0;
967     base64bytes = 0;
968     while (line && line[0] != '-') {
969         char *p;
970         for (p = line; *p; p++) {
971             base64in[base64bytes++] = *p;
972             if (base64bytes == 4) {
973                 int n = base64_decode_atom(base64in, base64out);
974                 if (pubbloblen + n > pubblobsize) {
975                     pubblobsize = (pubbloblen + n) * 5 / 4 + 1024;
976                     pubblob = sresize(pubblob, pubblobsize, unsigned char);
977                 }
978                 memcpy(pubblob + pubbloblen, base64out, n);
979                 pubbloblen += n;
980                 base64bytes = 0;
981             }
982         }
983         sfree(line); line = NULL;
984         line = chomp(fgetline(fp));
985     }
986
987     /*
988      * Finally, check the END line makes sense.
989      */
990     if (!line || 0 != strcmp(line, "---- END SSH2 PUBLIC KEY ----")) {
991         error = "invalid end line in SSH-2 public key file";
992         goto error;
993     }
994     sfree(line); line = NULL;
995
996     /*
997      * OK, we now have a public blob and optionally a comment. We must
998      * return the key algorithm string too, so look for that at the
999      * start of the public blob.
1000      */
1001     if (pubbloblen < 4) {
1002         error = "not enough data in SSH-2 public key file";
1003         goto error;
1004     }
1005     alglen = toint(GET_32BIT(pubblob));
1006     if (alglen < 0 || alglen > pubbloblen-4) {
1007         error = "invalid algorithm prefix in SSH-2 public key file";
1008         goto error;
1009     }
1010     if (algorithm)
1011         *algorithm = dupprintf("%.*s", alglen, pubblob+4);
1012     if (pub_blob_len)
1013         *pub_blob_len = pubbloblen;
1014     if (commentptr)
1015         *commentptr = comment;
1016     else
1017         sfree(comment);
1018     return pubblob;
1019
1020   error:
1021     sfree(line);
1022     sfree(comment);
1023     sfree(pubblob);
1024     if (errorstr)
1025         *errorstr = error;
1026     return NULL;
1027 }
1028
1029 unsigned char *openssh_loadpub(FILE *fp, char **algorithm,
1030                                int *pub_blob_len, char **commentptr,
1031                                const char **errorstr)
1032 {
1033     const char *error;
1034     char *line, *base64;
1035     char *comment = NULL;
1036     unsigned char *pubblob = NULL;
1037     int pubbloblen, pubblobsize;
1038     int alglen;
1039
1040     line = chomp(fgetline(fp));
1041
1042     base64 = strchr(line, ' ');
1043     if (!base64) {
1044         error = "no key blob in OpenSSH public key file";
1045         goto error;
1046     }
1047     *base64++ = '\0';
1048
1049     comment = strchr(base64, ' ');
1050     if (comment) {
1051         *comment++ = '\0';
1052         comment = dupstr(comment);
1053     }
1054
1055     pubblobsize = strlen(base64) / 4 * 3;
1056     pubblob = snewn(pubblobsize, unsigned char);
1057     pubbloblen = 0;
1058
1059     while (!memchr(base64, '\0', 4)) {
1060         assert(pubbloblen + 3 <= pubblobsize);
1061         pubbloblen += base64_decode_atom(base64, pubblob + pubbloblen);
1062         base64 += 4;
1063     }
1064     if (*base64) {
1065         error = "invalid length for base64 data in OpenSSH public key file";
1066         goto error;
1067     }
1068
1069     /*
1070      * Sanity check: the first word on the line should be the key
1071      * algorithm, and should match the encoded string at the start of
1072      * the public blob.
1073      */
1074     alglen = strlen(line);
1075     if (pubbloblen < alglen + 4 ||
1076         GET_32BIT(pubblob) != alglen ||
1077         0 != memcmp(pubblob + 4, line, alglen)) {
1078         error = "key algorithms do not match in OpenSSH public key file";
1079         goto error;
1080     }
1081
1082     /*
1083      * Done.
1084      */
1085     if (algorithm)
1086         *algorithm = dupstr(line);
1087     if (pub_blob_len)
1088         *pub_blob_len = pubbloblen;
1089     if (commentptr)
1090         *commentptr = comment;
1091     else
1092         sfree(comment);
1093     return pubblob;
1094
1095   error:
1096     sfree(line);
1097     sfree(comment);
1098     sfree(pubblob);
1099     if (errorstr)
1100         *errorstr = error;
1101     return NULL;
1102 }
1103
1104 unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
1105                                     int *pub_blob_len, char **commentptr,
1106                                     const char **errorstr)
1107 {
1108     FILE *fp;
1109     char header[40], *b;
1110     const struct ssh_signkey *alg;
1111     unsigned char *public_blob;
1112     int public_blob_len;
1113     int type, i;
1114     const char *error = NULL;
1115     char *comment = NULL;
1116
1117     public_blob = NULL;
1118
1119     fp = f_open(filename, "rb", FALSE);
1120     if (!fp) {
1121         error = "can't open file";
1122         goto error;
1123     }
1124
1125     /* Initially, check if this is a public-only key file. Sometimes
1126      * we'll be asked to read a public blob from one of those. */
1127     type = key_type_fp(fp);
1128     if (type == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) {
1129         unsigned char *ret = rfc4716_loadpub(fp, algorithm, pub_blob_len,
1130                                              commentptr, errorstr);
1131         fclose(fp);
1132         return ret;
1133     } else if (type == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
1134         unsigned char *ret = openssh_loadpub(fp, algorithm, pub_blob_len,
1135                                              commentptr, errorstr);
1136         fclose(fp);
1137         return ret;
1138     } else if (type != SSH_KEYTYPE_SSH2) {
1139         error = "not a PuTTY SSH-2 private key";
1140         goto error;
1141     }
1142
1143     /* Read the first header line which contains the key type. */
1144     if (!read_header(fp, header)
1145         || (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
1146             0 != strcmp(header, "PuTTY-User-Key-File-1"))) {
1147         if (0 == strncmp(header, "PuTTY-User-Key-File-", 20))
1148             error = "PuTTY key format too new";
1149         else
1150             error = "not a PuTTY SSH-2 private key";
1151         goto error;
1152     }
1153     error = "file format error";
1154     if ((b = read_body(fp)) == NULL)
1155         goto error;
1156     /* Select key algorithm structure. */
1157     alg = find_pubkey_alg(b);
1158     sfree(b);
1159     if (!alg) {
1160         goto error;
1161     }
1162
1163     /* Read the Encryption header line. */
1164     if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))
1165         goto error;
1166     if ((b = read_body(fp)) == NULL)
1167         goto error;
1168     sfree(b);                          /* we don't care */
1169
1170     /* Read the Comment header line. */
1171     if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))
1172         goto error;
1173     if ((comment = read_body(fp)) == NULL)
1174         goto error;
1175
1176     if (commentptr)
1177         *commentptr = comment;
1178     else
1179         sfree(comment);
1180
1181     /* Read the Public-Lines header line and the public blob. */
1182     if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))
1183         goto error;
1184     if ((b = read_body(fp)) == NULL)
1185         goto error;
1186     i = atoi(b);
1187     sfree(b);
1188     if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
1189         goto error;
1190
1191     fclose(fp);
1192     if (pub_blob_len)
1193         *pub_blob_len = public_blob_len;
1194     if (algorithm)
1195         *algorithm = dupstr(alg->name);
1196     return public_blob;
1197
1198     /*
1199      * Error processing.
1200      */
1201   error:
1202     if (fp)
1203         fclose(fp);
1204     if (public_blob)
1205         sfree(public_blob);
1206     if (errorstr)
1207         *errorstr = error;
1208     if (comment && commentptr) {
1209         sfree(comment);
1210         *commentptr = NULL;
1211     }
1212     return NULL;
1213 }
1214
1215 int ssh2_userkey_encrypted(const Filename *filename, char **commentptr)
1216 {
1217     FILE *fp;
1218     char header[40], *b, *comment;
1219     int ret;
1220
1221     if (commentptr)
1222         *commentptr = NULL;
1223
1224     fp = f_open(filename, "rb", FALSE);
1225     if (!fp)
1226         return 0;
1227     if (!read_header(fp, header)
1228         || (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
1229             0 != strcmp(header, "PuTTY-User-Key-File-1"))) {
1230         fclose(fp);
1231         return 0;
1232     }
1233     if ((b = read_body(fp)) == NULL) {
1234         fclose(fp);
1235         return 0;
1236     }
1237     sfree(b);                          /* we don't care about key type here */
1238     /* Read the Encryption header line. */
1239     if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) {
1240         fclose(fp);
1241         return 0;
1242     }
1243     if ((b = read_body(fp)) == NULL) {
1244         fclose(fp);
1245         return 0;
1246     }
1247
1248     /* Read the Comment header line. */
1249     if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) {
1250         fclose(fp);
1251         sfree(b);
1252         return 1;
1253     }
1254     if ((comment = read_body(fp)) == NULL) {
1255         fclose(fp);
1256         sfree(b);
1257         return 1;
1258     }
1259
1260     if (commentptr)
1261         *commentptr = comment;
1262     else
1263         sfree(comment);
1264
1265     fclose(fp);
1266     if (!strcmp(b, "aes256-cbc"))
1267         ret = 1;
1268     else
1269         ret = 0;
1270     sfree(b);
1271     return ret;
1272 }
1273
1274 int base64_lines(int datalen)
1275 {
1276     /* When encoding, we use 64 chars/line, which equals 48 real chars. */
1277     return (datalen + 47) / 48;
1278 }
1279
1280 void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl)
1281 {
1282     int linelen = 0;
1283     char out[4];
1284     int n, i;
1285
1286     while (datalen > 0) {
1287         n = (datalen < 3 ? datalen : 3);
1288         base64_encode_atom(data, n, out);
1289         data += n;
1290         datalen -= n;
1291         for (i = 0; i < 4; i++) {
1292             if (linelen >= cpl) {
1293                 linelen = 0;
1294                 fputc('\n', fp);
1295             }
1296             fputc(out[i], fp);
1297             linelen++;
1298         }
1299     }
1300     fputc('\n', fp);
1301 }
1302
1303 int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
1304                       char *passphrase)
1305 {
1306     FILE *fp;
1307     unsigned char *pub_blob, *priv_blob, *priv_blob_encrypted;
1308     int pub_blob_len, priv_blob_len, priv_encrypted_len;
1309     int passlen;
1310     int cipherblk;
1311     int i;
1312     const char *cipherstr;
1313     unsigned char priv_mac[20];
1314
1315     /*
1316      * Fetch the key component blobs.
1317      */
1318     pub_blob = key->alg->public_blob(key->data, &pub_blob_len);
1319     priv_blob = key->alg->private_blob(key->data, &priv_blob_len);
1320     if (!pub_blob || !priv_blob) {
1321         sfree(pub_blob);
1322         sfree(priv_blob);
1323         return 0;
1324     }
1325
1326     /*
1327      * Determine encryption details, and encrypt the private blob.
1328      */
1329     if (passphrase) {
1330         cipherstr = "aes256-cbc";
1331         cipherblk = 16;
1332     } else {
1333         cipherstr = "none";
1334         cipherblk = 1;
1335     }
1336     priv_encrypted_len = priv_blob_len + cipherblk - 1;
1337     priv_encrypted_len -= priv_encrypted_len % cipherblk;
1338     priv_blob_encrypted = snewn(priv_encrypted_len, unsigned char);
1339     memset(priv_blob_encrypted, 0, priv_encrypted_len);
1340     memcpy(priv_blob_encrypted, priv_blob, priv_blob_len);
1341     /* Create padding based on the SHA hash of the unpadded blob. This prevents
1342      * too easy a known-plaintext attack on the last block. */
1343     SHA_Simple(priv_blob, priv_blob_len, priv_mac);
1344     assert(priv_encrypted_len - priv_blob_len < 20);
1345     memcpy(priv_blob_encrypted + priv_blob_len, priv_mac,
1346            priv_encrypted_len - priv_blob_len);
1347
1348     /* Now create the MAC. */
1349     {
1350         unsigned char *macdata;
1351         int maclen;
1352         unsigned char *p;
1353         int namelen = strlen(key->alg->name);
1354         int enclen = strlen(cipherstr);
1355         int commlen = strlen(key->comment);
1356         SHA_State s;
1357         unsigned char mackey[20];
1358         char header[] = "putty-private-key-file-mac-key";
1359
1360         maclen = (4 + namelen +
1361                   4 + enclen +
1362                   4 + commlen +
1363                   4 + pub_blob_len +
1364                   4 + priv_encrypted_len);
1365         macdata = snewn(maclen, unsigned char);
1366         p = macdata;
1367 #define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)
1368         DO_STR(key->alg->name, namelen);
1369         DO_STR(cipherstr, enclen);
1370         DO_STR(key->comment, commlen);
1371         DO_STR(pub_blob, pub_blob_len);
1372         DO_STR(priv_blob_encrypted, priv_encrypted_len);
1373
1374         SHA_Init(&s);
1375         SHA_Bytes(&s, header, sizeof(header)-1);
1376         if (passphrase)
1377             SHA_Bytes(&s, passphrase, strlen(passphrase));
1378         SHA_Final(&s, mackey);
1379         hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac);
1380         smemclr(macdata, maclen);
1381         sfree(macdata);
1382         smemclr(mackey, sizeof(mackey));
1383         smemclr(&s, sizeof(s));
1384     }
1385
1386     if (passphrase) {
1387         unsigned char key[40];
1388         SHA_State s;
1389
1390         passlen = strlen(passphrase);
1391
1392         SHA_Init(&s);
1393         SHA_Bytes(&s, "\0\0\0\0", 4);
1394         SHA_Bytes(&s, passphrase, passlen);
1395         SHA_Final(&s, key + 0);
1396         SHA_Init(&s);
1397         SHA_Bytes(&s, "\0\0\0\1", 4);
1398         SHA_Bytes(&s, passphrase, passlen);
1399         SHA_Final(&s, key + 20);
1400         aes256_encrypt_pubkey(key, priv_blob_encrypted,
1401                               priv_encrypted_len);
1402
1403         smemclr(key, sizeof(key));
1404         smemclr(&s, sizeof(s));
1405     }
1406
1407     fp = f_open(filename, "w", TRUE);
1408     if (!fp) {
1409         sfree(pub_blob);
1410         smemclr(priv_blob, priv_blob_len);
1411         sfree(priv_blob);
1412         smemclr(priv_blob_encrypted, priv_blob_len);
1413         sfree(priv_blob_encrypted);
1414         return 0;
1415     }
1416     fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name);
1417     fprintf(fp, "Encryption: %s\n", cipherstr);
1418     fprintf(fp, "Comment: %s\n", key->comment);
1419     fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob_len));
1420     base64_encode(fp, pub_blob, pub_blob_len, 64);
1421     fprintf(fp, "Private-Lines: %d\n", base64_lines(priv_encrypted_len));
1422     base64_encode(fp, priv_blob_encrypted, priv_encrypted_len, 64);
1423     fprintf(fp, "Private-MAC: ");
1424     for (i = 0; i < 20; i++)
1425         fprintf(fp, "%02x", priv_mac[i]);
1426     fprintf(fp, "\n");
1427     fclose(fp);
1428
1429     sfree(pub_blob);
1430     smemclr(priv_blob, priv_blob_len);
1431     sfree(priv_blob);
1432     smemclr(priv_blob_encrypted, priv_blob_len);
1433     sfree(priv_blob_encrypted);
1434     return 1;
1435 }
1436
1437 /* ----------------------------------------------------------------------
1438  * Output public keys.
1439  */
1440 char *ssh1_pubkey_str(struct RSAKey *key)
1441 {
1442     char *buffer;
1443     char *dec1, *dec2;
1444
1445     dec1 = bignum_decimal(key->exponent);
1446     dec2 = bignum_decimal(key->modulus);
1447     buffer = dupprintf("%d %s %s%s%s", bignum_bitcount(key->modulus),
1448                        dec1, dec2,
1449                        key->comment ? " " : "",
1450                        key->comment ? key->comment : "");
1451     sfree(dec1);
1452     sfree(dec2);
1453     return buffer;
1454 }
1455
1456 void ssh1_write_pubkey(FILE *fp, struct RSAKey *key)
1457 {
1458     char *buffer = ssh1_pubkey_str(key);
1459     fprintf(fp, "%s\n", buffer);
1460     sfree(buffer);
1461 }
1462
1463 static char *ssh2_pubkey_openssh_str_internal(const char *comment,
1464                                               const void *v_pub_blob,
1465                                               int pub_len)
1466 {
1467     const unsigned char *ssh2blob = (const unsigned char *)v_pub_blob;
1468     const char *alg;
1469     int alglen;
1470     char *buffer, *p;
1471     int i;
1472
1473     if (pub_len < 4) {
1474         alg = NULL;
1475     } else {
1476         alglen = GET_32BIT(ssh2blob);
1477         if (alglen > 0 && alglen < pub_len - 4) {
1478             alg = (const char *)ssh2blob + 4;
1479         } else {
1480             alg = NULL;
1481         }
1482     }
1483
1484     if (!alg) {
1485         alg = "INVALID-ALGORITHM";
1486         alglen = strlen(alg);
1487     }
1488
1489     buffer = snewn(alglen +
1490                    4 * ((pub_len+2) / 3) +
1491                    (comment ? strlen(comment) : 0) + 3, char);
1492     p = buffer + sprintf(buffer, "%.*s ", alglen, alg);
1493     i = 0;
1494     while (i < pub_len) {
1495         int n = (pub_len - i < 3 ? pub_len - i : 3);
1496         base64_encode_atom(ssh2blob + i, n, p);
1497         i += n;
1498         p += 4;
1499     }
1500     if (*comment) {
1501         *p++ = ' ';
1502         strcpy(p, comment);
1503     } else
1504         *p++ = '\0';
1505
1506     return buffer;
1507 }
1508
1509 char *ssh2_pubkey_openssh_str(struct ssh2_userkey *key)
1510 {
1511     int bloblen;
1512     unsigned char *blob;
1513     char *ret;
1514
1515     blob = key->alg->public_blob(key->data, &bloblen);
1516     ret = ssh2_pubkey_openssh_str_internal(key->comment, blob, bloblen);
1517     sfree(blob);
1518
1519     return ret;
1520 }
1521
1522 void ssh2_write_pubkey(FILE *fp, const char *comment,
1523                        const void *v_pub_blob, int pub_len,
1524                        int keytype)
1525 {
1526     unsigned char *pub_blob = (unsigned char *)v_pub_blob;
1527
1528     if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) {
1529         const char *p;
1530         int i, column;
1531
1532         fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
1533
1534         if (comment) {
1535             fprintf(fp, "Comment: \"");
1536             for (p = comment; *p; p++) {
1537                 if (*p == '\\' || *p == '\"')
1538                     fputc('\\', fp);
1539                 fputc(*p, fp);
1540             }
1541             fprintf(fp, "\"\n");
1542         }
1543
1544         i = 0;
1545         column = 0;
1546         while (i < pub_len) {
1547             char buf[5];
1548             int n = (pub_len - i < 3 ? pub_len - i : 3);
1549             base64_encode_atom(pub_blob + i, n, buf);
1550             i += n;
1551             buf[4] = '\0';
1552             fputs(buf, fp);
1553             if (++column >= 16) {
1554                 fputc('\n', fp);
1555                 column = 0;
1556             }
1557         }
1558         if (column > 0)
1559             fputc('\n', fp);
1560
1561         fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
1562     } else if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
1563         char *buffer = ssh2_pubkey_openssh_str_internal(comment,
1564                                                         v_pub_blob, pub_len);
1565         fprintf(fp, "%s\n", buffer);
1566         sfree(buffer);
1567     } else {
1568         assert(0 && "Bad key type in ssh2_write_pubkey");
1569     }
1570 }
1571
1572 /* ----------------------------------------------------------------------
1573  * Utility functions to compute SSH-2 fingerprints in a uniform way.
1574  */
1575 char *ssh2_fingerprint_blob(const void *blob, int bloblen)
1576 {
1577     unsigned char digest[16];
1578     char fingerprint_str[16*3];
1579     const char *algstr;
1580     int alglen;
1581     const struct ssh_signkey *alg;
1582     int i;
1583
1584     /*
1585      * The fingerprint hash itself is always just the MD5 of the blob.
1586      */
1587     MD5Simple(blob, bloblen, digest);
1588     for (i = 0; i < 16; i++)
1589         sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":");
1590
1591     /*
1592      * Identify the key algorithm, if possible.
1593      */
1594     alglen = toint(GET_32BIT((const unsigned char *)blob));
1595     if (alglen > 0 && alglen < bloblen-4) {
1596         algstr = (const char *)blob + 4;
1597
1598         /*
1599          * If we can actually identify the algorithm as one we know
1600          * about, get hold of the key's bit count too.
1601          */
1602         alg = find_pubkey_alg_len(alglen, algstr);
1603         if (alg) {
1604             int bits = alg->pubkey_bits(alg, blob, bloblen);
1605             return dupprintf("%.*s %d %s", alglen, algstr,
1606                              bits, fingerprint_str);
1607         } else {
1608             return dupprintf("%.*s %s", alglen, algstr, fingerprint_str);
1609         }
1610     } else {
1611         /*
1612          * No algorithm available (which means a seriously confused
1613          * key blob, but there we go). Return only the hash.
1614          */
1615         return dupstr(fingerprint_str);
1616     }
1617 }
1618
1619 char *ssh2_fingerprint(const struct ssh_signkey *alg, void *data)
1620 {
1621     int len;
1622     unsigned char *blob = alg->public_blob(data, &len);
1623     char *ret = ssh2_fingerprint_blob(blob, len);
1624     sfree(blob);
1625     return ret;
1626 }
1627
1628 /* ----------------------------------------------------------------------
1629  * Determine the type of a private key file.
1630  */
1631 static int key_type_fp(FILE *fp)
1632 {
1633     char buf[1024];
1634     const char public_std_sig[] = "---- BEGIN SSH2 PUBLIC KEY";
1635     const char putty2_sig[] = "PuTTY-User-Key-File-";
1636     const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT";
1637     const char openssh_new_sig[] = "-----BEGIN OPENSSH PRIVATE KEY";
1638     const char openssh_sig[] = "-----BEGIN ";
1639     int i;
1640     char *p;
1641
1642     i = fread(buf, 1, sizeof(buf)-1, fp);
1643     rewind(fp);
1644
1645     if (i < 0)
1646         return SSH_KEYTYPE_UNOPENABLE;
1647     if (i < 32)
1648         return SSH_KEYTYPE_UNKNOWN;
1649     assert(i > 0 && i < sizeof(buf));
1650     buf[i] = '\0';
1651     if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1))
1652         return SSH_KEYTYPE_SSH1;
1653     if (!memcmp(buf, public_std_sig, sizeof(public_std_sig)-1))
1654         return SSH_KEYTYPE_SSH2_PUBLIC_RFC4716;
1655     if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1))
1656         return SSH_KEYTYPE_SSH2;
1657     if (!memcmp(buf, openssh_new_sig, sizeof(openssh_new_sig)-1))
1658         return SSH_KEYTYPE_OPENSSH_NEW;
1659     if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1))
1660         return SSH_KEYTYPE_OPENSSH_PEM;
1661     if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1))
1662         return SSH_KEYTYPE_SSHCOM;
1663     if ((p = buf + strspn(buf, "0123456789"), *p == ' ') &&
1664         (p = p+1 + strspn(p+1, "0123456789"), *p == ' ') &&
1665         (p = p+1 + strspn(p+1, "0123456789"), *p == ' ' || *p == '\n' || !*p))
1666         return SSH_KEYTYPE_SSH1_PUBLIC;
1667     if ((p = buf + strcspn(buf, " "), find_pubkey_alg_len(p-buf, buf)) &&
1668         (p = p+1 + strspn(p+1, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij"
1669                           "klmnopqrstuvwxyz+/="),
1670          *p == ' ' || *p == '\n' || !*p))
1671         return SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH;
1672     return SSH_KEYTYPE_UNKNOWN;        /* unrecognised or EOF */
1673 }
1674
1675 int key_type(const Filename *filename)
1676 {
1677     FILE *fp;
1678     int ret;
1679
1680     fp = f_open(filename, "r", FALSE);
1681     if (!fp)
1682         return SSH_KEYTYPE_UNOPENABLE;
1683     ret = key_type_fp(fp);
1684     fclose(fp);
1685     return ret;
1686 }
1687
1688 /*
1689  * Convert the type word to a string, for `wrong type' error
1690  * messages.
1691  */
1692 const char *key_type_to_str(int type)
1693 {
1694     switch (type) {
1695       case SSH_KEYTYPE_UNOPENABLE: return "unable to open file"; break;
1696       case SSH_KEYTYPE_UNKNOWN: return "not a recognised key file format"; break;
1697       case SSH_KEYTYPE_SSH1_PUBLIC: return "SSH-1 public key"; break;
1698       case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: return "SSH-2 public key (RFC 4716 format)"; break;
1699       case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: return "SSH-2 public key (OpenSSH format)"; break;
1700       case SSH_KEYTYPE_SSH1: return "SSH-1 private key"; break;
1701       case SSH_KEYTYPE_SSH2: return "PuTTY SSH-2 private key"; break;
1702       case SSH_KEYTYPE_OPENSSH_PEM: return "OpenSSH SSH-2 private key (old PEM format)"; break;
1703       case SSH_KEYTYPE_OPENSSH_NEW: return "OpenSSH SSH-2 private key (new format)"; break;
1704       case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH-2 private key"; break;
1705         /*
1706          * This function is called with a key type derived from
1707          * looking at an actual key file, so the output-only type
1708          * OPENSSH_AUTO should never get here, and is much an INTERNAL
1709          * ERROR as a code we don't even understand.
1710          */
1711       case SSH_KEYTYPE_OPENSSH_AUTO: return "INTERNAL ERROR (OPENSSH_AUTO)"; break;
1712       default: return "INTERNAL ERROR"; break;
1713     }
1714 }