shithub: drawterm

Download patch

ref: f85ef344505b687ed057d5f452db033292253f00
parent: 72da0579a82cc83574d6f5eb7710d702d6df865e
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Apr 25 18:44:03 EDT 2016

libsec: update ecc and tlshand (sync with 9front)

--- a/include/libsec.h
+++ b/include/libsec.h
@@ -485,6 +485,7 @@
 	int inf;
 	mpint *x;
 	mpint *y;
+	mpint *z;
 } ECpoint;
 
 typedef ECpoint ECpub;
--- a/libsec/Makefile
+++ b/libsec/Makefile
@@ -26,6 +26,7 @@
 	dsasign.$O\
 	dsaverify.$O\
 	ecc.$O\
+	jacobian.$O\
 	egalloc.$O\
 	egdecrypt.$O\
 	egencrypt.$O\
--- a/libsec/ecc.c
+++ b/libsec/ecc.c
@@ -3,20 +3,38 @@
 #include <libsec.h>
 #include <ctype.h>
 
+extern void jacobian_affine(mpint *p,
+	mpint *X, mpint *Y, mpint *Z);
+extern void jacobian_dbl(mpint *p, mpint *a,
+	mpint *X1, mpint *Y1, mpint *Z1,
+	mpint *X3, mpint *Y3, mpint *Z3);
+extern void jacobian_add(mpint *p, mpint *a,
+	mpint *X1, mpint *Y1, mpint *Z1,
+	mpint *X2, mpint *Y2, mpint *Z2,
+	mpint *X3, mpint *Y3, mpint *Z3);
+
 void
 ecassign(ECdomain *dom, ECpoint *a, ECpoint *b)
 {
-	USED(dom);
-	b->inf = a->inf;
+	if((b->inf = a->inf) != 0)
+		return;
 	mpassign(a->x, b->x);
 	mpassign(a->y, b->y);
+	if(b->z != nil){
+		mpassign(a->z != nil ? a->z : mpone, b->z);
+		return;
+	}
+	if(a->z != nil){
+		b->z = mpcopy(a->z);
+		jacobian_affine(dom->p, b->x, b->y, b->z);
+		mpfree(b->z);
+		b->z = nil;
+	}
 }
 
 void
 ecadd(ECdomain *dom, ECpoint *a, ECpoint *b, ECpoint *s)
 {
-	mpint *l, *k, *sx, *sy;
-
 	if(a->inf && b->inf){
 		s->inf = 1;
 		return;
@@ -29,66 +47,27 @@
 		ecassign(dom, a, s);
 		return;
 	}
-	if(mpcmp(a->x, b->x) == 0 && (mpcmp(a->y, mpzero) == 0 || mpcmp(a->y, b->y) != 0)){
-		s->inf = 1;
-		return;
-	}
-	l = mpnew(0);
-	k = mpnew(0);
-	sx = mpnew(0);
-	sy = mpnew(0);
-	if(mpcmp(a->x, b->x) == 0 && mpcmp(a->y, b->y) == 0){
-		mpadd(mpone, mptwo, k);
-		mpmul(a->x, a->x, l);
-		mpmul(l, k, l);
-		mpadd(l, dom->a, l);
-		mpleft(a->y, 1, k);
-		mpmod(k, dom->p, k);
-		mpinvert(k, dom->p, k);
-		mpmul(k, l, l);
-		mpmod(l, dom->p, l);
 
-		mpleft(a->x, 1, k);
-		mpmul(l, l, sx);
-		mpsub(sx, k, sx);
-		mpmod(sx, dom->p, sx);
-
-		mpsub(a->x, sx, sy);
-		mpmul(l, sy, sy);
-		mpsub(sy, a->y, sy);
-		mpmod(sy, dom->p, sy);
-		mpassign(sx, s->x);
-		mpassign(sy, s->y);
-		mpfree(sx);
-		mpfree(sy);
-		mpfree(l);
-		mpfree(k);
+	if(s->z == nil){
+		s->z = mpcopy(mpone);
+		ecadd(dom, a, b, s);
+		if(!s->inf)
+			jacobian_affine(dom->p, s->x, s->y, s->z);
+		mpfree(s->z);
+		s->z = nil;
 		return;
 	}
-	mpsub(b->y, a->y, l);
-	mpmod(l, dom->p, l);
-	mpsub(b->x, a->x, k);
-	mpmod(k, dom->p, k);
-	mpinvert(k, dom->p, k);
-	mpmul(k, l, l);
-	mpmod(l, dom->p, l);
-	
-	mpmul(l, l, sx);
-	mpsub(sx, a->x, sx);
-	mpsub(sx, b->x, sx);
-	mpmod(sx, dom->p, sx);
-	
-	mpsub(a->x, sx, sy);
-	mpmul(sy, l, sy);
-	mpsub(sy, a->y, sy);
-	mpmod(sy, dom->p, sy);
-	
-	mpassign(sx, s->x);
-	mpassign(sy, s->y);
-	mpfree(sx);
-	mpfree(sy);
-	mpfree(l);
-	mpfree(k);
+
+	if(a == b)
+		jacobian_dbl(dom->p, dom->a,
+			a->x, a->y, a->z != nil ? a->z : mpone,
+			s->x, s->y, s->z);
+	else
+		jacobian_add(dom->p, dom->a,
+			a->x, a->y, a->z != nil ? a->z : mpone,
+			b->x, b->y, b->z != nil ? b->z : mpone,
+			s->x, s->y, s->z);
+	s->inf = mpcmp(s->z, mpzero) == 0;
 }
 
 void
@@ -104,8 +83,10 @@
 	ns.inf = 1;
 	ns.x = mpnew(0);
 	ns.y = mpnew(0);
+	ns.z = mpnew(0);
 	na.x = mpnew(0);
 	na.y = mpnew(0);
+	na.z = mpnew(0);
 	ecassign(dom, a, &na);
 	l = mpcopy(k);
 	l->sign = 1;
@@ -115,7 +96,7 @@
 		ecadd(dom, &na, &na, &na);
 		mpright(l, 1, l);
 	}
-	if(k->sign < 0){
+	if(k->sign < 0 && !ns.inf){
 		ns.y->sign = -1;
 		mpmod(ns.y, dom->p, ns.y);
 	}
@@ -122,8 +103,10 @@
 	ecassign(dom, &ns, s);
 	mpfree(ns.x);
 	mpfree(ns.y);
+	mpfree(ns.z);
 	mpfree(na.x);
 	mpfree(na.y);
+	mpfree(na.z);
 	mpfree(l);
 }
 
@@ -135,16 +118,15 @@
 
 	if(a->inf)
 		return 1;
-	
+
+	assert(a->z == nil);	/* need affine coordinates */
 	p = mpnew(0);
 	q = mpnew(0);
-	mpmul(a->y, a->y, p);
-	mpmod(p, dom->p, p);
-	mpmul(a->x, a->x, q);
-	mpadd(q, dom->a, q);
-	mpmul(a->x, q, q);
-	mpadd(q, dom->b, q);
-	mpmod(q, dom->p, q);
+	mpmodmul(a->y, a->y, dom->p, p);
+	mpmodmul(a->x, a->x, dom->p, q);
+	mpmodadd(q, dom->a, dom->p, q);
+	mpmodmul(q, a->x, dom->p, q);
+	mpmodadd(q, dom->b, dom->p, q);
 	r = mpcmp(p, q);
 	mpfree(p);
 	mpfree(q);
@@ -163,10 +145,12 @@
 		return 0;
 	p.x = mpnew(0);
 	p.y = mpnew(0);
+	p.z = mpnew(0);
 	ecmul(dom, a, dom->n, &p);
 	r = p.inf;
 	mpfree(p.x);
 	mpfree(p.y);
+	mpfree(p.z);
 	return r;
 }
 
@@ -273,8 +257,8 @@
 	zq = mpnew(0);
 	for(;;){
 		for(;;){
-			mprand(mpsignif(p), genrandom, a);
-			if(mpcmp(a, mpzero) > 0 && mpcmp(a, p) < 0)
+			mpnrand(p, genrandom, a);
+			if(mpcmp(a, mpzero) > 0)
 				break;
 		}
 		mpmul(a, a, t);
@@ -346,11 +330,12 @@
 		ret->x = mpnew(0);
 		ret->y = mpnew(0);
 	}
+	ret->inf = 0;
 	o = 0;
 	switch(octet(&s)){
 	case 0:
 		ret->inf = 1;
-		return ret;
+		break;
 	case 3:
 		o = 1;
 	case 2:
@@ -371,7 +356,7 @@
 		mpfree(r);
 		if(!ecverify(dom, ret))
 			goto err;
-		return ret;
+		break;
 	case 4:
 		if(halfpt(dom, s, &s, ret->x) == nil)
 			goto err;
@@ -379,8 +364,12 @@
 			goto err;
 		if(!ecverify(dom, ret))
 			goto err;
-		return ret;
+		break;
 	}
+	if(ret->z != nil && !ret->inf)
+		mpassign(mpone, ret->z);
+	return ret;
+
 err:
 	if(rptr)
 		*rptr = s;
@@ -404,11 +393,11 @@
 		p->d = mpnew(0);
 	}
 	for(;;){
-		mprand(mpsignif(dom->n), genrandom, p->d);
-		if(mpcmp(p->d, mpzero) > 0 && mpcmp(p->d, dom->n) < 0)
+		mpnrand(dom->n, genrandom, p->d);
+		if(mpcmp(p->d, mpzero) > 0)
 			break;
 	}
-	ecmul(dom, &dom->G, p->d, (ECpoint*)p);
+	ecmul(dom, &dom->G, p->d, &p->a);
 	return p;
 }
 
@@ -420,6 +409,7 @@
 
 	tmp.a.x = mpnew(0);
 	tmp.a.y = mpnew(0);
+	tmp.a.z = nil;
 	tmp.d = mpnew(0);
 	E = betomp(dig, len, nil);
 	t = mpnew(0);
@@ -433,8 +423,7 @@
 		mpmul(r, priv->d, s);
 		mpadd(E, s, s);
 		mpinvert(tmp.d, dom->n, t);
-		mpmul(s, t, s);
-		mpmod(s, dom->n, s);
+		mpmodmul(s, t, dom->n, s);
 		if(mpcmp(s, mpzero) != 0)
 			break;
 	}
@@ -462,18 +451,19 @@
 	u2 = mpnew(0);
 	R.x = mpnew(0);
 	R.y = mpnew(0);
+	R.z = mpnew(0);
 	S.x = mpnew(0);
 	S.y = mpnew(0);
+	S.z = mpnew(0);
 	mpinvert(s, dom->n, t);
-	mpmul(E, t, u1);
-	mpmod(u1, dom->n, u1);
-	mpmul(r, t, u2);
-	mpmod(u2, dom->n, u2);
+	mpmodmul(E, t, dom->n, u1);
+	mpmodmul(r, t, dom->n, u2);
 	ecmul(dom, &dom->G, u1, &R);
 	ecmul(dom, pub, u2, &S);
 	ecadd(dom, &R, &S, &R);
 	ret = 0;
 	if(!R.inf){
+		jacobian_affine(dom->p, R.x, R.y, R.z);
 		mpmod(R.x, dom->n, t);
 		ret = mpcmp(r, t) == 0;
 	}
@@ -483,8 +473,10 @@
 	mpfree(u2);
 	mpfree(R.x);
 	mpfree(R.y);
+	mpfree(R.z);
 	mpfree(S.x);
 	mpfree(S.y);
+	mpfree(S.z);
 	return ret;
 }
 
--- /dev/null
+++ b/libsec/jacobian.c
@@ -1,0 +1,166 @@
+#include "os.h"
+#include <mp.h>
+void jacobian_new(mpint *x, mpint *y, mpint *z, mpint *X, mpint *Y, mpint *Z){
+	mpassign(x, X);
+	mpassign(y, Y);
+	mpassign(z, Z);
+	}
+void jacobian_inf(mpint *X, mpint *Y, mpint *Z){
+	jacobian_new(mpzero, mpone, mpzero, X, Y, Z);
+	}
+void jacobian_affine(mpint *p, mpint *X, mpint *Y, mpint *Z){
+	mpint *ZZZ = mpnew(0);
+	mpint *ZZ = mpnew(0);
+	if(mpcmp(Z, mpzero) != 0){
+		mpmodmul(Z, Z, p, ZZ);
+		mpmodmul(ZZ, Z, p, ZZZ);
+		mpint *tmp1 = mpnew(0);
+		mpinvert(ZZ, p, tmp1);
+		mpmodmul(X, tmp1, p, X);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		mpinvert(ZZZ, p, tmp1);
+		mpmodmul(Y, tmp1, p, Y);
+		mpfree(tmp1);
+		mpassign(mpone, Z);
+		}
+	mpfree(ZZZ);
+	mpfree(ZZ);
+	}
+void jacobian_dbl(mpint *p, mpint *a, mpint *X1, mpint *Y1, mpint *Z1, mpint *X3, mpint *Y3, mpint *Z3){
+	mpint *M = mpnew(0);
+	mpint *S = mpnew(0);
+	mpint *ZZ = mpnew(0);
+	mpint *YYYY = mpnew(0);
+	mpint *YY = mpnew(0);
+	mpint *XX = mpnew(0);
+	if(mpcmp(Y1, mpzero) == 0){
+		jacobian_inf(X3, Y3, Z3);
+		}else{
+		mpmodmul(X1, X1, p, XX);
+		mpmodmul(Y1, Y1, p, YY);
+		mpmodmul(YY, YY, p, YYYY);
+		mpmodmul(Z1, Z1, p, ZZ);
+		mpint *tmp1 = mpnew(0);
+		mpmodadd(X1, YY, p, tmp1);
+		mpmodmul(tmp1, tmp1, p, tmp1);
+		mpmodsub(tmp1, XX, p, tmp1);
+		mpmodsub(tmp1, YYYY, p, tmp1);
+		mpmodadd(tmp1, tmp1, p, S); // 2*tmp1
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		uitomp(3UL, tmp1);
+		mpmodmul(tmp1, XX, p, M);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		mpint *tmp2 = mpnew(0);
+		mpmodmul(ZZ, ZZ, p, tmp2);
+		mpmodmul(a, tmp2, p, tmp1);
+		mpfree(tmp2);
+		mpmodadd(M, tmp1, p, M);
+		mpfree(tmp1);
+		mpmodadd(Y1, Z1, p, Z3);
+		mpmodmul(Z3, Z3, p, Z3);
+		mpmodsub(Z3, YY, p, Z3);
+		mpmodsub(Z3, ZZ, p, Z3);
+		mpmodmul(M, M, p, X3);
+		tmp1 = mpnew(0);
+		mpmodadd(S, S, p, tmp1); // 2*S
+		mpmodsub(X3, tmp1, p, X3);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		mpmodsub(S, X3, p, tmp1);
+		mpmodmul(M, tmp1, p, Y3);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		tmp2 = mpnew(0);
+		uitomp(8UL, tmp2);
+		mpmodmul(tmp2, YYYY, p, tmp1);
+		mpfree(tmp2);
+		mpmodsub(Y3, tmp1, p, Y3);
+		mpfree(tmp1);
+		}
+	mpfree(M);
+	mpfree(S);
+	mpfree(ZZ);
+	mpfree(YYYY);
+	mpfree(YY);
+	mpfree(XX);
+	}
+void jacobian_add(mpint *p, mpint *a, mpint *X1, mpint *Y1, mpint *Z1, mpint *X2, mpint *Y2, mpint *Z2, mpint *X3, mpint *Y3, mpint *Z3){
+	mpint *V = mpnew(0);
+	mpint *r = mpnew(0);
+	mpint *J = mpnew(0);
+	mpint *I = mpnew(0);
+	mpint *H = mpnew(0);
+	mpint *S2 = mpnew(0);
+	mpint *S1 = mpnew(0);
+	mpint *U2 = mpnew(0);
+	mpint *U1 = mpnew(0);
+	mpint *Z2Z2 = mpnew(0);
+	mpint *Z1Z1 = mpnew(0);
+	mpmodmul(Z1, Z1, p, Z1Z1);
+	mpmodmul(Z2, Z2, p, Z2Z2);
+	mpmodmul(X1, Z2Z2, p, U1);
+	mpmodmul(X2, Z1Z1, p, U2);
+	mpint *tmp1 = mpnew(0);
+	mpmodmul(Y1, Z2, p, tmp1);
+	mpmodmul(tmp1, Z2Z2, p, S1);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	mpmodmul(Y2, Z1, p, tmp1);
+	mpmodmul(tmp1, Z1Z1, p, S2);
+	mpfree(tmp1);
+	if(mpcmp(U1, U2) == 0){
+		if(mpcmp(S1, S2) != 0){
+			jacobian_inf(X3, Y3, Z3);
+			}else{
+			jacobian_dbl(p, a, X1, Y1, Z1, X3, Y3, Z3);
+			}
+		}else{
+		mpmodsub(U2, U1, p, H);
+		mpmodadd(H, H, p, I); // 2*H
+		mpmodmul(I, I, p, I);
+		mpmodmul(H, I, p, J);
+		mpint *tmp2 = mpnew(0);
+		mpmodsub(S2, S1, p, tmp2);
+		mpmodadd(tmp2, tmp2, p, r); // 2*tmp2
+		mpfree(tmp2);
+		mpmodmul(U1, I, p, V);
+		mpmodmul(r, r, p, X3);
+		mpmodsub(X3, J, p, X3);
+		tmp2 = mpnew(0);
+		mpmodadd(V, V, p, tmp2); // 2*V
+		mpmodsub(X3, tmp2, p, X3);
+		mpfree(tmp2);
+		tmp2 = mpnew(0);
+		mpmodsub(V, X3, p, tmp2);
+		mpmodmul(r, tmp2, p, Y3);
+		mpfree(tmp2);
+		tmp2 = mpnew(0);
+		mpint *tmp3 = mpnew(0);
+		mpmodadd(S1, S1, p, tmp3); // 2*S1
+		mpmodmul(tmp3, J, p, tmp2);
+		mpfree(tmp3);
+		mpmodsub(Y3, tmp2, p, Y3);
+		mpfree(tmp2);
+		tmp2 = mpnew(0);
+		mpmodadd(Z1, Z2, p, tmp2);
+		mpmodmul(tmp2, tmp2, p, tmp2);
+		mpmodsub(tmp2, Z1Z1, p, tmp2);
+		mpmodsub(tmp2, Z2Z2, p, tmp2);
+		mpmodmul(tmp2, H, p, Z3);
+		mpfree(tmp2);
+		}
+	mpfree(V);
+	mpfree(r);
+	mpfree(J);
+	mpfree(I);
+	mpfree(H);
+	mpfree(S2);
+	mpfree(S1);
+	mpfree(U2);
+	mpfree(U1);
+	mpfree(Z2Z2);
+	mpfree(Z1Z1);
+	}
--- a/libsec/tlshand.c
+++ b/libsec/tlshand.c
@@ -69,6 +69,14 @@
 	uchar sec[MasterSecretSize];	// master secret
 	uchar crandom[RandomSize];	// client random
 	uchar srandom[RandomSize];	// server random
+
+	// diffie hellman state
+	DHstate dh;
+	struct {
+		ECdomain dom;
+		ECpriv Q;
+	} ec;
+
 	// byte generation and handshake checksum
 	void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int);
 	void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int);
@@ -86,7 +94,7 @@
 	int cipher;
 	int nsecret;	// amount of secret data to init keys
 	char *digest;	// name of digest algorithm to use
-	char *enc;		// name of encryption algorithm to use
+	char *enc;	// name of encryption algorithm to use
 
 	// for finished messages
 	HandshakeHash	handhash;
@@ -203,6 +211,7 @@
 	EProtocolVersion = 70,
 	EInsufficientSecurity = 71,
 	EInternalError = 80,
+	EInappropriateFallback = 86,
 	EUserCanceled = 90,
 	ENoRenegotiation = 100,
 	EUnknownPSKidentity = 115,
@@ -287,6 +296,8 @@
 	TLS_PSK_WITH_CHACHA20_POLY1305		= 0xCCAB,
 	TLS_PSK_WITH_AES_128_CBC_SHA256		= 0x00AE,
 	TLS_PSK_WITH_AES_128_CBC_SHA		= 0x008C,
+
+	TLS_FALLBACK_SCSV = 0x5600,
 };
 
 // compression methods
@@ -296,34 +307,38 @@
 };
 
 static Algs cipherAlgs[] = {
-	{"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+	// ECDHE-ECDSA
 	{"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},
-	{"ccpoly96_aead", "clear", 2*(32+12), TLS_DHE_RSA_WITH_CHACHA20_POLY1305},
-
-	{"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305},
 	{"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},
-	{"ccpoly64_aead", "clear", 2*32, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305},
-
-	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
-	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_RSA_WITH_AES_128_GCM_SHA256},
-
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
+
+	// ECDHE-RSA
+	{"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+	{"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
 	{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
 	{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+
+	// DHE-RSA
+	{"ccpoly96_aead", "clear", 2*(32+12), TLS_DHE_RSA_WITH_CHACHA20_POLY1305},
+	{"ccpoly64_aead", "clear", 2*32, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305},
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
 	{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
 	{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
+	{"3des_ede_cbc","sha1",	2*(4*8+SHA1dlen), TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
+
+	// RSA
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_RSA_WITH_AES_128_GCM_SHA256},
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_RSA_WITH_AES_128_CBC_SHA256},
 	{"aes_256_cbc", "sha256", 2*(32+16+SHA2_256dlen), TLS_RSA_WITH_AES_256_CBC_SHA256},
 	{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA},
 	{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA},
-	{"3des_ede_cbc","sha1",	2*(4*8+SHA1dlen), TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
 	{"3des_ede_cbc","sha1",	2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA},
 
-	// PSK cipher suits
+	// PSK
 	{"ccpoly96_aead", "clear", 2*(32+12), TLS_PSK_WITH_CHACHA20_POLY1305},
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_PSK_WITH_AES_128_CBC_SHA256},
 	{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_PSK_WITH_AES_128_CBC_SHA},
@@ -345,6 +360,7 @@
 	DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*);
 	int len;
 } hashfun[] = {
+/*	[0x00]  is reserved for MD5+SHA1 for < TLS1.2 */
 	[0x01]	{md5,		MD5dlen},
 	[0x02]	{sha1,		SHA1dlen},
 	[0x03]	{sha2_224,	SHA2_224dlen},
@@ -384,6 +400,11 @@
 static int finishedMatch(TlsConnection *c, Finished *f);
 static void tlsConnectionFree(TlsConnection *c);
 
+static int isDHE(int tlsid);
+static int isECDHE(int tlsid);
+static int isPSK(int tlsid);
+static int isECDSA(int tlsid);
+
 static int setAlgs(TlsConnection *c, int a);
 static int okCipher(Ints *cv, int ispsk);
 static int okCompression(Bytes *cv);
@@ -390,24 +411,28 @@
 static int initCiphers(void);
 static Ints* makeciphers(int ispsk);
 
+static AuthRpc* factotum_rsa_open(RSApub *rsapub);
+static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
+static void factotum_rsa_close(AuthRpc *rpc);
+
 static void	tlsSecInits(TlsSec *sec, int cvers, uchar *crandom);
 static int	tlsSecRSAs(TlsSec *sec, Bytes *epm);
-static void	tlsSecPSKs(TlsSec *sec);
+static Bytes*	tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc);
+static int	tlsSecECDHEs2(TlsSec *sec, Bytes *Yc);
 static void	tlsSecInitc(TlsSec *sec, int cvers);
 static Bytes*	tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert);
-static void	tlsSecPSKc(TlsSec *sec);
 static Bytes*	tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys);
 static Bytes*	tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys);
 static void	tlsSecVers(TlsSec *sec, int v);
 static int	tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient);
 static void	setMasterSecret(TlsSec *sec, Bytes *pm);
+static int	digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg);
+static char*	verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg);
+
 static Bytes*	pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype);
 static Bytes*	pkcs1_decrypt(TlsSec *sec, Bytes *cipher);
+static Bytes*	pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg);
 
-static AuthRpc* factotum_rsa_open(RSApub *rsapub);
-static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
-static void factotum_rsa_close(AuthRpc *rpc);
-
 static void* emalloc(int);
 static void* erealloc(void*, int);
 static void put32(uchar *p, u32int);
@@ -423,6 +448,7 @@
 static void freebytes(Bytes* b);
 static Ints* newints(int len);
 static void freeints(Ints* b);
+static int lookupid(Ints* b, int id);
 
 /* x509.c */
 extern mpint*	pkcs1padbuf(uchar *buf, int len, mpint *modulus);
@@ -519,7 +545,7 @@
 	}
 
 	// ECDHE
-	if(1){
+	if(ProtocolVersion >= TLS10Version){
 		m = p - b;
 		b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats));
 		p = b + m;
@@ -654,9 +680,9 @@
 	char *pskid, uchar *psk, int psklen,
 	int (*trace)(char*fmt, ...), PEMChain *chp)
 {
+	int cipher, compressor, numcerts, i;
 	TlsConnection *c;
 	Msg m;
-	int cipher, compressor, numcerts, i;
 
 	if(trace)
 		trace("tlsServer2\n");
@@ -685,7 +711,11 @@
 		tlsError(c, EIllegalParameter, "incompatible version");
 		goto Err;
 	}
-
+	if(c->version < ProtocolVersion
+	&& lookupid(m.u.clientHello.ciphers, TLS_FALLBACK_SCSV) >= 0){
+		tlsError(c, EInappropriateFallback, "inappropriate fallback");
+		goto Err;
+	}
 	cipher = okCipher(m.u.clientHello.ciphers, psklen > 0);
 	if(cipher < 0 || !setAlgs(c, cipher)) {
 		tlsError(c, EHandshakeFailure, "no matching cipher suite");
@@ -741,6 +771,36 @@
 			goto Err;
 	}
 
+	if(isECDHE(cipher)){
+		Namedcurve *nc = &namedcurves[0];	/* secp256r1 */
+
+		m.tag = HServerKeyExchange;
+		m.u.serverKeyExchange.curve = nc->tlsid;
+		m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc);
+		if(m.u.serverKeyExchange.dh_parameters == nil){
+			tlsError(c, EInternalError, "can't set DH parameters");
+			goto Err;
+		}
+
+		/* sign the DH parameters */
+		if(certlen > 0){
+			uchar digest[MAXdlen];
+			int digestlen;
+
+			if(c->version >= TLS12Version)
+				m.u.serverKeyExchange.sigalg = 0x0401;	/* RSA SHA256 */
+			digestlen = digestDHparams(c->sec, m.u.serverKeyExchange.dh_parameters,
+				digest, m.u.serverKeyExchange.sigalg);
+			if((m.u.serverKeyExchange.dh_signature = pkcs1_sign(c->sec, digest, digestlen,
+				m.u.serverKeyExchange.sigalg)) == nil){
+				tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
+				goto Err;
+			}
+		}
+		if(!msgSend(c, &m, AQueue))
+			goto Err;
+	}
+
 	m.tag = HServerHelloDone;
 	if(!msgSend(c, &m, AFlush))
 		goto Err;
@@ -759,13 +819,18 @@
 			goto Err;
 		}
 	}
-	if(certlen > 0){
+	if(isECDHE(cipher)){
+		if(tlsSecECDHEs2(c->sec, m.u.clientKeyExchange.key) < 0){
+			tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
+			goto Err;
+		}
+	} else if(certlen > 0){
 		if(tlsSecRSAs(c->sec, m.u.clientKeyExchange.key) < 0){
 			tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
 			goto Err;
 		}
 	} else if(psklen > 0){
-		tlsSecPSKs(c->sec);
+		setMasterSecret(c->sec, newbytes(psklen));
 	} else {
 		tlsError(c, EInternalError, "no psk or certificate");
 		goto Err;
@@ -822,79 +887,29 @@
 	return nil;
 }
 
-static int
-isDHE(int tlsid)
-{
-	switch(tlsid){
-	case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
- 	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
- 	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
- 	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-	case TLS_DHE_RSA_WITH_CHACHA20_POLY1305:
-	case GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305:
-		return 1;
-	}
-	return 0;
-}
-
-static int
-isECDHE(int tlsid)
-{
-	switch(tlsid){
-	case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
-	case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
-
-	case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
-	case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
-
-	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
-	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-
-	case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-	case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-	case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-	case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-		return 1;
-	}
-	return 0;
-}
-
-static int
-isPSK(int tlsid)
-{
-	switch(tlsid){
-	case TLS_PSK_WITH_CHACHA20_POLY1305:
-	case TLS_PSK_WITH_AES_128_CBC_SHA256:
-	case TLS_PSK_WITH_AES_128_CBC_SHA:
-		return 1;
-	}
-	return 0;
-}
-
 static Bytes*
 tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys)
 {
+	DHstate *dh = &sec->dh;
 	mpint *G, *P, *Y, *K;
-	Bytes *epm;
-	DHstate dh;
+	Bytes *Yc;
 
 	if(p == nil || g == nil || Ys == nil)
 		return nil;
 
-	epm = nil;
+	Yc = nil;
 	P = bytestomp(p);
 	G = bytestomp(g);
 	Y = bytestomp(Ys);
 	K = nil;
 
-	if(P == nil || G == nil || Y == nil || dh_new(&dh, P, nil, G) == nil)
+	if(dh_new(dh, P, nil, G) == nil)
 		goto Out;
-	epm = mptobytes(dh.y);
-	K = dh_finish(&dh, Y);
+	Yc = mptobytes(dh->y);
+	K = dh_finish(dh, Y);	/* zeros dh */
 	if(K == nil){
-		freebytes(epm);
-		epm = nil;
+		freebytes(Yc);
+		Yc = nil;
 		goto Out;
 	}
 	setMasterSecret(sec, mptobytes(K));
@@ -905,134 +920,55 @@
 	mpfree(G);
 	mpfree(P);
 
-	return epm;
+	return Yc;
 }
 
 static Bytes*
 tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys)
 {
-	Namedcurve *nc, *enc;
-	Bytes *epm;
-	ECdomain dom;
+	ECdomain *dom = &sec->ec.dom;
+	ECpriv *Q = &sec->ec.Q;
+	Namedcurve *nc;
 	ECpub *pub;
 	ECpoint K;
-	ECpriv Q;
+	Bytes *Yc;
 
 	if(Ys == nil)
 		return nil;
-
-	enc = &namedcurves[nelem(namedcurves)];
-	for(nc = namedcurves; nc != enc; nc++)
+	for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++)
 		if(nc->tlsid == curve)
-			break;
+			goto Found;
+	return nil;
 
-	if(nc == enc)
+Found:
+	ecdominit(dom, nc->init);
+	pub = ecdecodepub(dom, Ys->data, Ys->len);
+	if(pub == nil)
 		return nil;
-	
-	ecdominit(&dom, nc->init);
-	pub = ecdecodepub(&dom, Ys->data, Ys->len);
-	if(pub == nil){
-		ecdomfree(&dom);
-		return nil;
-	}
 
-	memset(&Q, 0, sizeof(Q));
-	Q.a.x = mpnew(0);
-	Q.a.y = mpnew(0);
-	Q.d = mpnew(0);
+	memset(Q, 0, sizeof(*Q));
+	Q->a.x = mpnew(0);
+	Q->a.y = mpnew(0);
+	Q->d = mpnew(0);
 
 	memset(&K, 0, sizeof(K));
 	K.x = mpnew(0);
 	K.y = mpnew(0);
 
-	epm = nil;
-	if(ecgen(&dom, &Q) != nil){
-		ecmul(&dom, pub, Q.d, &K);
-		setMasterSecret(sec, mptobytes(K.x));
-		epm = newbytes(1 + 2*((mpsignif(dom.p)+7)/8));
-		epm->len = ecencodepub(&dom, (ECpub*)&Q, epm->data, epm->len);
-	}
+	ecgen(dom, Q);
+	ecmul(dom, pub, Q->d, &K);
+	setMasterSecret(sec, mptobytes(K.x));
+	Yc = newbytes(1 + 2*((mpsignif(dom->p)+7)/8));
+	Yc->len = ecencodepub(dom, (ECpub*)Q, Yc->data, Yc->len);
 
 	mpfree(K.x);
 	mpfree(K.y);
-	mpfree(Q.a.x);
-	mpfree(Q.a.y);
-	mpfree(Q.d);
 
 	ecpubfree(pub);
-	ecdomfree(&dom);
 
-	return epm;
+	return Yc;
 }
 
-static char*
-verifyDHparams(TlsConnection *c, Bytes *par, Bytes *sig, int sigalg)
-{
-	uchar digest[MAXdlen];
-	int digestlen;
-	ECdomain dom;
-	ECpub *ecpk;
-	RSApub *rsapk;
-	Bytes *blob;
-	char *err;
-
-	if(par == nil || par->len <= 0)
-		return "no DH parameters";
-
-	if(sig == nil || sig->len <= 0){
-		if(c->sec->psklen > 0)
-			return nil;
-		return "no signature";
-	}
-
-	if(c->cert == nil)
-		return "no certificate";
-
-	blob = newbytes(2*RandomSize + par->len);
-	memmove(blob->data+0*RandomSize, c->sec->crandom, RandomSize);
-	memmove(blob->data+1*RandomSize, c->sec->srandom, RandomSize);
-	memmove(blob->data+2*RandomSize, par->data, par->len);
-	if(c->version < TLS12Version){
-		digestlen = MD5dlen + SHA1dlen;
-		md5(blob->data, blob->len, digest, nil);
-		sha1(blob->data, blob->len, digest+MD5dlen, nil);
-		sigalg = 1; // only RSA signatures supported for version <= TLS1.1
-	} else {
-		int hashalg = (sigalg>>8) & 0xFF;
-		digestlen = -1;
-		if(hashalg < nelem(hashfun) && hashfun[hashalg].fun != nil){
-			digestlen = hashfun[hashalg].len;
-			(*hashfun[hashalg].fun)(blob->data, blob->len, digest, nil);
-		}
-	}
-	freebytes(blob);
-
-	if(digestlen <= 0)
-		return "unknown signature digest algorithm";
-	
-	switch(sigalg & 0xFF){
-	case 0x01:
-		rsapk = X509toRSApub(c->cert->data, c->cert->len, nil, 0);
-		if(rsapk == nil)
-			return "bad certificate";
-		err = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, rsapk);
-		rsapubfree(rsapk);
-		break;
-	case 0x03:
-		ecpk = X509toECpub(c->cert->data, c->cert->len, &dom);
-		if(ecpk == nil)
-			return "bad certificate";
-		err = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, &dom, ecpk);
-		ecdomfree(&dom);
-		ecpubfree(ecpk);
-		break;
-	default:
-		err = "signaure algorithm not RSA or ECDSA";
-	}
-
-	return err;
-}
-
 static TlsConnection *
 tlsClient2(int ctl, int hand,
 	uchar *cert, int certlen,
@@ -1040,10 +976,10 @@
 	uchar *ext, int extlen,
 	int (*trace)(char*fmt, ...))
 {
-	TlsConnection *c;
-	Msg m;
 	int creq, dhx, cipher;
+	TlsConnection *c;
 	Bytes *epm;
+	Msg m;
 
 	if(!initCiphers())
 		return nil;
@@ -1129,10 +1065,11 @@
 	}
 	if(m.tag == HServerKeyExchange) {
 		if(dhx){
-			char *err = verifyDHparams(c,
+			char *err = verifyDHparams(c->sec,
 				m.u.serverKeyExchange.dh_parameters,
+				c->cert,
 				m.u.serverKeyExchange.dh_signature,
-				m.u.serverKeyExchange.sigalg);
+				c->version<TLS12Version ? 0x01 : m.u.serverKeyExchange.sigalg);
 			if(err != nil){
 				tlsError(c, EBadCertificate, "can't verify DH parameters: %s", err);
 				goto Err;
@@ -1183,7 +1120,7 @@
 				goto Err;
 			}
 		} else if(psklen > 0){
-			tlsSecPSKc(c->sec);
+			setMasterSecret(c->sec, newbytes(psklen));
 		} else {
 			tlsError(c, EInternalError, "no psk or certificate");
 			goto Err;
@@ -1223,39 +1160,28 @@
 
 	/* certificate verify */
 	if(creq && certlen > 0) {
-		mpint *signedMP, *paddedHashes;
 		HandshakeHash hsave;
-		uchar buf[512];
-		int buflen;
+		uchar digest[MAXdlen];
+		int digestlen;
 
 		/* save the state for the Finish message */
 		hsave = c->handhash;
-		if(c->version >= TLS12Version){
-			uchar digest[SHA2_256dlen];
-
+		if(c->version < TLS12Version){
+			md5(nil, 0, digest, &c->handhash.md5);
+			sha1(nil, 0, digest+MD5dlen, &c->handhash.sha1);
+			digestlen = MD5dlen+SHA1dlen;
+		} else {
 			m.u.certificateVerify.sigalg = 0x0401;	/* RSA SHA256 */
 			sha2_256(nil, 0, digest, &c->handhash.sha2_256);
-			buflen = asn1encodedigest(sha2_256, digest, buf, sizeof(buf));
-		} else {
-			md5(nil, 0, buf, &c->handhash.md5);
-			sha1(nil, 0, buf+MD5dlen, &c->handhash.sha1);
-			buflen = MD5dlen+SHA1dlen;
+			digestlen = SHA2_256dlen;
 		}
 		c->handhash = hsave;
 
-		if(buflen <= 0){
-			tlsError(c, EInternalError, "can't encode handshake hashes");
+		if((m.u.certificateVerify.signature = pkcs1_sign(c->sec, digest, digestlen,
+			m.u.certificateVerify.sigalg)) == nil){
+			tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
 			goto Err;
 		}
-		
-		paddedHashes = pkcs1padbuf(buf, buflen, c->sec->rsapub->n);
-		signedMP = factotum_rsa_decrypt(c->sec->rpc, paddedHashes);
-		if(signedMP == nil){
-			tlsError(c, EHandshakeFailure, "factotum_rsa_decrypt: %r");
-			goto Err;
-		}
-		m.u.certificateVerify.signature = mptobytes(signedMP);
-		mpfree(signedMP);
 
 		m.tag = HCertificateVerify;
 		if(!msgSend(c, &m, AFlush))
@@ -1439,6 +1365,30 @@
 		memmove(p, m->u.certificateVerify.signature->data, m->u.certificateVerify.signature->len);
 		p += m->u.certificateVerify.signature->len;
 		break;
+	case HServerKeyExchange:
+		if(m->u.serverKeyExchange.pskid != nil){
+			n = m->u.serverKeyExchange.pskid->len;
+			put16(p, n);
+			p += 2;
+			memmove(p, m->u.serverKeyExchange.pskid->data, n);
+			p += n;
+		}
+		if(m->u.serverKeyExchange.dh_parameters == nil)
+			break;
+		n = m->u.serverKeyExchange.dh_parameters->len;
+		memmove(p, m->u.serverKeyExchange.dh_parameters->data, n);
+		p += n;
+		if(m->u.serverKeyExchange.dh_signature == nil)
+			break;
+		if(c->version >= TLS12Version){
+			put16(p, m->u.serverKeyExchange.sigalg);
+			p += 2;
+		}
+		n = m->u.serverKeyExchange.dh_signature->len;
+		put16(p, n), p += 2;
+		memmove(p, m->u.serverKeyExchange.dh_signature->data, n);
+		p += n;
+		break;
 	case HClientKeyExchange:
 		if(m->u.clientKeyExchange.pskid != nil){
 			n = m->u.clientKeyExchange.pskid->len;
@@ -1822,10 +1772,6 @@
 		}
 		break;		
 	case HClientKeyExchange:
-		/*
-		 * this message depends upon the encryption selected
-		 * assume rsa.
-		 */
 		if(isPSK(c->cipher)){
 			if(n < 2)
 				goto Short;
@@ -1843,8 +1789,12 @@
 		else{
 			if(n < 2)
 				goto Short;
-			nn = get16(p);
-			p += 2, n -= 2;
+			if(isECDHE(c->cipher))
+				nn = *p++, n--;
+			else {
+				nn = get16(p);
+				p += 2, n -= 2;
+			}
 		}
 		if(n < nn)
 			goto Short;
@@ -1940,11 +1890,11 @@
 	if(b == nil)
 		bs = seprint(bs, be, "nil");
 	else {
-		bs = seprint(bs, be, "<%d> [", b->len);
+		bs = seprint(bs, be, "<%d> [ ", b->len);
 		for(i=0; i<b->len; i++)
 			bs = seprint(bs, be, "%.2x ", b->data[i]);
+		bs = seprint(bs, be, "]");
 	}
-	bs = seprint(bs, be, "]");
 	if(s1)
 		bs = seprint(bs, be, "%s", s1);
 	return bs;
@@ -1957,13 +1907,14 @@
 
 	if(s0)
 		bs = seprint(bs, be, "%s", s0);
-	bs = seprint(bs, be, "[");
 	if(b == nil)
 		bs = seprint(bs, be, "nil");
-	else
+	else {
+		bs = seprint(bs, be, "[ ");
 		for(i=0; i<b->len; i++)
 			bs = seprint(bs, be, "%x ", b->data[i]);
-	bs = seprint(bs, be, "]");
+		bs = seprint(bs, be, "]");
+	}
 	if(s1)
 		bs = seprint(bs, be, "%s", s1);
 	return bs;
@@ -2075,10 +2026,10 @@
 	va_end(arg);
 	if(c->trace)
 		c->trace("tlsError: %s\n", msg);
-	else if(c->erred)
+	if(c->erred)
 		fprint(2, "double error: %r, %s", msg);
 	else
-		werrstr("tls: local %s", msg);
+		errstr(msg, sizeof(msg));
 	c->erred = 1;
 	fprint(c->ctl, "alert %d", err);
 }
@@ -2115,9 +2066,18 @@
 {
 	if(c == nil)
 		return;
+
+	dh_finish(&c->sec->dh, nil);
+
+	mpfree(c->sec->ec.Q.a.x);
+	mpfree(c->sec->ec.Q.a.y);
+	mpfree(c->sec->ec.Q.d);
+	ecdomfree(&c->sec->ec.dom);
+
 	factotum_rsa_close(c->sec->rpc);
 	rsapubfree(c->sec->rsapub);
 	freebytes(c->cert);
+
 	memset(c, 0, sizeof(*c));
 	free(c);
 }
@@ -2126,6 +2086,69 @@
 //================= cipher choices ========================
 
 static int
+isDHE(int tlsid)
+{
+	switch(tlsid){
+	case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ 	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ 	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ 	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+	case TLS_DHE_RSA_WITH_CHACHA20_POLY1305:
+	case GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305:
+		return 1;
+	}
+	return 0;
+}
+
+static int
+isECDHE(int tlsid)
+{
+	switch(tlsid){
+	case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+	case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+	case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+	case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+
+	case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+	case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+	case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+	case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+		return 1;
+	}
+	return 0;
+}
+
+static int
+isPSK(int tlsid)
+{
+	switch(tlsid){
+	case TLS_PSK_WITH_CHACHA20_POLY1305:
+	case TLS_PSK_WITH_AES_128_CBC_SHA256:
+	case TLS_PSK_WITH_AES_128_CBC_SHA:
+		return 1;
+	}
+	return 0;
+}
+
+static int
+isECDSA(int tlsid)
+{
+	switch(tlsid){
+	case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+	case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+	case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+		return 1;
+	}
+	return 0;
+}
+
+static int
 setAlgs(TlsConnection *c, int a)
 {
 	int i;
@@ -2147,15 +2170,14 @@
 static int
 okCipher(Ints *cv, int ispsk)
 {
-	int i, j, c;
+	int i, c;
 
-	for(i = 0; i < cv->len; i++) {
-		c = cv->data[i];
-		if(isDHE(c) || isECDHE(c) || isPSK(c) != ispsk)
-			continue;	/* TODO: not implemented for server */
-		for(j = 0; j < nelem(cipherAlgs); j++)
-			if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c)
-				return c;
+	for(i = 0; i < nelem(cipherAlgs); i++) {
+		c = cipherAlgs[i].tlsid;
+		if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c) || isPSK(c) != ispsk)
+			continue;
+		if(lookupid(cv, c) >= 0)
+			return c;
 	}
 	return -1;
 }
@@ -2163,14 +2185,12 @@
 static int
 okCompression(Bytes *cv)
 {
-	int i, j, c;
+	int i, c;
 
-	for(i = 0; i < cv->len; i++) {
-		c = cv->data[i];
-		for(j = 0; j < nelem(compressors); j++) {
-			if(compressors[j] == c)
-				return c;
-		}
+	for(i = 0; i < nelem(compressors); i++) {
+		c = compressors[i];
+		if(memchr(cv->data, c, cv->len) != nil)
+			return c;
 	}
 	return -1;
 }
@@ -2260,7 +2280,6 @@
 }
 
 
-
 //================= security functions ========================
 
 // given a public key, set up connection to factotum
@@ -2527,8 +2546,6 @@
 	p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen);
 }
 
-/* the keys are verified to have the same public components
- * and to function correctly with pkcs 1 encryption and decryption. */
 static void
 tlsSecInits(TlsSec *sec, int cvers, uchar *crandom)
 {
@@ -2561,12 +2578,64 @@
 	return 0;
 }
 
-static void
-tlsSecPSKs(TlsSec *sec)
+static Bytes*
+tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc)
 {
-	setMasterSecret(sec, newbytes(sec->psklen));
+	ECdomain *dom = &sec->ec.dom;
+	ECpriv *Q = &sec->ec.Q;
+	Bytes *par;
+	int n;
+
+	ecdominit(dom, nc->init);
+	memset(Q, 0, sizeof(*Q));
+	Q->a.x = mpnew(0);
+	Q->a.y = mpnew(0);
+	Q->d = mpnew(0);
+	ecgen(dom, Q);
+	n = 1 + 2*((mpsignif(dom->p)+7)/8);
+	par = newbytes(1+2+1+n);
+	par->data[0] = 3;
+	put16(par->data+1, nc->tlsid);
+	n = ecencodepub(dom, (ECpub*)Q, par->data+4, par->len-4);
+	par->data[3] = n;
+	par->len = 1+2+1+n;
+
+	return par;
 }
 
+static int
+tlsSecECDHEs2(TlsSec *sec, Bytes *Yc)
+{
+	ECdomain *dom = &sec->ec.dom;
+	ECpriv *Q = &sec->ec.Q;
+	ECpoint K;
+	ECpub *Y;
+
+	if(Yc == nil){
+		werrstr("no public key");
+		return -1;
+	}
+
+	if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){
+		werrstr("bad public key");
+		return -1;
+	}
+
+	memset(&K, 0, sizeof(K));
+	K.x = mpnew(0);
+	K.y = mpnew(0);
+
+	ecmul(dom, Y, Q->d, &K);
+	setMasterSecret(sec, mptobytes(K.x));
+
+	mpfree(K.x);
+	mpfree(K.y);
+
+	ecpubfree(Y);
+
+	return 0;
+}
+
 static void
 tlsSecInitc(TlsSec *sec, int cvers)
 {
@@ -2576,12 +2645,6 @@
 	genrandom(sec->crandom+4, RandomSize-4);
 }
 
-static void
-tlsSecPSKc(TlsSec *sec)
-{
-	setMasterSecret(sec, newbytes(sec->psklen));
-}
-
 static Bytes*
 tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert)
 {
@@ -2694,28 +2757,82 @@
 	freebytes(pm);
 }
 
-static mpint*
-bytestomp(Bytes* bytes)
+static int
+digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg)
 {
-	return betomp(bytes->data, bytes->len, nil);
+	int hashalg = (sigalg>>8) & 0xFF;
+	int digestlen;
+	Bytes *blob;
+
+	blob = newbytes(2*RandomSize + par->len);
+	memmove(blob->data+0*RandomSize, sec->crandom, RandomSize);
+	memmove(blob->data+1*RandomSize, sec->srandom, RandomSize);
+	memmove(blob->data+2*RandomSize, par->data, par->len);
+	if(hashalg == 0){
+		digestlen = MD5dlen+SHA1dlen;
+		md5(blob->data, blob->len, digest, nil);
+		sha1(blob->data, blob->len, digest+MD5dlen, nil);
+	} else {
+		digestlen = -1;
+		if(hashalg < nelem(hashfun) && hashfun[hashalg].fun != nil){
+			digestlen = hashfun[hashalg].len;
+			(*hashfun[hashalg].fun)(blob->data, blob->len, digest, nil);
+		}
+	}
+	freebytes(blob);
+	return digestlen;
 }
 
-/*
- * Convert mpint* to Bytes, putting high order byte first.
- */
-static Bytes*
-mptobytes(mpint* big)
+static char*
+verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg)
 {
-	Bytes* ans;
-	int n;
+	uchar digest[MAXdlen];
+	int digestlen;
+	ECdomain dom;
+	ECpub *ecpk;
+	RSApub *rsapk;
+	char *err;
 
-	n = (mpsignif(big)+7)/8;
-	if(n == 0) n = 1;
-	ans = newbytes(n);
-	mptober(big, ans->data, ans->len);
-	return ans;
+	if(par == nil || par->len <= 0)
+		return "no DH parameters";
+
+	if(sig == nil || sig->len <= 0){
+		if(sec->psklen > 0)
+			return nil;
+		return "no signature";
+	}
+
+	if(cert == nil)
+		return "no certificate";
+
+	digestlen = digestDHparams(sec, par, digest, sigalg);
+	if(digestlen <= 0)
+		return "unknown signature digest algorithm";
+	
+	switch(sigalg & 0xFF){
+	case 0x01:
+		rsapk = X509toRSApub(cert->data, cert->len, nil, 0);
+		if(rsapk == nil)
+			return "bad certificate";
+		err = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, rsapk);
+		rsapubfree(rsapk);
+		break;
+	case 0x03:
+		ecpk = X509toECpub(cert->data, cert->len, &dom);
+		if(ecpk == nil)
+			return "bad certificate";
+		err = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, &dom, ecpk);
+		ecdomfree(&dom);
+		ecpubfree(ecpk);
+		break;
+	default:
+		err = "signaure algorithm not RSA or ECDSA";
+	}
+
+	return err;
 }
 
+
 // Do RSA computation on block according to key, and pad
 // result on left with zeros to make it modlen long.
 static Bytes*
@@ -2816,7 +2933,33 @@
 	return nil;
 }
 
+static Bytes*
+pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg)
+{
+	int hashalg = (sigalg>>8)&0xFF;
+	mpint *signedMP;
+	Bytes *signature;
+	uchar buf[128];
 
+	if(hashalg > 0 && hashalg < nelem(hashfun) && hashfun[hashalg].len == digestlen)
+		digestlen = asn1encodedigest(hashfun[hashalg].fun, digest, buf, sizeof(buf));
+	else if(digestlen == MD5dlen+SHA1dlen)
+		memmove(buf, digest, digestlen);
+	else
+		digestlen = -1;
+	if(digestlen <= 0){
+		werrstr("bad digest algorithm");
+		return nil;
+	}
+	signedMP = factotum_rsa_decrypt(sec->rpc, pkcs1padbuf(buf, digestlen, sec->rsapub->n));
+	if(signedMP == nil)
+		return nil;
+	signature = mptobytes(signedMP);
+	mpfree(signedMP);
+	return signature;
+}
+
+
 //================= general utility functions ========================
 
 static void *
@@ -2919,6 +3062,28 @@
 	free(b);
 }
 
+static mpint*
+bytestomp(Bytes* bytes)
+{
+	return betomp(bytes->data, bytes->len, nil);
+}
+
+/*
+ * Convert mpint* to Bytes, putting high order byte first.
+ */
+static Bytes*
+mptobytes(mpint* big)
+{
+	Bytes* ans;
+	int n;
+
+	n = (mpsignif(big)+7)/8;
+	if(n == 0) n = 1;
+	ans = newbytes(n);
+	mptober(big, ans->data, ans->len);
+	return ans;
+}
+
 /* len is number of ints */
 static Ints*
 newints(int len)
@@ -2936,4 +3101,15 @@
 freeints(Ints* b)
 {
 	free(b);
+}
+
+static int
+lookupid(Ints* b, int id)
+{
+	int i;
+
+	for(i=0; i<b->len; i++)
+		if(b->data[i] == id)
+			return i;
+	return -1;
 }