From: Jacob Nevins Date: Mon, 5 Jan 2015 23:49:25 +0000 (+0000) Subject: Merge branch 'pre-0.64' X-Git-Tag: 0.68~636 X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=5904545cc18289541702da284b00490cb25a753e;hp=f3685eb9482ab98e7c7ecee06970795a016834b6;p=PuTTY.git Merge branch 'pre-0.64' --- diff --git a/LICENCE b/LICENCE index eab8817e..3e15aeb9 100644 --- a/LICENCE +++ b/LICENCE @@ -3,7 +3,7 @@ PuTTY is copyright 1997-2015 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus -Kuhn, Colin Watson, and CORE SDI S.A. +Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff --git a/Recipe b/Recipe index bac258f1..aa2f1949 100644 --- 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 diff --git a/cmdgen.c b/cmdgen.c index c15c01dd..80e69b5a 100644 --- 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); diff --git a/config.c b/config.c index c2acd628..0d292b8a 100644 --- 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 } }; diff --git a/contrib/logparse.pl b/contrib/logparse.pl index 3445baa8..4805c0f4 100755 --- a/contrib/logparse.pl +++ b/contrib/logparse.pl @@ -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) = @_; diff --git a/doc/licence.but b/doc/licence.but index 0856d863..333a9192 100644 --- a/doc/licence.but +++ b/doc/licence.but @@ -5,7 +5,7 @@ PuTTY is \i{copyright} 1997-2015 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus -Kuhn, Colin Watson, and CORE SDI S.A. +Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff --git a/import.c b/import.c index bc35a4ab..ff09dee5 100644 --- a/import.c +++ b/import.c @@ -307,7 +307,7 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret) * Code to read and write OpenSSH private keys. */ -enum { OSSH_DSA, OSSH_RSA }; +enum { OSSH_DSA, OSSH_RSA, OSSH_ECDSA }; enum { OSSH_ENC_3DES, OSSH_ENC_AES }; struct openssh_key { int type; @@ -354,6 +354,8 @@ static struct openssh_key *load_openssh_key(const Filename *filename, ret->type = OSSH_RSA; else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) ret->type = OSSH_DSA; + else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----")) + ret->type = OSSH_ECDSA; else { errmsg = "unrecognised key type"; goto error; @@ -591,6 +593,10 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, * * - For DSA, we expect them to be 0, p, q, g, y, x in that * order. + * + * - In ECDSA the format is totally different: we see the + * SEQUENCE, but beneath is an INTEGER 1, OCTET STRING priv + * EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint */ p = key->keyblob; @@ -613,91 +619,211 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, else num_integers = 0; /* placate compiler warnings */ - /* - * Space to create key blob in. - */ - blobsize = 256+key->keyblob_len; - blob = snewn(blobsize, unsigned char); - PUT_32BIT(blob, 7); - if (key->type == OSSH_DSA) - memcpy(blob+4, "ssh-dss", 7); - else if (key->type == OSSH_RSA) - memcpy(blob+4, "ssh-rsa", 7); - blobptr = 4+7; - privptr = -1; - - for (i = 0; i < num_integers; i++) { + + if (key->type == OSSH_ECDSA) + { + /* And now for something completely different */ + unsigned char *priv; + int privlen; + struct ec_curve *curve; + /* Read INTEGER 1 */ + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len || + len != 1 || p[0] != 1) { + errmsg = "ASN.1 decoding failure"; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; + goto error; + } + p += 1; + /* Read private key OCTET STRING */ + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 4 || key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; + goto error; + } + priv = p; + privlen = len; + p += len; + /* Read curve OID */ + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 0 || key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; + goto error; + } ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, &id, &len, &flags); p += ret; - if (ret < 0 || id != 2 || - key->keyblob+key->keyblob_len-p < len) { + if (ret < 0 || id != 6 || key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } + if (len == 8 && !memcmp(p, nistp256_oid, nistp256_oid_len)) { + curve = ec_p256(); + } else if (len == 5 && !memcmp(p, nistp384_oid, nistp384_oid_len)) { + curve = ec_p384(); + } else if (len == 5 && !memcmp(p, nistp521_oid, nistp521_oid_len)) { + curve = ec_p521(); + } else { + errmsg = "Unsupported ECDSA curve."; + retval = NULL; + goto error; + } + p += len; + /* Read BIT STRING point */ + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 1 || key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; + goto error; + } + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 3 || key->keyblob+key->keyblob_len-p < len || + len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) { + errmsg = "ASN.1 decoding failure"; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; + goto error; + } + p += 1; len -= 1; /* Skip 0x00 before point */ + + /* Construct the key */ + retkey = snew(struct ssh2_userkey); + if (!retkey) { + errmsg = "out of memory"; + goto error; + } + if (curve->fieldBits == 256) { + retkey->alg = &ssh_ecdsa_nistp256; + } else if (curve->fieldBits == 384) { + retkey->alg = &ssh_ecdsa_nistp384; + } else { + retkey->alg = &ssh_ecdsa_nistp521; + } + blob = snewn((4+19 + 4+8 + 4+len) + (4+privlen), unsigned char); + if (!blob) { + sfree(retkey); + errmsg = "out of memory"; + goto error; + } + PUT_32BIT(blob, 19); + sprintf((char*)blob+4, "ecdsa-sha2-nistp%d", curve->fieldBits); + PUT_32BIT(blob+4+19, 8); + sprintf((char*)blob+4+19+4, "nistp%d", curve->fieldBits); + PUT_32BIT(blob+4+19+4+8, len); + memcpy(blob+4+19+4+8+4, p, len); + PUT_32BIT(blob+4+19+4+8+4+len, privlen); + memcpy(blob+4+19+4+8+4+len+4, priv, privlen); + retkey->data = retkey->alg->createkey(blob, 4+19+4+8+4+len, + blob+4+19+4+8+4+len, 4+privlen); + if (!retkey->data) { + sfree(retkey); + errmsg = "unable to create key data structure"; + goto error; + } - if (i == 0) { - /* - * The first integer should be zero always (I think - * this is some sort of version indication). - */ - if (len != 1 || p[0] != 0) { - errmsg = "version number mismatch"; + } else if (key->type == OSSH_RSA || key->type == OSSH_DSA) { + + /* + * Space to create key blob in. + */ + blobsize = 256+key->keyblob_len; + blob = snewn(blobsize, unsigned char); + PUT_32BIT(blob, 7); + if (key->type == OSSH_DSA) + memcpy(blob+4, "ssh-dss", 7); + else if (key->type == OSSH_RSA) + memcpy(blob+4, "ssh-rsa", 7); + blobptr = 4+7; + privptr = -1; + + for (i = 0; i < num_integers; i++) { + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 2 || + key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } - } else if (key->type == OSSH_RSA) { - /* - * Integers 1 and 2 go into the public blob but in the - * opposite order; integers 3, 4, 5 and 8 go into the - * private blob. The other two (6 and 7) are ignored. - */ - if (i == 1) { - /* Save the details for after we deal with number 2. */ - modptr = (char *)p; - modlen = len; - } else if (i != 6 && i != 7) { + + if (i == 0) { + /* + * The first integer should be zero always (I think + * this is some sort of version indication). + */ + if (len != 1 || p[0] != 0) { + errmsg = "version number mismatch"; + goto error; + } + } else if (key->type == OSSH_RSA) { + /* + * Integers 1 and 2 go into the public blob but in the + * opposite order; integers 3, 4, 5 and 8 go into the + * private blob. The other two (6 and 7) are ignored. + */ + if (i == 1) { + /* Save the details for after we deal with number 2. */ + modptr = (char *)p; + modlen = len; + } else if (i != 6 && i != 7) { + PUT_32BIT(blob+blobptr, len); + memcpy(blob+blobptr+4, p, len); + blobptr += 4+len; + if (i == 2) { + PUT_32BIT(blob+blobptr, modlen); + memcpy(blob+blobptr+4, modptr, modlen); + blobptr += 4+modlen; + privptr = blobptr; + } + } + } else if (key->type == OSSH_DSA) { + /* + * Integers 1-4 go into the public blob; integer 5 goes + * into the private blob. + */ PUT_32BIT(blob+blobptr, len); memcpy(blob+blobptr+4, p, len); blobptr += 4+len; - if (i == 2) { - PUT_32BIT(blob+blobptr, modlen); - memcpy(blob+blobptr+4, modptr, modlen); - blobptr += 4+modlen; + if (i == 4) privptr = blobptr; - } } - } else if (key->type == OSSH_DSA) { - /* - * Integers 1-4 go into the public blob; integer 5 goes - * into the private blob. - */ - PUT_32BIT(blob+blobptr, len); - memcpy(blob+blobptr+4, p, len); - blobptr += 4+len; - if (i == 4) - privptr = blobptr; + + /* Skip past the number. */ + p += len; } - /* Skip past the number. */ - p += len; - } + /* + * Now put together the actual key. Simplest way to do this is + * to assemble our own key blobs and feed them to the createkey + * functions; this is a bit faffy but it does mean we get all + * the sanity checks for free. + */ + assert(privptr > 0); /* should have bombed by now if not */ + retkey = snew(struct ssh2_userkey); + retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss); + retkey->data = retkey->alg->createkey(blob, privptr, + blob+privptr, blobptr-privptr); + if (!retkey->data) { + sfree(retkey); + errmsg = "unable to create key data structure"; + goto error; + } - /* - * Now put together the actual key. Simplest way to do this is - * to assemble our own key blobs and feed them to the createkey - * functions; this is a bit faffy but it does mean we get all - * the sanity checks for free. - */ - assert(privptr > 0); /* should have bombed by now if not */ - retkey = snew(struct ssh2_userkey); - retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss); - retkey->data = retkey->alg->createkey(blob, privptr, - blob+privptr, blobptr-privptr); - if (!retkey->data) { - sfree(retkey); - errmsg = "unable to create key data structure"; - goto error; + } else { + assert(0 && "Bad key type from load_openssh_key"); } retkey->comment = dupstr("imported-openssh-key"); @@ -739,157 +865,226 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, privblob = key->alg->private_blob(key->data, &privlen); spareblob = outblob = NULL; + outblob = NULL; + len = 0; + /* - * Find the sequence of integers to be encoded into the OpenSSH - * key blob, and also decide on the header line. + * Encode the OpenSSH key blob, and also decide on the header + * line. */ - if (key->alg == &ssh_rsa) { - int pos; - struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1; - Bignum bd, bp, bq, bdmp1, bdmq1; - + if (key->alg == &ssh_rsa || key->alg == &ssh_dss) { /* - * These blobs were generated from inside PuTTY, so we needn't - * treat them as untrusted. + * The RSA and DSS handlers share some code because the two + * key types have very similar ASN.1 representations, as a + * plain SEQUENCE of big integers. So we set up a list of + * bignums per key type and then construct the actual blob in + * common code after that. */ - pos = 4 + GET_32BIT(pubblob); - pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); - pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); - pos = 0; - pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); - pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); - pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); - pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); + if (key->alg == &ssh_rsa) { + int pos; + struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1; + Bignum bd, bp, bq, bdmp1, bdmq1; - assert(e.start && iqmp.start); /* can't go wrong */ + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ + pos = 4 + GET_32BIT(pubblob); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); + pos = 0; + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); + + assert(e.start && iqmp.start); /* can't go wrong */ + + /* We also need d mod (p-1) and d mod (q-1). */ + bd = bignum_from_bytes(d.start, d.bytes); + bp = bignum_from_bytes(p.start, p.bytes); + bq = bignum_from_bytes(q.start, q.bytes); + decbn(bp); + decbn(bq); + bdmp1 = bigmod(bd, bp); + bdmq1 = bigmod(bd, bq); + freebn(bd); + freebn(bp); + freebn(bq); + + dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8; + dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8; + sparelen = dmp1.bytes + dmq1.bytes; + spareblob = snewn(sparelen, unsigned char); + dmp1.start = spareblob; + dmq1.start = spareblob + dmp1.bytes; + for (i = 0; i < dmp1.bytes; i++) + spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i); + for (i = 0; i < dmq1.bytes; i++) + spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i); + freebn(bdmp1); + freebn(bdmq1); + + numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; + numbers[1] = n; + numbers[2] = e; + numbers[3] = d; + numbers[4] = p; + numbers[5] = q; + numbers[6] = dmp1; + numbers[7] = dmq1; + numbers[8] = iqmp; + + nnumbers = 9; + header = "-----BEGIN RSA PRIVATE KEY-----\n"; + footer = "-----END RSA PRIVATE KEY-----\n"; + } else { /* ssh-dss */ + int pos; + struct mpint_pos p, q, g, y, x; - /* We also need d mod (p-1) and d mod (q-1). */ - bd = bignum_from_bytes(d.start, d.bytes); - bp = bignum_from_bytes(p.start, p.bytes); - bq = bignum_from_bytes(q.start, q.bytes); - decbn(bp); - decbn(bq); - bdmp1 = bigmod(bd, bp); - bdmq1 = bigmod(bd, bq); - freebn(bd); - freebn(bp); - freebn(bq); - - dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8; - dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8; - sparelen = dmp1.bytes + dmq1.bytes; - spareblob = snewn(sparelen, unsigned char); - dmp1.start = spareblob; - dmq1.start = spareblob + dmp1.bytes; - for (i = 0; i < dmp1.bytes; i++) - spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i); - for (i = 0; i < dmq1.bytes; i++) - spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i); - freebn(bdmp1); - freebn(bdmq1); - - numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; - numbers[1] = n; - numbers[2] = e; - numbers[3] = d; - numbers[4] = p; - numbers[5] = q; - numbers[6] = dmp1; - numbers[7] = dmq1; - numbers[8] = iqmp; - - nnumbers = 9; - header = "-----BEGIN RSA PRIVATE KEY-----\n"; - footer = "-----END RSA PRIVATE KEY-----\n"; - } else if (key->alg == &ssh_dss) { - int pos; - struct mpint_pos p, q, g, y, x; + /* + * These blobs were generated from inside PuTTY, so we needn't + * treat them as untrusted. + */ + pos = 4 + GET_32BIT(pubblob); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); + pos = 0; + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); + + assert(y.start && x.start); /* can't go wrong */ + + numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; + numbers[1] = p; + numbers[2] = q; + numbers[3] = g; + numbers[4] = y; + numbers[5] = x; + + nnumbers = 6; + header = "-----BEGIN DSA PRIVATE KEY-----\n"; + footer = "-----END DSA PRIVATE KEY-----\n"; + } /* - * These blobs were generated from inside PuTTY, so we needn't - * treat them as untrusted. + * Now count up the total size of the ASN.1 encoded integers, + * so as to determine the length of the containing SEQUENCE. + */ + len = 0; + for (i = 0; i < nnumbers; i++) { + len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); + len += numbers[i].bytes; + } + seqlen = len; + /* Now add on the SEQUENCE header. */ + len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); + + /* + * Now we know how big outblob needs to be. Allocate it. + */ + outblob = snewn(len, unsigned char); + + /* + * And write the data into it. */ - pos = 4 + GET_32BIT(pubblob); - pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); - pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); - pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); - pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); pos = 0; - pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); + pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); + for (i = 0; i < nnumbers; i++) { + pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); + memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); + pos += numbers[i].bytes; + } + } else if (key->alg == &ssh_ecdsa_nistp256 || + key->alg == &ssh_ecdsa_nistp384 || + key->alg == &ssh_ecdsa_nistp521) { + unsigned char *oid; + int oidlen; + int pointlen; - assert(y.start && x.start); /* can't go wrong */ + /* + * Structure of asn1: + * SEQUENCE + * INTEGER 1 + * OCTET STRING (private key) + * [0] + * OID (curve) + * [1] + * BIT STRING (0x00 public key point) + */ + switch (((struct ec_key *)key->data)->publicKey.curve->fieldBits) { + case 256: + /* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */ + oid = nistp256_oid; + oidlen = nistp256_oid_len; + pointlen = 32 * 2; + break; + case 384: + /* OID: 1.3.132.0.34 (secp384r1) */ + oid = nistp384_oid; + oidlen = nistp384_oid_len; + pointlen = 48 * 2; + break; + case 521: + /* OID: 1.3.132.0.35 (secp521r1) */ + oid = nistp521_oid; + oidlen = nistp521_oid_len; + pointlen = 66 * 2; + break; + default: + assert(0); + } - numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; - numbers[1] = p; - numbers[2] = q; - numbers[3] = g; - numbers[4] = y; - numbers[5] = x; + len = ber_write_id_len(NULL, 2, 1, 0); + len += 1; + len += ber_write_id_len(NULL, 4, privlen - 4, 0); + len+= privlen - 4; + len += ber_write_id_len(NULL, 0, oidlen + + ber_write_id_len(NULL, 6, oidlen, 0), + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); + len += ber_write_id_len(NULL, 6, oidlen, 0); + len += oidlen; + len += ber_write_id_len(NULL, 1, 2 + pointlen + + ber_write_id_len(NULL, 3, 2 + pointlen, 0), + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); + len += ber_write_id_len(NULL, 3, 2 + pointlen, 0); + len += 2 + pointlen; + + seqlen = len; + len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); + + outblob = snewn(len, unsigned char); + assert(outblob); - nnumbers = 6; - header = "-----BEGIN DSA PRIVATE KEY-----\n"; - footer = "-----END DSA PRIVATE KEY-----\n"; + pos = 0; + pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); + pos += ber_write_id_len(outblob+pos, 2, 1, 0); + outblob[pos++] = 1; + pos += ber_write_id_len(outblob+pos, 4, privlen - 4, 0); + memcpy(outblob+pos, privblob + 4, privlen - 4); + pos += privlen - 4; + pos += ber_write_id_len(outblob+pos, 0, oidlen + + ber_write_id_len(NULL, 6, oidlen, 0), + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); + pos += ber_write_id_len(outblob+pos, 6, oidlen, 0); + memcpy(outblob+pos, oid, oidlen); + pos += oidlen; + pos += ber_write_id_len(outblob+pos, 1, 2 + pointlen + + ber_write_id_len(NULL, 3, 2 + pointlen, 0), + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); + pos += ber_write_id_len(outblob+pos, 3, 2 + pointlen, 0); + outblob[pos++] = 0; + memcpy(outblob+pos, pubblob+39, 1 + pointlen); + pos += 1 + pointlen; + + header = "-----BEGIN EC PRIVATE KEY-----\n"; + footer = "-----END EC PRIVATE KEY-----\n"; } else { assert(0); /* zoinks! */ exit(1); /* XXX: GCC doesn't understand assert() on some systems. */ } - /* - * Now count up the total size of the ASN.1 encoded integers, - * so as to determine the length of the containing SEQUENCE. - */ - len = 0; - for (i = 0; i < nnumbers; i++) { - len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); - len += numbers[i].bytes; - } - seqlen = len; - /* Now add on the SEQUENCE header. */ - len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); - /* Round up to the cipher block size, ensuring we have at least one - * byte of padding (see below). */ - outlen = len; - if (passphrase) - outlen = (outlen+8) &~ 7; - - /* - * Now we know how big outblob needs to be. Allocate it. - */ - outblob = snewn(outlen, unsigned char); - - /* - * And write the data into it. - */ - pos = 0; - pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); - for (i = 0; i < nnumbers; i++) { - pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); - memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); - pos += numbers[i].bytes; - } - - /* - * Padding on OpenSSH keys is deterministic. The number of - * padding bytes is always more than zero, and always at most - * the cipher block length. The value of each padding byte is - * equal to the number of padding bytes. So a plaintext that's - * an exact multiple of the block size will be padded with 08 - * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a - * plaintext one byte less than a multiple of the block size - * will be padded with just 01. - * - * This enables the OpenSSL key decryption function to strip - * off the padding algorithmically and return the unpadded - * plaintext to the next layer: it looks at the final byte, and - * then expects to find that many bytes at the end of the data - * with the same value. Those are all removed and the rest is - * returned. - */ - assert(pos == len); - while (pos < outlen) { - outblob[pos++] = outlen - len; - } - /* * Encrypt the key. * @@ -897,6 +1092,44 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, * old-style 3DES. */ if (passphrase) { + struct MD5Context md5c; + unsigned char keybuf[32]; + + /* + * Round up to the cipher block size, ensuring we have at + * least one byte of padding (see below). + */ + outlen = (len+8) &~ 7; + { + unsigned char *tmp = snewn(outlen, unsigned char); + memcpy(tmp, outblob, len); + smemclr(outblob, len); + sfree(outblob); + outblob = tmp; + } + + /* + * Padding on OpenSSH keys is deterministic. The number of + * padding bytes is always more than zero, and always at most + * the cipher block length. The value of each padding byte is + * equal to the number of padding bytes. So a plaintext that's + * an exact multiple of the block size will be padded with 08 + * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a + * plaintext one byte less than a multiple of the block size + * will be padded with just 01. + * + * This enables the OpenSSL key decryption function to strip + * off the padding algorithmically and return the unpadded + * plaintext to the next layer: it looks at the final byte, and + * then expects to find that many bytes at the end of the data + * with the same value. Those are all removed and the rest is + * returned. + */ + assert(pos == len); + while (pos < outlen) { + outblob[pos++] = outlen - len; + } + /* * Invent an iv. Then derive encryption key from passphrase * and iv/salt: @@ -906,9 +1139,6 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, * - block C would be MD5(B || passphrase || iv) and so on * - encryption key is the first N bytes of A || B */ - struct MD5Context md5c; - unsigned char keybuf[32]; - for (i = 0; i < 8; i++) iv[i] = random_byte(); MD5Init(&md5c); @@ -929,6 +1159,12 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, smemclr(&md5c, sizeof(md5c)); smemclr(keybuf, sizeof(keybuf)); + } else { + /* + * If no encryption, the blob has exactly its original + * cleartext size. + */ + outlen = len; } /* diff --git a/ldisc.c b/ldisc.c index 31185406..0e864874 100644 --- a/ldisc.c +++ b/ldisc.c @@ -128,28 +128,19 @@ void ldisc_free(void *handle) sfree(ldisc); } +void ldisc_echoedit_update(void *handle) +{ + Ldisc ldisc = (Ldisc) handle; + frontend_echoedit_update(ldisc->frontend, ECHOING, EDITING); +} + void ldisc_send(void *handle, char *buf, int len, int interactive) { Ldisc ldisc = (Ldisc) handle; int keyflag = 0; - /* - * Called with len=0 when the options change. We must inform - * the front end in case it needs to know. - */ - if (len == 0) { - ldisc_update(ldisc->frontend, ECHOING, EDITING); - return; - } - /* - * If that wasn't true, then we expect ldisc->term to be non-NULL - * hereafter. (The only front ends which have an ldisc but no term - * are those which do networking but no terminal emulation, in - * which case they need the above if statement to handle - * ldisc_updates passed from the back ends, but should never send - * any actual input through this function.) - */ assert(ldisc->term); + assert(len); /* * Notify the front end that something was pressed, in case diff --git a/macosx/osxwin.m b/macosx/osxwin.m index b58742d9..3bf85cd8 100644 --- a/macosx/osxwin.m +++ b/macosx/osxwin.m @@ -907,7 +907,7 @@ void notify_remote_exit(void *frontend) [win notifyRemoteExit]; } -void ldisc_update(void *frontend, int echo, int edit) +void frontend_echoedit_update(void *frontend, int echo, int edit) { //SessionWindow *win = (SessionWindow *)frontend; /* diff --git a/pscp.c b/pscp.c index e56d760f..50ab90b5 100644 --- a/pscp.c +++ b/pscp.c @@ -60,16 +60,7 @@ const char *const appname = "PSCP"; */ #define MAX_SCP_BUFSIZE 16384 -void ldisc_send(void *handle, char *buf, int len, int interactive) -{ - /* - * This is only here because of the calls to ldisc_send(NULL, - * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc - * as an ldisc. So if we get called with any real data, I want - * to know about it. - */ - assert(len == 0); -} +void ldisc_echoedit_update(void *handle) { } static void tell_char(FILE * stream, char c) { diff --git a/psftp.c b/psftp.c index 1d331603..a8baad6d 100644 --- a/psftp.c +++ b/psftp.c @@ -2505,16 +2505,7 @@ void connection_fatal(void *frontend, char *fmt, ...) cleanup_exit(1); } -void ldisc_send(void *handle, char *buf, int len, int interactive) -{ - /* - * This is only here because of the calls to ldisc_send(NULL, - * 0) in ssh.c. Nothing in PSFTP actually needs to use the - * ldisc as an ldisc. So if we get called with any real data, I - * want to know about it. - */ - assert(len == 0); -} +void ldisc_echoedit_update(void *handle) { } /* * In psftp, all agent requests should be synchronous, so this is a diff --git a/putty.h b/putty.h index 5e043960..520de490 100644 --- a/putty.h +++ b/putty.h @@ -253,6 +253,7 @@ enum { KEX_DHGROUP14, KEX_DHGEX, KEX_RSA, + KEX_ECDH, KEX_MAX }; @@ -601,7 +602,7 @@ void begin_session(void *frontend); void sys_cursor(void *frontend, int x, int y); void request_paste(void *frontend); void frontend_keypress(void *frontend); -void ldisc_update(void *frontend, int echo, int edit); +void frontend_echoedit_update(void *frontend, int echo, int edit); /* It's the backend's responsibility to invoke this at the start of a * connection, if necessary; it can also invoke it later if the set of * special commands changes. It does not need to invoke it at session @@ -1066,6 +1067,7 @@ void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *); void ldisc_configure(void *, Conf *); void ldisc_free(void *); void ldisc_send(void *handle, char *buf, int len, int interactive); +void ldisc_echoedit_update(void *handle); /* * Exports from ldiscucs.c. diff --git a/settings.c b/settings.c index a7a88785..a8c73ac5 100644 --- a/settings.c +++ b/settings.c @@ -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 a7f5882f..c4c4fb90 100644 --- 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 @@ -5826,7 +5832,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, ssh_special(ssh, TS_EOF); if (ssh->ldisc) - ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ + ldisc_echoedit_update(ssh->ldisc); /* cause ldisc to notice changes */ ssh->send_ok = 1; ssh->channels = newtree234(ssh_channelcmp); while (1) { @@ -5933,7 +5939,8 @@ static void ssh1_protocol(Ssh ssh, void *vin, int inlen, /* * Utility routine for decoding comma-separated strings in KEXINIT. */ -static int in_commasep_string(char *needle, char *haystack, int haylen) +static int in_commasep_string(char const *needle, char const *haystack, + int haylen) { int needlen; if (!needle || !haystack) /* protect against null pointers */ @@ -5964,7 +5971,8 @@ static int in_commasep_string(char *needle, char *haystack, int haylen) /* * Similar routine for checking whether we have the first string in a list. */ -static int first_in_commasep_string(char *needle, char *haystack, int haylen) +static int first_in_commasep_string(char const *needle, char const *haystack, + int haylen) { int needlen; if (!needle || !haystack) /* protect against null pointers */ @@ -5982,6 +5990,19 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen) return 0; } +/* + * Add a value to the comma-separated string at the end of the packet. + * If the value is already in the string, don't bother adding it again. + */ +static void ssh2_pkt_addstring_commasep(struct Packet *pkt, const char *data) +{ + if (in_commasep_string(data, (char *)pkt->data + pkt->savedpos, + pkt->length - pkt->savedpos)) return; + if (pkt->length - pkt->savedpos > 0) + ssh_pkt_addstring_str(pkt, ","); + ssh_pkt_addstring_str(pkt, data); +} + /* * SSH-2 key creation method. @@ -6037,6 +6058,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]; @@ -6076,7 +6098,7 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, begin_key_exchange: ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { - int i, j, k, commalist_started; + int i, j, k; /* * Set up the preferred key exchange. (NULL => warn below here) @@ -6100,6 +6122,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. */ @@ -6170,16 +6196,11 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte()); /* List key exchange algorithms. */ ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; for (i = 0; i < s->n_preferred_kex; i++) { const struct ssh_kexes *k = s->preferred_kex[i]; if (!k) continue; /* warning flag */ - for (j = 0; j < k->nkexes; j++) { - if (commalist_started) - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, k->list[j]->name); - commalist_started = 1; - } + for (j = 0; j < k->nkexes; j++) + ssh2_pkt_addstring_commasep(s->pktout, k->list[j]->name); } /* List server host key algorithms. */ if (!s->got_session_id) { @@ -6188,11 +6209,8 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, * we're prepared to cope with. */ ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < lenof(hostkey_algs); i++) { - ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name); - if (i < lenof(hostkey_algs) - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } + for (i = 0; i < lenof(hostkey_algs); i++) + ssh2_pkt_addstring_commasep(s->pktout, hostkey_algs[i]->name); } else { /* * In subsequent key exchanges, we list only the kex @@ -6207,26 +6225,18 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, /* List encryption algorithms (client->server then server->client). */ for (k = 0; k < 2; k++) { ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; - } + for (j = 0; j < c->nciphers; j++) + ssh2_pkt_addstring_commasep(s->pktout, c->list[j]->name); } } /* List MAC algorithms (client->server then server->client). */ for (j = 0; j < 2; j++) { ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } + for (i = 0; i < s->nmacs; i++) + ssh2_pkt_addstring_commasep(s->pktout, s->maclist[i]->name); } /* List client->server compression algorithms, * then server->client compression algorithms. (We use the @@ -6235,25 +6245,18 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_addstring_start(s->pktout); assert(lenof(compressions) > 1); /* Prefer non-delayed versions */ - ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name); + ssh2_pkt_addstring_commasep(s->pktout, s->preferred_comp->name); /* We don't even list delayed versions of algorithms until * they're allowed to be used, to avoid a race. See the end of * this function. */ - if (s->userauth_succeeded && s->preferred_comp->delayed_name) { - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, - s->preferred_comp->delayed_name); - } + if (s->userauth_succeeded && s->preferred_comp->delayed_name) + ssh2_pkt_addstring_commasep(s->pktout, + s->preferred_comp->delayed_name); for (i = 0; i < lenof(compressions); i++) { const struct ssh_compress *c = compressions[i]; - if (c != s->preferred_comp) { - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->name); - if (s->userauth_succeeded && c->delayed_name) { - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->delayed_name); - } - } + ssh2_pkt_addstring_commasep(s->pktout, c->name); + if (s->userauth_succeeded && c->delayed_name) + ssh2_pkt_addstring_commasep(s->pktout, c->delayed_name); } } /* List client->server languages. Empty list. */ @@ -6666,6 +6669,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); @@ -10246,7 +10326,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Transfer data! */ if (ssh->ldisc) - ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ + ldisc_echoedit_update(ssh->ldisc); /* cause ldisc to notice changes */ if (ssh->mainchan) ssh->send_ok = 1; while (1) { diff --git a/ssh.h b/ssh.h index 89f772d6..2f4dd61c 100644 --- a/ssh.h +++ b/ssh.h @@ -99,6 +99,38 @@ struct dss_key { Bignum p, q, g, y, x; }; +struct ec_curve; + +struct ec_point { + const struct ec_curve *curve; + Bignum x, y; + Bignum z; /* Jacobian denominator */ + unsigned char infinity; +}; + +void ec_point_free(struct ec_point *point); + +struct ec_curve { + unsigned int fieldBits; + Bignum p, a, b, n; + struct ec_point G; +}; + +extern unsigned char nistp256_oid[]; +extern unsigned char nistp384_oid[]; +extern unsigned char nistp521_oid[]; +extern int nistp256_oid_len; +extern int nistp384_oid_len; +extern int nistp521_oid_len; +struct ec_curve *ec_p256(void); +struct ec_curve *ec_p384(void); +struct ec_curve *ec_p521(void); + +struct ec_key { + struct ec_point publicKey; + Bignum privateKey; +}; + int makekey(unsigned char *data, int len, struct RSAKey *result, unsigned char **keystr, int order); int makeprivate(unsigned char *data, int len, struct RSAKey *result); @@ -141,6 +173,20 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen, unsigned char *out, int outlen, void *key); +/* + * SSH2 ECDH key exchange functions + */ +void *ssh_ecdhkex_newkey(struct ec_curve *curve); +void ssh_ecdhkex_freekey(void *key); +char *ssh_ecdhkex_getpublic(void *key, int *len); +Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen); + +/* + * Helper function for k generation in DSA, reused in ECDSA + */ +Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key, + unsigned char *digest, int digest_len); + typedef struct { uint32 h[4]; } MD5_Core_State; @@ -198,10 +244,15 @@ typedef struct { int blkused; uint32 len[4]; } SHA512_State; +#define SHA384_State SHA512_State void SHA512_Init(SHA512_State * s); void SHA512_Bytes(SHA512_State * s, const void *p, int len); void SHA512_Final(SHA512_State * s, unsigned char *output); void SHA512_Simple(const void *p, int len, unsigned char *output); +void SHA384_Init(SHA384_State * s); +#define SHA384_Bytes(s, p, len) SHA512_Bytes(s, p, len) +void SHA384_Final(SHA384_State * s, unsigned char *output); +void SHA384_Simple(const void *p, int len, unsigned char *output); struct ssh_cipher { void *(*make_context)(void); @@ -260,7 +311,7 @@ struct ssh_hash { struct ssh_kex { char *name, *groupname; - enum { KEXTYPE_DH, KEXTYPE_RSA } main_type; + enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH } main_type; /* For DH */ const unsigned char *pdata, *gdata; /* NULL means group exchange */ int plen, glen; @@ -316,7 +367,7 @@ struct ssh2_userkey { }; /* The maximum length of any hash algorithm used in kex. (bytes) */ -#define SSH2_KEX_MAX_HASH_LEN (32) /* SHA-256 */ +#define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */ extern const struct ssh_cipher ssh_3des; extern const struct ssh_cipher ssh_des; @@ -328,12 +379,18 @@ extern const struct ssh2_ciphers ssh2_blowfish; extern const struct ssh2_ciphers ssh2_arcfour; extern const struct ssh_hash ssh_sha1; extern const struct ssh_hash ssh_sha256; +extern const struct ssh_hash ssh_sha384; +extern const struct ssh_hash ssh_sha512; extern const struct ssh_kexes ssh_diffiehellman_group1; extern const struct ssh_kexes ssh_diffiehellman_group14; extern const struct ssh_kexes ssh_diffiehellman_gex; extern const struct ssh_kexes ssh_rsa_kex; +extern const struct ssh_kexes ssh_ecdh_kex; extern const struct ssh_signkey ssh_dss; extern const struct ssh_signkey ssh_rsa; +extern const struct ssh_signkey ssh_ecdsa_nistp256; +extern const struct ssh_signkey ssh_ecdsa_nistp384; +extern const struct ssh_signkey ssh_ecdsa_nistp521; extern const struct ssh_mac ssh_hmac_md5; extern const struct ssh_mac ssh_hmac_sha1; extern const struct ssh_mac ssh_hmac_sha1_buggy; @@ -502,9 +559,11 @@ Bignum bignum_from_long(unsigned long n); void freebn(Bignum b); Bignum modpow(Bignum base, Bignum exp, Bignum mod); Bignum modmul(Bignum a, Bignum b, Bignum mod); +Bignum modsub(const Bignum a, const Bignum b, const Bignum n); void decbn(Bignum n); extern Bignum Zero, One; Bignum bignum_from_bytes(const unsigned char *data, int nbytes); +Bignum bignum_random_in_range(const Bignum lower, const Bignum upper); int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result); int bignum_bitcount(Bignum bn); int ssh1_bignum_length(Bignum bn); @@ -525,6 +584,7 @@ Bignum bigmod(Bignum a, Bignum b); Bignum modinv(Bignum number, Bignum modulus); Bignum bignum_bitmask(Bignum number); Bignum bignum_rshift(Bignum number, int shift); +Bignum bignum_lshift(Bignum number, int shift); int bignum_cmp(Bignum a, Bignum b); char *bignum_decimal(Bignum x); @@ -617,6 +677,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam); int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, void *pfnparam); +int ec_generate(struct ec_key *key, int bits, progfn_t pfn, + void *pfnparam); Bignum primegen(int bits, int modulus, int residue, Bignum factor, int phase, progfn_t pfn, void *pfnparam, unsigned firstbits); void invent_firstbits(unsigned *one, unsigned *two); @@ -726,6 +788,10 @@ void platform_ssh_share_cleanup(const char *name); #define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */ #define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */ #define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */ +#define SSH2_MSG_KEX_ECDH_INIT 30 /* 0x1e */ +#define SSH2_MSG_KEX_ECDH_REPLY 31 /* 0x1f */ +#define SSH2_MSG_KEX_ECMQV_INIT 30 /* 0x1e */ +#define SSH2_MSG_KEX_ECMQV_REPLY 31 /* 0x1f */ #define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ #define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ #define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ diff --git a/sshbn.c b/sshbn.c index b1ea5d63..8da7d8a0 100644 --- a/sshbn.c +++ b/sshbn.c @@ -1092,6 +1092,38 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod) return result; } +Bignum modsub(const Bignum a, const Bignum b, const Bignum n) +{ + Bignum a1, b1, ret; + + if (bignum_cmp(a, n) >= 0) a1 = bigmod(a, n); + else a1 = a; + if (bignum_cmp(b, n) >= 0) b1 = bigmod(b, n); + else b1 = b; + + if (bignum_cmp(a1, b1) >= 0) /* a >= b */ + { + ret = bigsub(a1, b1); + } + else + { + /* Handle going round the corner of the modulus without having + * negative support in Bignum */ + Bignum tmp = bigsub(n, b1); + if (tmp) { + ret = bigadd(tmp, a1); + freebn(tmp); + } else { + ret = NULL; + } + } + + if (a != a1) freebn(a1); + if (b != b1) freebn(b1); + + return ret; +} + /* * Compute p % mod. * The most significant word of mod MUST be non-zero. @@ -1201,6 +1233,39 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes) return result; } +Bignum bignum_random_in_range(const Bignum lower, const Bignum upper) +{ + Bignum ret = NULL; + unsigned char *bytes; + int upper_len = bignum_bitcount(upper); + int upper_bytes = upper_len / 8; + int upper_bits = upper_len % 8; + if (upper_bits) ++upper_bytes; + + bytes = snewn(upper_bytes, unsigned char); + do { + int i; + + if (ret) freebn(ret); + + for (i = 0; i < upper_bytes; ++i) + { + bytes[i] = (unsigned char)random_byte(); + } + /* Mask the top to reduce failure rate to 50/50 */ + if (upper_bits) + { + bytes[i - 1] &= 0xFF >> (8 - upper_bits); + } + + ret = bignum_from_bytes(bytes, upper_bytes); + } while (bignum_cmp(ret, lower) < 0 || bignum_cmp(ret, upper) > 0); + smemclr(bytes, upper_bytes); + sfree(bytes); + + return ret; +} + /* * Read an SSH-1-format bignum from a data buffer. Return the number * of bytes consumed, or -1 if there wasn't enough data. @@ -1375,6 +1440,45 @@ Bignum bignum_rshift(Bignum a, int shift) return ret; } +/* + * Left-shift one bignum to form another. + */ +Bignum bignum_lshift(Bignum a, int shift) +{ + Bignum ret; + int bits, shiftWords, shiftBits; + + assert(shift >= 0); + + bits = bignum_bitcount(a) + shift; + ret = newbn((bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS); + if (!ret) return NULL; + + shiftWords = shift / BIGNUM_INT_BITS; + shiftBits = shift % BIGNUM_INT_BITS; + + if (shiftBits == 0) + { + memcpy(&ret[1 + shiftWords], &a[1], sizeof(BignumInt) * a[0]); + } + else + { + int i; + BignumInt carry = 0; + + /* Remember that Bignum[0] is length, so add 1 */ + for (i = shiftWords + 1; i < ((int)a[0]) + shiftWords + 1; ++i) + { + BignumInt from = a[i - shiftWords]; + ret[i] = (from << shiftBits) | carry; + carry = from >> (BIGNUM_INT_BITS - shiftBits); + } + if (carry) ret[i] = carry; + } + + return ret; +} + /* * Non-modular multiplication and addition. */ diff --git a/sshdss.c b/sshdss.c index 84fcdac7..3682ad69 100644 --- a/sshdss.c +++ b/sshdss.c @@ -518,7 +518,8 @@ static int dss_pubkey_bits(void *blob, int len) return ret; } -static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) +Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key, + unsigned char *digest, int digest_len) { /* * The basic DSS signing algorithm is: @@ -591,21 +592,16 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) * Computer Security Group for helping to argue out all the * fine details. */ - struct dss_key *dss = (struct dss_key *) key; SHA512_State ss; - unsigned char digest[20], digest512[64]; - Bignum proto_k, k, gkp, hash, kinv, hxr, r, s; - unsigned char *bytes; - int nbytes, i; - - SHA_Simple(data, datalen, digest); + unsigned char digest512[64]; + Bignum proto_k, k; /* * Hash some identifying text plus x. */ SHA512_Init(&ss); - SHA512_Bytes(&ss, "DSA deterministic k generator", 30); - sha512_mpint(&ss, dss->x); + SHA512_Bytes(&ss, id_string, strlen(id_string) + 1); + sha512_mpint(&ss, private_key); SHA512_Final(&ss, digest512); /* @@ -613,7 +609,7 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) */ SHA512_Init(&ss); SHA512_Bytes(&ss, digest512, sizeof(digest512)); - SHA512_Bytes(&ss, digest, sizeof(digest)); + SHA512_Bytes(&ss, digest, digest_len); while (1) { SHA512_State ss2 = ss; /* structure copy */ @@ -625,23 +621,37 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) * Now convert the result into a bignum, and reduce it mod q. */ proto_k = bignum_from_bytes(digest512, 64); - k = bigmod(proto_k, dss->q); + k = bigmod(proto_k, modulus); freebn(proto_k); - kinv = modinv(k, dss->q); /* k^-1 mod q */ - if (!kinv) { /* very unlikely */ - freebn(k); - /* Perturb the hash to think of a different k. */ - SHA512_Bytes(&ss, "x", 1); - /* Go round and try again. */ - continue; + + if (bignum_cmp(k, One) != 0 && bignum_cmp(k, Zero) != 0) { + smemclr(&ss, sizeof(ss)); + smemclr(digest512, sizeof(digest512)); + return k; } - break; + /* Very unlikely we get here, but if so, k was unsuitable. */ + freebn(k); + /* Perturb the hash to think of a different k. */ + SHA512_Bytes(&ss, "x", 1); + /* Go round and try again. */ } +} - smemclr(&ss, sizeof(ss)); +static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) +{ + struct dss_key *dss = (struct dss_key *) key; + Bignum k, gkp, hash, kinv, hxr, r, s; + unsigned char digest[20]; + unsigned char *bytes; + int nbytes, i; + + SHA_Simple(data, datalen, digest); - smemclr(digest512, sizeof(digest512)); + k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x, + digest, sizeof(digest)); + kinv = modinv(k, dss->q); /* k^-1 mod q */ + assert(kinv); /* * Now we have k, so just go ahead and compute the signature. diff --git a/sshecc.c b/sshecc.c new file mode 100644 index 00000000..10c4fc11 --- /dev/null +++ b/sshecc.c @@ -0,0 +1,2111 @@ +/* + * 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 +#include + +#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 (!XmZ2) { + 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)) { + freebn(point->x); + point->x = NULL; + freebn(point->y); + point->y = NULL; + 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-", string "", 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); + return NULL; + } + + 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 /* nistpXXX */ + + 4 + 1 + (pointlen * 2) /* 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); + + freebn(r); + freebn(s); + + 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 index 00000000..049967d6 --- /dev/null +++ b/sshecdsag.c @@ -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; +} diff --git a/sshpubk.c b/sshpubk.c index 87f1ebf3..cd35afd5 100644 --- 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; } diff --git a/sshsh512.c b/sshsh512.c index 8d0b1ae3..cd2b296e 100644 --- a/sshsh512.c +++ b/sshsh512.c @@ -2,6 +2,8 @@ * SHA-512 algorithm as described at * * http://csrc.nist.gov/cryptval/shs.html + * + * Modifications made for SHA-384 also */ #include "ssh.h" @@ -61,6 +63,22 @@ static void SHA512_Core_Init(SHA512_State *s) { s->h[i] = iv[i]; } +static void SHA384_Core_Init(SHA512_State *s) { + static const uint64 iv[] = { + INIT(0xcbbb9d5d, 0xc1059ed8), + INIT(0x629a292a, 0x367cd507), + INIT(0x9159015a, 0x3070dd17), + INIT(0x152fecd8, 0xf70e5939), + INIT(0x67332667, 0xffc00b31), + INIT(0x8eb44a87, 0x68581511), + INIT(0xdb0c2e0d, 0x64f98fa7), + INIT(0x47b5481d, 0xbefa4fa4), + }; + int i; + for (i = 0; i < 8; i++) + s->h[i] = iv[i]; +} + static void SHA512_Block(SHA512_State *s, uint64 *block) { uint64 w[80]; uint64 a,b,c,d,e,f,g,h; @@ -175,6 +193,14 @@ void SHA512_Init(SHA512_State *s) { s->len[i] = 0; } +void SHA384_Init(SHA512_State *s) { + int i; + SHA384_Core_Init(s); + s->blkused = 0; + for (i = 0; i < 4; i++) + s->len[i] = 0; +} + void SHA512_Bytes(SHA512_State *s, const void *p, int len) { unsigned char *q = (unsigned char *)p; uint64 wordblock[16]; @@ -268,6 +294,12 @@ void SHA512_Final(SHA512_State *s, unsigned char *digest) { } } +void SHA384_Final(SHA512_State *s, unsigned char *digest) { + unsigned char biggerDigest[512 / 8]; + SHA512_Final(s, biggerDigest); + memcpy(digest, biggerDigest, 384 / 8); +} + void SHA512_Simple(const void *p, int len, unsigned char *output) { SHA512_State s; @@ -276,6 +308,67 @@ void SHA512_Simple(const void *p, int len, unsigned char *output) { SHA512_Final(&s, output); } +void SHA384_Simple(const void *p, int len, unsigned char *output) { + SHA512_State s; + + SHA384_Init(&s); + SHA512_Bytes(&s, p, len); + SHA384_Final(&s, output); +} + +/* + * Thin abstraction for things where hashes are pluggable. + */ + +static void *sha512_init(void) +{ + SHA512_State *s; + + s = snew(SHA512_State); + SHA512_Init(s); + return s; +} + +static void sha512_bytes(void *handle, void *p, int len) +{ + SHA512_State *s = handle; + + SHA512_Bytes(s, p, len); +} + +static void sha512_final(void *handle, unsigned char *output) +{ + SHA512_State *s = handle; + + SHA512_Final(s, output); + sfree(s); +} + +const struct ssh_hash ssh_sha512 = { + sha512_init, sha512_bytes, sha512_final, 64, "SHA-512" +}; + +static void *sha384_init(void) +{ + SHA512_State *s; + + s = snew(SHA512_State); + SHA384_Init(s); + return s; +} + +static void sha384_final(void *handle, unsigned char *output) +{ + SHA512_State *s = handle; + + SHA384_Final(s, output); + sfree(s); +} + +const struct ssh_hash ssh_sha384 = { + sha384_init, sha512_bytes, sha384_final, 48, "SHA-384" +}; + #ifdef TEST #include diff --git a/telnet.c b/telnet.c index 098db292..8717a972 100644 --- a/telnet.c +++ b/telnet.c @@ -264,7 +264,7 @@ static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled) else if (o->option == TELOPT_SGA && o->send == DO) telnet->editing = !enabled; if (telnet->ldisc) /* cause ldisc to notice the change */ - ldisc_send(telnet->ldisc, NULL, 0, 0); + ldisc_echoedit_update(telnet->ldisc); /* Ensure we get the minimum options */ if (!telnet->activated) { diff --git a/terminal.c b/terminal.c index ac8f5dff..7570a63b 100644 --- a/terminal.c +++ b/terminal.c @@ -1350,7 +1350,7 @@ void term_pwron(Terminal *term, int clear) { power_on(term, clear); if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); term->disptop = 0; deselect(term); term_update(term); @@ -2574,7 +2574,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) case 10: /* DECEDM: set local edit mode */ term->term_editing = state; if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); break; case 25: /* DECTCEM: enable/disable cursor */ compatibility2(OTHER, VT220); @@ -2638,7 +2638,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) case 12: /* SRM: set echo mode */ term->term_echoing = !state; if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); break; case 20: /* LNM: Return sends ... */ term->cr_lf_return = state; @@ -3361,7 +3361,7 @@ static void term_out(Terminal *term) compatibility(VT100); power_on(term, TRUE); if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); if (term->reset_132) { if (!term->no_remote_resize) request_resize(term->frontend, 80, term->rows); diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 2b8fec17..9fd28ce7 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -3444,7 +3444,7 @@ static void licence_clicked(GtkButton *button, gpointer data) "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas " "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, " - "Markus Kuhn, Colin Watson, and CORE SDI S.A.\n\n" + "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\n\n" "Permission is hereby granted, free of charge, to any person " "obtaining a copy of this software and associated documentation " diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 4cce11e6..f3a17d58 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -194,7 +194,7 @@ int platform_default_i(const char *name, int def) } /* Dummy routine, only required in plink. */ -void ldisc_update(void *frontend, int echo, int edit) +void frontend_echoedit_update(void *frontend, int echo, int edit) { } @@ -3020,7 +3020,7 @@ void reset_terminal_menuitem(GtkMenuItem *item, gpointer data) struct gui_data *inst = (struct gui_data *)data; term_pwron(inst->term, TRUE); if (inst->ldisc) - ldisc_send(inst->ldisc, NULL, 0, 0); + ldisc_echoedit_update(inst->ldisc); } void copy_all_menuitem(GtkMenuItem *item, gpointer data) @@ -3088,7 +3088,7 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) */ if (inst->ldisc) { ldisc_configure(inst->ldisc, inst->conf); - ldisc_send(inst->ldisc, NULL, 0, 0); + ldisc_echoedit_update(inst->ldisc); } /* Pass new config data to the terminal */ term_reconfig(inst->term, inst->conf); @@ -3924,7 +3924,7 @@ int pt_main(int argc, char **argv) start_backend(inst); - ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ + ldisc_echoedit_update(inst->ldisc); /* cause ldisc to notice changes */ /* now we're reday to deal with the child exit handler being * called */ diff --git a/unix/uxplink.c b/unix/uxplink.c index 3bcec398..4f991ba8 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -152,7 +152,7 @@ int term_ldisc(Terminal *term, int mode) { return FALSE; } -void ldisc_update(void *frontend, int echo, int edit) +void frontend_echoedit_update(void *frontend, int echo, int edit) { /* Update stdin read mode to reflect changes in line discipline. */ struct termios mode; @@ -178,8 +178,9 @@ void ldisc_update(void *frontend, int echo, int edit) mode.c_cc[VMIN] = 1; mode.c_cc[VTIME] = 0; /* FIXME: perhaps what we do with IXON/IXOFF should be an - * argument to ldisc_update(), to allow implementation of SSH-2 - * "xon-xoff" and Rlogin's equivalent? */ + * argument to frontend_echoedit_update(), to allow + * implementation of SSH-2 "xon-xoff" and Rlogin's + * equivalent? */ mode.c_iflag &= ~IXON; mode.c_iflag &= ~IXOFF; } @@ -981,7 +982,7 @@ int main(int argc, char **argv) */ local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0); atexit(cleanup_termios); - ldisc_update(NULL, 1, 1); + frontend_echoedit_update(NULL, 1, 1); sending = FALSE; now = GETTICKCOUNT(); diff --git a/windows/pageant.rc b/windows/pageant.rc index 20ec509b..dee8a12e 100644 --- a/windows/pageant.rc +++ b/windows/pageant.rc @@ -50,41 +50,42 @@ BEGIN END /* No accelerators used */ -214 DIALOG DISCARDABLE 50, 50, 226, 263 +214 DIALOG DISCARDABLE 50, 50, 226, 271 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 + DEFPUSHBUTTON "OK", IDOK, 98, 251, 44, 14 LTEXT "Copyright \251 1997-2015 Simon Tatham", 1000, 10, 10, 206, 8 LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 - LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 + LTEXT "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI", 1004, 10, 50, 206, 8 + LTEXT "S.A.", 1005, 10, 58, 206, 8 - LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 - LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 - LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 - LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 - LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 - LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 - LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8 + LTEXT "Permission is hereby granted, free of charge, to any person", 1006, 10, 74, 206, 8 + LTEXT "obtaining a copy of this software and associated documentation", 1007, 10, 82, 206, 8 + LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1008, 10, 90, 206, 8 + LTEXT "including without limitation the rights to use, copy, modify, merge,", 1009, 10, 98, 206, 8 + LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1010, 10, 106, 206, 8 + LTEXT "and to permit persons to whom the Software is furnished to do so,", 1011, 10, 114, 206, 8 + LTEXT "subject to the following conditions:", 1012, 10, 122, 206, 8 - LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8 - LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8 + LTEXT "The above copyright notice and this permission notice shall be", 1013, 10, 138, 206, 8 + LTEXT "included in all copies or substantial portions of the Software.", 1014, 10, 146, 206, 8 - LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8 - LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8 - LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8 - LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8 - LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8 - LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8 - LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8 - LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8 - LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8 - LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8 + LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1015, 10, 162, 206, 8 + LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1016, 10, 170, 206, 8 + LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1017, 10, 178, 206, 8 + LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1018, 10, 186, 206, 8 + LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1019, 10, 194, 206, 8 + LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1020, 10, 202, 206, 8 + LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1021, 10, 210, 206, 8 + LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1022, 10, 218, 206, 8 + LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1023, 10, 226, 206, 8 + LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1024, 10, 234, 206, 8 END diff --git a/windows/puttygen.rc b/windows/puttygen.rc index 9547e754..8982674a 100644 --- a/windows/puttygen.rc +++ b/windows/puttygen.rc @@ -43,41 +43,42 @@ BEGIN END /* No accelerators used */ -214 DIALOG DISCARDABLE 50, 50, 226, 263 +214 DIALOG DISCARDABLE 50, 50, 226, 271 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 + DEFPUSHBUTTON "OK", IDOK, 98, 251, 44, 14 LTEXT "Copyright \251 1997-2015 Simon Tatham", 1000, 10, 10, 206, 8 LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 - LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 + LTEXT "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI", 1004, 10, 50, 206, 8 + LTEXT "S.A.", 1005, 10, 58, 206, 8 - LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 - LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 - LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 - LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 - LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 - LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 - LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8 + LTEXT "Permission is hereby granted, free of charge, to any person", 1006, 10, 74, 206, 8 + LTEXT "obtaining a copy of this software and associated documentation", 1007, 10, 82, 206, 8 + LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1008, 10, 90, 206, 8 + LTEXT "including without limitation the rights to use, copy, modify, merge,", 1009, 10, 98, 206, 8 + LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1010, 10, 106, 206, 8 + LTEXT "and to permit persons to whom the Software is furnished to do so,", 1011, 10, 114, 206, 8 + LTEXT "subject to the following conditions:", 1012, 10, 122, 206, 8 - LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8 - LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8 + LTEXT "The above copyright notice and this permission notice shall be", 1013, 10, 138, 206, 8 + LTEXT "included in all copies or substantial portions of the Software.", 1014, 10, 146, 206, 8 - LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8 - LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8 - LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8 - LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8 - LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8 - LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8 - LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8 - LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8 - LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8 - LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8 + LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1015, 10, 162, 206, 8 + LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1016, 10, 170, 206, 8 + LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1017, 10, 178, 206, 8 + LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1018, 10, 186, 206, 8 + LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1019, 10, 194, 206, 8 + LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1020, 10, 202, 206, 8 + LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1021, 10, 210, 206, 8 + LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1022, 10, 218, 206, 8 + LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1023, 10, 226, 206, 8 + LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1024, 10, 234, 206, 8 END diff --git a/windows/win_res.rc2 b/windows/win_res.rc2 index b7dfd945..744780ba 100644 --- a/windows/win_res.rc2 +++ b/windows/win_res.rc2 @@ -51,41 +51,42 @@ BEGIN END /* No accelerators used */ -IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 263 +IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 271 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 + DEFPUSHBUTTON "OK", IDOK, 98, 251, 44, 14 LTEXT "Copyright \251 1997-2015 Simon Tatham", 1000, 10, 10, 206, 8 LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 - LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 + LTEXT "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI", 1004, 10, 50, 206, 8 + LTEXT "S.A.", 1005, 10, 58, 206, 8 - LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 - LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 - LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 - LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 - LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 - LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 - LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8 + LTEXT "Permission is hereby granted, free of charge, to any person", 1006, 10, 74, 206, 8 + LTEXT "obtaining a copy of this software and associated documentation", 1007, 10, 82, 206, 8 + LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1008, 10, 90, 206, 8 + LTEXT "including without limitation the rights to use, copy, modify, merge,", 1009, 10, 98, 206, 8 + LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1010, 10, 106, 206, 8 + LTEXT "and to permit persons to whom the Software is furnished to do so,", 1011, 10, 114, 206, 8 + LTEXT "subject to the following conditions:", 1012, 10, 122, 206, 8 - LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8 - LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8 + LTEXT "The above copyright notice and this permission notice shall be", 1013, 10, 138, 206, 8 + LTEXT "included in all copies or substantial portions of the Software.", 1014, 10, 146, 206, 8 - LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8 - LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8 - LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8 - LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8 - LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8 - LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8 - LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8 - LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8 - LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8 - LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8 + LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1015, 10, 162, 206, 8 + LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1016, 10, 170, 206, 8 + LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1017, 10, 178, 206, 8 + LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1018, 10, 186, 206, 8 + LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1019, 10, 194, 206, 8 + LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1020, 10, 202, 206, 8 + LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1021, 10, 210, 206, 8 + LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1022, 10, 218, 206, 8 + LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1023, 10, 226, 206, 8 + LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1024, 10, 234, 206, 8 END diff --git a/windows/window.c b/windows/window.c index 7c320a0a..04059f06 100644 --- a/windows/window.c +++ b/windows/window.c @@ -219,7 +219,7 @@ const int share_can_be_downstream = TRUE; const int share_can_be_upstream = TRUE; /* Dummy routine, only required in plink. */ -void ldisc_update(void *frontend, int echo, int edit) +void frontend_echoedit_update(void *frontend, int echo, int edit) { } @@ -2236,7 +2236,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, */ if (ldisc) { ldisc_configure(ldisc, conf); - ldisc_send(ldisc, NULL, 0, 0); + ldisc_echoedit_update(ldisc); } if (pal) DeleteObject(pal); @@ -2379,7 +2379,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case IDM_RESET: term_pwron(term, TRUE); if (ldisc) - ldisc_send(ldisc, NULL, 0, 0); + ldisc_echoedit_update(ldisc); break; case IDM_ABOUT: showabout(hwnd); diff --git a/windows/winpgen.c b/windows/winpgen.c index 33d76c64..8f263efc 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -315,6 +315,8 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, return 0; } +typedef enum {RSA, DSA, ECDSA} keytype; + /* * Thread to generate a key. */ @@ -322,9 +324,12 @@ struct rsa_key_thread_params { HWND progressbar; /* notify this with progress */ HWND dialog; /* notify this on completion */ int keysize; /* bits in key */ - int is_dsa; - struct RSAKey *key; - struct dss_key *dsskey; + keytype keytype; + union { + struct RSAKey *key; + struct dss_key *dsskey; + struct ec_key *eckey; + }; }; static DWORD WINAPI generate_rsa_key_thread(void *param) { @@ -335,8 +340,10 @@ static DWORD WINAPI generate_rsa_key_thread(void *param) progress_update(&prog, PROGFN_INITIALISE, 0, 0); - if (params->is_dsa) + if (params->keytype == DSA) dsa_generate(params->dsskey, params->keysize, progress_update, &prog); + else if (params->keytype == ECDSA) + ec_generate(params->eckey, params->keysize, progress_update, &prog); else rsa_generate(params->key, params->keysize, progress_update, &prog); @@ -352,12 +359,16 @@ struct MainDlgState { int key_exists; int entropy_got, entropy_required, entropy_size; int keysize; - int ssh2, is_dsa; + int ssh2; + keytype keytype; char **commentptr; /* points to key.comment or ssh2key.comment */ struct ssh2_userkey ssh2key; unsigned *entropy; - struct RSAKey key; - struct dss_key dsskey; + union { + struct RSAKey key; + struct dss_key dsskey; + struct ec_key eckey; + }; HMENU filemenu, keymenu, cvtmenu; }; @@ -519,6 +530,7 @@ enum { IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB, IDC_BOX_PARAMS, IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA, + IDC_KEYSSH2ECDSA, IDC_BITSSTATIC, IDC_BITS, IDC_ABOUT, IDC_GIVEHELP, @@ -555,6 +567,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); + EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); @@ -563,6 +576,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND); + EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, + MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH, MF_GRAYED|MF_BYCOMMAND); @@ -580,6 +595,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0); + EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0); EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); @@ -588,6 +604,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND); + EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, + MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH, MF_GRAYED|MF_BYCOMMAND); @@ -605,6 +623,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); + EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND); @@ -613,6 +632,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND); + EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, + MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); /* * Enable export menu items if and only if the key type @@ -855,6 +876,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key"); + AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key"); state->keymenu = menu1; @@ -896,7 +918,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, { struct ctlpos cp, cp2; - /* Accelerators used: acglops1rbd */ + /* Accelerators used: acglops1rbde */ ctlposinit(&cp, hwnd, 4, 4, 4); beginbox(&cp, "Key", IDC_BOX_KEY); @@ -930,16 +952,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, "&Save private key", IDC_SAVE); endbox(&cp); beginbox(&cp, "Parameters", IDC_BOX_PARAMS); - radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3, + radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 4, "SSH-&1 (RSA)", IDC_KEYSSH1, "SSH-2 &RSA", IDC_KEYSSH2RSA, - "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL); + "SSH-2 &DSA", IDC_KEYSSH2DSA, + "SSH-2 &ECDSA", IDC_KEYSSH2ECDSA, NULL); staticedit(&cp, "Number of &bits in a generated key:", IDC_BITSSTATIC, IDC_BITS, 20); endbox(&cp); } - CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA); - CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA, + CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA); + CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA, MF_BYCOMMAND); SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE); @@ -990,7 +1013,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS); params->dialog = hwnd; params->keysize = state->keysize; - params->is_dsa = state->is_dsa; + params->keytype = state->keytype; params->key = &state->key; params->dsskey = &state->dsskey; @@ -1011,6 +1034,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, case IDC_KEYSSH1: case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: + case IDC_KEYSSH2ECDSA: { state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); @@ -1019,6 +1043,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, LOWORD(wParam)); CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA, LOWORD(wParam), MF_BYCOMMAND); + CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, + LOWORD(wParam)); + CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, + IDC_KEYSSH2ECDSA, + LOWORD(wParam), MF_BYCOMMAND); } break; case IDC_QUIT: @@ -1070,7 +1099,12 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, state->keysize = DEFAULT_KEYSIZE; /* If we ever introduce a new key type, check it here! */ state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1); - state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA); + state->keytype = RSA; + if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { + state->keytype = DSA; + } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { + state->keytype = ECDSA; + } if (state->keysize < 256) { int ret = MessageBox(hwnd, "PuTTYgen will not generate a key" @@ -1083,6 +1117,20 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, state->keysize = 256; SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE); } + if (state->keytype == ECDSA && !(state->keysize == 256 || + state->keysize == 384 || + state->keysize == 521)) { + int ret = MessageBox(hwnd, + "Only 256, 384 and 521 bit elliptic" + " curves are supported.\n" + "Key length reset to 256. Continue?", + "PuTTYgen Warning", + MB_ICONWARNING | MB_OKCANCEL); + if (ret != IDOK) + break; + state->keysize = 256; + SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE); + } ui_set_state(hwnd, state, 1); SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg); state->key_exists = FALSE; @@ -1276,9 +1324,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0); if (state->ssh2) { - if (state->is_dsa) { + if (state->keytype == DSA) { state->ssh2key.data = &state->dsskey; state->ssh2key.alg = &ssh_dss; + } else if (state->keytype == ECDSA) { + state->ssh2key.data = &state->eckey; + if (state->eckey.publicKey.curve->fieldBits == 256) + state->ssh2key.alg = &ssh_ecdsa_nistp256; + else if (state->eckey.publicKey.curve->fieldBits == 384) + state->ssh2key.alg = &ssh_ecdsa_nistp384; + else + state->ssh2key.alg = &ssh_ecdsa_nistp521; } else { state->ssh2key.data = &state->key; state->ssh2key.alg = &ssh_rsa; @@ -1297,8 +1353,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, { struct tm tm; tm = ltime(); - if (state->is_dsa) + if (state->keytype == DSA) strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm); + else if (state->keytype == ECDSA) + strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm); else strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm); } @@ -1389,6 +1447,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, case IDC_KEYSSH1: case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: + case IDC_KEYSSH2ECDSA: topic = WINHELP_CTX_puttygen_keytype; break; case IDC_BITSSTATIC: case IDC_BITS: diff --git a/windows/winpgnt.c b/windows/winpgnt.c index 22b60788..828747da 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -349,22 +349,24 @@ static void keylist_update(void) } for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) { char *listentry, *p; - int fp_len; + int pos, fp_len; /* - * Replace two spaces in the fingerprint with tabs, for - * nice alignment in the box. + * Replace spaces with tabs in the fingerprint prefix, for + * nice alignment in the list box, until we encounter a : + * meaning we're into the fingerprint proper. */ p = skey->alg->fingerprint(skey->data); listentry = dupprintf("%s\t%s", p, skey->comment); fp_len = strlen(listentry); sfree(p); - p = strchr(listentry, ' '); - if (p && p < listentry + fp_len) - *p = '\t'; - p = strchr(listentry, ' '); - if (p && p < listentry + fp_len) - *p = '\t'; + pos = 0; + while (1) { + pos += strcspn(listentry + pos, " :"); + if (listentry[pos] == ':') + break; + listentry[pos++] = '\t'; + } SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, (LPARAM) listentry); @@ -1141,6 +1143,12 @@ static void answer_msg(void *msg) key->alg = &ssh_rsa; else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7)) key->alg = &ssh_dss; + else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19)) + key->alg = &ssh_ecdsa_nistp256; + else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19)) + key->alg = &ssh_ecdsa_nistp384; + else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19)) + key->alg = &ssh_ecdsa_nistp521; else { sfree(key); goto failure; @@ -1508,7 +1516,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg, keylist = hwnd; { - static int tabs[] = { 35, 60, 210 }; + static int tabs[] = { 35, 75, 250 }; SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS, sizeof(tabs) / sizeof(*tabs), (LPARAM) tabs); diff --git a/windows/winplink.c b/windows/winplink.c index 188d89bb..f154ebc9 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -98,7 +98,7 @@ int term_ldisc(Terminal *term, int mode) { return FALSE; } -void ldisc_update(void *frontend, int echo, int edit) +void frontend_echoedit_update(void *frontend, int echo, int edit) { /* Update stdin read mode to reflect changes in line discipline. */ DWORD mode;