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
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
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
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
{
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;
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);
}
}
+ 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;
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);
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);
{ "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 }
};
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) = @_;
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
* 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;
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;
*
* - 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;
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");
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.
*
* 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:
* - 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);
smemclr(&md5c, sizeof(md5c));
smemclr(keybuf, sizeof(keybuf));
+ } else {
+ /*
+ * If no encryption, the blob has exactly its original
+ * cleartext size.
+ */
+ outlen = len;
}
/*
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
[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;
/*
*/
#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)
{
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
KEX_DHGROUP14,
KEX_DHGEX,
KEX_RSA,
+ KEX_ECDH,
KEX_MAX
};
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
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.
};
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 },
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);
}
SSH2_PKTCTX_NOKEX,
SSH2_PKTCTX_DHGROUP,
SSH2_PKTCTX_DHGEX,
+ SSH2_PKTCTX_ECDHKEX,
SSH2_PKTCTX_RSAKEX
} Pkt_KCtx;
typedef enum {
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);
#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
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) {
/*
* 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 */
/*
* 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 */
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.
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];
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)
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. */
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) {
* 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
/* 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
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. */
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);
* 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) {
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);
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;
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);
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;
};
/* 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;
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;
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);
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);
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);
#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 */
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.
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.
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.
*/
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:
* 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);
/*
*/
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 */
* 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.
--- /dev/null
+/*
+ * Elliptic-curve crypto module for PuTTY
+ * Implements the three required curves, no optional curves
+ * NOTE: Only curves on prime field are handled by the maths functions
+ */
+
+/*
+ * References:
+ *
+ * Elliptic curves in SSH are specified in RFC 5656:
+ * http://tools.ietf.org/html/rfc5656
+ *
+ * That specification delegates details of public key formatting and a
+ * lot of underlying mechanism to SEC 1:
+ * http://www.secg.org/sec1-v2.pdf
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ssh.h"
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve definitions
+ */
+
+static int initialise_curve(struct ec_curve *curve, int bits, unsigned char *p,
+ unsigned char *a, unsigned char *b,
+ unsigned char *n, unsigned char *Gx,
+ unsigned char *Gy)
+{
+ int length = bits / 8;
+ if (bits % 8) ++length;
+
+ curve->fieldBits = bits;
+ curve->p = bignum_from_bytes(p, length);
+ if (!curve->p) goto error;
+
+ /* Curve co-efficients */
+ curve->a = bignum_from_bytes(a, length);
+ if (!curve->a) goto error;
+ curve->b = bignum_from_bytes(b, length);
+ if (!curve->b) goto error;
+
+ /* Group order and generator */
+ curve->n = bignum_from_bytes(n, length);
+ if (!curve->n) goto error;
+ curve->G.x = bignum_from_bytes(Gx, length);
+ if (!curve->G.x) goto error;
+ curve->G.y = bignum_from_bytes(Gy, length);
+ if (!curve->G.y) goto error;
+ curve->G.curve = curve;
+ curve->G.infinity = 0;
+
+ return 1;
+ error:
+ if (curve->p) freebn(curve->p);
+ if (curve->a) freebn(curve->a);
+ if (curve->b) freebn(curve->b);
+ if (curve->n) freebn(curve->n);
+ if (curve->G.x) freebn(curve->G.x);
+ return 0;
+}
+
+unsigned char nistp256_oid[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07};
+int nistp256_oid_len = 8;
+unsigned char nistp384_oid[] = {0x2b, 0x81, 0x04, 0x00, 0x22};
+int nistp384_oid_len = 5;
+unsigned char nistp521_oid[] = {0x2b, 0x81, 0x04, 0x00, 0x23};
+int nistp521_oid_len = 5;
+
+struct ec_curve *ec_p256(void)
+{
+ static struct ec_curve curve = { 0 };
+ static unsigned char initialised = 0;
+
+ if (!initialised)
+ {
+ unsigned char p[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ unsigned char a[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc
+ };
+ unsigned char b[] = {
+ 0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7,
+ 0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86, 0xbc,
+ 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6,
+ 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b
+ };
+ unsigned char n[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
+ 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
+ };
+ unsigned char Gx[] = {
+ 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
+ 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+ 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+ 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96
+ };
+ unsigned char Gy[] = {
+ 0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
+ 0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+ 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+ 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5
+ };
+
+ if (!initialise_curve(&curve, 256, p, a, b, n, Gx, Gy)) {
+ return NULL;
+ }
+
+ /* Now initialised, no need to do it again */
+ initialised = 1;
+ }
+
+ return &curve;
+}
+
+struct ec_curve *ec_p384(void)
+{
+ static struct ec_curve curve = { 0 };
+ static unsigned char initialised = 0;
+
+ if (!initialised)
+ {
+ unsigned char p[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+ };
+ unsigned char a[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc
+ };
+ unsigned char b[] = {
+ 0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4,
+ 0x98, 0x8e, 0x05, 0x6b, 0xe3, 0xf8, 0x2d, 0x19,
+ 0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, 0x41, 0x12,
+ 0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a,
+ 0xc6, 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d,
+ 0x2a, 0x85, 0xc8, 0xed, 0xd3, 0xec, 0x2a, 0xef
+ };
+ unsigned char n[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
+ 0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
+ 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73
+ };
+ unsigned char Gx[] = {
+ 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37,
+ 0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74,
+ 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
+ 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38,
+ 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c,
+ 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7
+ };
+ unsigned char Gy[] = {
+ 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f,
+ 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29,
+ 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+ 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0,
+ 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
+ 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f
+ };
+
+ if (!initialise_curve(&curve, 384, p, a, b, n, Gx, Gy)) {
+ return NULL;
+ }
+
+ /* Now initialised, no need to do it again */
+ initialised = 1;
+ }
+
+ return &curve;
+}
+
+struct ec_curve *ec_p521(void)
+{
+ static struct ec_curve curve = { 0 };
+ static unsigned char initialised = 0;
+
+ if (!initialised)
+ {
+ unsigned char p[] = {
+ 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff
+ };
+ unsigned char a[] = {
+ 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfc
+ };
+ unsigned char b[] = {
+ 0x00, 0x51, 0x95, 0x3e, 0xb9, 0x61, 0x8e, 0x1c,
+ 0x9a, 0x1f, 0x92, 0x9a, 0x21, 0xa0, 0xb6, 0x85,
+ 0x40, 0xee, 0xa2, 0xda, 0x72, 0x5b, 0x99, 0xb3,
+ 0x15, 0xf3, 0xb8, 0xb4, 0x89, 0x91, 0x8e, 0xf1,
+ 0x09, 0xe1, 0x56, 0x19, 0x39, 0x51, 0xec, 0x7e,
+ 0x93, 0x7b, 0x16, 0x52, 0xc0, 0xbd, 0x3b, 0xb1,
+ 0xbf, 0x07, 0x35, 0x73, 0xdf, 0x88, 0x3d, 0x2c,
+ 0x34, 0xf1, 0xef, 0x45, 0x1f, 0xd4, 0x6b, 0x50,
+ 0x3f, 0x00
+ };
+ unsigned char n[] = {
+ 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f,
+ 0x96, 0x6b, 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09,
+ 0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c,
+ 0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38,
+ 0x64, 0x09
+ };
+ unsigned char Gx[] = {
+ 0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04,
+ 0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
+ 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
+ 0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
+ 0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
+ 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
+ 0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
+ 0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
+ 0xbd, 0x66
+ };
+ unsigned char Gy[] = {
+ 0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b,
+ 0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
+ 0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+ 0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
+ 0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
+ 0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+ 0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
+ 0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
+ 0x66, 0x50
+ };
+
+ if (!initialise_curve(&curve, 521, p, a, b, n, Gx, Gy)) {
+ return NULL;
+ }
+
+ /* Now initialised, no need to do it again */
+ initialised = 1;
+ }
+
+ return &curve;
+}
+
+static struct ec_curve *ec_name_to_curve(char *name, int len) {
+ if (len == 8 && !memcmp(name, "nistp", 5)) {
+ name += 5;
+ if (!memcmp(name, "256", 3)) {
+ return ec_p256();
+ } else if (!memcmp(name, "384", 3)) {
+ return ec_p384();
+ } else if (!memcmp(name, "521", 3)) {
+ return ec_p521();
+ }
+ }
+
+ return NULL;
+}
+
+static int ec_curve_to_name(const struct ec_curve *curve, unsigned char *name, int len) {
+ /* Return length of string */
+ if (name == NULL) return 8;
+
+ /* Not enough space for the name */
+ if (len < 8) return 0;
+
+ /* Put the name in the buffer */
+ switch (curve->fieldBits) {
+ case 256:
+ memcpy(name+5, "256", 3);
+ break;
+ case 384:
+ memcpy(name+5, "384", 3);
+ break;
+ case 521:
+ memcpy(name+5, "521", 3);
+ break;
+ default:
+ return 0;
+ }
+
+ memcpy(name, "nistp", 5);
+ return 8;
+}
+
+/* Return 1 if a is -3 % p, otherwise return 0
+ * This is used because there are some maths optimisations */
+static int ec_aminus3(const struct ec_curve *curve)
+{
+ int ret;
+ Bignum _p;
+
+ _p = bignum_add_long(curve->a, 3);
+ if (!_p) return 0;
+
+ ret = !bignum_cmp(curve->p, _p);
+ freebn(_p);
+ return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve field maths
+ */
+
+static Bignum ecf_add(const Bignum a, const Bignum b,
+ const struct ec_curve *curve)
+{
+ Bignum a1, b1, ab, ret;
+
+ a1 = bigmod(a, curve->p);
+ if (!a1) return NULL;
+ b1 = bigmod(b, curve->p);
+ if (!b1)
+ {
+ freebn(a1);
+ return NULL;
+ }
+
+ ab = bigadd(a1, b1);
+ freebn(a1);
+ freebn(b1);
+ if (!ab) return NULL;
+
+ ret = bigmod(ab, curve->p);
+ freebn(ab);
+
+ return ret;
+}
+
+static Bignum ecf_square(const Bignum a, const struct ec_curve *curve)
+{
+ return modmul(a, a, curve->p);
+}
+
+static Bignum ecf_treble(const Bignum a, const struct ec_curve *curve)
+{
+ Bignum ret, tmp;
+
+ /* Double */
+ tmp = bignum_lshift(a, 1);
+ if (!tmp) return NULL;
+
+ /* Add itself (i.e. treble) */
+ ret = bigadd(tmp, a);
+ freebn(tmp);
+
+ /* Normalise */
+ while (ret != NULL && bignum_cmp(ret, curve->p) >= 0)
+ {
+ tmp = bigsub(ret, curve->p);
+ freebn(ret);
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+static Bignum ecf_double(const Bignum a, const struct ec_curve *curve)
+{
+ Bignum ret = bignum_lshift(a, 1);
+ if (!ret) return NULL;
+ if (bignum_cmp(ret, curve->p) >= 0)
+ {
+ Bignum tmp = bigsub(ret, curve->p);
+ freebn(ret);
+ return tmp;
+ }
+ else
+ {
+ return ret;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Memory functions
+ */
+
+void ec_point_free(struct ec_point *point)
+{
+ if (point == NULL) return;
+ point->curve = 0;
+ if (point->x) freebn(point->x);
+ if (point->y) freebn(point->y);
+ if (point->z) freebn(point->z);
+ point->infinity = 0;
+ sfree(point);
+}
+
+static struct ec_point *ec_point_new(const struct ec_curve *curve,
+ const Bignum x, const Bignum y, const Bignum z,
+ unsigned char infinity)
+{
+ struct ec_point *point = snewn(1, struct ec_point);
+ point->curve = curve;
+ point->x = x;
+ point->y = y;
+ point->z = z;
+ point->infinity = infinity ? 1 : 0;
+ return point;
+}
+
+static struct ec_point *ec_point_copy(const struct ec_point *a)
+{
+ if (a == NULL) return NULL;
+ return ec_point_new(a->curve,
+ a->x ? copybn(a->x) : NULL,
+ a->y ? copybn(a->y) : NULL,
+ a->z ? copybn(a->z) : NULL,
+ a->infinity);
+}
+
+static int ec_point_verify(const struct ec_point *a)
+{
+ if (a->infinity)
+ {
+ return 1;
+ }
+ else
+ {
+ /* Verify y^2 = x^3 + ax + b */
+ int ret = 0;
+
+ Bignum lhs = NULL, x3 = NULL, ax = NULL, x3ax = NULL, x3axm = NULL, x3axb = NULL, rhs = NULL;
+
+ Bignum Three = bignum_from_long(3);
+ if (!Three) return 0;
+
+ lhs = modmul(a->y, a->y, a->curve->p);
+ if (!lhs) goto error;
+
+ /* This uses montgomery multiplication to optimise */
+ x3 = modpow(a->x, Three, a->curve->p);
+ freebn(Three);
+ if (!x3) goto error;
+ ax = modmul(a->curve->a, a->x, a->curve->p);
+ if (!ax) goto error;
+ x3ax = bigadd(x3, ax);
+ if (!x3ax) goto error;
+ freebn(x3); x3 = NULL;
+ freebn(ax); ax = NULL;
+ x3axm = bigmod(x3ax, a->curve->p);
+ if (!x3axm) goto error;
+ freebn(x3ax); x3ax = NULL;
+ x3axb = bigadd(x3axm, a->curve->b);
+ if (!x3axb) goto error;
+ freebn(x3axm); x3axm = NULL;
+ rhs = bigmod(x3axb, a->curve->p);
+ if (!rhs) goto error;
+ freebn(x3axb);
+
+ ret = bignum_cmp(lhs, rhs) ? 0 : 1;
+ freebn(lhs);
+ freebn(rhs);
+
+ return ret;
+
+ error:
+ if (x3) freebn(x3);
+ if (ax) freebn(ax);
+ if (x3ax) freebn(x3ax);
+ if (x3axm) freebn(x3axm);
+ if (x3axb) freebn(x3axb);
+ if (lhs) freebn(lhs);
+ return 0;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve point maths
+ */
+
+/* Returns 1 on success and 0 on memory error */
+static int ecp_normalise(struct ec_point *a)
+{
+ Bignum Z2, Z2inv, Z3, Z3inv, tx, ty;
+
+ /* In Jacobian Coordinates the triple (X, Y, Z) represents
+ the affine point (X / Z^2, Y / Z^3) */
+ if (!a) {
+ /* No point */
+ return 0;
+ }
+ if (a->infinity) {
+ /* Point is at infinity - i.e. normalised */
+ return 1;
+ } else if (!a->x || !a->y) {
+ /* No point defined */
+ return 0;
+ } else if (!a->z) {
+ /* Already normalised */
+ return 1;
+ }
+
+ Z2 = ecf_square(a->z, a->curve);
+ if (!Z2) {
+ return 0;
+ }
+ Z2inv = modinv(Z2, a->curve->p);
+ if (!Z2inv) {
+ freebn(Z2);
+ return 0;
+ }
+ tx = modmul(a->x, Z2inv, a->curve->p);
+ freebn(Z2inv);
+ if (!tx) {
+ freebn(Z2);
+ return 0;
+ }
+
+ Z3 = modmul(Z2, a->z, a->curve->p);
+ freebn(Z2);
+ if (!Z3) {
+ freebn(tx);
+ return 0;
+ }
+ Z3inv = modinv(Z3, a->curve->p);
+ freebn(Z3);
+ if (!Z3inv) {
+ freebn(tx);
+ return 0;
+ }
+ ty = modmul(a->y, Z3inv, a->curve->p);
+ freebn(Z3inv);
+ if (!ty) {
+ freebn(tx);
+ return 0;
+ }
+
+ freebn(a->x);
+ a->x = tx;
+ freebn(a->y);
+ a->y = ty;
+ freebn(a->z);
+ a->z = NULL;
+ return 1;
+}
+
+static struct ec_point *ecp_double(const struct ec_point *a, const int aminus3)
+{
+ Bignum S, M, outx, outy, outz;
+
+ if (a->infinity || bignum_cmp(a->y, Zero) == 0)
+ {
+ /* Identity */
+ return ec_point_new(a->curve, NULL, NULL, NULL, 1);
+ }
+
+ /* S = 4*X*Y^2 */
+ {
+ Bignum Y2, XY2, _2XY2;
+
+ Y2 = ecf_square(a->y, a->curve);
+ if (!Y2) {
+ return NULL;
+ }
+ XY2 = modmul(a->x, Y2, a->curve->p);
+ freebn(Y2);
+ if (!XY2) {
+ return NULL;
+ }
+
+ _2XY2 = ecf_double(XY2, a->curve);
+ freebn(XY2);
+ if (!_2XY2) {
+ return NULL;
+ }
+ S = ecf_double(_2XY2, a->curve);
+ freebn(_2XY2);
+ if (!S) {
+ return NULL;
+ }
+ }
+
+ /* Faster calculation if a = -3 */
+ if (aminus3) {
+ /* if a = -3, then M can also be calculated as M = 3*(X + Z^2)*(X - Z^2) */
+ Bignum Z2, XpZ2, XmZ2, second;
+
+ if (a->z == NULL) {
+ Z2 = copybn(One);
+ } else {
+ Z2 = ecf_square(a->z, a->curve);
+ }
+ if (!Z2) {
+ freebn(S);
+ return NULL;
+ }
+
+ XpZ2 = ecf_add(a->x, Z2, a->curve);
+ if (!XpZ2) {
+ freebn(S);
+ freebn(Z2);
+ return NULL;
+ }
+ XmZ2 = modsub(a->x, Z2, a->curve->p);
+ freebn(Z2);
+ if (!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-<name>", string "<name>", 0x04 point x, y.
+ */
+ bloblen = 4 + 11 + namelen + 4 + namelen + 4 + 1 + (pointlen * 2);
+ blob = snewn(bloblen, unsigned char);
+
+ p = blob;
+ PUT_32BIT(p, 11 + namelen);
+ p += 4;
+ memcpy(p, "ecdsa-sha2-", 11);
+ p += 11;
+ p += ec_curve_to_name(ec->publicKey.curve, p, namelen);
+ PUT_32BIT(p, namelen);
+ p += 4;
+ p += ec_curve_to_name(ec->publicKey.curve, p, namelen);
+ PUT_32BIT(p, (2 * pointlen) + 1);
+ p += 4;
+ *p++ = 0x04;
+ for (i = pointlen; i--;)
+ *p++ = bignum_byte(ec->publicKey.x, i);
+ for (i = pointlen; i--;)
+ *p++ = bignum_byte(ec->publicKey.y, i);
+
+ assert(p == blob + bloblen);
+ *len = bloblen;
+
+ return blob;
+}
+
+static unsigned char *ecdsa_private_blob(void *key, int *len)
+{
+ struct ec_key *ec = (struct ec_key *) key;
+ int keylen, bloblen;
+ int i;
+ unsigned char *blob, *p;
+
+ if (!ec->privateKey) return NULL;
+
+ keylen = (bignum_bitcount(ec->privateKey) + 8) / 8;
+
+ /*
+ * mpint privateKey. Total 4 + keylen.
+ */
+ bloblen = 4 + keylen;
+ blob = snewn(bloblen, unsigned char);
+
+ p = blob;
+ PUT_32BIT(p, keylen);
+ p += 4;
+ for (i = keylen; i--;)
+ *p++ = bignum_byte(ec->privateKey, i);
+
+ assert(p == blob + bloblen);
+ *len = bloblen;
+ return blob;
+}
+
+static void *ecdsa_createkey(unsigned char *pub_blob, int pub_len,
+ unsigned char *priv_blob, int priv_len)
+{
+ struct ec_key *ec;
+ struct ec_point *publicKey;
+ char *pb = (char *) priv_blob;
+
+ ec = (struct ec_key*)ecdsa_newkey((char *) pub_blob, pub_len);
+ if (!ec) {
+ return NULL;
+ }
+
+ ec->privateKey = getmp(&pb, &priv_len);
+ if (!ec->privateKey) {
+ ecdsa_freekey(ec);
+ return NULL;
+ }
+
+ /* Check that private key generates public key */
+ publicKey = ecp_mul(&ec->publicKey.curve->G, ec->privateKey);
+
+ if (!publicKey ||
+ bignum_cmp(publicKey->x, ec->publicKey.x) ||
+ bignum_cmp(publicKey->y, ec->publicKey.y))
+ {
+ ecdsa_freekey(ec);
+ ec = NULL;
+ }
+ ec_point_free(publicKey);
+
+ return ec;
+}
+
+static void *ecdsa_openssh_createkey(unsigned char **blob, int *len)
+{
+ char **b = (char **) blob;
+ char *p;
+ int slen;
+ struct ec_key *ec;
+ struct ec_curve *curve;
+ struct ec_point *publicKey;
+
+ getstring(b, len, &p, &slen);
+
+ if (!p) {
+ return NULL;
+ }
+ curve = ec_name_to_curve(p, slen);
+ if (!curve) return NULL;
+
+ ec = snew(struct ec_key);
+
+ ec->publicKey.curve = curve;
+ ec->publicKey.infinity = 0;
+ ec->publicKey.x = NULL;
+ ec->publicKey.y = NULL;
+ ec->publicKey.z = NULL;
+ if (!getmppoint(b, len, &ec->publicKey)) {
+ ecdsa_freekey(ec);
+ return NULL;
+ }
+ ec->privateKey = NULL;
+
+ if (!ec->publicKey.x || !ec->publicKey.y ||
+ bignum_cmp(ec->publicKey.x, curve->p) >= 0 ||
+ bignum_cmp(ec->publicKey.y, curve->p) >= 0)
+ {
+ ecdsa_freekey(ec);
+ return NULL;
+ }
+
+ ec->privateKey = getmp(b, len);
+ if (ec->privateKey == NULL)
+ {
+ ecdsa_freekey(ec);
+ return NULL;
+ }
+
+ /* Now check that the private key makes the public key */
+ publicKey = ecp_mul(&ec->publicKey.curve->G, ec->privateKey);
+ if (!publicKey)
+ {
+ ecdsa_freekey(ec);
+ return NULL;
+ }
+
+ if (bignum_cmp(ec->publicKey.x, publicKey->x) ||
+ bignum_cmp(ec->publicKey.y, publicKey->y))
+ {
+ /* Private key doesn't make the public key on the given curve */
+ ecdsa_freekey(ec);
+ ec_point_free(publicKey);
+ 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 /* <LEN> nistpXXX */
+ + 4 + 1 + (pointlen * 2) /* <LEN> 0x04 pX pY */
+ + ssh2_bignum_length(ec->privateKey);
+
+ int i;
+
+ if (bloblen > len)
+ return bloblen;
+
+ bloblen = 0;
+
+ PUT_32BIT(blob+bloblen, namelen);
+ bloblen += 4;
+
+ bloblen += ec_curve_to_name(ec->publicKey.curve, blob+bloblen, namelen);
+
+ PUT_32BIT(blob+bloblen, 1 + (pointlen * 2));
+ bloblen += 4;
+ blob[bloblen++] = 0x04;
+ for (i = pointlen; i--; )
+ blob[bloblen++] = bignum_byte(ec->publicKey.x, i);
+ for (i = pointlen; i--; )
+ blob[bloblen++] = bignum_byte(ec->publicKey.y, i);
+
+ pointlen = (bignum_bitcount(ec->privateKey) + 8) / 8;
+ PUT_32BIT(blob+bloblen, pointlen);
+ bloblen += 4;
+ for (i = pointlen; i--; )
+ blob[bloblen++] = bignum_byte(ec->privateKey, i);
+
+ return bloblen;
+}
+
+static int ecdsa_pubkey_bits(void *blob, int len)
+{
+ struct ec_key *ec;
+ int ret;
+
+ ec = (struct ec_key*)ecdsa_newkey((char *) blob, len);
+ if (!ec)
+ return -1;
+ ret = ec->publicKey.curve->fieldBits;
+ ecdsa_freekey(ec);
+
+ return ret;
+}
+
+static char *ecdsa_fingerprint(void *key)
+{
+ struct ec_key *ec = (struct ec_key *) key;
+ struct MD5Context md5c;
+ unsigned char digest[16], lenbuf[4];
+ char *ret;
+ unsigned char *name;
+ int pointlen, namelen, i, j;
+
+ namelen = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+ name = snewn(namelen, unsigned char);
+ ec_curve_to_name(ec->publicKey.curve, name, namelen);
+
+ MD5Init(&md5c);
+
+ PUT_32BIT(lenbuf, namelen + 11);
+ MD5Update(&md5c, lenbuf, 4);
+ MD5Update(&md5c, (const unsigned char *)"ecdsa-sha2-", 11);
+ MD5Update(&md5c, name, namelen);
+
+ PUT_32BIT(lenbuf, namelen);
+ MD5Update(&md5c, lenbuf, 4);
+ MD5Update(&md5c, name, namelen);
+
+ pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+ PUT_32BIT(lenbuf, 1 + (pointlen * 2));
+ MD5Update(&md5c, lenbuf, 4);
+ MD5Update(&md5c, (const unsigned char *)"\x04", 1);
+ for (i = pointlen; i--; ) {
+ unsigned char c = bignum_byte(ec->publicKey.x, i);
+ MD5Update(&md5c, &c, 1);
+ }
+ for (i = pointlen; i--; ) {
+ unsigned char c = bignum_byte(ec->publicKey.y, i);
+ MD5Update(&md5c, &c, 1);
+ }
+
+ MD5Final(digest, &md5c);
+
+ ret = snewn(11 + namelen + 1 + (16 * 3), char);
+
+ i = 11;
+ memcpy(ret, "ecdsa-sha2-", 11);
+ memcpy(ret+i, name, namelen);
+ i += namelen;
+ sfree(name);
+ ret[i++] = ' ';
+ for (j = 0; j < 16; j++)
+ i += sprintf(ret + i, "%s%02x", j ? ":" : "", digest[j]);
+
+ return ret;
+}
+
+static int ecdsa_verifysig(void *key, char *sig, int siglen,
+ char *data, int datalen)
+{
+ struct ec_key *ec = (struct ec_key *) key;
+ char *p;
+ int slen;
+ unsigned char digest[512 / 8];
+ int digestLen;
+ Bignum r, s;
+ int ret;
+
+ if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
+ return 0;
+
+ /* Check the signature curve matches the key curve */
+ getstring(&sig, &siglen, &p, &slen);
+ if (!p || slen < 11 || memcmp(p, "ecdsa-sha2-", 11)) {
+ return 0;
+ }
+ if (ec->publicKey.curve != ec_name_to_curve(p+11, slen-11)) {
+ return 0;
+ }
+
+ getstring(&sig, &siglen, &p, &slen);
+ r = getmp(&p, &slen);
+ if (!r) return 0;
+ s = getmp(&p, &slen);
+ if (!s) {
+ freebn(r);
+ return 0;
+ }
+
+ /* Perform correct hash function depending on curve size */
+ if (ec->publicKey.curve->fieldBits <= 256) {
+ SHA256_Simple(data, datalen, digest);
+ digestLen = 256 / 8;
+ } else if (ec->publicKey.curve->fieldBits <= 384) {
+ SHA384_Simple(data, datalen, digest);
+ digestLen = 384 / 8;
+ } else {
+ SHA512_Simple(data, datalen, digest);
+ digestLen = 512 / 8;
+ }
+
+ /* Verify the signature */
+ if (!_ecdsa_verify(&ec->publicKey, digest, digestLen, r, s)) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ freebn(r);
+ freebn(s);
+
+ return ret;
+}
+
+static unsigned char *ecdsa_sign(void *key, char *data, int datalen,
+ int *siglen)
+{
+ struct ec_key *ec = (struct ec_key *) key;
+ unsigned char digest[512 / 8];
+ int digestLen;
+ Bignum r = NULL, s = NULL;
+ unsigned char *buf, *p;
+ int rlen, slen, namelen;
+ int i;
+
+ if (!ec->privateKey || !ec->publicKey.curve) {
+ return NULL;
+ }
+
+ /* Perform correct hash function depending on curve size */
+ if (ec->publicKey.curve->fieldBits <= 256) {
+ SHA256_Simple(data, datalen, digest);
+ digestLen = 256 / 8;
+ } else if (ec->publicKey.curve->fieldBits <= 384) {
+ SHA384_Simple(data, datalen, digest);
+ digestLen = 384 / 8;
+ } else {
+ SHA512_Simple(data, datalen, digest);
+ digestLen = 512 / 8;
+ }
+
+ /* Do the signature */
+ _ecdsa_sign(ec->privateKey, ec->publicKey.curve, digest, digestLen, &r, &s);
+ if (!r || !s) {
+ if (r) freebn(r);
+ if (s) freebn(s);
+ return NULL;
+ }
+
+ rlen = (bignum_bitcount(r) + 8) / 8;
+ slen = (bignum_bitcount(s) + 8) / 8;
+
+ namelen = ec_curve_to_name(ec->publicKey.curve, NULL, 0);
+
+ /* Format the output */
+ *siglen = 8+11+namelen+rlen+slen+8;
+ buf = snewn(*siglen, unsigned char);
+ p = buf;
+ PUT_32BIT(p, 11+namelen);
+ p += 4;
+ memcpy(p, "ecdsa-sha2-", 11);
+ p += 11;
+ p += ec_curve_to_name(ec->publicKey.curve, p, namelen);
+ PUT_32BIT(p, rlen + slen + 8);
+ p += 4;
+ PUT_32BIT(p, rlen);
+ p += 4;
+ for (i = rlen; i--;)
+ *p++ = bignum_byte(r, i);
+ PUT_32BIT(p, slen);
+ p += 4;
+ for (i = slen; i--;)
+ *p++ = bignum_byte(s, i);
+
+ 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
+};
--- /dev/null
+/*
+ * 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;
+}
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;
}
* SHA-512 algorithm as described at
*
* http://csrc.nist.gov/cryptval/shs.html
+ *
+ * Modifications made for SHA-384 also
*/
#include "ssh.h"
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;
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];
}
}
+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;
SHA512_Final(&s, output);
}
+void SHA384_Simple(const void *p, int len, unsigned char *output) {
+ SHA512_State s;
+
+ SHA384_Init(&s);
+ SHA512_Bytes(&s, p, len);
+ SHA384_Final(&s, output);
+}
+
+/*
+ * Thin abstraction for things where hashes are pluggable.
+ */
+
+static void *sha512_init(void)
+{
+ SHA512_State *s;
+
+ s = snew(SHA512_State);
+ SHA512_Init(s);
+ return s;
+}
+
+static void sha512_bytes(void *handle, void *p, int len)
+{
+ SHA512_State *s = handle;
+
+ SHA512_Bytes(s, p, len);
+}
+
+static void sha512_final(void *handle, unsigned char *output)
+{
+ SHA512_State *s = handle;
+
+ SHA512_Final(s, output);
+ sfree(s);
+}
+
+const struct ssh_hash ssh_sha512 = {
+ sha512_init, sha512_bytes, sha512_final, 64, "SHA-512"
+};
+
+static void *sha384_init(void)
+{
+ SHA512_State *s;
+
+ s = snew(SHA512_State);
+ SHA384_Init(s);
+ return s;
+}
+
+static void sha384_final(void *handle, unsigned char *output)
+{
+ SHA512_State *s = handle;
+
+ SHA384_Final(s, output);
+ sfree(s);
+}
+
+const struct ssh_hash ssh_sha384 = {
+ sha384_init, sha512_bytes, sha384_final, 48, "SHA-384"
+};
+
#ifdef TEST
#include <stdio.h>
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) {
{
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);
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);
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;
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);
"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 "
}
/* 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)
{
}
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)
*/
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);
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 */
{
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;
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;
}
*/
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();
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
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
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
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)
{
}
*/
if (ldisc) {
ldisc_configure(ldisc, conf);
- ldisc_send(ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(ldisc);
}
if (pal)
DeleteObject(pal);
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);
SetEvent(ctx->ev_to_main);
- if (!ctx->len)
+ if (!ctx->len) {
+ /*
+ * The read operation has returned end-of-file. Telling
+ * that to the main thread will cause it to set its
+ * 'defunct' flag and dispose of the handle structure at
+ * the next opportunity, so we must not touch ctx at all
+ * after this.
+ */
break;
+ }
WaitForSingleObject(ctx->ev_from_main, INFINITE);
if (ctx->done) {
+ /*
+ * The main thread has asked us to shut down. Send back an
+ * event indicating that we've done so. Hereafter we must
+ * not touch ctx at all, because the main thread might
+ * have freed it.
+ */
SetEvent(ctx->ev_to_main);
- break; /* main thread told us to shut down */
+ break;
}
}
while (1) {
WaitForSingleObject(ctx->ev_from_main, INFINITE);
if (ctx->done) {
+ /*
+ * The main thread has asked us to shut down. Send back an
+ * event indicating that we've done so. Hereafter we must
+ * not touch ctx at all, because the main thread might
+ * have freed it.
+ */
SetEvent(ctx->ev_to_main);
break;
}
}
SetEvent(ctx->ev_to_main);
- if (!writeret)
+ if (!writeret) {
+ /*
+ * The write operation has suffered an error. Telling that
+ * to the main thread will cause it to set its 'defunct'
+ * flag and dispose of the handle structure at the next
+ * opportunity, so we must not touch ctx at all after
+ * this.
+ */
break;
+ }
}
if (povl)
if (h->u.g.moribund) {
/*
- * A moribund handle is already treated as dead from the
- * external user's point of view, so do nothing with the
- * actual event. Just signal the thread to die if
- * necessary, or destroy the handle if not.
+ * A moribund handle is one which we have either already
+ * signalled to die, or are waiting until its current I/O op
+ * completes to do so. Either way, it's treated as already
+ * dead from the external user's point of view, so we ignore
+ * the actual I/O result. We just signal the thread to die if
+ * we haven't yet done so, or destroy the handle if not.
*/
if (h->u.g.done) {
handle_destroy(h);
return 0;
}
+typedef enum {RSA, DSA, ECDSA} keytype;
+
/*
* Thread to generate a key.
*/
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)
{
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);
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;
};
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,
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);
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);
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);
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);
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);
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
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;
{
struct ctlpos cp, cp2;
- /* Accelerators used: acglops1rbd */
+ /* Accelerators used: acglops1rbde */
ctlposinit(&cp, hwnd, 4, 4, 4);
beginbox(&cp, "Key", IDC_BOX_KEY);
"&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);
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;
case IDC_KEYSSH1:
case IDC_KEYSSH2RSA:
case IDC_KEYSSH2DSA:
+ case IDC_KEYSSH2ECDSA:
{
state = (struct MainDlgState *)
GetWindowLongPtr(hwnd, GWLP_USERDATA);
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:
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"
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;
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;
{
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);
}
case IDC_KEYSSH1:
case IDC_KEYSSH2RSA:
case IDC_KEYSSH2DSA:
+ case IDC_KEYSSH2ECDSA:
topic = WINHELP_CTX_puttygen_keytype; break;
case IDC_BITSSTATIC:
case IDC_BITS:
}
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);
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;
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);
{
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;