Elliptic-curve cryptography support.

This provides support for ECDSA public keys, for both hosts and users,
and also ECDH key exchange. Supported curves are currently just the
three NIST curves required by RFC 5656.
This commit is contained in:
Chris Staite 2014-11-01 09:45:20 +00:00 коммит произвёл Simon Tatham
Родитель 7d1c30cd50
Коммит 2bf8688355
14 изменённых файлов: 2602 добавлений и 15 удалений

9
Recipe
Просмотреть файл

@ -217,7 +217,7 @@ NONSSH = telnet raw rlogin ldisc pinger
SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf
+ sshgssc pgssapi sshshare
+ sshgssc pgssapi sshshare sshecc
WINSSH = SSH winnoise winsecur winpgntc wingss winshare winnps winnpc
+ winhsock errsock
UXSSH = SSH uxnoise uxagentc uxgss uxshare
@ -270,12 +270,13 @@ psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234
+ misc sshaes sshsha winsecur winpgntc sshdss sshsh256 sshsh512
+ winutils winmisc winhelp conf pageant.res LIBS
+ winutils sshecc winmisc winhelp conf pageant.res LIBS
puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+ sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc
+ sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res
+ tree234 notiming winhelp winnojmp conf LIBS wintime
+ tree234 notiming winhelp winnojmp conf LIBS wintime sshecc
+ sshecdsag
pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
+ uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
@ -293,7 +294,7 @@ plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+ sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
+ sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234
+ uxgen notiming conf
+ uxgen notiming conf sshecc sshecdsag
pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC

Просмотреть файл

@ -265,7 +265,7 @@ int main(int argc, char **argv)
{
char *infile = NULL;
Filename *infilename = NULL, *outfilename = NULL;
enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN;
enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA } keytype = NOKEYGEN;
char *outfile = NULL, *outfiletmp = NULL;
enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE;
int bits = 2048;
@ -437,6 +437,8 @@ int main(int argc, char **argv)
keytype = RSA1, sshver = 1;
else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
keytype = DSA, sshver = 2;
else if (!strcmp(p, "ecdsa"))
keytype = ECDSA, sshver = 2;
else {
fprintf(stderr,
"puttygen: unknown key type `%s'\n", p);
@ -497,6 +499,11 @@ int main(int argc, char **argv)
}
}
if (keytype == ECDSA && (bits != 256 && bits != 384 && bits != 521)) {
fprintf(stderr, "puttygen: invalid bits for ECDSA, choose 256, 384 or 521\n");
errs = TRUE;
}
if (errs)
return 1;
@ -663,6 +670,8 @@ int main(int argc, char **argv)
tm = ltime();
if (keytype == DSA)
strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
else if (keytype == ECDSA)
strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
else
strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
@ -684,6 +693,19 @@ int main(int argc, char **argv)
ssh2key->data = dsskey;
ssh2key->alg = &ssh_dss;
ssh1key = NULL;
} else if (keytype == ECDSA) {
struct ec_key *ec = snew(struct ec_key);
ec_generate(ec, bits, progressfn, &prog);
ssh2key = snew(struct ssh2_userkey);
ssh2key->data = ec;
if (bits == 256) {
ssh2key->alg = &ssh_ecdsa_nistp256;
} else if (bits == 384) {
ssh2key->alg = &ssh_ecdsa_nistp384;
} else {
ssh2key->alg = &ssh_ecdsa_nistp521;
}
ssh1key = NULL;
} else {
struct RSAKey *rsakey = snew(struct RSAKey);
rsa_generate(rsakey, bits, progressfn, &prog);

Просмотреть файл

@ -433,6 +433,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
{ "Diffie-Hellman group 14", KEX_DHGROUP14 },
{ "Diffie-Hellman group exchange", KEX_DHGEX },
{ "RSA-based key exchange", KEX_RSA },
{ "ECDH key exchange", KEX_ECDH },
{ "-- warn below here --", KEX_WARN }
};

Просмотреть файл

@ -115,6 +115,16 @@ my %packets = (
my ($direction, $seq, $data) = @_;
print "\n";
},
#define SSH2_MSG_KEX_ECDH_INIT 30 /* 0x1e */
'SSH2_MSG_KEX_ECDH_INIT' => sub {
my ($direction, $seq, $data) = @_;
print "\n";
},
#define SSH2_MSG_KEX_ECDH_REPLY 31 /* 0x1f */
'SSH2_MSG_KEX_ECDH_REPLY' => sub {
my ($direction, $seq, $data) = @_;
print "\n";
},
#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */
'SSH2_MSG_USERAUTH_REQUEST' => sub {
my ($direction, $seq, $data) = @_;

209
import.c
Просмотреть файл

@ -307,7 +307,7 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret)
* Code to read and write OpenSSH private keys.
*/
enum { OSSH_DSA, OSSH_RSA };
enum { OSSH_DSA, OSSH_RSA, OSSH_ECDSA };
enum { OSSH_ENC_3DES, OSSH_ENC_AES };
struct openssh_key {
int type;
@ -354,6 +354,8 @@ static struct openssh_key *load_openssh_key(const Filename *filename,
ret->type = OSSH_RSA;
else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----"))
ret->type = OSSH_DSA;
else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----"))
ret->type = OSSH_ECDSA;
else {
errmsg = "unrecognised key type";
goto error;
@ -591,6 +593,10 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
*
* - For DSA, we expect them to be 0, p, q, g, y, x in that
* order.
*
* - In ECDSA the format is totally different: we see the
* SEQUENCE, but beneath is an INTEGER 1, OCTET STRING priv
* EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint
*/
p = key->keyblob;
@ -613,7 +619,122 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
else
num_integers = 0; /* placate compiler warnings */
if (key->type == OSSH_RSA || key->type == OSSH_DSA) {
if (key->type == OSSH_ECDSA)
{
/* And now for something completely different */
unsigned char *priv;
int privlen;
struct ec_curve *curve;
/* Read INTEGER 1 */
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len ||
len != 1 || p[0] != 1) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
p += 1;
/* Read private key OCTET STRING */
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 4 || key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
priv = p;
privlen = len;
p += len;
/* Read curve OID */
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 0 || key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 6 || key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
if (len == 8 && !memcmp(p, nistp256_oid, nistp256_oid_len)) {
curve = ec_p256();
} else if (len == 5 && !memcmp(p, nistp384_oid, nistp384_oid_len)) {
curve = ec_p384();
} else if (len == 5 && !memcmp(p, nistp521_oid, nistp521_oid_len)) {
curve = ec_p521();
} else {
errmsg = "Unsupported ECDSA curve.";
retval = NULL;
goto error;
}
p += len;
/* Read BIT STRING point */
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 1 || key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 3 || key->keyblob+key->keyblob_len-p < len ||
len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
p += 1; len -= 1; /* Skip 0x00 before point */
/* Construct the key */
retkey = snew(struct ssh2_userkey);
if (!retkey) {
errmsg = "out of memory";
goto error;
}
if (curve->fieldBits == 256) {
retkey->alg = &ssh_ecdsa_nistp256;
} else if (curve->fieldBits == 384) {
retkey->alg = &ssh_ecdsa_nistp384;
} else {
retkey->alg = &ssh_ecdsa_nistp521;
}
blob = snewn((4+19 + 4+8 + 4+len) + (4+privlen), unsigned char);
if (!blob) {
sfree(retkey);
errmsg = "out of memory";
goto error;
}
PUT_32BIT(blob, 19);
sprintf((char*)blob+4, "ecdsa-sha2-nistp%d", curve->fieldBits);
PUT_32BIT(blob+4+19, 8);
sprintf((char*)blob+4+19+4, "nistp%d", curve->fieldBits);
PUT_32BIT(blob+4+19+4+8, len);
memcpy(blob+4+19+4+8+4, p, len);
PUT_32BIT(blob+4+19+4+8+4+len, privlen);
memcpy(blob+4+19+4+8+4+len+4, priv, privlen);
retkey->data = retkey->alg->createkey(blob, 4+19+4+8+4+len,
blob+4+19+4+8+4+len, 4+privlen);
if (!retkey->data) {
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
}
} else if (key->type == OSSH_RSA || key->type == OSSH_DSA) {
/*
* Space to create key blob in.
*/
@ -875,6 +996,90 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
pos += numbers[i].bytes;
}
} else if (key->alg == &ssh_ecdsa_nistp256 ||
key->alg == &ssh_ecdsa_nistp384 ||
key->alg == &ssh_ecdsa_nistp521) {
unsigned char *oid;
int oidlen;
int pointlen;
/*
* Structure of asn1:
* SEQUENCE
* INTEGER 1
* OCTET STRING (private key)
* [0]
* OID (curve)
* [1]
* BIT STRING (0x00 public key point)
*/
switch (((struct ec_key *)key->data)->publicKey.curve->fieldBits) {
case 256:
/* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */
oid = nistp256_oid;
oidlen = nistp256_oid_len;
pointlen = 32 * 2;
break;
case 384:
/* OID: 1.3.132.0.34 (secp384r1) */
oid = nistp384_oid;
oidlen = nistp384_oid_len;
pointlen = 48 * 2;
break;
case 521:
/* OID: 1.3.132.0.35 (secp521r1) */
oid = nistp521_oid;
oidlen = nistp521_oid_len;
pointlen = 66 * 2;
break;
default:
assert(0);
}
len = ber_write_id_len(NULL, 2, 1, 0);
len += 1;
len += ber_write_id_len(NULL, 4, privlen - 4, 0);
len+= privlen - 4;
len += ber_write_id_len(NULL, 0, oidlen +
ber_write_id_len(NULL, 6, oidlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
len += ber_write_id_len(NULL, 6, oidlen, 0);
len += oidlen;
len += ber_write_id_len(NULL, 1, 2 + pointlen +
ber_write_id_len(NULL, 3, 2 + pointlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
len += ber_write_id_len(NULL, 3, 2 + pointlen, 0);
len += 2 + pointlen;
seqlen = len;
len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
outblob = snewn(len, unsigned char);
assert(outblob);
pos = 0;
pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
pos += ber_write_id_len(outblob+pos, 2, 1, 0);
outblob[pos++] = 1;
pos += ber_write_id_len(outblob+pos, 4, privlen - 4, 0);
memcpy(outblob+pos, privblob + 4, privlen - 4);
pos += privlen - 4;
pos += ber_write_id_len(outblob+pos, 0, oidlen +
ber_write_id_len(NULL, 6, oidlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
pos += ber_write_id_len(outblob+pos, 6, oidlen, 0);
memcpy(outblob+pos, oid, oidlen);
pos += oidlen;
pos += ber_write_id_len(outblob+pos, 1, 2 + pointlen +
ber_write_id_len(NULL, 3, 2 + pointlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
pos += ber_write_id_len(outblob+pos, 3, 2 + pointlen, 0);
outblob[pos++] = 0;
memcpy(outblob+pos, pubblob+39, 1 + pointlen);
pos += 1 + pointlen;
header = "-----BEGIN EC PRIVATE KEY-----\n";
footer = "-----END EC PRIVATE KEY-----\n";
} else {
assert(0); /* zoinks! */
exit(1); /* XXX: GCC doesn't understand assert() on some systems. */

Просмотреть файл

@ -253,6 +253,7 @@ enum {
KEX_DHGROUP14,
KEX_DHGEX,
KEX_RSA,
KEX_ECDH,
KEX_MAX
};

Просмотреть файл

@ -19,6 +19,7 @@ static const struct keyvalwhere ciphernames[] = {
};
static const struct keyvalwhere kexnames[] = {
{ "ecdh", KEX_ECDH, -1, +1 },
{ "dh-gex-sha1", KEX_DHGEX, -1, -1 },
{ "dh-group14-sha1", KEX_DHGROUP14, -1, -1 },
{ "dh-group1-sha1", KEX_DHGROUP1, -1, -1 },
@ -770,9 +771,11 @@ void load_open_settings(void *sesskey, Conf *conf)
char *default_kexes;
i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0);
if (i == FORCE_ON)
default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";
default_kexes = "ecdh,dh-group14-sha1,dh-group1-sha1,rsa,"
"WARN,dh-gex-sha1";
else
default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";
default_kexes = "ecdh,dh-gex-sha1,dh-group14-sha1,"
"dh-group1-sha1,rsa,WARN";
gprefs(sesskey, "KEX", default_kexes,
kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
}

90
ssh.c
Просмотреть файл

@ -32,6 +32,7 @@ typedef enum {
SSH2_PKTCTX_NOKEX,
SSH2_PKTCTX_DHGROUP,
SSH2_PKTCTX_DHGEX,
SSH2_PKTCTX_ECDHKEX,
SSH2_PKTCTX_RSAKEX
} Pkt_KCtx;
typedef enum {
@ -254,6 +255,8 @@ static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
translate(SSH2_MSG_USERAUTH_REQUEST);
translate(SSH2_MSG_USERAUTH_FAILURE);
translate(SSH2_MSG_USERAUTH_SUCCESS);
@ -397,7 +400,10 @@ static void ssh_channel_destroy(struct ssh_channel *c);
#define OUR_V2_MAXPKT 0x4000UL
#define OUR_V2_PACKETLIMIT 0x9000UL
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
const static struct ssh_signkey *hostkey_algs[] = {
&ssh_ecdsa_nistp256, &ssh_ecdsa_nistp384, &ssh_ecdsa_nistp521,
&ssh_rsa, &ssh_dss
};
const static struct ssh_mac *macs[] = {
&ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
@ -6024,6 +6030,7 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
int hostkeylen, siglen, rsakeylen;
void *hkey; /* actual host key */
void *rsakey; /* for RSA kex */
void *eckey; /* for ECDH kex */
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
int n_preferred_kex;
const struct ssh_kexes *preferred_kex[KEX_MAX];
@ -6087,6 +6094,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
s->preferred_kex[s->n_preferred_kex++] =
&ssh_rsa_kex;
break;
case KEX_ECDH:
s->preferred_kex[s->n_preferred_kex++] =
&ssh_ecdh_kex;
break;
case KEX_WARN:
/* Flag for later. Don't bother if it's the last in
* the list. */
@ -6653,6 +6664,83 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
freebn(s->g);
freebn(s->p);
}
} else if (ssh->kex->main_type == KEXTYPE_ECDH) {
logeventf(ssh, "Doing ECDH key exchange with hash %s",
ssh->kex->hash->text_name);
ssh->pkt_kctx = SSH2_PKTCTX_ECDHKEX;
s->eckey = NULL;
if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp256")) {
s->eckey = ssh_ecdhkex_newkey(ec_p256());
} else if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp384")) {
s->eckey = ssh_ecdhkex_newkey(ec_p384());
} else if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp521")) {
s->eckey = ssh_ecdhkex_newkey(ec_p521());
}
if (!s->eckey) {
bombout(("Unable to generate key for ECDH"));
crStopV;
}
{
char *publicPoint;
int publicPointLength;
publicPoint = ssh_ecdhkex_getpublic(s->eckey, &publicPointLength);
if (!publicPoint) {
ssh_ecdhkex_freekey(s->eckey);
bombout(("Unable to encode public key for ECDH"));
crStopV;
}
s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_ECDH_INIT);
ssh2_pkt_addstring_start(s->pktout);
ssh2_pkt_addstring_data(s->pktout, publicPoint, publicPointLength);
sfree(publicPoint);
}
ssh2_pkt_send_noqueue(ssh, s->pktout);
crWaitUntilV(pktin);
if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) {
ssh_ecdhkex_freekey(s->eckey);
bombout(("expected ECDH reply packet from server"));
crStopV;
}
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
{
char *publicPoint;
int publicPointLength;
publicPoint = ssh_ecdhkex_getpublic(s->eckey, &publicPointLength);
if (!publicPoint) {
ssh_ecdhkex_freekey(s->eckey);
bombout(("Unable to encode public key for ECDH hash"));
crStopV;
}
hash_string(ssh->kex->hash, ssh->exhash,
publicPoint, publicPointLength);
sfree(publicPoint);
}
{
char *keydata;
int keylen;
ssh_pkt_getstring(pktin, &keydata, &keylen);
hash_string(ssh->kex->hash, ssh->exhash, keydata, keylen);
s->K = ssh_ecdhkex_getkey(s->eckey, keydata, keylen);
if (!s->K) {
ssh_ecdhkex_freekey(s->eckey);
bombout(("point received in ECDH was not valid"));
crStopV;
}
}
ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
ssh_ecdhkex_freekey(s->eckey);
} else {
logeventf(ssh, "Doing RSA key exchange with hash %s",
ssh->kex->hash->text_name);

54
ssh.h
Просмотреть файл

@ -99,6 +99,38 @@ struct dss_key {
Bignum p, q, g, y, x;
};
struct ec_curve;
struct ec_point {
const struct ec_curve *curve;
Bignum x, y;
Bignum z; // Jacobian denominator
unsigned char infinity;
};
void ec_point_free(struct ec_point *point);
struct ec_curve {
unsigned int fieldBits;
Bignum p, a, b, n;
struct ec_point G;
};
extern unsigned char nistp256_oid[];
extern unsigned char nistp384_oid[];
extern unsigned char nistp521_oid[];
extern int nistp256_oid_len;
extern int nistp384_oid_len;
extern int nistp521_oid_len;
struct ec_curve *ec_p256(void);
struct ec_curve *ec_p384(void);
struct ec_curve *ec_p521(void);
struct ec_key {
struct ec_point publicKey;
Bignum privateKey;
};
int makekey(unsigned char *data, int len, struct RSAKey *result,
unsigned char **keystr, int order);
int makeprivate(unsigned char *data, int len, struct RSAKey *result);
@ -141,6 +173,14 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
unsigned char *out, int outlen,
void *key);
/*
* SSH2 ECDH key exchange functions
*/
void *ssh_ecdhkex_newkey(struct ec_curve *curve);
void ssh_ecdhkex_freekey(void *key);
char *ssh_ecdhkex_getpublic(void *key, int *len);
Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen);
/*
* Helper function for k generation in DSA, reused in ECDSA
*/
@ -271,7 +311,7 @@ struct ssh_hash {
struct ssh_kex {
char *name, *groupname;
enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH } main_type;
/* For DH */
const unsigned char *pdata, *gdata; /* NULL means group exchange */
int plen, glen;
@ -327,7 +367,7 @@ struct ssh2_userkey {
};
/* The maximum length of any hash algorithm used in kex. (bytes) */
#define SSH2_KEX_MAX_HASH_LEN (32) /* SHA-256 */
#define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */
extern const struct ssh_cipher ssh_3des;
extern const struct ssh_cipher ssh_des;
@ -345,8 +385,12 @@ extern const struct ssh_kexes ssh_diffiehellman_group1;
extern const struct ssh_kexes ssh_diffiehellman_group14;
extern const struct ssh_kexes ssh_diffiehellman_gex;
extern const struct ssh_kexes ssh_rsa_kex;
extern const struct ssh_kexes ssh_ecdh_kex;
extern const struct ssh_signkey ssh_dss;
extern const struct ssh_signkey ssh_rsa;
extern const struct ssh_signkey ssh_ecdsa_nistp256;
extern const struct ssh_signkey ssh_ecdsa_nistp384;
extern const struct ssh_signkey ssh_ecdsa_nistp521;
extern const struct ssh_mac ssh_hmac_md5;
extern const struct ssh_mac ssh_hmac_sha1;
extern const struct ssh_mac ssh_hmac_sha1_buggy;
@ -633,6 +677,8 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
void *pfnparam);
int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
void *pfnparam);
int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
void *pfnparam);
Bignum primegen(int bits, int modulus, int residue, Bignum factor,
int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
void invent_firstbits(unsigned *one, unsigned *two);
@ -742,6 +788,10 @@ void platform_ssh_share_cleanup(const char *name);
#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */
#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */
#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */
#define SSH2_MSG_KEX_ECDH_INIT 30 /* 0x1e */
#define SSH2_MSG_KEX_ECDH_REPLY 31 /* 0x1f */
#define SSH2_MSG_KEX_ECMQV_INIT 30 /* 0x1e */
#define SSH2_MSG_KEX_ECMQV_REPLY 31 /* 0x1f */
#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */
#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */
#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */

2104
sshecc.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

41
sshecdsag.c Normal file
Просмотреть файл

@ -0,0 +1,41 @@
/*
* EC key generation.
*/
#include "ssh.h"
/* Forward reference from sshecc.c */
struct ec_point *ecp_mul(const struct ec_point *a, const Bignum b);
int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
void *pfnparam)
{
struct ec_point *publicKey;
if (bits == 256) {
key->publicKey.curve = ec_p256();
} else if (bits == 384) {
key->publicKey.curve = ec_p384();
} else if (bits == 521) {
key->publicKey.curve = ec_p521();
} else {
return 0;
}
key->privateKey = bignum_random_in_range(One, key->publicKey.curve->n);
if (!key->privateKey) return 0;
publicKey = ecp_mul(&key->publicKey.curve->G, key->privateKey);
if (!publicKey) {
freebn(key->privateKey);
key->privateKey = NULL;
return 0;
}
key->publicKey.x = publicKey->x;
key->publicKey.y = publicKey->y;
key->publicKey.z = NULL;
sfree(publicKey);
return 1;
}

Просмотреть файл

@ -563,6 +563,12 @@ const struct ssh_signkey *find_pubkey_alg(const char *name)
return &ssh_rsa;
else if (!strcmp(name, "ssh-dss"))
return &ssh_dss;
else if (!strcmp(name, "ecdsa-sha2-nistp256"))
return &ssh_ecdsa_nistp256;
else if (!strcmp(name, "ecdsa-sha2-nistp384"))
return &ssh_ecdsa_nistp384;
else if (!strcmp(name, "ecdsa-sha2-nistp521"))
return &ssh_ecdsa_nistp521;
else
return NULL;
}

Просмотреть файл

@ -328,6 +328,7 @@ struct rsa_key_thread_params {
union {
struct RSAKey *key;
struct dss_key *dsskey;
struct ec_key *eckey;
};
};
static DWORD WINAPI generate_rsa_key_thread(void *param)
@ -341,6 +342,8 @@ static DWORD WINAPI generate_rsa_key_thread(void *param)
if (params->keytype == DSA)
dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
else if (params->keytype == ECDSA)
ec_generate(params->eckey, params->keysize, progress_update, &prog);
else
rsa_generate(params->key, params->keysize, progress_update, &prog);
@ -364,6 +367,7 @@ struct MainDlgState {
union {
struct RSAKey key;
struct dss_key dsskey;
struct ec_key eckey;
};
HMENU filemenu, keymenu, cvtmenu;
};
@ -526,6 +530,7 @@ enum {
IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
IDC_BOX_PARAMS,
IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
IDC_KEYSSH2ECDSA,
IDC_BITSSTATIC, IDC_BITS,
IDC_ABOUT,
IDC_GIVEHELP,
@ -562,6 +567,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
@ -570,6 +576,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
MF_GRAYED|MF_BYCOMMAND);
@ -587,6 +595,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
@ -595,6 +604,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
MF_GRAYED|MF_BYCOMMAND);
@ -612,6 +623,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
@ -620,6 +632,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
MF_ENABLED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
/*
* Enable export menu items if and only if the key type
@ -862,6 +876,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
state->keymenu = menu1;
@ -940,13 +955,14 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
"SSH-&1 (RSA)", IDC_KEYSSH1,
"SSH-2 &RSA", IDC_KEYSSH2RSA,
"SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
"SSH-2 &DSA", IDC_KEYSSH2DSA,
"SSH-2 &ECDSA", IDC_KEYSSH2ECDSA, NULL);
staticedit(&cp, "Number of &bits in a generated key:",
IDC_BITSSTATIC, IDC_BITS, 20);
endbox(&cp);
}
CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
IDC_KEYSSH2RSA, MF_BYCOMMAND);
SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
@ -1018,6 +1034,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
case IDC_KEYSSH1:
case IDC_KEYSSH2RSA:
case IDC_KEYSSH2DSA:
case IDC_KEYSSH2ECDSA:
{
state = (struct MainDlgState *)
GetWindowLongPtr(hwnd, GWLP_USERDATA);
@ -1026,6 +1043,11 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
LOWORD(wParam));
CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
LOWORD(wParam), MF_BYCOMMAND);
CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
LOWORD(wParam));
CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
IDC_KEYSSH2ECDSA,
LOWORD(wParam), MF_BYCOMMAND);
}
break;
case IDC_QUIT:
@ -1080,6 +1102,8 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
state->keytype = RSA;
if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
state->keytype = DSA;
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
state->keytype = ECDSA;
}
if (state->keysize < 256) {
int ret = MessageBox(hwnd,
@ -1093,6 +1117,20 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
state->keysize = 256;
SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
}
if (state->keytype == ECDSA && !(state->keysize == 256 ||
state->keysize == 384 ||
state->keysize == 521)) {
int ret = MessageBox(hwnd,
"Only 256, 384 and 521 bit elliptic"
" curves are supported.\n"
"Key length reset to 256. Continue?",
"PuTTYgen Warning",
MB_ICONWARNING | MB_OKCANCEL);
if (ret != IDOK)
break;
state->keysize = 256;
SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
}
ui_set_state(hwnd, state, 1);
SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
state->key_exists = FALSE;
@ -1289,6 +1327,14 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
if (state->keytype == DSA) {
state->ssh2key.data = &state->dsskey;
state->ssh2key.alg = &ssh_dss;
} else if (state->keytype == ECDSA) {
state->ssh2key.data = &state->eckey;
if (state->eckey.publicKey.curve->fieldBits == 256)
state->ssh2key.alg = &ssh_ecdsa_nistp256;
else if (state->eckey.publicKey.curve->fieldBits == 384)
state->ssh2key.alg = &ssh_ecdsa_nistp384;
else
state->ssh2key.alg = &ssh_ecdsa_nistp521;
} else {
state->ssh2key.data = &state->key;
state->ssh2key.alg = &ssh_rsa;
@ -1309,6 +1355,8 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
tm = ltime();
if (state->keytype == DSA)
strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
else if (state->keytype == ECDSA)
strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
else
strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
}
@ -1399,6 +1447,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
case IDC_KEYSSH1:
case IDC_KEYSSH2RSA:
case IDC_KEYSSH2DSA:
case IDC_KEYSSH2ECDSA:
topic = WINHELP_CTX_puttygen_keytype; break;
case IDC_BITSSTATIC:
case IDC_BITS:

Просмотреть файл

@ -1141,6 +1141,12 @@ static void answer_msg(void *msg)
key->alg = &ssh_rsa;
else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
key->alg = &ssh_dss;
else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19))
key->alg = &ssh_ecdsa_nistp256;
else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19))
key->alg = &ssh_ecdsa_nistp384;
else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19))
key->alg = &ssh_ecdsa_nistp521;
else {
sfree(key);
goto failure;