- djm@cvs.openbsd.org 2010/08/31 11:54:45
     [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys auth2-jpake.c authfd.c]
     [authfile.c buffer.h dns.c kex.c kex.h key.c key.h monitor.c]
     [monitor_wrap.c myproposal.h packet.c packet.h pathnames.h readconf.c]
     [ssh-add.1 ssh-add.c ssh-agent.1 ssh-agent.c ssh-keygen.1 ssh-keygen.c]
     [ssh-keyscan.1 ssh-keyscan.c ssh-keysign.8 ssh.1 ssh.c ssh2.h]
     [ssh_config.5 sshconnect.c sshconnect2.c sshd.8 sshd.c sshd_config.5]
     [uuencode.c uuencode.h bufec.c kexecdh.c kexecdhc.c kexecdhs.c ssh-ecdsa.c]
     Implement Elliptic Curve Cryptography modes for key exchange (ECDH) and
     host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA offer
     better performance than plain DH and DSA at the same equivalent symmetric
     key length, as well as much shorter keys.

     Only the mandatory sections of RFC5656 are implemented, specifically the
     three REQUIRED curves nistp256, nistp384 and nistp521 and only ECDH and
     ECDSA. Point compression (optional in RFC5656 is NOT implemented).

     Certificate host and user keys using the new ECDSA key types are supported.

     Note that this code has not been tested for interoperability and may be
     subject to change.

     feedback and ok markus@
diff --git a/ChangeLog b/ChangeLog
index 2f4acd9..889580e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -25,6 +25,29 @@
      * actually, we allow a single one at the end of the string for now because
      we don't know how many deployed implementations get this wrong, but don't
      count on this to remain indefinitely.
+   - djm@cvs.openbsd.org 2010/08/31 11:54:45
+     [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys auth2-jpake.c authfd.c]
+     [authfile.c buffer.h dns.c kex.c kex.h key.c key.h monitor.c]
+     [monitor_wrap.c myproposal.h packet.c packet.h pathnames.h readconf.c]
+     [ssh-add.1 ssh-add.c ssh-agent.1 ssh-agent.c ssh-keygen.1 ssh-keygen.c]
+     [ssh-keyscan.1 ssh-keyscan.c ssh-keysign.8 ssh.1 ssh.c ssh2.h]
+     [ssh_config.5 sshconnect.c sshconnect2.c sshd.8 sshd.c sshd_config.5]
+     [uuencode.c uuencode.h bufec.c kexecdh.c kexecdhc.c kexecdhs.c ssh-ecdsa.c]
+     Implement Elliptic Curve Cryptography modes for key exchange (ECDH) and
+     host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA offer
+     better performance than plain DH and DSA at the same equivalent symmetric
+     key length, as well as much shorter keys.
+     
+     Only the mandatory sections of RFC5656 are implemented, specifically the
+     three REQUIRED curves nistp256, nistp384 and nistp521 and only ECDH and
+     ECDSA. Point compression (optional in RFC5656 is NOT implemented).
+     
+     Certificate host and user keys using the new ECDSA key types are supported.
+     
+     Note that this code has not been tested for interoperability and may be
+     subject to change.
+     
+     feedback and ok markus@
 
 20100827
  - (dtucker) [contrib/redhat/sshd.init] Bug #1810: initlog is deprecated,
diff --git a/PROTOCOL b/PROTOCOL
index 5fc31ea..5d2a711 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -12,7 +12,9 @@
 The protocol used by OpenSSH's ssh-agent is described in the file
 PROTOCOL.agent
 
-1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com"
+1. Transport protocol changes
+
+1.1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com"
 
 This is a new transport-layer MAC method using the UMAC algorithm
 (rfc4418). This method is identical to the "umac-64" method documented
@@ -20,7 +22,7 @@
 
 http://www.openssh.com/txt/draft-miller-secsh-umac-01.txt
 
-2. transport: Protocol 2 compression algorithm "zlib@openssh.com"
+1.2. transport: Protocol 2 compression algorithm "zlib@openssh.com"
 
 This transport-layer compression method uses the zlib compression
 algorithm (identical to the "zlib" method in rfc4253), but delays the
@@ -31,14 +33,27 @@
 
 http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt
 
-3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and
-   "ssh-dsa-cert-v00@openssh.com"
+1.3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com",
+     "ssh-dsa-cert-v00@openssh.com",
+     "ecdsa-sha2-nistp256-cert-v01@openssh.com",
+     "ecdsa-sha2-nistp384-cert-v01@openssh.com" and
+     "ecdsa-sha2-nistp521-cert-v01@openssh.com"
 
-OpenSSH introduces two new public key algorithms to support certificate
+OpenSSH introduces new public key algorithms to support certificate
 authentication for users and hostkeys. These methods are documented in
 the file PROTOCOL.certkeys
 
-4. connection: Channel write close extension "eow@openssh.com"
+1.4. transport: Elliptic Curve cryptography
+
+OpenSSH supports ECC key exchange and public key authentication as
+specified in RFC5656. Only the ecdsa-sha2-nistp256, ecdsa-sha2-nistp384
+and ecdsa-sha2-nistp521 curves over GF(p) are supported. Elliptic
+curve points encoded using point compression are NOT accepted or
+generated.
+
+2. Connection protocol changes
+
+2.1. connection: Channel write close extension "eow@openssh.com"
 
 The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF
 message to allow an endpoint to signal its peer that it will send no
@@ -77,8 +92,8 @@
 Other SSH implementations may be whitelisted to receive this message
 upon request.
 
-5. connection: disallow additional sessions extension
-   "no-more-sessions@openssh.com"
+2.2. connection: disallow additional sessions extension
+     "no-more-sessions@openssh.com"
 
 Most SSH connections will only ever request a single session, but a
 attacker may abuse a running ssh client to surreptitiously open
@@ -105,7 +120,7 @@
 servers (identified by banner). Other SSH implementations may be
 whitelisted to receive this message upon request.
 
-6. connection: Tunnel forward extension "tun@openssh.com"
+2.3. connection: Tunnel forward extension "tun@openssh.com"
 
 OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com"
 channel type. This channel type supports forwarding of network packets
@@ -166,7 +181,9 @@
 The "frame" field contains an IEEE 802.3 Ethernet frame, including
 header.
 
-7. sftp: Reversal of arguments to SSH_FXP_SYMLINK
+3. SFTP protocol changes
+
+3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK
 
 When OpenSSH's sftp-server was implemented, the order of the arguments
 to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
@@ -179,7 +196,7 @@
 	string		targetpath
 	string		linkpath
 
-8. sftp: Server extension announcement in SSH_FXP_VERSION
+3.2. sftp: Server extension announcement in SSH_FXP_VERSION
 
 OpenSSH's sftp-server lists the extensions it supports using the
 standard extension announcement mechanism in the SSH_FXP_VERSION server
@@ -200,7 +217,7 @@
 extension with multiple versions (though this is unlikely). Clients MUST
 check the version number before attempting to use the extension.
 
-9. sftp: Extension request "posix-rename@openssh.com"
+3.3. sftp: Extension request "posix-rename@openssh.com"
 
 This operation provides a rename operation with POSIX semantics, which
 are different to those provided by the standard SSH_FXP_RENAME in
@@ -217,7 +234,7 @@
 This extension is advertised in the SSH_FXP_VERSION hello with version
 "1".
 
-10. sftp: Extension requests "statvfs@openssh.com" and
+3.4. sftp: Extension requests "statvfs@openssh.com" and
          "fstatvfs@openssh.com"
 
 These requests correspond to the statvfs and fstatvfs POSIX system
@@ -258,4 +275,4 @@
 Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are
 advertised in the SSH_FXP_VERSION hello with version "2".
 
-$OpenBSD: PROTOCOL,v 1.15 2010/02/26 20:29:54 djm Exp $
+$OpenBSD: PROTOCOL,v 1.16 2010/08/31 11:54:45 djm Exp $
diff --git a/PROTOCOL.agent b/PROTOCOL.agent
index b34fcd3..de94d03 100644
--- a/PROTOCOL.agent
+++ b/PROTOCOL.agent
@@ -159,8 +159,8 @@
 
 2.2.3 Add protocol 2 key
 
-The OpenSSH agent supports DSA and RSA keys for protocol 2. DSA keys may
-be added using the following request
+The OpenSSH agent supports DSA, ECDSA and RSA keys for protocol 2. DSA
+keys may be added using the following request
 
 	byte			SSH2_AGENTC_ADD_IDENTITY or
 				SSH2_AGENTC_ADD_ID_CONSTRAINED
@@ -182,6 +182,30 @@
 	string			key_comment
 	constraint[]		key_constraints
 
+ECDSA keys may be added using the following request
+
+	byte			SSH2_AGENTC_ADD_IDENTITY or
+				SSH2_AGENTC_ADD_ID_CONSTRAINED
+	string			"ecdsa-sha2-nistp256" |
+				"ecdsa-sha2-nistp384" |
+				"ecdsa-sha2-nistp521"
+	string			ecdsa_curve_name
+	string			ecdsa_public_key
+	mpint			ecdsa_private
+	string			key_comment
+	constraint[]		key_constraints
+
+ECDSA certificates may be added with:
+	byte			SSH2_AGENTC_ADD_IDENTITY or
+				SSH2_AGENTC_ADD_ID_CONSTRAINED
+	string			"ecdsa-sha2-nistp256-cert-v01@openssh.com" |
+				"ecdsa-sha2-nistp384-cert-v01@openssh.com" |
+				"ecdsa-sha2-nistp521-cert-v01@openssh.com"
+	string			certificate
+	mpint			ecdsa_private_key
+	string			key_comment
+	constraint[]		key_constraints
+
 RSA keys may be added with this request:
 
 	byte			SSH2_AGENTC_ADD_IDENTITY or
@@ -214,7 +238,7 @@
 protocol 1 "add key" request, the private key is overspecified to avoid
 redundant processing.
 
-For both DSA and RSA key add requests, "key_constraints" may only be
+For DSA, ECDSA and RSA key add requests, "key_constraints" may only be
 present if the request type is SSH2_AGENTC_ADD_ID_CONSTRAINED.
 
 The agent will reply with a SSH_AGENT_SUCCESS if the key has been
@@ -294,8 +318,7 @@
 	string			key_blob
 
 Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
-Algorithms" for either of the supported key types: "ssh-dss" or
-"ssh-rsa".
+Algorithms" for any of the supported protocol 2 key types.
 
 The agent will delete any private key matching the specified public key
 and return SSH_AGENT_SUCCESS. If no such key was found, the agent will
@@ -364,8 +387,7 @@
 	string			key_comment
 
 Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
-Algorithms" for either of the supported key types: "ssh-dss" or
-"ssh-rsa".
+Algorithms" for any of the supported protocol 2 key types.
 
 2.6 Private key operations
 
@@ -429,9 +451,9 @@
 	uint32			flags
 
 Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
-Algorithms" for either of the supported key types: "ssh-dss" or
-"ssh-rsa". "flags" is a bit-mask, but at present only one possible value
-is defined (see below for its meaning):
+Algorithms" for any of the supported protocol 2 key types. "flags" is
+a bit-mask, but at present only one possible value is defined (see below
+for its meaning):
 
 	SSH_AGENT_OLD_SIGNATURE		1
 
@@ -535,4 +557,4 @@
 	SSH_AGENT_CONSTRAIN_LIFETIME			1
 	SSH_AGENT_CONSTRAIN_CONFIRM			2
 
-$OpenBSD: PROTOCOL.agent,v 1.5 2010/02/26 20:29:54 djm Exp $
+$OpenBSD: PROTOCOL.agent,v 1.6 2010/08/31 11:54:45 djm Exp $
diff --git a/PROTOCOL.certkeys b/PROTOCOL.certkeys
index 1d1be13..2f97649 100644
--- a/PROTOCOL.certkeys
+++ b/PROTOCOL.certkeys
@@ -5,31 +5,37 @@
 ----------
 
 The SSH protocol currently supports a simple public key authentication
-mechanism. Unlike other public key implementations, SSH eschews the
-use of X.509 certificates and uses raw keys. This approach has some
-benefits relating to simplicity of configuration and minimisation
-of attack surface, but it does not support the important use-cases
-of centrally managed, passwordless authentication and centrally
-certified host keys.
+mechanism. Unlike other public key implementations, SSH eschews the use
+of X.509 certificates and uses raw keys. This approach has some benefits
+relating to simplicity of configuration and minimisation of attack
+surface, but it does not support the important use-cases of centrally
+managed, passwordless authentication and centrally certified host keys.
 
 These protocol extensions build on the simple public key authentication
-system already in SSH to allow certificate-based authentication.
-The certificates used are not traditional X.509 certificates, with
-numerous options and complex encoding rules, but something rather
-more minimal: a key, some identity information and usage options
-that have been signed with some other trusted key.
+system already in SSH to allow certificate-based authentication. The
+certificates used are not traditional X.509 certificates, with numerous
+options and complex encoding rules, but something rather more minimal: a
+key, some identity information and usage options that have been signed
+with some other trusted key.
 
 A sshd server may be configured to allow authentication via certified
-keys, by extending the existing ~/.ssh/authorized_keys mechanism
-to allow specification of certification authority keys in addition
-to raw user keys. The ssh client will support automatic verification
-of acceptance of certified host keys, by adding a similar ability
-to specify CA keys in ~/.ssh/known_hosts.
+keys, by extending the existing ~/.ssh/authorized_keys mechanism to
+allow specification of certification authority keys in addition to
+raw user keys. The ssh client will support automatic verification of
+acceptance of certified host keys, by adding a similar ability to
+specify CA keys in ~/.ssh/known_hosts.
 
-Certified keys are represented using two new key types:
-ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com that
-include certification information along with the public key that is used
-to sign challenges. ssh-keygen performs the CA signing operation.
+Certified keys are represented using new key types:
+
+    ssh-rsa-cert-v01@openssh.com
+    ssh-dss-cert-v01@openssh.com
+    ecdsa-sha2-nistp256-cert-v01@openssh.com
+    ecdsa-sha2-nistp384-cert-v01@openssh.com
+    ecdsa-sha2-nistp521-cert-v01@openssh.com
+
+These include certification information along with the public key
+that is used to sign challenges. ssh-keygen performs the CA signing
+operation.
 
 Protocol extensions
 -------------------
@@ -47,10 +53,9 @@
 New public key formats
 ----------------------
 
-The ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com key
-types take a similar high-level format (note: data types and
-encoding are as per RFC4251 section 5). The serialised wire encoding of
-these certificates is also used for storing them on disk.
+The certificate key types take a similar high-level format (note: data
+types and encoding are as per RFC4251 section 5). The serialised wire
+encoding of these certificates is also used for storing them on disk.
 
 #define SSH_CERT_TYPE_USER    1
 #define SSH_CERT_TYPE_HOST    2
@@ -93,6 +98,26 @@
     string    signature key
     string    signature
 
+ECDSA certificate
+
+    string    "ecdsa-sha2-nistp256@openssh.com" |
+              "ecdsa-sha2-nistp384@openssh.com" |
+              "ecdsa-sha2-nistp521@openssh.com"
+    string    nonce
+    string    curve
+    string    public_key
+    uint64    serial
+    uint32    type
+    string    key id
+    string    valid principals
+    uint64    valid after
+    uint64    valid before
+    string    critical options
+    string    extensions
+    string    reserved
+    string    signature key
+    string    signature
+
 The nonce field is a CA-provided random bitstring of arbitrary length
 (but typically 16 or 32 bytes) included to make attacks that depend on
 inducing collisions in the signature hash infeasible.
@@ -101,6 +126,9 @@
 
 p, q, g, y are the DSA parameters as described in FIPS-186-2.
 
+curve and public key are respectively the ECDSA "[identifier]" and "Q"
+defined in section 3.1 of RFC5656.
+
 serial is an optional certificate serial number set by the CA to
 provide an abbreviated way to refer to certificates from that CA.
 If a CA does not wish to number its certificates it must set this
@@ -123,7 +151,8 @@
 "valid after" and "valid before" specify a validity period for the
 certificate. Each represents a time in seconds since 1970-01-01
 00:00:00. A certificate is considered valid if:
-	 valid after <= current time < valid before
+
+    valid after <= current time < valid before
 
 criticial options is a set of zero or more key options encoded as
 below. All such options are "critical" in the sense that an implementation
@@ -137,15 +166,17 @@
 the protocol.
 
 signature key contains the CA key used to sign the certificate.
-The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained"
+The valid key types for CA keys are ssh-rsa, ssh-dss and the ECDSA types
+ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521. "Chained"
 certificates, where the signature key type is a certificate type itself
 are NOT supported. Note that it is possible for a RSA certificate key to
-be signed by a DSS CA key and vice-versa.
+be signed by a DSS or ECDSA CA key and vice-versa.
 
 signature is computed over all preceding fields from the initial string
 up to, and including the signature key. Signatures are computed and
 encoded according to the rules defined for the CA's public key algorithm
-(RFC4253 section 6.6 for ssh-rsa and ssh-dss).
+(RFC4253 section 6.6 for ssh-rsa and ssh-dss, RFC5656 for the ECDSA
+types).
 
 Critical options
 ----------------
@@ -222,4 +253,4 @@
                                       of this script will not be permitted if
                                       this option is not present.
 
-$OpenBSD: PROTOCOL.certkeys,v 1.7 2010/08/04 05:40:39 djm Exp $
+$OpenBSD: PROTOCOL.certkeys,v 1.8 2010/08/31 11:54:45 djm Exp $
diff --git a/auth2-jpake.c b/auth2-jpake.c
index 5de5506..a460e82 100644
--- a/auth2-jpake.c
+++ b/auth2-jpake.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 djm Exp $ */
+/* $OpenBSD: auth2-jpake.c,v 1.4 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
  *
@@ -162,6 +162,11 @@
 			fatal("%s: DSA key missing priv_key", __func__);
 		buffer_put_bignum2(&b, k->dsa->priv_key);
 		break;
+	case KEY_ECDSA:
+		if (EC_KEY_get0_private_key(k->ecdsa) == NULL)
+			fatal("%s: ECDSA key missing priv_key", __func__);
+		buffer_put_bignum2(&b, EC_KEY_get0_private_key(k->ecdsa));
+		break;
 	default:
 		fatal("%s: unknown key type %d", __func__, k->type);
 	}
diff --git a/authfd.c b/authfd.c
index 739722f..ec537d2 100644
--- a/authfd.c
+++ b/authfd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfd.c,v 1.83 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: authfd.c,v 1.84 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -509,6 +509,19 @@
 		    buffer_len(&key->cert->certblob));
 		buffer_put_bignum2(b, key->dsa->priv_key);
 		break;
+	case KEY_ECDSA:
+		buffer_put_cstring(b, key_curve_nid_to_name(key->ecdsa_nid));
+		buffer_put_ecpoint(b, EC_KEY_get0_group(key->ecdsa),
+		    EC_KEY_get0_public_key(key->ecdsa));
+		buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa));
+		break;
+	case KEY_ECDSA_CERT:
+		if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
+			fatal("%s: no cert/certblob", __func__);
+		buffer_put_string(b, buffer_ptr(&key->cert->certblob),
+		    buffer_len(&key->cert->certblob));
+		buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa));
+		break;
 	}
 	buffer_put_cstring(b, comment);
 }
@@ -541,6 +554,8 @@
 	case KEY_DSA:
 	case KEY_DSA_CERT:
 	case KEY_DSA_CERT_V00:
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
 		type = constrained ?
 		    SSH2_AGENTC_ADD_ID_CONSTRAINED :
 		    SSH2_AGENTC_ADD_IDENTITY;
@@ -589,7 +604,8 @@
 		buffer_put_bignum(&msg, key->rsa->e);
 		buffer_put_bignum(&msg, key->rsa->n);
 	} else if (key_type_plain(key->type) == KEY_DSA ||
-	    key_type_plain(key->type) == KEY_RSA) {
+	    key_type_plain(key->type) == KEY_RSA ||
+	    key_type_plain(key->type) == KEY_ECDSA) {
 		key_to_blob(key, &blob, &blen);
 		buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY);
 		buffer_put_string(&msg, blob, blen);
diff --git a/authfile.c b/authfile.c
index 2bd8878..865e7fa 100644
--- a/authfile.c
+++ b/authfile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfile.c,v 1.82 2010/08/04 05:49:22 djm Exp $ */
+/* $OpenBSD: authfile.c,v 1.83 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -213,6 +213,10 @@
 		success = PEM_write_DSAPrivateKey(fp, key->dsa,
 		    cipher, passphrase, len, NULL, NULL);
 		break;
+	case KEY_ECDSA:
+		success = PEM_write_ECPrivateKey(fp, key->ecdsa,
+		    cipher, passphrase, len, NULL, NULL);
+		break;
 	case KEY_RSA:
 		success = PEM_write_RSAPrivateKey(fp, key->rsa,
 		    cipher, passphrase, len, NULL, NULL);
@@ -231,6 +235,7 @@
 		return key_save_private_rsa1(key, filename, passphrase,
 		    comment);
 	case KEY_DSA:
+	case KEY_ECDSA:
 	case KEY_RSA:
 		return key_save_private_pem(key, filename, passphrase,
 		    comment);
@@ -510,6 +515,29 @@
 #ifdef DEBUG_PK
 		DSA_print_fp(stderr, prv->dsa, 8);
 #endif
+	} else if (pk->type == EVP_PKEY_EC &&
+	    (type == KEY_UNSPEC||type==KEY_ECDSA)) {
+		prv = key_new(KEY_UNSPEC);
+		prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
+		prv->type = KEY_ECDSA;
+		prv->ecdsa_nid = key_ecdsa_group_to_nid(
+		    EC_KEY_get0_group(prv->ecdsa));
+		if (key_curve_nid_to_name(prv->ecdsa_nid) == NULL) {
+			key_free(prv);
+			prv = NULL;
+		}
+		if (key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
+		    EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
+		    key_ec_validate_private(prv->ecdsa) != 0) {
+			error("%s: bad ECDSA key", __func__);
+			key_free(prv);
+			prv = NULL;
+		}
+		name = "dsa w/o comment";
+#ifdef DEBUG_PK
+		if (prv->ecdsa != NULL)
+			key_dump_ec_key(prv->ecdsa);
+#endif
 	} else {
 		error("PEM_read_PrivateKey: mismatch or "
 		    "unknown EVP_PKEY save_type %d", pk->save_type);
@@ -581,6 +609,7 @@
 		    commentp);
 		/* closes fd */
 	case KEY_DSA:
+	case KEY_ECDSA:
 	case KEY_RSA:
 	case KEY_UNSPEC:
 		return key_load_private_pem(fd, type, passphrase, commentp);
@@ -721,6 +750,7 @@
 	switch (type) {
 	case KEY_RSA:
 	case KEY_DSA:
+	case KEY_ECDSA:
 		break;
 	default:
 		error("%s: unsupported key type", __func__);
diff --git a/bufec.c b/bufec.c
new file mode 100644
index 0000000..dff9c69
--- /dev/null
+++ b/bufec.c
@@ -0,0 +1,140 @@
+/* $OpenBSD: bufec.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */
+/*
+ * Copyright (c) 2010 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+#include <string.h>
+#include <stdarg.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "log.h"
+#include "misc.h"
+
+/*
+ * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed
+ * encoding represents this as two bitstring points that should each
+ * be no longer than the field length, SEC1 specifies a 1 byte
+ * point type header.
+ * Being paranoid here may insulate us to parsing problems in
+ * EC_POINT_oct2point.
+ */
+#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1)
+
+/*
+ * Append an EC_POINT to the buffer as a string containing a SEC1 encoded
+ * uncompressed point. Fortunately OpenSSL handles the gory details for us.
+ */
+int
+buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
+    const EC_POINT *point)
+{
+	u_char *buf = NULL;
+	size_t len;
+	BN_CTX *bnctx;
+	int ret = -1;
+
+	/* Determine length */
+	if ((bnctx = BN_CTX_new()) == NULL)
+		fatal("%s: BN_CTX_new failed", __func__);
+	len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
+	    NULL, 0, bnctx);
+	if (len > BUFFER_MAX_ECPOINT_LEN) {
+		error("%s: giant EC point: len = %lu (max %u)",
+		    __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN);
+		goto out;
+	}
+	/* Convert */
+	buf = xmalloc(len);
+	if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
+	    buf, len, bnctx) != len) {
+		error("%s: EC_POINT_point2oct length mismatch", __func__);
+		goto out;
+	}
+	/* Append */
+	buffer_put_string(buffer, buf, len);
+	ret = 0;
+ out:
+	if (buf != NULL) {
+		bzero(buf, len);
+		xfree(buf);
+	}
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+void
+buffer_put_ecpoint(Buffer *buffer, const EC_GROUP *curve,
+    const EC_POINT *point)
+{
+	if (buffer_put_ecpoint_ret(buffer, curve, point) == -1)
+		fatal("%s: buffer error", __func__);
+}
+
+int
+buffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
+    EC_POINT *point)
+{
+	u_char *buf;
+	u_int len;
+	BN_CTX *bnctx;
+	int ret = -1;
+
+	if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) {
+		error("%s: invalid point", __func__);
+		return -1;
+	}
+	if ((bnctx = BN_CTX_new()) == NULL)
+		fatal("%s: BN_CTX_new failed", __func__);
+	if (len > BUFFER_MAX_ECPOINT_LEN) {
+		error("%s: EC_POINT too long: %u > max %u", __func__,
+		    len, BUFFER_MAX_ECPOINT_LEN);
+		goto out;
+	}
+	if (len == 0) {
+		error("%s: EC_POINT buffer is empty", __func__);
+		goto out;
+	}
+	if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) {
+		error("%s: EC_POINT is in an incorrect form: "
+		    "0x%02x (want 0x%02x)", __func__, buf[0],
+		    POINT_CONVERSION_UNCOMPRESSED);
+		goto out;
+	}
+	if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) {
+		error("buffer_get_bignum2_ret: BN_bin2bn failed");
+		goto out;
+	}
+	/* EC_POINT_oct2point verifies that the point is on the curve for us */
+	ret = 0;
+ out:
+	BN_CTX_free(bnctx);
+	bzero(buf, len);
+	xfree(buf);
+	return ret;
+}
+
+void
+buffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve,
+    EC_POINT *point)
+{
+	if (buffer_get_ecpoint_ret(buffer, curve, point) == -1)
+		fatal("%s: buffer error", __func__);
+}
+
diff --git a/buffer.h b/buffer.h
index 93baae2..1fb3f16 100644
--- a/buffer.h
+++ b/buffer.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: buffer.h,v 1.20 2010/08/31 09:58:37 djm Exp $ */
+/* $OpenBSD: buffer.h,v 1.21 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -86,4 +86,11 @@
 void	*buffer_get_string_ptr_ret(Buffer *, u_int *);
 int	buffer_get_char_ret(char *, Buffer *);
 
+#include <openssl/ec.h>
+
+int	buffer_put_ecpoint_ret(Buffer *, const EC_GROUP *, const EC_POINT *);
+void	buffer_put_ecpoint(Buffer *, const EC_GROUP *, const EC_POINT *);
+int	buffer_get_ecpoint_ret(Buffer *, const EC_GROUP *, EC_POINT *);
+void	buffer_get_ecpoint(Buffer *, const EC_GROUP *, EC_POINT *);
+
 #endif				/* BUFFER_H */
diff --git a/dns.c b/dns.c
index 2e7bb5a..131cb3d 100644
--- a/dns.c
+++ b/dns.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: dns.c,v 1.27 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -86,6 +86,7 @@
 	case KEY_DSA:
 		*algorithm = SSHFP_KEY_DSA;
 		break;
+	/* XXX KEY_ECDSA */
 	default:
 		*algorithm = SSHFP_KEY_RESERVED; /* 0 */
 	}
diff --git a/kex.c b/kex.c
index ca5aae3..abe9b9f 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.83 2010/08/31 09:58:37 djm Exp $ */
+/* $OpenBSD: kex.c,v 1.84 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
@@ -325,6 +325,10 @@
 	} else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) {
 		k->kex_type = KEX_DH_GEX_SHA256;
 		k->evp_md = evp_ssh_sha256();
+	} else if (strncmp(k->name, KEX_ECDH_SHA256,
+	    sizeof(KEX_ECDH_SHA256) - 1) == 0) {
+		k->kex_type = KEX_ECDH_SHA2;
+		k->evp_md = evp_ssh_sha256();
 #endif
 	} else
 		fatal("bad kex alg %s", k->name);
@@ -559,11 +563,11 @@
 	memset(&md, 0, sizeof(md));
 }
 
-#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
+#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
 void
 dump_digest(char *msg, u_char *digest, int len)
 {
-	u_int i;
+	int i;
 
 	fprintf(stderr, "%s\n", msg);
 	for (i = 0; i < len; i++) {
diff --git a/kex.h b/kex.h
index 62fa2ea..a183ffd 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.49 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: kex.h,v 1.50 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -29,6 +29,7 @@
 #include <signal.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#include <openssl/ec.h>
 
 #define KEX_COOKIE_LEN	16
 
@@ -37,6 +38,8 @@
 #define	KEX_DHGEX_SHA1		"diffie-hellman-group-exchange-sha1"
 #define	KEX_DHGEX_SHA256	"diffie-hellman-group-exchange-sha256"
 #define	KEX_RESUME		"resume@appgate.com"
+/* The following represents the family of ECDH methods */
+#define	KEX_ECDH_SHA256		"ecdh-sha2-"
 
 #define COMP_NONE	0
 #define COMP_ZLIB	1
@@ -67,6 +70,7 @@
 	KEX_DH_GRP14_SHA1,
 	KEX_DH_GEX_SHA1,
 	KEX_DH_GEX_SHA256,
+	KEX_ECDH_SHA2,
 	KEX_MAX
 };
 
@@ -145,6 +149,8 @@
 void	 kexdh_server(Kex *);
 void	 kexgex_client(Kex *);
 void	 kexgex_server(Kex *);
+void	 kexecdh_client(Kex *);
+void	 kexecdh_server(Kex *);
 
 void
 kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
@@ -153,11 +159,17 @@
 kexgex_hash(const EVP_MD *, char *, char *, char *, int, char *,
     int, u_char *, int, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *,
     BIGNUM *, BIGNUM *, u_char **, u_int *);
+void
+kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, int,
+    char *, int, u_char *, int, const EC_POINT *, const EC_POINT *,
+    const BIGNUM *, u_char **, u_int *);
+
+int	kex_ecdh_name_to_nid(const char *);
 
 void
 derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]);
 
-#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
+#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
 void	dump_digest(char *, u_char *, int);
 #endif
 
diff --git a/kexecdh.c b/kexecdh.c
new file mode 100644
index 0000000..a5a14f4
--- /dev/null
+++ b/kexecdh.c
@@ -0,0 +1,108 @@
+/* $OpenBSD: kexecdh.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <signal.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+
+#include "buffer.h"
+#include "ssh2.h"
+#include "key.h"
+#include "cipher.h"
+#include "kex.h"
+#include "log.h"
+
+int
+kex_ecdh_name_to_nid(const char *kexname)
+{
+	int ret;
+
+	if (strlen(kexname) < sizeof(KEX_ECDH_SHA256) - 1)
+		fatal("%s: kexname too short \"%s\"", __func__, kexname);
+	ret = key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA256) - 1);
+	if (ret == -1)
+		fatal("%s: unsupported curve negotiated \"%s\"", __func__,
+		    kexname);
+	return ret;
+}
+
+void
+kex_ecdh_hash(
+    const EVP_MD *evp_md,
+    const EC_GROUP *ec_group,
+    char *client_version_string,
+    char *server_version_string,
+    char *ckexinit, int ckexinitlen,
+    char *skexinit, int skexinitlen,
+    u_char *serverhostkeyblob, int sbloblen,
+    const EC_POINT *client_dh_pub,
+    const EC_POINT *server_dh_pub,
+    const BIGNUM *shared_secret,
+    u_char **hash, u_int *hashlen)
+{
+	Buffer b;
+	EVP_MD_CTX md;
+	static u_char digest[EVP_MAX_MD_SIZE];
+
+	buffer_init(&b);
+	buffer_put_cstring(&b, client_version_string);
+	buffer_put_cstring(&b, server_version_string);
+
+	/* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
+	buffer_put_int(&b, ckexinitlen+1);
+	buffer_put_char(&b, SSH2_MSG_KEXINIT);
+	buffer_append(&b, ckexinit, ckexinitlen);
+	buffer_put_int(&b, skexinitlen+1);
+	buffer_put_char(&b, SSH2_MSG_KEXINIT);
+	buffer_append(&b, skexinit, skexinitlen);
+
+	buffer_put_string(&b, serverhostkeyblob, sbloblen);
+	buffer_put_ecpoint(&b, ec_group, client_dh_pub);
+	buffer_put_ecpoint(&b, ec_group, server_dh_pub);
+	buffer_put_bignum2(&b, shared_secret);
+
+#ifdef DEBUG_KEX
+	buffer_dump(&b);
+#endif
+	EVP_DigestInit(&md, evp_md);
+	EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+	EVP_DigestFinal(&md, digest, NULL);
+
+	buffer_free(&b);
+
+#ifdef DEBUG_KEX
+	dump_digest("hash", digest, EVP_MD_size(evp_md));
+#endif
+	*hash = digest;
+	*hashlen = EVP_MD_size(evp_md);
+}
+
diff --git a/kexecdhc.c b/kexecdhc.c
new file mode 100644
index 0000000..f6d9977
--- /dev/null
+++ b/kexecdhc.c
@@ -0,0 +1,156 @@
+/* $OpenBSD: kexecdhc.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include <openssl/ecdh.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "key.h"
+#include "cipher.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+
+void
+kexecdh_client(Kex *kex)
+{
+	EC_KEY *client_key;
+	EC_POINT *server_public;
+	const EC_GROUP *group;
+	BIGNUM *shared_secret;
+	Key *server_host_key;
+	u_char *server_host_key_blob = NULL, *signature = NULL;
+	u_char *kbuf, *hash;
+	u_int klen, slen, sbloblen, hashlen;
+	int curve_nid;
+
+	curve_nid = kex_ecdh_name_to_nid(kex->name);
+	if ((client_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL)
+		fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+	if (EC_KEY_generate_key(client_key) != 1)
+		fatal("%s: EC_KEY_generate_key failed", __func__);
+	group = EC_KEY_get0_group(client_key);
+
+	packet_start(SSH2_MSG_KEX_ECDH_INIT);
+	packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key));
+	packet_send();
+	debug("sending SSH2_MSG_KEX_ECDH_INIT");
+
+#ifdef DEBUG_KEXECDH
+	fputs("client private key:\n", stderr);
+	key_dump_ec_key(client_key);
+#endif
+
+	debug("expecting SSH2_MSG_KEX_ECDH_REPLY");
+	packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY);
+
+	/* hostkey */
+	server_host_key_blob = packet_get_string(&sbloblen);
+	server_host_key = key_from_blob(server_host_key_blob, sbloblen);
+	if (server_host_key == NULL)
+		fatal("cannot decode server_host_key_blob");
+	if (server_host_key->type != kex->hostkey_type)
+		fatal("type mismatch for decoded server_host_key_blob");
+	if (kex->verify_host_key == NULL)
+		fatal("cannot verify server_host_key");
+	if (kex->verify_host_key(server_host_key) == -1)
+		fatal("server_host_key verification failed");
+
+	/* Q_S, server public key */
+	if ((server_public = EC_POINT_new(group)) == NULL)
+		fatal("%s: EC_POINT_new failed", __func__);
+	packet_get_ecpoint(group, server_public);
+
+	if (key_ec_validate_public(group, server_public) != 0)
+		fatal("%s: invalid server public key", __func__);
+
+#ifdef DEBUG_KEXECDH
+	fputs("server public key:\n", stderr);
+	key_dump_ec_point(group, server_public);
+#endif
+
+	/* signed H */
+	signature = packet_get_string(&slen);
+	packet_check_eom();
+
+	klen = (EC_GROUP_get_degree(group) + 7) / 8;
+	kbuf = xmalloc(klen);
+	if (ECDH_compute_key(kbuf, klen, server_public,
+	    client_key, NULL) != (int)klen)
+		fatal("%s: ECDH_compute_key failed", __func__);
+
+#ifdef DEBUG_KEXECDH
+	dump_digest("shared secret", kbuf, klen);
+#endif
+	if ((shared_secret = BN_new()) == NULL)
+		fatal("%s: BN_new failed", __func__);
+	if (BN_bin2bn(kbuf, klen, shared_secret) == NULL)
+		fatal("%s: BN_bin2bn failed", __func__);
+	memset(kbuf, 0, klen);
+	xfree(kbuf);
+
+	/* calc and verify H */
+	kex_ecdh_hash(
+	    kex->evp_md,
+	    group,
+	    kex->client_version_string,
+	    kex->server_version_string,
+	    buffer_ptr(&kex->my), buffer_len(&kex->my),
+	    buffer_ptr(&kex->peer), buffer_len(&kex->peer),
+	    server_host_key_blob, sbloblen,
+	    EC_KEY_get0_public_key(client_key),
+	    server_public,
+	    shared_secret,
+	    &hash, &hashlen
+	);
+	xfree(server_host_key_blob);
+	EC_POINT_clear_free(server_public);
+	EC_KEY_free(client_key);
+
+	if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1)
+		fatal("key_verify failed for server_host_key");
+	key_free(server_host_key);
+	xfree(signature);
+
+	/* save session id */
+	if (kex->session_id == NULL) {
+		kex->session_id_len = hashlen;
+		kex->session_id = xmalloc(kex->session_id_len);
+		memcpy(kex->session_id, hash, kex->session_id_len);
+	}
+
+	kex_derive_keys(kex, hash, hashlen, shared_secret);
+	BN_clear_free(shared_secret);
+	kex_finish(kex);
+}
diff --git a/kexecdhs.c b/kexecdhs.c
new file mode 100644
index 0000000..d733338
--- /dev/null
+++ b/kexecdhs.c
@@ -0,0 +1,161 @@
+/* $OpenBSD: kexecdhs.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <signal.h>
+
+#include <openssl/ecdh.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "key.h"
+#include "cipher.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
+
+void
+kexecdh_server(Kex *kex)
+{
+	EC_POINT *client_public;
+	EC_KEY *server_key;
+	const EC_GROUP *group;
+	BIGNUM *shared_secret;
+	Key *server_host_private, *server_host_public;
+	u_char *server_host_key_blob = NULL, *signature = NULL;
+	u_char *kbuf, *hash;
+	u_int klen, slen, sbloblen, hashlen;
+	int curve_nid;
+
+	curve_nid = kex_ecdh_name_to_nid(kex->name);
+	if ((server_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL)
+		fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+	if (EC_KEY_generate_key(server_key) != 1)
+		fatal("%s: EC_KEY_generate_key failed", __func__);
+	group = EC_KEY_get0_group(server_key);
+
+#ifdef DEBUG_KEXECDH
+	fputs("server private key:\n", stderr);
+	key_dump_ec_key(server_key);
+#endif
+
+	if (kex->load_host_public_key == NULL ||
+	    kex->load_host_private_key == NULL)
+		fatal("Cannot load hostkey");
+	server_host_public = kex->load_host_public_key(kex->hostkey_type);
+	if (server_host_public == NULL)
+		fatal("Unsupported hostkey type %d", kex->hostkey_type);
+	server_host_private = kex->load_host_private_key(kex->hostkey_type);
+	if (server_host_private == NULL)
+		fatal("Missing private key for hostkey type %d",
+		    kex->hostkey_type);
+
+	debug("expecting SSH2_MSG_KEX_ECDH_INIT");
+	packet_read_expect(SSH2_MSG_KEX_ECDH_INIT);
+	if ((client_public = EC_POINT_new(group)) == NULL)
+		fatal("%s: EC_POINT_new failed", __func__);
+	packet_get_ecpoint(group, client_public);
+	packet_check_eom();
+
+	if (key_ec_validate_public(group, client_public) != 0)
+		fatal("%s: invalid client public key", __func__);
+
+#ifdef DEBUG_KEXECDH
+	fputs("client public key:\n", stderr);
+	key_dump_ec_point(group, client_public);
+#endif
+
+	/* Calculate shared_secret */
+	klen = (EC_GROUP_get_degree(group) + 7) / 8;
+	kbuf = xmalloc(klen);
+	if (ECDH_compute_key(kbuf, klen, client_public,
+	    server_key, NULL) != (int)klen)
+		fatal("%s: ECDH_compute_key failed", __func__);
+
+#ifdef DEBUG_KEXDH
+	dump_digest("shared secret", kbuf, klen);
+#endif
+	if ((shared_secret = BN_new()) == NULL)
+		fatal("%s: BN_new failed", __func__);
+	if (BN_bin2bn(kbuf, klen, shared_secret) == NULL)
+		fatal("%s: BN_bin2bn failed", __func__);
+	memset(kbuf, 0, klen);
+	xfree(kbuf);
+
+	/* calc H */
+	key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
+	kex_ecdh_hash(
+	    kex->evp_md,
+	    group,
+	    kex->client_version_string,
+	    kex->server_version_string,
+	    buffer_ptr(&kex->peer), buffer_len(&kex->peer),
+	    buffer_ptr(&kex->my), buffer_len(&kex->my),
+	    server_host_key_blob, sbloblen,
+	    client_public,
+	    EC_KEY_get0_public_key(server_key),
+	    shared_secret,
+	    &hash, &hashlen
+	);
+	EC_POINT_clear_free(client_public);
+
+	/* save session id := H */
+	if (kex->session_id == NULL) {
+		kex->session_id_len = hashlen;
+		kex->session_id = xmalloc(kex->session_id_len);
+		memcpy(kex->session_id, hash, kex->session_id_len);
+	}
+
+	/* sign H */
+	if (PRIVSEP(key_sign(server_host_private, &signature, &slen,
+	    hash, hashlen)) < 0)
+		fatal("kexdh_server: key_sign failed");
+
+	/* destroy_sensitive_data(); */
+
+	/* send server hostkey, ECDH pubkey 'Q_S' and signed H */
+	packet_start(SSH2_MSG_KEX_ECDH_REPLY);
+	packet_put_string(server_host_key_blob, sbloblen);
+	packet_put_ecpoint(group, EC_KEY_get0_public_key(server_key));
+	packet_put_string(signature, slen);
+	packet_send();
+
+	xfree(signature);
+	xfree(server_host_key_blob);
+	/* have keys, free server key */
+	EC_KEY_free(server_key);
+
+	kex_derive_keys(kex, hash, hashlen, shared_secret);
+	BN_clear_free(shared_secret);
+	kex_finish(kex);
+}
diff --git a/key.c b/key.c
index aed4678..842280a 100644
--- a/key.c
+++ b/key.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.91 2010/08/31 09:58:37 djm Exp $ */
+/* $OpenBSD: key.c,v 1.92 2010/08/31 11:54:45 djm Exp $ */
 /*
  * read_bignum():
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -78,6 +78,8 @@
 	DSA *dsa;
 	k = xcalloc(1, sizeof(*k));
 	k->type = type;
+	k->ecdsa = NULL;
+	k->ecdsa_nid = -1;
 	k->dsa = NULL;
 	k->rsa = NULL;
 	k->cert = NULL;
@@ -109,6 +111,10 @@
 			fatal("key_new: BN_new failed");
 		k->dsa = dsa;
 		break;
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
+		/* Cannot do anything until we know the group */
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -149,6 +155,10 @@
 		if ((k->dsa->priv_key = BN_new()) == NULL)
 			fatal("key_new_private: BN_new failed");
 		break;
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
+		/* Cannot do anything until we know the group */
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -204,6 +214,12 @@
 			DSA_free(k->dsa);
 		k->dsa = NULL;
 		break;
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
+		if (k->ecdsa != NULL)
+			EC_KEY_free(k->ecdsa);
+		k->ecdsa = NULL;
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -241,6 +257,8 @@
 int
 key_equal_public(const Key *a, const Key *b)
 {
+	BN_CTX *bnctx;
+
 	if (a == NULL || b == NULL ||
 	    key_type_plain(a->type) != key_type_plain(b->type))
 		return 0;
@@ -261,6 +279,24 @@
 		    BN_cmp(a->dsa->q, b->dsa->q) == 0 &&
 		    BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
 		    BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
+	case KEY_ECDSA_CERT:
+	case KEY_ECDSA:
+		if (a->ecdsa == NULL || b->ecdsa == NULL ||
+		    EC_KEY_get0_public_key(a->ecdsa) == NULL ||
+		    EC_KEY_get0_public_key(b->ecdsa) == NULL)
+			return 0;
+		if ((bnctx = BN_CTX_new()) == NULL)
+			fatal("%s: BN_CTX_new failed", __func__);
+		if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
+		    EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
+		    EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
+		    EC_KEY_get0_public_key(a->ecdsa),
+		    EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
+			BN_CTX_free(bnctx);
+			return 0;
+		}
+		BN_CTX_free(bnctx);
+		return 1;
 	default:
 		fatal("key_equal: bad key type %d", a->type);
 	}
@@ -312,12 +348,14 @@
 		BN_bn2bin(k->rsa->e, blob + nlen);
 		break;
 	case KEY_DSA:
+	case KEY_ECDSA:
 	case KEY_RSA:
 		key_to_blob(k, &blob, &len);
 		break;
 	case KEY_DSA_CERT_V00:
 	case KEY_RSA_CERT_V00:
 	case KEY_DSA_CERT:
+	case KEY_ECDSA_CERT:
 	case KEY_RSA_CERT:
 		/* We want a fingerprint of the _key_ not of the cert */
 		otype = k->type;
@@ -612,7 +650,7 @@
 	Key *k;
 	int success = -1;
 	char *cp, *space;
-	int len, n, type;
+	int len, n, type, curve_nid = -1;
 	u_int bits;
 	u_char *blob;
 
@@ -644,9 +682,11 @@
 	case KEY_UNSPEC:
 	case KEY_RSA:
 	case KEY_DSA:
+	case KEY_ECDSA:
 	case KEY_DSA_CERT_V00:
 	case KEY_RSA_CERT_V00:
 	case KEY_DSA_CERT:
+	case KEY_ECDSA_CERT:
 	case KEY_RSA_CERT:
 		space = strchr(cp, ' ');
 		if (space == NULL) {
@@ -655,6 +695,11 @@
 		}
 		*space = '\0';
 		type = key_type_from_name(cp);
+		if (key_type_plain(type) == KEY_ECDSA &&
+		    (curve_nid = key_ecdsa_nid_from_name(cp)) == -1) {
+			debug("key_read: invalid curve");
+			return -1;
+		}
 		*space = ' ';
 		if (type == KEY_UNSPEC) {
 			debug3("key_read: missing keytype");
@@ -691,6 +736,12 @@
 			key_free(k);
 			return -1;
 		}
+		if (key_type_plain(type) == KEY_ECDSA &&
+		    curve_nid != k->ecdsa_nid) {
+			error("key_read: type mismatch: EC curve mismatch");
+			key_free(k);
+			return -1;
+		}
 /*XXXX*/
 		if (key_is_cert(ret)) {
 			if (!key_is_cert(k)) {
@@ -721,6 +772,17 @@
 			DSA_print_fp(stderr, ret->dsa, 8);
 #endif
 		}
+		if (key_type_plain(ret->type) == KEY_ECDSA) {
+			if (ret->ecdsa != NULL)
+				EC_KEY_free(ret->ecdsa);
+			ret->ecdsa = k->ecdsa;
+			ret->ecdsa_nid = k->ecdsa_nid;
+			k->ecdsa = NULL;
+			k->ecdsa_nid = -1;
+#ifdef DEBUG_PK
+			key_dump_ec_key(ret->ecdsa);
+#endif
+		}
 		success = 1;
 /*XXXX*/
 		key_free(k);
@@ -777,6 +839,11 @@
 		if (key->dsa == NULL)
 			return 0;
 		break;
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
+		if (key->ecdsa == NULL)
+			return 0;
+		break;
 	case KEY_RSA:
 	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
@@ -810,6 +877,8 @@
 		return "RSA";
 	case KEY_DSA:
 		return "DSA";
+	case KEY_ECDSA:
+		return "ECDSA";
 	case KEY_RSA_CERT_V00:
 		return "RSA-CERT-V00";
 	case KEY_DSA_CERT_V00:
@@ -818,6 +887,8 @@
 		return "RSA-CERT";
 	case KEY_DSA_CERT:
 		return "DSA-CERT";
+	case KEY_ECDSA_CERT:
+		return "ECDSA-CERT";
 	}
 	return "unknown";
 }
@@ -835,10 +906,10 @@
 	}
 }
 
-const char *
-key_ssh_name(const Key *k)
+static const char *
+key_ssh_name_from_type_nid(int type, int nid)
 {
-	switch (k->type) {
+	switch (type) {
 	case KEY_RSA:
 		return "ssh-rsa";
 	case KEY_DSA:
@@ -851,10 +922,47 @@
 		return "ssh-rsa-cert-v01@openssh.com";
 	case KEY_DSA_CERT:
 		return "ssh-dss-cert-v01@openssh.com";
+	case KEY_ECDSA:
+		switch (nid) {
+		case NID_X9_62_prime256v1:
+			return "ecdsa-sha2-nistp256";
+		case NID_secp384r1:
+			return "ecdsa-sha2-nistp384";
+		case NID_secp521r1:
+			return "ecdsa-sha2-nistp521";
+		default:
+			break;
+		}
+		break;
+	case KEY_ECDSA_CERT:
+		switch (nid) {
+		case NID_X9_62_prime256v1:
+			return "ecdsa-sha2-nistp256-cert-v01@openssh.com";
+		case NID_secp384r1:
+			return "ecdsa-sha2-nistp384-cert-v01@openssh.com";
+		case NID_secp521r1:
+			return "ecdsa-sha2-nistp521-cert-v01@openssh.com";
+		default:
+			break;
+		}
+		break;
 	}
 	return "ssh-unknown";
 }
 
+const char *
+key_ssh_name(const Key *k)
+{
+	return key_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
+}
+
+const char *
+key_ssh_name_plain(const Key *k)
+{
+	return key_ssh_name_from_type_nid(key_type_plain(k->type),
+	    k->ecdsa_nid);
+}
+
 u_int
 key_size(const Key *k)
 {
@@ -868,6 +976,19 @@
 	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		return BN_num_bits(k->dsa->p);
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
+		switch (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;
 }
@@ -897,6 +1018,69 @@
 	return private;
 }
 
+int
+key_ecdsa_bits_to_nid(int bits)
+{
+	switch (bits) {
+	case 256:
+		return NID_X9_62_prime256v1;
+	case 384:
+		return NID_secp384r1;
+	case 521:
+		return NID_secp521r1;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * This is horrid, but OpenSSL's PEM_read_PrivateKey seems not to restore
+ * the EC_GROUP nid when loading a key...
+ */
+int
+key_ecdsa_group_to_nid(const EC_GROUP *g)
+{
+	EC_GROUP *eg;
+	int nids[] = {
+		NID_X9_62_prime256v1,
+		NID_secp384r1,
+		NID_secp521r1,
+		-1
+	};
+	u_int i;
+	BN_CTX *bnctx;
+
+	if ((bnctx = BN_CTX_new()) == NULL)
+		fatal("%s: BN_CTX_new() failed", __func__);
+	for (i = 0; nids[i] != -1; i++) {
+		if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
+			fatal("%s: EC_GROUP_new_by_curve_name failed",
+			    __func__);
+		if (EC_GROUP_cmp(g, eg, bnctx) == 0) {
+			EC_GROUP_free(eg);
+			break;
+		}
+		EC_GROUP_free(eg);
+	}
+	BN_CTX_free(bnctx);
+	debug3("%s: nid = %d", __func__, nids[i]);
+	return nids[i];
+}
+
+static EC_KEY*
+ecdsa_generate_private_key(u_int bits, int *nid)
+{
+	EC_KEY *private;
+
+	if ((*nid = key_ecdsa_bits_to_nid(bits)) == -1)
+		fatal("%s: invalid key length", __func__);
+	if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL)
+		fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+	if (EC_KEY_generate_key(private) != 1)
+		fatal("%s: EC_KEY_generate_key failed", __func__);
+	return private;
+}
+
 Key *
 key_generate(int type, u_int bits)
 {
@@ -905,6 +1089,9 @@
 	case KEY_DSA:
 		k->dsa = dsa_generate_private_key(bits);
 		break;
+	case KEY_ECDSA:
+		k->ecdsa = ecdsa_generate_private_key(bits, &k->ecdsa_nid);
+		break;
 	case KEY_RSA:
 	case KEY_RSA1:
 		k->rsa = rsa_generate_private_key(bits);
@@ -981,6 +1168,16 @@
 		    (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL))
 			fatal("key_from_private: BN_copy failed");
 		break;
+	case KEY_ECDSA:
+	case KEY_ECDSA_CERT:
+		n = key_new(k->type);
+		n->ecdsa_nid = k->ecdsa_nid;
+		if ((n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
+			fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+		if (EC_KEY_set_public_key(n->ecdsa,
+		    EC_KEY_get0_public_key(k->ecdsa)) != 1)
+			fatal("%s: EC_KEY_set_public_key failed", __func__);
+		break;
 	case KEY_RSA:
 	case KEY_RSA1:
 	case KEY_RSA_CERT_V00:
@@ -1012,6 +1209,11 @@
 		return KEY_RSA;
 	} else if (strcmp(name, "ssh-dss") == 0) {
 		return KEY_DSA;
+	} else if (strcmp(name, "ecdsa") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp256") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp384") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp521") == 0) {
+		return KEY_ECDSA;
 	} else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) {
 		return KEY_RSA_CERT_V00;
 	} else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) {
@@ -1020,12 +1222,33 @@
 		return KEY_RSA_CERT;
 	} else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) {
 		return KEY_DSA_CERT;
-	}
+	} else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0)
+		return KEY_ECDSA_CERT;
+
 	debug2("key_type_from_name: unknown key type '%s'", name);
 	return KEY_UNSPEC;
 }
 
 int
+key_ecdsa_nid_from_name(const char *name)
+{
+	if (strcmp(name, "ecdsa-sha2-nistp256") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0)
+		return NID_X9_62_prime256v1;
+	if (strcmp(name, "ecdsa-sha2-nistp384") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0)
+		return NID_secp384r1;
+	if (strcmp(name, "ecdsa-sha2-nistp521") == 0 ||
+	    strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0)
+		return NID_secp521r1;
+
+	debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name);
+	return -1;
+}
+
+int
 key_names_valid2(const char *names)
 {
 	char *s, *cp, *p;
@@ -1146,7 +1369,8 @@
 		goto out;
 	}
 	if (key->cert->signature_key->type != KEY_RSA &&
-	    key->cert->signature_key->type != KEY_DSA) {
+	    key->cert->signature_key->type != KEY_DSA &&
+	    key->cert->signature_key->type != KEY_ECDSA) {
 		error("%s: Invalid signature key type %s (%d)", __func__,
 		    key_type(key->cert->signature_key),
 		    key->cert->signature_key->type);
@@ -1186,9 +1410,10 @@
 key_from_blob(const u_char *blob, u_int blen)
 {
 	Buffer b;
-	int rlen, type;
-	char *ktype = NULL;
+	int rlen, type, nid = -1;
+	char *ktype = NULL, *curve = NULL;
 	Key *key = NULL;
+	EC_POINT *q = NULL;
 
 #ifdef DEBUG_PK
 	dump_base64(stderr, blob, blen);
@@ -1201,6 +1426,8 @@
 	}
 
 	type = key_type_from_name(ktype);
+	if (key_type_plain(type) == KEY_ECDSA)
+		nid = key_ecdsa_nid_from_name(ktype);
 
 	switch (type) {
 	case KEY_RSA_CERT:
@@ -1238,6 +1465,41 @@
 		DSA_print_fp(stderr, key->dsa, 8);
 #endif
 		break;
+	case KEY_ECDSA_CERT:
+		(void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
+		/* FALLTHROUGH */
+	case KEY_ECDSA:
+		key = key_new(type);
+		key->ecdsa_nid = nid;
+		if ((curve = buffer_get_string_ret(&b, NULL)) == NULL) {
+			error("key_from_blob: can't read ecdsa curve");
+			goto badkey;
+		}
+		if (key->ecdsa_nid != key_curve_name_to_nid(curve)) {
+			error("key_from_blob: ecdsa curve doesn't match type");
+			goto badkey;
+		}
+		if (key->ecdsa != NULL)
+			EC_KEY_free(key->ecdsa);
+		if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
+		    == NULL)
+			fatal("key_from_blob: EC_KEY_new_by_curve_name failed");
+		if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL)
+			fatal("key_from_blob: EC_POINT_new failed");
+		if (buffer_get_ecpoint_ret(&b, EC_KEY_get0_group(key->ecdsa),
+		    q) == -1) {
+			error("key_from_blob: can't read ecdsa key point");
+			goto badkey;
+		}
+		if (key_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
+		    q) != 0)
+			goto badkey;
+		if (EC_KEY_set_public_key(key->ecdsa, q) != 1)
+			fatal("key_from_blob: EC_KEY_set_public_key failed");
+#ifdef DEBUG_PK
+		key_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
+#endif
+		break;
 	case KEY_UNSPEC:
 		key = key_new(type);
 		break;
@@ -1255,6 +1517,10 @@
  out:
 	if (ktype != NULL)
 		xfree(ktype);
+	if (curve != NULL)
+		xfree(curve);
+	if (q != NULL)
+		EC_POINT_free(q);
 	buffer_free(&b);
 	return key;
 }
@@ -1274,6 +1540,7 @@
 	case KEY_DSA_CERT_V00:
 	case KEY_RSA_CERT_V00:
 	case KEY_DSA_CERT:
+	case KEY_ECDSA_CERT:
 	case KEY_RSA_CERT:
 		/* Use the existing blob */
 		buffer_append(&b, buffer_ptr(&key->cert->certblob),
@@ -1286,6 +1553,12 @@
 		buffer_put_bignum2(&b, key->dsa->g);
 		buffer_put_bignum2(&b, key->dsa->pub_key);
 		break;
+	case KEY_ECDSA:
+		buffer_put_cstring(&b, key_ssh_name(key));
+		buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid));
+		buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa),
+		    EC_KEY_get0_public_key(key->ecdsa));
+		break;
 	case KEY_RSA:
 		buffer_put_cstring(&b, key_ssh_name(key));
 		buffer_put_bignum2(&b, key->rsa->e);
@@ -1319,6 +1592,9 @@
 	case KEY_DSA_CERT:
 	case KEY_DSA:
 		return ssh_dss_sign(key, sigp, lenp, data, datalen);
+	case KEY_ECDSA_CERT:
+	case KEY_ECDSA:
+		return ssh_ecdsa_sign(key, sigp, lenp, data, datalen);
 	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_RSA:
@@ -1347,6 +1623,9 @@
 	case KEY_DSA_CERT:
 	case KEY_DSA:
 		return ssh_dss_verify(key, signature, signaturelen, data, datalen);
+	case KEY_ECDSA_CERT:
+	case KEY_ECDSA:
+		return ssh_ecdsa_verify(key, signature, signaturelen, data, datalen);
 	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_RSA:
@@ -1366,7 +1645,9 @@
 	pk = xcalloc(1, sizeof(*pk));
 	pk->type = k->type;
 	pk->flags = k->flags;
+	pk->ecdsa_nid = k->ecdsa_nid;
 	pk->dsa = NULL;
+	pk->ecdsa = NULL;
 	pk->rsa = NULL;
 
 	switch (k->type) {
@@ -1399,6 +1680,16 @@
 		if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL)
 			fatal("key_demote: BN_dup failed");
 		break;
+	case KEY_ECDSA_CERT:
+		key_cert_copy(k, pk);
+		/* FALLTHROUGH */
+	case KEY_ECDSA:
+		if ((pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid)) == NULL)
+			fatal("key_demote: EC_KEY_new_by_curve_name failed");
+		if (EC_KEY_set_public_key(pk->ecdsa,
+		    EC_KEY_get0_public_key(k->ecdsa)) != 1)
+			fatal("key_demote: EC_KEY_set_public_key failed");
+		break;
 	default:
 		fatal("key_free: bad key type %d", k->type);
 		break;
@@ -1417,6 +1708,7 @@
 	case KEY_DSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_DSA_CERT:
+	case KEY_ECDSA_CERT:
 		return 1;
 	default:
 		return 0;
@@ -1434,6 +1726,8 @@
 	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		return KEY_DSA;
+	case KEY_ECDSA_CERT:
+		return KEY_ECDSA;
 	default:
 		return type;
 	}
@@ -1452,6 +1746,10 @@
 		k->cert = cert_new();
 		k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT;
 		return 0;
+	case KEY_ECDSA:
+		k->cert = cert_new();
+		k->type = KEY_ECDSA_CERT;
+		return 0;
 	default:
 		error("%s: key has incorrect type %s", __func__, key_type(k));
 		return -1;
@@ -1473,13 +1771,20 @@
 		cert_free(k->cert);
 		k->type = KEY_DSA;
 		return 0;
+	case KEY_ECDSA_CERT:
+		cert_free(k->cert);
+		k->type = KEY_ECDSA;
+		return 0;
 	default:
 		error("%s: key has incorrect type %s", __func__, key_type(k));
 		return -1;
 	}
 }
 
-/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */
+/*
+ * Sign a KEY_RSA_CERT, KEY_DSA_CERT or KEY_ECDSA_CERT, (re-)generating
+ * the signed certblob
+ */
 int
 key_certify(Key *k, Key *ca)
 {
@@ -1498,7 +1803,8 @@
 		return -1;
 	}
 
-	if (ca->type != KEY_RSA && ca->type != KEY_DSA) {
+	if (ca->type != KEY_RSA && ca->type != KEY_DSA &&
+	    ca->type != KEY_ECDSA) {
 		error("%s: CA key has unsupported type %s", __func__,
 		    key_type(ca));
 		return -1;
@@ -1510,7 +1816,7 @@
 	buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));
 
 	/* -v01 certs put nonce first */
-	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) {
+	if (!key_cert_is_legacy(k)) {
 		arc4random_buf(&nonce, sizeof(nonce));
 		buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
 	}
@@ -1523,6 +1829,13 @@
 		buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
 		buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
 		break;
+	case KEY_ECDSA_CERT:
+		buffer_put_cstring(&k->cert->certblob,
+		    key_curve_nid_to_name(k->ecdsa_nid));
+		buffer_put_ecpoint(&k->cert->certblob,
+		    EC_KEY_get0_group(k->ecdsa),
+		    EC_KEY_get0_public_key(k->ecdsa));
+		break;
 	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
@@ -1536,7 +1849,7 @@
 	}
 
 	/* -v01 certs have a serial number next */
-	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT)
+	if (!key_cert_is_legacy(k))
 		buffer_put_int64(&k->cert->certblob, k->cert->serial);
 
 	buffer_put_int(&k->cert->certblob, k->cert->type);
@@ -1555,14 +1868,14 @@
 	    buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical));
 
 	/* -v01 certs have non-critical options here */
-	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) {
+	if (!key_cert_is_legacy(k)) {
 		buffer_put_string(&k->cert->certblob,
 		    buffer_ptr(&k->cert->extensions),
 		    buffer_len(&k->cert->extensions));
 	}
 
 	/* -v00 certs put the nonce at the end */
-	if (k->type == KEY_DSA_CERT_V00 || k->type == KEY_RSA_CERT_V00)
+	if (key_cert_is_legacy(k))
 		buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
 
 	buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
@@ -1647,3 +1960,201 @@
 		return 0;
 	}
 }
+
+int
+key_curve_name_to_nid(const char *name)
+{
+	if (strcmp(name, "nistp256") == 0)
+		return NID_X9_62_prime256v1;
+	else if (strcmp(name, "nistp384") == 0)
+		return NID_secp384r1;
+	else if (strcmp(name, "nistp521") == 0)
+		return NID_secp521r1;
+
+	debug("%s: unsupported EC curve name \"%.100s\"", __func__, name);
+	return -1;
+}
+
+const char *
+key_curve_nid_to_name(int nid)
+{
+	if (nid == NID_X9_62_prime256v1)
+		return "nistp256";
+	else if (nid == NID_secp384r1)
+		return "nistp384";
+	else if (nid == NID_secp521r1)
+		return "nistp521";
+
+	error("%s: unsupported EC curve nid %d", __func__, nid);
+	return NULL;
+}
+
+int
+key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
+{
+	BN_CTX *bnctx;
+	EC_POINT *nq = NULL;
+	BIGNUM *order, *x, *y, *tmp;
+	int ret = -1;
+
+	if ((bnctx = BN_CTX_new()) == NULL)
+		fatal("%s: BN_CTX_new failed", __func__);
+	BN_CTX_start(bnctx);
+
+	/*
+	 * We shouldn't ever hit this case because bignum_get_ecpoint()
+	 * refuses to load GF2m points.
+	 */
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+	    NID_X9_62_prime_field) {
+		error("%s: group is not a prime field", __func__);
+		goto out;
+	}
+
+	/* Q != infinity */
+	if (EC_POINT_is_at_infinity(group, public)) {
+		error("%s: received degenerate public key (infinity)",
+		    __func__);
+		goto out;
+	}
+
+	if ((x = BN_CTX_get(bnctx)) == NULL ||
+	    (y = BN_CTX_get(bnctx)) == NULL ||
+	    (order = BN_CTX_get(bnctx)) == NULL ||
+	    (tmp = BN_CTX_get(bnctx)) == NULL)
+		fatal("%s: BN_CTX_get failed", __func__);
+
+	/* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
+	if (EC_GROUP_get_order(group, order, bnctx) != 1)
+		fatal("%s: EC_GROUP_get_order failed", __func__);
+	if (EC_POINT_get_affine_coordinates_GFp(group, public,
+	    x, y, bnctx) != 1)
+		fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
+	if (BN_num_bits(x) <= BN_num_bits(order) / 2) {
+		error("%s: public key x coordinate too small: "
+		    "bits(x) = %d, bits(order)/2 = %d", __func__,
+		    BN_num_bits(x), BN_num_bits(order) / 2);
+		goto out;
+	}
+	if (BN_num_bits(y) <= BN_num_bits(order) / 2) {
+		error("%s: public key y coordinate too small: "
+		    "bits(y) = %d, bits(order)/2 = %d", __func__,
+		    BN_num_bits(x), BN_num_bits(order) / 2);
+		goto out;
+	}
+
+	/* nQ == infinity (n == order of subgroup) */
+	if ((nq = EC_POINT_new(group)) == NULL)
+		fatal("%s: BN_CTX_tmp failed", __func__);
+	if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1)
+		fatal("%s: EC_GROUP_mul failed", __func__);
+	if (EC_POINT_is_at_infinity(group, nq) != 1) {
+		error("%s: received degenerate public key (nQ != infinity)",
+		    __func__);
+		goto out;
+	}
+
+	/* x < order - 1, y < order - 1 */
+	if (!BN_sub(tmp, order, BN_value_one()))
+		fatal("%s: BN_sub failed", __func__);
+	if (BN_cmp(x, tmp) >= 0) {
+		error("%s: public key x coordinate >= group order - 1",
+		    __func__);
+		goto out;
+	}
+	if (BN_cmp(y, tmp) >= 0) {
+		error("%s: public key y coordinate >= group order - 1",
+		    __func__);
+		goto out;
+	}
+	ret = 0;
+ out:
+	BN_CTX_free(bnctx);
+	EC_POINT_free(nq);
+	return ret;
+}
+
+int
+key_ec_validate_private(const EC_KEY *key)
+{
+	BN_CTX *bnctx;
+	BIGNUM *order, *tmp;
+	int ret = -1;
+
+	if ((bnctx = BN_CTX_new()) == NULL)
+		fatal("%s: BN_CTX_new failed", __func__);
+	BN_CTX_start(bnctx);
+
+	if ((order = BN_CTX_get(bnctx)) == NULL ||
+	    (tmp = BN_CTX_get(bnctx)) == NULL)
+		fatal("%s: BN_CTX_get failed", __func__);
+
+	/* log2(private) > log2(order)/2 */
+	if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1)
+		fatal("%s: EC_GROUP_get_order failed", __func__);
+	if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
+	    BN_num_bits(order) / 2) {
+		error("%s: private key too small: "
+		    "bits(y) = %d, bits(order)/2 = %d", __func__,
+		    BN_num_bits(EC_KEY_get0_private_key(key)),
+		    BN_num_bits(order) / 2);
+		goto out;
+	}
+
+	/* private < order - 1 */
+	if (!BN_sub(tmp, order, BN_value_one()))
+		fatal("%s: BN_sub failed", __func__);
+	if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) {
+		error("%s: private key >= group order - 1", __func__);
+		goto out;
+	}
+	ret = 0;
+ out:
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK)
+void
+key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
+{
+	BIGNUM *x, *y;
+	BN_CTX *bnctx;
+
+	if (point == NULL) {
+		fputs("point=(NULL)\n", stderr);
+		return;
+	}
+	if ((bnctx = BN_CTX_new()) == NULL)
+		fatal("%s: BN_CTX_new failed", __func__);
+	BN_CTX_start(bnctx);
+	if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL)
+		fatal("%s: BN_CTX_get failed", __func__);
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+	    NID_X9_62_prime_field)
+		fatal("%s: group is not a prime field", __func__);
+	if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) != 1)
+		fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
+	fputs("x=", stderr);
+	BN_print_fp(stderr, x);
+	fputs("\ny=", stderr);
+	BN_print_fp(stderr, y);
+	fputs("\n", stderr);
+	BN_CTX_free(bnctx);
+}
+
+void
+key_dump_ec_key(const EC_KEY *key)
+{
+	const BIGNUM *exponent;
+
+	key_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key));
+	fputs("exponent=", stderr);
+	if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
+		fputs("(NULL)", stderr);
+	else
+		BN_print_fp(stderr, EC_KEY_get0_private_key(key));
+	fputs("\n", stderr);
+}
+#endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */
+
diff --git a/key.h b/key.h
index 11d30ea..2eb1243 100644
--- a/key.h
+++ b/key.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.h,v 1.30 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: key.h,v 1.31 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -29,14 +29,17 @@
 #include "buffer.h"
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
+#include <openssl/ec.h>
 
 typedef struct Key Key;
 enum types {
 	KEY_RSA1,
 	KEY_RSA,
 	KEY_DSA,
+	KEY_ECDSA,
 	KEY_RSA_CERT,
 	KEY_DSA_CERT,
+	KEY_ECDSA_CERT,
 	KEY_RSA_CERT_V00,
 	KEY_DSA_CERT_V00,
 	KEY_UNSPEC
@@ -73,6 +76,8 @@
 	int	 flags;
 	RSA	*rsa;
 	DSA	*dsa;
+	int	 ecdsa_nid;	/* NID of curve */
+	EC_KEY	*ecdsa;
 	struct KeyCert *cert;
 };
 
@@ -104,9 +109,18 @@
 	    const char **);
 int	 key_cert_is_legacy(Key *);
 
+int		 key_ecdsa_nid_from_name(const char *);
+int		 key_curve_name_to_nid(const char *);
+const char *	 key_curve_nid_to_name(int);
+int		 key_ecdsa_bits_to_nid(int);
+int		 key_ecdsa_group_to_nid(const EC_GROUP *);
+int		 key_ec_validate_public(const EC_GROUP *, const EC_POINT *);
+int		 key_ec_validate_private(const EC_KEY *);
+
 Key		*key_from_blob(const u_char *, u_int);
 int		 key_to_blob(const Key *, u_char **, u_int *);
 const char	*key_ssh_name(const Key *);
+const char	*key_ssh_name_plain(const Key *);
 int		 key_names_valid2(const char *);
 
 int	 key_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
@@ -114,7 +128,14 @@
 
 int	 ssh_dss_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
 int	 ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
+int	 ssh_ecdsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
+int	 ssh_ecdsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 int	 ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
 int	 ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 
+#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK)
+void	key_dump_ec_point(const EC_GROUP *, const EC_POINT *);
+void	key_dump_ec_key(const EC_KEY *);
+#endif
+
 #endif
diff --git a/monitor.c b/monitor.c
index 9eb4e35..32395ee 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.108 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.109 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -1691,6 +1691,7 @@
 	kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
 	kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
 	kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+	kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
 	kex->server = 1;
 	kex->hostkey_type = buffer_get_int(m);
 	kex->kex_type = buffer_get_int(m);
diff --git a/monitor_wrap.c b/monitor_wrap.c
index faeb02c..1a5dda5 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.69 2010/03/07 11:57:13 dtucker Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.70 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -73,6 +73,7 @@
 #include "misc.h"
 #include "schnorr.h"
 #include "jpake.h"
+#include "uuencode.h"
 
 #include "channels.h"
 #include "session.h"
diff --git a/myproposal.h b/myproposal.h
index 7bedfab..71f90ee 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: myproposal.h,v 1.25 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: myproposal.h,v 1.26 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -32,20 +32,38 @@
 	"diffie-hellman-group-exchange-sha1," \
 	"diffie-hellman-group14-sha1," \
 	"diffie-hellman-group1-sha1"
+
+#define	KEX_DEFAULT_PK_ALG	\
+	"ssh-rsa-cert-v01@openssh.com," \
+	"ssh-dss-cert-v01@openssh.com," \
+	"ssh-rsa-cert-v00@openssh.com," \
+	"ssh-dss-cert-v00@openssh.com," \
+	"ssh-rsa," \
+	"ssh-dss"
 #else
 # define KEX_DEFAULT_KEX		\
+	"ecdh-sha2-nistp521," \
+	"ecdh-sha2-nistp256," \
+	"ecdh-sha2-nistp384," \
 	"diffie-hellman-group-exchange-sha256," \
 	"diffie-hellman-group-exchange-sha1," \
 	"diffie-hellman-group14-sha1," \
 	"diffie-hellman-group1-sha1"
-#endif
 
 #define	KEX_DEFAULT_PK_ALG	\
-				"ssh-rsa-cert-v01@openssh.com," \
-				"ssh-dss-cert-v01@openssh.com," \
-				"ssh-rsa-cert-v00@openssh.com," \
- 				"ssh-dss-cert-v00@openssh.com," \
- 				"ssh-rsa,ssh-dss"
+	"ecdsa-sha2-nistp256-cert-v01@openssh.com," \
+	"ecdsa-sha2-nistp384-cert-v01@openssh.com," \
+	"ecdsa-sha2-nistp521-cert-v01@openssh.com," \
+	"ssh-rsa-cert-v01@openssh.com," \
+	"ssh-dss-cert-v01@openssh.com," \
+	"ssh-rsa-cert-v00@openssh.com," \
+	"ssh-dss-cert-v00@openssh.com," \
+	"ecdsa-sha2-nistp256," \
+	"ecdsa-sha2-nistp384," \
+	"ecdsa-sha2-nistp521," \
+	"ssh-rsa," \
+	"ssh-dss"
+#endif
 
 #define	KEX_DEFAULT_ENCRYPT \
 	"aes128-ctr,aes192-ctr,aes256-ctr," \
diff --git a/packet.c b/packet.c
index 49aa973..a06c5e3 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.169 2010/08/31 09:58:37 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.170 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -641,6 +641,12 @@
 	buffer_put_bignum2(&active_state->outgoing_packet, value);
 }
 
+void
+packet_put_ecpoint(const EC_GROUP *curve, const EC_POINT *point)
+{
+	buffer_put_ecpoint(&active_state->outgoing_packet, curve, point);
+}
+
 /*
  * Finalizes and sends the packet.  If the encryption key has been set,
  * encrypts the packet before sending.
@@ -1511,6 +1517,12 @@
 	buffer_get_bignum2(&active_state->incoming_packet, value);
 }
 
+void
+packet_get_ecpoint(const EC_GROUP *curve, EC_POINT *point)
+{
+	buffer_get_ecpoint(&active_state->incoming_packet, curve, point);
+}
+
 void *
 packet_get_raw(u_int *length_ptr)
 {
diff --git a/packet.h b/packet.h
index fd0b056..827561c 100644
--- a/packet.h
+++ b/packet.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.h,v 1.53 2010/08/31 09:58:37 djm Exp $ */
+/* $OpenBSD: packet.h,v 1.54 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -19,6 +19,7 @@
 #include <termios.h>
 
 #include <openssl/bn.h>
+#include <openssl/ec.h>
 
 void     packet_set_connection(int, int);
 void     packet_set_timeout(int, int);
@@ -42,6 +43,7 @@
 void     packet_put_int64(u_int64_t value);
 void     packet_put_bignum(BIGNUM * value);
 void     packet_put_bignum2(BIGNUM * value);
+void     packet_put_ecpoint(const EC_GROUP *, const EC_POINT *);
 void     packet_put_string(const void *buf, u_int len);
 void     packet_put_cstring(const char *str);
 void     packet_put_raw(const void *buf, u_int len);
@@ -59,6 +61,7 @@
 u_int64_t packet_get_int64(void);
 void     packet_get_bignum(BIGNUM * value);
 void     packet_get_bignum2(BIGNUM * value);
+void	 packet_get_ecpoint(const EC_GROUP *, EC_POINT *);
 void	*packet_get_raw(u_int *length_ptr);
 void	*packet_get_string(u_int *length_ptr);
 char	*packet_get_cstring(u_int *length_ptr);
diff --git a/pathnames.h b/pathnames.h
index 9e50950..e2dd49a 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pathnames.h,v 1.19 2010/02/11 20:37:47 djm Exp $ */
+/* $OpenBSD: pathnames.h,v 1.20 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -38,6 +38,7 @@
 #define _PATH_HOST_CONFIG_FILE		SSHDIR "/ssh_config"
 #define _PATH_HOST_KEY_FILE		SSHDIR "/ssh_host_key"
 #define _PATH_HOST_DSA_KEY_FILE		SSHDIR "/ssh_host_dsa_key"
+#define _PATH_HOST_ECDSA_KEY_FILE	SSHDIR "/ssh_host_ecdsa_key"
 #define _PATH_HOST_RSA_KEY_FILE		SSHDIR "/ssh_host_rsa_key"
 #define _PATH_DH_MODULI			SSHDIR "/moduli"
 /* Backwards compatibility */
@@ -74,6 +75,7 @@
  */
 #define _PATH_SSH_CLIENT_IDENTITY	".ssh/identity"
 #define _PATH_SSH_CLIENT_ID_DSA		".ssh/id_dsa"
+#define _PATH_SSH_CLIENT_ID_ECDSA	".ssh/id_ecdsa"
 #define _PATH_SSH_CLIENT_ID_RSA		".ssh/id_rsa"
 
 /*
diff --git a/readconf.c b/readconf.c
index 0296590..98ce301 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.187 2010/07/19 09:15:12 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.188 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1214,6 +1214,12 @@
 			    xmalloc(len);
 			snprintf(options->identity_files[options->num_identity_files++],
 			    len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA);
+
+			len = 2 + strlen(_PATH_SSH_CLIENT_ID_ECDSA) + 1;
+			options->identity_files[options->num_identity_files] =
+			    xmalloc(len);
+			snprintf(options->identity_files[options->num_identity_files++],
+			    len, "~/%.100s", _PATH_SSH_CLIENT_ID_ECDSA);
 		}
 	}
 	if (options->escape_char == -1)
diff --git a/ssh-add.1 b/ssh-add.1
index d7cc531..3699db5 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: ssh-add.1,v 1.52 2010/03/05 10:28:21 djm Exp $
+.\"	$OpenBSD: ssh-add.1,v 1.53 2010/08/31 11:54:45 djm Exp $
 .\"
 .\"  -*- nroff -*-
 .\"
@@ -37,12 +37,12 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: March 5 2010 $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH-ADD 1
 .Os
 .Sh NAME
 .Nm ssh-add
-.Nd adds RSA or DSA identities to the authentication agent
+.Nd adds private key identities to the authentication agent
 .Sh SYNOPSIS
 .Nm ssh-add
 .Op Fl cDdLlXx
@@ -54,11 +54,12 @@
 .Fl e Ar pkcs11
 .Sh DESCRIPTION
 .Nm
-adds RSA or DSA identities to the authentication agent,
+adds private key identities to the authentication agent,
 .Xr ssh-agent 1 .
 When run without arguments, it adds the files
 .Pa ~/.ssh/id_rsa ,
-.Pa ~/.ssh/id_dsa
+.Pa ~/.ssh/id_dsa ,
+.Pa ~/.ssh/id_ecdsa
 and
 .Pa ~/.ssh/identity .
 After loading a private key,
@@ -165,6 +166,8 @@
 Contains the protocol version 1 RSA authentication identity of the user.
 .It Pa ~/.ssh/id_dsa
 Contains the protocol version 2 DSA authentication identity of the user.
+.It Pa ~/.ssh/id_ecdsa
+Contains the protocol version 2 ECDSA authentication identity of the user.
 .It Pa ~/.ssh/id_rsa
 Contains the protocol version 2 RSA authentication identity of the user.
 .El
diff --git a/ssh-add.c b/ssh-add.c
index 7f8fb2c..31e6183 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-add.c,v 1.98 2010/08/16 04:06:06 djm Exp $ */
+/* $OpenBSD: ssh-add.c,v 1.99 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -70,6 +70,7 @@
 static char *default_files[] = {
 	_PATH_SSH_CLIENT_ID_RSA,
 	_PATH_SSH_CLIENT_ID_DSA,
+	_PATH_SSH_CLIENT_ID_ECDSA,
 	_PATH_SSH_CLIENT_IDENTITY,
 	NULL
 };
diff --git a/ssh-agent.1 b/ssh-agent.1
index f65e8e6..88ad490 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ssh-agent.1,v 1.50 2010/01/17 21:49:09 tedu Exp $
+.\" $OpenBSD: ssh-agent.1,v 1.51 2010/08/31 11:54:45 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: January 17 2010 $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH-AGENT 1
 .Os
 .Sh NAME
@@ -53,7 +53,7 @@
 .Sh DESCRIPTION
 .Nm
 is a program to hold private keys used for public key authentication
-(RSA, DSA).
+(RSA, DSA, ECDSA).
 The idea is that
 .Nm
 is started in the beginning of an X-session or a login session, and
@@ -114,7 +114,8 @@
 .Xr ssh-add 1
 adds the files
 .Pa ~/.ssh/id_rsa ,
-.Pa ~/.ssh/id_dsa
+.Pa ~/.ssh/id_dsa ,
+.Pa ~/.ssh/id_ecdsa
 and
 .Pa ~/.ssh/identity .
 If the identity has a passphrase,
@@ -187,6 +188,8 @@
 Contains the protocol version 1 RSA authentication identity of the user.
 .It Pa ~/.ssh/id_dsa
 Contains the protocol version 2 DSA authentication identity of the user.
+.It Pa ~/.ssh/id_ecdsa
+Contains the protocol version 2 ECDSA authentication identity of the user.
 .It Pa ~/.ssh/id_rsa
 Contains the protocol version 2 RSA authentication identity of the user.
 .It Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt
diff --git a/ssh-agent.c b/ssh-agent.c
index e6725ea..fbfd79c 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.168 2010/08/16 04:06:06 djm Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.169 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -466,8 +466,10 @@
 	Idtab *tab = idtab_lookup(version);
 	Identity *id;
 	int type, success = 0, death = 0, confirm = 0;
-	char *type_name, *comment;
+	char *type_name, *comment, *curve;
 	Key *k = NULL;
+	BIGNUM *exponent;
+	EC_POINT *q;
 	u_char *cert;
 	u_int len;
 
@@ -490,7 +492,6 @@
 	case 2:
 		type_name = buffer_get_string(&e->request, NULL);
 		type = key_type_from_name(type_name);
-		xfree(type_name);
 		switch (type) {
 		case KEY_DSA:
 			k = key_new_private(type);
@@ -509,6 +510,57 @@
 			key_add_private(k);
 			buffer_get_bignum2(&e->request, k->dsa->priv_key);
 			break;
+		case KEY_ECDSA:
+			k = key_new_private(type);
+			k->ecdsa_nid = key_ecdsa_nid_from_name(type_name);
+			curve = buffer_get_string(&e->request, NULL);
+			if (k->ecdsa_nid != key_curve_name_to_nid(curve))
+				fatal("%s: curve names mismatch", __func__);
+			xfree(curve);
+			k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
+			if (k->ecdsa == NULL)
+				fatal("%s: EC_KEY_new_by_curve_name failed",
+				    __func__);
+			q = EC_POINT_new(EC_KEY_get0_group(k->ecdsa));
+			if (q == NULL)
+				fatal("%s: BN_new failed", __func__);
+			if ((exponent = BN_new()) == NULL)
+				fatal("%s: BN_new failed", __func__);
+			buffer_get_ecpoint(&e->request,
+				EC_KEY_get0_group(k->ecdsa), q);
+			buffer_get_bignum2(&e->request, exponent);
+			if (EC_KEY_set_public_key(k->ecdsa, q) != 1)
+				fatal("%s: EC_KEY_set_public_key failed",
+				    __func__);
+			if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1)
+				fatal("%s: EC_KEY_set_private_key failed",
+				    __func__);
+			if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
+			    EC_KEY_get0_public_key(k->ecdsa)) != 0)
+				fatal("%s: bad ECDSA public key", __func__);
+			if (key_ec_validate_private(k->ecdsa) != 0)
+				fatal("%s: bad ECDSA private key", __func__);
+			BN_clear_free(exponent);
+			EC_POINT_free(q);
+			break;
+		case KEY_ECDSA_CERT:
+			cert = buffer_get_string(&e->request, &len);
+			if ((k = key_from_blob(cert, len)) == NULL)
+				fatal("Certificate parse failed");
+			xfree(cert);
+			key_add_private(k);
+			if ((exponent = BN_new()) == NULL)
+				fatal("%s: BN_new failed", __func__);
+			buffer_get_bignum2(&e->request, exponent);
+			if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1)
+				fatal("%s: EC_KEY_set_private_key failed",
+				    __func__);
+			if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
+			    EC_KEY_get0_public_key(k->ecdsa)) != 0 ||
+			    key_ec_validate_private(k->ecdsa) != 0)
+				fatal("%s: bad ECDSA key", __func__);
+			BN_clear_free(exponent);
+			break;
 		case KEY_RSA:
 			k = key_new_private(type);
 			buffer_get_bignum2(&e->request, k->rsa->n);
@@ -534,9 +586,11 @@
 			buffer_get_bignum2(&e->request, k->rsa->q);
 			break;
 		default:
+			xfree(type_name);
 			buffer_clear(&e->request);
 			goto send;
 		}
+		xfree(type_name);
 		break;
 	}
 	/* enable blinding */
diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c
new file mode 100644
index 0000000..a1c1bdb
--- /dev/null
+++ b/ssh-ecdsa.c
@@ -0,0 +1,160 @@
+/* $OpenBSD */
+/*
+ * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+
+#include <string.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "compat.h"
+#include "log.h"
+#include "key.h"
+
+int
+ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp,
+    const u_char *data, u_int datalen)
+{
+	ECDSA_SIG *sig;
+	const EVP_MD *evp_md = EVP_sha256();
+	EVP_MD_CTX md;
+	u_char digest[EVP_MAX_MD_SIZE];
+	u_int len, dlen;
+	Buffer b, bb;
+
+	if (key == NULL || key->ecdsa == NULL ||
+	    (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) {
+		error("%s: no ECDSA key", __func__);
+		return -1;
+	}
+	EVP_DigestInit(&md, evp_md);
+	EVP_DigestUpdate(&md, data, datalen);
+	EVP_DigestFinal(&md, digest, &dlen);
+
+	sig = ECDSA_do_sign(digest, dlen, key->ecdsa);
+	memset(digest, 'd', sizeof(digest));
+
+	if (sig == NULL) {
+		error("%s: sign failed", __func__);
+		return -1;
+	}
+
+	buffer_init(&bb);
+	buffer_put_bignum2(&bb, sig->r);
+	buffer_put_bignum2(&bb, sig->s);
+	ECDSA_SIG_free(sig);
+
+	buffer_init(&b);
+	buffer_put_cstring(&b, key_ssh_name_plain(key));
+	buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb));
+	buffer_free(&bb);
+	len = buffer_len(&b);
+	if (lenp != NULL)
+		*lenp = len;
+	if (sigp != NULL) {
+		*sigp = xmalloc(len);
+		memcpy(*sigp, buffer_ptr(&b), len);
+	}
+	buffer_free(&b);
+
+	return 0;
+}
+int
+ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
+    const u_char *data, u_int datalen)
+{
+	ECDSA_SIG *sig;
+	const EVP_MD *evp_md = EVP_sha256();
+	EVP_MD_CTX md;
+	u_char digest[EVP_MAX_MD_SIZE], *sigblob;
+	u_int len, dlen;
+	int rlen, ret;
+	Buffer b, bb;
+
+	if (key == NULL || key->ecdsa == NULL ||
+	    (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) {
+		error("%s: no ECDSA key", __func__);
+		return -1;
+	}
+
+	/* fetch signature */
+	char *ktype;
+	buffer_init(&b);
+	buffer_append(&b, signature, signaturelen);
+	ktype = buffer_get_string(&b, NULL);
+	if (strcmp(key_ssh_name_plain(key), ktype) != 0) {
+		error("%s: cannot handle type %s", __func__, ktype);
+		buffer_free(&b);
+		xfree(ktype);
+		return -1;
+	}
+	xfree(ktype);
+	sigblob = buffer_get_string(&b, &len);
+	rlen = buffer_len(&b);
+	buffer_free(&b);
+	if (rlen != 0) {
+		error("%s: remaining bytes in signature %d", __func__, rlen);
+		xfree(sigblob);
+		return -1;
+	}
+
+	/* parse signature */
+	if ((sig = ECDSA_SIG_new()) == NULL)
+		fatal("%s: ECDSA_SIG_new failed", __func__);
+	if ((sig->r = BN_new()) == NULL ||
+	    (sig->s = BN_new()) == NULL)
+		fatal("%s: BN_new failed", __func__);
+
+	buffer_init(&bb);
+	buffer_append(&bb, sigblob, len);
+	buffer_get_bignum2(&bb, sig->r);
+	buffer_get_bignum2(&bb, sig->s);
+	if (buffer_len(&bb) != 0)
+		fatal("%s: remaining bytes in inner sigblob", __func__);
+
+	/* clean up */
+	memset(sigblob, 0, len);
+	xfree(sigblob);
+
+	/* hash the data */
+	EVP_DigestInit(&md, evp_md);
+	EVP_DigestUpdate(&md, data, datalen);
+	EVP_DigestFinal(&md, digest, &dlen);
+
+	ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa);
+	memset(digest, 'd', sizeof(digest));
+
+	ECDSA_SIG_free(sig);
+
+	debug("%s: signature %s", __func__,
+	    ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error");
+	return ret;
+}
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index 9acd8f8..4b95a4e 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: ssh-keygen.1,v 1.98 2010/08/04 06:07:11 djm Exp $
+.\"	$OpenBSD: ssh-keygen.1,v 1.99 2010/08/31 11:54:45 djm Exp $
 .\"
 .\"  -*- nroff -*-
 .\"
@@ -37,7 +37,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: August 4 2010 $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH-KEYGEN 1
 .Os
 .Sh NAME
@@ -125,7 +125,7 @@
 generates, manages and converts authentication keys for
 .Xr ssh 1 .
 .Nm
-can create RSA keys for use by SSH protocol version 1 and RSA or DSA
+can create RSA keys for use by SSH protocol version 1 and RSA, DSA or ECDSA
 keys for use by SSH protocol version 2.
 The type of key to be generated is specified with the
 .Fl t
@@ -142,9 +142,10 @@
 section for details.
 .Pp
 Normally each user wishing to use SSH
-with RSA or DSA authentication runs this once to create the authentication
+with public key authentication runs this once to create the authentication
 key in
 .Pa ~/.ssh/identity ,
+.Pa ~/.ssh/id_ecdsa ,
 .Pa ~/.ssh/id_dsa
 or
 .Pa ~/.ssh/id_rsa .
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 93f5980..4485851 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.199 2010/08/16 04:06:06 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.200 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -57,6 +57,7 @@
 /* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
 #define DEFAULT_BITS		2048
 #define DEFAULT_BITS_DSA	1024
+#define DEFAULT_BITS_ECDSA	521
 u_int32_t bits = 0;
 
 /*
@@ -176,6 +177,10 @@
 		case KEY_DSA:
 			name = _PATH_SSH_CLIENT_ID_DSA;
 			break;
+		case KEY_ECDSA_CERT:
+		case KEY_ECDSA:
+			name = _PATH_SSH_CLIENT_ID_ECDSA;
+			break;
 		case KEY_RSA_CERT:
 		case KEY_RSA_CERT_V00:
 		case KEY_RSA:
@@ -260,6 +265,10 @@
 		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
 			fatal("PEM_write_DSA_PUBKEY failed");
 		break;
+	case KEY_ECDSA:
+		if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
+			fatal("PEM_write_EC_PUBKEY failed");
+		break;
 	default:
 		fatal("%s: unsupported key type %s", __func__, key_type(k));
 	}
@@ -280,6 +289,7 @@
 			fatal("PEM_write_DSAPublicKey failed");
 		break;
 #endif
+	/* XXX ECDSA? */
 	default:
 		fatal("%s: unsupported key type %s", __func__, key_type(k));
 	}
@@ -539,6 +549,13 @@
 		(*k)->type = KEY_DSA;
 		(*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
 		break;
+	case EVP_PKEY_EC:
+		*k = key_new(KEY_UNSPEC);
+		(*k)->type = KEY_ECDSA;
+		(*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
+		(*k)->ecdsa_nid = key_ecdsa_group_to_nid(
+		    EC_KEY_get0_group((*k)->ecdsa));
+		break;
 	default:
 		fatal("%s: unsupported pubkey type %d", __func__,
 		    EVP_PKEY_type(pubkey->type));
@@ -574,6 +591,7 @@
 		fclose(fp);
 		return;
 	}
+	/* XXX ECDSA */
 #endif
 	fatal("%s: unrecognised raw private key format", __func__);
 }
@@ -614,6 +632,10 @@
 			ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
 			    NULL, 0, NULL, NULL);
 			break;
+		case KEY_ECDSA:
+			ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
+			    NULL, 0, NULL, NULL);
+			break;
 		case KEY_RSA:
 			ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
 			    NULL, 0, NULL, NULL);
@@ -1404,7 +1426,8 @@
 		tmp = tilde_expand_filename(argv[i], pw->pw_uid);
 		if ((public = key_load_public(tmp, &comment)) == NULL)
 			fatal("%s: unable to open \"%s\"", __func__, tmp);
-		if (public->type != KEY_RSA && public->type != KEY_DSA)
+		if (public->type != KEY_RSA && public->type != KEY_DSA &&
+		    public->type != KEY_ECDSA)
 			fatal("%s: key \"%s\" type %s cannot be certified",
 			    __func__, tmp, key_type(public));
 
@@ -2086,8 +2109,14 @@
 		fprintf(stderr, "unknown key type %s\n", key_type_name);
 		exit(1);
 	}
-	if (bits == 0)
-		bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS;
+	if (bits == 0) {
+		if (type == KEY_DSA)
+			bits = DEFAULT_BITS_DSA;
+		else if (type == KEY_ECDSA)
+			bits = DEFAULT_BITS_ECDSA;
+		else
+			bits = DEFAULT_BITS;
+	}
 	maxbits = (type == KEY_DSA) ?
 	    OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS;
 	if (bits > maxbits) {
@@ -2096,6 +2125,9 @@
 	}
 	if (type == KEY_DSA && bits != 1024)
 		fatal("DSA keys must be 1024 bits");
+	else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(bits) == -1)
+		fatal("Invalid ECDSA key length - valid lengths are "
+		    "256, 384 or 521 bits");
 	if (!quiet)
 		printf("Generating public/private %s key pair.\n", key_type_name);
 	private = key_generate(type, bits);
diff --git a/ssh-keyscan.1 b/ssh-keyscan.1
index 78255ff..fe9bb6e 100644
--- a/ssh-keyscan.1
+++ b/ssh-keyscan.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: ssh-keyscan.1,v 1.28 2010/01/09 23:04:13 dtucker Exp $
+.\"	$OpenBSD: ssh-keyscan.1,v 1.29 2010/08/31 11:54:45 djm Exp $
 .\"
 .\" Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
 .\"
@@ -6,7 +6,7 @@
 .\" permitted provided that due credit is given to the author and the
 .\" OpenBSD project by leaving this copyright notice intact.
 .\"
-.Dd $Mdocdate: January 9 2010 $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH-KEYSCAN 1
 .Os
 .Sh NAME
@@ -88,9 +88,10 @@
 The possible values are
 .Dq rsa1
 for protocol version 1 and
-.Dq rsa
+.Dq dsa ,
+.Dq ecdsa
 or
-.Dq dsa
+.Dq rsa
 for protocol version 2.
 Multiple values may be specified by separating them with commas.
 The default is
@@ -122,7 +123,7 @@
 host-or-namelist bits exponent modulus
 .Ed
 .Pp
-.Pa Output format for rsa and dsa keys:
+.Pa Output format for rsa, dsa and ecdsa keys:
 .Bd -literal
 host-or-namelist keytype base64-encoded-key
 .Ed
@@ -130,9 +131,12 @@
 Where
 .Pa keytype
 is either
-.Dq ssh-rsa
+.Dq ecdsa-sha2-nistp256 ,
+.Dq ecdsa-sha2-nistp384 ,
+.Dq ecdsa-sha2-nistp521 ,
+.Dq ssh-dss
 or
-.Dq ssh-dss .
+.Dq ssh-rsa .
 .Pp
 .Pa /etc/ssh/ssh_known_hosts
 .Sh EXAMPLES
@@ -149,7 +153,7 @@
 which have new or different keys from those in the sorted file
 .Pa ssh_known_hosts :
 .Bd -literal
-$ ssh-keyscan -t rsa,dsa -f ssh_hosts | \e
+$ ssh-keyscan -t rsa,dsa,ecdsa -f ssh_hosts | \e
 	sort -u - ssh_known_hosts | diff ssh_known_hosts -
 .Ed
 .Sh SEE ALSO
diff --git a/ssh-keyscan.c b/ssh-keyscan.c
index b6cf427..3fb1214 100644
--- a/ssh-keyscan.c
+++ b/ssh-keyscan.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keyscan.c,v 1.82 2010/06/22 04:54:30 djm Exp $ */
+/* $OpenBSD: ssh-keyscan.c,v 1.83 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
  *
@@ -52,9 +52,10 @@
 
 int ssh_port = SSH_DEFAULT_PORT;
 
-#define KT_RSA1	1
-#define KT_DSA	2
-#define KT_RSA	4
+#define KT_RSA1		1
+#define KT_DSA		2
+#define KT_RSA		4
+#define KT_ECDSA	8
 
 int get_keytypes = KT_RSA;	/* Get only RSA keys by default */
 
@@ -251,6 +252,7 @@
 	c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
 	c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
 	c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
+	c->c_kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
 	c->c_kex->verify_host_key = hostjump;
 
 	if (!(j = setjmp(kexjmp))) {
@@ -673,6 +675,9 @@
 				case KEY_DSA:
 					get_keytypes |= KT_DSA;
 					break;
+				case KEY_ECDSA:
+					get_keytypes |= KT_ECDSA;
+					break;
 				case KEY_RSA:
 					get_keytypes |= KT_RSA;
 					break;
diff --git a/ssh-keysign.8 b/ssh-keysign.8
index 2e47f12..5e09e02 100644
--- a/ssh-keysign.8
+++ b/ssh-keysign.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ssh-keysign.8,v 1.11 2010/08/08 19:36:30 jmc Exp $
+.\" $OpenBSD: ssh-keysign.8,v 1.12 2010/08/31 11:54:45 djm Exp $
 .\"
 .\" Copyright (c) 2002 Markus Friedl.  All rights reserved.
 .\"
@@ -22,7 +22,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: August 8 2010 $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH-KEYSIGN 8
 .Os
 .Sh NAME
@@ -62,6 +62,7 @@
 is enabled.
 .Pp
 .It Pa /etc/ssh/ssh_host_dsa_key
+.It Pa /etc/ssh/ssh_host_ecdsa_key
 .It Pa /etc/ssh/ssh_host_rsa_key
 These files contain the private parts of the host keys used to
 generate the digital signature.
@@ -72,6 +73,7 @@
 must be set-uid root if host-based authentication is used.
 .Pp
 .It Pa /etc/ssh/ssh_host_dsa_key-cert.pub
+.It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub
 .It Pa /etc/ssh/ssh_host_rsa_key-cert.pub
 If these files exist they are assumed to contain public certificate
 information corresponding with the private keys above.
diff --git a/ssh.1 b/ssh.1
index 9b134f4..a3d0011 100644
--- a/ssh.1
+++ b/ssh.1
@@ -34,8 +34,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh.1,v 1.309 2010/08/08 19:36:30 jmc Exp $
-.Dd $Mdocdate: August 8 2010 $
+.\" $OpenBSD: ssh.1,v 1.310 2010/08/31 11:54:45 djm Exp $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH 1
 .Os
 .Sh NAME
@@ -269,13 +269,14 @@
 private RSA key.
 .It Fl i Ar identity_file
 Selects a file from which the identity (private key) for
-RSA or DSA authentication is read.
+public key authentication is read.
 The default is
 .Pa ~/.ssh/identity
 for protocol version 1, and
-.Pa ~/.ssh/id_rsa
+.Pa ~/.ssh/id_dsa ,
+.Pa ~/.ssh/id_ecdsa
 and
-.Pa ~/.ssh/id_dsa
+.Pa ~/.ssh/id_rsa
 for protocol version 2.
 Identity files may also be specified on
 a per-host basis in the configuration file.
@@ -721,9 +722,9 @@
 The server knows the public key, and only the user knows the private key.
 .Nm
 implements public key authentication protocol automatically,
-using either the RSA or DSA algorithms.
+using one of the DSA, ECDSA or RSA algorithms.
 Protocol 1 is restricted to using only RSA keys,
-but protocol 2 may use either.
+but protocol 2 may use any.
 The
 .Sx HISTORY
 section of
@@ -748,6 +749,8 @@
 (protocol 1),
 .Pa ~/.ssh/id_dsa
 (protocol 2 DSA),
+.Pa ~/.ssh/id_ecdsa
+(protocol 2 ECDSA),
 or
 .Pa ~/.ssh/id_rsa
 (protocol 2 RSA)
@@ -756,6 +759,8 @@
 (protocol 1),
 .Pa ~/.ssh/id_dsa.pub
 (protocol 2 DSA),
+.Pa ~/.ssh/id_ecdsa.pub
+(protocol 2 ECDSA),
 or
 .Pa ~/.ssh/id_rsa.pub
 (protocol 2 RSA)
@@ -1277,7 +1282,8 @@
 and not accessible by others.
 .Pp
 .It Pa ~/.ssh/authorized_keys
-Lists the public keys (RSA/DSA) that can be used for logging in as this user.
+Lists the public keys (DSA/ECDSA/RSA) that can be used for logging in as
+this user.
 The format of this file is described in the
 .Xr sshd 8
 manual page.
@@ -1298,6 +1304,7 @@
 .Pp
 .It Pa ~/.ssh/identity
 .It Pa ~/.ssh/id_dsa
+.It Pa ~/.ssh/id_ecdsa
 .It Pa ~/.ssh/id_rsa
 Contains the private key for authentication.
 These files
@@ -1311,6 +1318,7 @@
 .Pp
 .It Pa ~/.ssh/identity.pub
 .It Pa ~/.ssh/id_dsa.pub
+.It Pa ~/.ssh/id_ecdsa.pub
 .It Pa ~/.ssh/id_rsa.pub
 Contains the public key for authentication.
 These files are not
@@ -1349,6 +1357,7 @@
 .Pp
 .It Pa /etc/ssh/ssh_host_key
 .It Pa /etc/ssh/ssh_host_dsa_key
+.It Pa /etc/ssh/ssh_host_ecdsa_key
 .It Pa /etc/ssh/ssh_host_rsa_key
 These three files contain the private parts of the host keys
 and are used for host-based authentication.
diff --git a/ssh.c b/ssh.c
index 44b570b..1cdfc58 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.348 2010/08/16 04:06:06 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.349 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -780,7 +780,7 @@
 	sensitive_data.external_keysign = 0;
 	if (options.rhosts_rsa_authentication ||
 	    options.hostbased_authentication) {
-		sensitive_data.nkeys = 5;
+		sensitive_data.nkeys = 7;
 		sensitive_data.keys = xcalloc(sensitive_data.nkeys,
 		    sizeof(Key));
 
@@ -789,25 +789,34 @@
 		    _PATH_HOST_KEY_FILE, "", NULL, NULL);
 		sensitive_data.keys[1] = key_load_private_cert(KEY_DSA,
 		    _PATH_HOST_DSA_KEY_FILE, "", NULL);
-		sensitive_data.keys[2] = key_load_private_cert(KEY_RSA,
+		sensitive_data.keys[2] = key_load_private_cert(KEY_ECDSA,
+		    _PATH_HOST_ECDSA_KEY_FILE, "", NULL);
+		sensitive_data.keys[3] = key_load_private_cert(KEY_RSA,
 		    _PATH_HOST_RSA_KEY_FILE, "", NULL);
-		sensitive_data.keys[3] = key_load_private_type(KEY_DSA,
+		sensitive_data.keys[4] = key_load_private_type(KEY_DSA,
 		    _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL);
-		sensitive_data.keys[4] = key_load_private_type(KEY_RSA,
+		sensitive_data.keys[5] = key_load_private_type(KEY_ECDSA,
+		    _PATH_HOST_ECDSA_KEY_FILE, "", NULL, NULL);
+		sensitive_data.keys[6] = key_load_private_type(KEY_RSA,
 		    _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL);
 		PRIV_END;
 
 		if (options.hostbased_authentication == 1 &&
 		    sensitive_data.keys[0] == NULL &&
-		    sensitive_data.keys[3] == NULL &&
-		    sensitive_data.keys[4] == NULL) {
+		    sensitive_data.keys[4] == NULL &&
+		    sensitive_data.keys[5] == NULL &&
+		    sensitive_data.keys[6] == NULL) {
 			sensitive_data.keys[1] = key_load_cert(
 			    _PATH_HOST_DSA_KEY_FILE);
 			sensitive_data.keys[2] = key_load_cert(
+			    _PATH_HOST_ECDSA_KEY_FILE);
+			sensitive_data.keys[3] = key_load_cert(
 			    _PATH_HOST_RSA_KEY_FILE);
-			sensitive_data.keys[3] = key_load_public(
-			    _PATH_HOST_DSA_KEY_FILE, NULL);
 			sensitive_data.keys[4] = key_load_public(
+			    _PATH_HOST_DSA_KEY_FILE, NULL);
+			sensitive_data.keys[5] = key_load_public(
+			    _PATH_HOST_ECDSA_KEY_FILE, NULL);
+			sensitive_data.keys[6] = key_load_public(
 			    _PATH_HOST_RSA_KEY_FILE, NULL);
 			sensitive_data.external_keysign = 1;
 		}
diff --git a/ssh2.h b/ssh2.h
index 3ffaf68..51a963c 100644
--- a/ssh2.h
+++ b/ssh2.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh2.h,v 1.13 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: ssh2.h,v 1.14 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -98,6 +98,10 @@
 #define SSH2_MSG_KEX_DH_GEX_REPLY			33
 #define SSH2_MSG_KEX_DH_GEX_REQUEST			34
 
+/* ecdh */
+#define SSH2_MSG_KEX_ECDH_INIT				30
+#define SSH2_MSG_KEX_ECDH_REPLY				31
+
 /* user authentication: generic */
 
 #define SSH2_MSG_USERAUTH_REQUEST			50
diff --git a/ssh_config.5 b/ssh_config.5
index ddb806e..33038ff 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -34,8 +34,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh_config.5,v 1.138 2010/08/04 05:37:01 djm Exp $
-.Dd $Mdocdate: August 4 2010 $
+.\" $OpenBSD: ssh_config.5,v 1.139 2010/08/31 11:54:45 djm Exp $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSH_CONFIG 5
 .Os
 .Sh NAME
@@ -547,7 +547,15 @@
 Specifies the protocol version 2 host key algorithms
 that the client wants to use in order of preference.
 The default for this option is:
-.Dq ssh-rsa,ssh-dss .
+.Bd -literal -offset 3n
+ecdsa-sha2-nistp256-cert-v01@openssh.com,
+ecdsa-sha2-nistp384-cert-v01@openssh.com,
+ecdsa-sha2-nistp521-cert-v01@openssh.com,
+ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,
+ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,
+ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+ssh-rsa,ssh-dss
+.Ed
 .It Cm HostKeyAlias
 Specifies an alias that should be used instead of the
 real host name when looking up or saving the host key
@@ -583,14 +591,15 @@
 The default is
 .Dq no .
 .It Cm IdentityFile
-Specifies a file from which the user's RSA or DSA authentication identity
-is read.
+Specifies a file from which the user's DSA, ECDSA or DSA authentication
+identity is read.
 The default is
 .Pa ~/.ssh/identity
 for protocol version 1, and
-.Pa ~/.ssh/id_rsa
+.Pa ~/.ssh/id_dsa ,
+.Pa ~/.ssh/id_ecdsa
 and
-.Pa ~/.ssh/id_dsa
+.Pa ~/.ssh/id_rsa
 for protocol version 2.
 Additionally, any identities represented by the authentication agent
 will be used for authentication.
diff --git a/sshconnect.c b/sshconnect.c
index f55beff..4d3a085 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.224 2010/04/16 21:14:27 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.225 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1173,7 +1173,7 @@
 static int
 show_other_keys(const char *host, Key *key)
 {
-	int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1};
+	int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1};
 	int i, found = 0;
 
 	for (i = 0; type[i] != -1; i++) {
diff --git a/sshconnect2.c b/sshconnect2.c
index 4c379ae..a31a663 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.183 2010/04/26 22:28:24 djm Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.184 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
@@ -145,6 +145,7 @@
 	kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
 	kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
 	kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
+	kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
 	kex->client_version_string=client_version_string;
 	kex->server_version_string=server_version_string;
 	kex->verify_host_key=&verify_host_key_callback;
diff --git a/sshd.8 b/sshd.8
index bf9d6a2..9d2efc7 100644
--- a/sshd.8
+++ b/sshd.8
@@ -34,8 +34,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd.8,v 1.258 2010/08/08 19:36:30 jmc Exp $
-.Dd $Mdocdate: August 8 2010 $
+.\" $OpenBSD: sshd.8,v 1.259 2010/08/31 11:54:45 djm Exp $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSHD 8
 .Os
 .Sh NAME
@@ -170,9 +170,10 @@
 The default is
 .Pa /etc/ssh/ssh_host_key
 for protocol version 1, and
-.Pa /etc/ssh/ssh_host_rsa_key
+.Pa /etc/ssh/ssh_host_dsa_key ,
+.Pa /etc/ssh/ssh_host_ecdsa_key
 and
-.Pa /etc/ssh/ssh_host_dsa_key
+.Pa /etc/ssh/ssh_host_rsa_key
 for protocol version 2.
 It is possible to have multiple host key files for
 the different protocol versions and host key algorithms.
@@ -275,7 +276,7 @@
 .Cm Protocol
 option in
 .Xr sshd_config 5 .
-Protocol 2 supports both RSA and DSA keys;
+Protocol 2 supports DSA, ECDSA and RSA keys;
 protocol 1 only supports RSA keys.
 For both protocols,
 each host has a host-specific key,
@@ -483,6 +484,9 @@
 comment field is not used for anything (but may be convenient for the
 user to identify the key).
 For protocol version 2 the keytype is
+.Dq ecdsa-sha2-nistp256 ,
+.Dq ecdsa-sha2-nistp384 ,
+.Dq ecdsa-sha2-nistp521 ,
 .Dq ssh-dss
 or
 .Dq ssh-rsa .
@@ -494,6 +498,7 @@
 You don't want to type them in; instead, copy the
 .Pa identity.pub ,
 .Pa id_dsa.pub ,
+.Pa id_ecdsa.pub ,
 or the
 .Pa id_rsa.pub
 file and edit it.
@@ -792,7 +797,8 @@
 and not accessible by others.
 .Pp
 .It Pa ~/.ssh/authorized_keys
-Lists the public keys (RSA/DSA) that can be used for logging in as this user.
+Lists the public keys (DSA/ECDSA/RSA) that can be used for logging in
+as this user.
 The format of this file is described above.
 The content of the file is not highly sensitive, but the recommended
 permissions are read/write for the user, and not accessible by others.
@@ -871,6 +877,7 @@
 .Pp
 .It Pa /etc/ssh/ssh_host_key
 .It Pa /etc/ssh/ssh_host_dsa_key
+.It Pa /etc/ssh/ssh_host_ecdsa_key
 .It Pa /etc/ssh/ssh_host_rsa_key
 These three files contain the private parts of the host keys.
 These files should only be owned by root, readable only by root, and not
@@ -881,6 +888,7 @@
 .Pp
 .It Pa /etc/ssh/ssh_host_key.pub
 .It Pa /etc/ssh/ssh_host_dsa_key.pub
+.It Pa /etc/ssh/ssh_host_ecdsa_key.pub
 .It Pa /etc/ssh/ssh_host_rsa_key.pub
 These three files contain the public parts of the host keys.
 These files should be world-readable but writable only by
diff --git a/sshd.c b/sshd.c
index 52a3789..658a497 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.377 2010/08/16 04:06:06 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.378 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -733,6 +733,7 @@
 		switch (key->type) {
 		case KEY_RSA:
 		case KEY_DSA:
+		case KEY_ECDSA:
 			if (buffer_len(&b) > 0)
 				buffer_append(&b, ",", 1);
 			p = key_ssh_name(key);
@@ -748,6 +749,7 @@
 		case KEY_DSA_CERT_V00:
 		case KEY_RSA_CERT:
 		case KEY_DSA_CERT:
+		case KEY_ECDSA_CERT:
 			if (buffer_len(&b) > 0)
 				buffer_append(&b, ",", 1);
 			p = key_ssh_name(key);
@@ -774,6 +776,7 @@
 		case KEY_DSA_CERT_V00:
 		case KEY_RSA_CERT:
 		case KEY_DSA_CERT:
+		case KEY_ECDSA_CERT:
 			key = sensitive_data.host_certificates[i];
 			break;
 		default:
@@ -1576,6 +1579,7 @@
 			break;
 		case KEY_RSA:
 		case KEY_DSA:
+		case KEY_ECDSA:
 			sensitive_data.have_ssh2_key = 1;
 			break;
 		}
@@ -2302,6 +2306,7 @@
 	kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
 	kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
 	kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+	kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
 	kex->server = 1;
 	kex->client_version_string=client_version_string;
 	kex->server_version_string=server_version_string;
diff --git a/sshd_config.5 b/sshd_config.5
index 596a728..af3d89b 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -34,8 +34,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd_config.5,v 1.125 2010/06/30 07:28:34 jmc Exp $
-.Dd $Mdocdate: June 30 2010 $
+.\" $OpenBSD: sshd_config.5,v 1.126 2010/08/31 11:54:45 djm Exp $
+.Dd $Mdocdate: August 31 2010 $
 .Dt SSHD_CONFIG 5
 .Os
 .Sh NAME
@@ -470,9 +470,10 @@
 The default is
 .Pa /etc/ssh/ssh_host_key
 for protocol version 1, and
-.Pa /etc/ssh/ssh_host_rsa_key
+.Pa /etc/ssh/ssh_host_dsa_key ,
+.Pa /etc/ssh/ssh_host_ecdsa_key
 and
-.Pa /etc/ssh/ssh_host_dsa_key
+.Pa /etc/ssh/ssh_host_rsa_key
 for protocol version 2.
 Note that
 .Xr sshd 8
@@ -480,7 +481,8 @@
 It is possible to have multiple host key files.
 .Dq rsa1
 keys are used for version 1 and
-.Dq dsa
+.Dq dsa ,
+.Dq ecdsa
 or
 .Dq rsa
 are used for version 2 of the SSH protocol.
diff --git a/uuencode.c b/uuencode.c
index b9e57e9..09d80d2 100644
--- a/uuencode.c
+++ b/uuencode.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uuencode.c,v 1.25 2009/03/05 11:30:50 djm Exp $ */
+/* $OpenBSD: uuencode.c,v 1.26 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -72,7 +72,7 @@
 }
 
 void
-dump_base64(FILE *fp, u_char *data, u_int len)
+dump_base64(FILE *fp, const u_char *data, u_int len)
 {
 	char *buf;
 	int i, n;
diff --git a/uuencode.h b/uuencode.h
index fec55b4..4d98881 100644
--- a/uuencode.h
+++ b/uuencode.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: uuencode.h,v 1.13 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: uuencode.h,v 1.14 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -26,4 +26,4 @@
 
 int	 uuencode(const u_char *, u_int, char *, size_t);
 int	 uudecode(const char *, u_char *, size_t);
-void	 dump_base64(FILE *, u_char *, u_int);
+void	 dump_base64(FILE *, const u_char *, u_int);