]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Elliptic-curve cryptography support.
authorChris Staite <chris@yourdreamnet.co.uk>
Sat, 1 Nov 2014 09:45:20 +0000 (09:45 +0000)
committerSimon Tatham <anakin@pobox.com>
Sun, 2 Nov 2014 18:16:54 +0000 (18:16 +0000)
This provides support for ECDSA public keys, for both hosts and users,
and also ECDH key exchange. Supported curves are currently just the
three NIST curves required by RFC 5656.

14 files changed:
Recipe
cmdgen.c
config.c
contrib/logparse.pl
import.c
putty.h
settings.c
ssh.c
ssh.h
sshecc.c [new file with mode: 0644]
sshecdsag.c [new file with mode: 0644]
sshpubk.c
windows/winpgen.c
windows/winpgnt.c

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 657f6069a908e31a70faef2a5a22b13d0775cf57..c01c6812a90cdc7b41e9f63e389ddd248c9b9745 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 3ab967e4abac21e47a6f2145bc59d559b26c0609..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,7 +619,122 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
     else
        num_integers = 0;              /* placate compiler warnings */
 
-    if (key->type == OSSH_RSA || key->type == OSSH_DSA) {
+
+    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 != 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;
+        }
+
+    } else if (key->type == OSSH_RSA || key->type == OSSH_DSA) {
+
         /*
          * Space to create key blob in.
          */
@@ -875,6 +996,90 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
             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;
+
+        /*
+         * 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);
+        }
+
+        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);
+
+        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. */
diff --git a/putty.h b/putty.h
index ff0f3156a111eca11a206ab82c6917e79870c1de..a0262caa26e51104cc0391e0c1056ae736f9f7bf 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..5b0957ef73e4f933e71af593a0755dca07358695 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
@@ -6024,6 +6030,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];
@@ -6087,6 +6094,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. */
@@ -6653,6 +6664,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 a68a99465385c48cfdb7b65862002c782fd0a20d..3bb8593b6ecdf15ea951ccc61dc31bc8d97a1d8a 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,14 @@ 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
  */
@@ -271,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;
@@ -327,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;
@@ -345,8 +385,12 @@ 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;
@@ -633,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);
@@ -742,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/sshecc.c b/sshecc.c
new file mode 100644 (file)
index 0000000..3c2d829
--- /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 9ec04b4b9a4ae42efc342452cd071a4080c7d39b..db47e60f8be7e2661302582d95dfeac29af6e27c 100644 (file)
@@ -328,6 +328,7 @@ struct rsa_key_thread_params {
     union {
         struct RSAKey *key;
         struct dss_key *dsskey;
+        struct ec_key *eckey;
     };
 };
 static DWORD WINAPI generate_rsa_key_thread(void *param)
@@ -341,6 +342,8 @@ static DWORD WINAPI generate_rsa_key_thread(void *param)
 
     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);
 
@@ -364,6 +367,7 @@ struct MainDlgState {
     union {
         struct RSAKey key;
         struct dss_key dsskey;
+        struct ec_key eckey;
     };
     HMENU filemenu, keymenu, cvtmenu;
 };
@@ -526,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,
@@ -562,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);
@@ -570,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);
@@ -587,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);
@@ -595,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);
@@ -612,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);
@@ -620,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
@@ -862,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;
 
@@ -940,13 +955,14 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
                      "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);
 
@@ -1018,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);
@@ -1026,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:
@@ -1080,6 +1102,8 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                 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,
@@ -1093,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;
@@ -1289,6 +1327,14 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
             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;
@@ -1309,6 +1355,8 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            tm = ltime();
             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);
        }
@@ -1399,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..b9addd196dac5a8cba3afc451256c649ebf92275 100644 (file)
@@ -1141,6 +1141,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;