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