- djm@cvs.openbsd.org 2010/09/09 10:45:45

[kex.c kex.h kexecdh.c key.c key.h monitor.c ssh-ecdsa.c]
     ECDH/ECDSA compliance fix: these methods vary the hash function they use
     (SHA256/384/512) depending on the length of the curve in use. The previous
     code incorrectly used SHA256 in all cases.

     This fix will cause authentication failure when using 384 or 521-bit curve
     keys if one peer hasn't been upgraded and the other has. (256-bit curve
     keys work ok). In particular you may need to specify HostkeyAlgorithms
     when connecting to a server that has not been upgraded from an upgraded
     client.

     ok naddy@
This commit is contained in:
Damien Miller 2010-09-10 11:23:34 +10:00
parent 3796ab47d3
commit 041ab7c1e7
8 changed files with 80 additions and 31 deletions

View File

@ -49,6 +49,19 @@
gcc, at least in earlier versions, but this does not forgive your current gcc, at least in earlier versions, but this does not forgive your current
transgressions) seen between zlib and openssl transgressions) seen between zlib and openssl
ok djm ok djm
- djm@cvs.openbsd.org 2010/09/09 10:45:45
[kex.c kex.h kexecdh.c key.c key.h monitor.c ssh-ecdsa.c]
ECDH/ECDSA compliance fix: these methods vary the hash function they use
(SHA256/384/512) depending on the length of the curve in use. The previous
code incorrectly used SHA256 in all cases.
This fix will cause authentication failure when using 384 or 521-bit curve
keys if one peer hasn't been upgraded and the other has. (256-bit curve
keys work ok). In particular you may need to specify HostkeyAlgorithms
when connecting to a server that has not been upgraded from an upgraded
client.
ok naddy@
20100831 20100831
- OpenBSD CVS Sync - OpenBSD CVS Sync

8
kex.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.c,v 1.84 2010/08/31 11:54:45 djm Exp $ */ /* $OpenBSD: kex.c,v 1.85 2010/09/09 10:45:45 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* *
@ -325,10 +325,10 @@ choose_kex(Kex *k, char *client, char *server)
} else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) { } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) {
k->kex_type = KEX_DH_GEX_SHA256; k->kex_type = KEX_DH_GEX_SHA256;
k->evp_md = evp_ssh_sha256(); k->evp_md = evp_ssh_sha256();
} else if (strncmp(k->name, KEX_ECDH_SHA256, } else if (strncmp(k->name, KEX_ECDH_SHA2_STEM,
sizeof(KEX_ECDH_SHA256) - 1) == 0) { sizeof(KEX_ECDH_SHA2_STEM) - 1) == 0) {
k->kex_type = KEX_ECDH_SHA2; k->kex_type = KEX_ECDH_SHA2;
k->evp_md = evp_ssh_sha256(); k->evp_md = kex_ecdh_name_to_evpmd(k->name);
#endif #endif
} else } else
fatal("bad kex alg %s", k->name); fatal("bad kex alg %s", k->name);

5
kex.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.h,v 1.50 2010/08/31 11:54:45 djm Exp $ */ /* $OpenBSD: kex.h,v 1.51 2010/09/09 10:45:45 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -39,7 +39,7 @@
#define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" #define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256"
#define KEX_RESUME "resume@appgate.com" #define KEX_RESUME "resume@appgate.com"
/* The following represents the family of ECDH methods */ /* The following represents the family of ECDH methods */
#define KEX_ECDH_SHA256 "ecdh-sha2-" #define KEX_ECDH_SHA2_STEM "ecdh-sha2-"
#define COMP_NONE 0 #define COMP_NONE 0
#define COMP_ZLIB 1 #define COMP_ZLIB 1
@ -165,6 +165,7 @@ kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, int,
const BIGNUM *, u_char **, u_int *); const BIGNUM *, u_char **, u_int *);
int kex_ecdh_name_to_nid(const char *); int kex_ecdh_name_to_nid(const char *);
const EVP_MD *kex_ecdh_name_to_evpmd(const char *);
void void
derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]); derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: kexecdh.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */ /* $OpenBSD: kexecdh.c,v 1.2 2010/09/09 10:45:45 djm Exp $ */
/* /*
* Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved.
@ -48,15 +48,23 @@ kex_ecdh_name_to_nid(const char *kexname)
{ {
int ret; int ret;
if (strlen(kexname) < sizeof(KEX_ECDH_SHA256) - 1) if (strlen(kexname) < sizeof(KEX_ECDH_SHA2_STEM) - 1)
fatal("%s: kexname too short \"%s\"", __func__, kexname); fatal("%s: kexname too short \"%s\"", __func__, kexname);
ret = key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA256) - 1); ret = key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA2_STEM) - 1);
if (ret == -1) if (ret == -1)
fatal("%s: unsupported curve negotiated \"%s\"", __func__, fatal("%s: unsupported curve negotiated \"%s\"", __func__,
kexname); kexname);
return ret; return ret;
} }
const EVP_MD *
kex_ecdh_name_to_evpmd(const char *kexname)
{
int nid = kex_ecdh_name_to_nid(kexname);
return key_ec_nid_to_evpmd(nid);
}
void void
kex_ecdh_hash( kex_ecdh_hash(
const EVP_MD *evp_md, const EVP_MD *evp_md,

47
key.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: key.c,v 1.92 2010/08/31 11:54:45 djm Exp $ */ /* $OpenBSD: key.c,v 1.93 2010/09/09 10:45:45 djm Exp $ */
/* /*
* read_bignum(): * read_bignum():
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -978,17 +978,7 @@ key_size(const Key *k)
return BN_num_bits(k->dsa->p); return BN_num_bits(k->dsa->p);
case KEY_ECDSA: case KEY_ECDSA:
case KEY_ECDSA_CERT: case KEY_ECDSA_CERT:
switch (k->ecdsa_nid) { return key_curve_nid_to_bits(k->ecdsa_nid);
case NID_X9_62_prime256v1:
return 256;
case NID_secp384r1:
return 384;
case NID_secp521r1:
return 521;
default:
break;
}
break;
} }
return 0; return 0;
} }
@ -1961,6 +1951,7 @@ key_cert_is_legacy(Key *k)
} }
} }
/* XXX: these are really begging for a table-driven approach */
int int
key_curve_name_to_nid(const char *name) key_curve_name_to_nid(const char *name)
{ {
@ -1975,6 +1966,22 @@ key_curve_name_to_nid(const char *name)
return -1; return -1;
} }
u_int
key_curve_nid_to_bits(int nid)
{
switch (nid) {
case NID_X9_62_prime256v1:
return 256;
case NID_secp384r1:
return 384;
case NID_secp521r1:
return 521;
default:
error("%s: unsupported EC curve nid %d", __func__, nid);
return 0;
}
}
const char * const char *
key_curve_nid_to_name(int nid) key_curve_nid_to_name(int nid)
{ {
@ -1989,6 +1996,22 @@ key_curve_nid_to_name(int nid)
return NULL; return NULL;
} }
const EVP_MD *
key_ec_nid_to_evpmd(int nid)
{
int kbits = key_curve_nid_to_bits(nid);
if (kbits == 0)
fatal("%s: invalid nid %d", __func__, nid);
/* RFC5656 section 6.2.1 */
if (kbits <= 256)
return EVP_sha256();
else if (kbits <= 384)
return EVP_sha384();
else
return EVP_sha512();
}
int int
key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
{ {

4
key.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: key.h,v 1.31 2010/08/31 11:54:45 djm Exp $ */ /* $OpenBSD: key.h,v 1.32 2010/09/09 10:45:45 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -112,8 +112,10 @@ int key_cert_is_legacy(Key *);
int key_ecdsa_nid_from_name(const char *); int key_ecdsa_nid_from_name(const char *);
int key_curve_name_to_nid(const char *); int key_curve_name_to_nid(const char *);
const char * key_curve_nid_to_name(int); const char * key_curve_nid_to_name(int);
u_int key_curve_nid_to_bits(int);
int key_ecdsa_bits_to_nid(int); int key_ecdsa_bits_to_nid(int);
int key_ecdsa_group_to_nid(const EC_GROUP *); int key_ecdsa_group_to_nid(const EC_GROUP *);
const EVP_MD * key_ec_nid_to_evpmd(int nid);
int key_ec_validate_public(const EC_GROUP *, const EC_POINT *); int key_ec_validate_public(const EC_GROUP *, const EC_POINT *);
int key_ec_validate_private(const EC_KEY *); int key_ec_validate_private(const EC_KEY *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.109 2010/08/31 11:54:45 djm Exp $ */ /* $OpenBSD: monitor.c,v 1.110 2010/09/09 10:45:45 djm Exp $ */
/* /*
* Copyright 2002 Niels Provos <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org> * Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -590,10 +590,10 @@ mm_answer_sign(int sock, Buffer *m)
p = buffer_get_string(m, &datlen); p = buffer_get_string(m, &datlen);
/* /*
* Supported KEX types will only return SHA1 (20 byte) or * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes),
* SHA256 (32 byte) hashes * SHA384 (48 bytes) and SHA512 (64 bytes).
*/ */
if (datlen != 20 && datlen != 32) if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64)
fatal("%s: data length incorrect: %u", __func__, datlen); fatal("%s: data length incorrect: %u", __func__, datlen);
/* save session id, it will be passed on the first call */ /* save session id, it will be passed on the first call */

View File

@ -1,4 +1,4 @@
/* $OpenBSD$ */ /* $OpenBSD: ssh-ecdsa.c,v 1.4 2010/09/10 01:04:10 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved.
@ -46,7 +46,7 @@ ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp,
const u_char *data, u_int datalen) const u_char *data, u_int datalen)
{ {
ECDSA_SIG *sig; ECDSA_SIG *sig;
const EVP_MD *evp_md = EVP_sha256(); const EVP_MD *evp_md;
EVP_MD_CTX md; EVP_MD_CTX md;
u_char digest[EVP_MAX_MD_SIZE]; u_char digest[EVP_MAX_MD_SIZE];
u_int len, dlen; u_int len, dlen;
@ -57,6 +57,7 @@ ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp,
error("%s: no ECDSA key", __func__); error("%s: no ECDSA key", __func__);
return -1; return -1;
} }
evp_md = key_ec_nid_to_evpmd(key->ecdsa_nid);
EVP_DigestInit(&md, evp_md); EVP_DigestInit(&md, evp_md);
EVP_DigestUpdate(&md, data, datalen); EVP_DigestUpdate(&md, data, datalen);
EVP_DigestFinal(&md, digest, &dlen); EVP_DigestFinal(&md, digest, &dlen);
@ -94,21 +95,22 @@ ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
const u_char *data, u_int datalen) const u_char *data, u_int datalen)
{ {
ECDSA_SIG *sig; ECDSA_SIG *sig;
const EVP_MD *evp_md = EVP_sha256(); const EVP_MD *evp_md;
EVP_MD_CTX md; EVP_MD_CTX md;
u_char digest[EVP_MAX_MD_SIZE], *sigblob; u_char digest[EVP_MAX_MD_SIZE], *sigblob;
u_int len, dlen; u_int len, dlen;
int rlen, ret; int rlen, ret;
Buffer b, bb; Buffer b, bb;
char *ktype;
if (key == NULL || key->ecdsa == NULL || if (key == NULL || key->ecdsa == NULL ||
(key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) { (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) {
error("%s: no ECDSA key", __func__); error("%s: no ECDSA key", __func__);
return -1; return -1;
} }
evp_md = key_ec_nid_to_evpmd(key->ecdsa_nid);
/* fetch signature */ /* fetch signature */
char *ktype;
buffer_init(&b); buffer_init(&b);
buffer_append(&b, signature, signaturelen); buffer_append(&b, signature, signaturelen);
ktype = buffer_get_string(&b, NULL); ktype = buffer_get_string(&b, NULL);