]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Merge shorter option name from 'pre-0.64'.
authorJacob Nevins <jacobn@chiark.greenend.org.uk>
Tue, 4 Nov 2014 18:38:46 +0000 (18:38 +0000)
committerJacob Nevins <jacobn@chiark.greenend.org.uk>
Tue, 4 Nov 2014 18:38:46 +0000 (18:38 +0000)
23 files changed:
LICENCE
Recipe
cmdgen.c
config.c
contrib/logparse.pl
doc/licence.but
import.c
putty.h
settings.c
ssh.c
ssh.h
sshbn.c
sshdss.c
sshecc.c [new file with mode: 0644]
sshecdsag.c [new file with mode: 0644]
sshpubk.c
sshsh512.c
unix/gtkdlg.c
windows/pageant.rc
windows/puttygen.rc
windows/win_res.rc2
windows/winpgen.c
windows/winpgnt.c

diff --git a/LICENCE b/LICENCE
index 20525522c3ac48a76cf62b0c1cde9580ea3e05b3..2d3a374dd5987ce89570cc72fd5d4c32de05aa52 100644 (file)
--- a/LICENCE
+++ b/LICENCE
@@ -3,7 +3,7 @@ PuTTY is copyright 1997-2014 Simon Tatham.
 Portions copyright Robert de Bath, Joris van Rantwijk, Delian
 Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
 Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
-Kuhn, Colin Watson, and CORE SDI S.A.
+Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation files
diff --git a/Recipe b/Recipe
index bac258f18b8fc5bf5f2164d1255ecfa3482e6a63..aa2f19494b6965e28cc6ba48d97b1dbb94f0dc83 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -217,7 +217,7 @@ NONSSH   = telnet raw rlogin ldisc pinger
 SSH      = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
          + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
          + sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf
-         + sshgssc pgssapi sshshare
+         + sshgssc pgssapi sshshare sshecc
 WINSSH   = SSH winnoise winsecur winpgntc wingss winshare winnps winnpc
          + winhsock errsock
 UXSSH    = SSH uxnoise uxagentc uxgss uxshare
@@ -270,12 +270,13 @@ psftp    : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
 
 pageant  : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234
          + misc sshaes sshsha winsecur winpgntc sshdss sshsh256 sshsh512
-        + winutils winmisc winhelp conf pageant.res LIBS
+         + winutils sshecc winmisc winhelp conf pageant.res LIBS
 
 puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc
          + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res
-        + tree234 notiming winhelp winnojmp conf LIBS wintime
+         + tree234 notiming winhelp winnojmp conf LIBS wintime sshecc
+         + sshecdsag
 
 pterm    : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
          + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
@@ -293,7 +294,7 @@ plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
 puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
          + sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234
-        + uxgen notiming conf
+         + uxgen notiming conf sshecc sshecdsag
 
 pscp     : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
 psftp    : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
index c15c01dd34bab2e82bd6529b78387344bbd93980..80e69b5a7eaf331ef68232626a10009c6f5fbabd 100644 (file)
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -265,7 +265,7 @@ int main(int argc, char **argv)
 {
     char *infile = NULL;
     Filename *infilename = NULL, *outfilename = NULL;
-    enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN;    
+    enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA } keytype = NOKEYGEN;
     char *outfile = NULL, *outfiletmp = NULL;
     enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE;
     int bits = 2048;
@@ -437,6 +437,8 @@ int main(int argc, char **argv)
                            keytype = RSA1, sshver = 1;
                        else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
                            keytype = DSA, sshver = 2;
+                        else if (!strcmp(p, "ecdsa"))
+                            keytype = ECDSA, sshver = 2;
                        else {
                            fprintf(stderr,
                                    "puttygen: unknown key type `%s'\n", p);
@@ -497,6 +499,11 @@ int main(int argc, char **argv)
        }
     }
 
+    if (keytype == ECDSA && (bits != 256 && bits != 384 && bits != 521)) {
+        fprintf(stderr, "puttygen: invalid bits for ECDSA, choose 256, 384 or 521\n");
+        errs = TRUE;
+    }
+
     if (errs)
        return 1;
 
@@ -663,6 +670,8 @@ int main(int argc, char **argv)
        tm = ltime();
        if (keytype == DSA)
            strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
+        else if (keytype == ECDSA)
+            strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
        else
            strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
 
@@ -684,6 +693,19 @@ int main(int argc, char **argv)
            ssh2key->data = dsskey;
            ssh2key->alg = &ssh_dss;
            ssh1key = NULL;
+        } else if (keytype == ECDSA) {
+            struct ec_key *ec = snew(struct ec_key);
+            ec_generate(ec, bits, progressfn, &prog);
+            ssh2key = snew(struct ssh2_userkey);
+            ssh2key->data = ec;
+            if (bits == 256) {
+                ssh2key->alg = &ssh_ecdsa_nistp256;
+            } else if (bits == 384) {
+                ssh2key->alg = &ssh_ecdsa_nistp384;
+            } else {
+                ssh2key->alg = &ssh_ecdsa_nistp521;
+            }
+            ssh1key = NULL;
        } else {
            struct RSAKey *rsakey = snew(struct RSAKey);
            rsa_generate(rsakey, bits, progressfn, &prog);
index 7e4cc242bbf01b3ad53f133769bc13924b3d40fb..e126d4dce4124f458ed319297d8de83343a2edd1 100644 (file)
--- a/config.c
+++ b/config.c
@@ -433,6 +433,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
            { "Diffie-Hellman group 14",        KEX_DHGROUP14 },
            { "Diffie-Hellman group exchange",  KEX_DHGEX },
            { "RSA-based key exchange",         KEX_RSA },
+            { "ECDH key exchange",              KEX_ECDH },
            { "-- warn below here --",          KEX_WARN }
        };
 
index 3445baa8a217b4e48706a0b8d1c04685871d8679..4805c0f4bdf994300be67053f8fe036864585204 100755 (executable)
@@ -115,6 +115,16 @@ my %packets = (
         my ($direction, $seq, $data) = @_;
         print "\n";
     },
+#define SSH2_MSG_KEX_ECDH_INIT                    30    /* 0x1e */
+    'SSH2_MSG_KEX_ECDH_INIT' => sub {
+        my ($direction, $seq, $data) = @_;
+        print "\n";
+    },
+#define SSH2_MSG_KEX_ECDH_REPLY                   31    /* 0x1f */
+    'SSH2_MSG_KEX_ECDH_REPLY' => sub {
+        my ($direction, $seq, $data) = @_;
+        print "\n";
+    },
 #define SSH2_MSG_USERAUTH_REQUEST                 50   /* 0x32 */
     'SSH2_MSG_USERAUTH_REQUEST' => sub {
         my ($direction, $seq, $data) = @_;
index bff3d70aa28a207725012a9237855d4aa71b75cf..68cba42029a596d65fc4aad966bcb91626943ae9 100644 (file)
@@ -5,7 +5,7 @@ PuTTY is \i{copyright} 1997-2014 Simon Tatham.
 Portions copyright Robert de Bath, Joris van Rantwijk, Delian
 Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
 Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
-Kuhn, Colin Watson, and CORE SDI S.A.
+Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation files
index bc35a4ab7d08b5aebb64332e7d0b84dad2f50bb2..ff09dee542463b39ef3d9feca47f71bb572fe1e8 100644 (file)
--- a/import.c
+++ b/import.c
@@ -307,7 +307,7 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret)
  * Code to read and write OpenSSH private keys.
  */
 
-enum { OSSH_DSA, OSSH_RSA };
+enum { OSSH_DSA, OSSH_RSA, OSSH_ECDSA };
 enum { OSSH_ENC_3DES, OSSH_ENC_AES };
 struct openssh_key {
     int type;
@@ -354,6 +354,8 @@ static struct openssh_key *load_openssh_key(const Filename *filename,
        ret->type = OSSH_RSA;
     else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----"))
        ret->type = OSSH_DSA;
+    else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----"))
+        ret->type = OSSH_ECDSA;
     else {
        errmsg = "unrecognised key type";
        goto error;
@@ -591,6 +593,10 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
      *
      *  - For DSA, we expect them to be 0, p, q, g, y, x in that
      *    order.
+     *
+     *  - In ECDSA the format is totally different: we see the
+     *    SEQUENCE, but beneath is an INTEGER 1, OCTET STRING priv
+     *    EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint
      */
     
     p = key->keyblob;
@@ -613,91 +619,211 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
     else
        num_integers = 0;              /* placate compiler warnings */
 
-    /*
-     * Space to create key blob in.
-     */
-    blobsize = 256+key->keyblob_len;
-    blob = snewn(blobsize, unsigned char);
-    PUT_32BIT(blob, 7);
-    if (key->type == OSSH_DSA)
-       memcpy(blob+4, "ssh-dss", 7);
-    else if (key->type == OSSH_RSA)
-       memcpy(blob+4, "ssh-rsa", 7);
-    blobptr = 4+7;
-    privptr = -1;
-
-    for (i = 0; i < num_integers; i++) {
+
+    if (key->type == OSSH_ECDSA)
+    {
+        /* And now for something completely different */
+        unsigned char *priv;
+        int privlen;
+        struct ec_curve *curve;
+        /* Read INTEGER 1 */
+        ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+                              &id, &len, &flags);
+        p += ret;
+        if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len ||
+            len != 1 || p[0] != 1) {
+            errmsg = "ASN.1 decoding failure";
+            retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+            goto error;
+        }
+        p += 1;
+        /* Read private key OCTET STRING */
+        ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+                              &id, &len, &flags);
+        p += ret;
+        if (ret < 0 || id != 4 || key->keyblob+key->keyblob_len-p < len) {
+            errmsg = "ASN.1 decoding failure";
+            retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+            goto error;
+        }
+        priv = p;
+        privlen = len;
+        p += len;
+        /* Read curve OID */
+        ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+                              &id, &len, &flags);
+        p += ret;
+        if (ret < 0 || id != 0 || key->keyblob+key->keyblob_len-p < len) {
+            errmsg = "ASN.1 decoding failure";
+            retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+            goto error;
+        }
        ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                              &id, &len, &flags);
        p += ret;
-       if (ret < 0 || id != 2 ||
-           key->keyblob+key->keyblob_len-p < len) {
+        if (ret < 0 || id != 6 || key->keyblob+key->keyblob_len-p < len) {
            errmsg = "ASN.1 decoding failure";
            retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
            goto error;
        }
+        if (len == 8 && !memcmp(p, nistp256_oid, nistp256_oid_len)) {
+            curve = ec_p256();
+        } else if (len == 5 && !memcmp(p, nistp384_oid, nistp384_oid_len)) {
+            curve = ec_p384();
+        } else if (len == 5 && !memcmp(p, nistp521_oid, nistp521_oid_len)) {
+            curve = ec_p521();
+        } else {
+            errmsg = "Unsupported ECDSA curve.";
+            retval = NULL;
+            goto error;
+        }
+        p += len;
+        /* Read BIT STRING point */
+        ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+                              &id, &len, &flags);
+        p += ret;
+        if (ret < 0 || id != 1 || key->keyblob+key->keyblob_len-p < len) {
+            errmsg = "ASN.1 decoding failure";
+            retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+            goto error;
+        }
+        ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+                              &id, &len, &flags);
+        p += ret;
+        if (ret < 0 || id != 3 || key->keyblob+key->keyblob_len-p < len ||
+            len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
+            errmsg = "ASN.1 decoding failure";
+            retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+            goto error;
+        }
+        p += 1; len -= 1; /* Skip 0x00 before point */
+
+        /* Construct the key */
+        retkey = snew(struct ssh2_userkey);
+        if (!retkey) {
+            errmsg = "out of memory";
+            goto error;
+        }
+        if (curve->fieldBits == 256) {
+            retkey->alg = &ssh_ecdsa_nistp256;
+        } else if (curve->fieldBits == 384) {
+            retkey->alg = &ssh_ecdsa_nistp384;
+        } else {
+            retkey->alg = &ssh_ecdsa_nistp521;
+        }
+        blob = snewn((4+19 + 4+8 + 4+len) + (4+privlen), unsigned char);
+        if (!blob) {
+            sfree(retkey);
+            errmsg = "out of memory";
+            goto error;
+        }
+        PUT_32BIT(blob, 19);
+        sprintf((char*)blob+4, "ecdsa-sha2-nistp%d", curve->fieldBits);
+        PUT_32BIT(blob+4+19, 8);
+        sprintf((char*)blob+4+19+4, "nistp%d", curve->fieldBits);
+        PUT_32BIT(blob+4+19+4+8, len);
+        memcpy(blob+4+19+4+8+4, p, len);
+        PUT_32BIT(blob+4+19+4+8+4+len, privlen);
+        memcpy(blob+4+19+4+8+4+len+4, priv, privlen);
+        retkey->data = retkey->alg->createkey(blob, 4+19+4+8+4+len,
+                                              blob+4+19+4+8+4+len, 4+privlen);
+        if (!retkey->data) {
+            sfree(retkey);
+            errmsg = "unable to create key data structure";
+            goto error;
+        }
 
-       if (i == 0) {
-           /*
-            * The first integer should be zero always (I think
-            * this is some sort of version indication).
-            */
-           if (len != 1 || p[0] != 0) {
-               errmsg = "version number mismatch";
+    } else if (key->type == OSSH_RSA || key->type == OSSH_DSA) {
+
+        /*
+         * Space to create key blob in.
+         */
+        blobsize = 256+key->keyblob_len;
+        blob = snewn(blobsize, unsigned char);
+        PUT_32BIT(blob, 7);
+        if (key->type == OSSH_DSA)
+            memcpy(blob+4, "ssh-dss", 7);
+        else if (key->type == OSSH_RSA)
+            memcpy(blob+4, "ssh-rsa", 7);
+        blobptr = 4+7;
+        privptr = -1;
+
+        for (i = 0; i < num_integers; i++) {
+            ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+                                  &id, &len, &flags);
+            p += ret;
+            if (ret < 0 || id != 2 ||
+                key->keyblob+key->keyblob_len-p < len) {
+                errmsg = "ASN.1 decoding failure";
+                retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
                goto error;
            }
-       } else if (key->type == OSSH_RSA) {
-           /*
-            * Integers 1 and 2 go into the public blob but in the
-            * opposite order; integers 3, 4, 5 and 8 go into the
-            * private blob. The other two (6 and 7) are ignored.
-            */
-           if (i == 1) {
-               /* Save the details for after we deal with number 2. */
-               modptr = (char *)p;
-               modlen = len;
-           } else if (i != 6 && i != 7) {
+
+            if (i == 0) {
+                /*
+                 * The first integer should be zero always (I think
+                 * this is some sort of version indication).
+                 */
+                if (len != 1 || p[0] != 0) {
+                    errmsg = "version number mismatch";
+                    goto error;
+                }
+            } else if (key->type == OSSH_RSA) {
+                /*
+                 * Integers 1 and 2 go into the public blob but in the
+                 * opposite order; integers 3, 4, 5 and 8 go into the
+                 * private blob. The other two (6 and 7) are ignored.
+                 */
+                if (i == 1) {
+                    /* Save the details for after we deal with number 2. */
+                    modptr = (char *)p;
+                    modlen = len;
+                } else if (i != 6 && i != 7) {
+                    PUT_32BIT(blob+blobptr, len);
+                    memcpy(blob+blobptr+4, p, len);
+                    blobptr += 4+len;
+                    if (i == 2) {
+                        PUT_32BIT(blob+blobptr, modlen);
+                        memcpy(blob+blobptr+4, modptr, modlen);
+                        blobptr += 4+modlen;
+                        privptr = blobptr;
+                    }
+                }
+            } else if (key->type == OSSH_DSA) {
+                /*
+                 * Integers 1-4 go into the public blob; integer 5 goes
+                 * into the private blob.
+                 */
                PUT_32BIT(blob+blobptr, len);
                memcpy(blob+blobptr+4, p, len);
                blobptr += 4+len;
-               if (i == 2) {
-                   PUT_32BIT(blob+blobptr, modlen);
-                   memcpy(blob+blobptr+4, modptr, modlen);
-                   blobptr += 4+modlen;
+                if (i == 4)
                    privptr = blobptr;
-               }
            }
-       } else if (key->type == OSSH_DSA) {
-           /*
-            * Integers 1-4 go into the public blob; integer 5 goes
-            * into the private blob.
-            */
-           PUT_32BIT(blob+blobptr, len);
-           memcpy(blob+blobptr+4, p, len);
-           blobptr += 4+len;
-           if (i == 4)
-               privptr = blobptr;
+
+            /* Skip past the number. */
+            p += len;
        }
 
-       /* Skip past the number. */
-       p += len;
-    }
+        /*
+         * Now put together the actual key. Simplest way to do this is
+         * to assemble our own key blobs and feed them to the createkey
+         * functions; this is a bit faffy but it does mean we get all
+         * the sanity checks for free.
+         */
+        assert(privptr > 0);          /* should have bombed by now if not */
+        retkey = snew(struct ssh2_userkey);
+        retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
+        retkey->data = retkey->alg->createkey(blob, privptr,
+                                              blob+privptr, blobptr-privptr);
+        if (!retkey->data) {
+            sfree(retkey);
+            errmsg = "unable to create key data structure";
+            goto error;
+        }
 
-    /*
-     * Now put together the actual key. Simplest way to do this is
-     * to assemble our own key blobs and feed them to the createkey
-     * functions; this is a bit faffy but it does mean we get all
-     * the sanity checks for free.
-     */
-    assert(privptr > 0);              /* should have bombed by now if not */
-    retkey = snew(struct ssh2_userkey);
-    retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
-    retkey->data = retkey->alg->createkey(blob, privptr,
-                                         blob+privptr, blobptr-privptr);
-    if (!retkey->data) {
-       sfree(retkey);
-       errmsg = "unable to create key data structure";
-       goto error;
+    } else {
+        assert(0 && "Bad key type from load_openssh_key");
     }
 
     retkey->comment = dupstr("imported-openssh-key");
@@ -739,157 +865,226 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
     privblob = key->alg->private_blob(key->data, &privlen);
     spareblob = outblob = NULL;
 
+    outblob = NULL;
+    len = 0;
+
     /*
-     * Find the sequence of integers to be encoded into the OpenSSH
-     * key blob, and also decide on the header line.
+     * Encode the OpenSSH key blob, and also decide on the header
+     * line.
      */
-    if (key->alg == &ssh_rsa) {
-        int pos;
-        struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
-        Bignum bd, bp, bq, bdmp1, bdmq1;
-
+    if (key->alg == &ssh_rsa || key->alg == &ssh_dss) {
         /*
-         * These blobs were generated from inside PuTTY, so we needn't
-         * treat them as untrusted.
+         * The RSA and DSS handlers share some code because the two
+         * key types have very similar ASN.1 representations, as a
+         * plain SEQUENCE of big integers. So we set up a list of
+         * bignums per key type and then construct the actual blob in
+         * common code after that.
          */
-        pos = 4 + GET_32BIT(pubblob);
-        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
-        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
-        pos = 0;
-        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
-        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
-        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
-        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
+        if (key->alg == &ssh_rsa) {
+            int pos;
+            struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
+            Bignum bd, bp, bq, bdmp1, bdmq1;
 
-        assert(e.start && iqmp.start); /* can't go wrong */
+            /*
+             * These blobs were generated from inside PuTTY, so we needn't
+             * treat them as untrusted.
+             */
+            pos = 4 + GET_32BIT(pubblob);
+            pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
+            pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
+            pos = 0;
+            pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
+            pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
+            pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
+            pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
+
+            assert(e.start && iqmp.start); /* can't go wrong */
+
+            /* We also need d mod (p-1) and d mod (q-1). */
+            bd = bignum_from_bytes(d.start, d.bytes);
+            bp = bignum_from_bytes(p.start, p.bytes);
+            bq = bignum_from_bytes(q.start, q.bytes);
+            decbn(bp);
+            decbn(bq);
+            bdmp1 = bigmod(bd, bp);
+            bdmq1 = bigmod(bd, bq);
+            freebn(bd);
+            freebn(bp);
+            freebn(bq);
+
+            dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;
+            dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;
+            sparelen = dmp1.bytes + dmq1.bytes;
+            spareblob = snewn(sparelen, unsigned char);
+            dmp1.start = spareblob;
+            dmq1.start = spareblob + dmp1.bytes;
+            for (i = 0; i < dmp1.bytes; i++)
+                spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);
+            for (i = 0; i < dmq1.bytes; i++)
+                spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);
+            freebn(bdmp1);
+            freebn(bdmq1);
+
+            numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+            numbers[1] = n;
+            numbers[2] = e;
+            numbers[3] = d;
+            numbers[4] = p;
+            numbers[5] = q;
+            numbers[6] = dmp1;
+            numbers[7] = dmq1;
+            numbers[8] = iqmp;
+
+            nnumbers = 9;
+            header = "-----BEGIN RSA PRIVATE KEY-----\n";
+            footer = "-----END RSA PRIVATE KEY-----\n";
+        } else {                       /* ssh-dss */
+            int pos;
+            struct mpint_pos p, q, g, y, x;
 
-        /* We also need d mod (p-1) and d mod (q-1). */
-        bd = bignum_from_bytes(d.start, d.bytes);
-        bp = bignum_from_bytes(p.start, p.bytes);
-        bq = bignum_from_bytes(q.start, q.bytes);
-        decbn(bp);
-        decbn(bq);
-        bdmp1 = bigmod(bd, bp);
-        bdmq1 = bigmod(bd, bq);
-        freebn(bd);
-        freebn(bp);
-        freebn(bq);
-
-        dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;
-        dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;
-        sparelen = dmp1.bytes + dmq1.bytes;
-        spareblob = snewn(sparelen, unsigned char);
-        dmp1.start = spareblob;
-        dmq1.start = spareblob + dmp1.bytes;
-        for (i = 0; i < dmp1.bytes; i++)
-            spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);
-        for (i = 0; i < dmq1.bytes; i++)
-            spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);
-        freebn(bdmp1);
-        freebn(bdmq1);
-
-        numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
-        numbers[1] = n;
-        numbers[2] = e;
-        numbers[3] = d;
-        numbers[4] = p;
-        numbers[5] = q;
-        numbers[6] = dmp1;
-        numbers[7] = dmq1;
-        numbers[8] = iqmp;
-
-        nnumbers = 9;
-        header = "-----BEGIN RSA PRIVATE KEY-----\n";
-        footer = "-----END RSA PRIVATE KEY-----\n";
-    } else if (key->alg == &ssh_dss) {
-        int pos;
-        struct mpint_pos p, q, g, y, x;
+            /*
+             * These blobs were generated from inside PuTTY, so we needn't
+             * treat them as untrusted.
+             */
+            pos = 4 + GET_32BIT(pubblob);
+            pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
+            pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
+            pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
+            pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
+            pos = 0;
+            pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
+
+            assert(y.start && x.start); /* can't go wrong */
+
+            numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+            numbers[1] = p;
+            numbers[2] = q;
+            numbers[3] = g;
+            numbers[4] = y;
+            numbers[5] = x;
+
+            nnumbers = 6;
+            header = "-----BEGIN DSA PRIVATE KEY-----\n";
+            footer = "-----END DSA PRIVATE KEY-----\n";
+        }
 
         /*
-         * These blobs were generated from inside PuTTY, so we needn't
-         * treat them as untrusted.
+         * Now count up the total size of the ASN.1 encoded integers,
+         * so as to determine the length of the containing SEQUENCE.
+         */
+        len = 0;
+        for (i = 0; i < nnumbers; i++) {
+            len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
+            len += numbers[i].bytes;
+        }
+        seqlen = len;
+        /* Now add on the SEQUENCE header. */
+        len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
+
+        /*
+         * Now we know how big outblob needs to be. Allocate it.
+         */
+        outblob = snewn(len, unsigned char);
+
+        /*
+         * And write the data into it.
          */
-        pos = 4 + GET_32BIT(pubblob);
-        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
-        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
-        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
-        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
         pos = 0;
-        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
+        pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
+        for (i = 0; i < nnumbers; i++) {
+            pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
+            memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
+            pos += numbers[i].bytes;
+        }
+    } else if (key->alg == &ssh_ecdsa_nistp256 ||
+               key->alg == &ssh_ecdsa_nistp384 ||
+               key->alg == &ssh_ecdsa_nistp521) {
+        unsigned char *oid;
+        int oidlen;
+        int pointlen;
 
-        assert(y.start && x.start); /* can't go wrong */
+        /*
+         * Structure of asn1:
+         * SEQUENCE
+         *   INTEGER 1
+         *   OCTET STRING (private key)
+         *   [0]
+         *     OID (curve)
+         *   [1]
+         *     BIT STRING (0x00 public key point)
+         */
+        switch (((struct ec_key *)key->data)->publicKey.curve->fieldBits) {
+          case 256:
+            /* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */
+            oid = nistp256_oid;
+            oidlen = nistp256_oid_len;
+            pointlen = 32 * 2;
+            break;
+          case 384:
+            /* OID: 1.3.132.0.34 (secp384r1) */
+            oid = nistp384_oid;
+            oidlen = nistp384_oid_len;
+            pointlen = 48 * 2;
+            break;
+          case 521:
+            /* OID: 1.3.132.0.35 (secp521r1) */
+            oid = nistp521_oid;
+            oidlen = nistp521_oid_len;
+            pointlen = 66 * 2;
+            break;
+          default:
+            assert(0);
+        }
 
-        numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; 
-        numbers[1] = p;
-        numbers[2] = q;
-        numbers[3] = g;
-        numbers[4] = y;
-        numbers[5] = x;
+        len = ber_write_id_len(NULL, 2, 1, 0);
+        len += 1;
+        len += ber_write_id_len(NULL, 4, privlen - 4, 0);
+        len+= privlen - 4;
+        len += ber_write_id_len(NULL, 0, oidlen +
+                                ber_write_id_len(NULL, 6, oidlen, 0),
+                                ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
+        len += ber_write_id_len(NULL, 6, oidlen, 0);
+        len += oidlen;
+        len += ber_write_id_len(NULL, 1, 2 + pointlen +
+                                ber_write_id_len(NULL, 3, 2 + pointlen, 0),
+                                ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
+        len += ber_write_id_len(NULL, 3, 2 + pointlen, 0);
+        len += 2 + pointlen;
+
+        seqlen = len;
+        len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
+
+        outblob = snewn(len, unsigned char);
+        assert(outblob);
 
-        nnumbers = 6;
-        header = "-----BEGIN DSA PRIVATE KEY-----\n";
-        footer = "-----END DSA PRIVATE KEY-----\n";
+        pos = 0;
+        pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
+        pos += ber_write_id_len(outblob+pos, 2, 1, 0);
+        outblob[pos++] = 1;
+        pos += ber_write_id_len(outblob+pos, 4, privlen - 4, 0);
+        memcpy(outblob+pos, privblob + 4, privlen - 4);
+        pos += privlen - 4;
+        pos += ber_write_id_len(outblob+pos, 0, oidlen +
+                                ber_write_id_len(NULL, 6, oidlen, 0),
+                                ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
+        pos += ber_write_id_len(outblob+pos, 6, oidlen, 0);
+        memcpy(outblob+pos, oid, oidlen);
+        pos += oidlen;
+        pos += ber_write_id_len(outblob+pos, 1, 2 + pointlen +
+                                ber_write_id_len(NULL, 3, 2 + pointlen, 0),
+                                ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
+        pos += ber_write_id_len(outblob+pos, 3, 2 + pointlen, 0);
+        outblob[pos++] = 0;
+        memcpy(outblob+pos, pubblob+39, 1 + pointlen);
+        pos += 1 + pointlen;
+
+        header = "-----BEGIN EC PRIVATE KEY-----\n";
+        footer = "-----END EC PRIVATE KEY-----\n";
     } else {
         assert(0);                     /* zoinks! */
        exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
     }
 
-    /*
-     * Now count up the total size of the ASN.1 encoded integers,
-     * so as to determine the length of the containing SEQUENCE.
-     */
-    len = 0;
-    for (i = 0; i < nnumbers; i++) {
-       len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
-       len += numbers[i].bytes;
-    }
-    seqlen = len;
-    /* Now add on the SEQUENCE header. */
-    len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
-    /* Round up to the cipher block size, ensuring we have at least one
-     * byte of padding (see below). */
-    outlen = len;
-    if (passphrase)
-       outlen = (outlen+8) &~ 7;
-
-    /*
-     * Now we know how big outblob needs to be. Allocate it.
-     */
-    outblob = snewn(outlen, unsigned char);
-
-    /*
-     * And write the data into it.
-     */
-    pos = 0;
-    pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
-    for (i = 0; i < nnumbers; i++) {
-       pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
-       memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
-       pos += numbers[i].bytes;
-    }
-
-    /*
-     * Padding on OpenSSH keys is deterministic. The number of
-     * padding bytes is always more than zero, and always at most
-     * the cipher block length. The value of each padding byte is
-     * equal to the number of padding bytes. So a plaintext that's
-     * an exact multiple of the block size will be padded with 08
-     * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
-     * plaintext one byte less than a multiple of the block size
-     * will be padded with just 01.
-     * 
-     * This enables the OpenSSL key decryption function to strip
-     * off the padding algorithmically and return the unpadded
-     * plaintext to the next layer: it looks at the final byte, and
-     * then expects to find that many bytes at the end of the data
-     * with the same value. Those are all removed and the rest is
-     * returned.
-     */
-    assert(pos == len);
-    while (pos < outlen) {
-        outblob[pos++] = outlen - len;
-    }
-
     /*
      * Encrypt the key.
      *
@@ -897,6 +1092,44 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
      * old-style 3DES.
      */
     if (passphrase) {
+       struct MD5Context md5c;
+       unsigned char keybuf[32];
+
+        /*
+         * Round up to the cipher block size, ensuring we have at
+         * least one byte of padding (see below).
+         */
+        outlen = (len+8) &~ 7;
+        {
+            unsigned char *tmp = snewn(outlen, unsigned char);
+            memcpy(tmp, outblob, len);
+            smemclr(outblob, len);
+            sfree(outblob);
+            outblob = tmp;
+        }
+
+        /*
+         * Padding on OpenSSH keys is deterministic. The number of
+         * padding bytes is always more than zero, and always at most
+         * the cipher block length. The value of each padding byte is
+         * equal to the number of padding bytes. So a plaintext that's
+         * an exact multiple of the block size will be padded with 08
+         * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
+         * plaintext one byte less than a multiple of the block size
+         * will be padded with just 01.
+         *
+         * This enables the OpenSSL key decryption function to strip
+         * off the padding algorithmically and return the unpadded
+         * plaintext to the next layer: it looks at the final byte, and
+         * then expects to find that many bytes at the end of the data
+         * with the same value. Those are all removed and the rest is
+         * returned.
+         */
+        assert(pos == len);
+        while (pos < outlen) {
+            outblob[pos++] = outlen - len;
+        }
+
        /*
         * Invent an iv. Then derive encryption key from passphrase
         * and iv/salt:
@@ -906,9 +1139,6 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
         *  - block C would be MD5(B || passphrase || iv) and so on
         *  - encryption key is the first N bytes of A || B
         */
-       struct MD5Context md5c;
-       unsigned char keybuf[32];
-
        for (i = 0; i < 8; i++) iv[i] = random_byte();
 
        MD5Init(&md5c);
@@ -929,6 +1159,12 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
 
         smemclr(&md5c, sizeof(md5c));
         smemclr(keybuf, sizeof(keybuf));
+    } else {
+        /*
+         * If no encryption, the blob has exactly its original
+         * cleartext size.
+         */
+        outlen = len;
     }
 
     /*
diff --git a/putty.h b/putty.h
index 5e0439609bfcd3db653b7b8bc7b9c537ea83f33a..d8bddbae5891a4ec302e9cde50141647a9ce4a90 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -253,6 +253,7 @@ enum {
     KEX_DHGROUP14,
     KEX_DHGEX,
     KEX_RSA,
+    KEX_ECDH,
     KEX_MAX
 };
 
index 898c0dabc97ea18841962689dfc6f71a67ece2f7..9d2e4140d86ceb39019952be82bb4af88273c79d 100644 (file)
@@ -19,6 +19,7 @@ static const struct keyvalwhere ciphernames[] = {
 };
 
 static const struct keyvalwhere kexnames[] = {
+    { "ecdh",               KEX_ECDH,       -1, +1 },
     { "dh-gex-sha1",        KEX_DHGEX,      -1, -1 },
     { "dh-group14-sha1",    KEX_DHGROUP14,  -1, -1 },
     { "dh-group1-sha1",     KEX_DHGROUP1,   -1, -1 },
@@ -770,9 +771,11 @@ void load_open_settings(void *sesskey, Conf *conf)
        char *default_kexes;
        i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0);
        if (i == FORCE_ON)
-           default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";
+            default_kexes = "ecdh,dh-group14-sha1,dh-group1-sha1,rsa,"
+                "WARN,dh-gex-sha1";
        else
-           default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";
+            default_kexes = "ecdh,dh-gex-sha1,dh-group14-sha1,"
+                "dh-group1-sha1,rsa,WARN";
        gprefs(sesskey, "KEX", default_kexes,
               kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
     }
diff --git a/ssh.c b/ssh.c
index d395d7b5c556e72347a814cd2c8509eed27c1aa1..bc079e91754b8f6df64a61e7a973b42bc1f15e2c 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -32,6 +32,7 @@ typedef enum {
     SSH2_PKTCTX_NOKEX,
     SSH2_PKTCTX_DHGROUP,
     SSH2_PKTCTX_DHGEX,
+    SSH2_PKTCTX_ECDHKEX,
     SSH2_PKTCTX_RSAKEX
 } Pkt_KCtx;
 typedef enum {
@@ -254,6 +255,8 @@ static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
     translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
     translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
     translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
+    translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
     translate(SSH2_MSG_USERAUTH_REQUEST);
     translate(SSH2_MSG_USERAUTH_FAILURE);
     translate(SSH2_MSG_USERAUTH_SUCCESS);
@@ -397,7 +400,10 @@ static void ssh_channel_destroy(struct ssh_channel *c);
 #define OUR_V2_MAXPKT 0x4000UL
 #define OUR_V2_PACKETLIMIT 0x9000UL
 
-const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
+const static struct ssh_signkey *hostkey_algs[] = {
+    &ssh_ecdsa_nistp256, &ssh_ecdsa_nistp384, &ssh_ecdsa_nistp521,
+    &ssh_rsa, &ssh_dss
+};
 
 const static struct ssh_mac *macs[] = {
     &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
@@ -5920,7 +5926,8 @@ static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
 /*
  * Utility routine for decoding comma-separated strings in KEXINIT.
  */
-static int in_commasep_string(char *needle, char *haystack, int haylen)
+static int in_commasep_string(char const *needle, char const *haystack,
+                             int haylen)
 {
     int needlen;
     if (!needle || !haystack)         /* protect against null pointers */
@@ -5951,7 +5958,8 @@ static int in_commasep_string(char *needle, char *haystack, int haylen)
 /*
  * Similar routine for checking whether we have the first string in a list.
  */
-static int first_in_commasep_string(char *needle, char *haystack, int haylen)
+static int first_in_commasep_string(char const *needle, char const *haystack,
+                                   int haylen)
 {
     int needlen;
     if (!needle || !haystack)         /* protect against null pointers */
@@ -5969,6 +5977,19 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen)
     return 0;
 }
 
+/*
+ * Add a value to the comma-separated string at the end of the packet.
+ * If the value is already in the string, don't bother adding it again.
+ */
+static void ssh2_pkt_addstring_commasep(struct Packet *pkt, const char *data)
+{
+    if (in_commasep_string(data, (char *)pkt->data + pkt->savedpos,
+                          pkt->length - pkt->savedpos)) return;
+    if (pkt->length - pkt->savedpos > 0)
+       ssh_pkt_addstring_str(pkt, ",");
+    ssh_pkt_addstring_str(pkt, data);
+}
+
 
 /*
  * SSH-2 key creation method.
@@ -6024,6 +6045,7 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        int hostkeylen, siglen, rsakeylen;
        void *hkey;                    /* actual host key */
        void *rsakey;                  /* for RSA kex */
+        void *eckey;                   /* for ECDH kex */
        unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
        int n_preferred_kex;
        const struct ssh_kexes *preferred_kex[KEX_MAX];
@@ -6063,7 +6085,7 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
   begin_key_exchange:
     ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
     {
-       int i, j, k, commalist_started;
+       int i, j, k;
 
        /*
         * Set up the preferred key exchange. (NULL => warn below here)
@@ -6087,6 +6109,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                s->preferred_kex[s->n_preferred_kex++] =
                    &ssh_rsa_kex;
                break;
+              case KEX_ECDH:
+                s->preferred_kex[s->n_preferred_kex++] =
+                    &ssh_ecdh_kex;
+                break;
              case KEX_WARN:
                /* Flag for later. Don't bother if it's the last in
                 * the list. */
@@ -6157,16 +6183,11 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());
        /* List key exchange algorithms. */
        ssh2_pkt_addstring_start(s->pktout);
-       commalist_started = 0;
        for (i = 0; i < s->n_preferred_kex; i++) {
            const struct ssh_kexes *k = s->preferred_kex[i];
            if (!k) continue;          /* warning flag */
-           for (j = 0; j < k->nkexes; j++) {
-               if (commalist_started)
-                   ssh2_pkt_addstring_str(s->pktout, ",");
-               ssh2_pkt_addstring_str(s->pktout, k->list[j]->name);
-               commalist_started = 1;
-           }
+           for (j = 0; j < k->nkexes; j++)
+               ssh2_pkt_addstring_commasep(s->pktout, k->list[j]->name);
        }
        /* List server host key algorithms. */
         if (!s->got_session_id) {
@@ -6175,11 +6196,8 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
              * we're prepared to cope with.
              */
             ssh2_pkt_addstring_start(s->pktout);
-            for (i = 0; i < lenof(hostkey_algs); i++) {
-                ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);
-                if (i < lenof(hostkey_algs) - 1)
-                    ssh2_pkt_addstring_str(s->pktout, ",");
-            }
+            for (i = 0; i < lenof(hostkey_algs); i++)
+                ssh2_pkt_addstring_commasep(s->pktout, hostkey_algs[i]->name);
         } else {
             /*
              * In subsequent key exchanges, we list only the kex
@@ -6194,26 +6212,18 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        /* List encryption algorithms (client->server then server->client). */
        for (k = 0; k < 2; k++) {
            ssh2_pkt_addstring_start(s->pktout);
-           commalist_started = 0;
            for (i = 0; i < s->n_preferred_ciphers; i++) {
                const struct ssh2_ciphers *c = s->preferred_ciphers[i];
                if (!c) continue;              /* warning flag */
-               for (j = 0; j < c->nciphers; j++) {
-                   if (commalist_started)
-                       ssh2_pkt_addstring_str(s->pktout, ",");
-                   ssh2_pkt_addstring_str(s->pktout, c->list[j]->name);
-                   commalist_started = 1;
-               }
+               for (j = 0; j < c->nciphers; j++)
+                   ssh2_pkt_addstring_commasep(s->pktout, c->list[j]->name);
            }
        }
        /* List MAC algorithms (client->server then server->client). */
        for (j = 0; j < 2; j++) {
            ssh2_pkt_addstring_start(s->pktout);
-           for (i = 0; i < s->nmacs; i++) {
-               ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name);
-               if (i < s->nmacs - 1)
-                   ssh2_pkt_addstring_str(s->pktout, ",");
-           }
+           for (i = 0; i < s->nmacs; i++)
+               ssh2_pkt_addstring_commasep(s->pktout, s->maclist[i]->name);
        }
        /* List client->server compression algorithms,
         * then server->client compression algorithms. (We use the
@@ -6222,25 +6232,18 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            ssh2_pkt_addstring_start(s->pktout);
            assert(lenof(compressions) > 1);
            /* Prefer non-delayed versions */
-           ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
+           ssh2_pkt_addstring_commasep(s->pktout, s->preferred_comp->name);
            /* We don't even list delayed versions of algorithms until
             * they're allowed to be used, to avoid a race. See the end of
             * this function. */
-           if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
-               ssh2_pkt_addstring_str(s->pktout, ",");
-               ssh2_pkt_addstring_str(s->pktout,
-                                      s->preferred_comp->delayed_name);
-           }
+           if (s->userauth_succeeded && s->preferred_comp->delayed_name)
+               ssh2_pkt_addstring_commasep(s->pktout,
+                                           s->preferred_comp->delayed_name);
            for (i = 0; i < lenof(compressions); i++) {
                const struct ssh_compress *c = compressions[i];
-               if (c != s->preferred_comp) {
-                   ssh2_pkt_addstring_str(s->pktout, ",");
-                   ssh2_pkt_addstring_str(s->pktout, c->name);
-                   if (s->userauth_succeeded && c->delayed_name) {
-                       ssh2_pkt_addstring_str(s->pktout, ",");
-                       ssh2_pkt_addstring_str(s->pktout, c->delayed_name);
-                   }
-               }
+               ssh2_pkt_addstring_commasep(s->pktout, c->name);
+               if (s->userauth_succeeded && c->delayed_name)
+                   ssh2_pkt_addstring_commasep(s->pktout, c->delayed_name);
            }
        }
        /* List client->server languages. Empty list. */
@@ -6653,6 +6656,83 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
             freebn(s->g);
             freebn(s->p);
         }
+    } else if (ssh->kex->main_type == KEXTYPE_ECDH) {
+
+        logeventf(ssh, "Doing ECDH key exchange with hash %s",
+                  ssh->kex->hash->text_name);
+        ssh->pkt_kctx = SSH2_PKTCTX_ECDHKEX;
+
+        s->eckey = NULL;
+        if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp256")) {
+            s->eckey = ssh_ecdhkex_newkey(ec_p256());
+        } else if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp384")) {
+            s->eckey = ssh_ecdhkex_newkey(ec_p384());
+        } else if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp521")) {
+            s->eckey = ssh_ecdhkex_newkey(ec_p521());
+        }
+        if (!s->eckey) {
+            bombout(("Unable to generate key for ECDH"));
+            crStopV;
+        }
+
+        {
+            char *publicPoint;
+            int publicPointLength;
+            publicPoint = ssh_ecdhkex_getpublic(s->eckey, &publicPointLength);
+            if (!publicPoint) {
+                ssh_ecdhkex_freekey(s->eckey);
+                bombout(("Unable to encode public key for ECDH"));
+                crStopV;
+            }
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_ECDH_INIT);
+            ssh2_pkt_addstring_start(s->pktout);
+            ssh2_pkt_addstring_data(s->pktout, publicPoint, publicPointLength);
+            sfree(publicPoint);
+        }
+
+        ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+        crWaitUntilV(pktin);
+        if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) {
+            ssh_ecdhkex_freekey(s->eckey);
+            bombout(("expected ECDH reply packet from server"));
+            crStopV;
+        }
+
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+        hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
+        s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+
+        {
+            char *publicPoint;
+            int publicPointLength;
+            publicPoint = ssh_ecdhkex_getpublic(s->eckey, &publicPointLength);
+            if (!publicPoint) {
+                ssh_ecdhkex_freekey(s->eckey);
+                bombout(("Unable to encode public key for ECDH hash"));
+                crStopV;
+            }
+            hash_string(ssh->kex->hash, ssh->exhash,
+                        publicPoint, publicPointLength);
+            sfree(publicPoint);
+        }
+
+        {
+            char *keydata;
+            int keylen;
+            ssh_pkt_getstring(pktin, &keydata, &keylen);
+            hash_string(ssh->kex->hash, ssh->exhash, keydata, keylen);
+            s->K = ssh_ecdhkex_getkey(s->eckey, keydata, keylen);
+            if (!s->K) {
+                ssh_ecdhkex_freekey(s->eckey);
+                bombout(("point received in ECDH was not valid"));
+                crStopV;
+            }
+        }
+
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        ssh_ecdhkex_freekey(s->eckey);
     } else {
        logeventf(ssh, "Doing RSA key exchange with hash %s",
                  ssh->kex->hash->text_name);
diff --git a/ssh.h b/ssh.h
index 89f772d661e34bfa1395874ff4418d4b97e91d7a..2f4dd61c900198f0adedc2e6fad7460f0badd1ac 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -99,6 +99,38 @@ struct dss_key {
     Bignum p, q, g, y, x;
 };
 
+struct ec_curve;
+
+struct ec_point {
+    const struct ec_curve *curve;
+    Bignum x, y;
+    Bignum z;  /* Jacobian denominator */
+    unsigned char infinity;
+};
+
+void ec_point_free(struct ec_point *point);
+
+struct ec_curve {
+    unsigned int fieldBits;
+    Bignum p, a, b, n;
+    struct ec_point G;
+};
+
+extern unsigned char nistp256_oid[];
+extern unsigned char nistp384_oid[];
+extern unsigned char nistp521_oid[];
+extern int nistp256_oid_len;
+extern int nistp384_oid_len;
+extern int nistp521_oid_len;
+struct ec_curve *ec_p256(void);
+struct ec_curve *ec_p384(void);
+struct ec_curve *ec_p521(void);
+
+struct ec_key {
+    struct ec_point publicKey;
+    Bignum privateKey;
+};
+
 int makekey(unsigned char *data, int len, struct RSAKey *result,
            unsigned char **keystr, int order);
 int makeprivate(unsigned char *data, int len, struct RSAKey *result);
@@ -141,6 +173,20 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
                         unsigned char *out, int outlen,
                         void *key);
 
+/*
+ * SSH2 ECDH key exchange functions
+ */
+void *ssh_ecdhkex_newkey(struct ec_curve *curve);
+void ssh_ecdhkex_freekey(void *key);
+char *ssh_ecdhkex_getpublic(void *key, int *len);
+Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen);
+
+/*
+ * Helper function for k generation in DSA, reused in ECDSA
+ */
+Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
+                  unsigned char *digest, int digest_len);
+
 typedef struct {
     uint32 h[4];
 } MD5_Core_State;
@@ -198,10 +244,15 @@ typedef struct {
     int blkused;
     uint32 len[4];
 } SHA512_State;
+#define SHA384_State SHA512_State
 void SHA512_Init(SHA512_State * s);
 void SHA512_Bytes(SHA512_State * s, const void *p, int len);
 void SHA512_Final(SHA512_State * s, unsigned char *output);
 void SHA512_Simple(const void *p, int len, unsigned char *output);
+void SHA384_Init(SHA384_State * s);
+#define SHA384_Bytes(s, p, len) SHA512_Bytes(s, p, len)
+void SHA384_Final(SHA384_State * s, unsigned char *output);
+void SHA384_Simple(const void *p, int len, unsigned char *output);
 
 struct ssh_cipher {
     void *(*make_context)(void);
@@ -260,7 +311,7 @@ struct ssh_hash {
 
 struct ssh_kex {
     char *name, *groupname;
-    enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
+    enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH } main_type;
     /* For DH */
     const unsigned char *pdata, *gdata; /* NULL means group exchange */
     int plen, glen;
@@ -316,7 +367,7 @@ struct ssh2_userkey {
 };
 
 /* The maximum length of any hash algorithm used in kex. (bytes) */
-#define SSH2_KEX_MAX_HASH_LEN (32) /* SHA-256 */
+#define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */
 
 extern const struct ssh_cipher ssh_3des;
 extern const struct ssh_cipher ssh_des;
@@ -328,12 +379,18 @@ extern const struct ssh2_ciphers ssh2_blowfish;
 extern const struct ssh2_ciphers ssh2_arcfour;
 extern const struct ssh_hash ssh_sha1;
 extern const struct ssh_hash ssh_sha256;
+extern const struct ssh_hash ssh_sha384;
+extern const struct ssh_hash ssh_sha512;
 extern const struct ssh_kexes ssh_diffiehellman_group1;
 extern const struct ssh_kexes ssh_diffiehellman_group14;
 extern const struct ssh_kexes ssh_diffiehellman_gex;
 extern const struct ssh_kexes ssh_rsa_kex;
+extern const struct ssh_kexes ssh_ecdh_kex;
 extern const struct ssh_signkey ssh_dss;
 extern const struct ssh_signkey ssh_rsa;
+extern const struct ssh_signkey ssh_ecdsa_nistp256;
+extern const struct ssh_signkey ssh_ecdsa_nistp384;
+extern const struct ssh_signkey ssh_ecdsa_nistp521;
 extern const struct ssh_mac ssh_hmac_md5;
 extern const struct ssh_mac ssh_hmac_sha1;
 extern const struct ssh_mac ssh_hmac_sha1_buggy;
@@ -502,9 +559,11 @@ Bignum bignum_from_long(unsigned long n);
 void freebn(Bignum b);
 Bignum modpow(Bignum base, Bignum exp, Bignum mod);
 Bignum modmul(Bignum a, Bignum b, Bignum mod);
+Bignum modsub(const Bignum a, const Bignum b, const Bignum n);
 void decbn(Bignum n);
 extern Bignum Zero, One;
 Bignum bignum_from_bytes(const unsigned char *data, int nbytes);
+Bignum bignum_random_in_range(const Bignum lower, const Bignum upper);
 int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result);
 int bignum_bitcount(Bignum bn);
 int ssh1_bignum_length(Bignum bn);
@@ -525,6 +584,7 @@ Bignum bigmod(Bignum a, Bignum b);
 Bignum modinv(Bignum number, Bignum modulus);
 Bignum bignum_bitmask(Bignum number);
 Bignum bignum_rshift(Bignum number, int shift);
+Bignum bignum_lshift(Bignum number, int shift);
 int bignum_cmp(Bignum a, Bignum b);
 char *bignum_decimal(Bignum x);
 
@@ -617,6 +677,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
                 void *pfnparam);
 int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
                 void *pfnparam);
+int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
+                void *pfnparam);
 Bignum primegen(int bits, int modulus, int residue, Bignum factor,
                int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
 void invent_firstbits(unsigned *one, unsigned *two);
@@ -726,6 +788,10 @@ void platform_ssh_share_cleanup(const char *name);
 #define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
 #define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
 #define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
+#define SSH2_MSG_KEX_ECDH_INIT                    30    /* 0x1e */
+#define SSH2_MSG_KEX_ECDH_REPLY                   31    /* 0x1f */
+#define SSH2_MSG_KEX_ECMQV_INIT                   30    /* 0x1e */
+#define SSH2_MSG_KEX_ECMQV_REPLY                  31    /* 0x1f */
 #define SSH2_MSG_USERAUTH_REQUEST                 50   /* 0x32 */
 #define SSH2_MSG_USERAUTH_FAILURE                 51   /* 0x33 */
 #define SSH2_MSG_USERAUTH_SUCCESS                 52   /* 0x34 */
diff --git a/sshbn.c b/sshbn.c
index b1ea5d63c488cde28e8744285a2368601863f56b..b40781ff2996dc65fe9659e4d15ef8921d1f52f3 100644 (file)
--- a/sshbn.c
+++ b/sshbn.c
@@ -1092,6 +1092,38 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod)
     return result;
 }
 
+Bignum modsub(const Bignum a, const Bignum b, const Bignum n)
+{
+    Bignum a1, b1, ret;
+
+    if (bignum_cmp(a, n) >= 0) a1 = bigmod(a, n);
+    else a1 = a;
+    if (bignum_cmp(b, n) >= 0) b1 = bigmod(b, n);
+    else b1 = b;
+
+    if (bignum_cmp(a1, b1) >= 0) /* a >= b */
+    {
+        ret = bigsub(a1, b1);
+    }
+    else
+    {
+        /* Handle going round the corner of the modulus without having
+         * negative support in Bignum */
+        Bignum tmp = bigsub(n, b1);
+        if (tmp) {
+            ret = bigadd(tmp, a1);
+            freebn(tmp);
+        } else {
+            ret = NULL;
+        }
+    }
+
+    if (a != a1) freebn(a1);
+    if (b != b1) freebn(b1);
+
+    return ret;
+}
+
 /*
  * Compute p % mod.
  * The most significant word of mod MUST be non-zero.
@@ -1201,6 +1233,37 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes)
     return result;
 }
 
+Bignum bignum_random_in_range(const Bignum lower, const Bignum upper)
+{
+    Bignum ret = NULL;
+    int upper_len = bignum_bitcount(upper);
+    int upper_bytes = upper_len / 8;
+    int upper_bits = upper_len % 8;
+    if (upper_bits) ++upper_bytes;
+
+    do {
+        unsigned char *bytes;
+        int i;
+
+        if (ret) freebn(ret);
+
+        bytes = snewn(upper_bytes, unsigned char);
+        for (i = 0; i < upper_bytes; ++i)
+        {
+            bytes[i] = (unsigned char)random_byte();
+        }
+        /* Mask the top to reduce failure rate to 50/50 */
+        if (upper_bits)
+        {
+            bytes[i - 1] &= 0xFF >> (8 - upper_bits);
+        }
+
+        ret = bignum_from_bytes(bytes, upper_bytes);
+    } while (bignum_cmp(ret, lower) < 0 || bignum_cmp(ret, upper) > 0);
+
+    return ret;
+}
+
 /*
  * Read an SSH-1-format bignum from a data buffer. Return the number
  * of bytes consumed, or -1 if there wasn't enough data.
@@ -1375,6 +1438,45 @@ Bignum bignum_rshift(Bignum a, int shift)
     return ret;
 }
 
+/*
+ * Left-shift one bignum to form another.
+ */
+Bignum bignum_lshift(Bignum a, int shift)
+{
+    Bignum ret;
+    int bits, shiftWords, shiftBits;
+
+    assert(shift >= 0);
+
+    bits = bignum_bitcount(a) + shift;
+    ret = newbn((bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS);
+    if (!ret) return NULL;
+
+    shiftWords = shift / BIGNUM_INT_BITS;
+    shiftBits = shift % BIGNUM_INT_BITS;
+
+    if (shiftBits == 0)
+    {
+        memcpy(&ret[1 + shiftWords], &a[1], sizeof(BignumInt) * a[0]);
+    }
+    else
+    {
+        int i;
+        BignumInt carry = 0;
+
+        /* Remember that Bignum[0] is length, so add 1 */
+        for (i = shiftWords + 1; i < ((int)a[0]) + shiftWords + 1; ++i)
+        {
+            BignumInt from = a[i - shiftWords];
+            ret[i] = (from << shiftBits) | carry;
+            carry = from >> (BIGNUM_INT_BITS - shiftBits);
+        }
+        if (carry) ret[i] = carry;
+    }
+
+    return ret;
+}
+
 /*
  * Non-modular multiplication and addition.
  */
index 84fcdac799d76ce8dc7acca97641a55f3de9044a..3682ad69e8ce85e223945e1965d58fb7281ddbef 100644 (file)
--- a/sshdss.c
+++ b/sshdss.c
@@ -518,7 +518,8 @@ static int dss_pubkey_bits(void *blob, int len)
     return ret;
 }
 
-static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
+Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
+                  unsigned char *digest, int digest_len)
 {
     /*
      * The basic DSS signing algorithm is:
@@ -591,21 +592,16 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
      * Computer Security Group for helping to argue out all the
      * fine details.
      */
-    struct dss_key *dss = (struct dss_key *) key;
     SHA512_State ss;
-    unsigned char digest[20], digest512[64];
-    Bignum proto_k, k, gkp, hash, kinv, hxr, r, s;
-    unsigned char *bytes;
-    int nbytes, i;
-
-    SHA_Simple(data, datalen, digest);
+    unsigned char digest512[64];
+    Bignum proto_k, k;
 
     /*
      * Hash some identifying text plus x.
      */
     SHA512_Init(&ss);
-    SHA512_Bytes(&ss, "DSA deterministic k generator", 30);
-    sha512_mpint(&ss, dss->x);
+    SHA512_Bytes(&ss, id_string, strlen(id_string) + 1);
+    sha512_mpint(&ss, private_key);
     SHA512_Final(&ss, digest512);
 
     /*
@@ -613,7 +609,7 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
      */
     SHA512_Init(&ss);
     SHA512_Bytes(&ss, digest512, sizeof(digest512));
-    SHA512_Bytes(&ss, digest, sizeof(digest));
+    SHA512_Bytes(&ss, digest, digest_len);
 
     while (1) {
         SHA512_State ss2 = ss;         /* structure copy */
@@ -625,23 +621,37 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
          * Now convert the result into a bignum, and reduce it mod q.
          */
         proto_k = bignum_from_bytes(digest512, 64);
-        k = bigmod(proto_k, dss->q);
+        k = bigmod(proto_k, modulus);
         freebn(proto_k);
-        kinv = modinv(k, dss->q);             /* k^-1 mod q */
-        if (!kinv) {                           /* very unlikely */
-            freebn(k);
-            /* Perturb the hash to think of a different k. */
-            SHA512_Bytes(&ss, "x", 1);
-            /* Go round and try again. */
-            continue;
+
+        if (bignum_cmp(k, One) != 0 && bignum_cmp(k, Zero) != 0) {
+            smemclr(&ss, sizeof(ss));
+            smemclr(digest512, sizeof(digest512));
+            return k;
         }
 
-        break;
+        /* Very unlikely we get here, but if so, k was unsuitable. */
+        freebn(k);
+        /* Perturb the hash to think of a different k. */
+        SHA512_Bytes(&ss, "x", 1);
+        /* Go round and try again. */
     }
+}
 
-    smemclr(&ss, sizeof(ss));
+static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
+{
+    struct dss_key *dss = (struct dss_key *) key;
+    Bignum k, gkp, hash, kinv, hxr, r, s;
+    unsigned char digest[20];
+    unsigned char *bytes;
+    int nbytes, i;
+
+    SHA_Simple(data, datalen, digest);
 
-    smemclr(digest512, sizeof(digest512));
+    k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x,
+                  digest, sizeof(digest));
+    kinv = modinv(k, dss->q);         /* k^-1 mod q */
+    assert(kinv);
 
     /*
      * Now we have k, so just go ahead and compute the signature.
diff --git a/sshecc.c b/sshecc.c
new file mode 100644 (file)
index 0000000..7d877ba
--- /dev/null
+++ b/sshecc.c
@@ -0,0 +1,2104 @@
+/*
+ * Elliptic-curve crypto module for PuTTY
+ * Implements the three required curves, no optional curves
+ * NOTE: Only curves on prime field are handled by the maths functions
+ */
+
+/*
+ * References:
+ *
+ * Elliptic curves in SSH are specified in RFC 5656:
+ *   http://tools.ietf.org/html/rfc5656
+ *
+ * That specification delegates details of public key formatting and a
+ * lot of underlying mechanism to SEC 1:
+ *   http://www.secg.org/sec1-v2.pdf
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ssh.h"
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve definitions
+ */
+
+static int initialise_curve(struct ec_curve *curve, int bits, unsigned char *p,
+                            unsigned char *a, unsigned char *b,
+                            unsigned char *n, unsigned char *Gx,
+                            unsigned char *Gy)
+{
+    int length = bits / 8;
+    if (bits % 8) ++length;
+
+    curve->fieldBits = bits;
+    curve->p = bignum_from_bytes(p, length);
+    if (!curve->p) goto error;
+
+    /* Curve co-efficients */
+    curve->a = bignum_from_bytes(a, length);
+    if (!curve->a) goto error;
+    curve->b = bignum_from_bytes(b, length);
+    if (!curve->b) goto error;
+
+    /* Group order and generator */
+    curve->n = bignum_from_bytes(n, length);
+    if (!curve->n) goto error;
+    curve->G.x = bignum_from_bytes(Gx, length);
+    if (!curve->G.x) goto error;
+    curve->G.y = bignum_from_bytes(Gy, length);
+    if (!curve->G.y) goto error;
+    curve->G.curve = curve;
+    curve->G.infinity = 0;
+
+    return 1;
+  error:
+    if (curve->p) freebn(curve->p);
+    if (curve->a) freebn(curve->a);
+    if (curve->b) freebn(curve->b);
+    if (curve->n) freebn(curve->n);
+    if (curve->G.x) freebn(curve->G.x);
+    return 0;
+}
+
+unsigned char nistp256_oid[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07};
+int nistp256_oid_len = 8;
+unsigned char nistp384_oid[] = {0x2b, 0x81, 0x04, 0x00, 0x22};
+int nistp384_oid_len = 5;
+unsigned char nistp521_oid[] = {0x2b, 0x81, 0x04, 0x00, 0x23};
+int nistp521_oid_len = 5;
+
+struct ec_curve *ec_p256(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        unsigned char p[] = {
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+        };
+        unsigned char a[] = {
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc
+        };
+        unsigned char b[] = {
+            0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7,
+            0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86, 0xbc,
+            0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6,
+            0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b
+        };
+        unsigned char n[] = {
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
+            0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
+        };
+        unsigned char Gx[] = {
+            0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
+            0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+            0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+            0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96
+        };
+        unsigned char Gy[] = {
+            0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
+            0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+            0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+            0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5
+        };
+
+        if (!initialise_curve(&curve, 256, p, a, b, n, Gx, Gy)) {
+            return NULL;
+        }
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+struct ec_curve *ec_p384(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        unsigned char p[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+        };
+        unsigned char a[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc
+        };
+        unsigned char b[] = {
+            0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4,
+            0x98, 0x8e, 0x05, 0x6b, 0xe3, 0xf8, 0x2d, 0x19,
+            0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, 0x41, 0x12,
+            0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a,
+            0xc6, 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d,
+            0x2a, 0x85, 0xc8, 0xed, 0xd3, 0xec, 0x2a, 0xef
+        };
+        unsigned char n[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
+            0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
+            0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73
+        };
+        unsigned char Gx[] = {
+            0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37,
+            0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74,
+            0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
+            0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38,
+            0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c,
+            0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7
+        };
+        unsigned char Gy[] = {
+            0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f,
+            0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29,
+            0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+            0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0,
+            0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
+            0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f
+        };
+
+        if (!initialise_curve(&curve, 384, p, a, b, n, Gx, Gy)) {
+            return NULL;
+        }
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+struct ec_curve *ec_p521(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        unsigned char p[] = {
+            0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff
+        };
+        unsigned char a[] = {
+            0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xfc
+        };
+        unsigned char b[] = {
+            0x00, 0x51, 0x95, 0x3e, 0xb9, 0x61, 0x8e, 0x1c,
+            0x9a, 0x1f, 0x92, 0x9a, 0x21, 0xa0, 0xb6, 0x85,
+            0x40, 0xee, 0xa2, 0xda, 0x72, 0x5b, 0x99, 0xb3,
+            0x15, 0xf3, 0xb8, 0xb4, 0x89, 0x91, 0x8e, 0xf1,
+            0x09, 0xe1, 0x56, 0x19, 0x39, 0x51, 0xec, 0x7e,
+            0x93, 0x7b, 0x16, 0x52, 0xc0, 0xbd, 0x3b, 0xb1,
+            0xbf, 0x07, 0x35, 0x73, 0xdf, 0x88, 0x3d, 0x2c,
+            0x34, 0xf1, 0xef, 0x45, 0x1f, 0xd4, 0x6b, 0x50,
+            0x3f, 0x00
+        };
+        unsigned char n[] = {
+            0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f,
+            0x96, 0x6b, 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09,
+            0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c,
+            0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38,
+            0x64, 0x09
+        };
+        unsigned char Gx[] = {
+            0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04,
+            0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
+            0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
+            0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
+            0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
+            0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
+            0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
+            0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
+            0xbd, 0x66
+        };
+        unsigned char Gy[] = {
+            0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b,
+            0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
+            0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+            0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
+            0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
+            0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+            0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
+            0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
+            0x66, 0x50
+        };
+
+        if (!initialise_curve(&curve, 521, p, a, b, n, Gx, Gy)) {
+            return NULL;
+        }
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+static struct ec_curve *ec_name_to_curve(char *name, int len) {
+    if (len == 8 && !memcmp(name, "nistp", 5)) {
+        name += 5;
+        if (!memcmp(name, "256", 3)) {
+            return ec_p256();
+        } else if (!memcmp(name, "384", 3)) {
+            return ec_p384();
+        } else if (!memcmp(name, "521", 3)) {
+            return ec_p521();
+        }
+    }
+
+    return NULL;
+}
+
+static int ec_curve_to_name(const struct ec_curve *curve, unsigned char *name, int len) {
+    /* Return length of string */
+    if (name == NULL) return 8;
+
+    /* Not enough space for the name */
+    if (len < 8) return 0;
+
+    /* Put the name in the buffer */
+    switch (curve->fieldBits) {
+      case 256:
+        memcpy(name+5, "256", 3);
+        break;
+      case 384:
+        memcpy(name+5, "384", 3);
+        break;
+      case 521:
+        memcpy(name+5, "521", 3);
+        break;
+      default:
+        return 0;
+    }
+
+    memcpy(name, "nistp", 5);
+    return 8;
+}
+
+/* Return 1 if a is -3 % p, otherwise return 0
+ * This is used because there are some maths optimisations */
+static int ec_aminus3(const struct ec_curve *curve)
+{
+    int ret;
+    Bignum _p;
+
+    _p = bignum_add_long(curve->a, 3);
+    if (!_p) return 0;
+
+    ret = !bignum_cmp(curve->p, _p);
+    freebn(_p);
+    return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve field maths
+ */
+
+static Bignum ecf_add(const Bignum a, const Bignum b,
+                      const struct ec_curve *curve)
+{
+    Bignum a1, b1, ab, ret;
+
+    a1 = bigmod(a, curve->p);
+    if (!a1) return NULL;
+    b1 = bigmod(b, curve->p);
+    if (!b1)
+    {
+        freebn(a1);
+        return NULL;
+    }
+
+    ab = bigadd(a1, b1);
+    freebn(a1);
+    freebn(b1);
+    if (!ab) return NULL;
+
+    ret = bigmod(ab, curve->p);
+    freebn(ab);
+
+    return ret;
+}
+
+static Bignum ecf_square(const Bignum a, const struct ec_curve *curve)
+{
+    return modmul(a, a, curve->p);
+}
+
+static Bignum ecf_treble(const Bignum a, const struct ec_curve *curve)
+{
+    Bignum ret, tmp;
+
+    /* Double */
+    tmp = bignum_lshift(a, 1);
+    if (!tmp) return NULL;
+
+    /* Add itself (i.e. treble) */
+    ret = bigadd(tmp, a);
+    freebn(tmp);
+
+    /* Normalise */
+    while (ret != NULL && bignum_cmp(ret, curve->p) >= 0)
+    {
+        tmp = bigsub(ret, curve->p);
+        freebn(ret);
+        ret = tmp;
+    }
+
+    return ret;
+}
+
+static Bignum ecf_double(const Bignum a, const struct ec_curve *curve)
+{
+    Bignum ret = bignum_lshift(a, 1);
+    if (!ret) return NULL;
+    if (bignum_cmp(ret, curve->p) >= 0)
+    {
+        Bignum tmp = bigsub(ret, curve->p);
+        freebn(ret);
+        return tmp;
+    }
+    else
+    {
+        return ret;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Memory functions
+ */
+
+void ec_point_free(struct ec_point *point)
+{
+    if (point == NULL) return;
+    point->curve = 0;
+    if (point->x) freebn(point->x);
+    if (point->y) freebn(point->y);
+    if (point->z) freebn(point->z);
+    point->infinity = 0;
+    sfree(point);
+}
+
+static struct ec_point *ec_point_new(const struct ec_curve *curve,
+                                     const Bignum x, const Bignum y, const Bignum z,
+                                     unsigned char infinity)
+{
+    struct ec_point *point = snewn(1, struct ec_point);
+    point->curve = curve;
+    point->x = x;
+    point->y = y;
+    point->z = z;
+    point->infinity = infinity ? 1 : 0;
+    return point;
+}
+
+static struct ec_point *ec_point_copy(const struct ec_point *a)
+{
+    if (a == NULL) return NULL;
+    return ec_point_new(a->curve,
+                        a->x ? copybn(a->x) : NULL,
+                        a->y ? copybn(a->y) : NULL,
+                        a->z ? copybn(a->z) : NULL,
+                        a->infinity);
+}
+
+static int ec_point_verify(const struct ec_point *a)
+{
+    if (a->infinity)
+    {
+        return 1;
+    }
+    else
+    {
+        /* Verify y^2 = x^3 + ax + b */
+        int ret = 0;
+
+        Bignum lhs = NULL, x3 = NULL, ax = NULL, x3ax = NULL, x3axm = NULL, x3axb = NULL, rhs = NULL;
+
+        Bignum Three = bignum_from_long(3);
+        if (!Three) return 0;
+
+        lhs = modmul(a->y, a->y, a->curve->p);
+        if (!lhs) goto error;
+
+        /* This uses montgomery multiplication to optimise */
+        x3 = modpow(a->x, Three, a->curve->p);
+        freebn(Three);
+        if (!x3) goto error;
+        ax = modmul(a->curve->a, a->x, a->curve->p);
+        if (!ax) goto error;
+        x3ax = bigadd(x3, ax);
+        if (!x3ax) goto error;
+        freebn(x3); x3 = NULL;
+        freebn(ax); ax = NULL;
+        x3axm = bigmod(x3ax, a->curve->p);
+        if (!x3axm) goto error;
+        freebn(x3ax); x3ax = NULL;
+        x3axb = bigadd(x3axm, a->curve->b);
+        if (!x3axb) goto error;
+        freebn(x3axm); x3axm = NULL;
+        rhs = bigmod(x3axb, a->curve->p);
+        if (!rhs) goto error;
+        freebn(x3axb);
+
+        ret = bignum_cmp(lhs, rhs) ? 0 : 1;
+        freebn(lhs);
+        freebn(rhs);
+
+        return ret;
+
+      error:
+        if (x3) freebn(x3);
+        if (ax) freebn(ax);
+        if (x3ax) freebn(x3ax);
+        if (x3axm) freebn(x3axm);
+        if (x3axb) freebn(x3axb);
+        if (lhs) freebn(lhs);
+        return 0;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve point maths
+ */
+
+/* Returns 1 on success and 0 on memory error */
+static int ecp_normalise(struct ec_point *a)
+{
+    Bignum Z2, Z2inv, Z3, Z3inv, tx, ty;
+
+    /* In Jacobian Coordinates the triple (X, Y, Z) represents
+       the affine point (X / Z^2, Y / Z^3) */
+    if (!a) {
+        /* No point */
+        return 0;
+    }
+    if (a->infinity) {
+        /* Point is at infinity - i.e. normalised */
+        return 1;
+    } else if (!a->x || !a->y) {
+        /* No point defined */
+        return 0;
+    } else if (!a->z) {
+        /* Already normalised */
+        return 1;
+    }
+
+    Z2 = ecf_square(a->z, a->curve);
+    if (!Z2) {
+        return 0;
+    }
+    Z2inv = modinv(Z2, a->curve->p);
+    if (!Z2inv) {
+        freebn(Z2);
+        return 0;
+    }
+    tx = modmul(a->x, Z2inv, a->curve->p);
+    freebn(Z2inv);
+    if (!tx) {
+        freebn(Z2);
+        return 0;
+    }
+
+    Z3 = modmul(Z2, a->z, a->curve->p);
+    freebn(Z2);
+    if (!Z3) {
+        freebn(tx);
+        return 0;
+    }
+    Z3inv = modinv(Z3, a->curve->p);
+    freebn(Z3);
+    if (!Z3inv) {
+        freebn(tx);
+        return 0;
+    }
+    ty = modmul(a->y, Z3inv, a->curve->p);
+    freebn(Z3inv);
+    if (!ty) {
+        freebn(tx);
+        return 0;
+    }
+
+    freebn(a->x);
+    a->x = tx;
+    freebn(a->y);
+    a->y = ty;
+    freebn(a->z);
+    a->z = NULL;
+    return 1;
+}
+
+static struct ec_point *ecp_double(const struct ec_point *a, const int aminus3)
+{
+    Bignum S, M, outx, outy, outz;
+
+    if (a->infinity || bignum_cmp(a->y, Zero) == 0)
+    {
+        /* Identity */
+        return ec_point_new(a->curve, NULL, NULL, NULL, 1);
+    }
+
+    /* S = 4*X*Y^2 */
+    {
+        Bignum Y2, XY2, _2XY2;
+
+        Y2 = ecf_square(a->y, a->curve);
+        if (!Y2) {
+            return NULL;
+        }
+        XY2 = modmul(a->x, Y2, a->curve->p);
+        freebn(Y2);
+        if (!XY2) {
+            return NULL;
+        }
+
+        _2XY2 = ecf_double(XY2, a->curve);
+        freebn(XY2);
+        if (!_2XY2) {
+            return NULL;
+        }
+        S = ecf_double(_2XY2, a->curve);
+        freebn(_2XY2);
+        if (!S) {
+            return NULL;
+        }
+    }
+
+    /* Faster calculation if a = -3 */
+    if (aminus3) {
+        /* if a = -3, then M can also be calculated as M = 3*(X + Z^2)*(X - Z^2) */
+        Bignum Z2, XpZ2, XmZ2, second;
+
+        if (a->z == NULL) {
+            Z2 = copybn(One);
+        } else {
+            Z2 = ecf_square(a->z, a->curve);
+        }
+        if (!Z2) {
+            freebn(S);
+            return NULL;
+        }
+
+        XpZ2 = ecf_add(a->x, Z2, a->curve);
+        if (!XpZ2) {
+            freebn(S);
+            freebn(Z2);
+            return NULL;
+        }
+        XmZ2 = modsub(a->x, Z2, a->curve->p);
+        freebn(Z2);
+        if (!XpZ2) {
+            freebn(S);
+            freebn(XpZ2);
+            return NULL;
+        }
+
+        second = modmul(XpZ2, XmZ2, a->curve->p);
+        freebn(XpZ2);
+        freebn(XmZ2);
+        if (!second) {
+            freebn(S);
+            return NULL;
+        }
+
+        M = ecf_treble(second, a->curve);
+        freebn(second);
+        if (!M) {
+            freebn(S);
+            return NULL;
+        }
+    } else {
+        /* M = 3*X^2 + a*Z^4 */
+        Bignum _3X2, X2, aZ4;
+
+        if (a->z == NULL) {
+            aZ4 = copybn(a->curve->a);
+        } else {
+            Bignum Z2, Z4;
+
+            Z2 = ecf_square(a->z, a->curve);
+            if (!Z2) {
+                freebn(S);
+                return NULL;
+            }
+            Z4 = ecf_square(Z2, a->curve);
+            freebn(Z2);
+            if (!Z4) {
+                freebn(S);
+                return NULL;
+            }
+            aZ4 = modmul(a->curve->a, Z4, a->curve->p);
+            freebn(Z4);
+        }
+        if (!aZ4) {
+            freebn(S);
+            return NULL;
+        }
+
+        X2 = modmul(a->x, a->x, a->curve->p);
+        if (!X2) {
+            freebn(S);
+            freebn(aZ4);
+            return NULL;
+        }
+        _3X2 = ecf_treble(X2, a->curve);
+        freebn(X2);
+        if (!_3X2) {
+            freebn(S);
+            freebn(aZ4);
+            return NULL;
+        }
+        M = ecf_add(_3X2, aZ4, a->curve);
+        freebn(_3X2);
+        freebn(aZ4);
+        if (!M) {
+            freebn(S);
+            return NULL;
+        }
+    }
+
+    /* X' = M^2 - 2*S */
+    {
+        Bignum M2, _2S;
+
+        M2 = ecf_square(M, a->curve);
+        if (!M2) {
+            freebn(S);
+            freebn(M);
+            return NULL;
+        }
+
+        _2S = ecf_double(S, a->curve);
+        if (!_2S) {
+            freebn(M2);
+            freebn(S);
+            freebn(M);
+            return NULL;
+        }
+
+        outx = modsub(M2, _2S, a->curve->p);
+        freebn(M2);
+        freebn(_2S);
+        if (!outx) {
+            freebn(S);
+            freebn(M);
+            return NULL;
+        }
+    }
+
+    /* Y' = M*(S - X') - 8*Y^4 */
+    {
+        Bignum SX, MSX, Eight, Y2, Y4, _8Y4;
+
+        SX = modsub(S, outx, a->curve->p);
+        freebn(S);
+        if (!SX) {
+            freebn(M);
+            freebn(outx);
+            return NULL;
+        }
+        MSX = modmul(M, SX, a->curve->p);
+        freebn(SX);
+        freebn(M);
+        if (!MSX) {
+            freebn(outx);
+            return NULL;
+        }
+        Y2 = ecf_square(a->y, a->curve);
+        if (!Y2) {
+            freebn(outx);
+            freebn(MSX);
+            return NULL;
+        }
+        Y4 = ecf_square(Y2, a->curve);
+        freebn(Y2);
+        if (!Y4) {
+            freebn(outx);
+            freebn(MSX);
+            return NULL;
+        }
+        Eight = bignum_from_long(8);
+        if (!Eight) {
+            freebn(outx);
+            freebn(MSX);
+            freebn(Y4);
+            return NULL;
+        }
+        _8Y4 = modmul(Eight, Y4, a->curve->p);
+        freebn(Eight);
+        freebn(Y4);
+        if (!_8Y4) {
+            freebn(outx);
+            freebn(MSX);
+            return NULL;
+        }
+        outy = modsub(MSX, _8Y4, a->curve->p);
+        freebn(MSX);
+        freebn(_8Y4);
+        if (!outy) {
+            freebn(outx);
+            return NULL;
+        }
+    }
+
+    /* Z' = 2*Y*Z */
+    {
+        Bignum YZ;
+
+        if (a->z == NULL) {
+            YZ = copybn(a->y);
+        } else {
+            YZ = modmul(a->y, a->z, a->curve->p);
+        }
+        if (!YZ) {
+            freebn(outx);
+            freebn(outy);
+            return NULL;
+        }
+
+        outz = ecf_double(YZ, a->curve);
+        freebn(YZ);
+        if (!outz) {
+            freebn(outx);
+            freebn(outy);
+            return NULL;
+        }
+    }
+
+    return ec_point_new(a->curve, outx, outy, outz, 0);
+}
+
+static struct ec_point *ecp_add(const struct ec_point *a,
+                                const struct ec_point *b,
+                                const int aminus3)
+{
+    Bignum U1, U2, S1, S2, outx, outy, outz;
+
+    /* Check if multiplying by infinity */
+    if (a->infinity) return ec_point_copy(b);
+    if (b->infinity) return ec_point_copy(a);
+
+    /* U1 = X1*Z2^2 */
+    /* S1 = Y1*Z2^3 */
+    if (b->z) {
+        Bignum Z2, Z3;
+
+        Z2 = ecf_square(b->z, a->curve);
+        if (!Z2) {
+            return NULL;
+        }
+        U1 = modmul(a->x, Z2, a->curve->p);
+        if (!U1) {
+            freebn(Z2);
+            return NULL;
+        }
+        Z3 = modmul(Z2, b->z, a->curve->p);
+        freebn(Z2);
+        if (!Z3) {
+            freebn(U1);
+            return NULL;
+        }
+        S1 = modmul(a->y, Z3, a->curve->p);
+        freebn(Z3);
+        if (!S1) {
+            freebn(U1);
+            return NULL;
+        }
+    } else {
+        U1 = copybn(a->x);
+        if (!U1) {
+            return NULL;
+        }
+        S1 = copybn(a->y);
+        if (!S1) {
+            freebn(U1);
+            return NULL;
+        }
+    }
+
+    /* U2 = X2*Z1^2 */
+    /* S2 = Y2*Z1^3 */
+    if (a->z) {
+        Bignum Z2, Z3;
+
+        Z2 = ecf_square(a->z, b->curve);
+        if (!Z2) {
+            freebn(U1);
+            freebn(S1);
+            return NULL;
+        }
+        U2 = modmul(b->x, Z2, b->curve->p);
+        if (!U2) {
+            freebn(U1);
+            freebn(S1);
+            freebn(Z2);
+            return NULL;
+        }
+        Z3 = modmul(Z2, a->z, b->curve->p);
+        freebn(Z2);
+        if (!Z3) {
+            freebn(U1);
+            freebn(S1);
+            freebn(U2);
+            return NULL;
+        }
+        S2 = modmul(b->y, Z3, b->curve->p);
+        freebn(Z3);
+        if (!S2) {
+            freebn(U1);
+            freebn(S1);
+            freebn(U2);
+            return NULL;
+        }
+    } else {
+        U2 = copybn(b->x);
+        if (!U2) {
+            freebn(U1);
+            freebn(S1);
+            return NULL;
+        }
+        S2 = copybn(b->y);
+        if (!S2) {
+            freebn(U1);
+            freebn(S1);
+            freebn(U2);
+            return NULL;
+        }
+    }
+
+    /* Check if multiplying by self */
+    if (bignum_cmp(U1, U2) == 0)
+    {
+        freebn(U1);
+        freebn(U2);
+        if (bignum_cmp(S1, S2) == 0)
+        {
+            freebn(S1);
+            freebn(S2);
+            return ecp_double(a, aminus3);
+        }
+        else
+        {
+            freebn(S1);
+            freebn(S2);
+            /* Infinity */
+            return ec_point_new(a->curve, NULL, NULL, NULL, 1);
+        }
+    }
+
+    {
+        Bignum H, R, UH2, H3;
+
+        /* H = U2 - U1 */
+        H = modsub(U2, U1, a->curve->p);
+        freebn(U2);
+        if (!H) {
+            freebn(U1);
+            freebn(S1);
+            freebn(S2);
+            return NULL;
+        }
+
+        /* R = S2 - S1 */
+        R = modsub(S2, S1, a->curve->p);
+        freebn(S2);
+        if (!R) {
+            freebn(H);
+            freebn(S1);
+            freebn(U1);
+            return NULL;
+        }
+
+        /* X3 = R^2 - H^3 - 2*U1*H^2 */
+        {
+            Bignum R2, H2, _2UH2, first;
+
+            H2 = ecf_square(H, a->curve);
+            if (!H2) {
+                freebn(U1);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            UH2 = modmul(U1, H2, a->curve->p);
+            freebn(U1);
+            if (!UH2) {
+                freebn(H2);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            H3 = modmul(H2, H, a->curve->p);
+            freebn(H2);
+            if (!H3) {
+                freebn(UH2);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            R2 = ecf_square(R, a->curve);
+            if (!R2) {
+                freebn(H3);
+                freebn(UH2);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            _2UH2 = ecf_double(UH2, a->curve);
+            if (!_2UH2) {
+                freebn(R2);
+                freebn(H3);
+                freebn(UH2);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            first = modsub(R2, H3, a->curve->p);
+            freebn(R2);
+            if (!first) {
+                freebn(H3);
+                freebn(_2UH2);
+                freebn(UH2);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            outx = modsub(first, _2UH2, a->curve->p);
+            freebn(first);
+            freebn(_2UH2);
+            if (!outx) {
+                freebn(H3);
+                freebn(UH2);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+        }
+
+        /* Y3 = R*(U1*H^2 - X3) - S1*H^3 */
+        {
+            Bignum RUH2mX, UH2mX, SH3;
+
+            UH2mX = modsub(UH2, outx, a->curve->p);
+            freebn(UH2);
+            if (!UH2mX) {
+                freebn(outx);
+                freebn(H3);
+                freebn(S1);
+                freebn(H);
+                freebn(R);
+                return NULL;
+            }
+            RUH2mX = modmul(R, UH2mX, a->curve->p);
+            freebn(UH2mX);
+            freebn(R);
+            if (!RUH2mX) {
+                freebn(outx);
+                freebn(H3);
+                freebn(S1);
+                freebn(H);
+                return NULL;
+            }
+            SH3 = modmul(S1, H3, a->curve->p);
+            freebn(S1);
+            freebn(H3);
+            if (!SH3) {
+                freebn(RUH2mX);
+                freebn(outx);
+                freebn(H);
+                return NULL;
+            }
+
+            outy = modsub(RUH2mX, SH3, a->curve->p);
+            freebn(RUH2mX);
+            freebn(SH3);
+            if (!outy) {
+                freebn(outx);
+                freebn(H);
+                return NULL;
+            }
+        }
+
+        /* Z3 = H*Z1*Z2 */
+        if (a->z && b->z) {
+            Bignum ZZ;
+
+            ZZ = modmul(a->z, b->z, a->curve->p);
+            if (!ZZ) {
+                freebn(outx);
+                freebn(outy);
+                freebn(H);
+                return NULL;
+            }
+            outz = modmul(H, ZZ, a->curve->p);
+            freebn(H);
+            freebn(ZZ);
+            if (!outz) {
+                freebn(outx);
+                freebn(outy);
+                return NULL;
+            }
+        } else if (a->z) {
+            outz = modmul(H, a->z, a->curve->p);
+            freebn(H);
+            if (!outz) {
+                freebn(outx);
+                freebn(outy);
+                return NULL;
+            }
+        } else if (b->z) {
+            outz = modmul(H, b->z, a->curve->p);
+            freebn(H);
+            if (!outz) {
+                freebn(outx);
+                freebn(outy);
+                return NULL;
+            }
+        } else {
+            outz = H;
+        }
+    }
+
+    return ec_point_new(a->curve, outx, outy, outz, 0);
+}
+
+static struct ec_point *ecp_mul_(const struct ec_point *a, const Bignum b, int aminus3)
+{
+    struct ec_point *A, *ret;
+    int bits, i;
+
+    A = ec_point_copy(a);
+    ret = ec_point_new(a->curve, NULL, NULL, NULL, 1);
+
+    bits = bignum_bitcount(b);
+    for (i = 0; ret != NULL && A != NULL && i < bits; ++i)
+    {
+        if (bignum_bit(b, i))
+        {
+            struct ec_point *tmp = ecp_add(ret, A, aminus3);
+            ec_point_free(ret);
+            ret = tmp;
+        }
+        if (i+1 != bits)
+        {
+            struct ec_point *tmp = ecp_double(A, aminus3);
+            ec_point_free(A);
+            A = tmp;
+        }
+    }
+
+    if (!A) {
+        ec_point_free(ret);
+        ret = NULL;
+    } else {
+        ec_point_free(A);
+    }
+
+    return ret;
+}
+
+/* Not static because it is used by sshecdsag.c to generate a new key */
+struct ec_point *ecp_mul(const struct ec_point *a, const Bignum b)
+{
+    struct ec_point *ret = ecp_mul_(a, b, ec_aminus3(a->curve));
+
+    if (!ecp_normalise(ret)) {
+        ec_point_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+static struct ec_point *ecp_summul(const Bignum a, const Bignum b,
+                                   const struct ec_point *point)
+{
+    struct ec_point *aG, *bP, *ret;
+    int aminus3 = ec_aminus3(point->curve);
+
+    aG = ecp_mul_(&point->curve->G, a, aminus3);
+    if (!aG) return NULL;
+    bP = ecp_mul_(point, b, aminus3);
+    if (!bP) {
+        ec_point_free(aG);
+        return NULL;
+    }
+
+    ret = ecp_add(aG, bP, aminus3);
+
+    ec_point_free(aG);
+    ec_point_free(bP);
+
+    if (!ecp_normalise(ret)) {
+        ec_point_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Basic sign and verify routines
+ */
+
+static int _ecdsa_verify(const struct ec_point *publicKey,
+                         const unsigned char *data, const int dataLen,
+                         const Bignum r, const Bignum s)
+{
+    int z_bits, n_bits;
+    Bignum z;
+    int valid = 0;
+
+    /* Sanity checks */
+    if (bignum_cmp(r, Zero) == 0 || bignum_cmp(r, publicKey->curve->n) >= 0
+        || bignum_cmp(s, Zero) == 0 || bignum_cmp(s, publicKey->curve->n) >= 0)
+    {
+        return 0;
+    }
+
+    /* z = left most bitlen(curve->n) of data */
+    z = bignum_from_bytes(data, dataLen);
+    if (!z) return 0;
+    n_bits = bignum_bitcount(publicKey->curve->n);
+    z_bits = bignum_bitcount(z);
+    if (z_bits > n_bits)
+    {
+        Bignum tmp = bignum_rshift(z, z_bits - n_bits);
+        freebn(z);
+        z = tmp;
+        if (!z) return 0;
+    }
+
+    /* Ensure z in range of n */
+    {
+        Bignum tmp = bigmod(z, publicKey->curve->n);
+        freebn(z);
+        z = tmp;
+        if (!z) return 0;
+    }
+
+    /* Calculate signature */
+    {
+        Bignum w, x, u1, u2;
+        struct ec_point *tmp;
+
+        w = modinv(s, publicKey->curve->n);
+        if (!w) {
+            freebn(z);
+            return 0;
+        }
+        u1 = modmul(z, w, publicKey->curve->n);
+        if (!u1) {
+            freebn(z);
+            freebn(w);
+            return 0;
+        }
+        u2 = modmul(r, w, publicKey->curve->n);
+        freebn(w);
+        if (!u2) {
+            freebn(z);
+            freebn(u1);
+            return 0;
+        }
+
+        tmp = ecp_summul(u1, u2, publicKey);
+        freebn(u1);
+        freebn(u2);
+        if (!tmp) {
+            freebn(z);
+            return 0;
+        }
+
+        x = bigmod(tmp->x, publicKey->curve->n);
+        ec_point_free(tmp);
+        if (!x) {
+            freebn(z);
+            return 0;
+        }
+
+        valid = (bignum_cmp(r, x) == 0) ? 1 : 0;
+        freebn(x);
+    }
+
+    freebn(z);
+
+    return valid;
+}
+
+static void _ecdsa_sign(const Bignum privateKey, const struct ec_curve *curve,
+                        const unsigned char *data, const int dataLen,
+                        Bignum *r, Bignum *s)
+{
+    unsigned char digest[20];
+    int z_bits, n_bits;
+    Bignum z, k;
+    struct ec_point *kG;
+
+    *r = NULL;
+    *s = NULL;
+
+    /* z = left most bitlen(curve->n) of data */
+    z = bignum_from_bytes(data, dataLen);
+    if (!z) return;
+    n_bits = bignum_bitcount(curve->n);
+    z_bits = bignum_bitcount(z);
+    if (z_bits > n_bits)
+    {
+        Bignum tmp;
+        tmp = bignum_rshift(z, z_bits - n_bits);
+        freebn(z);
+        z = tmp;
+        if (!z) return;
+    }
+
+    /* Generate k between 1 and curve->n, using the same deterministic
+     * k generation system we use for conventional DSA. */
+    SHA_Simple(data, dataLen, digest);
+    k = dss_gen_k("ECDSA deterministic k generator", curve->n, privateKey,
+                  digest, sizeof(digest));
+    if (!k) return;
+
+    kG = ecp_mul(&curve->G, k);
+    if (!kG) {
+        freebn(z);
+        freebn(k);
+        return;
+    }
+
+    /* r = kG.x mod n */
+    *r = bigmod(kG->x, curve->n);
+    ec_point_free(kG);
+    if (!*r) {
+        freebn(z);
+        freebn(k);
+        return;
+    }
+
+    /* s = (z + r * priv)/k mod n */
+    {
+        Bignum rPriv, zMod, first, firstMod, kInv;
+        rPriv = modmul(*r, privateKey, curve->n);
+        if (!rPriv) {
+            freebn(*r);
+            freebn(z);
+            freebn(k);
+            return;
+        }
+        zMod = bigmod(z, curve->n);
+        freebn(z);
+        if (!zMod) {
+            freebn(rPriv);
+            freebn(*r);
+            freebn(k);
+            return;
+        }
+        first = bigadd(rPriv, zMod);
+        freebn(rPriv);
+        freebn(zMod);
+        if (!first) {
+            freebn(*r);
+            freebn(k);
+            return;
+        }
+        firstMod = bigmod(first, curve->n);
+        freebn(first);
+        if (!firstMod) {
+            freebn(*r);
+            freebn(k);
+            return;
+        }
+        kInv = modinv(k, curve->n);
+        freebn(k);
+        if (!kInv) {
+            freebn(firstMod);
+            freebn(*r);
+            return;
+        }
+        *s = modmul(firstMod, kInv, curve->n);
+        freebn(firstMod);
+        freebn(kInv);
+        if (!*s) {
+            freebn(*r);
+            return;
+        }
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Misc functions
+ */
+
+static void getstring(char **data, int *datalen, char **p, int *length)
+{
+    *p = NULL;
+    if (*datalen < 4)
+        return;
+    *length = toint(GET_32BIT(*data));
+    if (*length < 0)
+        return;
+    *datalen -= 4;
+    *data += 4;
+    if (*datalen < *length)
+        return;
+    *p = *data;
+    *data += *length;
+    *datalen -= *length;
+}
+
+static Bignum getmp(char **data, int *datalen)
+{
+    char *p;
+    int length;
+
+    getstring(data, datalen, &p, &length);
+    if (!p)
+        return NULL;
+    if (p[0] & 0x80)
+        return NULL;                   /* negative mp */
+    return bignum_from_bytes((unsigned char *)p, length);
+}
+
+static int decodepoint(char *p, int length, struct ec_point *point)
+{
+    if (length < 1 || p[0] != 0x04) /* Only support uncompressed point */
+        return 0;
+    /* Skip compression flag */
+    ++p;
+    --length;
+    /* The two values must be equal length */
+    if (length % 2 != 0) {
+        point->x = NULL;
+        point->y = NULL;
+        point->z = NULL;
+        return 0;
+    }
+    length = length / 2;
+    point->x = bignum_from_bytes((unsigned char *)p, length);
+    if (!point->x) return 0;
+    p += length;
+    point->y = bignum_from_bytes((unsigned char *)p, length);
+    if (!point->y) {
+        freebn(point->x);
+        point->x = NULL;
+        return 0;
+    }
+    point->z = NULL;
+
+    /* Verify the point is on the curve */
+    if (!ec_point_verify(point)) {
+        ec_point_free(point);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int getmppoint(char **data, int *datalen, struct ec_point *point)
+{
+    char *p;
+    int length;
+
+    getstring(data, datalen, &p, &length);
+    if (!p) return 0;
+    return decodepoint(p, length, point);
+}
+
+/* ----------------------------------------------------------------------
+ * Exposed ECDSA interface
+ */
+
+static void ecdsa_freekey(void *key)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    if (!ec) return;
+
+    if (ec->publicKey.x)
+        freebn(ec->publicKey.x);
+    if (ec->publicKey.y)
+        freebn(ec->publicKey.y);
+    if (ec->publicKey.z)
+        freebn(ec->publicKey.z);
+    if (ec->privateKey)
+        freebn(ec->privateKey);
+    sfree(ec);
+}
+
+static void *ecdsa_newkey(char *data, int len)
+{
+    char *p;
+    int slen;
+    struct ec_key *ec;
+    struct ec_curve *curve;
+
+    getstring(&data, &len, &p, &slen);
+
+    if (!p || slen < 11 || memcmp(p, "ecdsa-sha2-", 11)) {
+        return NULL;
+    }
+    curve = ec_name_to_curve(p+11, slen-11);
+    if (!curve) return NULL;
+
+    getstring(&data, &len, &p, &slen);
+
+    if (curve != ec_name_to_curve(p, slen)) return NULL;
+
+    ec = snew(struct ec_key);
+
+    ec->publicKey.curve = curve;
+    ec->publicKey.infinity = 0;
+    ec->publicKey.x = NULL;
+    ec->publicKey.y = NULL;
+    ec->publicKey.z = NULL;
+    if (!getmppoint(&data, &len, &ec->publicKey)) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+    ec->privateKey = NULL;
+
+    if (!ec->publicKey.x || !ec->publicKey.y ||
+        bignum_cmp(ec->publicKey.x, curve->p) >= 0 ||
+        bignum_cmp(ec->publicKey.y, curve->p) >= 0)
+    {
+        ecdsa_freekey(ec);
+        ec = NULL;
+    }
+
+    return ec;
+}
+
+static char *ecdsa_fmtkey(void *key)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    char *p;
+    int len, i, pos, nibbles;
+    static const char hex[] = "0123456789abcdef";
+    if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
+        return NULL;
+
+    pos = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+    if (pos == 0) return NULL;
+
+    len = 4 + 2 + 1;                  /* 2 x "0x", punctuation, \0 */
+    len += pos; /* Curve name */
+    len += 4 * (bignum_bitcount(ec->publicKey.x) + 15) / 16;
+    len += 4 * (bignum_bitcount(ec->publicKey.y) + 15) / 16;
+    p = snewn(len, char);
+
+    pos = ec_curve_to_name(ec->publicKey.curve, (unsigned char*)p, pos);
+    pos += sprintf(p + pos, ",0x");
+    nibbles = (3 + bignum_bitcount(ec->publicKey.x)) / 4;
+    if (nibbles < 1)
+        nibbles = 1;
+    for (i = nibbles; i--;) {
+        p[pos++] =
+            hex[(bignum_byte(ec->publicKey.x, i / 2) >> (4 * (i % 2))) & 0xF];
+    }
+    pos += sprintf(p + pos, ",0x");
+    nibbles = (3 + bignum_bitcount(ec->publicKey.y)) / 4;
+    if (nibbles < 1)
+        nibbles = 1;
+    for (i = nibbles; i--;) {
+        p[pos++] =
+            hex[(bignum_byte(ec->publicKey.y, i / 2) >> (4 * (i % 2))) & 0xF];
+    }
+    p[pos] = '\0';
+    return p;
+}
+
+static unsigned char *ecdsa_public_blob(void *key, int *len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    int pointlen, bloblen, namelen;
+    int i;
+    unsigned char *blob, *p;
+
+    namelen = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+    if (namelen == 0) return NULL;
+
+    pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+
+    /*
+     * string "ecdsa-sha2-<name>", string "<name>", 0x04 point x, y.
+     */
+    bloblen = 4 + 11 + namelen + 4 + namelen + 4 + 1 + (pointlen * 2);
+    blob = snewn(bloblen, unsigned char);
+
+    p = blob;
+    PUT_32BIT(p, 11 + namelen);
+    p += 4;
+    memcpy(p, "ecdsa-sha2-", 11);
+    p += 11;
+    p += ec_curve_to_name(ec->publicKey.curve, p, namelen);
+    PUT_32BIT(p, namelen);
+    p += 4;
+    p += ec_curve_to_name(ec->publicKey.curve, p, namelen);
+    PUT_32BIT(p, (2 * pointlen) + 1);
+    p += 4;
+    *p++ = 0x04;
+    for (i = pointlen; i--;)
+        *p++ = bignum_byte(ec->publicKey.x, i);
+    for (i = pointlen; i--;)
+        *p++ = bignum_byte(ec->publicKey.y, i);
+
+    assert(p == blob + bloblen);
+    *len = bloblen;
+
+    return blob;
+}
+
+static unsigned char *ecdsa_private_blob(void *key, int *len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    int keylen, bloblen;
+    int i;
+    unsigned char *blob, *p;
+
+    if (!ec->privateKey) return NULL;
+
+    keylen = (bignum_bitcount(ec->privateKey) + 8) / 8;
+
+    /*
+     * mpint privateKey. Total 4 + keylen.
+     */
+    bloblen = 4 + keylen;
+    blob = snewn(bloblen, unsigned char);
+
+    p = blob;
+    PUT_32BIT(p, keylen);
+    p += 4;
+    for (i = keylen; i--;)
+        *p++ = bignum_byte(ec->privateKey, i);
+
+    assert(p == blob + bloblen);
+    *len = bloblen;
+    return blob;
+}
+
+static void *ecdsa_createkey(unsigned char *pub_blob, int pub_len,
+                             unsigned char *priv_blob, int priv_len)
+{
+    struct ec_key *ec;
+    struct ec_point *publicKey;
+    char *pb = (char *) priv_blob;
+
+    ec = (struct ec_key*)ecdsa_newkey((char *) pub_blob, pub_len);
+    if (!ec) {
+        return NULL;
+    }
+
+    ec->privateKey = getmp(&pb, &priv_len);
+    if (!ec->privateKey) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    /* Check that private key generates public key */
+    publicKey = ecp_mul(&ec->publicKey.curve->G, ec->privateKey);
+
+    if (!publicKey ||
+        bignum_cmp(publicKey->x, ec->publicKey.x) ||
+        bignum_cmp(publicKey->y, ec->publicKey.y))
+    {
+        ecdsa_freekey(ec);
+        ec = NULL;
+    }
+    ec_point_free(publicKey);
+
+    return ec;
+}
+
+static void *ecdsa_openssh_createkey(unsigned char **blob, int *len)
+{
+    char **b = (char **) blob;
+    char *p;
+    int slen;
+    struct ec_key *ec;
+    struct ec_curve *curve;
+    struct ec_point *publicKey;
+
+    getstring(b, len, &p, &slen);
+
+    if (!p) {
+        return NULL;
+    }
+    curve = ec_name_to_curve(p, slen);
+    if (!curve) return NULL;
+
+    ec = snew(struct ec_key);
+
+    ec->publicKey.curve = curve;
+    ec->publicKey.infinity = 0;
+    ec->publicKey.x = NULL;
+    ec->publicKey.y = NULL;
+    ec->publicKey.z = NULL;
+    if (!getmppoint(b, len, &ec->publicKey)) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+    ec->privateKey = NULL;
+
+    if (!ec->publicKey.x || !ec->publicKey.y ||
+        bignum_cmp(ec->publicKey.x, curve->p) >= 0 ||
+        bignum_cmp(ec->publicKey.y, curve->p) >= 0)
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    ec->privateKey = getmp(b, len);
+    if (ec->privateKey == NULL)
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    /* Now check that the private key makes the public key */
+    publicKey = ecp_mul(&ec->publicKey.curve->G, ec->privateKey);
+    if (!publicKey)
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    if (bignum_cmp(ec->publicKey.x, publicKey->x) ||
+        bignum_cmp(ec->publicKey.y, publicKey->y))
+    {
+        /* Private key doesn't make the public key on the given curve */
+        ecdsa_freekey(ec);
+        ec_point_free(publicKey);
+    }
+
+    ec_point_free(publicKey);
+
+    return ec;
+}
+
+static int ecdsa_openssh_fmtkey(void *key, unsigned char *blob, int len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+
+    int pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+
+    int namelen = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+
+    int bloblen =
+        4 + namelen /* <LEN> nistpXXX */
+        + 4 + 1 + (pointlen * 2) /* <LEN> 0x04 pX pY */
+        + ssh2_bignum_length(ec->privateKey);
+
+    int i;
+
+    if (bloblen > len)
+        return bloblen;
+
+    bloblen = 0;
+
+    PUT_32BIT(blob+bloblen, namelen);
+    bloblen += 4;
+
+    bloblen += ec_curve_to_name(ec->publicKey.curve, blob+bloblen, namelen);
+
+    PUT_32BIT(blob+bloblen, 1 + (pointlen * 2));
+    bloblen += 4;
+    blob[bloblen++] = 0x04;
+    for (i = pointlen; i--; )
+        blob[bloblen++] = bignum_byte(ec->publicKey.x, i);
+    for (i = pointlen; i--; )
+        blob[bloblen++] = bignum_byte(ec->publicKey.y, i);
+
+    pointlen = (bignum_bitcount(ec->privateKey) + 8) / 8;
+    PUT_32BIT(blob+bloblen, pointlen);
+    bloblen += 4;
+    for (i = pointlen; i--; )
+        blob[bloblen++] = bignum_byte(ec->privateKey, i);
+
+    return bloblen;
+}
+
+static int ecdsa_pubkey_bits(void *blob, int len)
+{
+    struct ec_key *ec;
+    int ret;
+
+    ec = (struct ec_key*)ecdsa_newkey((char *) blob, len);
+    if (!ec)
+        return -1;
+    ret = ec->publicKey.curve->fieldBits;
+    ecdsa_freekey(ec);
+
+    return ret;
+}
+
+static char *ecdsa_fingerprint(void *key)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    struct MD5Context md5c;
+    unsigned char digest[16], lenbuf[4];
+    char *ret;
+    unsigned char *name;
+    int pointlen, namelen, i, j;
+
+    namelen = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+    name = snewn(namelen, unsigned char);
+    ec_curve_to_name(ec->publicKey.curve, name, namelen);
+
+    MD5Init(&md5c);
+
+    PUT_32BIT(lenbuf, namelen + 11);
+    MD5Update(&md5c, lenbuf, 4);
+    MD5Update(&md5c, (const unsigned char *)"ecdsa-sha2-", 11);
+    MD5Update(&md5c, name, namelen);
+
+    PUT_32BIT(lenbuf, namelen);
+    MD5Update(&md5c, lenbuf, 4);
+    MD5Update(&md5c, name, namelen);
+
+    pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+    PUT_32BIT(lenbuf, 1 + (pointlen * 2));
+    MD5Update(&md5c, lenbuf, 4);
+    MD5Update(&md5c, (const unsigned char *)"\x04", 1);
+    for (i = pointlen; i--; ) {
+        unsigned char c = bignum_byte(ec->publicKey.x, i);
+        MD5Update(&md5c, &c, 1);
+    }
+    for (i = pointlen; i--; ) {
+        unsigned char c = bignum_byte(ec->publicKey.y, i);
+        MD5Update(&md5c, &c, 1);
+    }
+
+    MD5Final(digest, &md5c);
+
+    ret = snewn(11 + namelen + 1 + (16 * 3), char);
+
+    i = 11;
+    memcpy(ret, "ecdsa-sha2-", 11);
+    memcpy(ret+i, name, namelen);
+    i += namelen;
+    sfree(name);
+    ret[i++] = ' ';
+    for (j = 0; j < 16; j++)
+        i += sprintf(ret + i, "%s%02x", j ? ":" : "", digest[j]);
+
+    return ret;
+}
+
+static int ecdsa_verifysig(void *key, char *sig, int siglen,
+                           char *data, int datalen)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    char *p;
+    int slen;
+    unsigned char digest[512 / 8];
+    int digestLen;
+    Bignum r, s;
+    int ret;
+
+    if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
+        return 0;
+
+    /* Check the signature curve matches the key curve */
+    getstring(&sig, &siglen, &p, &slen);
+    if (!p || slen < 11 || memcmp(p, "ecdsa-sha2-", 11)) {
+        return 0;
+    }
+    if (ec->publicKey.curve != ec_name_to_curve(p+11, slen-11)) {
+        return 0;
+    }
+
+    getstring(&sig, &siglen, &p, &slen);
+    r = getmp(&p, &slen);
+    if (!r) return 0;
+    s = getmp(&p, &slen);
+    if (!s) {
+        freebn(r);
+        return 0;
+    }
+
+    /* Perform correct hash function depending on curve size */
+    if (ec->publicKey.curve->fieldBits <= 256) {
+        SHA256_Simple(data, datalen, digest);
+        digestLen = 256 / 8;
+    } else if (ec->publicKey.curve->fieldBits <= 384) {
+        SHA384_Simple(data, datalen, digest);
+        digestLen = 384 / 8;
+    } else {
+        SHA512_Simple(data, datalen, digest);
+        digestLen = 512 / 8;
+    }
+
+    /* Verify the signature */
+    if (!_ecdsa_verify(&ec->publicKey, digest, digestLen, r, s)) {
+        ret = 0;
+    } else {
+        ret = 1;
+    }
+
+    freebn(r);
+    freebn(s);
+
+    return ret;
+}
+
+static unsigned char *ecdsa_sign(void *key, char *data, int datalen,
+                                 int *siglen)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    unsigned char digest[512 / 8];
+    int digestLen;
+    Bignum r = NULL, s = NULL;
+    unsigned char *buf, *p;
+    int rlen, slen, namelen;
+    int i;
+
+    if (!ec->privateKey || !ec->publicKey.curve) {
+        return NULL;
+    }
+
+    /* Perform correct hash function depending on curve size */
+    if (ec->publicKey.curve->fieldBits <= 256) {
+        SHA256_Simple(data, datalen, digest);
+        digestLen = 256 / 8;
+    } else if (ec->publicKey.curve->fieldBits <= 384) {
+        SHA384_Simple(data, datalen, digest);
+        digestLen = 384 / 8;
+    } else {
+        SHA512_Simple(data, datalen, digest);
+        digestLen = 512 / 8;
+    }
+
+    /* Do the signature */
+    _ecdsa_sign(ec->privateKey, ec->publicKey.curve, digest, digestLen, &r, &s);
+    if (!r || !s) {
+        if (r) freebn(r);
+        if (s) freebn(s);
+        return NULL;
+    }
+
+    rlen = (bignum_bitcount(r) + 8) / 8;
+    slen = (bignum_bitcount(s) + 8) / 8;
+
+    namelen = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+
+    /* Format the output */
+    *siglen = 8+11+namelen+rlen+slen+8;
+    buf = snewn(*siglen, unsigned char);
+    p = buf;
+    PUT_32BIT(p, 11+namelen);
+    p += 4;
+    memcpy(p, "ecdsa-sha2-", 11);
+    p += 11;
+    p += ec_curve_to_name(ec->publicKey.curve, p, namelen);
+    PUT_32BIT(p, rlen + slen + 8);
+    p += 4;
+    PUT_32BIT(p, rlen);
+    p += 4;
+    for (i = rlen; i--;)
+        *p++ = bignum_byte(r, i);
+    PUT_32BIT(p, slen);
+    p += 4;
+    for (i = slen; i--;)
+        *p++ = bignum_byte(s, i);
+
+    return buf;
+}
+
+const struct ssh_signkey ssh_ecdsa_nistp256 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ecdsa_openssh_createkey,
+    ecdsa_openssh_fmtkey,
+    ecdsa_pubkey_bits,
+    ecdsa_fingerprint,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ecdsa-sha2-nistp256",
+    "ecdsa-sha2-nistp256",
+};
+
+const struct ssh_signkey ssh_ecdsa_nistp384 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ecdsa_openssh_createkey,
+    ecdsa_openssh_fmtkey,
+    ecdsa_pubkey_bits,
+    ecdsa_fingerprint,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ecdsa-sha2-nistp384",
+    "ecdsa-sha2-nistp384",
+};
+
+const struct ssh_signkey ssh_ecdsa_nistp521 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ecdsa_openssh_createkey,
+    ecdsa_openssh_fmtkey,
+    ecdsa_pubkey_bits,
+    ecdsa_fingerprint,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ecdsa-sha2-nistp521",
+    "ecdsa-sha2-nistp521",
+};
+
+/* ----------------------------------------------------------------------
+ * Exposed ECDH interface
+ */
+
+static Bignum ecdh_calculate(const Bignum private,
+                             const struct ec_point *public)
+{
+    struct ec_point *p;
+    Bignum ret;
+    p = ecp_mul(public, private);
+    if (!p) return NULL;
+    ret = p->x;
+    p->x = NULL;
+    ec_point_free(p);
+    return ret;
+}
+
+void *ssh_ecdhkex_newkey(struct ec_curve *curve)
+{
+    struct ec_key *key = snew(struct ec_key);
+    struct ec_point *publicKey;
+    key->publicKey.curve = curve;
+    key->privateKey = bignum_random_in_range(One, key->publicKey.curve->n);
+    if (!key->privateKey) {
+        sfree(key);
+        return NULL;
+    }
+    publicKey = ecp_mul(&key->publicKey.curve->G, key->privateKey);
+    if (!publicKey) {
+        freebn(key->privateKey);
+        sfree(key);
+        return NULL;
+    }
+    key->publicKey.x = publicKey->x;
+    key->publicKey.y = publicKey->y;
+    key->publicKey.z = NULL;
+    sfree(publicKey);
+    return key;
+}
+
+char *ssh_ecdhkex_getpublic(void *key, int *len)
+{
+    struct ec_key *ec = (struct ec_key*)key;
+    char *point, *p;
+    int i;
+    int pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+
+    *len = 1 + pointlen * 2;
+    point = (char*)snewn(*len, char);
+
+    p = point;
+    *p++ = 0x04;
+    for (i = pointlen; i--;)
+        *p++ = bignum_byte(ec->publicKey.x, i);
+    for (i = pointlen; i--;)
+        *p++ = bignum_byte(ec->publicKey.y, i);
+
+    return point;
+}
+
+Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen)
+{
+    struct ec_key *ec = (struct ec_key*) key;
+    struct ec_point remote;
+
+    remote.curve = ec->publicKey.curve;
+    remote.infinity = 0;
+    if (!decodepoint(remoteKey, remoteKeyLen, &remote)) {
+        return NULL;
+    }
+
+    return ecdh_calculate(ec->privateKey, &remote);
+}
+
+void ssh_ecdhkex_freekey(void *key)
+{
+    ecdsa_freekey(key);
+}
+
+static const struct ssh_kex ssh_ec_kex_nistp256 = {
+    "ecdh-sha2-nistp256", NULL, KEXTYPE_ECDH, NULL, NULL, 0, 0, &ssh_sha256
+};
+
+static const struct ssh_kex ssh_ec_kex_nistp384 = {
+    "ecdh-sha2-nistp384", NULL, KEXTYPE_ECDH, NULL, NULL, 0, 0, &ssh_sha384
+};
+
+static const struct ssh_kex ssh_ec_kex_nistp521 = {
+    "ecdh-sha2-nistp521", NULL, KEXTYPE_ECDH, NULL, NULL, 0, 0, &ssh_sha512
+};
+
+static const struct ssh_kex *const ec_kex_list[] = {
+    &ssh_ec_kex_nistp256,
+    &ssh_ec_kex_nistp384,
+    &ssh_ec_kex_nistp521
+};
+
+const struct ssh_kexes ssh_ecdh_kex = {
+    sizeof(ec_kex_list) / sizeof(*ec_kex_list),
+    ec_kex_list
+};
diff --git a/sshecdsag.c b/sshecdsag.c
new file mode 100644 (file)
index 0000000..049967d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * EC key generation.
+ */
+
+#include "ssh.h"
+
+/* Forward reference from sshecc.c */
+struct ec_point *ecp_mul(const struct ec_point *a, const Bignum b);
+
+int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
+                void *pfnparam)
+{
+    struct ec_point *publicKey;
+
+    if (bits == 256) {
+        key->publicKey.curve = ec_p256();
+    } else if (bits == 384) {
+        key->publicKey.curve = ec_p384();
+    } else if (bits == 521) {
+        key->publicKey.curve = ec_p521();
+    } else {
+        return 0;
+    }
+
+    key->privateKey = bignum_random_in_range(One, key->publicKey.curve->n);
+    if (!key->privateKey) return 0;
+
+    publicKey = ecp_mul(&key->publicKey.curve->G, key->privateKey);
+    if (!publicKey) {
+        freebn(key->privateKey);
+        key->privateKey = NULL;
+        return 0;
+    }
+
+    key->publicKey.x = publicKey->x;
+    key->publicKey.y = publicKey->y;
+    key->publicKey.z = NULL;
+    sfree(publicKey);
+
+    return 1;
+}
index 87f1ebf3db179abbbec1c5fa996e3cb3505d49b3..cd35afd5ad2a15265324b03fa22bdedfeb4779f5 100644 (file)
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -563,6 +563,12 @@ const struct ssh_signkey *find_pubkey_alg(const char *name)
        return &ssh_rsa;
     else if (!strcmp(name, "ssh-dss"))
        return &ssh_dss;
+    else if (!strcmp(name, "ecdsa-sha2-nistp256"))
+        return &ssh_ecdsa_nistp256;
+    else if (!strcmp(name, "ecdsa-sha2-nistp384"))
+        return &ssh_ecdsa_nistp384;
+    else if (!strcmp(name, "ecdsa-sha2-nistp521"))
+        return &ssh_ecdsa_nistp521;
     else
        return NULL;
 }
index 8d0b1ae3629791d3e52dbf2bb5e220f59a0c2157..cd2b296e63bdf1efed34b2423257f6aa9ef704ed 100644 (file)
@@ -2,6 +2,8 @@
  * SHA-512 algorithm as described at
  * 
  *   http://csrc.nist.gov/cryptval/shs.html
+ *
+ * Modifications made for SHA-384 also
  */
 
 #include "ssh.h"
@@ -61,6 +63,22 @@ static void SHA512_Core_Init(SHA512_State *s) {
        s->h[i] = iv[i];
 }
 
+static void SHA384_Core_Init(SHA512_State *s) {
+    static const uint64 iv[] = {
+        INIT(0xcbbb9d5d, 0xc1059ed8),
+        INIT(0x629a292a, 0x367cd507),
+        INIT(0x9159015a, 0x3070dd17),
+        INIT(0x152fecd8, 0xf70e5939),
+        INIT(0x67332667, 0xffc00b31),
+        INIT(0x8eb44a87, 0x68581511),
+        INIT(0xdb0c2e0d, 0x64f98fa7),
+        INIT(0x47b5481d, 0xbefa4fa4),
+    };
+    int i;
+    for (i = 0; i < 8; i++)
+        s->h[i] = iv[i];
+}
+
 static void SHA512_Block(SHA512_State *s, uint64 *block) {
     uint64 w[80];
     uint64 a,b,c,d,e,f,g,h;
@@ -175,6 +193,14 @@ void SHA512_Init(SHA512_State *s) {
        s->len[i] = 0;
 }
 
+void SHA384_Init(SHA512_State *s) {
+    int i;
+    SHA384_Core_Init(s);
+    s->blkused = 0;
+    for (i = 0; i < 4; i++)
+        s->len[i] = 0;
+}
+
 void SHA512_Bytes(SHA512_State *s, const void *p, int len) {
     unsigned char *q = (unsigned char *)p;
     uint64 wordblock[16];
@@ -268,6 +294,12 @@ void SHA512_Final(SHA512_State *s, unsigned char *digest) {
     }
 }
 
+void SHA384_Final(SHA512_State *s, unsigned char *digest) {
+    unsigned char biggerDigest[512 / 8];
+    SHA512_Final(s, biggerDigest);
+    memcpy(digest, biggerDigest, 384 / 8);
+}
+
 void SHA512_Simple(const void *p, int len, unsigned char *output) {
     SHA512_State s;
 
@@ -276,6 +308,67 @@ void SHA512_Simple(const void *p, int len, unsigned char *output) {
     SHA512_Final(&s, output);
 }
 
+void SHA384_Simple(const void *p, int len, unsigned char *output) {
+    SHA512_State s;
+
+    SHA384_Init(&s);
+    SHA512_Bytes(&s, p, len);
+    SHA384_Final(&s, output);
+}
+
+/*
+ * Thin abstraction for things where hashes are pluggable.
+ */
+
+static void *sha512_init(void)
+{
+    SHA512_State *s;
+
+    s = snew(SHA512_State);
+    SHA512_Init(s);
+    return s;
+}
+
+static void sha512_bytes(void *handle, void *p, int len)
+{
+    SHA512_State *s = handle;
+
+    SHA512_Bytes(s, p, len);
+}
+
+static void sha512_final(void *handle, unsigned char *output)
+{
+    SHA512_State *s = handle;
+
+    SHA512_Final(s, output);
+    sfree(s);
+}
+
+const struct ssh_hash ssh_sha512 = {
+    sha512_init, sha512_bytes, sha512_final, 64, "SHA-512"
+};
+
+static void *sha384_init(void)
+{
+    SHA512_State *s;
+
+    s = snew(SHA512_State);
+    SHA384_Init(s);
+    return s;
+}
+
+static void sha384_final(void *handle, unsigned char *output)
+{
+    SHA512_State *s = handle;
+
+    SHA384_Final(s, output);
+    sfree(s);
+}
+
+const struct ssh_hash ssh_sha384 = {
+    sha384_init, sha512_bytes, sha384_final, 48, "SHA-384"
+};
+
 #ifdef TEST
 
 #include <stdio.h>
index 3776df8c1998c50f20bf84b2c1cc35d37eefd1e4..dc1e31b1bafa219f87edeab5e2d13aeb36aba4ee 100644 (file)
@@ -3444,7 +3444,7 @@ static void licence_clicked(GtkButton *button, gpointer data)
        "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
        "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
        "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
-       "Markus Kuhn, Colin Watson, and CORE SDI S.A.\n\n"
+       "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\n\n"
 
        "Permission is hereby granted, free of charge, to any person "
        "obtaining a copy of this software and associated documentation "
index 2526bbce8283b8d13315aa69cc59623ac26e8311..d354a9c751603a14c3ff2480473f4b6dcf2e34e7 100644 (file)
@@ -50,41 +50,42 @@ BEGIN
 END
 
 /* No accelerators used */
-214 DIALOG DISCARDABLE 50, 50, 226, 263
+214 DIALOG DISCARDABLE 50, 50, 226, 271
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "PuTTY Licence"
 FONT 8, "MS Shell Dlg"
 BEGIN
-    DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
+    DEFPUSHBUTTON "OK", IDOK, 98, 251, 44, 14
 
     LTEXT "Copyright \251 1997-2014 Simon Tatham", 1000, 10, 10, 206, 8
 
     LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
     LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
     LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8
-    LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8
+    LTEXT "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI", 1004, 10, 50, 206, 8
+    LTEXT "S.A.", 1005, 10, 58, 206, 8
 
-    LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8
-    LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8
-    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8
-    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8
-    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8
-    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8
-    LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1006, 10, 74, 206, 8
+    LTEXT "obtaining a copy of this software and associated documentation", 1007, 10, 82, 206, 8
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1008, 10, 90, 206, 8
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1009, 10, 98, 206, 8
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1010, 10, 106, 206, 8
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1011, 10, 114, 206, 8
+    LTEXT "subject to the following conditions:", 1012, 10, 122, 206, 8
 
-    LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8
-    LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8
+    LTEXT "The above copyright notice and this permission notice shall be", 1013, 10, 138, 206, 8
+    LTEXT "included in all copies or substantial portions of the Software.", 1014, 10, 146, 206, 8
 
-    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8
-    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8
-    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8
-    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8
-    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8
-    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8
-    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8
-    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8
-    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8
-    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1015, 10, 162, 206, 8
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1016, 10, 170, 206, 8
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1017, 10, 178, 206, 8
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1018, 10, 186, 206, 8
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1019, 10, 194, 206, 8
+    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1020, 10, 202, 206, 8
+    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1021, 10, 210, 206, 8
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1022, 10, 218, 206, 8
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1023, 10, 226, 206, 8
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1024, 10, 234, 206, 8
 
 END
 
index a6c80a15b4e708194d4bc725d972eb01a67647b4..63c0bcc076d9b3b4cc67165113c834864c9a7073 100644 (file)
@@ -43,41 +43,42 @@ BEGIN
 END
 
 /* No accelerators used */
-214 DIALOG DISCARDABLE 50, 50, 226, 263
+214 DIALOG DISCARDABLE 50, 50, 226, 271
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "PuTTY Licence"
 FONT 8, "MS Shell Dlg"
 BEGIN
-    DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
+    DEFPUSHBUTTON "OK", IDOK, 98, 251, 44, 14
 
     LTEXT "Copyright \251 1997-2014 Simon Tatham", 1000, 10, 10, 206, 8
 
     LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
     LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
     LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8
-    LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8
+    LTEXT "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI", 1004, 10, 50, 206, 8
+    LTEXT "S.A.", 1005, 10, 58, 206, 8
 
-    LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8
-    LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8
-    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8
-    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8
-    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8
-    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8
-    LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1006, 10, 74, 206, 8
+    LTEXT "obtaining a copy of this software and associated documentation", 1007, 10, 82, 206, 8
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1008, 10, 90, 206, 8
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1009, 10, 98, 206, 8
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1010, 10, 106, 206, 8
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1011, 10, 114, 206, 8
+    LTEXT "subject to the following conditions:", 1012, 10, 122, 206, 8
 
-    LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8
-    LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8
+    LTEXT "The above copyright notice and this permission notice shall be", 1013, 10, 138, 206, 8
+    LTEXT "included in all copies or substantial portions of the Software.", 1014, 10, 146, 206, 8
 
-    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8
-    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8
-    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8
-    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8
-    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8
-    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8
-    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8
-    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8
-    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8
-    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1015, 10, 162, 206, 8
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1016, 10, 170, 206, 8
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1017, 10, 178, 206, 8
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1018, 10, 186, 206, 8
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1019, 10, 194, 206, 8
+    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1020, 10, 202, 206, 8
+    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1021, 10, 210, 206, 8
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1022, 10, 218, 206, 8
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1023, 10, 226, 206, 8
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1024, 10, 234, 206, 8
 
 END
 
index 6f19d99ee23f5d45dbeda3ace058a940aaf366ab..d0273350a4604ff43489091cded39bbcd6b6c02c 100644 (file)
@@ -51,41 +51,42 @@ BEGIN
 END
 
 /* No accelerators used */
-IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 263
+IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 271
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "PuTTY Licence"
 FONT 8, "MS Shell Dlg"
 BEGIN
-    DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
+    DEFPUSHBUTTON "OK", IDOK, 98, 251, 44, 14
 
     LTEXT "Copyright \251 1997-2014 Simon Tatham", 1000, 10, 10, 206, 8
 
     LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
     LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
     LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8
-    LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8
+    LTEXT "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI", 1004, 10, 50, 206, 8
+    LTEXT "S.A.", 1005, 10, 58, 206, 8
 
-    LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8
-    LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8
-    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8
-    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8
-    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8
-    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8
-    LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1006, 10, 74, 206, 8
+    LTEXT "obtaining a copy of this software and associated documentation", 1007, 10, 82, 206, 8
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1008, 10, 90, 206, 8
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1009, 10, 98, 206, 8
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1010, 10, 106, 206, 8
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1011, 10, 114, 206, 8
+    LTEXT "subject to the following conditions:", 1012, 10, 122, 206, 8
 
-    LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8
-    LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8
+    LTEXT "The above copyright notice and this permission notice shall be", 1013, 10, 138, 206, 8
+    LTEXT "included in all copies or substantial portions of the Software.", 1014, 10, 146, 206, 8
 
-    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8
-    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8
-    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8
-    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8
-    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8
-    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8
-    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8
-    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8
-    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8
-    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1015, 10, 162, 206, 8
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1016, 10, 170, 206, 8
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1017, 10, 178, 206, 8
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1018, 10, 186, 206, 8
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1019, 10, 194, 206, 8
+    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1020, 10, 202, 206, 8
+    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1021, 10, 210, 206, 8
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1022, 10, 218, 206, 8
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1023, 10, 226, 206, 8
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1024, 10, 234, 206, 8
 
 END
 
index 33d76c64f6e880be0fc678c9772d07d22b4f43cc..8f263efcbe3b421de14e1be74b492511a24dbd2e 100644 (file)
@@ -315,6 +315,8 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg,
     return 0;
 }
 
+typedef enum {RSA, DSA, ECDSA} keytype;
+
 /*
  * Thread to generate a key.
  */
@@ -322,9 +324,12 @@ struct rsa_key_thread_params {
     HWND progressbar;                 /* notify this with progress */
     HWND dialog;                      /* notify this on completion */
     int keysize;                      /* bits in key */
-    int is_dsa;
-    struct RSAKey *key;
-    struct dss_key *dsskey;
+    keytype keytype;
+    union {
+        struct RSAKey *key;
+        struct dss_key *dsskey;
+        struct ec_key *eckey;
+    };
 };
 static DWORD WINAPI generate_rsa_key_thread(void *param)
 {
@@ -335,8 +340,10 @@ static DWORD WINAPI generate_rsa_key_thread(void *param)
 
     progress_update(&prog, PROGFN_INITIALISE, 0, 0);
 
-    if (params->is_dsa)
+    if (params->keytype == DSA)
        dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
+    else if (params->keytype == ECDSA)
+        ec_generate(params->eckey, params->keysize, progress_update, &prog);
     else
        rsa_generate(params->key, params->keysize, progress_update, &prog);
 
@@ -352,12 +359,16 @@ struct MainDlgState {
     int key_exists;
     int entropy_got, entropy_required, entropy_size;
     int keysize;
-    int ssh2, is_dsa;
+    int ssh2;
+    keytype keytype;
     char **commentptr;                /* points to key.comment or ssh2key.comment */
     struct ssh2_userkey ssh2key;
     unsigned *entropy;
-    struct RSAKey key;
-    struct dss_key dsskey;
+    union {
+        struct RSAKey key;
+        struct dss_key dsskey;
+        struct ec_key eckey;
+    };
     HMENU filemenu, keymenu, cvtmenu;
 };
 
@@ -519,6 +530,7 @@ enum {
     IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
     IDC_BOX_PARAMS,
     IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
+    IDC_KEYSSH2ECDSA,
     IDC_BITSSTATIC, IDC_BITS,
     IDC_ABOUT,
     IDC_GIVEHELP,
@@ -555,6 +567,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
+        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
        EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
@@ -563,6 +576,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
        EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
+        EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
+                       MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
                       MF_GRAYED|MF_BYCOMMAND);
@@ -580,6 +595,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
+        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
        EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
        EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
        EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
@@ -588,6 +604,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
        EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
        EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
        EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
+        EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
+                       MF_GRAYED|MF_BYCOMMAND);
        EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
        EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
                       MF_GRAYED|MF_BYCOMMAND);
@@ -605,6 +623,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
+        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
        EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
@@ -613,6 +632,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
        EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
+        EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
+                       MF_ENABLED|MF_BYCOMMAND);
        EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
        /*
         * Enable export menu items if and only if the key type
@@ -855,6 +876,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
            AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
            AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
+            AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
            AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
            state->keymenu = menu1;
 
@@ -896,7 +918,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        {
            struct ctlpos cp, cp2;
 
-           /* Accelerators used: acglops1rbd */
+           /* Accelerators used: acglops1rbde */
 
            ctlposinit(&cp, hwnd, 4, 4, 4);
            beginbox(&cp, "Key", IDC_BOX_KEY);
@@ -930,16 +952,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                       "&Save private key", IDC_SAVE);
            endbox(&cp);
            beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
-           radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
+           radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 4,
                      "SSH-&1 (RSA)", IDC_KEYSSH1,
                      "SSH-2 &RSA", IDC_KEYSSH2RSA,
-                     "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
+                      "SSH-2 &DSA", IDC_KEYSSH2DSA,
+                      "SSH-2 &ECDSA", IDC_KEYSSH2ECDSA, NULL);
            staticedit(&cp, "Number of &bits in a generated key:",
                       IDC_BITSSTATIC, IDC_BITS, 20);
            endbox(&cp);
        }
-       CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
-       CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
+        CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
+        CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
                           IDC_KEYSSH2RSA, MF_BYCOMMAND);
        SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
 
@@ -990,7 +1013,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
                params->dialog = hwnd;
                params->keysize = state->keysize;
-               params->is_dsa = state->is_dsa;
+                params->keytype = state->keytype;
                params->key = &state->key;
                params->dsskey = &state->dsskey;
 
@@ -1011,6 +1034,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
          case IDC_KEYSSH1:
          case IDC_KEYSSH2RSA:
          case IDC_KEYSSH2DSA:
+          case IDC_KEYSSH2ECDSA:
            {
                state = (struct MainDlgState *)
                    GetWindowLongPtr(hwnd, GWLP_USERDATA);
@@ -1019,6 +1043,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                                     LOWORD(wParam));
                CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
                                   LOWORD(wParam), MF_BYCOMMAND);
+                CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
+                                 LOWORD(wParam));
+                CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
+                                   IDC_KEYSSH2ECDSA,
+                                   LOWORD(wParam), MF_BYCOMMAND);
            }
            break;
          case IDC_QUIT:
@@ -1070,7 +1099,12 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                    state->keysize = DEFAULT_KEYSIZE;
                /* If we ever introduce a new key type, check it here! */
                state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
-               state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
+                state->keytype = RSA;
+                if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
+                    state->keytype = DSA;
+                } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
+                    state->keytype = ECDSA;
+                }
                if (state->keysize < 256) {
                    int ret = MessageBox(hwnd,
                                         "PuTTYgen will not generate a key"
@@ -1083,6 +1117,20 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                    state->keysize = 256;
                    SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
                }
+                if (state->keytype == ECDSA && !(state->keysize == 256 ||
+                                                 state->keysize == 384 ||
+                                                 state->keysize == 521)) {
+                    int ret = MessageBox(hwnd,
+                                         "Only 256, 384 and 521 bit elliptic"
+                                         " curves are supported.\n"
+                                         "Key length reset to 256. Continue?",
+                                         "PuTTYgen Warning",
+                                         MB_ICONWARNING | MB_OKCANCEL);
+                    if (ret != IDOK)
+                        break;
+                    state->keysize = 256;
+                    SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
+                }
                ui_set_state(hwnd, state, 1);
                SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
                state->key_exists = FALSE;
@@ -1276,9 +1324,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                           MAKELPARAM(0, PROGRESSRANGE));
        SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
        if (state->ssh2) {
-           if (state->is_dsa) {
+            if (state->keytype == DSA) {
                state->ssh2key.data = &state->dsskey;
                state->ssh2key.alg = &ssh_dss;
+            } else if (state->keytype == ECDSA) {
+                state->ssh2key.data = &state->eckey;
+                if (state->eckey.publicKey.curve->fieldBits == 256)
+                    state->ssh2key.alg = &ssh_ecdsa_nistp256;
+                else if (state->eckey.publicKey.curve->fieldBits == 384)
+                    state->ssh2key.alg = &ssh_ecdsa_nistp384;
+                else
+                    state->ssh2key.alg = &ssh_ecdsa_nistp521;
            } else {
                state->ssh2key.data = &state->key;
                state->ssh2key.alg = &ssh_rsa;
@@ -1297,8 +1353,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        {
            struct tm tm;
            tm = ltime();
-           if (state->is_dsa)
+            if (state->keytype == DSA)
                strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
+            else if (state->keytype == ECDSA)
+                strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
            else
                strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
        }
@@ -1389,6 +1447,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
               case IDC_KEYSSH1:
               case IDC_KEYSSH2RSA:
               case IDC_KEYSSH2DSA:
+              case IDC_KEYSSH2ECDSA:
                 topic = WINHELP_CTX_puttygen_keytype; break;
               case IDC_BITSSTATIC:
               case IDC_BITS:
index 22b607881572d9a39e810e5d3ce6245b980b7bf9..828747dac62a83bdb391f616d53c934bc510f730 100644 (file)
@@ -349,22 +349,24 @@ static void keylist_update(void)
        }
        for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
            char *listentry, *p;
-           int fp_len;
+           int pos, fp_len;
            /*
-            * Replace two spaces in the fingerprint with tabs, for
-            * nice alignment in the box.
+            * Replace spaces with tabs in the fingerprint prefix, for
+            * nice alignment in the list box, until we encounter a :
+            * meaning we're into the fingerprint proper.
             */
            p = skey->alg->fingerprint(skey->data);
             listentry = dupprintf("%s\t%s", p, skey->comment);
             fp_len = strlen(listentry);
             sfree(p);
 
-           p = strchr(listentry, ' ');
-           if (p && p < listentry + fp_len)
-               *p = '\t';
-           p = strchr(listentry, ' ');
-           if (p && p < listentry + fp_len)
-               *p = '\t';
+            pos = 0;
+            while (1) {
+                pos += strcspn(listentry + pos, " :");
+                if (listentry[pos] == ':')
+                    break;
+                listentry[pos++] = '\t';
+            }
 
            SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
                               (LPARAM) listentry);
@@ -1141,6 +1143,12 @@ static void answer_msg(void *msg)
                key->alg = &ssh_rsa;
            else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
                key->alg = &ssh_dss;
+            else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19))
+                key->alg = &ssh_ecdsa_nistp256;
+            else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19))
+                key->alg = &ssh_ecdsa_nistp384;
+            else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19))
+                key->alg = &ssh_ecdsa_nistp521;
            else {
                sfree(key);
                goto failure;
@@ -1508,7 +1516,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
 
        keylist = hwnd;
        {
-           static int tabs[] = { 35, 60, 210 };
+           static int tabs[] = { 35, 75, 250 };
            SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
                               sizeof(tabs) / sizeof(*tabs),
                               (LPARAM) tabs);