shithub: drawterm

Download patch

ref: d6ca0cfb326c9ac13c1796de917cc0547d421626
parent: 445b6293d60eb6ebd6d361596cbbc92eb8e81dc8
author: cinap_lenrek <[email protected]>
date: Thu Feb 18 13:37:53 EST 2016

port 9front libauthsrv/libsec/libmp and implement dp9ik and rcpu support

--- a/Make.win32
+++ b/Make.win32
@@ -3,7 +3,7 @@
 # on another platform.  Otherwise the binaries are just
 # named gcc, etc.
 
-MING=i586-mingw32msvc-
+MING=i686-w64-mingw32-
 #MING=
 AR=$(MING)ar
 CC=$(MING)gcc
--- a/cpu-bl.c
+++ /dev/null
@@ -1,714 +1,0 @@
-/*
- * cpu.c - Make a connection to a cpu server
- *
- *	   Invoked by listen as 'cpu -R | -N service net netdir'
- *	    	   by users  as 'cpu [-h system] [-c cmd args ...]'
- */
-
-#include <u.h>
-#include <libc.h>
-#include <auth.h>
-#include <fcall.h>
-#include <authsrv.h>
-#include <libsec.h>
-#include "args.h"
-#include "drawterm.h"
-
-#define Maxfdata 8192
-#define MaxStr 128
-
-static void	fatal(int, char*, ...);
-static void	usage(void);
-static void	writestr(int, char*, char*, int);
-static int	readstr(int, char*, int);
-static char	*rexcall(int*, char*, char*);
-static char *keyspec = "";
-static AuthInfo *p9any(int);
-
-#define system csystem
-static char	*system;
-static int	cflag;
-extern int	dbg;
-
-static char	*srvname = "ncpu";
-static char	*ealgs = "rc4_256 sha1";
-
-/* message size for exportfs; may be larger so we can do big graphics in CPU window */
-static int	msgsize = Maxfdata+IOHDRSZ;
-
-/* authentication mechanisms */
-static int	netkeyauth(int);
-static int	netkeysrvauth(int, char*);
-static int	p9auth(int);
-static int	srvp9auth(int, char*);
-
-char *authserver;
-
-typedef struct AuthMethod AuthMethod;
-struct AuthMethod {
-	char	*name;			/* name of method */
-	int	(*cf)(int);		/* client side authentication */
-	int	(*sf)(int, char*);	/* server side authentication */
-} authmethod[] =
-{
-	{ "p9",		p9auth,		srvp9auth,},
-	{ "netkey",	netkeyauth,	netkeysrvauth,},
-//	{ "none",	noauth,		srvnoauth,},
-	{ nil,	nil}
-};
-AuthMethod *am = authmethod;	/* default is p9 */
-
-char *p9authproto = "p9any";
-
-int setam(char*);
-
-void
-exits(char *s)
-{
-	print("\ngoodbye\n");
-	for(;;) osyield();
-}
-
-void
-usage(void)
-{
-	fprint(2, "usage: drawterm [-a authserver] [-c cpuserver] [-s secstore] [-u user]\n");
-	exits("usage");
-}
-int fdd;
-
-int
-mountfactotum(void)
-{
-	int fd;
-	
-	if((fd = dialfactotum()) < 0)
-		return -1;
-	if(sysmount(fd, -1, "/mnt/factotum", MREPL, "") < 0){
-		fprint(2, "mount factotum: %r\n");
-		return -1;
-	}
-	if((fd = open("/mnt/factotum/ctl", OREAD)) < 0){
-		fprint(2, "open /mnt/factotum/ctl: %r\n");
-		return -1;
-	}
-	close(fd);
-	return 0;
-}
-
-void
-cpumain(int argc, char **argv)
-{
-	char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *err, *secstoreserver, *p, *s;
-	int fd, ms, data;
-
-	/* see if we should use a larger message size */
-	fd = open("/dev/draw", OREAD);
-	if(fd > 0){
-		ms = iounit(fd);
-		if(msgsize < ms+IOHDRSZ)
-			msgsize = ms+IOHDRSZ;
-		close(fd);
-	}
-
-        user = getenv("USER");
-        if(user == nil)
-        	user = readcons("user", nil, 0);
-	secstoreserver = nil;
-	authserver = getenv("auth");
-	if(authserver == nil)
-		authserver = "lookout.cs.bell-labs.com";
-	system = getenv("cpu");
-	if(system == nil)
-		system = "anna.cs.bell-labs.com";
-	ARGBEGIN{
-	case 'o':
-		authserver = "plan9.bell-labs.com";
-		system = "plan9.bell-labs.com";
-		break;
-	case 'a':
-		authserver = EARGF(usage());
-		break;
-	case 'c':
-		system = EARGF(usage());
-		break;
-	case 'd':
-		dbg++;
-		break;
-	case 'e':
-		ealgs = EARGF(usage());
-		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
-			ealgs = nil;
-		break;
-	case 'C':
-		cflag++;
-		cmd[0] = '!';
-		cmd[1] = '\0';
-		while((p = ARGF()) != nil) {
-			strcat(cmd, " ");
-			strcat(cmd, p);
-		}
-		break;
-	case 'k':
-		keyspec = EARGF(usage());
-		break;
-	case 'u':
-		user = EARGF(usage());
-		break;
-	case 's':
-		secstoreserver = EARGF(usage());
-		break;
-	default:
-		usage();
-	}ARGEND;
-
-	if(argc != 0)
-		usage();
-
-	if(mountfactotum() < 0){
-		if(secstoreserver == nil)
-			secstoreserver = authserver;
-	        if(havesecstore(secstoreserver, user)){
-	                s = secstorefetch(secstoreserver, user, nil);
-	                if(s){
-	                        if(strlen(s) >= sizeof secstorebuf)
-	                                sysfatal("secstore data too big");
-	                        strcpy(secstorebuf, s);
-	                }
-	        }
-	}
-
-	if((err = rexcall(&data, system, srvname)))
-		fatal(1, "%s: %s", err, system);
-
-	/* Tell the remote side the command to execute and where our working directory is */
-	if(cflag)
-		writestr(data, cmd, "command", 0);
-	if(getcwd(dat, sizeof(dat)) == 0)
-		writestr(data, "NO", "dir", 0);
-	else
-		writestr(data, dat, "dir", 0);
-
-	/* 
-	 *  Wait for the other end to execute and start our file service
-	 *  of /mnt/term
-	 */
-	if(readstr(data, buf, sizeof(buf)) < 0)
-		fatal(1, "waiting for FS: %r");
-	if(strncmp("FS", buf, 2) != 0) {
-		print("remote cpu: %s", buf);
-		exits(buf);
-	}
-
-	if(readstr(data, buf, sizeof buf) < 0)
-		fatal(1, "waiting for remote export: %r");
-	if(strcmp(buf, "/") != 0){
-		print("remote cpu: %s" , buf);
-		exits(buf);
-	}
-	write(data, "OK", 2);
-
-	/* Begin serving the gnot namespace */
-	exportfs(data, msgsize);
-	fatal(1, "starting exportfs");
-}
-
-void
-fatal(int syserr, char *fmt, ...)
-{
-	Fmt f;
-	char *str;
-	va_list arg;
-
-	fmtstrinit(&f);
-	fmtprint(&f, "cpu: ");
-	va_start(arg, fmt);
-	fmtvprint(&f, fmt, arg);
-	va_end(arg);
-	if(syserr)
-		fmtprint(&f, ": %r");
-	fmtprint(&f, "\n");
-	str = fmtstrflush(&f);
-	write(2, str, strlen(str));
-	exits(str);
-}
-
-char *negstr = "negotiating authentication method";
-
-char bug[256];
-
-char*
-rexcall(int *fd, char *host, char *service)
-{
-	char *na;
-	char dir[MaxStr];
-	char err[ERRMAX];
-	char msg[MaxStr];
-	int n;
-
-	na = netmkaddr(host, "tcp", "17010");
-	if((*fd = dial(na, 0, dir, 0)) < 0)
-		return "can't dial";
-
-	/* negotiate authentication mechanism */
-	if(ealgs != nil)
-		snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);
-	else
-		snprint(msg, sizeof(msg), "%s", am->name);
-	writestr(*fd, msg, negstr, 0);
-	n = readstr(*fd, err, sizeof err);
-	if(n < 0)
-		return negstr;
-	if(*err){
-		werrstr(err);
-		return negstr;
-	}
-
-	/* authenticate */
-	*fd = (*am->cf)(*fd);
-	if(*fd < 0)
-		return "can't authenticate";
-	return 0;
-}
-
-void
-writestr(int fd, char *str, char *thing, int ignore)
-{
-	int l, n;
-
-	l = strlen(str);
-	n = write(fd, str, l+1);
-	if(!ignore && n < 0)
-		fatal(1, "writing network: %s", thing);
-}
-
-int
-readstr(int fd, char *str, int len)
-{
-	int n;
-
-	while(len) {
-		n = read(fd, str, 1);
-		if(n < 0) 
-			return -1;
-		if(*str == '\0')
-			return 0;
-		str++;
-		len--;
-	}
-	return -1;
-}
-
-static int
-readln(char *buf, int n)
-{
-	int i;
-	char *p;
-
-	n--;	/* room for \0 */
-	p = buf;
-	for(i=0; i<n; i++){
-		if(read(0, p, 1) != 1)
-			break;
-		if(*p == '\n' || *p == '\r')
-			break;
-		p++;
-	}
-	*p = '\0';
-	return p-buf;
-}
-
-/*
- *  user level challenge/response
- */
-static int
-netkeyauth(int fd)
-{
-	char chall[32];
-	char resp[32];
-
-	strecpy(chall, chall+sizeof chall, getuser());
-	print("user[%s]: ", chall);
-	if(readln(resp, sizeof(resp)) < 0)
-		return -1;
-	if(*resp != 0)
-		strcpy(chall, resp);
-	writestr(fd, chall, "challenge/response", 1);
-
-	for(;;){
-		if(readstr(fd, chall, sizeof chall) < 0)
-			break;
-		if(*chall == 0)
-			return fd;
-		print("challenge: %s\nresponse: ", chall);
-		if(readln(resp, sizeof(resp)) < 0)
-			break;
-		writestr(fd, resp, "challenge/response", 1);
-	}
-	return -1;
-}
-
-static int
-netkeysrvauth(int fd, char *user)
-{
-	return -1;
-}
-
-static void
-mksecret(char *t, uchar *f)
-{
-	sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
-		f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
-}
-
-/*
- *  plan9 authentication followed by rc4 encryption
- */
-static int
-p9auth(int fd)
-{
-	uchar key[16];
-	uchar digest[SHA1dlen];
-	char fromclientsecret[21];
-	char fromserversecret[21];
-	int i;
-	AuthInfo *ai;
-
-	ai = p9any(fd);
-	if(ai == nil)
-		return -1;
-	memmove(key+4, ai->secret, ai->nsecret);
-	if(ealgs == nil)
-		return fd;
-
-	/* exchange random numbers */
-	for(i = 0; i < 4; i++)
-		key[i] = fastrand();
-	if(write(fd, key, 4) != 4)
-		return -1;
-	if(readn(fd, key+12, 4) != 4)
-		return -1;
-
-	/* scramble into two secrets */
-	sha1(key, sizeof(key), digest, nil);
-	mksecret(fromclientsecret, digest);
-	mksecret(fromserversecret, digest+10);
-
-	/* set up encryption */
-	i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
-	if(i < 0)
-		werrstr("can't establish ssl connection: %r");
-	return i;
-}
-
-int
-authdial(char *net, char *dom)
-{
-	int fd;
-	fd = dial(netmkaddr(authserver, "tcp", "567"), 0, 0, 0);
-	//print("authdial %d\n", fd);
-	return fd;
-}
-
-static int
-getastickets(Ticketreq *tr, char *trbuf, char *tbuf)
-{
-	int asfd, rv;
-	char *dom;
-
-	dom = tr->authdom;
-	asfd = authdial(nil, dom);
-	if(asfd < 0)
-		return -1;
-	rv = _asgetticket(asfd, trbuf, tbuf);
-	close(asfd);
-	return rv;
-}
-
-static int
-mkserverticket(Ticketreq *tr, char *authkey, char *tbuf)
-{
-	int i;
-	Ticket t;
-
-	if(strcmp(tr->authid, tr->hostid) != 0)
-		return -1;
-	memset(&t, 0, sizeof(t));
-	memmove(t.chal, tr->chal, CHALLEN);
-	strcpy(t.cuid, tr->uid);
-	strcpy(t.suid, tr->uid);
-	for(i=0; i<DESKEYLEN; i++)
-		t.key[i] = fastrand();
-	t.num = AuthTc;
-	convT2M(&t, tbuf, authkey);
-	t.num = AuthTs;
-	convT2M(&t, tbuf+TICKETLEN, authkey);
-	return 0;
-}
-
-static int
-gettickets(Ticketreq *tr, char *key, char *trbuf, char *tbuf)
-{
-	if(getastickets(tr, trbuf, tbuf) >= 0)
-		return 0;
-	return mkserverticket(tr, key, tbuf);
-}
-
-/*
- *  prompt user for a key.  don't care about memory leaks, runs standalone
- */
-static Attr*
-promptforkey(char *params)
-{
-	char *v;
-	int fd;
-	Attr *a, *attr;
-	char *def;
-
-	fd = open("/dev/cons", ORDWR);
-	if(fd < 0)
-		sysfatal("opening /dev/cons: %r");
-
-	attr = _parseattr(params);
-	fprint(fd, "\n!Adding key:");
-	for(a=attr; a; a=a->next)
-		if(a->type != AttrQuery && a->name[0] != '!')
-			fprint(fd, " %q=%q", a->name, a->val);
-	fprint(fd, "\n");
-
-	for(a=attr; a; a=a->next){
-		v = a->name;
-		if(a->type != AttrQuery || v[0]=='!')
-			continue;
-		def = nil;
-		if(strcmp(v, "user") == 0)
-			def = getuser();
-		a->val = readcons(v, def, 0);
-		if(a->val == nil)
-			sysfatal("user terminated key input");
-		a->type = AttrNameval;
-	}
-	for(a=attr; a; a=a->next){
-		v = a->name;
-		if(a->type != AttrQuery || v[0]!='!')
-			continue;
-		def = nil;
-		if(strcmp(v+1, "user") == 0)
-			def = getuser();
-		a->val = readcons(v+1, def, 1);
-		if(a->val == nil)
-			sysfatal("user terminated key input");
-		a->type = AttrNameval;
-	}
-	fprint(fd, "!\n");
-	close(fd);
-	return attr;
-}
-
-/*
- *  send a key to the mounted factotum
- */
-static int
-sendkey(Attr *attr)
-{
-	int fd, rv;
-	char buf[1024];
-
-	fd = open("/mnt/factotum/ctl", ORDWR);
-	if(fd < 0)
-		sysfatal("opening /mnt/factotum/ctl: %r");
-	rv = fprint(fd, "key %A\n", attr);
-	read(fd, buf, sizeof buf);
-	close(fd);
-	return rv;
-}
-
-int
-askuser(char *params)
-{
-	Attr *attr;
-	
-	fmtinstall('A', _attrfmt);
-	
-	attr = promptforkey(params);
-	if(attr == nil)
-		sysfatal("no key supplied");
-	if(sendkey(attr) < 0)
-		sysfatal("sending key to factotum: %r");
-	return 0;
-}
-
-AuthInfo*
-p9anyfactotum(int fd, int afd)
-{
-	return auth_proxy(fd, askuser, "proto=p9any role=client %s", keyspec);
-}
-
-AuthInfo*
-p9any(int fd)
-{
-	char buf[1024], buf2[1024], cchal[CHALLEN], *bbuf, *p, *dom, *u;
-	char *pass;
-	char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN];
-	char authkey[DESKEYLEN];
-	Authenticator auth;
-	int afd, i, v2;
-	Ticketreq tr;
-	Ticket t;
-	AuthInfo *ai;
-
-	if((afd = open("/mnt/factotum/ctl", ORDWR)) >= 0)
-		return p9anyfactotum(fd, afd);
-
-	if(readstr(fd, buf, sizeof buf) < 0)
-		fatal(1, "cannot read p9any negotiation");
-	bbuf = buf;
-	v2 = 0;
-	if(strncmp(buf, "v.2 ", 4) == 0){
-		v2 = 1;
-		bbuf += 4;
-	}
-	if((p = strchr(bbuf, ' ')))
-		*p = 0;
-	p = bbuf;
-	if((dom = strchr(p, '@')) == nil)
-		fatal(1, "bad p9any domain");
-	*dom++ = 0;
-	if(strcmp(p, "p9sk1") != 0)
-		fatal(1, "server did not offer p9sk1");
-
-	sprint(buf2, "%s %s", p, dom);
-	if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1)
-		fatal(1, "cannot write user/domain choice in p9any");
-	if(v2){
-		if(readstr(fd, buf, sizeof buf) != 3)
-			fatal(1, "cannot read OK in p9any");
-		if(memcmp(buf, "OK\0", 3) != 0)
-			fatal(1, "did not get OK in p9any");
-	}
-	for(i=0; i<CHALLEN; i++)
-		cchal[i] = fastrand();
-	if(write(fd, cchal, 8) != 8)
-		fatal(1, "cannot write p9sk1 challenge");
-
-	if(readn(fd, trbuf, TICKREQLEN) != TICKREQLEN)
-		fatal(1, "cannot read ticket request in p9sk1");
-
-
-	convM2TR(trbuf, &tr);
-	u = user;
-	pass = findkey(&u, tr.authdom);
-	if(pass == nil)
-	again:
-		pass = getkey(u, tr.authdom);
-	if(pass == nil)
-		fatal(1, "no password");
-
-	passtokey(authkey, pass);
-	memset(pass, 0, strlen(pass));
-
-	tr.type = AuthTreq;
-	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, u);
-	strecpy(tr.uid, tr.uid+sizeof tr.uid, u);
-	convTR2M(&tr, trbuf);
-
-	if(gettickets(&tr, authkey, trbuf, tbuf) < 0)
-		fatal(1, "cannot get auth tickets in p9sk1");
-
-	convM2T(tbuf, &t, authkey);
-	if(t.num != AuthTc){
-		print("?password mismatch with auth server\n");
-		goto again;
-	}
-	memmove(tbuf, tbuf+TICKETLEN, TICKETLEN);
-
-	auth.num = AuthAc;
-	memmove(auth.chal, tr.chal, CHALLEN);
-	auth.id = 0;
-	convA2M(&auth, tbuf+TICKETLEN, t.key);
-
-	if(write(fd, tbuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
-		fatal(1, "cannot send ticket and authenticator back in p9sk1");
-
-	if(readn(fd, tbuf, AUTHENTLEN) != AUTHENTLEN)
-		fatal(1, "cannot read authenticator in p9sk1");
-	
-	convM2A(tbuf, &auth, t.key);
-	if(auth.num != AuthAs
-	|| memcmp(auth.chal, cchal, CHALLEN) != 0
-	|| auth.id != 0){
-		print("?you and auth server agree about password.\n");
-		print("?server is confused.\n");
-		fatal(1, "server lies got %llux.%d want %llux.%d", *(vlong*)auth.chal, auth.id, *(vlong*)cchal, 0);
-	}
-	//print("i am %s there.\n", t.suid);
-	ai = mallocz(sizeof(AuthInfo), 1);
-	ai->secret = mallocz(8, 1);
-	des56to64((uchar*)t.key, ai->secret);
-	ai->nsecret = 8;
-	ai->suid = strdup(t.suid);
-	ai->cuid = strdup(t.cuid);
-	memset(authkey, 0, sizeof authkey);
-	return ai;
-}
-
-/*
-static int
-noauth(int fd)
-{
-	ealgs = nil;
-	return fd;
-}
-
-static int
-srvnoauth(int fd, char *user)
-{
-	strecpy(user, user+MaxStr, getuser());
-	ealgs = nil;
-	return fd;
-}
-*/
-
-void
-loghex(uchar *p, int n)
-{
-	char buf[100];
-	int i;
-
-	for(i = 0; i < n; i++)
-		sprint(buf+2*i, "%2.2ux", p[i]);
-//	syslog(0, "cpu", buf);
-}
-
-static int
-srvp9auth(int fd, char *user)
-{
-	return -1;
-}
-
-/*
- *  set authentication mechanism
- */
-int
-setam(char *name)
-{
-	for(am = authmethod; am->name != nil; am++)
-		if(strcmp(am->name, name) == 0)
-			return 0;
-	am = authmethod;
-	return -1;
-}
-
-/*
- *  set authentication mechanism and encryption/hash algs
- *
-int
-setamalg(char *s)
-{
-	ealgs = strchr(s, ' ');
-	if(ealgs != nil)
-		*ealgs++ = 0;
-	return setam(s);
-}
-
-*/
--- a/cpu.c
+++ b/cpu.c
@@ -38,8 +38,6 @@
 static int	msgsize = Maxfdata+IOHDRSZ;
 
 /* authentication mechanisms */
-static int	netkeyauth(int);
-static int	netkeysrvauth(int, char*);
 static int	p9auth(int);
 static int	srvp9auth(int, char*);
 
@@ -53,8 +51,6 @@
 } authmethod[] =
 {
 	{ "p9",		p9auth,		srvp9auth,},
-	{ "netkey",	netkeyauth,	netkeysrvauth,},
-//	{ "none",	noauth,		srvnoauth,},
 	{ 0 }
 };
 AuthMethod *am = authmethod;	/* default is p9 */
@@ -98,6 +94,47 @@
 }
 
 void
+rcpu(char *host)
+{
+	static char script[] = 
+"mount -nc /fd/0 /mnt/term || exit	\n"
+"bind -q /mnt/term/dev/cons /dev/cons	\n"
+"</dev/cons >/dev/cons >[2=1] {		\n"
+"	service=cpu exec rc -li		\n"
+"}					\n";
+	AuthInfo *ai;
+	TLSconn *conn;
+	char *na;
+	int fd;
+
+	na = netmkaddr(host, "tcp", "17019");
+	if((fd = dial(na, 0, 0, 0)) < 0)
+		return;
+
+	ai = p9any(fd);
+	if(ai == nil)
+		fatal(1, "can't authenticate");
+
+	conn = mallocz(sizeof(TLSconn), 1);
+	conn->pskID = "p9secret";
+	conn->psk = ai->secret;
+	conn->psklen = ai->nsecret;
+
+	fd = tlsClient(fd, conn);
+	if(fd < 0)
+		fatal(1, "tlsClient");
+
+	auth_freeAI(ai);
+
+	if(fprint(fd, "%7ld\n%s", strlen(script), script) < 0)
+		fatal(1, "sending script");
+
+	/* Begin serving the namespace */
+	exportfs(fd, msgsize);
+	fatal(1, "starting exportfs");
+}
+
+void
 cpumain(int argc, char **argv)
 {
 	char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *err, *secstoreserver, *p, *s;
@@ -179,6 +216,8 @@
 		}
 	}
 
+	rcpu(system);
+
 	if((err = rexcall(&data, system, srvname)))
 		fatal(1, "%s: %s", err, system);
 
@@ -300,61 +339,6 @@
 	return -1;
 }
 
-static int
-readln(char *buf, int n)
-{
-	int i;
-	char *p;
-
-	n--;	/* room for \0 */
-	p = buf;
-	for(i=0; i<n; i++){
-		if(read(0, p, 1) != 1)
-			break;
-		if(*p == '\n' || *p == '\r')
-			break;
-		p++;
-	}
-	*p = '\0';
-	return p-buf;
-}
-
-/*
- *  user level challenge/response
- */
-static int
-netkeyauth(int fd)
-{
-	char chall[32];
-	char resp[32];
-
-	strecpy(chall, chall+sizeof chall, getuser());
-	print("user[%s]: ", chall);
-	if(readln(resp, sizeof(resp)) < 0)
-		return -1;
-	if(*resp != 0)
-		strcpy(chall, resp);
-	writestr(fd, chall, "challenge/response", 1);
-
-	for(;;){
-		if(readstr(fd, chall, sizeof chall) < 0)
-			break;
-		if(*chall == 0)
-			return fd;
-		print("challenge: %s\nresponse: ", chall);
-		if(readln(resp, sizeof(resp)) < 0)
-			break;
-		writestr(fd, resp, "challenge/response", 1);
-	}
-	return -1;
-}
-
-static int
-netkeysrvauth(int fd, char *user)
-{
-	return -1;
-}
-
 static void
 mksecret(char *t, uchar *f)
 {
@@ -378,13 +362,17 @@
 	ai = p9any(fd);
 	if(ai == nil)
 		return -1;
-	memmove(key+4, ai->secret, ai->nsecret);
 	if(ealgs == nil)
 		return fd;
 
+	if(ai->nsecret < 8){
+		werrstr("secret too small");
+		return -1;
+	}
+	memmove(key+4, ai->secret, 8);
+
 	/* exchange random numbers */
-	for(i = 0; i < 4; i++)
-		key[i] = fastrand();
+	genrandom(key, 4);
 	if(write(fd, key, 4) != 4)
 		return -1;
 	if(readn(fd, key+12, 4) != 4)
@@ -412,7 +400,7 @@
 }
 
 static int
-getastickets(Ticketreq *tr, char *trbuf, char *tbuf)
+getastickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
 {
 	int asfd, rv;
 	char *dom;
@@ -421,38 +409,71 @@
 	asfd = authdial(nil, dom);
 	if(asfd < 0)
 		return -1;
-	rv = _asgetticket(asfd, trbuf, tbuf);
+	if(y != nil){
+		PAKpriv p;
+
+		rv = -1;
+		tr->type = AuthPAK;
+		if(_asrequest(asfd, tr) != 0 || write(asfd, y, PAKYLEN) != PAKYLEN)
+			goto Out;
+
+		authpak_new(&p, key, (uchar*)tbuf, 1);
+		if(write(asfd, tbuf, PAKYLEN) != PAKYLEN)
+			goto Out;
+
+		if(_asrdresp(asfd, tbuf, 2*PAKYLEN) != 2*PAKYLEN)
+			goto Out;
+	
+		memmove(y, tbuf, PAKYLEN);
+		if(authpak_finish(&p, key, (uchar*)tbuf+PAKYLEN))
+			goto Out;
+	}
+	tr->type = AuthTreq;
+	rv = _asgetticket(asfd, tr, tbuf, tbuflen);
+Out:
 	close(asfd);
 	return rv;
 }
 
 static int
-mkserverticket(Ticketreq *tr, char *authkey, char *tbuf)
+mkservertickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
 {
-	int i;
 	Ticket t;
+	int ret;
 
 	if(strcmp(tr->authid, tr->hostid) != 0)
 		return -1;
 	memset(&t, 0, sizeof(t));
+	ret = 0;
+	if(y != nil){
+		PAKpriv p;
+
+		t.form = 1;
+		memmove(tbuf, y, PAKYLEN);
+		authpak_new(&p, key, y, 0);
+		authpak_finish(&p, key, (uchar*)tbuf);
+	}
 	memmove(t.chal, tr->chal, CHALLEN);
 	strcpy(t.cuid, tr->uid);
 	strcpy(t.suid, tr->uid);
-	for(i=0; i<DESKEYLEN; i++)
-		t.key[i] = fastrand();
+	genrandom((uchar*)t.key, sizeof(t.key));
 	t.num = AuthTc;
-	convT2M(&t, tbuf, authkey);
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
 	t.num = AuthTs;
-	convT2M(&t, tbuf+TICKETLEN, authkey);
-	return 0;
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
+	memset(&t, 0, sizeof(t));
+
+	return ret;
 }
 
 static int
-gettickets(Ticketreq *tr, char *key, char *trbuf, char *tbuf)
+gettickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
 {
-	if(getastickets(tr, trbuf, tbuf) >= 0)
-		return 0;
-	return mkserverticket(tr, key, tbuf);
+	int ret;
+	ret = getastickets(key, tr, y, tbuf, tbuflen);
+	if(ret > 0)
+		return ret;
+	return mkservertickets(key, tr, y, tbuf, tbuflen);
 }
 
 /*
@@ -548,12 +569,13 @@
 AuthInfo*
 p9any(int fd)
 {
-	char buf[1024], buf2[1024], cchal[CHALLEN], *bbuf, *p, *dom, *u;
+	char buf[1024], buf2[1024], *bbuf, *p, *proto, *dom, *u;
 	char *pass;
-	char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN];
-	char authkey[DESKEYLEN];
+	uchar crand[2*NONCELEN], cchal[CHALLEN], y[PAKYLEN];
+	char tbuf[2*MAXTICKETLEN+MAXAUTHENTLEN+PAKYLEN], trbuf[TICKREQLEN+PAKYLEN];
+	Authkey authkey;
 	Authenticator auth;
-	int afd, i, n, v2;
+	int afd, i, n, m, v2, dp9ik;
 	Ticketreq tr;
 	Ticket t;
 	AuthInfo *ai;
@@ -570,16 +592,26 @@
 		v2 = 1;
 		bbuf += 4;
 	}
-	if((p = strchr(bbuf, ' ')))
-		*p = 0;
-	p = bbuf;
-	if((dom = strchr(p, '@')) == nil)
-		fatal(1, "bad p9any domain");
-	*dom++ = 0;
-	if(strcmp(p, "p9sk1") != 0)
-		fatal(1, "server did not offer p9sk1");
-
-	sprint(buf2, "%s %s", p, dom);
+	dp9ik = 0;
+	proto = nil;
+	while(bbuf != nil){
+		if((p = strchr(bbuf, ' ')))
+			*p++ = 0;
+		if((dom = strchr(bbuf, '@')) == nil)
+			fatal(1, "bad p9any domain");
+		*dom++ = 0;
+		if(strcmp(bbuf, "p9sk1") == 0 || strcmp(bbuf, "dp9ik") == 0){
+			proto = bbuf;
+			if(strcmp(proto, "dp9ik") == 0){
+				dp9ik = 1;
+				break;
+			}
+		}
+		bbuf = p;
+	}
+	if(proto == nil)
+		fatal(1, "server did not offer p9sk1 or dp9ik");
+	sprint(buf2, "%s %s", proto, dom);
 	if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1)
 		fatal(1, "cannot write user/domain choice in p9any");
 	if(v2){
@@ -588,16 +620,18 @@
 		if(memcmp(buf, "OK\0", 3) != 0)
 			fatal(1, "did not get OK in p9any");
 	}
-	for(i=0; i<CHALLEN; i++)
-		cchal[i] = fastrand();
-	if(write(fd, cchal, 8) != 8)
+	genrandom(crand, 2*NONCELEN);
+	genrandom(cchal, CHALLEN);
+	if(write(fd, cchal, CHALLEN) != CHALLEN)
 		fatal(1, "cannot write p9sk1 challenge");
 
-	if(readn(fd, trbuf, TICKREQLEN) != TICKREQLEN)
+	n = TICKREQLEN;
+	if(dp9ik)
+		n += PAKYLEN;
+
+	if(readn(fd, trbuf, n) != n || convM2TR(trbuf, TICKREQLEN, &tr) <= 0)
 		fatal(1, "cannot read ticket request in p9sk1");
 
-
-	convM2TR(trbuf, &tr);
 	u = user;
 	pass = findkey(&u, tr.authdom);
 	if(pass == nil)
@@ -606,36 +640,45 @@
 	if(pass == nil)
 		fatal(1, "no password");
 
-	passtokey(authkey, pass);
+	passtokey(&authkey, pass);
 	memset(pass, 0, strlen(pass));
 
-	tr.type = AuthTreq;
 	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, u);
 	strecpy(tr.uid, tr.uid+sizeof tr.uid, u);
-	convTR2M(&tr, trbuf);
 
-	if(gettickets(&tr, authkey, trbuf, tbuf) < 0)
+	if(dp9ik){
+		authpak_hash(&authkey, tr.hostid);
+		memmove(y, trbuf+TICKREQLEN, PAKYLEN);
+		n = gettickets(&authkey, &tr, y, tbuf, sizeof(tbuf));
+	} else {
+		n = gettickets(&authkey, &tr, nil, tbuf, sizeof(tbuf));
+	}
+	if(n <= 0)
 		fatal(1, "cannot get auth tickets in p9sk1");
 
-	convM2T(tbuf, &t, authkey);
-	if(t.num != AuthTc){
+	m = convM2T(tbuf, n, &t, &authkey);
+	if(m <= 0 || t.num != AuthTc){
 		print("?password mismatch with auth server\n");
 		goto again;
 	}
-	memmove(tbuf, tbuf+TICKETLEN, TICKETLEN);
+	n -= m;
+	memmove(tbuf, tbuf+m, n);
 
+	if(dp9ik && write(fd, y, PAKYLEN) != PAKYLEN)
+		fatal(1, "cannot send authpak public key back");
+
 	auth.num = AuthAc;
+	memmove(auth.rand, crand, NONCELEN);
 	memmove(auth.chal, tr.chal, CHALLEN);
-	auth.id = 0;
-	convA2M(&auth, tbuf+TICKETLEN, t.key);
+	m = convA2M(&auth, tbuf+n, sizeof(tbuf)-n, &t);
+	n += m;
 
-	if(write(fd, tbuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
-		fatal(1, "cannot send ticket and authenticator back in p9sk1");
+	if(write(fd, tbuf, n) != n)
+		fatal(1, "cannot send ticket and authenticator back");
 
-	if((n=readn(fd, tbuf, AUTHENTLEN)) != AUTHENTLEN ||
-			memcmp(tbuf, "cpu:", 4) == 0){
+	if((n=readn(fd, tbuf, m)) != m || memcmp(tbuf, "cpu:", 4) == 0){
 		if(n <= 4)
-			fatal(1, "cannot read authenticator in p9sk1");
+			fatal(1, "cannot read authenticator");
 
 		/*
 		 * didn't send back authenticator:
@@ -650,53 +693,43 @@
 		fatal(0, "server says: %s", buf);
 	}
 	
-	convM2A(tbuf, &auth, t.key);
-	if(auth.num != AuthAs
-	|| memcmp(auth.chal, cchal, CHALLEN) != 0
-	|| auth.id != 0){
+	if(convM2A(tbuf, n, &auth, &t) <= 0
+	|| auth.num != AuthAs || tsmemcmp(auth.chal, cchal, CHALLEN) != 0){
 		print("?you and auth server agree about password.\n");
 		print("?server is confused.\n");
-		fatal(0, "server lies got %llux.%d want %llux.%d", *(vlong*)auth.chal, auth.id, *(vlong*)cchal, 0);
+		fatal(0, "server lies got %llux want %llux", *(vlong*)auth.chal, *(vlong*)cchal);
 	}
-	//print("i am %s there.\n", t.suid);
+	memmove(crand+NONCELEN, auth.rand, NONCELEN);
+
+	// print("i am %s there.\n", t.suid);
+
 	ai = mallocz(sizeof(AuthInfo), 1);
-	ai->secret = mallocz(8, 1);
-	des56to64((uchar*)t.key, ai->secret);
-	ai->nsecret = 8;
 	ai->suid = strdup(t.suid);
 	ai->cuid = strdup(t.cuid);
-	memset(authkey, 0, sizeof authkey);
-	return ai;
-}
+	if(dp9ik){
+		static char info[] = "Plan 9 session secret";
+		ai->nsecret = 256;
+		ai->secret = mallocz(ai->nsecret, 1);
+		hkdf_x(	crand, 2*NONCELEN,
+			(uchar*)info, sizeof(info)-1,
+			(uchar*)t.key, NONCELEN,
+			ai->secret, ai->nsecret,
+			hmac_sha2_256, SHA2_256dlen);
+	} else {
+		ai->nsecret = 8;
+		ai->secret = mallocz(ai->nsecret, 1);
+		des56to64((uchar*)t.key, ai->secret);
+	}
 
-/*
-static int
-noauth(int fd)
-{
-	ealgs = nil;
-	return fd;
-}
+	memset(&t, 0, sizeof(t));
+	memset(&auth, 0, sizeof(auth));
+	memset(&authkey, 0, sizeof(authkey));
+	memset(cchal, 0, sizeof(cchal));
+	memset(crand, 0, sizeof(crand));
 
-static int
-srvnoauth(int fd, char *user)
-{
-	strecpy(user, user+MaxStr, getuser());
-	ealgs = nil;
-	return fd;
+	return ai;
 }
-*/
 
-void
-loghex(uchar *p, int n)
-{
-	char buf[100];
-	int i;
-
-	for(i = 0; i < n; i++)
-		sprint(buf+2*i, "%2.2ux", p[i]);
-//	syslog(0, "cpu", buf);
-}
-
 static int
 srvp9auth(int fd, char *user)
 {
@@ -715,17 +748,3 @@
 	am = authmethod;
 	return -1;
 }
-
-/*
- *  set authentication mechanism and encryption/hash algs
- *
-int
-setamalg(char *s)
-{
-	ealgs = strchr(s, ' ');
-	if(ealgs != nil)
-		*ealgs++ = 0;
-	return setam(s);
-}
-
-*/
--- a/include/authsrv.h
+++ b/include/authsrv.h
@@ -1,8 +1,3 @@
-#ifdef PLAN9
-#pragma	src	"/sys/src/libauthsrv"
-#pragma	lib	"libauthsrv.a"
-#endif
-
 /*
  * Interface for talking to authentication server.
  */
@@ -14,21 +9,35 @@
 typedef struct	OChapreply	OChapreply;
 typedef struct	OMSchapreply	OMSchapreply;
 
+typedef struct	Authkey		Authkey;
+
 enum
 {
-	ANAMELEN=	28,		/* maximum size of name in previous proto */
-	AERRLEN=	64,		/* maximum size of errstr in previous proto */
-	DOMLEN=		48,		/* length of an authentication domain name */
-	DESKEYLEN=	7,		/* length of a des key for encrypt/decrypt */
-	CHALLEN=	8,		/* length of a plan9 sk1 challenge */
-	NETCHLEN=	16,		/* max network challenge length (used in AS protocol) */
+	ANAMELEN=	28,	/* name max size in previous proto */
+	AERRLEN=	64,	/* errstr max size in previous proto */
+	DOMLEN=		48,	/* authentication domain name length */
+	DESKEYLEN=	7,	/* encrypt/decrypt des key length */
+	AESKEYLEN=	16,	/* encrypt/decrypt aes key length */
+
+	CHALLEN=	8,	/* plan9 sk1 challenge length */
+	NETCHLEN=	16,	/* max network challenge length (used in AS protocol) */
 	CONFIGLEN=	14,
-	SECRETLEN=	32,		/* max length of a secret */
+	SECRETLEN=	32,	/* secret max size */
 
-	KEYDBOFF=	8,		/* length of random data at the start of key file */
-	OKEYDBLEN=	ANAMELEN+DESKEYLEN+4+2,	/* length of an entry in old key file */
-	KEYDBLEN=	OKEYDBLEN+SECRETLEN,	/* length of an entry in key file */
+	NONCELEN=	32,
+
+	KEYDBOFF=	8,	/* bytes of random data at key file's start */
+	OKEYDBLEN=	ANAMELEN+DESKEYLEN+4+2,	/* old key file entry length */
+	KEYDBLEN=	OKEYDBLEN+SECRETLEN,	/* key file entry length */
 	OMD5LEN=	16,
+
+	/* AuthPAK constants */
+	PAKKEYLEN=	32,
+	PAKSLEN=	(448+7)/8,	/* ed448 scalar */
+	PAKPLEN=	4*PAKSLEN,	/* point in extended format X,Y,Z,T */
+	PAKHASHLEN=	2*PAKPLEN,	/* hashed points PM,PN */
+	PAKXLEN=	PAKSLEN,	/* random scalar secret key */ 
+	PAKYLEN=	PAKSLEN,	/* decaf encoded public key */
 };
 
 /* encryption numberings (anti-replay) */
@@ -47,8 +56,7 @@
 	AuthCram=12,	/* CRAM verification for IMAP (RFC2195 & rfc2104) */
 	AuthHttp=13,	/* http domain login */
 	AuthVNC=14,	/* VNC server login (deprecated) */
-
-
+	AuthPAK=19,	/* authenticated diffie hellman key agreement */
 	AuthTs=64,	/* ticket encrypted with server's key */
 	AuthTc,		/* ticket encrypted with client's key */
 	AuthAs,		/* server generated authenticator */
@@ -74,17 +82,19 @@
 	char	chal[CHALLEN];		/* server challenge */
 	char	cuid[ANAMELEN];		/* uid on client */
 	char	suid[ANAMELEN];		/* uid on server */
-	char	key[DESKEYLEN];		/* nonce DES key */
+	uchar	key[NONCELEN];		/* nonce key */
+
+	char	form;			/* (not transmitted) format (0 = des, 1 = ccpoly) */
 };
-#define	TICKETLEN	(CHALLEN+2*ANAMELEN+DESKEYLEN+1)
+#define	MAXTICKETLEN	(12+CHALLEN+2*ANAMELEN+NONCELEN+16)
 
 struct Authenticator
 {
 	char	num;			/* replay protection */
-	char	chal[CHALLEN];
-	ulong	id;			/* authenticator id, ++'d with each auth */
+	char	chal[CHALLEN];		/* server/client challenge */
+	uchar	rand[NONCELEN];		/* server/client nonce */
 };
-#define	AUTHENTLEN	(CHALLEN+4+1)
+#define	MAXAUTHENTLEN	(12+CHALLEN+NONCELEN+16)
 
 struct Passwordreq
 {
@@ -94,7 +104,7 @@
 	char	changesecret;
 	char	secret[SECRETLEN];	/* new secret */
 };
-#define	PASSREQLEN	(2*ANAMELEN+1+1+SECRETLEN)
+#define	MAXPASSREQLEN	(12+2*ANAMELEN+1+SECRETLEN+16)
 
 struct	OChapreply
 {
@@ -102,6 +112,7 @@
 	char	uid[ANAMELEN];
 	char	resp[OMD5LEN];
 };
+#define OCHAPREPLYLEN	(1+ANAMELEN+OMD5LEN)
 
 struct	OMSchapreply
 {
@@ -109,50 +120,70 @@
 	char	LMresp[24];		/* Lan Manager response */
 	char	NTresp[24];		/* NT response */
 };
+#define OMSCHAPREPLYLEN	(ANAMELEN+24+24)
 
+struct	Authkey
+{
+	char	des[DESKEYLEN];		/* DES key from password */
+	uchar	aes[AESKEYLEN];		/* AES key from password */
+	uchar	pakkey[PAKKEYLEN];	/* shared key from AuthPAK exchange (see authpak_finish()) */
+	uchar	pakhash[PAKHASHLEN];	/* secret hash from AES key and user name (see authpak_hash()) */
+};
+
 /*
  *  convert to/from wire format
  */
-extern	int	convT2M(Ticket*, char*, char*);
-extern	void	convM2T(char*, Ticket*, char*);
-extern	void	convM2Tnoenc(char*, Ticket*);
-extern	int	convA2M(Authenticator*, char*, char*);
-extern	void	convM2A(char*, Authenticator*, char*);
-extern	int	convTR2M(Ticketreq*, char*);
-extern	void	convM2TR(char*, Ticketreq*);
-extern	int	convPR2M(Passwordreq*, char*, char*);
-extern	void	convM2PR(char*, Passwordreq*, char*);
+extern	int	convT2M(Ticket*, char*, int, Authkey*);
+extern	int	convM2T(char*, int, Ticket*, Authkey*);
+extern	int	convA2M(Authenticator*, char*, int, Ticket*);
+extern	int	convM2A(char*, int, Authenticator*, Ticket*);
+extern	int	convTR2M(Ticketreq*, char*, int);
+extern	int	convM2TR(char*, int, Ticketreq*);
+extern	int	convPR2M(Passwordreq*, char*, int, Ticket*);
+extern	int	convM2PR(char*, int, Passwordreq*, Ticket*);
 
 /*
- *  convert ascii password to DES key
+ *  convert ascii password to auth key
  */
-extern	int	opasstokey(char*, char*);
-extern	int	passtokey(char*, char*);
+extern	void	passtokey(Authkey*, char*);
 
+extern	void	passtodeskey(char key[DESKEYLEN], char *p);
+extern	void	passtoaeskey(uchar key[AESKEYLEN], char *p);
+
 /*
  *  Nvram interface
  */
 enum {
-	NVwrite = 1<<0,		/* always prompt and rewrite nvram */
-	NVwriteonerr = 1<<1,	/* prompt and rewrite nvram when corrupt */
+	NVread		= 0,	/* just read */
+	NVwrite		= 1<<0,	/* always prompt and rewrite nvram */
+	NVwriteonerr	= 1<<1,	/* prompt and rewrite nvram when corrupt */
+	NVwritemem	= 1<<2,	/* don't prompt, write nvram from argument */
 };
 
+/* storage layout */
 struct Nvrsafe
 {
-	char	machkey[DESKEYLEN];
+	char	machkey[DESKEYLEN];	/* file server's authid's des key */
 	uchar	machsum;
-	char	authkey[DESKEYLEN];
+	char	authkey[DESKEYLEN];	/* authid's des key from password */
 	uchar	authsum;
+	/*
+	 * file server config string of device holding full configuration;
+	 * secstore key on non-file-servers.
+	 */
 	char	config[CONFIGLEN];
 	uchar	configsum;
-	char	authid[ANAMELEN];
+	char	authid[ANAMELEN];	/* auth userid, e.g., bootes */
 	uchar	authidsum;
-	char	authdom[DOMLEN];
+	char	authdom[DOMLEN];	/* auth domain, e.g., cs.bell-labs.com */
 	uchar	authdomsum;
+
+	uchar	aesmachkey[AESKEYLEN];
+	uchar	aesmachsum;
 };
 
 extern	uchar	nvcsum(void*, int);
-extern int	readnvram(Nvrsafe*, int);
+extern	int	readnvram(Nvrsafe*, int);
 
 /*
  *  call up auth server
@@ -162,7 +193,23 @@
 /*
  *  exchange messages with auth server
  */
-extern	int	_asgetticket(int, char*, char*);
+extern	int	_asgetpakkey(int, Ticketreq*, Authkey*);
+extern	int	_asgetticket(int, Ticketreq*, char*, int);
+extern	int	_asrequest(int, Ticketreq*);
+extern	int	_asgetresp(int, Ticket*, Authenticator*, Authkey *);
 extern	int	_asrdresp(int, char*, int);
-extern	int	sslnegotiate(int, Ticket*, char**, char**);
-extern	int	srvsslnegotiate(int, Ticket*, char**, char**);
+
+/*
+ *  AuthPAK protocol
+ */
+typedef struct PAKpriv PAKpriv;
+struct PAKpriv
+{
+	int	isclient;
+	uchar	x[PAKXLEN];
+	uchar	y[PAKYLEN];
+};
+
+extern	void	authpak_hash(Authkey *k, char *u);
+extern	void	authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient);
+extern	int	authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN]);
--- a/include/lib.h
+++ b/include/lib.h
@@ -20,6 +20,7 @@
 #define strtoll libstrtoll
 #undef timeradd
 #define timeradd	xtimeradd
+#define gmtime	libgmtime
 
 
 #define	nil	((void*)0)
@@ -32,6 +33,7 @@
 typedef unsigned short	p9_ushort;
 typedef unsigned int	Rune;
 typedef unsigned int	p9_u32int;
+typedef unsigned long long p9_u64int;
 typedef p9_u32int mpdigit;
 
 /* make sure we don't conflict with predefined types */
@@ -40,6 +42,7 @@
 #define ushort	p9_ushort
 #define uint	p9_uint
 #define u32int	p9_u32int
+#define u64int	p9_u64int
 
 /* #define long int rather than p9_long so that "unsigned long" is valid */
 #define long	int
@@ -277,3 +280,23 @@
 
 extern int (*fmtdoquote)(int);
 
+
+/*
+ * Time-of-day
+ */
+
+typedef
+struct Tm
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	mday;
+	int	mon;
+	int	year;
+	int	wday;
+	int	yday;
+	char	zone[4];
+	int	tzoff;
+} Tm;
+extern	Tm*	gmtime(long);
--- a/include/libsec.h
+++ b/include/libsec.h
@@ -1,11 +1,10 @@
-
 #ifndef _MPINT
 typedef struct mpint mpint;
 #endif
 
-/////////////////////////////////////////////////////////
-// AES definitions
-/////////////////////////////////////////////////////////
+/*
+ * AES definitions
+ */
 
 enum
 {
@@ -20,27 +19,35 @@
 	ulong	setup;
 	int	rounds;
 	int	keybytes;
-	uchar	key[AESmaxkey];		/* unexpanded key */
-	u32int	ekey[4*(AESmaxrounds + 1)];	/* encryption key */
-	u32int	dkey[4*(AESmaxrounds + 1)];	/* decryption key */
-	uchar	ivec[AESbsize];	/* initialization vector */
+	uchar	key[AESmaxkey];			/* unexpanded key */
+	ulong	ekey[4*(AESmaxrounds + 1)];	/* encryption key */
+	ulong	dkey[4*(AESmaxrounds + 1)];	/* decryption key */
+	uchar	ivec[AESbsize];			/* initialization vector */
+	uchar	mackey[3 * AESbsize];		/* 3 XCBC mac 96 keys */
 };
 
+/* block ciphers */
+void	aes_encrypt(ulong rk[], int Nr, uchar pt[16], uchar ct[16]);
+void	aes_decrypt(ulong rk[], int Nr, uchar ct[16], uchar pt[16]);
+
 void	setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec);
 void	aesCBCencrypt(uchar *p, int len, AESstate *s);
 void	aesCBCdecrypt(uchar *p, int len, AESstate *s);
 
-/////////////////////////////////////////////////////////
-// Blowfish Definitions
-/////////////////////////////////////////////////////////
+void	setupAESXCBCstate(AESstate *s);
+uchar*	aesXCBCmac(uchar *p, int len, AESstate *s);
 
+/*
+ * Blowfish Definitions
+ */
+
 enum
 {
 	BFbsize	= 8,
-	BFrounds	= 16
+	BFrounds= 16
 };
 
-// 16-round Blowfish
+/* 16-round Blowfish */
 typedef struct BFstate BFstate;
 struct BFstate
 {
@@ -59,16 +66,80 @@
 void	bfECBencrypt(uchar*, int, BFstate*);
 void	bfECBdecrypt(uchar*, int, BFstate*);
 
-/////////////////////////////////////////////////////////
-// DES definitions
-/////////////////////////////////////////////////////////
+/*
+ * Chacha definitions
+ */
 
 enum
 {
+	ChachaBsize=	64,
+	ChachaKeylen=	256/8,
+	ChachaIVlen=	96/8,
+};
+
+typedef struct Chachastate Chachastate;
+struct Chachastate
+{
+	union{
+		u32int	input[16];
+		struct {
+			u32int	constant[4];
+			u32int	key[8];
+			u32int	counter;
+			u32int	iv[3];
+		};
+	};
+	int	rounds;
+	int	ivwords;
+};
+
+void	setupChachastate(Chachastate*, uchar*, ulong, uchar*, ulong, int);
+void	chacha_setiv(Chachastate *, uchar*);
+void	chacha_setblock(Chachastate*, u64int);
+void	chacha_encrypt(uchar*, ulong, Chachastate*);
+void	chacha_encrypt2(uchar*, uchar*, ulong, Chachastate*);
+
+void	ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
+int	ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
+
+/*
+ * Salsa definitions
+ */
+enum
+{
+	SalsaBsize=	64,
+	SalsaKeylen=	256/8,
+	SalsaIVlen=	64/8,
+	XSalsaIVlen=	192/8,
+};
+
+typedef struct Salsastate Salsastate;
+struct Salsastate
+{
+	u32int	input[16];
+	u32int	key[8];
+	int	rounds;
+	int	ivwords;
+};
+
+void	setupSalsastate(Salsastate*, uchar*, ulong, uchar*, ulong, int);
+void	salsa_setiv(Salsastate*, uchar*);
+void	salsa_setblock(Salsastate*, u64int);
+void	salsa_encrypt(uchar*, ulong, Salsastate*);
+void	salsa_encrypt2(uchar*, uchar*, ulong, Salsastate*);
+
+void	hsalsa(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds);
+
+/*
+ * DES definitions
+ */
+
+enum
+{
 	DESbsize=	8
 };
 
-// single des
+/* single des */
 typedef struct DESstate DESstate;
 struct DESstate
 {
@@ -86,12 +157,12 @@
 void	desECBencrypt(uchar*, int, DESstate*);
 void	desECBdecrypt(uchar*, int, DESstate*);
 
-// for backward compatibility with 7 byte DES key format
+/* for backward compatibility with 7-byte DES key format */
 void	des56to64(uchar *k56, uchar *k64);
 void	des64to56(uchar *k64, uchar *k56);
 void	key_setup(uchar[7], ulong[32]);
 
-// triple des encrypt/decrypt orderings
+/* triple des encrypt/decrypt orderings */
 enum {
 	DES3E=		0,
 	DES3D=		1,
@@ -117,67 +188,97 @@
 void	des3ECBencrypt(uchar*, int, DES3state*);
 void	des3ECBdecrypt(uchar*, int, DES3state*);
 
-/////////////////////////////////////////////////////////
-// digests
-/////////////////////////////////////////////////////////
+/*
+ * digests
+ */
 
 enum
 {
 	SHA1dlen=	20,	/* SHA digest length */
+	SHA2_224dlen=	28,	/* SHA-224 digest length */
+	SHA2_256dlen=	32,	/* SHA-256 digest length */
+	SHA2_384dlen=	48,	/* SHA-384 digest length */
+	SHA2_512dlen=	64,	/* SHA-512 digest length */
 	MD4dlen=	16,	/* MD4 digest length */
-	MD5dlen=	16	/* MD5 digest length */
+	MD5dlen=	16,	/* MD5 digest length */
+	Poly1305dlen=	16,	/* Poly1305 digest length */
+
+	Hmacblksz	= 64,	/* in bytes; from rfc2104 */
 };
 
 typedef struct DigestState DigestState;
 struct DigestState
 {
-	ulong len;
-	u32int state[5];
-	uchar buf[128];
-	int blen;
-	char malloced;
-	char seeded;
+	uvlong	len;
+	union {
+		u32int	state[8];
+		u64int	bstate[8];
+	};
+	uchar	buf[256];
+	int	blen;
+	char	malloced;
+	char	seeded;
 };
 typedef struct DigestState SHAstate;	/* obsolete name */
 typedef struct DigestState SHA1state;
+typedef struct DigestState SHA2_224state;
+typedef struct DigestState SHA2_256state;
+typedef struct DigestState SHA2_384state;
+typedef struct DigestState SHA2_512state;
 typedef struct DigestState MD5state;
 typedef struct DigestState MD4state;
 
-DigestState* md4(uchar*, ulong, uchar*, DigestState*);
-DigestState* md5(uchar*, ulong, uchar*, DigestState*);
-DigestState* sha1(uchar*, ulong, uchar*, DigestState*);
-DigestState* hmac_md5(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
-DigestState* hmac_sha1(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
-char* sha1pickle(SHA1state*);
-SHA1state* sha1unpickle(char*);
+DigestState*	md4(uchar*, ulong, uchar*, DigestState*);
+DigestState*	md5(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha1(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_224(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_256(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_384(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_512(uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_x(uchar *p, ulong len, uchar *key, ulong klen,
+			uchar *digest, DigestState *s,
+			DigestState*(*x)(uchar*, ulong, uchar*, DigestState*),
+			int xlen);
+DigestState*	hmac_md5(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha1(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_224(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_256(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_384(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_512(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+char*		md5pickle(MD5state*);
+MD5state*	md5unpickle(char*);
+char*		sha1pickle(SHA1state*);
+SHA1state*	sha1unpickle(char*);
 
-/////////////////////////////////////////////////////////
-// random number generation
-/////////////////////////////////////////////////////////
+DigestState*	poly1305(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+
+/*
+ * random number generation
+ */
 void	genrandom(uchar *buf, int nbytes);
 void	prng(uchar *buf, int nbytes);
 ulong	fastrand(void);
 ulong	nfastrand(ulong);
 
-/////////////////////////////////////////////////////////
-// primes
-/////////////////////////////////////////////////////////
-void	genprime(mpint *p, int n, int accuracy); // generate an n bit probable prime
-void	gensafeprime(mpint *p, mpint *alpha, int n, int accuracy);	// prime and generator
-void	genstrongprime(mpint *p, int n, int accuracy);	// generate an n bit strong prime
+/*
+ * primes
+ */
+void	genprime(mpint *p, int n, int accuracy); /* generate n-bit probable prime */
+void	gensafeprime(mpint *p, mpint *alpha, int n, int accuracy); /* prime & generator */
+void	genstrongprime(mpint *p, int n, int accuracy); /* generate n-bit strong prime */
 void	DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen]);
-int	probably_prime(mpint *n, int nrep);	// miller-rabin test
-int	smallprimetest(mpint *p);		// returns -1 if not prime, 0 otherwise
+int	probably_prime(mpint *n, int nrep);	/* miller-rabin test */
+int	smallprimetest(mpint *p);  /* returns -1 if not prime, 0 otherwise */
 
-/////////////////////////////////////////////////////////
-// rc4
-/////////////////////////////////////////////////////////
+/*
+ * rc4
+ */
 typedef struct RC4state RC4state;
 struct RC4state
 {
-	 uchar state[256];
-	 uchar x;
-	 uchar y;
+	 uchar	state[256];
+	 uchar	x;
+	 uchar	y;
 };
 
 void	setupRC4state(RC4state*, uchar*, int);
@@ -185,34 +286,41 @@
 void	rc4skip(RC4state*, int);
 void	rc4back(RC4state*, int);
 
-/////////////////////////////////////////////////////////
-// rsa
-/////////////////////////////////////////////////////////
+/*
+ * rsa
+ */
 typedef struct RSApub RSApub;
 typedef struct RSApriv RSApriv;
+typedef struct PEMChain PEMChain;
 
-// public/encryption key
+/* public/encryption key */
 struct RSApub
 {
-	mpint	*n;	// modulus
-	mpint	*ek;	// exp (encryption key)
+	mpint	*n;	/* modulus */
+	mpint	*ek;	/* exp (encryption key) */
 };
 
-// private/decryption key
+/* private/decryption key */
 struct RSApriv
 {
 	RSApub	pub;
 
-	mpint	*dk;	// exp (decryption key)
+	mpint	*dk;	/* exp (decryption key) */
 
-	// precomputed values to help with chinese remainder theorem calc
+	/* precomputed values to help with chinese remainder theorem calc */
 	mpint	*p;
 	mpint	*q;
-	mpint	*kp;	// dk mod p-1
-	mpint	*kq;	// dk mod q-1
-	mpint	*c2;	// (inv p) mod q
+	mpint	*kp;	/* dk mod p-1 */
+	mpint	*kq;	/* dk mod q-1 */
+	mpint	*c2;	/* (inv p) mod q */
 };
 
+struct PEMChain{
+	PEMChain*next;
+	uchar	*pem;
+	int	pemlen;
+};
+
 RSApriv*	rsagen(int nlen, int elen, int rounds);
 RSApriv*	rsafill(mpint *n, mpint *e, mpint *d, mpint *p, mpint *q);
 mpint*		rsaencrypt(RSApub *k, mpint *in, mpint *out);
@@ -225,34 +333,38 @@
 RSApub*		X509toRSApub(uchar*, int, char*, int);
 RSApriv*	asn1toRSApriv(uchar*, int);
 void		asn1dump(uchar *der, int len);
-uchar*		decodepem(char *s, char *type, int *len);
-uchar*		X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen);
-uchar*		X509req(RSApriv *priv, char *subj, int *certlen);
-char*		X509verify(uchar *cert, int ncert, RSApub *pk);
+uchar*		decodePEM(char *s, char *type, int *len, char **new_s);
+PEMChain*	decodepemchain(char *s, char *type);
+uchar*		X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen);
+uchar*		X509rsareq(RSApriv *priv, char *subj, int *certlen);
+char*		X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk);
+char*		X509rsaverify(uchar *cert, int ncert, RSApub *pk);
+
 void		X509dump(uchar *cert, int ncert);
-/////////////////////////////////////////////////////////
-// elgamal
-/////////////////////////////////////////////////////////
+
+/*
+ * elgamal
+ */
 typedef struct EGpub EGpub;
 typedef struct EGpriv EGpriv;
 typedef struct EGsig EGsig;
 
-// public/encryption key
+/* public/encryption key */
 struct EGpub
 {
-	mpint	*p;	// modulus
-	mpint	*alpha;	// generator
-	mpint	*key;	// (encryption key) alpha**secret mod p
+	mpint	*p;	/* modulus */
+	mpint	*alpha;	/* generator */
+	mpint	*key;	/* (encryption key) alpha**secret mod p */
 };
 
-// private/decryption key
+/* private/decryption key */
 struct EGpriv
 {
 	EGpub	pub;
-	mpint	*secret; // (decryption key)
+	mpint	*secret;	/* (decryption key) */
 };
 
-// signature
+/* signature */
 struct EGsig
 {
 	mpint	*r, *s;
@@ -259,7 +371,7 @@
 };
 
 EGpriv*		eggen(int nlen, int rounds);
-mpint*		egencrypt(EGpub *k, mpint *in, mpint *out);
+mpint*		egencrypt(EGpub *k, mpint *in, mpint *out);	/* deprecated */
 mpint*		egdecrypt(EGpriv *k, mpint *in, mpint *out);
 EGsig*		egsign(EGpriv *k, mpint *m);
 int		egverify(EGpub *k, EGsig *sig, mpint *m);
@@ -271,36 +383,36 @@
 void		egsigfree(EGsig*);
 EGpub*		egprivtopub(EGpriv*);
 
-/////////////////////////////////////////////////////////
-// dsa
-/////////////////////////////////////////////////////////
+/*
+ * dsa
+ */
 typedef struct DSApub DSApub;
 typedef struct DSApriv DSApriv;
 typedef struct DSAsig DSAsig;
 
-// public/encryption key
+/* public/encryption key */
 struct DSApub
 {
-	mpint	*p;	// modulus
-	mpint	*q;	// group order, q divides p-1
-	mpint	*alpha;	// group generator
-	mpint	*key;	// (encryption key) alpha**secret mod p
+	mpint	*p;	/* modulus */
+	mpint	*q;	/* group order, q divides p-1 */
+	mpint	*alpha;	/* group generator */
+	mpint	*key;	/* (encryption key) alpha**secret mod p */
 };
 
-// private/decryption key
+/* private/decryption key */
 struct DSApriv
 {
 	DSApub	pub;
-	mpint	*secret; // (decryption key)
+	mpint	*secret;	/* (decryption key) */
 };
 
-// signature
+/* signature */
 struct DSAsig
 {
 	mpint	*r, *s;
 };
 
-DSApriv*	dsagen(DSApub *opub);
+DSApriv*	dsagen(DSApub *opub);	/* opub not checked for consistency! */
 DSAsig*		dsasign(DSApriv *k, mpint *m);
 int		dsaverify(DSApub *k, DSAsig *sig, mpint *m);
 DSApub*		dsapuballoc(void);
@@ -310,31 +422,135 @@
 DSAsig*		dsasigalloc(void);
 void		dsasigfree(DSAsig*);
 DSApub*		dsaprivtopub(DSApriv*);
+DSApriv*	asn1toDSApriv(uchar*, int);
 
-/////////////////////////////////////////////////////////
-// TLS
-/////////////////////////////////////////////////////////
+/*
+ * TLS
+ */
 typedef struct Thumbprint{
 	struct Thumbprint *next;
-	uchar sha1[SHA1dlen];
+	uchar	sha1[SHA1dlen];
 } Thumbprint;
 
 typedef struct TLSconn{
-	char dir[40];  // connection directory
-	uchar *cert;   // certificate (local on input, remote on output)
-	uchar *sessionID;
-	int certlen, sessionIDlen;
-	int (*trace)(char*fmt, ...);
+	char	dir[40];	/* connection directory */
+	uchar	*cert;	/* certificate (local on input, remote on output) */
+	uchar	*sessionID;
+	uchar	*psk;
+	int	certlen;
+	int	sessionIDlen;
+	int	psklen;
+	int	(*trace)(char*fmt, ...);
+	PEMChain*chain;	/* optional extra certificate evidence for servers to present */
+	char	*sessionType;
+	uchar	*sessionKey;
+	int	sessionKeylen;
+	char	*sessionConst;
+	char	*serverName;
+	char	*pskID;
 } TLSconn;
 
-// tlshand.c
-extern int tlsClient(int fd, TLSconn *c);
-extern int tlsServer(int fd, TLSconn *c);
+/* tlshand.c */
+int tlsClient(int fd, TLSconn *c);
+int tlsServer(int fd, TLSconn *c);
 
-// thumb.c
-extern Thumbprint* initThumbprints(char *ok, char *crl);
-extern void freeThumbprints(Thumbprint *ok);
-extern int okThumbprint(uchar *sha1, Thumbprint *ok);
+/* thumb.c */
+Thumbprint* initThumbprints(char *ok, char *crl);
+void	freeThumbprints(Thumbprint *ok);
+int	okThumbprint(uchar *sha1, Thumbprint *ok);
 
-// readcert.c
-extern uchar *readcert(char *filename, int *pcertlen);
+/* readcert.c */
+uchar	*readcert(char *filename, int *pcertlen);
+PEMChain*readcertchain(char *filename);
+
+/* aes_xts.c */
+int aes_xts_encrypt(ulong tweak[], ulong ecb[],  vlong sectorNumber, uchar *input, uchar *output, ulong len) ;
+int aes_xts_decrypt(ulong tweak[], ulong ecb[], vlong sectorNumber, uchar *input, uchar *output, ulong len);
+
+typedef struct ECpoint{
+	int inf;
+	mpint *x;
+	mpint *y;
+} ECpoint;
+
+typedef ECpoint ECpub;
+typedef struct ECpriv{
+	ECpoint;
+	mpint *d;
+} ECpriv;
+
+typedef struct ECdomain{
+	mpint *p;
+	mpint *a;
+	mpint *b;
+	ECpoint G;
+	mpint *n;
+	mpint *h;
+} ECdomain;
+
+void	ecdominit(ECdomain *, void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h));
+void	ecdomfree(ECdomain *);
+
+void	ecassign(ECdomain *, ECpoint *old, ECpoint *new);
+void	ecadd(ECdomain *, ECpoint *a, ECpoint *b, ECpoint *s);
+void	ecmul(ECdomain *, ECpoint *a, mpint *k, ECpoint *s);
+ECpoint*	strtoec(ECdomain *, char *, char **, ECpoint *);
+ECpriv*	ecgen(ECdomain *, ECpriv*);
+int	ecverify(ECdomain *, ECpoint *);
+int	ecpubverify(ECdomain *, ECpub *);
+void	ecdsasign(ECdomain *, ECpriv *, uchar *, int, mpint *, mpint *);
+int	ecdsaverify(ECdomain *, ECpub *, uchar *, int, mpint *, mpint *);
+void	base58enc(uchar *, char *, int);
+int	base58dec(char *, uchar *, int);
+
+ECpub*	ecdecodepub(ECdomain *dom, uchar *, int);
+int	ecencodepub(ECdomain *dom, ECpub *, uchar *, int);
+void	ecpubfree(ECpub *);
+
+ECpub*	X509toECpub(uchar *cert, int ncert, ECdomain *dom);
+char*	X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub);
+char*	X509ecdsaverify(uchar *sig, int siglen, ECdomain *dom, ECpub *pub);
+
+/* curves */
+void	secp256r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+void	secp256k1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+
+DigestState*	ripemd160(uchar *, ulong, uchar *, DigestState *);
+
+/*
+ * Diffie-Hellman key exchange
+ */
+
+typedef struct DHstate DHstate;
+struct DHstate
+{
+	mpint	*g;	/* base g */
+	mpint	*p;	/* large prime */
+	mpint	*q;	/* subgroup prime */
+	mpint	*x;	/* random secret */
+	mpint	*y;	/* public key y = g**x % p */
+};
+
+/* generate new public key: y = g**x % p */
+mpint* dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g);
+
+/* calculate shared key: k = y**x % p */
+mpint* dh_finish(DHstate *dh, mpint *y);
+
+/* Curve25519 elliptic curve, public key function */
+void curve25519(uchar mypublic[32], uchar secret[32], uchar basepoint[32]);
+
+/* Curve25519 diffie hellman */
+void curve25519_dh_new(uchar x[32], uchar y[32]);
+void curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]);
+
+/* password-based key derivation function 2 (rfc2898) */
+void pbkdf2_x(uchar *p, ulong plen, uchar *s, ulong slen, ulong rounds, uchar *d, ulong dlen,
+	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen);
+
+/* hmac-based key derivation function (rfc5869) */
+void hkdf_x(uchar *salt, ulong nsalt, uchar *info, ulong ninfo, uchar *key, ulong nkey, uchar *d, ulong dlen,
+	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen);
+
+/* timing safe memcmp() */
+int tsmemcmp(void*, void*, ulong);
--- a/include/mp.h
+++ b/include/mp.h
@@ -1,16 +1,17 @@
 #define _MPINT 1
 
-// the code assumes mpdigit to be at least an int
-// mpdigit must be an atomic type.  mpdigit is defined
-// in the architecture specific u.h
-
+/*
+ * the code assumes mpdigit to be at least an int
+ * mpdigit must be an atomic type.  mpdigit is defined
+ * in the architecture specific u.h
+ */
 typedef struct mpint mpint;
 
 struct mpint
 {
-	int	sign;	// +1 or -1
-	int	size;	// allocated digits
-	int	top;	// significant digits
+	int	sign;	/* +1 or -1 */
+	int	size;	/* allocated digits */
+	int	top;	/* significant digits */
 	mpdigit	*p;
 	char	flags;
 };
@@ -17,118 +18,158 @@
 
 enum
 {
-	MPstatic=	0x01,
-	Dbytes=		sizeof(mpdigit),	// bytes per digit
-	Dbits=		Dbytes*8		// bits per digit
+	MPstatic=	0x01,	/* static constant */
+	MPnorm=		0x02,	/* normalization status */
+	MPtimesafe=	0x04,	/* request time invariant computation */
+	MPfield=	0x08,	/* this mpint is a field modulus */
+
+	Dbytes=		sizeof(mpdigit),	/* bytes per digit */
+	Dbits=		Dbytes*8		/* bits per digit */
 };
 
-// allocation
-void	mpsetminbits(int n);	// newly created mpint's get at least n bits
-mpint*	mpnew(int n);		// create a new mpint with at least n bits
+/* allocation */
+void	mpsetminbits(int n);	/* newly created mpint's get at least n bits */
+mpint*	mpnew(int n);		/* create a new mpint with at least n bits */
 void	mpfree(mpint *b);
-void	mpbits(mpint *b, int n);	// ensure that b has at least n bits
-void	mpnorm(mpint *b);		// dump leading zeros
+void	mpbits(mpint *b, int n);	/* ensure that b has at least n bits */
+mpint*	mpnorm(mpint *b);		/* dump leading zeros */
 mpint*	mpcopy(mpint *b);
 void	mpassign(mpint *old, mpint *new);
 
-// random bits
+/* random bits */
 mpint*	mprand(int bits, void (*gen)(uchar*, int), mpint *b);
+/* return uniform random [0..n-1] */
+mpint*	mpnrand(mpint *n, void (*gen)(uchar*, int), mpint *b);
 
-// conversion
-mpint*	strtomp(char*, char**, int, mpint*);	// ascii
+/* conversion */
+mpint*	strtomp(char*, char**, int, mpint*);	/* ascii */
 int	mpfmt(Fmt*);
 char*	mptoa(mpint*, int, char*, int);
-mpint*	letomp(uchar*, uint, mpint*);	// byte array, little-endian
+mpint*	letomp(uchar*, uint, mpint*);	/* byte array, little-endian */
 int	mptole(mpint*, uchar*, uint, uchar**);
-mpint*	betomp(uchar*, uint, mpint*);	// byte array, little-endian
+void	mptolel(mpint *b, uchar *p, int n);
+mpint*	betomp(uchar*, uint, mpint*);	/* byte array, big-endian */
 int	mptobe(mpint*, uchar*, uint, uchar**);
-uint	mptoui(mpint*);			// unsigned int
+void	mptober(mpint *b, uchar *p, int n);
+uint	mptoui(mpint*);			/* unsigned int */
 mpint*	uitomp(uint, mpint*);
-int	mptoi(mpint*);			// int
+int	mptoi(mpint*);			/* int */
 mpint*	itomp(int, mpint*);
-uvlong	mptouv(mpint*);			// unsigned vlong
+uvlong	mptouv(mpint*);			/* unsigned vlong */
 mpint*	uvtomp(uvlong, mpint*);
-vlong	mptov(mpint*);			// vlong
+vlong	mptov(mpint*);			/* vlong */
 mpint*	vtomp(vlong, mpint*);
 
-// divide 2 digits by one
+/* divide 2 digits by one */
 void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
 
-// in the following, the result mpint may be
-// the same as one of the inputs.
-void	mpadd(mpint *b1, mpint *b2, mpint *sum);	// sum = b1+b2
-void	mpsub(mpint *b1, mpint *b2, mpint *diff);	// diff = b1-b2
-void	mpleft(mpint *b, int shift, mpint *res);	// res = b<<shift
-void	mpright(mpint *b, int shift, mpint *res);	// res = b>>shift
-void	mpmul(mpint *b1, mpint *b2, mpint *prod);	// prod = b1*b2
-void	mpexp(mpint *b, mpint *e, mpint *m, mpint *res);	// res = b**e mod m
-void	mpmod(mpint *b, mpint *m, mpint *remainder);	// remainder = b mod m
+/* in the following, the result mpint may be */
+/* the same as one of the inputs. */
+void	mpadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpsub(mpint *b1, mpint *b2, mpint *diff);	/* diff = b1-b2 */
+void	mpleft(mpint *b, int shift, mpint *res);	/* res = b<<shift */
+void	mpright(mpint *b, int shift, mpint *res);	/* res = b>>shift */
+void	mpmul(mpint *b1, mpint *b2, mpint *prod);	/* prod = b1*b2 */
+void	mpexp(mpint *b, mpint *e, mpint *m, mpint *res);	/* res = b**e mod m */
+void	mpmod(mpint *b, mpint *m, mpint *remainder);	/* remainder = b mod m */
 
-// quotient = dividend/divisor, remainder = dividend % divisor
+/* logical operations */
+void	mpand(mpint *b1, mpint *b2, mpint *res);
+void	mpbic(mpint *b1, mpint *b2, mpint *res);
+void	mpor(mpint *b1, mpint *b2, mpint *res);
+void	mpnot(mpint *b, mpint *res);
+void	mpxor(mpint *b1, mpint *b2, mpint *res);
+void	mptrunc(mpint *b, int n, mpint *res);
+void	mpxtend(mpint *b, int n, mpint *res);
+
+/* modular arithmetic, time invariant when 0≤b1≤m-1 and 0≤b2≤m-1 */
+void	mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum);	/* sum = b1+b2 % m */
+void	mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff);	/* diff = b1-b2 % m */
+void	mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod);	/* prod = b1*b2 % m */
+
+/* quotient = dividend/divisor, remainder = dividend % divisor */
 void	mpdiv(mpint *dividend, mpint *divisor,  mpint *quotient, mpint *remainder);
 
-// return neg, 0, pos as b1-b2 is neg, 0, pos
+/* return neg, 0, pos as b1-b2 is neg, 0, pos */
 int	mpcmp(mpint *b1, mpint *b2);
 
-// extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d
+/* res = s != 0 ? b1 : b2 */
+void	mpsel(int s, mpint *b1, mpint *b2, mpint *res);
+
+/* extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d */
 void	mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y);
 
-// res = b**-1 mod m
+/* res = b**-1 mod m */
 void	mpinvert(mpint *b, mpint *m, mpint *res);
 
-// bit counting
-int	mpsignif(mpint*);	// number of sigificant bits in mantissa
-int	mplowbits0(mpint*);	// k, where n = 2**k * q for odd q
+/* bit counting */
+int	mpsignif(mpint*);	/* number of sigificant bits in mantissa */
+int	mplowbits0(mpint*);	/* k, where n = 2**k * q for odd q */
 
-// well known constants
+/* well known constants */
 extern mpint	*mpzero, *mpone, *mptwo;
 
-// sum[0:alen] = a[0:alen-1] + b[0:blen-1]
-// prereq: alen >= blen, sum has room for alen+1 digits
+/* sum[0:alen] = a[0:alen-1] + b[0:blen-1] */
+/* prereq: alen >= blen, sum has room for alen+1 digits */
 void	mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum);
 
-// diff[0:alen-1] = a[0:alen-1] - b[0:blen-1]
-// prereq: alen >= blen, diff has room for alen digits
+/* diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] */
+/* prereq: alen >= blen, diff has room for alen digits */
 void	mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff);
 
-// p[0:n] += m * b[0:n-1]
-// prereq: p has room for n+1 digits
+/* p[0:n] += m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
 void	mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p);
 
-// p[0:n] -= m * b[0:n-1]
-// prereq: p has room for n+1 digits
+/* p[0:n] -= m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
 int	mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p);
 
-// p[0:alen*blen-1] = a[0:alen-1] * b[0:blen-1]
-// prereq: alen >= blen, p has room for m*n digits
+/* p[0:alen+blen-1] = a[0:alen-1] * b[0:blen-1] */
+/* prereq: alen >= blen, p has room for m*n digits */
 void	mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+void	mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
 
-// sign of a - b or zero if the same
+/* sign of a - b or zero if the same */
 int	mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen);
+int	mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen);
 
-// divide the 2 digit dividend by the one digit divisor and stick in quotient
-// we assume that the result is one digit - overflow is all 1's
+/* divide the 2 digit dividend by the one digit divisor and stick in quotient */
+/* we assume that the result is one digit - overflow is all 1's */
 void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
 
-// playing with magnitudes
+/* playing with magnitudes */
 int	mpmagcmp(mpint *b1, mpint *b2);
-void	mpmagadd(mpint *b1, mpint *b2, mpint *sum);	// sum = b1+b2
-void	mpmagsub(mpint *b1, mpint *b2, mpint *sum);	// sum = b1+b2
+void	mpmagadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpmagsub(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
 
-// chinese remainder theorem
-typedef struct CRTpre	CRTpre;		// precomputed values for converting
-					//  twixt residues and mpint
-typedef struct CRTres	CRTres;		// residue form of an mpint
+/* chinese remainder theorem */
+typedef struct CRTpre	CRTpre;		/* precomputed values for converting */
+					/*  twixt residues and mpint */
+typedef struct CRTres	CRTres;		/* residue form of an mpint */
 
 struct CRTres
 {
-	int	n;		// number of residues
-	mpint	*r[1];		// residues
+	int	n;		/* number of residues */
+	mpint	*r[1];		/* residues */
 };
 
-CRTpre*	crtpre(int, mpint**);			// precompute conversion values
-CRTres*	crtin(CRTpre*, mpint*);			// convert mpint to residues
-void	crtout(CRTpre*, CRTres*, mpint*);	// convert residues to mpint
+CRTpre*	crtpre(int, mpint**);			/* precompute conversion values */
+CRTres*	crtin(CRTpre*, mpint*);			/* convert mpint to residues */
+void	crtout(CRTpre*, CRTres*, mpint*);	/* convert residues to mpint */
 void	crtprefree(CRTpre*);
 void	crtresfree(CRTres*);
 
+/* fast field arithmetic */
+typedef struct Mfield	Mfield;
+
+struct Mfield
+{
+	mpint;
+	int	(*reduce)(Mfield*, mpint*, mpint*);
+};
+
+mpint *mpfield(mpint*);
+
+Mfield *gmfield(mpint*);
+Mfield *cnfield(mpint*);
--- a/include/user.h
+++ b/include/user.h
@@ -81,6 +81,7 @@
 extern	void	sleep(int);
 extern	void	osyield(void);
 extern	void	setmalloctag(void*, uintptr);
+extern	void	setrealloctag(void*, uintptr);
 extern	int	errstr(char*, uint);
 extern	int	rerrstr(char*, uint);
 extern	int	encrypt(void*, void*, int);
@@ -91,3 +92,4 @@
 extern	void	lock(Lock*);
 extern	void	unlock(Lock*);
 extern	int	iprint(char*, ...);
+extern	void	exits(char*);
--- a/kern/devcons.c
+++ b/kern/devcons.c
@@ -1191,3 +1191,29 @@
 	return n;
 }
 
+static	ulong	randn;
+
+static void
+seedrand(void)
+{
+	if(!waserror()){
+		randomread((void*)&randn, sizeof(randn));
+		poperror();
+	}
+}
+
+int
+nrand(int n)
+{
+	if(randn == 0)
+		seedrand();
+	randn = randn*1103515245 + 12345;
+	return (randn>>16) % n;
+}
+
+int
+rand(void)
+{
+	nrand(1);
+	return randn;
+}
--- a/kern/devssl.c
+++ b/kern/devssl.c
@@ -717,7 +717,7 @@
 randfill(uchar *buf, int len)
 {
 	while(len-- > 0)
-		*buf++ = fastrand();
+		*buf++ = nrand(256);
 }
 
 static long
--- a/kern/devtls.c
+++ b/kern/devtls.c
@@ -8,9 +8,9 @@
 #include	"error.h"
 
 #include	"libsec.h"
-
+ 
 typedef struct OneWay	OneWay;
-typedef struct Secret		Secret;
+typedef struct Secret	Secret;
 typedef struct TlsRec	TlsRec;
 typedef struct TlsErrs	TlsErrs;
 
@@ -17,15 +17,16 @@
 enum {
 	Statlen=	1024,		/* max. length of status or stats message */
 	/* buffer limits */
-	MaxRecLen		= 1<<14,	/* max payload length of a record layer message */
+	MaxRecLen	= 1<<14,	/* max payload length of a record layer message */
 	MaxCipherRecLen	= MaxRecLen + 2048,
-	RecHdrLen		= 5,
-	MaxMacLen		= SHA1dlen,
+	RecHdrLen	= 5,
+	MaxMacLen	= SHA2_256dlen,
 
 	/* protocol versions we can accept */
-	TLSVersion		= 0x0301,
-	SSL3Version		= 0x0300,
-	ProtocolVersion	= 0x0301,	/* maximum version we speak */
+	SSL3Version	= 0x0300,
+	TLS10Version	= 0x0301,
+	TLS11Version	= 0x0302,
+	TLS12Version	= 0x0303,
 	MinProtoVersion	= 0x0300,	/* limits on version we accept */
 	MaxProtoVersion	= 0x03ff,
 
@@ -72,6 +73,7 @@
 	EInternalError 			= 80,
 	EUserCanceled 			= 90,
 	ENoRenegotiation 		= 100,
+	EUnrecognizedName		= 112,
 
 	EMAX = 256
 };
@@ -84,10 +86,14 @@
 	int		(*dec)(Secret*, uchar*, int);
 	int		(*unpad)(uchar*, int, int);
 	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+
+	int		(*aead_enc)(Secret*, uchar*, int, uchar*, int);
+	int		(*aead_dec)(Secret*, uchar*, int, uchar*, int);
+
 	int		block;		/* encryption block len, 0 if none */
 	int		maclen;
 	void		*enckey;
-	uchar	mackey[MaxMacLen];
+	uchar		mackey[MaxMacLen];
 };
 
 struct OneWay
@@ -94,7 +100,7 @@
 {
 	QLock		io;		/* locks io access */
 	QLock		seclock;	/* locks secret paramaters */
-	ulong		seq;
+	u64int		seq;
 	Secret		*sec;		/* cipher in use */
 	Secret		*new;		/* cipher waiting for enable */
 };
@@ -116,8 +122,11 @@
 	int		state;
 	int		debug;
 
-	/* record layer mac functions for different protocol versions */
-	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
+	/*
+	 * function to genrate authenticated data blob for different
+	 * protocol versions
+	 */
+	int		(*packAAD)(u64int, uchar*, uchar*);
 
 	/* input side -- protected by in.io */
 	OneWay		in;
@@ -218,13 +227,14 @@
 static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
 static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
 static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
-static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
-static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
-static void	put64(uchar *p, vlong x);
+static int	sslPackAAD(u64int, uchar*, uchar*);
+static int	tlsPackAAD(u64int, uchar*, uchar*);
+static void	packMac(Secret*, uchar*, int, uchar*, int, uchar*);
+static void	put64(uchar *p, u64int);
 static void	put32(uchar *p, u32int);
 static void	put24(uchar *p, int);
 static void	put16(uchar *p, int);
-/* static u32int	get32(uchar *p); */
+static u32int	get32(uchar *p);
 static int	get16(uchar *p);
 static void	tlsSetState(TlsRec *tr, int new, int old);
 static void	rcvAlert(TlsRec *tr, int err);
@@ -233,6 +243,10 @@
 static int	rc4enc(Secret *sec, uchar *buf, int n);
 static int	des3enc(Secret *sec, uchar *buf, int n);
 static int	des3dec(Secret *sec, uchar *buf, int n);
+static int	aesenc(Secret *sec, uchar *buf, int n);
+static int	aesdec(Secret *sec, uchar *buf, int n);
+static int	ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *data, int len);
+static int	ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *data, int len);
 static int	noenc(Secret *sec, uchar *buf, int n);
 static int	sslunpad(uchar *buf, int n, int block);
 static int	tlsunpad(uchar *buf, int n, int block);
@@ -241,24 +255,20 @@
 static void	pdump(int, void*, char*);
 
 static char *tlsnames[] = {
-	/* unused */ 0,
-	/* topdir */ 0,
-	/* protodir */ 0,
-	"clone",
-	"encalgs",
-	"hashalgs",
-	/* convdir */ 0,
-	"data",
-	"ctl",
-	"hand",
-	"status",
-	"stats",
+[Qclonus]		"clone",
+[Qencalgs]	"encalgs",
+[Qhashalgs]	"hashalgs",
+[Qdata]		"data",
+[Qctl]		"ctl",
+[Qhand]		"hand",
+[Qstatus]		"status",
+[Qstats]		"stats",
 };
 
 static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
 
 static int
-tlsgen(Chan *c, char*unused1, Dirtab *unused2, int unused3, int s, Dir *dp)
+tlsgen(Chan *c, char *unused1, Dirtab *unused2, int unused3, int s, Dir *dp)
 {
 	Qid q;
 	TlsRec *tr;
@@ -377,7 +387,6 @@
 		unlock(&tdlock);
 		return 1;
 	}
-	return -1;
 }
 
 static Chan*
@@ -467,7 +476,7 @@
 			lock(&tr->hqlock);
 			if(tr->handq != nil)
 				error(Einuse);
-			tr->handq = qopen(2 * MaxCipherRecLen, 0, 0, nil);
+			tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
 			if(tr->handq == nil)
 				error("cannot allocate handshake queue");
 			tr->hqref = 1;
@@ -732,9 +741,10 @@
 {
 	OneWay *volatile in;
 	Block *volatile b;
-	uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
+	uchar *p, aad[8+RecHdrLen], header[RecHdrLen], hmac[MaxMacLen];
 	int volatile nconsumed;
-	int len, type, ver, unpad_len;
+	int len, type, ver, unpad_len, aadlen, ivlen;
+	Secret *sec;
 
 	nconsumed = 0;
 	if(waserror()){
@@ -796,25 +806,46 @@
 	}
 	qlock(&in->seclock);
 	p = b->rp;
-	if(in->sec != nil) {
+	sec = in->sec;
+	if(sec != nil) {
 		/* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
 		        should look alike, including timing of the response. */
-		unpad_len = (*in->sec->dec)(in->sec, p, len);
-		if(unpad_len >= in->sec->maclen)
-			len = unpad_len - in->sec->maclen;
+		if(sec->aead_dec != nil)
+			unpad_len = len;
+		else {
+			unpad_len = (*sec->dec)(sec, p, len);
 if(tr->debug) pprint("decrypted %d\n", unpad_len);
 if(tr->debug) pdump(unpad_len, p, "decrypted:");
+		}
 
+		if(tr->version >= TLS11Version){
+			ivlen = sec->block;
+			len -= ivlen;
+			if(len < 0)
+				rcvError(tr, EDecodeError, "runt record message");
+			unpad_len -= ivlen;
+			p += ivlen;
+		}
+
+		if(unpad_len >= sec->maclen)
+			len = unpad_len - sec->maclen;
+
 		/* update length */
 		put16(header+3, len);
-		put64(seq, in->seq);
-		in->seq++;
-		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
-		if(unpad_len < in->sec->maclen)
-			rcvError(tr, EBadRecordMac, "short record mac");
-		if(memcmp(hmac, p+len, in->sec->maclen) != 0)
-			rcvError(tr, EBadRecordMac, "record mac mismatch");
-		b->wp = b->rp + len;
+		aadlen = (*tr->packAAD)(in->seq++, header, aad);
+		if(sec->aead_dec != nil) {
+			len = (*sec->aead_dec)(sec, aad, aadlen, p, unpad_len);
+			if(len < 0)
+				rcvError(tr, EBadRecordMac, "record mac mismatch");
+		} else {
+			packMac(sec, aad, aadlen, p, len, hmac);
+			if(unpad_len < sec->maclen)
+				rcvError(tr, EBadRecordMac, "short record mac");
+			if(tsmemcmp(hmac, p + len, sec->maclen) != 0)
+				rcvError(tr, EBadRecordMac, "record mac mismatch");
+		}
+		b->rp = p;
+		b->wp = p+len;
 	}
 	qunlock(&in->seclock);
 	poperror();
@@ -823,7 +854,7 @@
 
 	switch(type) {
 	default:
-		rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
+		rcvError(tr, EIllegalParameter, "invalid record message %#x", type);
 		break;
 	case RChangeCipherSpec:
 		if(len != 1 || p[0] != 1)
@@ -848,20 +879,27 @@
 			rcvError(tr, EIllegalParameter, "invalid alert fatal code");
 
 		/*
-		 * propate non-fatal alerts to handshaker
+		 * propagate non-fatal alerts to handshaker
 		 */
-		if(p[1] == ECloseNotify) {
+		switch(p[1]){
+		case ECloseNotify:
 			tlsclosed(tr, SRClose);
 			if(tr->opened)
 				error("tls hungup");
 			error("close notify");
-		}
-		if(p[1] == ENoRenegotiation)
+			break;
+		case ENoRenegotiation:
 			alertHand(tr, "no renegotiation");
-		else if(p[1] == EUserCanceled)
+			break;
+		case EUserCanceled:
 			alertHand(tr, "handshake canceled by user");
-		else
+			break;
+		case EUnrecognizedName:
+			/* happens in response to SNI, can be ignored. */
+			break;
+		default:
 			rcvError(tr, EIllegalParameter, "invalid alert code");
+		}
 		break;
 	case RHandshake:
 		/*
@@ -1074,7 +1112,7 @@
 
 		/* return at most what was asked for */
 		b = qgrab(&tr->processed, n);
-if(tr->debug) pprint("consumed processed %d\n", BLEN(b));
+if(tr->debug) pprint("consumed processed %zd\n", BLEN(b));
 if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
 		qunlock(&tr->in.io);
 		poperror();
@@ -1142,7 +1180,7 @@
 		s = buf;
 		e = buf + Statlen;
 		s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
-		s = seprint(s, e, "Version: 0x%x\n", tr->version);
+		s = seprint(s, e, "Version: %#x\n", tr->version);
 		if(tr->in.sec != nil)
 			s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
 		if(tr->in.new != nil)
@@ -1202,6 +1240,13 @@
 	return n;
 }
 
+static void
+randfill(uchar *buf, int len)
+{
+	while(len-- > 0)
+		*buf++ = nrand(256);
+}
+
 /*
  *  write a block in tls records
  */
@@ -1210,9 +1255,10 @@
 {
 	Block *volatile bb;
 	Block *nb;
-	uchar *p, seq[8];
+	uchar *p, aad[8+RecHdrLen];
 	OneWay *volatile out;
-	int n, maclen, pad, ok;
+	int n, ivlen, maclen, aadlen, pad, ok;
+	Secret *sec;
 
 	out = &tr->out;
 	bb = b;
@@ -1223,7 +1269,7 @@
 		nexterror();
 	}
 	qlock(&out->io);
-if(tr->debug)pprint("send %d\n", BLEN(b));
+if(tr->debug)pprint("send %zd\n", BLEN(b));
 if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
 
 
@@ -1245,21 +1291,25 @@
 		qlock(&out->seclock);
 		maclen = 0;
 		pad = 0;
-		if(out->sec != nil){
-			maclen = out->sec->maclen;
-			pad = maclen + out->sec->block;
+		ivlen = 0;
+		sec = out->sec;
+		if(sec != nil){
+			maclen = sec->maclen;
+			pad = maclen + sec->block;
+			if(tr->version >= TLS11Version)
+				ivlen = sec->block;
 		}
 		n = BLEN(bb);
 		if(n > MaxRecLen){
 			n = MaxRecLen;
-			nb = allocb(n + pad + RecHdrLen);
-			memmove(nb->wp + RecHdrLen, bb->rp, n);
+			nb = allocb(RecHdrLen + ivlen + n + pad);
+			memmove(nb->wp + RecHdrLen + ivlen, bb->rp, n);
 			bb->rp += n;
 		}else{
 			/*
 			 * carefully reuse bb so it will get freed if we're out of memory
 			 */
-			bb = padblock(bb, RecHdrLen);
+			bb = padblock(bb, RecHdrLen + ivlen);
 			if(pad)
 				nb = padblock(bb, -pad);
 			else
@@ -1272,14 +1322,16 @@
 		put16(p+1, tr->version);
 		put16(p+3, n);
 
-		if(out->sec != nil){
-			put64(seq, out->seq);
-			out->seq++;
-			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n);
-			n += maclen;
-
-			/* encrypt */
-			n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
+		if(sec != nil){
+			if(ivlen > 0)
+				randfill(p + RecHdrLen, ivlen);
+			aadlen = (*tr->packAAD)(out->seq++, p, aad);
+			if(sec->aead_enc != nil)
+				n = (*sec->aead_enc)(sec, aad, aadlen, p + RecHdrLen + ivlen, n) + ivlen;
+			else {
+				packMac(sec, aad, aadlen, p + RecHdrLen + ivlen, n, p + RecHdrLen + ivlen + n);
+				n = (*sec->enc)(sec, p + RecHdrLen, ivlen + n + maclen);
+			}
 			nb->wp = p + RecHdrLen + n;
 
 			/* update length */
@@ -1323,7 +1375,7 @@
 
 	tr = tlsdevs[CONV(c->qid)];
 	if(tr == nil)
-		panic("tlsbread");
+		panic("tlsbwrite");
 
 	ty = TYPE(c->qid);
 	switch(ty) {
@@ -1363,7 +1415,7 @@
 }
 
 static void
-initclearmac(Hashalg *unused1, int unused2, Secret *s, uchar *unused3)
+initclearmac(Hashalg *ha, int version, Secret *s, uchar *p)
 {
 	s->maclen = 0;
 	s->mac = nomac;
@@ -1380,11 +1432,22 @@
 	memmove(s->mackey, p, ha->maclen);
 }
 
+static void
+initsha2_256key(Hashalg *ha, int version, Secret *s, uchar *p)
+{
+	if(version == SSL3Version)
+		error("sha256 cannot be used with SSL");
+	s->maclen = ha->maclen;
+	s->mac = hmac_sha2_256;
+	memmove(s->mackey, p, ha->maclen);
+}
+
 static Hashalg hashtab[] =
 {
-	{ "clear", 0, initclearmac, },
-	{ "md5", MD5dlen, initmd5key, },
-	{ "sha1", SHA1dlen, initsha1key, },
+	{ "clear",	0,		initclearmac, },
+	{ "md5",	MD5dlen,	initmd5key, },
+	{ "sha1",	SHA1dlen,	initsha1key, },
+	{ "sha256",	SHA2_256dlen,	initsha2_256key, },
 	{ 0 }
 };
 
@@ -1410,7 +1473,7 @@
 };
 
 static void
-initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *unused1)
+initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *iv)
 {
 	s->enckey = smalloc(sizeof(RC4state));
 	s->enc = rc4enc;
@@ -1420,7 +1483,7 @@
 }
 
 static void
-initDES3key(Encalg *unused1, Secret *s, uchar *p, uchar *iv)
+initDES3key(Encalg *ea, Secret *s, uchar *p, uchar *iv)
 {
 	s->enckey = smalloc(sizeof(DES3state));
 	s->enc = des3enc;
@@ -1430,18 +1493,53 @@
 }
 
 static void
-initclearenc(Encalg *unused1, Secret *s, uchar *unused2, uchar *unused3)
+initAESkey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
 {
+	s->enckey = smalloc(sizeof(AESstate));
+	s->enc = aesenc;
+	s->dec = aesdec;
+	s->block = 16;
+	setupAESstate(s->enckey, p, ea->keylen, iv);
+}
+
+static void
+initccpolykey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{
+	s->enckey = smalloc(sizeof(Chachastate));
 	s->enc = noenc;
 	s->dec = noenc;
+	s->mac = nomac;
+	s->aead_enc = ccpoly_aead_enc;
+	s->aead_dec = ccpoly_aead_dec;
 	s->block = 0;
+	s->maclen = Poly1305dlen;
+	if(ea->ivlen == 0) {
+		/* older draft version, iv is 64-bit sequence number */
+		setupChachastate(s->enckey, p, ea->keylen, nil, 64/8, 20);
+	} else {
+		/* IETF standard, 96-bit iv xored with sequence number */
+		memmove(s->mackey, iv, ea->ivlen);
+		setupChachastate(s->enckey, p, ea->keylen, iv, ea->ivlen, 20);
+	}
 }
 
+static void
+initclearenc(Encalg *ea, Secret *s, uchar *key, uchar *iv)
+{
+	s->enc = noenc;
+	s->dec = noenc;
+	s->block = 0;
+}
+
 static Encalg encrypttab[] =
 {
 	{ "clear", 0, 0, initclearenc },
 	{ "rc4_128", 128/8, 0, initRC4key },
 	{ "3des_ede_cbc", 3 * 8, 8, initDES3key },
+	{ "aes_128_cbc", 128/8, 16, initAESkey },
+	{ "aes_256_cbc", 256/8, 16, initAESkey },
+	{ "ccpoly64_aead", 256/8, 0, initccpolykey },
+	{ "ccpoly96_aead", 256/8, 96/8, initccpolykey },
 	{ 0 }
 };
 
@@ -1543,12 +1641,12 @@
 		if(tr->verset)
 			error("version already set");
 		m = strtol(cb->f[1], nil, 0);
+		if(m < MinProtoVersion || m > MaxProtoVersion)
+			error("unsupported version");
 		if(m == SSL3Version)
-			tr->packMac = sslPackMac;
-		else if(m == TLSVersion)
-			tr->packMac = tlsPackMac;
+			tr->packAAD = sslPackAAD;
 		else
-			error("unsupported version");
+			tr->packAAD = tlsPackAAD;
 		tr->verset = 1;
 		tr->version = m;
 	}else if(strcmp(cb->f[0], "secret") == 0){
@@ -1812,7 +1910,7 @@
 {
 	int s;
 
-if(tr->debug)pprint("tleError %s\n", msg);
+if(tr->debug)pprint("tlsError %s\n", msg);
 	lock(&tr->statelk);
 	s = tr->state;
 	tr->state = SError;
@@ -1951,7 +2049,7 @@
 }
 
 static int
-noenc(Secret *unused1, uchar *unused2, int n)
+noenc(Secret *sec, uchar *buf, int n)
 {
 	return n;
 }
@@ -2017,9 +2115,64 @@
 	des3CBCdecrypt(buf, n, sec->enckey);
 	return (*sec->unpad)(buf, n, 8);
 }
+
+static int
+aesenc(Secret *sec, uchar *buf, int n)
+{
+	n = blockpad(buf, n, 16);
+	aesCBCencrypt(buf, n, sec->enckey);
+	return n;
+}
+
+static int
+aesdec(Secret *sec, uchar *buf, int n)
+{
+	aesCBCdecrypt(buf, n, sec->enckey);
+	return (*sec->unpad)(buf, n, 16);
+}
+
+static void
+ccpoly_aead_setiv(Secret *sec, uchar seq[8])
+{
+	uchar iv[ChachaIVlen];
+	Chachastate *cs;
+	int i;
+
+	cs = (Chachastate*)sec->enckey;
+	if(cs->ivwords == 2){
+		chacha_setiv(cs, seq);
+		return;
+	}
+
+	memmove(iv, sec->mackey, ChachaIVlen);
+	for(i=0; i<8; i++)
+		iv[i+(ChachaIVlen-8)] ^= seq[i];
+
+	chacha_setiv(cs, iv);
+}
+
+static int
+ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *data, int len)
+{
+	ccpoly_aead_setiv(sec, aad);
+	ccpoly_encrypt(data, len, aad, aadlen, data+len, sec->enckey);
+	return len + sec->maclen;
+}
+
+static int
+ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *data, int len)
+{
+	len -= sec->maclen;
+	if(len < 0)
+		return -1;
+	ccpoly_aead_setiv(sec, aad);
+	if(ccpoly_decrypt(data, len, aad, aadlen, data+len, sec->enckey) != 0)
+		return -1;
+	return len;
+}
+
 static DigestState*
-nomac(uchar *unused1, ulong unused2, uchar *unused3, ulong unused4,
-	uchar *unused5, DigestState *unused6)
+nomac(uchar *p, ulong len, uchar *key, ulong keylen, uchar *digest, DigestState *s)
 {
 	return nil;
 }
@@ -2077,32 +2230,35 @@
 	return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
 }
 
-static void
-sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
+static int
+sslPackAAD(u64int seq, uchar *hdr, uchar *aad)
 {
-	DigestState *s;
-	uchar buf[11];
+	put64(aad, seq);
+	aad[8] = hdr[0];
+	aad[9] = hdr[3];
+	aad[10] = hdr[4];
+	return 11;
+}
 
-	memmove(buf, seq, 8);
-	buf[8] = header[0];
-	buf[9] = header[3];
-	buf[10] = header[4];
-
-	s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
-	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
+static int
+tlsPackAAD(u64int seq, uchar *hdr, uchar *aad)
+{
+	put64(aad, seq);
+	aad[8] = hdr[0];
+	aad[9] = hdr[1];
+	aad[10] = hdr[2];
+	aad[11] = hdr[3];
+	aad[12] = hdr[4];
+	return 13;
 }
 
 static void
-tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
+packMac(Secret *sec, uchar *aad, int aadlen, uchar *body, int bodylen, uchar *mac)
 {
 	DigestState *s;
-	uchar buf[13];
 
-	memmove(buf, seq, 8);
-	memmove(&buf[8], header, 5);
-
-	s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
-	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
+	s = (*sec->mac)(aad, aadlen, sec->mackey, sec->maclen, nil, nil);
+	(*sec->mac)(body, bodylen, sec->mackey, sec->maclen, mac, s);
 }
 
 static void
@@ -2115,10 +2271,10 @@
 }
 
 static void
-put64(uchar *p, vlong x)
+put64(uchar *p, u64int x)
 {
-	put32(p, (u32int)(x >> 32));
-	put32(p+4, (u32int)x);
+	put32(p, x >> 32);
+	put32(p+4, x);
 }
 
 static void
@@ -2136,13 +2292,11 @@
 	p[1] = x;
 }
 
-/*
 static u32int
 get32(uchar *p)
 {
 	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
 }
-*/
 
 static int
 get16(uchar *p)
--- a/kern/stub.c
+++ b/kern/stub.c
@@ -145,6 +145,13 @@
 	USED(tag);
 }
 
+void
+setrealloctag(void *v, uintptr tag)
+{
+	USED(v);
+	USED(tag);
+}
+
 int
 postnote(Proc *p, int x, char *msg, int flag)
 {
--- a/kern/sysfile.c
+++ b/kern/sysfile.c
@@ -850,7 +850,6 @@
 	return l;
 }
 
-
 static void
 starterror(void)
 {
@@ -858,6 +857,13 @@
 }
 
 static void
+enderror(void)
+{
+	assert(up->nerrlab == 1);
+	poperror();
+}
+
+static void
 _syserror(void)
 {
 	char *p;
@@ -865,13 +871,6 @@
 	p = up->syserrstr;
 	up->syserrstr = up->errstr;
 	up->errstr = p;
-}
-
-static void
-enderror(void)
-{
-	assert(up->nerrlab == 1);
-	poperror();
 }
 
 int
--- a/libauth/attr.c
+++ b/libauth/attr.c
@@ -139,7 +139,7 @@
 	a = nil;
 	for(i=ntok-1; i>=0; i--){
 		t = tok[i];
-		if((p = strchr(t, '='))){
+		if(p = strchr(t, '=')){
 			*p++ = '\0';
 		//	if(p-2 >= t && p[-2] == ':'){
 		//		p[-2] = '\0';
--- a/libauth/auth_getuserpasswd.c
+++ b/libauth/auth_getuserpasswd.c
@@ -15,7 +15,7 @@
 	for(;;){
 		if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
 			return ret;
-		if(getkey == 0)
+		if(getkey == nil)
 			return ARgiveup;	/* don't know how */
 		if((*getkey)(rpc->arg) < 0)
 			return ARgiveup;	/* user punted */
--- a/libauth/auth_proxy.c
+++ b/libauth/auth_proxy.c
@@ -4,7 +4,7 @@
 #include <auth.h>
 #include "authlocal.h"
 
-enum { 
+enum {
 	ARgiveup = 100,
 };
 
@@ -90,7 +90,6 @@
 
 	if(auth_rpc(rpc, "authinfo", nil, 0) != ARok)
 		return nil;
-	a = nil;
 	if(convM2AI((uchar*)rpc->arg, rpc->narg, &a) == nil){
 		werrstr("bad auth info from factotum");
 		return nil;
@@ -106,7 +105,7 @@
 	for(;;){
 		if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
 			return ret;
-		if(getkey == 0)
+		if(getkey == nil)
 			return ARgiveup;	/* don't know how */
 		if((*getkey)(rpc->arg) < 0)
 			return ARgiveup;	/* user punted */
@@ -124,6 +123,11 @@
 	AuthInfo *a;
 	char oerr[ERRMAX];
 
+	if(rpc == nil){
+		werrstr("fauth_proxy - no factotum");
+		return nil;
+	}
+
 	rerrstr(oerr, sizeof oerr);
 	werrstr("UNKNOWN AUTH ERROR");
 
@@ -140,7 +144,8 @@
 		case ARdone:
 			free(buf);
 			a = auth_getinfo(rpc);
-			errstr(oerr, sizeof oerr);	/* no error, restore whatever was there */
+			/* no error, restore whatever was there */
+			errstr(oerr, sizeof oerr);
 			return a;
 		case ARok:
 			if(write(fd, rpc->arg, rpc->narg) != rpc->narg){
@@ -152,18 +157,21 @@
 			n = 0;
 			memset(buf, 0, AuthRpcMax);
 			while((ret = dorpc(rpc, "write", buf, n, getkey)) == ARtoosmall){
-				if(atoi(rpc->arg) > AuthRpcMax)
+				m = atoi(rpc->arg);
+				if(m <= n || m > AuthRpcMax)
 					break;
-				m = read(fd, buf+n, atoi(rpc->arg)-n);
+				m = read(fd, buf + n, m - n);
 				if(m <= 0){
 					if(m == 0)
-						werrstr("auth_proxy short read: %s", buf);
+						werrstr("auth_proxy short read");
+					else
+						werrstr("auth_proxy read fd: %r");
 					goto Error;
 				}
 				n += m;
 			}
 			if(ret != ARok){
-				werrstr("auth_proxy rpc write: %s: %r", buf);
+				werrstr("auth_proxy rpc write: %r");
 				goto Error;
 			}
 			break;
@@ -191,6 +199,7 @@
 	p = vsmprint(fmt, arg);
 	va_end(arg);
 
+	ai = nil;
 	afd = open("/mnt/factotum/rpc", ORDWR);
 	if(afd < 0){
 		werrstr("opening /mnt/factotum/rpc: %r");
@@ -199,15 +208,11 @@
 	}
 
 	rpc = auth_allocrpc(afd);
-	if(rpc == nil){
-		free(p);
-		return nil;
+	if(rpc){
+		ai = fauth_proxy(fd, rpc, getkey, p);
+		auth_freerpc(rpc);
 	}
-
-	ai = fauth_proxy(fd, rpc, getkey, p);
-	free(p);
-	auth_freerpc(rpc);
 	close(afd);
+	free(p);
 	return ai;
 }
-
--- a/libauth/auth_respond.c
+++ b/libauth/auth_respond.c
@@ -16,7 +16,7 @@
 	for(;;){
 		if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
 			return ret;
-		if(getkey == 0)
+		if(getkey == nil)
 			return ARgiveup;	/* don't know how */
 		if((*getkey)(rpc->arg) < 0)
 			return ARgiveup;	/* user punted */
--- a/libauth/auth_userpasswd.c
+++ b/libauth/auth_userpasswd.c
@@ -11,22 +11,21 @@
  * this was copied from inet's guard.
  */
 static void
-netresp(char *key, long chal, char *answer)
+netresp(char key[DESKEYLEN], long chal, char *answer)
 {
 	uchar buf[8];
 
-	memset(buf, 0, 8);
-	sprint((char *)buf, "%lud", chal);
+	memset(buf, 0, sizeof buf);
+	snprint((char *)buf, sizeof buf, "%lud", chal);
 	if(encrypt(key, buf, 8) < 0)
 		abort();
-	chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
-	sprint(answer, "%.8lux", chal);
+	sprint(answer, "%.8ux", buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]);
 }
 
 AuthInfo*
 auth_userpasswd(char *user, char *passwd)
 {
-	char key[DESKEYLEN], resp[16];
+	char resp[16], key[DESKEYLEN];
 	AuthInfo *ai;
 	Chalstate *ch;
 
@@ -38,9 +37,9 @@
 	if((ch = auth_challenge("user=%q proto=p9cr role=server", user)) == nil)
 		return nil;
 
-	passtokey(key, passwd);
+	passtodeskey(key, passwd);
 	netresp(key, atol(ch->chal), resp);
-	memset(key, 0, sizeof key);
+	memset(key, 0, sizeof(key));
 
 	ch->resp = resp;
 	ch->nresp = strlen(resp);
--- a/libauthsrv/Makefile
+++ b/libauthsrv/Makefile
@@ -16,11 +16,19 @@
 	nvcsum.$O\
 	opasstokey.$O\
 	passtokey.$O\
+	_asgetpakkey.$O\
+	_asgetresp.$O\
+	_asrequest.$O\
+	authpak.$O\
+	form1.$O\
+	
 
 default: $(LIB)
 $(LIB): $(OFILES)
 	$(AR) r $(LIB) $(OFILES)
 	$(RANLIB) $(LIB)
+
+authpak.$O:	msqrt.mpc edwards.mpc ed448.mpc decaf.mpc elligator2.mpc spake2ee.mpc
 
 %.$O: %.c
 	$(CC) $(CFLAGS) $*.c
--- /dev/null
+++ b/libauthsrv/_asgetpakkey.c
@@ -1,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asgetpakkey(int fd, Ticketreq *tr, Authkey *a)
+{
+	uchar y[PAKYLEN];
+	PAKpriv p;
+	int type;
+
+	type = tr->type;
+	tr->type = AuthPAK;
+	if(_asrequest(fd, tr) != 0){
+		tr->type = type;
+		return -1;
+	}
+	tr->type = type;
+	authpak_new(&p, a, y, 1);
+	if(write(fd, y, PAKYLEN) != PAKYLEN
+	|| _asrdresp(fd, (char*)y, PAKYLEN) != PAKYLEN){
+		memset(&p, 0, sizeof(p));
+		return -1;
+	}
+	return authpak_finish(&p, a, y);
+}
--- /dev/null
+++ b/libauthsrv/_asgetresp.c
@@ -1,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asgetresp(int fd, Ticket *t, Authenticator *a, Authkey *k)
+{
+	char buf[MAXTICKETLEN+MAXAUTHENTLEN], err[ERRMAX];
+	int n, m;
+
+	memset(t, 0, sizeof(Ticket));
+	if(a != nil)
+		memset(a, 0, sizeof(Authenticator));
+
+	strcpy(err, "AS protocol botch");
+	errstr(err, ERRMAX);
+
+	if(_asrdresp(fd, buf, 0) < 0)
+		return -1;
+
+	for(n = 0; (m = convM2T(buf, n, t, k)) <= 0; n += m){
+		m = -m;
+		if(m <= n || m > sizeof(buf))
+			return -1;
+		m -= n;
+		if(readn(fd, buf+n, m) != m)
+			return -1;
+	}
+
+	if(a != nil){
+		for(n = 0; (m = convM2A(buf, n, a, t)) <= 0; n += m){
+			m = -m;
+			if(m <= n || m > sizeof(buf))
+				return -1;
+			m -= n;
+			if(readn(fd, buf+n, m) != m)
+				return -1;
+		}
+	}
+
+	errstr(err, ERRMAX);
+
+	return 0;
+}
--- a/libauthsrv/_asgetticket.c
+++ b/libauthsrv/_asgetticket.c
@@ -2,15 +2,36 @@
 #include <libc.h>
 #include <authsrv.h>
 
-static char *pbmsg = "AS protocol botch";
-
 int
-_asgetticket(int fd, char *trbuf, char *tbuf)
+_asgetticket(int fd, Ticketreq *tr, char *tbuf, int tbuflen)
 {
-	if(write(fd, trbuf, TICKREQLEN) < 0){
-		close(fd);
-		werrstr(pbmsg);
+	char err[ERRMAX];
+	int i, n, m, r;
+
+	strcpy(err, "AS protocol botch");
+	errstr(err, ERRMAX);
+
+	if(_asrequest(fd, tr) < 0)
 		return -1;
+	if(_asrdresp(fd, tbuf, 0) < 0)
+		return -1;
+
+	r = 0;
+	for(i = 0; i<2; i++){
+		for(n=0; (m = convM2T(tbuf, n, nil, nil)) <= 0; n += m){
+			m = -m;
+			if(m <= n || m > tbuflen)
+				return -1;
+			m -= n;
+			if(readn(fd, tbuf+n, m) != m)
+				return -1;
+		}
+		r += n;
+		tbuf += n;
+		tbuflen -= n;
 	}
-	return _asrdresp(fd, tbuf, 2*TICKETLEN);
+
+	errstr(err, ERRMAX);
+
+	return r;
 }
--- /dev/null
+++ b/libauthsrv/_asrequest.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asrequest(int fd, Ticketreq *tr)
+{
+	char trbuf[TICKREQLEN];
+	int n;
+
+	n = convTR2M(tr, trbuf, sizeof(trbuf));
+	if(write(fd, trbuf, n) != n)
+		return -1;
+
+	return 0;
+}
--- a/libauthsrv/authdial.c
+++ b/libauthsrv/authdial.c
@@ -7,25 +7,45 @@
 int
 authdial(char *netroot, char *dom)
 {
-	char server[Ndbvlen];
-	Ndbtuple *nt;
+	Ndbtuple *t, *nt;
+	char *p;
+	int rv;
 
-	
-	if(dom != nil){
-		/* look up an auth server in an authentication domain */
-		nt = csgetval(netroot, "authdom", dom, "auth", server);
-
-		/* if that didn't work, just try the IP domain */
-		if(nt == nil)
-			nt = csgetval(netroot, "dom", dom, "auth", server);
-		if(nt == nil){
-			werrstr("no auth server found for %s", dom);
-			return -1;
-		}
-		ndbfree(nt);
-		return dial(netmkaddr(server, netroot, "ticket"), 0, 0, 0);
-	} else {
+	if(dom == nil)
 		/* look for one relative to my machine */
 		return dial(netmkaddr("$auth", netroot, "ticket"), 0, 0, 0);
+
+	/* look up an auth server in an authentication domain */
+	p = csgetvalue(netroot, "authdom", dom, "auth", &t);
+
+	/* if that didn't work, just try the IP domain */
+	if(p == nil)
+		p = csgetvalue(netroot, "dom", dom, "auth", &t);
+
+	/*
+	 * if that didn't work, try p9auth.$dom.  this is very helpful if
+	 * you can't edit /lib/ndb.
+	 */
+	if(p == nil) {
+		p = smprint("p9auth.%s", dom);
+		t = ndbnew("auth", p);
 	}
+	free(p);
+
+	/*
+	 * allow multiple auth= attributes for backup auth servers,
+	 * try each one in order.
+	 */
+	rv = -1;
+	for(nt = t; nt != nil; nt = nt->entry) {
+		if(strcmp(nt->attr, "auth") == 0) {
+			p = netmkaddr(nt->val, netroot, "ticket");
+			rv = dial(p, 0, 0, 0);
+			if(rv >= 0)
+				break;
+		}
+	}
+	ndbfree(t);
+
+	return rv;
 }
--- /dev/null
+++ b/libauthsrv/authpak.c
@@ -1,0 +1,214 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+#include <authsrv.h>
+
+#include "msqrt.mpc"
+#include "decaf.mpc"
+#include "edwards.mpc"
+#include "elligator2.mpc"
+#include "spake2ee.mpc"
+#include "ed448.mpc"
+
+typedef struct PAKcurve PAKcurve;
+struct PAKcurve
+{
+	Lock;
+	mpint	*P;
+	mpint	*A;
+	mpint	*D;
+	mpint	*X;
+	mpint	*Y;
+};
+
+static PAKcurve*
+authpak_curve(void)
+{
+	static PAKcurve a;
+
+	lock(&a);
+	if(a.P == nil){
+		a.P = mpnew(0);
+		a.A = mpnew(0);
+		a.D = mpnew(0);
+		a.X = mpnew(0);
+		a.Y = mpnew(0);
+		ed448_curve(a.P, a.A, a.D, a.X, a.Y);
+		a.P = mpfield(a.P);
+	}
+	unlock(&a);
+	return &a;
+}
+
+void
+authpak_hash(Authkey *k, char *u)
+{
+	static char info[] = "Plan 9 AuthPAK hash";
+	uchar *bp, salt[SHA2_256dlen], h[2*PAKSLEN];
+	mpint *H, *PX,*PY,*PZ,*PT;
+	PAKcurve *c;
+
+	H = mpnew(0);
+	PX = mpnew(0);
+	PY = mpnew(0);
+	PZ = mpnew(0);
+	PT = mpnew(0);
+
+	sha2_256((uchar*)u, strlen(u), salt, nil);
+
+	hkdf_x(	salt, SHA2_256dlen,
+		(uchar*)info, sizeof(info)-1,
+		k->aes, AESKEYLEN,
+		h, sizeof(h),
+		hmac_sha2_256, SHA2_256dlen);
+
+	c = authpak_curve();
+
+	betomp(h + 0*PAKSLEN, PAKSLEN, H);		/* HM */
+	spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT);	/* PM */
+
+	bp = k->pakhash;
+	mptober(PX, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PY, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PZ, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PT, bp, PAKSLEN), bp += PAKSLEN;
+
+	betomp(h + 1*PAKSLEN, PAKSLEN, H);		/* HN */
+	spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT);	/* PN */
+
+	mptober(PX, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PY, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PZ, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PT, bp, PAKSLEN);
+
+	mpfree(PX);
+	mpfree(PY);
+	mpfree(PZ);
+	mpfree(PT);
+	mpfree(H);
+}
+
+void
+authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient)
+{
+	mpint *PX,*PY,*PZ,*PT, *X, *Y;
+	PAKcurve *c;
+	uchar *bp;
+
+	memset(p, 0, sizeof(PAKpriv));
+	p->isclient = isclient != 0;
+
+	X = mpnew(0);
+	Y = mpnew(0);
+
+	PX = mpnew(0);
+	PY = mpnew(0);
+	PZ = mpnew(0);
+	PT = mpnew(0);
+
+	PX->flags |= MPtimesafe;
+	PY->flags |= MPtimesafe;
+	PZ->flags |= MPtimesafe;
+	PT->flags |= MPtimesafe;
+
+	bp = k->pakhash + PAKPLEN*(p->isclient == 0);
+	betomp(bp, PAKSLEN, PX), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PY), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PZ), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PT);
+
+	c = authpak_curve();
+
+	X->flags |= MPtimesafe;
+	mpnrand(c->P, genrandom, X);
+
+	spake2ee_1(c->P,c->A,c->D, X, c->X,c->Y, PX,PY,PZ,PT, Y);
+
+	mptober(X, p->x, PAKXLEN);
+	mptober(Y, p->y, PAKYLEN);
+
+	memmove(y, p->y, PAKYLEN);
+
+	mpfree(PX);
+	mpfree(PY);
+	mpfree(PZ);
+	mpfree(PT);
+
+	mpfree(X);
+	mpfree(Y);
+}
+
+int
+authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN])
+{
+	static char info[] = "Plan 9 AuthPAK key";
+	uchar *bp, z[PAKSLEN], salt[SHA2_256dlen];
+	mpint *PX,*PY,*PZ,*PT, *X, *Y, *Z, *ok;
+	DigestState *s;
+	PAKcurve *c;
+	int ret;
+
+	X = mpnew(0);
+	Y = mpnew(0);
+	Z = mpnew(0);
+	ok = mpnew(0);
+
+	PX = mpnew(0);
+	PY = mpnew(0);
+	PZ = mpnew(0);
+	PT = mpnew(0);
+
+	PX->flags |= MPtimesafe;
+	PY->flags |= MPtimesafe;
+	PZ->flags |= MPtimesafe;
+	PT->flags |= MPtimesafe;
+
+	bp = k->pakhash + PAKPLEN*(p->isclient != 0);
+	betomp(bp, PAKSLEN, PX), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PY), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PZ), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PT);
+
+	Z->flags |= MPtimesafe;
+	X->flags |= MPtimesafe;
+	betomp(p->x, PAKXLEN, X);
+
+	betomp(y, PAKYLEN, Y);
+
+	c = authpak_curve();
+	spake2ee_2(c->P,c->A,c->D, PX,PY,PZ,PT, X, Y, ok, Z);
+
+	if(mpcmp(ok, mpzero) == 0){
+		ret = -1;
+		goto out;
+	}
+
+	mptober(Z, z, sizeof(z));
+
+	s = sha2_256(p->isclient ? p->y : y, PAKYLEN, nil, nil);
+	sha2_256(p->isclient ? y : p->y, PAKYLEN, salt, s);
+
+	hkdf_x(	salt, SHA2_256dlen,
+		(uchar*)info, sizeof(info)-1,
+		z, sizeof(z),
+		k->pakkey, PAKKEYLEN,
+		hmac_sha2_256, SHA2_256dlen);
+
+	ret = 0;
+out:
+	memset(z, 0, sizeof(z));
+	memset(p, 0, sizeof(PAKpriv));
+
+	mpfree(PX);
+	mpfree(PY);
+	mpfree(PZ);
+	mpfree(PT);
+
+	mpfree(X);
+	mpfree(Y);
+	mpfree(Z);
+	mpfree(ok);
+
+	return ret;
+}
--- a/libauthsrv/convA2M.c
+++ b/libauthsrv/convA2M.c
@@ -2,24 +2,35 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
+extern int form1B2M(char *ap, int n, uchar key[32]);
 
 int
-convA2M(Authenticator *f, char *ap, char *key)
+convA2M(Authenticator *f, char *ap, int n, Ticket *t)
 {
-	int n;
 	uchar *p;
 
+	if(n < 1+CHALLEN)
+		return 0;
+
 	p = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	LONG(id);
-	n = p - (uchar*)ap;
-	if(key)
-		encrypt(key, ap, n);
-	return n;
+	*p++ = f->num;
+	memmove(p, f->chal, CHALLEN), p += CHALLEN;
+	switch(t->form){
+	case 0:
+		if(n < 1+CHALLEN+4)
+			return 0;
+
+		memset(p, 0, 4), p += 4;	/* unused id field */
+		n = p - (uchar*)ap;
+		encrypt(t->key, ap, n);
+		return n;
+	case 1:
+		if(n < 12+CHALLEN+NONCELEN+16)
+			return 0;
+
+		memmove(p, f->rand, NONCELEN), p += NONCELEN;
+		return form1B2M(ap, (char*)p - ap, t->key);
+	}
+
+	return 0;
 }
--- a/libauthsrv/convM2A.c
+++ b/libauthsrv/convM2A.c
@@ -2,22 +2,35 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
+extern int form1M2B(char *ap, int n, uchar key[32]);
 
-void
-convM2A(char *ap, Authenticator *f, char *key)
+int
+convM2A(char *ap, int n, Authenticator *f, Ticket *t)
 {
-	uchar *p;
+	uchar buf[MAXAUTHENTLEN], *p;
+	int m;
 
-	if(key)
-		decrypt(key, ap, AUTHENTLEN);
-	p = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	LONG(id);
-	USED(p);
+	memset(f, 0, sizeof(Authenticator));
+	if(t->form == 0){
+		m = 1+CHALLEN+4;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		decrypt(t->key, buf, m);
+	} else {
+		m = 12+CHALLEN+NONCELEN+16;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		if(form1M2B((char*)buf, m, t->key) < 0)
+			return m;
+	}
+	p = buf;
+	f->num = *p++;
+	memmove(f->chal, p, CHALLEN);
+	p += CHALLEN;
+	if(t->form == 1)
+		memmove(f->rand, p, NONCELEN);
+
+	return m;
 }
--- a/libauthsrv/convM2PR.c
+++ b/libauthsrv/convM2PR.c
@@ -2,27 +2,38 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
+extern int form1M2B(char *ap, int n, uchar key[32]);
 
-void
-convM2PR(char *ap, Passwordreq *f, char *key)
+int
+convM2PR(char *ap, int n, Passwordreq *f, Ticket *t)
 {
-	uchar *p;
+	uchar *p, buf[MAXPASSREQLEN];
+	int m;
 
-	p = (uchar*)ap;
-	if(key)
-		decrypt(key, ap, PASSREQLEN);
-	CHAR(num);
-	STRING(old, ANAMELEN);
+	memset(f, 0, sizeof(Passwordreq));
+	if(t->form == 0){
+		m = 1+2*ANAMELEN+1+SECRETLEN;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		decrypt(t->key, buf, m);
+	} else {
+		m = 12+2*ANAMELEN+1+SECRETLEN+16;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		if(form1M2B((char*)buf, m, t->key) < 0)
+			return m;
+	}
+	p = buf;
+	f->num = *p++;
+	memmove(f->old, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->new, p, ANAMELEN), p += ANAMELEN;
+	f->changesecret = *p++;
+	memmove(f->secret, p, SECRETLEN);
 	f->old[ANAMELEN-1] = 0;
-	STRING(new, ANAMELEN);
 	f->new[ANAMELEN-1] = 0;
-	CHAR(changesecret);
-	STRING(secret, SECRETLEN);
 	f->secret[SECRETLEN-1] = 0;
-	USED(p);
+
+	return m;
 }
--- a/libauthsrv/convM2T.c
+++ b/libauthsrv/convM2T.c
@@ -2,27 +2,50 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
+extern int form1check(char *ap, int n);
+extern int form1M2B(char *ap, int n, uchar key[32]);
 
-void
-convM2T(char *ap, Ticket *f, char *key)
+int
+convM2T(char *ap, int n, Ticket *f, Authkey *k)
 {
-	uchar *p;
+	uchar buf[MAXTICKETLEN], *p;
+	int m;
 
-	if(key)
-		decrypt(key, ap, TICKETLEN);
-	p = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	STRING(cuid, ANAMELEN);
+	if(f != nil)
+		memset(f, 0, sizeof(Ticket));
+
+	if(n < 8)
+		return -8;
+
+	if(form1check(ap, n) < 0){
+		m = 1+CHALLEN+2*ANAMELEN+DESKEYLEN;
+		if(n < m)
+			return -m;
+		if(f == nil || k == nil)
+			return m;
+		f->form = 0;
+		memmove(buf, ap, m);
+		decrypt(k->des, buf, m);
+	} else {
+		m = 12+CHALLEN+2*ANAMELEN+NONCELEN+16;
+		if(n < m)
+			return -m;
+		if(f == nil || k == nil)
+			return m;
+		f->form = 1;
+		memmove(buf, ap, m);
+		if(form1M2B((char*)buf, m, k->pakkey) < 0)
+			return m;
+	}
+	p = buf;
+	f->num = *p++;
+	memmove(f->chal, p, CHALLEN), p += CHALLEN;
+	memmove(f->cuid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->suid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->key, p, f->form == 0 ? DESKEYLEN : NONCELEN);
+
 	f->cuid[ANAMELEN-1] = 0;
-	STRING(suid, ANAMELEN);
 	f->suid[ANAMELEN-1] = 0;
-	STRING(key, DESKEYLEN);
-	USED(p);
-}
 
+	return m;
+}
--- a/libauthsrv/convM2TR.c
+++ b/libauthsrv/convM2TR.c
@@ -2,27 +2,28 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
-
-void
-convM2TR(char *ap, Ticketreq *f)
+int
+convM2TR(char *ap, int n, Ticketreq *f)
 {
 	uchar *p;
 
+	memset(f, 0, sizeof(Ticketreq));
+	if(n < TICKREQLEN)
+		return -TICKREQLEN;
+
 	p = (uchar*)ap;
-	CHAR(type);
-	STRING(authid, ANAMELEN);
+	f->type = *p++;
+	memmove(f->authid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->authdom, p, DOMLEN), p += DOMLEN;
+	memmove(f->chal, p, CHALLEN), p += CHALLEN;
+	memmove(f->hostid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->uid, p, ANAMELEN), p += ANAMELEN;
+
 	f->authid[ANAMELEN-1] = 0;
-	STRING(authdom, DOMLEN);
 	f->authdom[DOMLEN-1] = 0;
-	STRING(chal, CHALLEN);
-	STRING(hostid, ANAMELEN);
 	f->hostid[ANAMELEN-1] = 0;
-	STRING(uid, ANAMELEN);
 	f->uid[ANAMELEN-1] = 0;
-	USED(p);
+	n = p - (uchar*)ap;
+
+	return n;
 }
--- a/libauthsrv/convPR2M.c
+++ b/libauthsrv/convPR2M.c
@@ -2,27 +2,33 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
+extern int form1B2M(char *ap, int n, uchar key[32]);
 
 int
-convPR2M(Passwordreq *f, char *ap, char *key)
+convPR2M(Passwordreq *f, char *ap, int n, Ticket *t)
 {
-	int n;
 	uchar *p;
 
+	if(n < 1+2*ANAMELEN+1+SECRETLEN)
+		return 0;
+
 	p = (uchar*)ap;
-	CHAR(num);
-	STRING(old, ANAMELEN);
-	STRING(new, ANAMELEN);
-	CHAR(changesecret);
-	STRING(secret, SECRETLEN);
-	n = p - (uchar*)ap;
-	if(key)
-		encrypt(key, ap, n);
-	return n;
+	*p++ = f->num;
+	memmove(p, f->old, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->new, ANAMELEN), p += ANAMELEN;
+	*p++ = f->changesecret;
+	memmove(p, f->secret, SECRETLEN), p += SECRETLEN;
+	switch(t->form){
+	case 0:
+		n = p - (uchar*)ap;
+		encrypt(t->key, ap, n);
+		return n;
+	case 1:
+		if(n < 12+2*ANAMELEN+1+SECRETLEN+16)
+			return 0;
+		return form1B2M(ap, p - (uchar*)ap, t->key);
+	}
+
+	return 0;
 }
 
--- a/libauthsrv/convT2M.c
+++ b/libauthsrv/convT2M.c
@@ -1,27 +1,39 @@
 #include <u.h>
 #include <libc.h>
 #include <authsrv.h>
+#include <libsec.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
+extern int form1B2M(char *ap, int n, uchar key[32]);
 
 int
-convT2M(Ticket *f, char *ap, char *key)
+convT2M(Ticket *f, char *ap, int n, Authkey *key)
 {
-	int n;
 	uchar *p;
 
+	if(n < 1+CHALLEN+2*ANAMELEN)
+		return 0;
+
 	p = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	STRING(cuid, ANAMELEN);
-	STRING(suid, ANAMELEN);
-	STRING(key, DESKEYLEN);
-	n = p - (uchar*)ap;
-	if(key)
-		encrypt(key, ap, n);
-	return n;
+	*p++ = f->num;
+	memmove(p, f->chal, CHALLEN), p += CHALLEN;
+	memmove(p, f->cuid, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->suid, ANAMELEN), p += ANAMELEN;
+	switch(f->form){
+	case 0:
+		if(n < 1+CHALLEN+2*ANAMELEN+DESKEYLEN)
+			return 0;
+
+		memmove(p, f->key, DESKEYLEN), p += DESKEYLEN;
+		n = p - (uchar*)ap;
+		encrypt(key->des, ap, n);
+		return n;
+	case 1:
+		if(n < 12+CHALLEN+2*ANAMELEN+NONCELEN+16)
+			return 0;
+
+		memmove(p, f->key, NONCELEN), p += NONCELEN;
+		return form1B2M(ap, p - (uchar*)ap, key->pakkey);
+	}
+
+	return 0;
 }
--- a/libauthsrv/convTR2M.c
+++ b/libauthsrv/convTR2M.c
@@ -2,26 +2,22 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
-
 int
-convTR2M(Ticketreq *f, char *ap)
+convTR2M(Ticketreq *f, char *ap, int n)
 {
-	int n;
 	uchar *p;
 
+	if(n < TICKREQLEN)
+		return 0;
+
 	p = (uchar*)ap;
-	CHAR(type);
-	STRING(authid, 28);	/* BUG */
-	STRING(authdom, DOMLEN);
-	STRING(chal, CHALLEN);
-	STRING(hostid, 28);	/* BUG */
-	STRING(uid, 28);	/* BUG */
+	*p++ = f->type;
+	memmove(p, f->authid, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->authdom, DOMLEN), p += DOMLEN;
+	memmove(p, f->chal, CHALLEN), p += CHALLEN;
+	memmove(p, f->hostid, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->uid, ANAMELEN), p += ANAMELEN;
 	n = p - (uchar*)ap;
+
 	return n;
 }
-
--- /dev/null
+++ b/libauthsrv/decaf.mpc
@@ -1,0 +1,130 @@
+void decaf_neg(mpint *p, mpint *n, mpint *r){
+	mpint *m = mpnew(0);
+	mpmodsub(mpzero, r, p, m);
+	mpint *tmp1 = mpnew(0);
+	mpsub(p, mpone, tmp1);
+	mpright(tmp1, 1, tmp1);
+	mpsel(-mpcmp(n, tmp1) >> (sizeof(int)*8-1), m, r, r);
+	mpfree(tmp1);
+	mpfree(m);
+	}
+void decaf_encode(mpint *p, mpint *a, mpint *d, mpint *X, mpint *Y, mpint *Z, mpint *T, mpint *s){
+	mpint *u = mpnew(0);
+	mpint *r = mpnew(0);
+	mpint *tmp1 = mpnew(0);
+	mpint *tmp2 = mpnew(0);
+	mpint *tmp3 = mpnew(0);
+	mpmodsub(a, d, p, tmp3);
+	mpint *tmp4 = mpnew(0);
+	mpmodadd(Z, Y, p, tmp4);
+	mpmodmul(tmp3, tmp4, p, tmp2);
+	mpfree(tmp3);
+	mpfree(tmp4);
+	tmp4 = mpnew(0);
+	mpmodsub(Z, Y, p, tmp4);
+	mpmodmul(tmp2, tmp4, p, tmp1);
+	mpfree(tmp2);
+	mpfree(tmp4);
+	misqrt(tmp1, p, r);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	mpmodsub(a, d, p, tmp1);
+	mpmodmul(tmp1, r, p, u);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	tmp4 = mpnew(0);
+	mpmodadd(u, u, p, tmp4); // 2*u
+	mpmodmul(tmp4, Z, p, tmp1);
+	mpfree(tmp4);
+	mpmodsub(mpzero, tmp1, p, tmp1);
+	decaf_neg(p, tmp1, r);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	tmp4 = mpnew(0);
+	tmp2 = mpnew(0);
+	tmp3 = mpnew(0);
+	mpmodmul(a, Z, p, tmp3);
+	mpmodmul(tmp3, X, p, tmp2);
+	mpfree(tmp3);
+	tmp3 = mpnew(0);
+	mpint *tmp5 = mpnew(0);
+	mpmodmul(d, Y, p, tmp5);
+	mpmodmul(tmp5, T, p, tmp3);
+	mpfree(tmp5);
+	mpmodsub(tmp2, tmp3, p, tmp2);
+	mpfree(tmp3);
+	mpmodmul(r, tmp2, p, tmp4);
+	mpfree(tmp2);
+	mpmodadd(tmp4, Y, p, tmp4);
+	mpmodmul(u, tmp4, p, tmp1);
+	mpfree(tmp4);
+	tmp4 = mpnew(0);
+	mpinvert(a, p, tmp4);
+	mpmodmul(tmp1, tmp4, p, s);
+	mpfree(tmp4);
+	mpfree(tmp1);
+	decaf_neg(p, s, s);
+	mpfree(u);
+	mpfree(r);
+	}
+void decaf_decode(mpint *p, mpint *a, mpint *d, mpint *s, mpint *ok, mpint *X, mpint *Y, mpint *Z, mpint *T){
+	mpint *w = mpnew(0);
+	mpint *v = mpnew(0);
+	mpint *u = mpnew(0);
+	mpint *ss = mpnew(0);
+	mpint *tmp1 = mpnew(0);
+	mpsub(p, mpone, tmp1);
+	mpright(tmp1, 1, tmp1);
+	if(mpcmp(s, tmp1) > 0){
+		mpassign(mpzero, ok);
+		}else{
+		mpmodmul(s, s, p, ss);
+		mpmodmul(a, ss, p, Z);
+		mpmodadd(mpone, Z, p, Z);
+		mpmodmul(Z, Z, p, u);
+		mpint *tmp2 = mpnew(0);
+		mpint *tmp3 = mpnew(0);
+		mpint *tmp4 = mpnew(0);
+		uitomp(4UL, tmp4);
+		mpmodmul(tmp4, d, p, tmp3);
+		mpfree(tmp4);
+		mpmodmul(tmp3, ss, p, tmp2);
+		mpfree(tmp3);
+		mpmodsub(u, tmp2, p, u);
+		mpfree(tmp2);
+		mpmodmul(u, ss, p, v);
+		if(mpcmp(v, mpzero) == 0){
+			mpassign(mpone, ok);
+			}else{
+			msqrt(v, p, ok);
+			if(mpcmp(ok, mpzero) != 0){
+				mpinvert(ok, p, v);
+				mpassign(mpone, ok);
+				}
+			}
+		if(mpcmp(ok, mpzero) != 0){
+			mpint *tmp5 = mpnew(0);
+			mpmodmul(u, v, p, tmp5);
+			decaf_neg(p, tmp5, v);
+			mpfree(tmp5);
+			tmp5 = mpnew(0);
+			mpmodmul(v, s, p, tmp5);
+			mpint *tmp6 = mpnew(0);
+			mpmodsub(mptwo, Z, p, tmp6);
+			mpmodmul(tmp5, tmp6, p, w);
+			mpfree(tmp5);
+			mpfree(tmp6);
+			if(mpcmp(s, mpzero) == 0){
+				mpmodadd(w, mpone, p, w);
+				}
+			mpmodadd(s, s, p, X); // 2*s
+			mpmodmul(w, Z, p, Y);
+			mpmodmul(w, X, p, T);
+			}
+		}
+	mpfree(tmp1);
+	mpfree(w);
+	mpfree(v);
+	mpfree(u);
+	mpfree(ss);
+	}
--- /dev/null
+++ b/libauthsrv/ed448.mpc
@@ -1,0 +1,8 @@
+void ed448_curve(mpint *p, mpint *a, mpint *d, mpint *x, mpint *y){
+	strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", nil, 16, p);
+	mpassign(mpone, a);
+	uitomp(39081UL, d);
+	d->sign = -1;
+	strtomp("297EA0EA2692FF1B4FAFF46098453A6A26ADF733245F065C3C59D0709CECFA96147EAAF3932D94C63D96C170033F4BA0C7F0DE840AED939F", nil, 16, x);
+	uitomp(19UL, y);
+	}
--- /dev/null
+++ b/libauthsrv/edwards.mpc
@@ -1,0 +1,98 @@
+void edwards_add(mpint *p, mpint *a, mpint *d, mpint *X1, mpint *Y1, mpint *Z1, mpint *T1, mpint *X2, mpint *Y2, mpint *Z2, mpint *T2, mpint *X3, mpint *Y3, mpint *Z3, mpint *T3){
+	mpint *H = mpnew(0);
+	mpint *G = mpnew(0);
+	mpint *F = mpnew(0);
+	mpint *E = mpnew(0);
+	mpint *D = mpnew(0);
+	mpint *C = mpnew(0);
+	mpint *B = mpnew(0);
+	mpint *A = mpnew(0);
+	mpmodmul(X1, X2, p, A);
+	mpmodmul(Y1, Y2, p, B);
+	mpint *tmp1 = mpnew(0);
+	mpmodmul(d, T1, p, tmp1);
+	mpmodmul(tmp1, T2, p, C);
+	mpfree(tmp1);
+	mpmodmul(Z1, Z2, p, D);
+	tmp1 = mpnew(0);
+	mpmodadd(X1, Y1, p, tmp1);
+	mpint *tmp2 = mpnew(0);
+	mpmodadd(X2, Y2, p, tmp2);
+	mpmodmul(tmp1, tmp2, p, E);
+	mpfree(tmp1);
+	mpfree(tmp2);
+	mpmodsub(E, A, p, E);
+	mpmodsub(E, B, p, E);
+	mpmodsub(D, C, p, F);
+	mpmodadd(D, C, p, G);
+	mpmodmul(a, A, p, H);
+	mpmodsub(B, H, p, H);
+	mpmodmul(E, F, p, X3);
+	mpmodmul(G, H, p, Y3);
+	mpmodmul(F, G, p, Z3);
+	mpmodmul(E, H, p, T3);
+	mpfree(H);
+	mpfree(G);
+	mpfree(F);
+	mpfree(E);
+	mpfree(D);
+	mpfree(C);
+	mpfree(B);
+	mpfree(A);
+	}
+void edwards_sel(mpint *s, mpint *X1, mpint *Y1, mpint *Z1, mpint *T1, mpint *X2, mpint *Y2, mpint *Z2, mpint *T2, mpint *X3, mpint *Y3, mpint *Z3, mpint *T3){
+	mpsel(mpcmp(s, mpzero), X1, X2, X3);
+	mpsel(mpcmp(s, mpzero), Y1, Y2, Y3);
+	mpsel(mpcmp(s, mpzero), Z1, Z2, Z3);
+	mpsel(mpcmp(s, mpzero), T1, T2, T3);
+	}
+void edwards_new(mpint *x, mpint *y, mpint *z, mpint *t, mpint *X, mpint *Y, mpint *Z, mpint *T){
+	mpassign(x, X);
+	mpassign(y, Y);
+	mpassign(z, Z);
+	mpassign(t, T);
+	}
+void edwards_scale(mpint *p, mpint *a, mpint *d, mpint *s, mpint *X1, mpint *Y1, mpint *Z1, mpint *T1, mpint *X3, mpint *Y3, mpint *Z3, mpint *T3){
+	mpint *j = mpnew(0);
+	mpint *k = mpnew(0);
+	mpint *T4 = mpnew(0);
+	mpint *Z4 = mpnew(0);
+	mpint *Y4 = mpnew(0);
+	mpint *X4 = mpnew(0);
+	mpint *T2 = mpnew(0);
+	mpint *Z2 = mpnew(0);
+	mpint *Y2 = mpnew(0);
+	mpint *X2 = mpnew(0);
+	edwards_new(X1, Y1, Z1, T1, X2, Y2, Z2, T2);
+	edwards_new(mpzero, mpone, mpone, mpzero, X4, Y4, Z4, T4);
+	mpint *tmp1 = mpnew(0);
+	mpmod(s, mptwo, tmp1);
+	edwards_sel(tmp1, X2, Y2, Z2, T2, X4, Y4, Z4, T4, X3, Y3, Z3, T3);
+	mpfree(tmp1);
+	mpright(s, 1, k);
+	mpright(p, 1, j);
+	for(;;){
+		if(mpcmp(j, mpzero) != 0){
+			edwards_add(p, a, d, X2, Y2, Z2, T2, X2, Y2, Z2, T2, X2, Y2, Z2, T2);
+			edwards_add(p, a, d, X2, Y2, Z2, T2, X3, Y3, Z3, T3, X4, Y4, Z4, T4);
+			mpint *tmp2 = mpnew(0);
+			mpmod(k, mptwo, tmp2);
+			edwards_sel(tmp2, X4, Y4, Z4, T4, X3, Y3, Z3, T3, X3, Y3, Z3, T3);
+			mpfree(tmp2);
+			mpright(k, 1, k);
+			mpright(j, 1, j);
+			}else{
+			break;
+			}
+		}
+	mpfree(j);
+	mpfree(k);
+	mpfree(T4);
+	mpfree(Z4);
+	mpfree(Y4);
+	mpfree(X4);
+	mpfree(T2);
+	mpfree(Z2);
+	mpfree(Y2);
+	mpfree(X2);
+	}
--- /dev/null
+++ b/libauthsrv/elligator2.mpc
@@ -1,0 +1,129 @@
+void elligator2(mpint *p, mpint *a, mpint *d, mpint *n, mpint *r0, mpint *X, mpint *Y, mpint *Z, mpint *T){
+	mpint *t = mpnew(0);
+	mpint *s = mpnew(0);
+	mpint *e = mpnew(0);
+	mpint *c = mpnew(0);
+	mpint *ND = mpnew(0);
+	mpint *N = mpnew(0);
+	mpint *D = mpnew(0);
+	mpint *r = mpnew(0);
+	mpint *tmp1 = mpnew(0);
+	mpmodmul(n, r0, p, tmp1);
+	mpmodmul(tmp1, r0, p, r);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	mpmodmul(d, r, p, tmp1);
+	mpmodadd(tmp1, a, p, tmp1);
+	mpmodsub(tmp1, d, p, tmp1);
+	mpint *tmp2 = mpnew(0);
+	mpmodmul(d, r, p, tmp2);
+	mpint *tmp3 = mpnew(0);
+	mpmodmul(a, r, p, tmp3);
+	mpmodsub(tmp2, tmp3, p, tmp2);
+	mpfree(tmp3);
+	mpmodsub(tmp2, d, p, tmp2);
+	mpmodmul(tmp1, tmp2, p, D);
+	mpfree(tmp1);
+	mpfree(tmp2);
+	tmp2 = mpnew(0);
+	mpmodadd(r, mpone, p, tmp2);
+	tmp1 = mpnew(0);
+	mpmodadd(d, d, p, tmp1); // 2*d
+	mpmodsub(a, tmp1, p, tmp1);
+	mpmodmul(tmp2, tmp1, p, N);
+	mpfree(tmp2);
+	mpfree(tmp1);
+	mpmodmul(N, D, p, ND);
+	if(mpcmp(ND, mpzero) == 0){
+		mpassign(mpone, c);
+		mpassign(mpzero, e);
+		}else{
+		msqrt(ND, p, e);
+		if(mpcmp(e, mpzero) != 0){
+			mpassign(mpone, c);
+			mpinvert(e, p, e);
+			}else{
+			mpmodsub(mpzero, mpone, p, c);
+			mpint *tmp4 = mpnew(0);
+			mpmodmul(n, r0, p, tmp4);
+			mpint *tmp5 = mpnew(0);
+			mpint *tmp6 = mpnew(0);
+			mpmodmul(n, ND, p, tmp6);
+			misqrt(tmp6, p, tmp5);
+			mpfree(tmp6);
+			mpmodmul(tmp4, tmp5, p, e);
+			mpfree(tmp4);
+			mpfree(tmp5);
+			}
+		}
+	tmp1 = mpnew(0);
+	mpmodmul(c, N, p, tmp1);
+	mpmodmul(tmp1, e, p, s);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	tmp2 = mpnew(0);
+	mpmodmul(c, N, p, tmp2);
+	tmp3 = mpnew(0);
+	mpmodsub(r, mpone, p, tmp3);
+	mpmodmul(tmp2, tmp3, p, tmp1);
+	mpfree(tmp2);
+	mpfree(tmp3);
+	tmp3 = mpnew(0);
+	tmp2 = mpnew(0);
+	mpmodadd(d, d, p, tmp2); // 2*d
+	mpmodsub(a, tmp2, p, tmp2);
+	mpmodmul(tmp2, e, p, tmp3);
+	mpfree(tmp2);
+	mpmodmul(tmp3, tmp3, p, tmp3);
+	mpmodmul(tmp1, tmp3, p, t);
+	mpfree(tmp1);
+	mpfree(tmp3);
+	mpmodsub(mpzero, t, p, t);
+	mpmodsub(t, mpone, p, t);
+	tmp3 = mpnew(0);
+	mpmodadd(s, s, p, tmp3); // 2*s
+	mpmodmul(tmp3, t, p, X);
+	mpfree(tmp3);
+	tmp3 = mpnew(0);
+	tmp1 = mpnew(0);
+	mpmodmul(a, s, p, tmp1);
+	mpmodmul(tmp1, s, p, tmp3);
+	mpfree(tmp1);
+	mpmodsub(mpone, tmp3, p, tmp3);
+	tmp1 = mpnew(0);
+	tmp2 = mpnew(0);
+	mpmodmul(a, s, p, tmp2);
+	mpmodmul(tmp2, s, p, tmp1);
+	mpfree(tmp2);
+	mpmodadd(mpone, tmp1, p, tmp1);
+	mpmodmul(tmp3, tmp1, p, Y);
+	mpfree(tmp3);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	tmp3 = mpnew(0);
+	mpmodmul(a, s, p, tmp3);
+	mpmodmul(tmp3, s, p, tmp1);
+	mpfree(tmp3);
+	mpmodadd(mpone, tmp1, p, tmp1);
+	mpmodmul(tmp1, t, p, Z);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	mpmodadd(s, s, p, tmp1); // 2*s
+	tmp3 = mpnew(0);
+	tmp2 = mpnew(0);
+	mpmodmul(a, s, p, tmp2);
+	mpmodmul(tmp2, s, p, tmp3);
+	mpfree(tmp2);
+	mpmodsub(mpone, tmp3, p, tmp3);
+	mpmodmul(tmp1, tmp3, p, T);
+	mpfree(tmp1);
+	mpfree(tmp3);
+	mpfree(t);
+	mpfree(s);
+	mpfree(e);
+	mpfree(c);
+	mpfree(ND);
+	mpfree(N);
+	mpfree(D);
+	mpfree(r);
+	}
--- /dev/null
+++ b/libauthsrv/form1.c
@@ -1,0 +1,90 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+/*
+ * new ticket format: the reply protector/type is replaced by a
+ * 8 byte signature and a 4 byte counter forming the 12 byte
+ * nonce for chacha20/poly1305 encryption. a 16 byte poly1305 
+ * authentication tag is appended for message authentication.
+ * the counter is needed for the AuthPass message which uses
+ * the same key for several messages.
+ */
+
+static struct {
+	char	num;
+	char	sig[8];
+} form1sig[] = {
+	AuthPass,	"form1 PR",	/* password change request encrypted with ticket key */
+	AuthTs,		"form1 Ts",	/* ticket encrypted with server's key */
+	AuthTc, 	"form1 Tc",	/* ticket encrypted with client's key */
+	AuthAs,		"form1 As",	/* server generated authenticator */
+	AuthAc,		"form1 Ac",	/* client generated authenticator */
+	AuthTp,		"form1 Tp",	/* ticket encrypted with client's key for password change */
+	AuthHr,		"form1 Hr",	/* http reply */
+};
+
+int
+form1check(char *ap, int n)
+{
+	if(n < 8)
+		return -1;
+
+	for(n=0; n<nelem(form1sig); n++)
+		if(memcmp(form1sig[n].sig, ap, 8) == 0)
+			return form1sig[n].num;
+
+	return -1;
+}
+
+int
+form1B2M(char *ap, int n, uchar key[32])
+{
+	static u32int counter;
+	Chachastate s;
+	uchar *p;
+	int i;
+
+	for(i=nelem(form1sig)-1; i>=0; i--)
+		if(form1sig[i].num == *ap)
+			break;
+	if(i < 0)
+		abort();
+
+	p = (uchar*)ap + 12;
+	memmove(p, ap+1, --n);
+
+	/* nonce[12] = sig[8] | counter[4] */
+	memmove(ap, form1sig[i].sig, 8);
+	i = counter++;
+	ap[8] = i, ap[9] = i>>8, ap[10] = i>>16, ap[11] = i>>24;
+
+	setupChachastate(&s, key, 32, (uchar*)ap, 12, 20);
+	ccpoly_encrypt(p, n, nil, 0, p+n, &s);
+	return 12+16 + n;
+}
+
+int
+form1M2B(char *ap, int n, uchar key[32])
+{
+	Chachastate s;
+	uchar *p;
+	int num;
+
+	num = form1check(ap, n);
+	if(num < 0)
+		return -1;
+	n -= 12+16;
+	if(n <= 0)
+		return -1;
+
+	p = (uchar*)ap + 12;
+	setupChachastate(&s, key, 32, (uchar*)ap, 12, 20);
+	if(ccpoly_decrypt(p, n, nil, 0, p+n, &s))
+		return -1;
+
+	memmove(ap+1, p, n);
+	ap[0] = num;
+	return n+1;
+}
--- /dev/null
+++ b/libauthsrv/msqrt.mpc
@@ -1,0 +1,149 @@
+void legendresymbol(mpint *a, mpint *p, mpint *r){
+	mpint *pm1 = mpnew(0);
+	mpsub(p, mpone, pm1);
+	mpright(pm1, 1, r);
+	mpexp(a, r, p, r);
+	if(mpcmp(r, pm1) == 0){
+		mpassign(mpone, r);
+		r->sign = -1;
+		}
+	mpfree(pm1);
+	}
+void msqrt(mpint *a, mpint *p, mpint *r){
+	mpint *gs = mpnew(0);
+	mpint *m = mpnew(0);
+	mpint *t = mpnew(0);
+	mpint *g = mpnew(0);
+	mpint *b = mpnew(0);
+	mpint *x = mpnew(0);
+	mpint *n = mpnew(0);
+	mpint *s = mpnew(0);
+	mpint *e = mpnew(0);
+	mpint *tmp1 = mpnew(0);
+	legendresymbol(a, p, tmp1);
+	if(mpcmp(tmp1, mpone) != 0){
+		mpassign(mpzero, r);
+		}else{
+		if(mpcmp(a, mpzero) == 0){
+			mpassign(mpzero, r);
+			}else{
+			if(mpcmp(p, mptwo) == 0){
+				mpassign(a, r);
+				}else{
+				mpint *tmp2 = mpnew(0);
+				uitomp(4UL, tmp2);
+				mpmod(p, tmp2, tmp2);
+				mpint *tmp3 = mpnew(0);
+				uitomp(3UL, tmp3);
+				if(mpcmp(tmp2, tmp3) == 0){
+					mpadd(p, mpone, e);
+					mpright(e, 2, e);
+					mpexp(a, e, p, r);
+					}else{
+					mpsub(p, mpone, s);
+					mpassign(mpzero, e);
+					for(;;){
+						mpint *tmp4 = mpnew(0);
+						mpmod(s, mptwo, tmp4);
+						if(mpcmp(tmp4, mpzero) == 0){
+							mpright(s, 1, s);
+							mpadd(e, mpone, e);
+							}else{
+							mpfree(tmp4);
+							break;
+							}
+						mpfree(tmp4);
+						}
+					mpassign(mptwo, n);
+					for(;;){
+						mpint *tmp5 = mpnew(0);
+						legendresymbol(n, p, tmp5);
+						mpint *tmp6 = mpnew(0);
+						mpassign(mpone, tmp6);
+						tmp6->sign = -1;
+						if(mpcmp(tmp5, tmp6) != 0){
+							mpadd(n, mpone, n);
+							}else{
+							mpfree(tmp6);
+							mpfree(tmp5);
+							break;
+							}
+						mpfree(tmp5);
+						mpfree(tmp6);
+						}
+					mpmodadd(s, mpone, p, x);
+					mpright(x, 1, x);
+					mpexp(a, x, p, x);
+					mpexp(a, s, p, b);
+					mpexp(n, s, p, g);
+					for(;;){
+						if(0 == 0){
+							mpassign(b, t);
+							mpassign(mpzero, m);
+							for(;;){
+								if(mpcmp(m, e) < 0){
+									if(mpcmp(t, mpone) == 0){
+										break;
+										}
+									mpmul(t, t, t);
+									mpmod(t, p, t);
+									mpadd(m, mpone, m);
+									}else{
+									break;
+									}
+								}
+							if(mpcmp(m, mpzero) == 0){
+								mpassign(x, r);
+								break;
+								}
+							mpsub(e, m, t);
+							mpsub(t, mpone, t);
+							mpexp(mptwo, t, nil, t);
+							mpexp(g, t, p, gs);
+							mpmodmul(gs, gs, p, g);
+							mpmodmul(x, gs, p, x);
+							mpmodmul(b, g, p, b);
+							mpassign(m, e);
+							}else{
+							break;
+							}
+						}
+					}
+				mpfree(tmp2);
+				mpfree(tmp3);
+				}
+			}
+		}
+	mpfree(tmp1);
+	mpfree(gs);
+	mpfree(m);
+	mpfree(t);
+	mpfree(g);
+	mpfree(b);
+	mpfree(x);
+	mpfree(n);
+	mpfree(s);
+	mpfree(e);
+	}
+void misqrt(mpint *a, mpint *p, mpint *r){
+	mpint *e = mpnew(0);
+	mpint *tmp1 = mpnew(0);
+	uitomp(4UL, tmp1);
+	mpmod(p, tmp1, tmp1);
+	mpint *tmp2 = mpnew(0);
+	uitomp(3UL, tmp2);
+	if(mpcmp(tmp1, tmp2) == 0){
+		uitomp(3UL, e);
+		mpsub(p, e, e);
+		mpright(e, 2, e);
+		mpexp(a, e, p, r);
+		}else{
+		msqrt(a, p, r);
+		if(mpcmp(r, mpzero) != 0){
+			mpinvert(r, p, r);
+			}
+		}
+	mpfree(tmp1);
+	mpfree(tmp2);
+	mpfree(e);
+	}
--- a/libauthsrv/passtokey.c
+++ b/libauthsrv/passtokey.c
@@ -1,9 +1,10 @@
 #include <u.h>
 #include <libc.h>
 #include <authsrv.h>
+#include <libsec.h>
 
-int
-passtokey(char *key, char *p)
+void
+passtodeskey(char key[DESKEYLEN], char *p)
 {
 	uchar buf[ANAMELEN], *t;
 	int i, n;
@@ -20,7 +21,7 @@
 		for(i = 0; i < DESKEYLEN; i++)
 			key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1)));
 		if(n <= 8)
-			return 1;
+			return;
 		n -= 8;
 		t += 8;
 		if(n < 8){
@@ -29,5 +30,19 @@
 		}
 		encrypt(key, t, 8);
 	}
-	return 1;	/* not reached */
+}
+
+void
+passtoaeskey(uchar key[AESKEYLEN], char *p)
+{
+	static char salt[] = "Plan 9 key derivation";
+	pbkdf2_x((uchar*)p, strlen(p), (uchar*)salt, sizeof(salt)-1, 9001, key, AESKEYLEN, hmac_sha1, SHA1dlen);
+}
+
+void
+passtokey(Authkey *key, char *pw)
+{
+	memset(key, 0, sizeof(Authkey));
+	passtodeskey(key->des, pw);
+	passtoaeskey(key->aes, pw);
 }
--- a/libauthsrv/readnvram.c
+++ b/libauthsrv/readnvram.c
@@ -27,15 +27,29 @@
 	"sparc", "#r/nvram", 1024+850, sizeof(Nvrsafe),
 	"pc", "#S/sdC0/nvram", 0, sizeof(Nvrsafe),
 	"pc", "#S/sdC0/9fat", -1, sizeof(Nvrsafe),
+	"pc", "#S/sdC1/nvram", 0, sizeof(Nvrsafe),
+	"pc", "#S/sdC1/9fat", -1, sizeof(Nvrsafe),
+	"pc", "#S/sdD0/nvram", 0, sizeof(Nvrsafe),
+	"pc", "#S/sdD0/9fat", -1, sizeof(Nvrsafe),
+	"pc", "#S/sdE0/nvram", 0, sizeof(Nvrsafe),
+	"pc", "#S/sdE0/9fat", -1, sizeof(Nvrsafe),
+	"pc", "#S/sdF0/nvram", 0, sizeof(Nvrsafe),
+	"pc", "#S/sdF0/9fat", -1, sizeof(Nvrsafe),
 	"pc", "#S/sd00/nvram", 0, sizeof(Nvrsafe),
 	"pc", "#S/sd00/9fat", -1, sizeof(Nvrsafe),
 	"pc", "#S/sd01/nvram", 0, sizeof(Nvrsafe),
 	"pc", "#S/sd01/9fat", -1, sizeof(Nvrsafe),
+	"pc", "#S/sd10/nvram", 0, sizeof(Nvrsafe),
+	"pc", "#S/sd10/9fat", -1, sizeof(Nvrsafe),
 	"pc", "#f/fd0disk", -1, 512,	/* 512: #f requires whole sector reads */
 	"pc", "#f/fd1disk", -1, 512,
 	"mips", "#r/nvram", 1024+900, sizeof(Nvrsafe),
-	"power", "#F/flash/flash0", 0x300000, sizeof(Nvrsafe),
+	"power", "#F/flash/flash0", 0x440000, sizeof(Nvrsafe),
+	"power", "#F/flash/flash", 0x440000, sizeof(Nvrsafe),
 	"power", "#r/nvram", 4352, sizeof(Nvrsafe),	/* OK for MTX-604e */
+	"power", "/nvram", 0, sizeof(Nvrsafe),	/* OK for Ucu */
+	"arm", "#F/flash/flash0", 0x100000, sizeof(Nvrsafe),
+	"arm", "#F/flash/flash", 0x100000, sizeof(Nvrsafe),
 	"debug", "/tmp/nvram", 0, sizeof(Nvrsafe),
 };
 
@@ -109,67 +123,72 @@
 				buf[m++] = line[0];
 		}
 	}
-	return buf;	/* how does this happen */
 }
 
+typedef struct {
+	int	fd;
+	int	safelen;
+	vlong	safeoff;
+} Nvrwhere;
 
-/*
- *  get key info out of nvram.  since there isn't room in the PC's nvram use
- *  a disk partition there.
- */
-int
-readnvram(Nvrsafe *safep, int flag)
+static char *nvrfile = nil, *cputype = nil;
+
+/* returns with *locp filled in and locp->fd open, if possible */
+static void
+findnvram(Nvrwhere *locp)
 {
-	char buf[1024], in[128], *cputype, *nvrfile, *nvrlen, *nvroff, *v[2];
-	int fd, err, i, safeoff, safelen;
-	Nvrsafe *safe;
+	char *nvrlen, *nvroff, *v[2];
+	int fd, i, safelen;
+	vlong safeoff;
 
-	err = 0;
-	memset(safep, 0, sizeof(*safep));
-
-	nvrfile = getenv("nvram");
-	cputype = getenv("cputype");
+	if (nvrfile == nil)
+		nvrfile = getenv("nvram");
+	if (cputype == nil)
+		cputype = getenv("cputype");
 	if(cputype == nil)
-		cputype = "mips";
-	if(strcmp(cputype, "386")==0 || strcmp(cputype, "alpha")==0)
-		cputype = "pc";
+		cputype = strdup("mips");
+	if(strcmp(cputype, "386")==0 || strcmp(cputype, "amd64")==0 || strcmp(cputype, "alpha")==0) {
+		free(cputype);
+		cputype = strdup("pc");
+	}
 
-	safe = (Nvrsafe*)buf;
-
 	fd = -1;
 	safeoff = -1;
 	safelen = -1;
-	if(nvrfile != nil){
+	if(nvrfile != nil && *nvrfile != '\0'){
 		/* accept device and device!file */
 		i = gettokens(nvrfile, v, nelem(v), "!");
+		if (i < 1) {
+			i = 1;
+			v[0] = "";
+			v[1] = nil;
+		}
 		fd = open(v[0], ORDWR);
+		if (fd < 0)
+			fd = open(v[0], OREAD);
 		safelen = sizeof(Nvrsafe);
 		if(strstr(v[0], "/9fat") == nil)
 			safeoff = 0;
 		nvrlen = getenv("nvrlen");
 		if(nvrlen != nil)
-			safelen = atoi(nvrlen);
+			safelen = strtol(nvrlen, 0, 0);
 		nvroff = getenv("nvroff");
-		if(nvroff != nil){
+		if(nvroff != nil)
 			if(strcmp(nvroff, "dos") == 0)
 				safeoff = -1;
 			else
-				safeoff = atoi(nvroff);
-		}
+				safeoff = strtoll(nvroff, 0, 0);
 		if(safeoff < 0 && fd >= 0){
 			safelen = 512;
-			safeoff = finddosfile(fd, i == 2 ? v[1] : "plan9.nvr");
-			if(safeoff < 0){
+			safeoff = finddosfile(fd, i == 2? v[1]: "plan9.nvr");
+			if(safeoff < 0){	/* didn't find plan9.nvr? */
 				close(fd);
 				fd = -1;
 			}
 		}
-		free(nvrfile);
-		if(nvrlen != nil)
-			free(nvrlen);
-		if(nvroff != nil)
-			free(nvroff);
-	}else{
+		free(nvroff);
+		free(nvrlen);
+	}else
 		for(i=0; i<nelem(nvtab); i++){
 			if(strcmp(cputype, nvtab[i].cputype) != 0)
 				continue;
@@ -179,7 +198,7 @@
 			safelen = nvtab[i].len;
 			if(safeoff == -1){
 				safeoff = finddosfile(fd, "plan9.nvr");
-				if(safeoff < 0){
+				if(safeoff < 0){  /* didn't find plan9.nvr? */
 					close(fd);
 					fd = -1;
 					continue;
@@ -187,43 +206,123 @@
 			}
 			break;
 		}
+	locp->fd = fd;
+	locp->safelen = safelen;
+	locp->safeoff = safeoff;
+}
+
+/*
+ *  get key info out of nvram.  since there isn't room in the PC's nvram use
+ *  a disk partition there.
+ */
+int
+readnvram(Nvrsafe *safep, int flag)
+{
+	int err;
+	char buf[512], in[128];		/* 512 for floppy i/o */
+	Nvrsafe *safe;
+	Nvrwhere loc;
+
+	err = 0;
+	safe = (Nvrsafe*)buf;
+	memset(&loc, 0, sizeof loc);
+	findnvram(&loc);
+	if (loc.safelen < 0)
+		loc.safelen = sizeof *safe;
+	else if (loc.safelen > sizeof buf)
+		loc.safelen = sizeof buf;
+	if (loc.safeoff < 0) {
+		fprint(2, "readnvram: couldn't find nvram\n");
+		if(!(flag&NVwritemem))
+			memset(safep, 0, sizeof(*safep));
+		safe = safep;
+		/*
+		 * allow user to type the data for authentication,
+		 * even if there's no nvram to store it in.
+		 */
 	}
 
-	if(fd < 0
-	|| seek(fd, safeoff, 0) < 0
-	|| read(fd, buf, safelen) != safelen){
-		err = 1;
-		if(flag&(NVwrite|NVwriteonerr))
-			fprint(2, "can't read nvram: %r\n");
-		memset(safep, 0, sizeof(*safep));
+	if(flag&NVwritemem)
 		safe = safep;
-	}else{
-		*safep = *safe;
-		safe = safep;
+	else {
+		memset(safep, 0, sizeof(*safep));
+		if(loc.fd < 0
+		|| seek(loc.fd, loc.safeoff, 0) < 0
+		|| read(loc.fd, buf, loc.safelen) != loc.safelen){
+			err = 1;
+			if(flag&(NVwrite|NVwriteonerr))
+				if(loc.fd < 0)
+					fprint(2, "can't open %s: %r\n", nvrfile);
+				else if (seek(loc.fd, loc.safeoff, 0) < 0)
+					fprint(2, "can't seek %s to %lld: %r\n",
+						nvrfile, loc.safeoff);
+				else
+					fprint(2, "can't read %d bytes from %s: %r\n",
+						loc.safelen, nvrfile);
+			/* start from scratch */
+			memset(safep, 0, sizeof(*safep));
+			safe = safep;
+		}else{
+			*safep = *safe;	/* overwrite arg with data read */
+			safe = safep;
 
-		err |= check(safe->machkey, DESKEYLEN, safe->machsum, "bad nvram key");
-//		err |= check(safe->config, CONFIGLEN, safe->configsum, "bad secstore key");
-		err |= check(safe->authid, ANAMELEN, safe->authidsum, "bad authentication id");
-		err |= check(safe->authdom, DOMLEN, safe->authdomsum, "bad authentication domain");
+			/* verify data read */
+			err |= check(safe->machkey, DESKEYLEN, safe->machsum,
+						"bad nvram des key");
+			err |= check(safe->authid, ANAMELEN, safe->authidsum,
+						"bad authentication id");
+			err |= check(safe->authdom, DOMLEN, safe->authdomsum,
+						"bad authentication domain");
+			if(0){
+				err |= check(safe->config, CONFIGLEN, safe->configsum,
+						"bad secstore key");
+				err |= check(safe->aesmachkey, AESKEYLEN, safe->aesmachsum,
+						"bad nvram aes key");
+			} else {
+				if(nvcsum(safe->config, CONFIGLEN) != safe->configsum)
+					memset(safe->config, 0, CONFIGLEN);
+				if(nvcsum(safe->aesmachkey, AESKEYLEN) != safe->aesmachsum)
+					memset(safe->aesmachkey, 0, AESKEYLEN);
+			}
+			if(err == 0)
+				if(safe->authid[0]==0 || safe->authdom[0]==0){
+					fprint(2, "empty nvram authid or authdom\n");
+					err = 1;
+				}
+		}
 	}
 
-	if((flag&NVwrite) || (err && (flag&NVwriteonerr))){
-		readcons("authid", nil, 0, safe->authid, sizeof(safe->authid));
-		readcons("authdom", nil, 0, safe->authdom, sizeof(safe->authdom));
-		readcons("secstore key", nil, 1, safe->config, sizeof(safe->config));
-		for(;;){
-			if(readcons("password", nil, 1, in, sizeof in) == nil)
-				goto Out;
-			if(passtokey(safe->machkey, in))
+	if((flag&(NVwrite|NVwritemem)) || (err && (flag&NVwriteonerr))){
+		if (!(flag&NVwritemem)) {
+			readcons("authid", nil, 0, safe->authid,
+					sizeof safe->authid);
+			readcons("authdom", nil, 0, safe->authdom,
+					sizeof safe->authdom);
+			readcons("secstore key", nil, 1, safe->config,
+					sizeof safe->config);
+			for(;;){
+				Authkey k;
+
+				if(readcons("password", nil, 1, in, sizeof in) == nil)
+					goto Out;
+				passtokey(&k, in);
+				memmove(safe->machkey, k.des, DESKEYLEN);
+				memmove(safe->aesmachkey, k.aes, AESKEYLEN);
 				break;
+			}
 		}
+
 		safe->machsum = nvcsum(safe->machkey, DESKEYLEN);
+		// safe->authsum = nvcsum(safe->authkey, DESKEYLEN);
 		safe->configsum = nvcsum(safe->config, CONFIGLEN);
-		safe->authidsum = nvcsum(safe->authid, sizeof(safe->authid));
-		safe->authdomsum = nvcsum(safe->authdom, sizeof(safe->authdom));
+		safe->authidsum = nvcsum(safe->authid, sizeof safe->authid);
+		safe->authdomsum = nvcsum(safe->authdom, sizeof safe->authdom);
+		safe->aesmachsum = nvcsum(safe->aesmachkey, AESKEYLEN);
+
 		*(Nvrsafe*)buf = *safe;
-		if(seek(fd, safeoff, 0) < 0
-		|| write(fd, buf, safelen) != safelen){
+		if(loc.fd < 0
+		|| seek(loc.fd, loc.safeoff, 0) < 0
+		|| write(loc.fd, buf, loc.safelen) != loc.safelen){
 			fprint(2, "can't write key to nvram: %r\n");
 			err = 1;
 		}else
@@ -230,8 +329,9 @@
 			err = 0;
 	}
 Out:
-	close(fd);
-	return err ? -1 : 0;
+	if (loc.fd >= 0)
+		close(loc.fd);
+	return err? -1: 0;
 }
 
 typedef struct Dosboot	Dosboot;
@@ -340,7 +440,7 @@
 	if(rootsects <= 0 || rootsects > 64)
 		return -1;
 
-	/* 
+	/*
 	 *  read root. it is contiguous to make stuff like
 	 *  this easier
 	 */
--- /dev/null
+++ b/libauthsrv/spake2ee.mpc
@@ -1,0 +1,63 @@
+void spake2ee_h2P(mpint *p, mpint *a, mpint *d, mpint *h, mpint *PX, mpint *PY, mpint *PZ, mpint *PT){
+	mpint *n = mpnew(0);
+	mpassign(mptwo, n);
+	for(;;){
+		mpint *tmp1 = mpnew(0);
+		legendresymbol(n, p, tmp1);
+		mpint *tmp2 = mpnew(0);
+		mpassign(mpone, tmp2);
+		tmp2->sign = -1;
+		if(mpcmp(tmp1, tmp2) != 0){
+			mpadd(n, mpone, n);
+			}else{
+			mpfree(tmp2);
+			mpfree(tmp1);
+			break;
+			}
+		mpfree(tmp1);
+		mpfree(tmp2);
+		}
+	mpint *tmp3 = mpnew(0);
+	mpmod(h, p, tmp3);
+	elligator2(p, a, d, n, tmp3, PX, PY, PZ, PT);
+	mpfree(tmp3);
+	mpfree(n);
+	}
+void spake2ee_1(mpint *p, mpint *a, mpint *d, mpint *x, mpint *GX, mpint *GY, mpint *PX, mpint *PY, mpint *PZ, mpint *PT, mpint *y){
+	mpint *T = mpnew(0);
+	mpint *Z = mpnew(0);
+	mpint *Y = mpnew(0);
+	mpint *X = mpnew(0);
+	mpint *tmp1 = mpnew(0);
+	mpmodmul(GX, GY, p, tmp1);
+	edwards_scale(p, a, d, x, GX, GY, mpone, tmp1, X, Y, Z, T);
+	mpfree(tmp1);
+	edwards_add(p, a, d, X, Y, Z, T, PX, PY, PZ, PT, X, Y, Z, T);
+	decaf_encode(p, a, d, X, Y, Z, T, y);
+	mpfree(T);
+	mpfree(Z);
+	mpfree(Y);
+	mpfree(X);
+	}
+void spake2ee_2(mpint *p, mpint *a, mpint *d, mpint *PX, mpint *PY, mpint *PZ, mpint *PT, mpint *x, mpint *y, mpint *ok, mpint *z){
+	mpint *T = mpnew(0);
+	mpint *Z = mpnew(0);
+	mpint *Y = mpnew(0);
+	mpint *X = mpnew(0);
+	decaf_decode(p, a, d, y, ok, X, Y, Z, T);
+	if(mpcmp(ok, mpzero) != 0){
+		mpint *tmp1 = mpnew(0);
+		mpmodsub(mpzero, PX, p, tmp1);
+		mpint *tmp2 = mpnew(0);
+		mpmodsub(mpzero, PT, p, tmp2);
+		edwards_add(p, a, d, X, Y, Z, T, tmp1, PY, PZ, tmp2, X, Y, Z, T);
+		mpfree(tmp1);
+		mpfree(tmp2);
+		edwards_scale(p, a, d, x, X, Y, Z, T, X, Y, Z, T);
+		decaf_encode(p, a, d, X, Y, Z, T, z);
+		}
+	mpfree(T);
+	mpfree(Z);
+	mpfree(Y);
+	mpfree(X);
+	}
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -9,6 +9,7 @@
 	convM2D.$O\
 	convM2S.$O\
 	convS2M.$O\
+	ctime.$O\
 	crypt.$O\
 	dial.$O\
 	dirfstat.$O\
--- /dev/null
+++ b/libc/ctime.c
@@ -1,0 +1,118 @@
+/*
+ * This routine converts time as follows.
+ * The epoch is 0000 Jan 1 1970 GMT.
+ * The argument time is in seconds since then.
+ * The localtime(t) entry returns a pointer to an array
+ * containing
+ *
+ *	seconds (0-59)
+ *	minutes (0-59)
+ *	hours (0-23)
+ *	day of month (1-31)
+ *	month (0-11)
+ *	year-1970
+ *	weekday (0-6, Sun is 0)
+ *	day of the year
+ *	daylight savings flag
+ *
+ * The routine gets the daylight savings time from the environment.
+ *
+ * asctime(tvec))
+ * where tvec is produced by localtime
+ * returns a ptr to a character string
+ * that has the ascii time in the form
+ *
+ *	                            \\
+ *	Thu Jan 01 00:00:00 GMT 1970n0
+ *	012345678901234567890123456789
+ *	0	  1	    2
+ *
+ * ctime(t) just calls localtime, then asctime.
+ */
+
+#include <u.h>
+#include <libc.h>
+
+static	char	dmsize[12] =
+{
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * The following table is used for 1974 and 1975 and
+ * gives the day number of the first day after the Sunday of the
+ * change.
+ */
+
+static	int	dysize(int);
+
+
+Tm*
+gmtime(long tim)
+{
+	int d0, d1;
+	long hms, day;
+	static Tm xtime;
+
+	/*
+	 * break initial number into days
+	 */
+	hms = tim % 86400L;
+	day = tim / 86400L;
+	if(hms < 0) {
+		hms += 86400L;
+		day -= 1;
+	}
+
+	/*
+	 * generate hours:minutes:seconds
+	 */
+	xtime.sec = hms % 60;
+	d1 = hms / 60;
+	xtime.min = d1 % 60;
+	d1 /= 60;
+	xtime.hour = d1;
+
+	/*
+	 * day is the day number.
+	 * generate day of the week.
+	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
+	 */
+
+	xtime.wday = (day + 7340036L) % 7;
+
+	/*
+	 * year number
+	 */
+	if(day >= 0)
+		for(d1 = 1970; day >= dysize(d1); d1++)
+			day -= dysize(d1);
+	else
+		for (d1 = 1970; day < 0; d1--)
+			day += dysize(d1-1);
+	xtime.year = d1-1900;
+	xtime.yday = d0 = day;
+
+	/*
+	 * generate month
+	 */
+
+	if(dysize(d1) == 366)
+		dmsize[1] = 29;
+	for(d1 = 0; d0 >= dmsize[d1]; d1++)
+		d0 -= dmsize[d1];
+	dmsize[1] = 28;
+	xtime.mday = d0 + 1;
+	xtime.mon = d1;
+	strcpy(xtime.zone, "GMT");
+	return &xtime;
+}
+
+static int
+dysize(int y)
+{
+
+	if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
+		return 366;
+	return 365;
+}
--- a/libmp/Makefile
+++ b/libmp/Makefile
@@ -6,7 +6,9 @@
 
 OFILES=\
 	betomp.$O\
+	cnfield.$O\
 	crt.$O\
+	gmfield.$O\
 	letomp.$O\
 	mpadd.$O\
 	mpaux.$O\
@@ -16,17 +18,25 @@
 	mpeuclid.$O\
 	mpexp.$O\
 	mpextendedgcd.$O\
+	mpfactorial.$O\
+	mpfield.$O\
 	mpfmt.$O\
 	mpinvert.$O\
 	mpleft.$O\
+	mplogic.$O\
 	mpmod.$O\
+	mpmodop.$O\
 	mpmul.$O\
+	mpnrand.$O\
 	mprand.$O\
 	mpright.$O\
+	mpsel.$O\
 	mpsub.$O\
 	mptobe.$O\
+	mptober.$O\
 	mptoi.$O\
 	mptole.$O\
+	mptolel.$O\
 	mptoui.$O\
 	mptouv.$O\
 	mptov.$O\
@@ -34,6 +44,7 @@
 	mpveccmp.$O\
 	mpvecdigmuladd.$O\
 	mpvecsub.$O\
+	mpvectscmp.$O\
 	strtomp.$O
 
 default: $(LIB)
--- a/libmp/betomp.c
+++ b/libmp/betomp.c
@@ -9,21 +9,16 @@
 	int m, s;
 	mpdigit x;
 
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(0);
-
-	// dump leading zeros
-	while(*p == 0 && n > 1){
-		p++;
-		n--;
+		setmalloctag(b, getcallerpc(&p));
 	}
-
-	// get the space
 	mpbits(b, n*8);
-	b->top = DIGITS(n*8);
-	m = b->top-1;
 
-	// first digit might not be Dbytes long
+	m = DIGITS(n*8);
+	b->top = m--;
+	b->sign = 1;
+
 	s = ((n-1)*8)%Dbits;
 	x = 0;
 	for(; n > 0; n--){
@@ -35,6 +30,5 @@
 			x = 0;
 		}
 	}
-
-	return b;
+	return mpnorm(b);
 }
--- /dev/null
+++ b/libmp/cnfield.c
@@ -1,0 +1,114 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ * fast reduction for crandall numbers of the form: 2^n - c
+ */
+
+enum {
+	MAXDIG = 1024 / Dbits,
+};
+
+typedef struct CNfield CNfield;
+struct CNfield
+{
+	Mfield;	
+
+	mpint	m[1];
+
+	int	s;
+	mpdigit	c;
+};
+
+static int
+cnreduce(Mfield *m, mpint *a, mpint *r)
+{
+	mpdigit q[MAXDIG-1], t[MAXDIG], d;
+	CNfield *f = (CNfield*)m;
+	int qn, tn, k;
+
+	k = f->top;
+	if((a->top - k) >= MAXDIG)
+		return -1;
+
+	mpleft(a, f->s, r);
+	if(r->top <= k)
+		mpbits(r, (k+1)*Dbits);
+
+	/* q = hi(r) */
+	qn = r->top - k;
+	memmove(q, r->p+k, qn*Dbytes);
+
+	/* r = lo(r) */
+	r->top = k;
+	r->sign = 1;
+
+	do {
+		/* t = q*c */
+		tn = qn+1;
+		memset(t, 0, tn*Dbytes);
+		mpvecdigmuladd(q, qn, f->c, t);
+
+		/* q = hi(t) */
+		qn = tn - k;
+		if(qn <= 0) qn = 0;
+		else memmove(q, t+k, qn*Dbytes);
+
+		/* r += lo(t) */
+		if(tn > k)
+			tn = k;
+		mpvecadd(r->p, k, t, tn, r->p);
+
+		/* if(r >= m) r -= m */
+		mpvecsub(r->p, k+1, f->m->p, k, t);
+		d = t[k];
+		for(tn = 0; tn < k; tn++)
+			r->p[tn] = (r->p[tn] & d) | (t[tn] & ~d);
+	} while(qn > 0);
+
+	if(f->s != 0)
+		mpright(r, f->s, r);
+	mpnorm(r);
+
+	return 0;
+}
+
+Mfield*
+cnfield(mpint *N)
+{
+	mpint *M, *C;
+	CNfield *f;
+	mpdigit d;
+	int s;
+
+	if(N->top <= 2 || N->top >= MAXDIG)
+		return nil;
+	f = nil;
+	d = N->p[N->top-1];
+	for(s = 0; (d & (mpdigit)1<<Dbits-1) == 0; s++)
+		d <<= 1;
+	C = mpnew(0);
+	M = mpcopy(N);
+	mpleft(N, s, M);
+	mpleft(mpone, M->top*Dbits, C);
+	mpsub(C, M, C);
+	if(C->top != 1)
+		goto out;
+	f = mallocz(sizeof(CNfield) + M->top*sizeof(mpdigit), 1);
+	if(f == nil)
+		goto out;
+	f->s = s;
+	f->c = C->p[0];
+	f->m->size = M->top;
+	f->m->p = (mpdigit*)&f[1];
+	mpassign(M, f->m);
+	mpassign(N, (mpint*)f);
+	f->reduce = cnreduce;
+	f->flags |= MPfield;
+out:
+	mpfree(M);
+	mpfree(C);
+
+	return (Mfield*)f;
+}
--- a/libmp/crt.c
+++ b/libmp/crt.c
@@ -1,6 +1,5 @@
 #include "os.h"
 #include <mp.h>
-#include <libsec.h>
 
 // chinese remainder theorem
 //
--- a/libmp/crttest.c
+++ b/libmp/crttest.c
@@ -1,6 +1,5 @@
 #include "os.h"
 #include <mp.h>
-#include <libsec.h>
 
 void
 testcrt(mpint **p)
@@ -8,9 +7,8 @@
 	CRTpre *crt;
 	CRTres *res;
 	mpint *m, *x, *y;
-	int i;
 
-	fmtinstall('B', mpconv);
+	fmtinstall('B', mpfmt);
 
 	// get a modulus and a test number
 	m = mpnew(1024+160);
@@ -49,6 +47,6 @@
 		mpfree(p[0]);
 		mpfree(p[1]);
 	}
-	print("%d secs with more\n", time(0)-start);
+	print("%ld secs with more\n", time(0)-start);
 	exits(0);
 }
--- a/libmp/dat.h
+++ b/libmp/dat.h
@@ -1,4 +1,4 @@
-#define	mpdighi  (mpdigit)(1U<<(Dbits-1))
+#define	mpdighi  (mpdigit)(1<<(Dbits-1))
 #define DIGITS(x) ((Dbits - 1 + (x))/Dbits)
 
 // for converting between int's and mpint's
@@ -7,9 +7,6 @@
 #define MININT (MAXINT+1)
 
 // for converting between vlongs's and mpint's
-// #define MAXUVLONG (~0ULL)
-// #define MAXVLONG (MAXUVLONG>>1)
-// #define MINVLONG (MAXVLONG+1ULL)
-#define MAXUVLONG ((uvlong) ~0)
+#define MAXUVLONG (~0ULL)
 #define MAXVLONG (MAXUVLONG>>1)
-#define MINVLONG (MAXVLONG+((uvlong) 1))
+#define MINVLONG (MAXVLONG+1ULL)
--- /dev/null
+++ b/libmp/gmfield.c
@@ -1,0 +1,173 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ * fast reduction for generalized mersenne numbers (GM)
+ * using a series of additions and subtractions.
+ */
+
+enum {
+	MAXDIG = 1024/Dbits,
+};
+
+typedef struct GMfield GMfield;
+struct GMfield
+{
+	Mfield;	
+
+	mpint	m2[1];
+
+	int	nadd;
+	int	nsub;
+	int	indx[256];
+};
+
+static int
+gmreduce(Mfield *m, mpint *a, mpint *r)
+{
+	GMfield *g = (GMfield*)m;
+	mpdigit d0, t[MAXDIG];
+	int i, j, d, *x;
+
+	if(mpmagcmp(a, g->m2) >= 0)
+		return -1;
+
+	if(a != r)
+		mpassign(a, r);
+
+	d = g->top;
+	mpbits(r, (d+1)*Dbits*2);
+	memmove(t+d, r->p+d, d*Dbytes);
+
+	r->sign = 1;
+	r->top = d;
+	r->p[d] = 0;
+
+	if(g->nsub > 0)
+		mpvecdigmuladd(g->p, d, g->nsub, r->p);
+
+	x = g->indx;
+	for(i=0; i<g->nadd; i++){
+		t[0] = 0;
+		d0 = t[*x++];
+		for(j=1; j<d; j++)
+			t[j] = t[*x++];
+		t[0] = d0;
+
+		mpvecadd(r->p, d+1, t, d, r->p);
+	}
+
+	for(i=0; i<g->nsub; i++){
+		t[0] = 0;
+		d0 = t[*x++];
+		for(j=1; j<d; j++)
+			t[j] = t[*x++];
+		t[0] = d0;
+
+		mpvecsub(r->p, d+1, t, d, r->p);
+	}
+
+	mpvecdigmulsub(g->p, d, r->p[d], r->p);
+	r->p[d] = 0;
+
+	mpvecsub(r->p, d+1, g->p, d, r->p+d+1);
+	d0 = r->p[2*d+1];
+	for(j=0; j<d; j++)
+		r->p[j] = (r->p[j] & d0) | (r->p[j+d+1] & ~d0);
+
+	mpnorm(r);
+
+	return 0;
+}
+
+Mfield*
+gmfield(mpint *N)
+{
+	int i,j,d, s, *C, *X, *x, *e;
+	mpint *M, *T;
+	GMfield *g;
+
+	d = N->top;
+	if(d <= 2 || d > MAXDIG/2 || (mpsignif(N) % Dbits) != 0)
+		return nil;
+	g = nil;
+	T = mpnew(0);
+	M = mpcopy(N);
+	C = malloc(sizeof(int)*(d+1));
+	X = malloc(sizeof(int)*(d*d));
+	if(C == nil || X == nil)
+		goto out;
+
+	for(i=0; i<=d; i++){
+		if((M->p[i]>>8) != 0 && (~M->p[i]>>8) != 0)
+			goto out;
+		j = M->p[i];
+		C[d - i] = -j;
+		itomp(j, T);
+		mpleft(T, i*Dbits, T);
+		mpsub(M, T, M);
+	}
+	for(j=0; j<d; j++)
+		X[j] = C[d-j];
+	for(i=1; i<d; i++){
+		X[d*i] = X[d*(i-1) + d-1]*C[d];
+		for(j=1; j<d; j++)
+			X[d*i + j] = X[d*(i-1) + j-1] + X[d*(i-1) + d-1]*C[d-j];
+	}
+	g = mallocz(sizeof(GMfield) + (d+1)*sizeof(mpdigit)*2, 1);
+	if(g == nil)
+		goto out;
+
+	g->m2->p = (mpdigit*)&g[1];
+	g->m2->size = d*2+1;
+	mpmul(N, N, g->m2);
+	mpassign(N, (mpint*)g);
+	g->reduce = gmreduce;
+	g->flags |= MPfield;
+
+	s = 0;
+	x = g->indx;
+	e = x + nelem(g->indx) - d;
+	for(g->nadd=0; x <= e; x += d, g->nadd++){
+		s = 0;
+		for(i=0; i<d; i++){
+			for(j=0; j<d; j++){
+				if(X[d*i+j] > 0 && x[j] == 0){
+					X[d*i+j]--;
+					x[j] = d+i;
+					s = 1;
+					break;
+				}
+			}
+		}
+		if(s == 0)
+			break;
+	}
+	for(g->nsub=0; x <= e; x += d, g->nsub++){
+		s = 0;
+		for(i=0; i<d; i++){
+			for(j=0; j<d; j++){
+				if(X[d*i+j] < 0 && x[j] == 0){
+					X[d*i+j]++;
+					x[j] = d+i;
+					s = 1;
+					break;
+				}
+			}
+		}
+		if(s == 0)
+			break;
+	}
+	if(s != 0){
+		mpfree((mpint*)g);
+		g = nil;
+	}
+out:
+	free(C);
+	free(X);
+	mpfree(M);
+	mpfree(T);
+	return (Mfield*)g;
+}
+
--- a/libmp/letomp.c
+++ b/libmp/letomp.c
@@ -9,10 +9,11 @@
 	int i=0, m = 0;
 	mpdigit x=0;
 
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(0);
+		setmalloctag(b, getcallerpc(&s));
+	}
 	mpbits(b, 8*n);
-	b->top = DIGITS(8*n);
 	for(; n > 0; n--){
 		x |= ((mpdigit)(*s++)) << i;
 		i += 8;
@@ -25,5 +26,6 @@
 	if(i > 0)
 		b->p[m++] = x;
 	b->top = m;
-	return b;
+	b->sign = 1;
+	return mpnorm(b);
 }
--- a/libmp/mpadd.c
+++ b/libmp/mpadd.c
@@ -9,6 +9,8 @@
 	int m, n;
 	mpint *t;
 
+	sum->flags |= (b1->flags | b2->flags) & MPtimesafe;
+
 	// get the sizes right
 	if(b2->top > b1->top){
 		t = b1;
@@ -41,6 +43,7 @@
 	int sign;
 
 	if(b1->sign != b2->sign){
+		assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
 		if(b1->sign < 0)
 			mpmagsub(b2, b1, sum);
 		else
--- a/libmp/mpaux.c
+++ b/libmp/mpaux.c
@@ -5,11 +5,9 @@
 static mpdigit _mptwodata[1] = { 2 };
 static mpint _mptwo =
 {
-	1,
-	1,
-	1,
+	1, 1, 1,
 	_mptwodata,
-	MPstatic
+	MPstatic|MPnorm
 };
 mpint *mptwo = &_mptwo;
 
@@ -16,11 +14,9 @@
 static mpdigit _mponedata[1] = { 1 };
 static mpint _mpone =
 {
-	1,
-	1,
-	1,
+	1, 1, 1,
 	_mponedata,
-	MPstatic
+	MPstatic|MPnorm
 };
 mpint *mpone = &_mpone;
 
@@ -27,11 +23,9 @@
 static mpdigit _mpzerodata[1] = { 0 };
 static mpint _mpzero =
 {
-	1,
-	1,
-	0,
+	1, 1, 0,
 	_mpzerodata,
-	MPstatic
+	MPstatic|MPnorm
 };
 mpint *mpzero = &_mpzero;
 
@@ -41,6 +35,8 @@
 void
 mpsetminbits(int n)
 {
+	if(n < 0)
+		sysfatal("mpsetminbits: n < 0");
 	if(n == 0)
 		n = 1;
 	mpmindigits = DIGITS(n);
@@ -52,18 +48,20 @@
 {
 	mpint *b;
 
-	b = mallocz(sizeof(mpint), 1);
-	if(b == nil)
-		sysfatal("mpnew: %r");
+	if(n < 0)
+		sysfatal("mpsetminbits: n < 0");
+
 	n = DIGITS(n);
 	if(n < mpmindigits)
 		n = mpmindigits;
-	n = n;
-	b->p = (mpdigit*)mallocz(n*Dbytes, 1);
-	if(b->p == nil)
+	b = mallocz(sizeof(mpint) + n*Dbytes, 1);
+	if(b == nil)
 		sysfatal("mpnew: %r");
+	setmalloctag(b, getcallerpc(&n));
+	b->p = (mpdigit*)&b[1];
 	b->size = n;
 	b->sign = 1;
+	b->flags = MPnorm;
 
 	return b;
 }
@@ -78,16 +76,23 @@
 	if(b->size >= n){
 		if(b->top >= n)
 			return;
-		memset(&b->p[b->top], 0, Dbytes*(n - b->top));
-		b->top = n;
-		return;
+	} else {
+		if(b->p == (mpdigit*)&b[1]){
+			b->p = (mpdigit*)mallocz(n*Dbytes, 0);
+			if(b->p == nil)
+				sysfatal("mpbits: %r");
+			memmove(b->p, &b[1], Dbytes*b->top);
+			memset(&b[1], 0, Dbytes*b->size);
+		} else {
+			b->p = (mpdigit*)realloc(b->p, n*Dbytes);
+			if(b->p == nil)
+				sysfatal("mpbits: %r");
+		}
+		b->size = n;
 	}
-	b->p = (mpdigit*)realloc(b->p, n*Dbytes);
-	if(b->p == nil)
-		sysfatal("mpbits: %r");
 	memset(&b->p[b->top], 0, Dbytes*(n - b->top));
-	b->size = n;
 	b->top = n;
+	b->flags &= ~MPnorm;
 }
 
 void
@@ -97,16 +102,22 @@
 		return;
 	if(b->flags & MPstatic)
 		sysfatal("freeing mp constant");
-	memset(b->p, 0, b->top*Dbytes);	// information hiding
-	free(b->p);
+	memset(b->p, 0, b->size*Dbytes);
+	if(b->p != (mpdigit*)&b[1])
+		free(b->p);
 	free(b);
 }
 
-void
+mpint*
 mpnorm(mpint *b)
 {
 	int i;
 
+	if(b->flags & MPtimesafe){
+		assert(b->sign == 1);
+		b->flags &= ~MPnorm;
+		return b;
+	}
 	for(i = b->top-1; i >= 0; i--)
 		if(b->p[i] != 0)
 			break;
@@ -113,6 +124,8 @@
 	b->top = i+1;
 	if(b->top == 0)
 		b->sign = 1;
+	b->flags |= MPnorm;
+	return b;
 }
 
 mpint*
@@ -121,8 +134,10 @@
 	mpint *new;
 
 	new = mpnew(Dbits*old->size);
-	new->top = old->top;
+	setmalloctag(new, getcallerpc(&old));
 	new->sign = old->sign;
+	new->top = old->top;
+	new->flags = old->flags & ~(MPstatic|MPfield);
 	memmove(new->p, old->p, Dbytes*old->top);
 	return new;
 }
@@ -130,9 +145,14 @@
 void
 mpassign(mpint *old, mpint *new)
 {
+	if(new == nil || old == new)
+		return;
+	new->top = 0;
 	mpbits(new, Dbits*old->top);
 	new->sign = old->sign;
 	new->top = old->top;
+	new->flags &= ~MPnorm;
+	new->flags |= old->flags & ~(MPstatic|MPfield);
 	memmove(new->p, old->p, Dbytes*old->top);
 }
 
@@ -162,6 +182,7 @@
 	int k, bit, digit;
 	mpdigit d;
 
+	assert(n->flags & MPnorm);
 	if(n->top==0)
 		return 0;
 	k = 0;
@@ -182,4 +203,3 @@
 	}
 	return k;
 }
-
--- a/libmp/mpcmp.c
+++ b/libmp/mpcmp.c
@@ -2,16 +2,20 @@
 #include <mp.h>
 #include "dat.h"
 
-// return 1, 0, -1 as abs(b1)-abs(b2) is neg, 0, pos
+// return neg, 0, pos as abs(b1)-abs(b2) is neg, 0, pos
 int
 mpmagcmp(mpint *b1, mpint *b2)
 {
 	int i;
 
-	i = b1->top - b2->top;
-	if(i)
-		return i;
-
+	i = b1->flags | b2->flags;
+	if(i & MPtimesafe)
+		return mpvectscmp(b1->p, b1->top, b2->p, b2->top);
+	if(i & MPnorm){
+		i = b1->top - b2->top;
+		if(i)
+			return i;
+	}
 	return mpveccmp(b1->p, b1->top, b2->p, b2->top);
 }
 
@@ -19,10 +23,8 @@
 int
 mpcmp(mpint *b1, mpint *b2)
 {
-	if(b1->sign != b2->sign)
-		return b1->sign - b2->sign;
-	if(b1->sign < 0)
-		return mpmagcmp(b2, b1);
-	else
-		return mpmagcmp(b1, b2);
+	int sign;
+
+	sign = (b1->sign - b2->sign) >> 1;	// -1, 0, 1
+	return sign | (sign&1)-1 & mpmagcmp(b1, b2)*b1->sign;
 }
--- a/libmp/mpdigdiv.c
+++ b/libmp/mpdigdiv.c
@@ -21,6 +21,19 @@
 		return;
 	}
 
+	// very common case
+	if(~divisor == 0){
+		lo += hi;
+		if(lo < hi){
+			hi++;
+			lo++;
+		}
+		if(lo+1 == 0)
+			hi++;
+		*quotient = hi;
+		return;
+	}
+
 	// at this point we know that hi < divisor
 	// just shift and subtract till we're done
 	q = 0;
--- a/libmp/mpdiv.c
+++ b/libmp/mpdiv.c
@@ -13,10 +13,29 @@
 	mpdigit qd, *up, *vp, *qp;
 	mpint *u, *v, *t;
 
+	assert(quotient != remainder);
+	assert(divisor->flags & MPnorm);
+
 	// divide bv zero
 	if(divisor->top == 0)
 		abort();
 
+	// division by one or small powers of two
+	if(divisor->top == 1 && (divisor->p[0] & divisor->p[0]-1) == 0){
+		vlong r = (vlong)dividend->sign * (dividend->p[0] & divisor->p[0]-1);
+		if(quotient != nil){
+			for(s = 0; ((divisor->p[0] >> s) & 1) == 0; s++)
+				;
+			mpright(dividend, s, quotient);
+		}
+		if(remainder != nil){
+			remainder->flags |= dividend->flags & MPtimesafe;
+			vtomp(r, remainder);
+		}
+		return;
+	}
+	assert((dividend->flags & MPtimesafe) == 0);
+
 	// quick check
 	if(mpmagcmp(dividend, divisor) < 0){
 		if(remainder != nil)
@@ -95,6 +114,7 @@
 		*up-- = 0;
 	}
 	if(qp != nil){
+		assert((quotient->flags & MPtimesafe) == 0);
 		mpnorm(quotient);
 		if(dividend->sign != divisor->sign)
 			quotient->sign = -1;
@@ -101,6 +121,7 @@
 	}
 
 	if(remainder != nil){
+		assert((remainder->flags & MPtimesafe) == 0);
 		mpright(u, s, remainder);	// u is the remainder shifted
 		remainder->sign = dividend->sign;
 	}
--- a/libmp/mpeuclid.c
+++ b/libmp/mpeuclid.c
@@ -13,6 +13,12 @@
 {
 	mpint *tmp, *x0, *x1, *x2, *y0, *y1, *y2, *q, *r;
 
+	assert((a->flags&b->flags) & MPnorm);
+	assert(((a->flags|b->flags|d->flags|x->flags|y->flags) & MPtimesafe) == 0);
+
+	if(a->sign<0 || b->sign<0)
+		sysfatal("mpeuclid: negative arg");
+
 	if(mpcmp(a, b) < 0){
 		tmp = a;
 		a = b;
--- a/libmp/mpexp.c
+++ b/libmp/mpexp.c
@@ -22,6 +22,18 @@
 	mpdigit d, bit;
 	int i, j;
 
+	assert(m == nil || m->flags & MPnorm);
+	assert((e->flags & MPtimesafe) == 0);
+	res->flags |= b->flags & MPtimesafe;
+
+	i = mpcmp(e,mpzero);
+	if(i==0){
+		mpassign(mpone, res);
+		return;
+	}
+	if(i<0)
+		sysfatal("mpexp: negative exponent");
+
 	t[0] = mpcopy(b);
 	t[1] = res;
 
@@ -49,24 +61,22 @@
 	j = 0;
 	for(;;){
 		for(; bit != 0; bit >>= 1){
-			mpmul(t[j], t[j], t[j^1]);
-			if(bit & d)
-				mpmul(t[j^1], b, t[j]);
+			if(m != nil)
+				mpmodmul(t[j], t[j], m, t[j^1]);
 			else
+				mpmul(t[j], t[j], t[j^1]);
+			if(bit & d) {
+				if(m != nil)
+					mpmodmul(t[j^1], b, m, t[j]);
+				else
+					mpmul(t[j^1], b, t[j]);
+			} else
 				j ^= 1;
-			if(m != nil && t[j]->top > m->top){
-				mpmod(t[j], m, t[j^1]);
-				j ^= 1;
-			}
 		}
 		if(--i < 0)
 			break;
 		bit = mpdighi;
 		d = e->p[i];
-	}
-	if(m != nil){
-		mpmod(t[j], m, t[j^1]);
-		j ^= 1;
 	}
 	if(t[j] == res){
 		mpfree(t[j^1]);
--- a/libmp/mpextendedgcd.c
+++ b/libmp/mpextendedgcd.c
@@ -5,7 +5,7 @@
 
 // extended binary gcd
 //
-// For a anv b it solves, v = gcd(a,b) and finds x and y s.t.
+// For a and b it solves, v = gcd(a,b) and finds x and y s.t.
 // ax + by = v
 //
 // Handbook of Applied Cryptography, Menezes et al, 1997, pg 608.  
@@ -15,6 +15,16 @@
 	mpint *u, *A, *B, *C, *D;
 	int g;
 
+	assert((a->flags&b->flags) & MPnorm);
+	assert(((a->flags|b->flags|v->flags|x->flags|y->flags) & MPtimesafe) == 0);
+
+	if(a->sign < 0 || b->sign < 0){
+		mpassign(mpzero, v);
+		mpassign(mpzero, y);
+		mpassign(mpzero, x);
+		return;
+	}
+
 	if(a->top == 0){
 		mpassign(b, v);
 		mpassign(mpone, y);
@@ -94,4 +104,6 @@
 	mpfree(u);
 	mpfree(a);
 	mpfree(b);
+
+	return;
 }
--- /dev/null
+++ b/libmp/mpfactorial.c
@@ -1,0 +1,74 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+mpint*
+mpfactorial(ulong n)
+{
+	int i;
+	ulong k;
+	unsigned cnt;
+	int max, mmax;
+	mpdigit p, pp[2];
+	mpint *r, *s, *stk[31];
+
+	cnt = 0;
+	max = mmax = -1;
+	p = 1;
+	r = mpnew(0);
+	for(k=2; k<=n; k++){
+		pp[0] = 0;
+		pp[1] = 0;
+		mpvecdigmuladd(&p, 1, (mpdigit)k, pp);
+		if(pp[1] == 0)	/* !overflow */
+			p = pp[0];
+		else{
+			cnt++;
+			if((cnt & 1) == 0){
+				s = stk[max];
+				mpbits(r, Dbits*(s->top+1+1));
+				memset(r->p, 0, Dbytes*(s->top+1+1));
+				mpvecmul(s->p, s->top, &p, 1, r->p);
+				r->sign = 1;
+				r->top = s->top+1+1;		/* XXX: norm */
+				mpassign(r, s);
+				for(i=4; (cnt & (i-1)) == 0; i=i<<1){
+					mpmul(stk[max], stk[max-1], r);
+					mpassign(r, stk[max-1]);
+					max--;
+				}
+			}else{
+				max++;
+				if(max > mmax){
+					mmax++;
+					if(max > nelem(stk))
+						abort();
+					stk[max] = mpnew(Dbits);
+				}
+				stk[max]->top = 1;
+				stk[max]->p[0] = p;
+			}
+			p = (mpdigit)k;
+		}
+	}
+	if(max < 0){
+		mpbits(r, Dbits);
+		r->top = 1;
+		r->sign = 1;
+		r->p[0] = p;
+	}else{
+		s = stk[max--];
+		mpbits(r, Dbits*(s->top+1+1));
+		memset(r->p, 0, Dbytes*(s->top+1+1));
+		mpvecmul(s->p, s->top, &p, 1, r->p);
+		r->sign = 1;
+		r->top = s->top+1+1;		/* XXX: norm */
+	}
+
+	while(max >= 0)
+		mpmul(r, stk[max--], r);
+	for(max=mmax; max>=0; max--)
+		mpfree(stk[max]);
+	mpnorm(r);
+	return r;
+}
--- /dev/null
+++ b/libmp/mpfield.c
@@ -1,0 +1,21 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+mpint*
+mpfield(mpint *N)
+{
+	Mfield *f;
+
+	if(N == nil || N->flags & (MPfield|MPstatic))
+		return N;
+	if((f = cnfield(N)) != nil)
+		goto Exchange;
+	if((f = gmfield(N)) != nil)
+		goto Exchange;
+	return N;
+Exchange:
+	setmalloctag(f, getcallerpc(&N));
+	mpfree(N);
+	return (mpint*)f;
+}
--- a/libmp/mpfmt.c
+++ b/libmp/mpfmt.c
@@ -1,10 +1,9 @@
 #include "os.h"
 #include <mp.h>
-#include <libsec.h>
 #include "dat.h"
 
 static int
-to64(mpint *b, char *buf, int len)
+toencx(mpint *b, char *buf, int len, int (*enc)(char*, int, uchar*, int))
 {
 	uchar *p;
 	int n, rv;
@@ -13,52 +12,30 @@
 	n = mptobe(b, nil, 0, &p);
 	if(n < 0)
 		return -1;
-	rv = enc64(buf, len, p, n);
+	rv = (*enc)(buf, len, p, n);
 	free(p);
 	return rv;
 }
 
-static int
-to32(mpint *b, char *buf, int len)
-{
-	uchar *p;
-	int n, rv;
-
-	// leave room for a multiple of 5 buffer size
-	n = b->top*Dbytes + 5;
-	p = malloc(n);
-	if(p == nil)
-		return -1;
-	n = mptobe(b, p, n, nil);
-	if(n < 0)
-		return -1;
-
-	// round up buffer size, enc32 only accepts a multiple of 5
-	if(n%5)
-		n += 5 - (n%5);
-	rv = enc32(buf, len, p, n);
-	free(p);
-	return rv;
-}
-
 static char set16[] = "0123456789ABCDEF";
 
 static int
-to16(mpint *b, char *buf, int len)
+topow2(mpint *b, char *buf, int len, int s)
 {
 	mpdigit *p, x;
-	int i, j;
+	int i, j, sn;
 	char *out, *eout;
 
 	if(len < 1)
 		return -1;
 
+	sn = 1<<s;
 	out = buf;
 	eout = buf+len;
 	for(p = &b->p[b->top-1]; p >= b->p; p--){
 		x = *p;
-		for(i = Dbits-4; i >= 0; i -= 4){
-			j = 0xf & (x>>i);
+		for(i = Dbits-s; i >= 0; i -= s){
+			j = x >> i & sn - 1;
 			if(j != 0 || out != buf){
 				if(out >= eout)
 					return -1;
@@ -102,6 +79,8 @@
 		return -1;
 
 	d = mpcopy(b);
+	d->flags &= ~MPtimesafe;
+	mpnorm(d);
 	r = mpnew(0);
 	billion = uitomp(1000000000, nil);
 	out = buf+len;
@@ -124,23 +103,91 @@
 	return 0;
 }
 
+static int
+to8(mpint *b, char *buf, int len)
+{
+	mpdigit x, y;
+	char *out;
+	int i, j;
+
+	if(len < 2)
+		return -1;
+
+	out = buf+len;
+	*--out = 0;
+
+	i = j = 0;
+	x = y = 0;
+	while(j < b->top){
+		y = b->p[j++];
+		if(i > 0)
+			x |= y << i;
+		else
+			x = y;
+		i += Dbits;
+		while(i >= 3){
+Digout:			i -= 3;
+			if(out > buf)
+				out--;
+			else if(x != 0)
+				return -1;
+			*out = '0' + (x & 7);
+			x = y >> Dbits-i;
+		}
+	}
+	if(i > 0)
+		goto Digout;
+
+	while(*out == '0') out++;
+	if(*out == '\0')
+		*--out = '0';
+
+	len -= out-buf;
+	if(out != buf)
+		memmove(buf, out, len);
+	return 0;
+}
+
 int
 mpfmt(Fmt *fmt)
 {
 	mpint *b;
-	char *p;
+	char *x, *p;
+	int base;
 
 	b = va_arg(fmt->args, mpint*);
 	if(b == nil)
 		return fmtstrcpy(fmt, "*");
-	
-	p = mptoa(b, fmt->prec, nil, 0);
-	fmt->flags &= ~FmtPrec;
 
+	base = fmt->prec;
+	if(base == 0)
+		base = 16;	/* default */
+	fmt->flags &= ~FmtPrec;
+	p = mptoa(b, base, nil, 0);
 	if(p == nil)
 		return fmtstrcpy(fmt, "*");
 	else{
-		fmtstrcpy(fmt, p);
+		if((fmt->flags & FmtSharp) != 0){
+			switch(base){
+			case 16:
+				x = "0x";
+				break;
+			case 8:
+				x = "0";
+				break;
+			case 2:
+				x = "0b";
+				break;
+			default:
+				x = "";
+			}
+			if(*p == '-')
+				fmtprint(fmt, "-%s%s", x, p + 1);
+			else
+				fmtprint(fmt, "%s%s", x, p);
+		}
+		else
+			fmtstrcpy(fmt, p);
 		free(p);
 		return 0;
 	}
@@ -152,9 +199,14 @@
 	char *out;
 	int rv, alloced;
 
+	if(base == 0)
+		base = 16;	/* default */
 	alloced = 0;
 	if(buf == nil){
-		len = ((b->top+1)*Dbits+2)/3 + 1;
+		/* rv <= log₂(base) */
+		for(rv=1; (base >> rv) > 1; rv++)
+			;
+		len = 10 + (b->top*Dbits / rv);
 		buf = malloc(len);
 		if(buf == nil)
 			return nil;
@@ -171,18 +223,29 @@
 	}
 	switch(base){
 	case 64:
-		rv = to64(b, out, len);
+		rv = toencx(b, out, len, enc64);
 		break;
 	case 32:
-		rv = to32(b, out, len);
+		rv = toencx(b, out, len, enc32);
 		break;
-	default:
 	case 16:
-		rv = to16(b, out, len);
+		rv = topow2(b, out, len, 4);
 		break;
 	case 10:
 		rv = to10(b, out, len);
 		break;
+	case 8:
+		rv = to8(b, out, len);
+		break;
+	case 4:
+		rv = topow2(b, out, len, 2);
+		break;
+	case 2:
+		rv = topow2(b, out, len, 1);
+		break;
+	default:
+		abort();
+		return nil;
 	}
 	if(rv < 0){
 		if(alloced)
--- a/libmp/mpleft.c
+++ b/libmp/mpleft.c
@@ -9,8 +9,14 @@
 	int d, l, r, i, otop;
 	mpdigit this, last;
 
-	// a negative left shift is a right shift
-	if(shift < 0){
+	res->sign = b->sign;
+	if(b->top==0){
+		res->top = 0;
+		return;
+	}
+
+	// a zero or negative left shift is a right shift
+	if(shift <= 0){
 		mpright(b, -shift, res);
 		return;
 	}
@@ -40,7 +46,6 @@
 	for(i = 0; i < d; i++)
 		res->p[i] = 0;
 
-	// normalize
-	while(res->top > 0 && res->p[res->top-1] == 0)
-		res->top--;
+	res->flags |= b->flags & MPtimesafe;
+	mpnorm(res);
 }
--- /dev/null
+++ b/libmp/mplogic.c
@@ -1,0 +1,194 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+	mplogic calculates b1|b2 subject to the
+	following flag bits (fl)
+
+	bit 0: subtract 1 from b1
+	bit 1: invert b1
+	bit 2: subtract 1 from b2
+	bit 3: invert b2
+	bit 4: add 1 to output
+	bit 5: invert output
+	
+	it inverts appropriate bits automatically
+	depending on the signs of the inputs
+*/
+
+static void
+mplogic(mpint *b1, mpint *b2, mpint *sum, int fl)
+{
+	mpint *t;
+	mpdigit *dp1, *dp2, *dpo, d1, d2, d;
+	int c1, c2, co;
+	int i;
+
+	assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
+	if(b1->sign < 0) fl ^= 0x03;
+	if(b2->sign < 0) fl ^= 0x0c;
+	sum->sign = (int)(((fl|fl>>2)^fl>>4)<<30)>>31|1;
+	if(sum->sign < 0) fl ^= 0x30;
+	if(b2->top > b1->top){
+		t = b1;
+		b1 = b2;
+		b2 = t;
+		fl = fl >> 2 & 0x03 | fl << 2 & 0x0c | fl & 0x30;
+	}
+	mpbits(sum, b1->top*Dbits);
+	dp1 = b1->p;
+	dp2 = b2->p;
+	dpo = sum->p;
+	c1 = fl & 1;
+	c2 = fl >> 2 & 1;
+	co = fl >> 4 & 1;
+	for(i = 0; i < b1->top; i++){
+		d1 = dp1[i] - c1;
+		if(i < b2->top)
+			d2 = dp2[i] - c2;
+		else
+			d2 = 0;
+		if(d1 != (mpdigit)-1) c1 = 0;
+		if(d2 != (mpdigit)-1) c2 = 0;
+		if((fl & 2) != 0) d1 ^= -1;
+		if((fl & 8) != 0) d2 ^= -1;
+		d = d1 | d2;
+		if((fl & 32) != 0) d ^= -1;
+		d += co;
+		if(d != 0) co = 0;
+		dpo[i] = d;
+	}
+	sum->top = i;
+	mpnorm(sum);
+}
+
+void
+mpor(mpint *b1, mpint *b2, mpint *sum)
+{
+	mplogic(b1, b2, sum, 0);
+}
+
+void
+mpand(mpint *b1, mpint *b2, mpint *sum)
+{
+	mplogic(b1, b2, sum, 0x2a);
+}
+
+void
+mpbic(mpint *b1, mpint *b2, mpint *sum)
+{
+	mplogic(b1, b2, sum, 0x22);
+}
+
+void
+mpnot(mpint *b, mpint *r)
+{
+	mpadd(b, mpone, r);
+	r->sign ^= -2;
+}
+
+void
+mpxor(mpint *b1, mpint *b2, mpint *sum)
+{
+	mpint *t;
+	mpdigit *dp1, *dp2, *dpo, d1, d2, d;
+	int c1, c2, co;
+	int i, fl;
+
+	assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
+	if(b2->top > b1->top){
+		t = b1;
+		b1 = b2;
+		b2 = t;
+	}
+	fl = (b1->sign & 10) ^ (b2->sign & 12);
+	sum->sign = (int)(fl << 28) >> 31;
+	mpbits(sum, b1->top*Dbits);
+	dp1 = b1->p;
+	dp2 = b2->p;
+	dpo = sum->p;
+	c1 = fl >> 1 & 1;
+	c2 = fl >> 2 & 1;
+	co = fl >> 3 & 1;
+	for(i = 0; i < b1->top; i++){
+		d1 = dp1[i] - c1;
+		if(i < b2->top)
+			d2 = dp2[i] - c2;
+		else
+			d2 = 0;
+		if(d1 != (mpdigit)-1) c1 = 0;
+		if(d2 != (mpdigit)-1) c2 = 0;
+		d = d1 ^ d2;
+		d += co;
+		if(d != 0) co = 0;
+		dpo[i] = d;
+	}
+	sum->top = i;
+	mpnorm(sum);
+}
+
+void
+mptrunc(mpint *b, int n, mpint *r)
+{
+	int d, m, i, c;
+
+	assert(((b->flags | r->flags) & MPtimesafe) == 0);
+	mpbits(r, n);
+	r->top = DIGITS(n);
+	d = n / Dbits;
+	m = n % Dbits;
+	if(b->sign == -1){
+		c = 1;
+		for(i = 0; i <= r->top; i++){
+			if(i < b->top)
+				r->p[i] = ~(b->p[i] - c);
+			else
+				r->p[i] = -1;
+			if(r->p[i] != 0)
+				c = 0;
+		}
+		if(m != 0)
+			r->p[d] &= (1<<m) - 1;
+	}else if(b->sign == 1){
+		if(d >= b->top){
+			mpassign(b, r);
+			return;
+		}
+		if(b != r)
+			for(i = 0; i < d; i++)
+				r->p[i] = b->p[i];
+		if(m != 0)
+			r->p[d] = b->p[d] & (1<<m)-1;
+	}
+	r->sign = 1;
+	mpnorm(r);
+}
+
+void
+mpxtend(mpint *b, int n, mpint *r)
+{
+	int d, m, c, i;
+
+	d = (n - 1) / Dbits;
+	m = (n - 1) % Dbits;
+	if(d >= b->top){
+		mpassign(b, r);
+		return;
+	}
+	mptrunc(b, n, r);
+	mpbits(r, n);
+	if((r->p[d] & 1<<m) == 0){
+		mpnorm(r);
+		return;
+	}
+	r->p[d] |= -(1<<m);
+	r->sign = -1;
+	c = 1;
+	for(i = 0; i < r->top; i++){
+		r->p[i] = ~(r->p[i] - c);
+		if(r->p[i] != 0)
+			c = 0;
+	}
+	mpnorm(r);
+}
--- a/libmp/mpmod.c
+++ b/libmp/mpmod.c
@@ -2,14 +2,15 @@
 #include <mp.h>
 #include "dat.h"
 
-// remainder = b mod m
-//
-// knuth, vol 2, pp 398-400
-
 void
-mpmod(mpint *b, mpint *m, mpint *remainder)
+mpmod(mpint *x, mpint *n, mpint *r)
 {
-	mpdiv(b, m, nil, remainder);
-	if(remainder->sign < 0)
-		mpadd(m, remainder, remainder);
+	int sign;
+
+	sign = x->sign;
+	if((n->flags & MPfield) == 0
+	|| ((Mfield*)n)->reduce((Mfield*)n, x, r) != 0)
+		mpdiv(x, n, nil, r);
+	if(sign < 0)
+		mpmagsub(n, r, r);
 }
--- /dev/null
+++ b/libmp/mpmodop.c
@@ -1,0 +1,96 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+
+/* operands need to have m->top+1 digits of space and satisfy 0 ≤ a ≤ m-1 */
+static mpint*
+modarg(mpint *a, mpint *m)
+{
+	if(a->size <= m->top || a->sign < 0 || mpmagcmp(a, m) >= 0){
+		a = mpcopy(a);
+		mpmod(a, m, a);
+		mpbits(a, Dbits*(m->top+1));
+		a->top = m->top;
+	} else if(a->top < m->top){
+		memset(&a->p[a->top], 0, (m->top - a->top)*Dbytes);
+	}
+	return a;
+}
+
+void
+mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum)
+{
+	mpint *a, *b;
+	mpdigit d;
+	int i, j;
+
+	a = modarg(b1, m);
+	b = modarg(b2, m);
+
+	sum->flags |= (a->flags | b->flags) & MPtimesafe;
+	mpbits(sum, Dbits*2*(m->top+1));
+
+	mpvecadd(a->p, m->top, b->p, m->top, sum->p);
+	mpvecsub(sum->p, m->top+1, m->p, m->top, sum->p+m->top+1);
+
+	d = sum->p[2*m->top+1];
+	for(i = 0, j = m->top+1; i < m->top; i++, j++)
+		sum->p[i] = (sum->p[i] & d) | (sum->p[j] & ~d);
+
+	sum->top = m->top;
+	sum->sign = 1;
+	mpnorm(sum);
+
+	if(a != b1)
+		mpfree(a);
+	if(b != b2)
+		mpfree(b);
+}
+
+void
+mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff)
+{
+	mpint *a, *b;
+	mpdigit d;
+	int i, j;
+
+	a = modarg(b1, m);
+	b = modarg(b2, m);
+
+	diff->flags |= (a->flags | b->flags) & MPtimesafe;
+	mpbits(diff, Dbits*2*(m->top+1));
+
+	a->p[m->top] = 0;
+	mpvecsub(a->p, m->top+1, b->p, m->top, diff->p);
+	mpvecadd(diff->p, m->top, m->p, m->top, diff->p+m->top+1);
+
+	d = ~diff->p[m->top];
+	for(i = 0, j = m->top+1; i < m->top; i++, j++)
+		diff->p[i] = (diff->p[i] & d) | (diff->p[j] & ~d);
+
+	diff->top = m->top;
+	diff->sign = 1;
+	mpnorm(diff);
+
+	if(a != b1)
+		mpfree(a);
+	if(b != b2)
+		mpfree(b);
+}
+
+void
+mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod)
+{
+	mpint *a, *b;
+
+	a = modarg(b1, m);
+	b = modarg(b2, m);
+
+	mpmul(a, b, prod);
+	mpmod(prod, m, prod);
+
+	if(a != b1)
+		mpfree(a);
+	if(b != b2)
+		mpfree(b);
+}
--- a/libmp/mpmul.c
+++ b/libmp/mpmul.c
@@ -113,10 +113,6 @@
 		a = b;
 		b = t;
 	}
-	if(blen == 0){
-		memset(p, 0, Dbytes*(alen+blen));
-		return;
-	}
 
 	if(alen >= KARATSUBAMIN && blen > 1){
 		// O(n^1.585)
@@ -132,24 +128,48 @@
 }
 
 void
+mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{
+	int i;
+	mpdigit *t;
+
+	if(alen < blen){
+		i = alen;
+		alen = blen;
+		blen = i;
+		t = a;
+		a = b;
+		b = t;
+	}
+	if(blen == 0)
+		return;
+	for(i = 0; i < blen; i++)
+		mpvecdigmuladd(a, alen, b[i], &p[i]);
+}
+
+void
 mpmul(mpint *b1, mpint *b2, mpint *prod)
 {
 	mpint *oprod;
 
-	oprod = nil;
+	oprod = prod;
 	if(prod == b1 || prod == b2){
-		oprod = prod;
 		prod = mpnew(0);
+		prod->flags = oprod->flags;
 	}
+	prod->flags |= (b1->flags | b2->flags) & MPtimesafe;
 
 	prod->top = 0;
 	mpbits(prod, (b1->top+b2->top+1)*Dbits);
-	mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
+	if(prod->flags & MPtimesafe)
+		mpvectsmul(b1->p, b1->top, b2->p, b2->top, prod->p);
+	else
+		mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
 	prod->top = b1->top+b2->top+1;
 	prod->sign = b1->sign*b2->sign;
 	mpnorm(prod);
 
-	if(oprod != nil){
+	if(oprod != prod){
 		mpassign(prod, oprod);
 		mpfree(prod);
 	}
--- /dev/null
+++ b/libmp/mpnrand.c
@@ -1,0 +1,35 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/* return uniform random [0..n-1] */
+mpint*
+mpnrand(mpint *n, void (*gen)(uchar*, int), mpint *b)
+{
+	mpint *m;
+	int bits;
+
+	/* m = 2^bits - 1 */
+	bits = mpsignif(n);
+	m = mpnew(bits+1);
+	mpleft(mpone, bits, m);
+	mpsub(m, mpone, m);
+
+	if(b == nil){
+		b = mpnew(bits);
+		setmalloctag(b, getcallerpc(&n));
+	}
+
+	/* m = m - (m % n) */
+	mpmod(m, n, b);
+	mpsub(m, b, m);
+
+	do {
+		mprand(bits, gen, b);
+	} while(mpcmp(b, m) >= 0);
+
+	mpmod(b, n, b);
+	mpfree(m);
+
+	return b;
+}
--- a/libmp/mprand.c
+++ b/libmp/mprand.c
@@ -1,24 +1,24 @@
 #include "os.h"
 #include <mp.h>
-#include <libsec.h>
 #include "dat.h"
 
 mpint*
 mprand(int bits, void (*gen)(uchar*, int), mpint *b)
 {
-	int n, m;
 	mpdigit mask;
+	int n, m;
 	uchar *p;
 
 	n = DIGITS(bits);
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(bits);
-	else
+		setmalloctag(b, getcallerpc(&bits));
+	}else
 		mpbits(b, bits);
 
 	p = malloc(n*Dbytes);
 	if(p == nil)
-		return nil;
+		sysfatal("mprand: %r");
 	(*gen)(p, n*Dbytes);
 	betomp(p, n*Dbytes, b);
 	free(p);
@@ -25,18 +25,12 @@
 
 	// make sure we don't give too many bits
 	m = bits%Dbits;
-	n--;
-	if(m > 0){
-		mask = 1;
-		mask <<= m;
-		mask--;
-		b->p[n] &= mask;
-	}
+	if(m == 0)
+		return b;
 
-	for(; n >= 0; n--)
-		if(b->p[n] != 0)
-			break;
-	b->top = n+1;
-	b->sign = 1;
-	return b;
+	mask = 1;
+	mask <<= m;
+	mask--;
+	b->p[n-1] &= mask;
+	return mpnorm(b);
 }
--- a/libmp/mpright.c
+++ b/libmp/mpright.c
@@ -9,6 +9,12 @@
 	int d, l, r, i;
 	mpdigit this, last;
 
+	res->sign = b->sign;
+	if(b->top==0){
+		res->top = 0;
+		return;
+	}
+
 	// a negative right shift is a left shift
 	if(shift < 0){
 		mpleft(b, -shift, res);
@@ -17,10 +23,20 @@
 
 	if(res != b)
 		mpbits(res, b->top*Dbits - shift);
+	else if(shift == 0)
+		return;
+
 	d = shift/Dbits;
 	r = shift - d*Dbits;
 	l = Dbits - r;
 
+	//  shift all the bits out == zero
+	if(d>=b->top){
+		res->sign = 1;
+		res->top = 0;
+		return;
+	}
+
 	// special case digit shifts
 	if(r == 0){
 		for(i = 0; i < b->top-d; i++)
@@ -34,7 +50,8 @@
 		}
 		res->p[i++] = last>>r;
 	}
-	while(i > 0 && res->p[i-1] == 0)
-		i--;
+
 	res->top = i;
+	res->flags |= b->flags & MPtimesafe;
+	mpnorm(res);
 }
--- /dev/null
+++ b/libmp/mpsel.c
@@ -1,0 +1,42 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = s != 0 ? b1 : b2
+void
+mpsel(int s, mpint *b1, mpint *b2, mpint *res)
+{
+	mpdigit d;
+	int n, m, i;
+
+	res->flags |= (b1->flags | b2->flags) & MPtimesafe;
+	if((res->flags & MPtimesafe) == 0){
+		mpassign(s ? b1 : b2, res);
+		return;
+	}
+	res->flags &= ~MPnorm;
+
+	n = b1->top;
+	m = b2->top;
+	mpbits(res, Dbits*(n >= m ? n : m));
+	res->top = n >= m ? n : m;
+
+	s = (-s^s|s)>>(sizeof(s)*8-1);
+	res->sign = (b1->sign & s) | (b2->sign & ~s);
+
+	d = -((mpdigit)s & 1);
+
+	i = 0;
+	while(i < n && i < m){
+		res->p[i] = (b1->p[i] & d) | (b2->p[i] & ~d);
+		i++;
+	}
+	while(i < n){
+		res->p[i] = b1->p[i] & d;
+		i++;
+	}
+	while(i < m){
+		res->p[i] = b2->p[i] & ~d;
+		i++;
+	}
+}
--- a/libmp/mpsub.c
+++ b/libmp/mpsub.c
@@ -11,12 +11,15 @@
 
 	// get the sizes right
 	if(mpmagcmp(b1, b2) < 0){
+		assert(((b1->flags | b2->flags | diff->flags) & MPtimesafe) == 0);
 		sign = -1;
 		t = b1;
 		b1 = b2;
 		b2 = t;
-	} else
+	} else {
+		diff->flags |= (b1->flags | b2->flags) & MPtimesafe;
 		sign = 1;
+	}
 	n = b1->top;
 	m = b2->top;
 	if(m == 0){
@@ -39,6 +42,7 @@
 	int sign;
 
 	if(b1->sign != b2->sign){
+		assert(((b1->flags | b2->flags | diff->flags) & MPtimesafe) == 0);
 		sign = b1->sign;
 		mpmagadd(b1, b2, diff);
 		diff->sign = sign;
--- a/libmp/mptobe.c
+++ b/libmp/mptobe.c
@@ -2,56 +2,31 @@
 #include <mp.h>
 #include "dat.h"
 
-// convert an mpint into a big endian byte array (most significant byte first)
+// convert an mpint into a big endian byte array (most significant byte first; left adjusted)
 //   return number of bytes converted
 //   if p == nil, allocate and result array
 int
 mptobe(mpint *b, uchar *p, uint n, uchar **pp)
 {
-	int i, j, suppress;
-	mpdigit x;
-	uchar *e, *s, c;
+	int m;
 
+	m = (mpsignif(b)+7)/8;
+	if(m == 0)
+		m++;
 	if(p == nil){
-		n = (b->top+1)*Dbytes;
+		n = m;
 		p = malloc(n);
+		if(p == nil)
+			sysfatal("mptobe: %r");
+		setmalloctag(p, getcallerpc(&b));
+	} else {
+		if(n < m)
+			return -1;
+		if(n > m)
+			memset(p+m, 0, n-m);
 	}
-	if(p == nil)
-		return -1;
 	if(pp != nil)
 		*pp = p;
-	memset(p, 0, n);
-
-	// special case 0
-	if(b->top == 0){
-		if(n < 1)
-			return -1;
-		else
-			return 1;
-	}
-		
-	s = p;
-	e = s+n;
-	suppress = 1;
-	for(i = b->top-1; i >= 0; i--){
-		x = b->p[i];
-		for(j = Dbits-8; j >= 0; j -= 8){
-			c = x>>j;
-			if(c == 0 && suppress)
-				continue;
-			if(p >= e)
-				return -1;
-			*p++ = c;
-			suppress = 0;
-		}
-	}
-
-	// guarantee at least one byte
-	if(s == p){
-		if(p >= e)
-			return -1;
-		*p++ = 0;
-	}
-
-	return p - s;
+	mptober(b, p, m);
+	return m;
 }
--- /dev/null
+++ b/libmp/mptober.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+void
+mptober(mpint *b, uchar *p, int n)
+{
+	int i, j, m;
+	mpdigit x;
+
+	memset(p, 0, n);
+
+	p += n;
+	m = b->top*Dbytes;
+	if(m < n)
+		n = m;
+
+	i = 0;
+	while(n >= Dbytes){
+		n -= Dbytes;
+		x = b->p[i++];
+		for(j = 0; j < Dbytes; j++){
+			*--p = x;
+			x >>= 8;
+		}
+	}
+	if(n > 0){
+		x = b->p[i];
+		for(j = 0; j < n; j++){
+			*--p = x;
+			x >>= 8;
+		}
+	}
+}
--- a/libmp/mptoi.c
+++ b/libmp/mptoi.c
@@ -10,17 +10,15 @@
 mpint*
 itomp(int i, mpint *b)
 {
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(0);
-	mpassign(mpzero, b);
-	if(i != 0)
-		b->top = 1;
-	if(i < 0){
-		b->sign = -1;
-		*b->p = -i;
-	} else
-		*b->p = i;
-	return b;
+		setmalloctag(b, getcallerpc(&i));
+	}
+	b->sign = (i >> (sizeof(i)*8 - 1)) | 1;
+	i *= b->sign;
+	*b->p = i;
+	b->top = 1;
+	return mpnorm(b);
 }
 
 int
@@ -28,6 +26,8 @@
 {
 	uint x;
 
+	if(b->top==0)
+		return 0;
 	x = *b->p;
 	if(b->sign > 0){
 		if(b->top > 1 || (x > MAXINT))
--- a/libmp/mptole.c
+++ b/libmp/mptole.c
@@ -3,52 +3,26 @@
 #include "dat.h"
 
 // convert an mpint into a little endian byte array (least significant byte first)
-
 //   return number of bytes converted
 //   if p == nil, allocate and result array
 int
 mptole(mpint *b, uchar *p, uint n, uchar **pp)
 {
-	int i, j;
-	mpdigit x;
-	uchar *e, *s;
+	int m;
 
+	m = (mpsignif(b)+7)/8;
+	if(m == 0)
+		m++;
 	if(p == nil){
-		n = (b->top+1)*Dbytes;
+		n = m;
 		p = malloc(n);
-	}
+		if(p == nil)
+			sysfatal("mptole: %r");
+		setmalloctag(p, getcallerpc(&b));
+	} else if(n < m)
+		return -1;
 	if(pp != nil)
 		*pp = p;
-	if(p == nil)
-		return -1;
-	memset(p, 0, n);
-
-	// special case 0
-	if(b->top == 0){
-		if(n < 1)
-			return -1;
-		else
-			return 0;
-	}
-		
-	s = p;
-	e = s+n;
-	for(i = 0; i < b->top-1; i++){
-		x = b->p[i];
-		for(j = 0; j < Dbytes; j++){
-			if(p >= e)
-				return -1;
-			*p++ = x;
-			x >>= 8;
-		}
-	}
-	x = b->p[i];
-	while(x > 0){
-		if(p >= e)
-			return -1;
-		*p++ = x;
-		x >>= 8;
-	}
-
-	return p - s;
+	mptolel(b, p, n);
+	return m;
 }
--- /dev/null
+++ b/libmp/mptolel.c
@@ -1,0 +1,33 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+void
+mptolel(mpint *b, uchar *p, int n)
+{
+	int i, j, m;
+	mpdigit x;
+
+	memset(p, 0, n);
+
+	m = b->top*Dbytes;
+	if(m < n)
+		n = m;
+
+	i = 0;
+	while(n >= Dbytes){
+		n -= Dbytes;
+		x = b->p[i++];
+		for(j = 0; j < Dbytes; j++){
+			*p++ = x;
+			x >>= 8;
+		}
+	}
+	if(n > 0){
+		x = b->p[i];
+		for(j = 0; j < n; j++){
+			*p++ = x;
+			x >>= 8;
+		}
+	}
+}
--- a/libmp/mptoui.c
+++ b/libmp/mptoui.c
@@ -10,13 +10,14 @@
 mpint*
 uitomp(uint i, mpint *b)
 {
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(0);
-	mpassign(mpzero, b);
-	if(i != 0)
-		b->top = 1;
+		setmalloctag(b, getcallerpc(&i));
+	}
 	*b->p = i;
-	return b;
+	b->top = 1;
+	b->sign = 1;
+	return mpnorm(b);
 }
 
 uint
@@ -25,11 +26,9 @@
 	uint x;
 
 	x = *b->p;
-	if(b->sign < 0){
+	if(b->sign < 0)
 		x = 0;
-	} else {
-		if(b->top > 1 || x > MAXUINT)
-			x =  MAXUINT;
-	}
+	else if(b->top > 1 || (sizeof(mpdigit) > sizeof(uint) && x > MAXUINT))
+		x =  MAXUINT;
 	return x;
 }
--- a/libmp/mptouv.c
+++ b/libmp/mptouv.c
@@ -13,19 +13,18 @@
 {
 	int s;
 
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(VLDIGITS*sizeof(mpdigit));
-	else
+		setmalloctag(b, getcallerpc(&v));
+	}else
 		mpbits(b, VLDIGITS*sizeof(mpdigit));
-	mpassign(mpzero, b);
-	if(v == 0)
-		return b;
-	for(s = 0; s < VLDIGITS && v != 0; s++){
+	b->sign = 1;
+	for(s = 0; s < VLDIGITS; s++){
 		b->p[s] = v;
 		v >>= sizeof(mpdigit)*8;
 	}
 	b->top = s;
-	return b;
+	return mpnorm(b);
 }
 
 uvlong
@@ -35,15 +34,14 @@
 	int s;
 
 	if(b->top == 0)
-		return (vlong) 0;
+		return 0LL;
 
-	mpnorm(b);
 	if(b->top > VLDIGITS)
 		return MAXVLONG;
 
-	v = (uvlong) 0;
+	v = 0ULL;
 	for(s = 0; s < b->top; s++)
-		v |= b->p[s]<<(s*sizeof(mpdigit)*8);
+		v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
 
 	return v;
 }
--- a/libmp/mptov.c
+++ b/libmp/mptov.c
@@ -14,24 +14,19 @@
 	int s;
 	uvlong uv;
 
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(VLDIGITS*sizeof(mpdigit));
-	else
+		setmalloctag(b, getcallerpc(&v));
+	}else
 		mpbits(b, VLDIGITS*sizeof(mpdigit));
-	mpassign(mpzero, b);
-	if(v == 0)
-		return b;
-	if(v < 0){
-		b->sign = -1;
-		uv = -v;
-	} else
-		uv = v;
-	for(s = 0; s < VLDIGITS && uv != 0; s++){
+	b->sign = (v >> (sizeof(v)*8 - 1)) | 1;
+	uv = v * b->sign;
+	for(s = 0; s < VLDIGITS; s++){
 		b->p[s] = uv;
 		uv >>= sizeof(mpdigit)*8;
 	}
 	b->top = s;
-	return b;
+	return mpnorm(b);
 }
 
 vlong
@@ -41,9 +36,8 @@
 	int s;
 
 	if(b->top == 0)
-		return (vlong) 0;
+		return 0LL;
 
-	mpnorm(b);
 	if(b->top > VLDIGITS){
 		if(b->sign > 0)
 			return (vlong)MAXVLONG;
@@ -51,7 +45,7 @@
 			return (vlong)MINVLONG;
 	}
 
-	v = (uvlong) 0;
+	v = 0ULL;
 	for(s = 0; s < b->top; s++)
 		v |= b->p[s]<<(s*sizeof(mpdigit)*8);
 
--- a/libmp/mpveccmp.c
+++ b/libmp/mpveccmp.c
@@ -2,7 +2,6 @@
 #include <mp.h>
 #include "dat.h"
 
-// prereq: alen >= blen
 int
 mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen)
 {
--- /dev/null
+++ b/libmp/mpvectscmp.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+int
+mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen)
+{
+	mpdigit x, y, z, v;
+	int m, p;
+
+	if(alen > blen){
+		v = 0;
+		while(alen > blen)
+			v |= a[--alen];
+		m = p = (-v^v|v)>>Dbits-1;
+	} else if(blen > alen){
+		v = 0;
+		while(blen > alen)
+			v |= b[--blen];
+		m = (-v^v|v)>>Dbits-1;
+		p = m^1;
+	} else
+		m = p = 0;
+	while(alen-- > 0){
+		x = a[alen];
+		y = b[alen];
+		z = x - y;
+		x = ~x;
+		v = ((-z^z|z)>>Dbits-1) & ~m;
+		p = ((~(x&y|x&z|y&z)>>Dbits-1) & v) | (p & ~v);
+		m |= v;
+	}
+	return (p-m) | m;
+}
--- a/libmp/strtomp.c
+++ b/libmp/strtomp.c
@@ -1,6 +1,5 @@
 #include "os.h"
 #include <mp.h>
-#include <libsec.h>
 #include "dat.h"
 
 static struct {
@@ -32,37 +31,40 @@
 	memset(tab.t10, INVAL, sizeof(tab.t10));
 
 	for(p = set64; *p; p++)
-		tab.t64[(uchar)*p] = p-set64;
+		tab.t64[*p] = p-set64;
 	for(p = set32; *p; p++)
-		tab.t32[(uchar)*p] = p-set32;
+		tab.t32[*p] = p-set32;
 	for(p = set16; *p; p++)
-		tab.t16[(uchar)*p] = (p-set16)%16;
+		tab.t16[*p] = (p-set16)%16;
 	for(p = set10; *p; p++)
-		tab.t10[(uchar)*p] = (p-set10);
+		tab.t10[*p] = (p-set10);
 
 	tab.inited = 1;
 }
 
 static char*
-from16(char *a, mpint *b)
+frompow2(char *a, mpint *b, int s)
 {
 	char *p, *next;
 	int i;
 	mpdigit x;
+	int sn;
 
-	b->top = 0;
+	sn = 1<<s;
 	for(p = a; *p; p++)
-		if(tab.t16[(uchar)*p] == INVAL)
+		if(tab.t16[*(uchar*)p] >= sn)
 			break;
-	mpbits(b, (p-a)*4);
+
+	mpbits(b, (p-a)*s);
 	b->top = 0;
 	next = p;
+
 	while(p > a){
 		x = 0;
-		for(i = 0; i < Dbits; i += 4){
+		for(i = 0; i < Dbits; i += s){
 			if(p <= a)
 				break;
-			x |= tab.t16[(uchar)*--p]<<i;
+			x |= tab.t16[*(uchar*)--p]<<i;
 		}
 		b->p[b->top++] = x;
 	}
@@ -69,6 +71,40 @@
 	return next;
 }
 
+static char*
+from8(char *a, mpint *b)
+{
+	char *p, *next;
+	mpdigit x, y;
+	int i;
+
+	for(p = a; *p; p++)
+		if(tab.t10[*(uchar*)p] >= 8)
+			break;
+
+	mpbits(b, (a-p)*3);
+	b->top = 0;
+	next = p;
+
+	i = 0;
+	x = y = 0;
+	while(p > a){
+		y = tab.t10[*(uchar*)--p];
+		x |= y << i;
+		i += 3;
+		if(i >= Dbits){
+Digout:
+			i -= Dbits;
+			b->p[b->top++] = x;
+			x = y >> 3-i;
+		}
+	}
+	if(i > 0)
+		goto Digout;
+
+	return next;
+}
+
 static ulong mppow10[] = {
 	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
 };
@@ -88,7 +124,7 @@
 		// do a billion at a time in native arithmetic
 		x = 0;
 		for(i = 0; i < 9; i++){
-			y = tab.t10[(uchar)*a];
+			y = tab.t10[*(uchar*)a];
 			if(y == INVAL)
 				break;
 			a++;
@@ -112,45 +148,28 @@
 }
 
 static char*
-from64(char *a, mpint *b)
+fromdecx(char *a, mpint *b, uchar tab[256], int (*dec)(uchar*, int, char*, int))
 {
 	char *buf = a;
 	uchar *p;
 	int n, m;
 
-	for(; tab.t64[(uchar)*a] != INVAL; a++)
+	b->top = 0;
+	for(; tab[*(uchar*)a] != INVAL; a++)
 		;
 	n = a-buf;
-	mpbits(b, n*6);
-	p = malloc(n);
-	if(p == nil)
-		return a;
-	m = dec64(p, n, buf, n);
-	betomp(p, m, b);
-	free(p);
+	if(n > 0){
+		p = malloc(n);
+		if(p == nil)
+			sysfatal("malloc: %r");
+		m = (*dec)(p, n, buf, n);
+		if(m > 0)
+			betomp(p, m, b);
+		free(p);
+	}
 	return a;
 }
 
-static char*
-from32(char *a, mpint *b)
-{
-	char *buf = a;
-	uchar *p;
-	int n, m;
-
-	for(; tab.t64[(uchar)*a] != INVAL; a++)
-		;
-	n = a-buf;
-	mpbits(b, n*5);
-	p = malloc(n);
-	if(p == nil)
-		return a;
-	m = dec32(p, n, buf, n);
-	betomp(p, m, b);
-	free(p);
-	return a;
-}
-
 mpint*
 strtomp(char *a, char **pp, int base, mpint *b)
 {
@@ -157,8 +176,10 @@
 	int sign;
 	char *e;
 
-	if(b == nil)
+	if(b == nil){
 		b = mpnew(0);
+		setmalloctag(b, getcallerpc(&a));
+	}
 
 	if(tab.inited == 0)
 		init();
@@ -176,20 +197,47 @@
 		break;
 	}
 
+	if(base == 0){
+		base = 10;
+		if(a[0] == '0'){
+			if(a[1] == 'x' || a[1] == 'X') {
+				a += 2;
+				base = 16;
+			} else if(a[1] == 'b' || a[1] == 'B') {
+				a += 2;
+				base = 2;
+			} else if(a[1] >= '0' && a[1] <= '7') {
+				a++;
+				base = 8;
+			}
+		}
+	}
+
 	switch(base){
+	case 2:
+		e = frompow2(a, b, 1);
+		break;
+	case 4:
+		e = frompow2(a, b, 2);
+		break;
+	case 8:
+		e = from8(a, b);
+		break;
 	case 10:
 		e = from10(a, b);
 		break;
-	default:
 	case 16:
-		e = from16(a, b);
+		e = frompow2(a, b, 4);
 		break;
 	case 32:
-		e = from32(a, b);
+		e = fromdecx(a, b, tab.t32, dec32);
 		break;
 	case 64:
-		e = from64(a, b);
+		e = fromdecx(a, b, tab.t64, dec64);
 		break;
+	default:
+		abort();
+		return nil;
 	}
 
 	// if no characters parsed, there wasn't a number to convert
@@ -196,10 +244,9 @@
 	if(e == a)
 		return nil;
 
-	mpnorm(b);
-	b->sign = sign;
 	if(pp != nil)
 		*pp = e;
 
-	return b;
+	b->sign = sign;
+	return mpnorm(b);
 }
--- a/libsec/Makefile
+++ b/libsec/Makefile
@@ -4,7 +4,12 @@
 
 OFILES=\
 	aes.$O\
+	aes_xts.$O\
 	blowfish.$O\
+	ccpoly.$O\
+	chacha.$O\
+	curve25519.$O\
+	curve25519_dh.$O\
 	decodepem.$O\
 	des.$O\
 	des3CBC.$O\
@@ -12,6 +17,7 @@
 	desCBC.$O\
 	desECB.$O\
 	desmodes.$O\
+	dh.$O\
 	dsaalloc.$O\
 	dsagen.$O\
 	dsaprimes.$O\
@@ -18,6 +24,7 @@
 	dsaprivtopub.$O\
 	dsasign.$O\
 	dsaverify.$O\
+	ecc.$O\
 	egalloc.$O\
 	egdecrypt.$O\
 	egencrypt.$O\
@@ -30,14 +37,18 @@
 	genrandom.$O\
 	gensafeprime.$O\
 	genstrongprime.$O\
+	hkdf.$O\
 	hmac.$O\
 	md4.$O\
 	md5.$O\
 	md5pickle.$O\
 	nfastrand.$O\
+	pbkdf2.$O\
+	poly1305.$O\
 	prng.$O\
 	probably_prime.$O\
 	rc4.$O\
+	ripemd.$O\
 	rsaalloc.$O\
 	rsadecrypt.$O\
 	rsaencrypt.$O\
@@ -44,9 +55,19 @@
 	rsafill.$O\
 	rsagen.$O\
 	rsaprivtopub.$O\
+	salsa.$O\
+	secp256k1.$O\
+	secp256r1.$O\
 	sha1.$O\
 	sha1pickle.$O\
-	smallprimes.$O
+	sha2_128.$O\
+	sha2_64.$O\
+	sha2block128.$O\
+	sha2block64.$O\
+	smallprimes.$O\
+	tsmemcmp.$O\
+	tlshand.$O\
+	x509.$O
 
 default: $(LIB)
 $(LIB): $(OFILES)
--- a/libsec/aes.c
+++ b/libsec/aes.c
@@ -30,11 +30,14 @@
  */
 #include <u.h>
 #include <libc.h>
+#include <mp.h>
 #include <libsec.h>
 
 typedef uchar	u8;
-typedef u32int	u32;
+typedef ulong	u32;
+
 #define FULL_UNROLL
+#define const
 
 static const u32 Td0[256];
 static const u32 Td1[256];
@@ -41,15 +44,30 @@
 static const u32 Td2[256];
 static const u32 Td3[256];
 static const u8  Te4[256];
+static uchar basekey[3][16] = {
+	{
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	},
+	{
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	},
+	{
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	},
+};
 
-static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
+static int aes_setupEnc(ulong rk[/*4*(Nr + 1)*/], const uchar cipherKey[],
+		int keyBits);
+static int aes_setupDec(ulong rk[/*4*(Nr + 1)*/], const uchar cipherKey[],
+		int keyBits);
+static int aes_setup(ulong erk[/*4*(Nr + 1)*/], ulong drk[/*4*(Nr + 1)*/],
+		const uchar cipherKey[], int keyBits);
 
-#ifdef NOT
-static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
-#endif
-static int rijndaelKeySetup(u32 erk[/*4*(Nr + 1)*/], u32 drk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
-static void	rijndaelEncrypt(const u32int rk[], int Nr, const uchar pt[16], uchar ct[16]);
-static void	rijndaelDecrypt(const u32int rk[], int Nr, const uchar ct[16], uchar pt[16]);
+void	aes_encrypt(const ulong rk[], int Nr, const uchar pt[16], uchar ct[16]);
+void	aes_decrypt(const ulong rk[], int Nr, const uchar ct[16], uchar pt[16]);
 
 void
 setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec)
@@ -59,18 +77,97 @@
 		keybytes = AESmaxkey;
 	memmove(s->key, key, keybytes);
 	s->keybytes = keybytes;
-	s->rounds = rijndaelKeySetup(s->ekey, s->dkey, s->key, keybytes * 8);
+	s->rounds = aes_setup(s->ekey, s->dkey, s->key, keybytes * 8);
 	if(ivec != nil)
 		memmove(s->ivec, ivec, AESbsize);
 	if(keybytes==16 || keybytes==24 || keybytes==32)
 		s->setup = 0xcafebabe;
-	// else rijndaelKeySetup was invalid
+	/* else aes_setup was invalid */
 }
 
-// Define by analogy with desCBCencrypt;  AES modes are not standardized yet.
-// Because of the way that non-multiple-of-16 buffers are handled,
-// the decryptor must be fed buffers of the same size as the encryptor.
+/*
+ * AES-XCBC-MAC-96 message authentication, per rfc3566.
+ */
+
 void
+setupAESXCBCstate(AESstate *s)		/* was setupmac96 */
+{
+	int i, j;
+	uint q[16 / sizeof(uint)];
+	uchar *p;
+
+	assert(s->keybytes == 16);
+	for(i = 0; i < 3; i++)
+		aes_encrypt(s->ekey, s->rounds, basekey[i],
+			s->mackey + AESbsize*i);
+
+	p = s->mackey;
+	memset(q, 0, AESbsize);
+
+	/*
+	 * put the in the right endian.  once figured, probably better
+	 * to use some fcall macros.
+	 * keys for encryption in local endianness for the algorithm...
+	 * only key1 is used for encryption;
+	 * BUG!!: I think this is what I got wrong.
+	 */
+	for(i = 0; i < 16 / sizeof(uint); i ++){
+		for(j = 0; j < sizeof(uint); j++)
+			q[i] |= p[sizeof(uint)-j-1] << 8*j;
+		p += sizeof(uint);
+	}
+	memmove(s->mackey, q, 16);
+}
+
+/*
+ * Not dealing with > 128-bit keys, not dealing with strange corner cases like
+ * empty message.  Should be fine for AES-XCBC-MAC-96.
+ */
+uchar*
+aesXCBCmac(uchar *p, int len, AESstate *s)
+{
+	uchar *p2, *ip, *eip, *mackey;
+	uchar q[AESbsize];
+
+	assert(s->keybytes == 16);	/* more complicated for bigger */
+	memset(s->ivec, 0, AESbsize);	/* E[0] is 0+ */
+
+	for(; len > AESbsize; len -= AESbsize){
+		memmove(q, p, AESbsize);
+		p2 = q;
+		ip = s->ivec;
+		for(eip = ip + AESbsize; ip < eip; )
+			*p2++ ^= *ip++;
+		aes_encrypt((ulong *)s->mackey, s->rounds, q, s->ivec);
+		p += AESbsize;
+	}
+	/* the last one */
+
+	memmove(q, p, len);
+	p2 = q+len;
+	if(len == AESbsize)
+		mackey = s->mackey + AESbsize;	/* k2 */
+	else{
+		mackey = s->mackey+2*AESbsize;	/* k3 */
+		*p2++ = 1 << 7;			/* padding */
+		len = AESbsize - len - 1;
+		memset(p2, 0, len);
+	}
+
+	ip = s->ivec;
+	p2 = q;
+	for(eip = ip + AESbsize; ip < eip; )
+		*p2++ ^= *ip++ ^ *mackey++;
+	aes_encrypt((ulong *)s->mackey, s->rounds, q, s->ivec);
+	return s->ivec;			/* only the 12 bytes leftmost */
+}
+
+/*
+ * Define by analogy with desCBCencrypt;  AES modes are not standardized yet.
+ * Because of the way that non-multiple-of-16 buffers are handled,
+ * the decryptor must be fed buffers of the same size as the encryptor.
+ */
+void
 aesCBCencrypt(uchar *p, int len, AESstate *s)
 {
 	uchar *p2, *ip, *eip;
@@ -81,7 +178,7 @@
 		ip = s->ivec;
 		for(eip = ip+AESbsize; ip < eip; )
 			*p2++ ^= *ip++;
-		rijndaelEncrypt(s->ekey, s->rounds, p, q);
+		aes_encrypt(s->ekey, s->rounds, p, q);
 		memmove(s->ivec, q, AESbsize);
 		memmove(p, q, AESbsize);
 		p += AESbsize;
@@ -89,7 +186,7 @@
 
 	if(len > 0){
 		ip = s->ivec;
-		rijndaelEncrypt(s->ekey, s->rounds, ip, q);
+		aes_encrypt(s->ekey, s->rounds, ip, q);
 		memmove(s->ivec, q, AESbsize);
 		for(eip = ip+len; ip < eip; )
 			*p++ ^= *ip++;
@@ -104,7 +201,7 @@
 
 	for(; len >= AESbsize; len -= AESbsize){
 		memmove(tmp, p, AESbsize);
-		rijndaelDecrypt(s->dkey, s->rounds, p, q);
+		aes_decrypt(s->dkey, s->rounds, p, q);
 		memmove(p, q, AESbsize);
 		tp = tmp;
 		ip = s->ivec;
@@ -116,7 +213,7 @@
 
 	if(len > 0){
 		ip = s->ivec;
-		rijndaelEncrypt(s->ekey, s->rounds, ip, q);
+		aes_encrypt(s->ekey, s->rounds, ip, q);
 		memmove(s->ivec, q, AESbsize);
 		for(eip = ip+len; ip < eip; )
 			*p++ ^= *ip++;
@@ -129,23 +226,26 @@
  *
  * @return	the number of rounds for the given cipher key size.
  */
-static int rijndaelKeySetup(u32 erk[/*4*(Nr + 1)*/], u32 drk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) {
+static int
+aes_setup(ulong erk[/* 4*(Nr + 1) */], ulong drk[/* 4*(Nr + 1) */],
+	const uchar cipherKey[], int keyBits)
+{
 	int Nr, i;
 
 	/* expand the cipher key: */
-	Nr = rijndaelKeySetupEnc(erk, cipherKey, keyBits);
+	Nr = aes_setupEnc(erk, cipherKey, keyBits);
 
 	/*
-	 * invert the order of the round keys and
-	 * apply the inverse MixColumn transform to all round keys but the first and the last
+	 * invert the order of the round keys and apply the inverse MixColumn
+	 * transform to all round keys but the first and the last
 	 */
-	drk[0       ] = erk[4*Nr    ]; 
+	drk[0       ] = erk[4*Nr    ];
 	drk[1       ] = erk[4*Nr + 1];
-	drk[2       ] = erk[4*Nr + 2]; 
+	drk[2       ] = erk[4*Nr + 2];
 	drk[3       ] = erk[4*Nr + 3];
-	drk[4*Nr    ] = erk[0       ]; 
+	drk[4*Nr    ] = erk[0       ];
 	drk[4*Nr + 1] = erk[1       ];
-	drk[4*Nr + 2] = erk[2       ]; 
+	drk[4*Nr + 2] = erk[2       ];
 	drk[4*Nr + 3] = erk[3       ];
 	erk += 4 * Nr;
 	for (i = 1; i < Nr; i++) {
@@ -175,6 +275,7 @@
 	return Nr;
 }
 
+
 /*
 Te0[x] = S [x].[02, 01, 01, 03];
 Te1[x] = S [x].[03, 02, 01, 01];
@@ -854,26 +955,24 @@
 static const u32 rcon[] = {
 	0x01000000, 0x02000000, 0x04000000, 0x08000000,
 	0x10000000, 0x20000000, 0x40000000, 0x80000000,
-	0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+	0x1B000000, 0x36000000,
+	/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
 };
 
-#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(pt) (((u32)(pt)[0]<<24) ^ ((u32)(pt)[1]<<16) ^ \
+		    ((u32)(pt)[2]<< 8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { (ct)[0] = (u8)((st)>>24); (ct)[1] = (u8)((st)>>16); \
+			 (ct)[2] = (u8)((st)>> 8); (ct)[3] = (u8)(st); }
 
-#ifdef _MSC_VER
-#define GETU32(p) SWAP(*((u32 *)(p)))
-#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
-#else
-#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
-#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
-#endif
-
-/**
+/*
  * Expand the cipher key into the encryption key schedule.
  *
  * @return	the number of rounds for the given cipher key size.
  */
-static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) {
-   	int i = 0;
+static int
+aes_setupEnc(ulong rk[/*4*(Nr + 1)*/], const uchar cipherKey[], int keyBits)
+{
+	int i = 0;
 	u32 temp;
 
 	rk[0] = GETU32(cipherKey     );
@@ -923,32 +1022,31 @@
 	rk[6] = GETU32(cipherKey + 24);
 	rk[7] = GETU32(cipherKey + 28);
 	if (keyBits == 256) {
-        for (;;) {
-        	temp = rk[ 7];
-        	rk[ 8] = rk[ 0] ^
-        		(Te4[(temp >> 16) & 0xff] << 24) ^
-        		(Te4[(temp >>  8) & 0xff] << 16) ^
-        		(Te4[(temp      ) & 0xff] <<  8) ^
-        		(Te4[(temp >> 24)       ]      ) ^
-        		rcon[i];
-        	rk[ 9] = rk[ 1] ^ rk[ 8];
-        	rk[10] = rk[ 2] ^ rk[ 9];
-        	rk[11] = rk[ 3] ^ rk[10];
+	        for (;;) {
+	        	temp = rk[ 7];
+	        	rk[ 8] = rk[ 0] ^
+	        		(Te4[(temp >> 16) & 0xff] << 24) ^
+	        		(Te4[(temp >>  8) & 0xff] << 16) ^
+	        		(Te4[(temp      ) & 0xff] <<  8) ^
+	        		(Te4[(temp >> 24)       ]      ) ^
+	        		rcon[i];
+	        	rk[ 9] = rk[ 1] ^ rk[ 8];
+	        	rk[10] = rk[ 2] ^ rk[ 9];
+	        	rk[11] = rk[ 3] ^ rk[10];
 			if (++i == 7) {
 				return 14;
 			}
-        	temp = rk[11];
-        	rk[12] = rk[ 4] ^
-        		(Te4[(temp >> 24)       ] << 24) ^
-        		(Te4[(temp >> 16) & 0xff] << 16) ^
-        		(Te4[(temp >>  8) & 0xff] <<  8) ^
-        		(Te4[(temp      ) & 0xff]      );
-        	rk[13] = rk[ 5] ^ rk[12];
-        	rk[14] = rk[ 6] ^ rk[13];
-        	rk[15] = rk[ 7] ^ rk[14];
-
+	        	temp = rk[11];
+	        	rk[12] = rk[ 4] ^
+	        		(Te4[(temp >> 24)       ] << 24) ^
+	        		(Te4[(temp >> 16) & 0xff] << 16) ^
+	        		(Te4[(temp >>  8) & 0xff] <<  8) ^
+	        		(Te4[(temp      ) & 0xff]      );
+	        	rk[13] = rk[ 5] ^ rk[12];
+	        	rk[14] = rk[ 6] ^ rk[13];
+	        	rk[15] = rk[ 7] ^ rk[14];
 			rk += 8;
-        }
+	        }
 	}
 	return 0;
 }
@@ -958,13 +1056,14 @@
  *
  * @return	the number of rounds for the given cipher key size.
  */
-#ifdef NOTUSED
-static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) {
+static int
+aes_setupDec(ulong rk[/* 4*(Nr + 1) */], const uchar cipherKey[], int keyBits)
+{
 	int Nr, i, j;
-	u32 temp;
+	ulong temp;
 
 	/* expand the cipher key: */
-	Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits);
+	Nr = aes_setupEnc(rk, cipherKey, keyBits);
 	/* invert the order of the round keys: */
 	for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
 		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
@@ -972,7 +1071,10 @@
 		temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
 		temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
 	}
-	/* apply the inverse MixColumn transform to all round keys but the first and the last: */
+	/*
+	 * apply the inverse MixColumn transform to all round keys
+	 * but the first and the last:
+	 */
 	for (i = 1; i < Nr; i++) {
 		rk += 4;
 		rk[0] =
@@ -998,15 +1100,18 @@
 	}
 	return Nr;
 }
-#endif
 
-static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) {
-	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+/* using round keys in rk, perform Nr rounds of encrypting pt into ct */
+void
+aes_encrypt(const ulong rk[/* 4*(Nr + 1) */], int Nr, const uchar pt[16],
+	uchar ct[16])
+{
+	ulong s0, s1, s2, s3, t0, t1, t2, t3;
 #ifndef FULL_UNROLL
-    int r;
+	int r;
 #endif /* ?FULL_UNROLL */
 
-    /*
+	/*
 	 * map byte array block to cipher state
 	 * and add initial round key:
 	 */
@@ -1015,7 +1120,7 @@
 	s2 = GETU32(pt +  8) ^ rk[2];
 	s3 = GETU32(pt + 12) ^ rk[3];
 #ifdef FULL_UNROLL
-    /* round 1: */
+	/* round 1: */
    	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
    	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
    	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
@@ -1025,7 +1130,7 @@
    	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
    	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
    	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
-    /* round 3: */
+	/* round 3: */
    	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
    	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
    	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
@@ -1035,7 +1140,7 @@
    	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
    	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
    	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
-    /* round 5: */
+	/* round 5: */
    	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
    	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
    	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
@@ -1045,7 +1150,7 @@
    	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
    	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
    	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
-    /* round 7: */
+	/* round 7: */
    	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
    	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
    	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
@@ -1055,99 +1160,98 @@
    	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
    	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
    	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
-    /* round 9: */
+	/* round 9: */
    	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
    	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
    	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
    	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
-    if (Nr > 10) {
-        /* round 10: */
-        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
-        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
-        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
-        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
-        /* round 11: */
-        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
-        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
-        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
-        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
-        if (Nr > 12) {
-            /* round 12: */
-            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
-            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
-            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
-            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
-            /* round 13: */
-            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
-            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
-            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
-            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
-        }
-    }
-    rk += Nr << 2;
-#else  /* !FULL_UNROLL */
-    /*
+	if (Nr > 10) {
+		/* round 10: */
+		s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+		s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+		s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+		s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+		/* round 11: */
+		t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+		t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+		t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+		t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+		if (Nr > 12) {
+			/* round 12: */
+			s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+			s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+			s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+			s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+			/* round 13: */
+			t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+			t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+			t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+			t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+		}
+	}
+	rk += Nr << 2;
+#else					/* !FULL_UNROLL */
+	/*
 	 * Nr - 1 full rounds:
 	 */
-    r = Nr >> 1;
-    for (;;) {
-        t0 =
-            Te0[(s0 >> 24)       ] ^
-            Te1[(s1 >> 16) & 0xff] ^
-            Te2[(s2 >>  8) & 0xff] ^
-            Te3[(s3      ) & 0xff] ^
-            rk[4];
-        t1 =
-            Te0[(s1 >> 24)       ] ^
-            Te1[(s2 >> 16) & 0xff] ^
-            Te2[(s3 >>  8) & 0xff] ^
-            Te3[(s0      ) & 0xff] ^
-            rk[5];
-        t2 =
-            Te0[(s2 >> 24)       ] ^
-            Te1[(s3 >> 16) & 0xff] ^
-            Te2[(s0 >>  8) & 0xff] ^
-            Te3[(s1      ) & 0xff] ^
-            rk[6];
-        t3 =
-            Te0[(s3 >> 24)       ] ^
-            Te1[(s0 >> 16) & 0xff] ^
-            Te2[(s1 >>  8) & 0xff] ^
-            Te3[(s2      ) & 0xff] ^
-            rk[7];
+	r = Nr >> 1;
+	for (;;) {
+	        t0 =
+	            Te0[(s0 >> 24)       ] ^
+	            Te1[(s1 >> 16) & 0xff] ^
+	            Te2[(s2 >>  8) & 0xff] ^
+	            Te3[(s3      ) & 0xff] ^
+	            rk[4];
+	        t1 =
+	            Te0[(s1 >> 24)       ] ^
+	            Te1[(s2 >> 16) & 0xff] ^
+	            Te2[(s3 >>  8) & 0xff] ^
+	            Te3[(s0      ) & 0xff] ^
+	            rk[5];
+	        t2 =
+	            Te0[(s2 >> 24)       ] ^
+	            Te1[(s3 >> 16) & 0xff] ^
+	            Te2[(s0 >>  8) & 0xff] ^
+	            Te3[(s1      ) & 0xff] ^
+	            rk[6];
+	        t3 =
+	            Te0[(s3 >> 24)       ] ^
+	            Te1[(s0 >> 16) & 0xff] ^
+	            Te2[(s1 >>  8) & 0xff] ^
+	            Te3[(s2      ) & 0xff] ^
+	            rk[7];
 
-        rk += 8;
-        if (--r == 0) {
-            break;
-        }
+	        rk += 8;
+	        if (--r == 0)
+	            break;
 
-        s0 =
-            Te0[(t0 >> 24)       ] ^
-            Te1[(t1 >> 16) & 0xff] ^
-            Te2[(t2 >>  8) & 0xff] ^
-            Te3[(t3      ) & 0xff] ^
-            rk[0];
-        s1 =
-            Te0[(t1 >> 24)       ] ^
-            Te1[(t2 >> 16) & 0xff] ^
-            Te2[(t3 >>  8) & 0xff] ^
-            Te3[(t0      ) & 0xff] ^
-            rk[1];
-        s2 =
-            Te0[(t2 >> 24)       ] ^
-            Te1[(t3 >> 16) & 0xff] ^
-            Te2[(t0 >>  8) & 0xff] ^
-            Te3[(t1      ) & 0xff] ^
-            rk[2];
-        s3 =
-            Te0[(t3 >> 24)       ] ^
-            Te1[(t0 >> 16) & 0xff] ^
-            Te2[(t1 >>  8) & 0xff] ^
-            Te3[(t2      ) & 0xff] ^
-            rk[3];
-    }
-#endif /* ?FULL_UNROLL */
-    /*
+	        s0 =
+	            Te0[(t0 >> 24)       ] ^
+	            Te1[(t1 >> 16) & 0xff] ^
+	            Te2[(t2 >>  8) & 0xff] ^
+	            Te3[(t3      ) & 0xff] ^
+	            rk[0];
+	        s1 =
+	            Te0[(t1 >> 24)       ] ^
+	            Te1[(t2 >> 16) & 0xff] ^
+	            Te2[(t3 >>  8) & 0xff] ^
+	            Te3[(t0      ) & 0xff] ^
+	            rk[1];
+	        s2 =
+	            Te0[(t2 >> 24)       ] ^
+	            Te1[(t3 >> 16) & 0xff] ^
+	            Te2[(t0 >>  8) & 0xff] ^
+	            Te3[(t1      ) & 0xff] ^
+	            rk[2];
+	        s3 =
+	            Te0[(t3 >> 24)       ] ^
+	            Te1[(t0 >> 16) & 0xff] ^
+	            Te2[(t1 >>  8) & 0xff] ^
+	            Te3[(t2      ) & 0xff] ^
+	            rk[3];
+	}
+#endif					/* ?FULL_UNROLL */
+	/*
 	 * apply last round and
 	 * map cipher state to byte array block:
 	 */
@@ -1181,13 +1285,16 @@
 	PUTU32(ct + 12, s3);
 }
 
-static void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[16]) {
-	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+void
+aes_decrypt(const ulong rk[/* 4*(Nr + 1) */], int Nr, const uchar ct[16],
+	uchar pt[16])
+{
+	ulong s0, s1, s2, s3, t0, t1, t2, t3;
 #ifndef FULL_UNROLL
-    int r;
-#endif /* ?FULL_UNROLL */
+	int r;
+#endif		/* ?FULL_UNROLL */
 
-    /*
+	/*
 	 * map byte array block to cipher state
 	 * and add initial round key:
 	 */
@@ -1265,8 +1372,8 @@
             t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
         }
     }
-	rk += Nr << 2;
-#else  /* !FULL_UNROLL */
+    rk += Nr << 2;
+#else					/* !FULL_UNROLL */
     /*
      * Nr - 1 full rounds:
      */
@@ -1298,9 +1405,8 @@
             rk[7];
 
         rk += 8;
-        if (--r == 0) {
+        if (--r == 0)
             break;
-        }
 
         s0 =
             Td0[(t0 >> 24)       ] ^
@@ -1327,8 +1433,8 @@
             Td3[(t0      ) & 0xff] ^
             rk[3];
     }
-#endif /* ?FULL_UNROLL */
-    /*
+#endif					/* ?FULL_UNROLL */
+	/*
 	 * apply last round and
 	 * map cipher state to byte array block:
 	 */
@@ -1364,11 +1470,14 @@
 
 #ifdef INTERMEDIATE_VALUE_KAT
 
-static void rijndaelEncryptRound(const u32 rk[/*4*(Nr + 1)*/], int Nr, u8 block[16], int rounds) {
+static void
+aes_encryptRound(const u32 rk[/* 4*(Nr + 1) */], int Nr, u8 block[16],
+	int rounds)
+{
 	int r;
 	u32 s0, s1, s2, s3, t0, t1, t2, t3;
 
-    /*
+	/*
 	 * map byte array block to cipher state
 	 * and add initial round key:
 	 */
@@ -1376,9 +1485,9 @@
 	s1 = GETU32(block +  4) ^ rk[1];
 	s2 = GETU32(block +  8) ^ rk[2];
 	s3 = GETU32(block + 12) ^ rk[3];
-    rk += 4;
+	rk += 4;
 
-    /*
+	/*
 	 * Nr - 1 full rounds:
 	 */
 	for (r = (rounds < Nr ? rounds : Nr - 1); r > 0; r--) {
@@ -1406,45 +1515,42 @@
 			Te2[(s1 >>  8) & 0xff] ^
 			Te3[(s2      ) & 0xff] ^
 			rk[3];
-
 		s0 = t0;
 		s1 = t1;
 		s2 = t2;
 		s3 = t3;
 		rk += 4;
+	}
 
-    }
-
-    /*
+	/*
 	 * apply last round and
 	 * map cipher state to byte array block:
 	 */
 	if (rounds == Nr) {
-    	t0 =
-    		(Te4[(s0 >> 24)       ] << 24) ^
-    		(Te4[(s1 >> 16) & 0xff] << 16) ^
-    		(Te4[(s2 >>  8) & 0xff] <<  8) ^
-    		(Te4[(s3      ) & 0xff]      ) ^
-    		rk[0];
-    	t1 =
-    		(Te4[(s1 >> 24)       ] << 24) ^
-    		(Te4[(s2 >> 16) & 0xff] << 16) ^
-    		(Te4[(s3 >>  8) & 0xff] <<  8) ^
-    		(Te4[(s0      ) & 0xff]      ) ^
-    		rk[1];
-    	t2 =
-    		(Te4[(s2 >> 24)       ] << 24) ^
-    		(Te4[(s3 >> 16) & 0xff] << 16) ^
-    		(Te4[(s0 >>  8) & 0xff] <<  8) ^
-    		(Te4[(s1      ) & 0xff]      ) ^
-    		rk[2];
-    	t3 =
-    		(Te4[(s3 >> 24)       ] << 24) ^
-    		(Te4[(s0 >> 16) & 0xff] << 16) ^
-    		(Te4[(s1 >>  8) & 0xff] <<  8) ^
-    		(Te4[(s2      ) & 0xff]      ) ^
-    		rk[3];
-		
+	    	t0 =
+	    		(Te4[(s0 >> 24)       ] << 24) ^
+	    		(Te4[(s1 >> 16) & 0xff] << 16) ^
+	    		(Te4[(s2 >>  8) & 0xff] <<  8) ^
+	    		(Te4[(s3      ) & 0xff]      ) ^
+	    		rk[0];
+	    	t1 =
+	    		(Te4[(s1 >> 24)       ] << 24) ^
+	    		(Te4[(s2 >> 16) & 0xff] << 16) ^
+	    		(Te4[(s3 >>  8) & 0xff] <<  8) ^
+	    		(Te4[(s0      ) & 0xff]      ) ^
+	    		rk[1];
+	    	t2 =
+	    		(Te4[(s2 >> 24)       ] << 24) ^
+	    		(Te4[(s3 >> 16) & 0xff] << 16) ^
+	    		(Te4[(s0 >>  8) & 0xff] <<  8) ^
+	    		(Te4[(s1      ) & 0xff]      ) ^
+	    		rk[2];
+	    	t3 =
+	    		(Te4[(s3 >> 24)       ] << 24) ^
+	    		(Te4[(s0 >> 16) & 0xff] << 16) ^
+	    		(Te4[(s1 >>  8) & 0xff] <<  8) ^
+	    		(Te4[(s2      ) & 0xff]      ) ^
+	    		rk[3];
 		s0 = t0;
 		s1 = t1;
 		s2 = t2;
@@ -1457,11 +1563,14 @@
 	PUTU32(block + 12, s3);
 }
 
-static void rijndaelDecryptRound(const u32 rk[/*4*(Nr + 1)*/], int Nr, u8 block[16], int rounds) {
+static void
+aes_decryptRound(const u32 rk[/* 4*(Nr + 1) */], int Nr, u8 block[16],
+	int rounds)
+{
 	int r;
 	u32 s0, s1, s2, s3, t0, t1, t2, t3;
 
-    /*
+	/*
 	 * map byte array block to cipher state
 	 * and add initial round key:
 	 */
@@ -1469,9 +1578,9 @@
 	s1 = GETU32(block +  4) ^ rk[1];
 	s2 = GETU32(block +  8) ^ rk[2];
 	s3 = GETU32(block + 12) ^ rk[3];
-    rk += 4;
+	rk += 4;
 
-    /*
+	/*
 	 * Nr - 1 full rounds:
 	 */
 	for (r = (rounds < Nr ? rounds : Nr) - 1; r > 0; r--) {
@@ -1505,10 +1614,9 @@
 		s2 = t2;
 		s3 = t3;
 		rk += 4;
+	}
 
-    }
-
-    /*
+	/*
 	 * complete the last round and
 	 * map cipher state to byte array block:
 	 */
@@ -1534,10 +1642,10 @@
 		(Td4[(s0      ) & 0xff]      );
 
 	if (rounds == Nr) {
-	    t0 ^= rk[0];
-	    t1 ^= rk[1];
-	    t2 ^= rk[2];
-	    t3 ^= rk[3];
+		t0 ^= rk[0];
+		t1 ^= rk[1];
+		t2 ^= rk[2];
+		t3 ^= rk[3];
 	}
 
 	PUTU32(block     , t0);
@@ -1546,4 +1654,4 @@
 	PUTU32(block + 12, t3);
 }
 
-#endif /* INTERMEDIATE_VALUE_KAT */
+#endif			/* INTERMEDIATE_VALUE_KAT */
--- /dev/null
+++ b/libsec/aes_xts.c
@@ -1,0 +1,70 @@
+// Author Taru Karttunen <[email protected]>
+// This file can be used as both Public Domain or Creative Commons CC0.
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+#define AesBlockSize 16
+
+static void xor128(uchar* o,uchar* i1,uchar* i2) {
+	((ulong*)o)[0] = ((ulong*)i1)[0] ^ ((ulong*)i2)[0];
+	((ulong*)o)[1] = ((ulong*)i1)[1] ^ ((ulong*)i2)[1];
+	((ulong*)o)[2] = ((ulong*)i1)[2] ^ ((ulong*)i2)[2];
+	((ulong*)o)[3] = ((ulong*)i1)[3] ^ ((ulong*)i2)[3];
+}
+
+static void gf_mulx(uchar* x) {
+    ulong t = ((((ulong*)(x))[3] & 0x80000000u) ? 0x00000087u : 0);;
+    ((ulong*)(x))[3] = (((ulong*)(x))[3] << 1) | (((ulong*)(x))[2] & 0x80000000u ? 1 : 0);
+    ((ulong*)(x))[2] = (((ulong*)(x))[2] << 1) | (((ulong*)(x))[1] & 0x80000000u ? 1 : 0);
+    ((ulong*)(x))[1] = (((ulong*)(x))[1] << 1) | (((ulong*)(x))[0] & 0x80000000u ? 1 : 0);
+    ((ulong*)(x))[0] = (((ulong*)(x))[0] << 1) ^ t;
+
+}
+
+int aes_xts_encrypt(ulong tweak[], ulong ecb[],  vlong sectorNumber, uchar *input, uchar *output, ulong len) {
+	uchar T[16], x[16];
+	int i;
+	
+	if(len % 16 != 0)
+		return -1;
+
+	for(i=0; i<AesBlockSize; i++) {
+		T[i] = (uchar)(sectorNumber & 0xFF);
+		sectorNumber = sectorNumber >> 8;
+	}
+	
+	aes_encrypt(tweak, 10, T, T);
+
+	for (i=0; i<len; i+=AesBlockSize) {
+		xor128(&x[0], &input[i], &T[0]);
+		aes_encrypt(ecb, 10, x, x);
+		xor128(&output[i], &x[0], &T[0]);
+		gf_mulx(&T[0]);
+	}
+	return 0;
+}
+
+int aes_xts_decrypt(ulong tweak[], ulong ecb[], vlong sectorNumber, uchar *input, uchar *output, ulong len) {
+	uchar T[16], x[16];
+	int i;
+	
+	if(len % 16 != 0)
+		return -1;
+
+	for(i=0; i<AesBlockSize; i++) {
+		T[i] = (uchar)(sectorNumber & 0xFF);
+		sectorNumber = sectorNumber >> 8;
+	}
+	
+	aes_encrypt(tweak, 10, T, T);
+
+	for (i=0; i<len; i+=AesBlockSize) {
+		xor128(&x[0], &input[i], &T[0]);
+		aes_decrypt(ecb, 10, x, x);
+		xor128(&output[i], &x[0], &T[0]);
+		gf_mulx(&T[0]);
+	}
+	return 0;
+}
+
--- a/libsec/blowfish.c
+++ b/libsec/blowfish.c
@@ -13,6 +13,37 @@
 static void bfencrypt(u32int *, BFstate *);
 static void bfdecrypt(u32int *, BFstate *);
 
+/*
+ * Endianess agnostic functions to convert a 
+ * block (8-byte buffer) to a u32int array and 
+ * viceversa.
+ */
+
+static void
+buf2ints(uchar *p, u32int *b)
+{
+	b[0] =  p[0]<<24 | p[1]<<16  | p[2]<<8 | p[3];
+	b[1] =  p[4]<<24 | p[5]<<16  | p[6]<<8 | p[7];
+}
+
+static void
+ints2buf(u32int *b, uchar *p)
+{
+	u32int u;
+
+	u = b[0];
+	p[0] = u>>24;
+	p[1] = u>>16;
+	p[2] = u>>8;
+	p[3] = u;
+
+	u = b[1];
+	p[4] = u>>24;
+	p[5] = u>>16;
+	p[6] = u>>8;
+	p[7] = u;
+}
+
 void
 setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec)
 {
@@ -31,7 +62,7 @@
 		memmove(s->ivec, ivec, sizeof(s->ivec));
 	else
 		memset(s->ivec, 0, sizeof(s->ivec));
-		
+
 	memmove(s->pbox, pbox, sizeof(pbox));
 	memmove(s->sbox, sbox, sizeof(sbox));
 
@@ -76,17 +107,13 @@
 bfCBCencrypt(uchar *buf, int n, BFstate *s)
 {
 	int i;
-	uchar *p;
-	u32int bo[2], bi[2], b;
+	u32int bo[2], bi[2];
 
 	assert((n & 7) == 0);
 
-	bo[0] =  s->ivec[0] | ((u32int) s->ivec[1]<<8) | ((u32int)s->ivec[2]<<16) | ((u32int)s->ivec[3]<<24);
-	bo[1] =  s->ivec[4] | ((u32int) s->ivec[5]<<8) | ((u32int)s->ivec[6]<<16) | ((u32int)s->ivec[7]<<24);
-
+	buf2ints(s->ivec, bo);
 	for(i=0; i < n; i += 8, buf += 8) {
-		bi[0] =  buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24);
-		bi[1] =  buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24);
+		buf2ints(buf, bi);
 
 		bi[0] ^= bo[0];
 		bi[1] ^= bo[1];
@@ -96,36 +123,9 @@
 		bo[0] = bi[0];
 		bo[1] = bi[1];
 
-		p = buf;
-		b = bo[0];
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-
-		b = bo[1];
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p = b;
+		ints2buf(bi, buf);
 	}
-
-	s->ivec[7] = bo[1] >> 24;
-	s->ivec[6] = bo[1] >> 16;
-	s->ivec[5] = bo[1] >> 8;
-	s->ivec[4] = bo[1];
-
-	s->ivec[3] = bo[0] >> 24;
-	s->ivec[2] = bo[0] >> 16;
-	s->ivec[1] = bo[0] >> 8;
-	s->ivec[0] = bo[0];
-
+	ints2buf(bo, s->ivec);
 	return;
 }
 
@@ -133,17 +133,13 @@
 bfCBCdecrypt(uchar *buf, int n, BFstate *s)
 {
 	int i;
-	uchar *p;
-	u32int b, bo[2], bi[2], xr[2];
+	u32int  bo[2], bi[2], xr[2];
 
 	assert((n & 7) == 0);
 
-	bo[0] =  s->ivec[0] | ((u32int) s->ivec[1]<<8) | ((u32int)s->ivec[2]<<16) | ((u32int)s->ivec[3]<<24);
-	bo[1] =  s->ivec[4] | ((u32int) s->ivec[5]<<8) | ((u32int)s->ivec[6]<<16) | ((u32int)s->ivec[7]<<24);
-
+	buf2ints(s->ivec, bo);
 	for(i=0; i < n; i += 8, buf += 8) {
-		bi[0] =  buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24);
-		bi[1] =  buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24);
+		buf2ints(buf, bi);
 
 		xr[0] = bi[0];
 		xr[1] = bi[1];
@@ -153,39 +149,12 @@
 		bo[0] ^= bi[0];
 		bo[1] ^= bi[1];
 
-		p = buf;
-		b = bo[0];
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
+		ints2buf(bo, buf);
 
-		b = bo[1];
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p++ = b;
-		b >>= 8;
-		*p = b;
-
 		bo[0] = xr[0];
 		bo[1] = xr[1];
 	}
-
-	s->ivec[7] = bo[1] >> 24;
-	s->ivec[6] = bo[1] >> 16;
-	s->ivec[5] = bo[1] >> 8;
-	s->ivec[4] = bo[1];
-
-	s->ivec[3] = bo[0] >> 24;
-	s->ivec[2] = bo[0] >> 16;
-	s->ivec[1] = bo[0] >> 8;
-	s->ivec[0] = bo[0];
-
+	ints2buf(bo, s->ivec);
 	return;
 }
 
@@ -196,20 +165,9 @@
 	u32int b[2];
 
 	for(i=0; i < n; i += 8, buf += 8) {
-		b[0] =  buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24);
-		b[1] =  buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24);
-
+		buf2ints(buf, b);
 		bfencrypt(b, s);
-
-		buf[7] = b[1] >> 24;
-		buf[6] = b[1] >> 16;
-		buf[5] = b[1] >> 8;
-		buf[4] = b[1];
-
-		buf[3] = b[0] >> 24;
-		buf[2] = b[0] >> 16;
-		buf[1] = b[0] >> 8;
-		buf[0] = b[0];
+		ints2buf(b, buf);
 	}
 
 	return;
@@ -222,20 +180,9 @@
 	u32int b[2];
 
 	for(i=0; i < n; i += 8, buf += 8) {
-		b[0] =  buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24);
-		b[1] =  buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24);
-
+		buf2ints(buf, b);
 		bfdecrypt(b, s);
-
-		buf[7] = b[1] >> 24;
-		buf[6] = b[1] >> 16;
-		buf[5] = b[1] >> 8;
-		buf[4] = b[1];
-
-		buf[3] = b[0] >> 24;
-		buf[2] = b[0] >> 16;
-		buf[1] = b[0] >> 8;
-		buf[0] = b[0];
+		ints2buf(b, buf);
 	}
 
 	return;		
@@ -575,5 +522,4 @@
 	0x90d4f869L, 0xa65cdea0L, 0x3f09252dL, 0xc208e69fL, 
 	0xb74e6132L, 0xce77e25bL, 0x578fdfe3L, 0x3ac372e6L, 
 };
-
 
--- /dev/null
+++ b/libsec/ccpoly.c
@@ -1,0 +1,91 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+static void
+ccpolyotk(Chachastate *cs, DigestState *ds)
+{
+	uchar otk[ChachaBsize];
+
+	memset(ds, 0, sizeof(*ds));
+	memset(otk, 0, 32);
+	chacha_setblock(cs, 0);
+	chacha_encrypt(otk, ChachaBsize, cs);
+	poly1305(nil, 0, otk, 32, nil, ds);
+}
+
+static void
+ccpolypad(uchar *buf, ulong nbuf, DigestState *ds)
+{
+	static uchar zeros[16] = {0};
+	ulong npad;
+
+	if(nbuf == 0)
+		return;
+	poly1305(buf, nbuf, nil, 0, nil, ds);
+	npad = nbuf % 16;
+	if(npad == 0)
+		return;
+	poly1305(zeros, 16 - npad, nil, 0, nil, ds);
+}
+
+static void
+ccpolylen(ulong n, uchar tag[16], DigestState *ds)
+{
+	uchar info[8];
+
+	info[0] = n;
+	info[1] = n>>8;
+	info[2] = n>>16;
+	info[3] = n>>24;
+	info[4] = 0;
+	info[5] = 0;
+	info[6] = 0;
+	info[7] = 0;
+	poly1305(info, 8, nil, 0, tag, ds);
+}
+
+void
+ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs)
+{
+	DigestState ds;
+
+	ccpolyotk(cs, &ds);
+	if(cs->ivwords == 2){
+		poly1305(aad, naad, nil, 0, nil, &ds);
+		ccpolylen(naad, nil, &ds);
+		chacha_encrypt(dat, ndat, cs);
+		poly1305(dat, ndat, nil, 0, nil, &ds);
+		ccpolylen(ndat, tag, &ds);
+	} else {
+		ccpolypad(aad, naad, &ds);
+		chacha_encrypt(dat, ndat, cs);
+		ccpolypad(dat, ndat, &ds);
+		ccpolylen(naad, nil, &ds);
+		ccpolylen(ndat, tag, &ds);
+	}
+}
+
+int
+ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs)
+{
+	DigestState ds;
+	uchar tmp[16];
+
+	ccpolyotk(cs, &ds);
+	if(cs->ivwords == 2){
+		poly1305(aad, naad, nil, 0, nil, &ds);
+		ccpolylen(naad, nil, &ds);
+		poly1305(dat, ndat, nil, 0, nil, &ds);
+		ccpolylen(ndat, tmp, &ds);
+	} else {
+		ccpolypad(aad, naad, &ds);
+		ccpolypad(dat, ndat, &ds);
+		ccpolylen(naad, nil, &ds);
+		ccpolylen(ndat, tmp, &ds);
+	}
+	if(tsmemcmp(tag, tmp, 16) != 0)
+		return -1;
+	chacha_encrypt(dat, ndat, cs);
+	return 0;
+}
--- /dev/null
+++ b/libsec/chacha.c
@@ -1,0 +1,187 @@
+/*
+Adapted from chacha-merged.c version 20080118
+D. J. Bernstein
+Public domain.
+
+modified for use in Plan 9 and Inferno (no algorithmic changes),
+and including the changes to block number and nonce defined in RFC7539
+*/
+
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+enum{
+	Blockwords=	ChachaBsize/sizeof(u32int)
+};
+
+/* little-endian data order */
+#define GET4(p)	((((((p)[3]<<8) | (p)[2])<<8) | (p)[1])<<8 | (p)[0])
+#define PUT4(p, v)	(((p)[0]=v), (v>>=8), ((p)[1]=v), (v>>=8), ((p)[2]=v), (v>>=8), ((p)[3]=v))
+
+#define ROTATE(v,c) ((u32int)((v) << (c)) | ((v) >> (32 - (c))))
+
+#define QUARTERROUND(ia,ib,ic,id) { \
+	u32int a, b, c, d, t;\
+	a = x[ia]; b = x[ib]; c = x[ic]; d = x[id]; \
+	a += b; t = d^a; d = ROTATE(t,16); \
+	c += d; t = b^c; b = ROTATE(t,12); \
+	a += b; t = d^a; d = ROTATE(t, 8); \
+	c += d; t = b^c; b = ROTATE(t, 7); \
+	x[ia] = a; x[ib] = b; x[ic] = c; x[id] = d; \
+}
+
+#define ENCRYPT(s, x, y, d) {\
+	u32int v; \
+	uchar *sp, *dp; \
+	sp = (s); \
+	v = GET4(sp); \
+	v ^= (x)+(y); \
+	dp = (d); \
+	PUT4(dp, v); \
+}
+
+static uchar sigma[16] = "expand 32-byte k";
+static uchar tau[16] = "expand 16-byte k";
+
+static void
+load(u32int *d, uchar *s, int nw)
+{
+	int i;
+
+	for(i = 0; i < nw; i++, s+=4)
+		d[i] = GET4(s);
+}
+
+void
+setupChachastate(Chachastate *s, uchar *key, ulong keylen, uchar *iv, ulong ivlen, int rounds)
+{
+	if(keylen != 256/8 && keylen != 128/8)
+		sysfatal("invalid chacha key length");
+	if(ivlen != 96/8 && ivlen != 64/8)
+		sysfatal("invalid chacha iv length");
+	if(rounds == 0)
+		rounds = 20;
+	s->rounds = rounds;
+	if(keylen == 256/8) { /* recommended */
+		load(&s->input[0], sigma, 4);
+		load(&s->input[4], key, 8);
+	}else{
+		load(&s->input[0], tau, 4);
+		load(&s->input[4], key, 4);
+		load(&s->input[8], key, 4);
+	}
+	s->ivwords = ivlen/sizeof(u32int);
+	s->input[12] = 0;
+	s->input[13] = 0;
+	if(iv == nil){
+		s->input[14] = 0;
+		s->input[15] = 0;
+	}else
+		chacha_setiv(s, iv);
+}
+
+void
+chacha_setiv(Chachastate *s, uchar *iv)
+{
+	load(&s->input[16 - s->ivwords], iv, s->ivwords);
+}
+
+void
+chacha_setblock(Chachastate *s, u64int blockno)
+{
+	s->input[12] = blockno;
+	if(s->ivwords == 2)
+		s->input[13] = blockno>>32;
+}
+
+static void
+encryptblock(Chachastate *s, uchar *src, uchar *dst)
+{
+	u32int x[Blockwords];
+	int i, rounds;
+
+	rounds = s->rounds;
+	x[0] = s->input[0];
+	x[1] = s->input[1];
+	x[2] = s->input[2];
+	x[3] = s->input[3];
+	x[4] = s->input[4];
+	x[5] = s->input[5];
+	x[6] = s->input[6];
+	x[7] = s->input[7];
+	x[8] = s->input[8];
+	x[9] = s->input[9];
+	x[10] = s->input[10];
+	x[11] = s->input[11];
+	x[12] = s->input[12];
+	x[13] = s->input[13];
+	x[14] = s->input[14];
+	x[15] = s->input[15];
+
+	for(i = rounds; i > 0; i -= 2) {
+		QUARTERROUND(0, 4, 8,12)
+		QUARTERROUND(1, 5, 9,13)
+		QUARTERROUND(2, 6,10,14)
+		QUARTERROUND(3, 7,11,15)
+
+		QUARTERROUND(0, 5,10,15)
+		QUARTERROUND(1, 6,11,12)
+		QUARTERROUND(2, 7, 8,13)
+		QUARTERROUND(3, 4, 9,14)
+	}
+
+#ifdef FULL_UNROLL
+	ENCRYPT(src+0*4, x[0], s->input[0], dst+0*4);
+	ENCRYPT(src+1*4, x[1], s->input[1], dst+1*4);
+	ENCRYPT(src+2*4, x[2], s->input[2], dst+2*4);
+	ENCRYPT(src+3*4, x[3], s->input[3], dst+3*4);
+	ENCRYPT(src+4*4, x[4], s->input[4], dst+4*4);
+	ENCRYPT(src+5*4, x[5], s->input[5], dst+5*4);
+	ENCRYPT(src+6*4, x[6], s->input[6], dst+6*4);
+	ENCRYPT(src+7*4, x[7], s->input[7], dst+7*4);
+	ENCRYPT(src+8*4, x[8], s->input[8], dst+8*4);
+	ENCRYPT(src+9*4, x[9], s->input[9], dst+9*4);
+	ENCRYPT(src+10*4, x[10], s->input[10], dst+10*4);
+	ENCRYPT(src+11*4, x[11], s->input[11], dst+11*4);
+	ENCRYPT(src+12*4, x[12], s->input[12], dst+12*4);
+	ENCRYPT(src+13*4, x[13], s->input[13], dst+13*4);
+	ENCRYPT(src+14*4, x[14], s->input[14], dst+14*4);
+	ENCRYPT(src+15*4, x[15], s->input[15], dst+15*4);
+#else
+	for(i=0; i<nelem(x); i+=4){
+		ENCRYPT(src, x[i], s->input[i], dst);
+		ENCRYPT(src+4, x[i+1], s->input[i+1], dst+4);
+		ENCRYPT(src+8, x[i+2], s->input[i+2], dst+8);
+		ENCRYPT(src+12, x[i+3], s->input[i+3], dst+12);
+		src += 16;
+		dst += 16;
+	}
+#endif
+
+	if(++s->input[12] == 0 && s->ivwords == 2)
+		s->input[13]++;
+}
+
+void
+chacha_encrypt2(uchar *src, uchar *dst, ulong bytes, Chachastate *s)
+{
+	uchar tmp[ChachaBsize];
+
+	for(; bytes >= ChachaBsize; bytes -= ChachaBsize){
+		encryptblock(s, src, dst);
+		src += ChachaBsize;
+		dst += ChachaBsize;
+	}
+	if(bytes > 0){
+		memmove(tmp, src, bytes);
+		encryptblock(s, tmp, tmp);
+		memmove(dst, tmp, bytes);
+	}
+}
+
+void
+chacha_encrypt(uchar *buf, ulong bytes, Chachastate *s)
+{
+	chacha_encrypt2(buf, buf, bytes, s);
+}
--- /dev/null
+++ b/libsec/curve25519.c
@@ -1,0 +1,571 @@
+/* Copyright 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ * curve25519: Curve25519 elliptic curve, public key function
+ *
+ * http://code.google.com/p/curve25519-donna/
+ *
+ * Adam Langley <[email protected]>
+ *
+ * Derived from public domain C code by Daniel J. Bernstein <[email protected]>
+ *
+ * More information about curve25519 can be found here
+ *   http://cr.yp.to/ecdh.html
+ *
+ * djb's sample implementation of curve25519 is written in a special assembly
+ * language called qhasm and uses the floating point registers.
+ *
+ * This is, almost, a clean room reimplementation from the curve25519 paper. It
+ * uses many of the tricks described therein. Only the crecip function is taken
+ * from the sample implementation.
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+typedef vlong felem;
+
+/* Sum two numbers: output += in */
+static void fsum(felem *output, felem *in) {
+  unsigned i;
+  for (i = 0; i < 10; i += 2) {
+    output[0+i] = (output[0+i] + in[0+i]);
+    output[1+i] = (output[1+i] + in[1+i]);
+  }
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!)
+ */
+static void fdifference(felem *output, felem *in) {
+  unsigned i;
+  for (i = 0; i < 10; ++i) {
+    output[i] = (in[i] - output[i]);
+  }
+}
+
+/* Multiply a number my a scalar: output = in * scalar */
+static void fscalar_product(felem *output, felem *in, felem scalar) {
+  unsigned i;
+  for (i = 0; i < 10; ++i) {
+    output[i] = in[i] * scalar;
+  }
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ */
+static void fproduct(felem *output, felem *in2, felem *in) {
+  output[0] =      in2[0] * in[0];
+  output[1] =      in2[0] * in[1] +
+                   in2[1] * in[0];
+  output[2] =  2 * in2[1] * in[1] +
+                   in2[0] * in[2] +
+                   in2[2] * in[0];
+  output[3] =      in2[1] * in[2] +
+                   in2[2] * in[1] +
+                   in2[0] * in[3] +
+                   in2[3] * in[0];
+  output[4] =      in2[2] * in[2] +
+               2 * (in2[1] * in[3] +
+                    in2[3] * in[1]) +
+                   in2[0] * in[4] +
+                   in2[4] * in[0];
+  output[5] =      in2[2] * in[3] +
+                   in2[3] * in[2] +
+                   in2[1] * in[4] +
+                   in2[4] * in[1] +
+                   in2[0] * in[5] +
+                   in2[5] * in[0];
+  output[6] =  2 * (in2[3] * in[3] +
+                    in2[1] * in[5] +
+                    in2[5] * in[1]) +
+                   in2[2] * in[4] +
+                   in2[4] * in[2] +
+                   in2[0] * in[6] +
+                   in2[6] * in[0];
+  output[7] =      in2[3] * in[4] +
+                   in2[4] * in[3] +
+                   in2[2] * in[5] +
+                   in2[5] * in[2] +
+                   in2[1] * in[6] +
+                   in2[6] * in[1] +
+                   in2[0] * in[7] +
+                   in2[7] * in[0];
+  output[8] =      in2[4] * in[4] +
+               2 * (in2[3] * in[5] +
+                    in2[5] * in[3] +
+                    in2[1] * in[7] +
+                    in2[7] * in[1]) +
+                   in2[2] * in[6] +
+                   in2[6] * in[2] +
+                   in2[0] * in[8] +
+                   in2[8] * in[0];
+  output[9] =      in2[4] * in[5] +
+                   in2[5] * in[4] +
+                   in2[3] * in[6] +
+                   in2[6] * in[3] +
+                   in2[2] * in[7] +
+                   in2[7] * in[2] +
+                   in2[1] * in[8] +
+                   in2[8] * in[1] +
+                   in2[0] * in[9] +
+                   in2[9] * in[0];
+  output[10] = 2 * (in2[5] * in[5] +
+                    in2[3] * in[7] +
+                    in2[7] * in[3] +
+                    in2[1] * in[9] +
+                    in2[9] * in[1]) +
+                   in2[4] * in[6] +
+                   in2[6] * in[4] +
+                   in2[2] * in[8] +
+                   in2[8] * in[2];
+  output[11] =     in2[5] * in[6] +
+                   in2[6] * in[5] +
+                   in2[4] * in[7] +
+                   in2[7] * in[4] +
+                   in2[3] * in[8] +
+                   in2[8] * in[3] +
+                   in2[2] * in[9] +
+                   in2[9] * in[2];
+  output[12] =     in2[6] * in[6] +
+               2 * (in2[5] * in[7] +
+                    in2[7] * in[5] +
+                    in2[3] * in[9] +
+                    in2[9] * in[3]) +
+                   in2[4] * in[8] +
+                   in2[8] * in[4];
+  output[13] =     in2[6] * in[7] +
+                   in2[7] * in[6] +
+                   in2[5] * in[8] +
+                   in2[8] * in[5] +
+                   in2[4] * in[9] +
+                   in2[9] * in[4];
+  output[14] = 2 * (in2[7] * in[7] +
+                    in2[5] * in[9] +
+                    in2[9] * in[5]) +
+                   in2[6] * in[8] +
+                   in2[8] * in[6];
+  output[15] =     in2[7] * in[8] +
+                   in2[8] * in[7] +
+                   in2[6] * in[9] +
+                   in2[9] * in[6];
+  output[16] =     in2[8] * in[8] +
+               2 * (in2[7] * in[9] +
+                    in2[9] * in[7]);
+  output[17] =     in2[8] * in[9] +
+                   in2[9] * in[8];
+  output[18] = 2 * in2[9] * in[9];
+}
+
+/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */
+static void freduce_degree(felem *output) {
+  output[8] += 19 * output[18];
+  output[7] += 19 * output[17];
+  output[6] += 19 * output[16];
+  output[5] += 19 * output[15];
+  output[4] += 19 * output[14];
+  output[3] += 19 * output[13];
+  output[2] += 19 * output[12];
+  output[1] += 19 * output[11];
+  output[0] += 19 * output[10];
+}
+
+/* Reduce all coefficients of the short form input to be -2**25 <= x <= 2**25
+ */
+static void freduce_coefficients(felem *output) {
+  unsigned i;
+  do {
+    output[10] = 0;
+
+    for (i = 0; i < 10; i += 2) {
+      felem over = output[i] / 0x2000000l;
+      felem over2 = (over + ((over >> 63) * 2) + 1) / 2;
+      output[i+1] += over2;
+      output[i] -= over2 * 0x4000000l;
+
+      over = output[i+1] / 0x2000000;
+      output[i+2] += over;
+      output[i+1] -= over * 0x2000000;
+    }
+    output[0] += 19 * output[10];
+  } while (output[10]);
+}
+
+/* A helpful wrapper around fproduct: output = in * in2.
+ *
+ * output must be distinct to both inputs. The output is reduced degree and
+ * reduced coefficient.
+ */
+static void
+fmul(felem *output, felem *in, felem *in2) {
+  felem t[19];
+  fproduct(t, in, in2);
+  freduce_degree(t);
+  freduce_coefficients(t);
+  memcpy(output, t, sizeof(felem) * 10);
+}
+
+static void fsquare_inner(felem *output, felem *in) {
+  felem tmp;
+  output[0] =      in[0] * in[0];
+  output[1] =  2 * in[0] * in[1];
+  output[2] =  2 * (in[1] * in[1] +
+                    in[0] * in[2]);
+  output[3] =  2 * (in[1] * in[2] +
+                    in[0] * in[3]);
+  output[4] =      in[2] * in[2] +
+               4 * in[1] * in[3] +
+               2 * in[0] * in[4];
+  output[5] =  2 * (in[2] * in[3] +
+                    in[1] * in[4] +
+                    in[0] * in[5]);
+  output[6] =  2 * (in[3] * in[3] +
+                    in[2] * in[4] +
+                    in[0] * in[6] +
+                2 * in[1] * in[5]);
+  output[7] =  2 * (in[3] * in[4] +
+                    in[2] * in[5] +
+                    in[1] * in[6] +
+                    in[0] * in[7]);
+  tmp = in[1] * in[7] + in[3] * in[5];
+  output[8] =      in[4] * in[4] +
+               2 * (in[2] * in[6] +
+                    in[0] * in[8] +
+                        2 * tmp);
+  output[9] =  2 * (in[4] * in[5] +
+                    in[3] * in[6] +
+                    in[2] * in[7] +
+                    in[1] * in[8] +
+                    in[0] * in[9]);
+  tmp = in[3] * in[7] + in[1] * in[9];
+  output[10] = 2 * (in[5] * in[5] +
+                   in[4] * in[6] +
+                   in[2] * in[8] +
+                       2 * tmp);
+  output[11] = 2 * (in[5] * in[6] +
+                    in[4] * in[7] +
+                    in[3] * in[8] +
+                    in[2] * in[9]);
+  output[12] =     in[6] * in[6] +
+               2 * (in[4] * in[8] +
+                2 * (in[5] * in[7] +
+                     in[3] * in[9]));
+  output[13] = 2 * (in[6] * in[7] +
+                    in[5] * in[8] +
+                    in[4] * in[9]);
+  output[14] = 2 * (in[7] * in[7] +
+                    in[6] * in[8] +
+                2 * in[5] * in[9]);
+  output[15] = 2 * (in[7] * in[8] +
+                    in[6] * in[9]);
+  output[16] =     in[8] * in[8] +
+               4 * in[7] * in[9];
+  output[17] = 2 * in[8] * in[9];
+  output[18] = 2 * in[9] * in[9];
+}
+
+static void
+fsquare(felem *output, felem *in) {
+  felem t[19];
+  fsquare_inner(t, in);
+  freduce_degree(t);
+  freduce_coefficients(t);
+  memcpy(output, t, sizeof(felem) * 10);
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void
+fexpand(felem *output, uchar *input) {
+#define F(n,start,shift,mask) \
+  output[n] = ((((felem) input[start + 0]) | \
+                ((felem) input[start + 1]) << 8 | \
+                ((felem) input[start + 2]) << 16 | \
+                ((felem) input[start + 3]) << 24) >> shift) & mask;
+  F(0, 0, 0, 0x3ffffff);
+  F(1, 3, 2, 0x1ffffff);
+  F(2, 6, 3, 0x3ffffff);
+  F(3, 9, 5, 0x1ffffff);
+  F(4, 12, 6, 0x3ffffff);
+  F(5, 16, 0, 0x1ffffff);
+  F(6, 19, 1, 0x3ffffff);
+  F(7, 22, 3, 0x1ffffff);
+  F(8, 25, 4, 0x3ffffff);
+  F(9, 28, 6, 0x1ffffff);
+#undef F
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array
+ */
+static void
+fcontract(uchar *output, felem *input) {
+  int i;
+
+  do {
+    for (i = 0; i < 9; ++i) {
+      if ((i & 1) == 1) {
+        while (input[i] < 0) {
+          input[i] += 0x2000000;
+          input[i + 1]--;
+        }
+      } else {
+        while (input[i] < 0) {
+          input[i] += 0x4000000;
+          input[i + 1]--;
+        }
+      }
+    }
+    while (input[9] < 0) {
+      input[9] += 0x2000000;
+      input[0] -= 19;
+    }
+  } while (input[0] < 0);
+
+  input[1] <<= 2;
+  input[2] <<= 3;
+  input[3] <<= 5;
+  input[4] <<= 6;
+  input[6] <<= 1;
+  input[7] <<= 3;
+  input[8] <<= 4;
+  input[9] <<= 6;
+#define F(i, s) \
+  output[s+0] |=  input[i] & 0xff; \
+  output[s+1]  = (input[i] >> 8) & 0xff; \
+  output[s+2]  = (input[i] >> 16) & 0xff; \
+  output[s+3]  = (input[i] >> 24) & 0xff;
+  output[0] = 0;
+  output[16] = 0;
+  F(0,0);
+  F(1,3);
+  F(2,6);
+  F(3,9);
+  F(4,12);
+  F(5,16);
+  F(6,19);
+  F(7,22);
+  F(8,25);
+  F(9,28);
+#undef F
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ *   x2 z3: long form
+ *   x3 z3: long form
+ *   x z: short form, destroyed
+ *   xprime zprime: short form, destroyed
+ *   qmqp: short form, preserved
+ */
+static void fmonty(felem *x2, felem *z2,  /* output 2Q */
+                   felem *x3, felem *z3,  /* output Q + Q' */
+                   felem *x, felem *z,    /* input Q */
+                   felem *xprime, felem *zprime,  /* input Q' */
+                   felem *qmqp /* input Q - Q' */) {
+  felem origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
+        zzprime[19], zzzprime[19], xxxprime[19];
+
+  memcpy(origx, x, 10 * sizeof(felem));
+  fsum(x, z);
+  fdifference(z, origx);  // does x - z
+
+  memcpy(origxprime, xprime, sizeof(felem) * 10);
+  fsum(xprime, zprime);
+  fdifference(zprime, origxprime);
+  fproduct(xxprime, xprime, z);
+  fproduct(zzprime, x, zprime);
+  freduce_degree(xxprime);
+  freduce_coefficients(xxprime);
+  freduce_degree(zzprime);
+  freduce_coefficients(zzprime);
+  memcpy(origxprime, xxprime, sizeof(felem) * 10);
+  fsum(xxprime, zzprime);
+  fdifference(zzprime, origxprime);
+  fsquare(xxxprime, xxprime);
+  fsquare(zzzprime, zzprime);
+  fproduct(zzprime, zzzprime, qmqp);
+  freduce_degree(zzprime);
+  freduce_coefficients(zzprime);
+  memcpy(x3, xxxprime, sizeof(felem) * 10);
+  memcpy(z3, zzprime, sizeof(felem) * 10);
+
+  fsquare(xx, x);
+  fsquare(zz, z);
+  fproduct(x2, xx, zz);
+  freduce_degree(x2);
+  freduce_coefficients(x2);
+  fdifference(zz, xx);  // does zz = xx - zz
+  memset(zzz + 10, 0, sizeof(felem) * 9);
+  fscalar_product(zzz, zz, 121665);
+  freduce_degree(zzz);
+  freduce_coefficients(zzz);
+  fsum(zzz, xx);
+  fproduct(z2, zz, zzz);
+  freduce_degree(z2);
+  freduce_coefficients(z2);
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ *   resultx/resultz: the x coordinate of the resulting curve point (short form)
+ *   n: a little endian, 32-byte number
+ *   q: a point of the curve (short form)
+ */
+static void
+cmult(felem *resultx, felem *resultz, uchar *n, felem *q) {
+  felem a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
+  felem *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+  felem e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
+  felem *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+  unsigned i, j;
+
+  memcpy(nqpqx, q, sizeof(felem) * 10);
+
+  for (i = 0; i < 32; ++i) {
+    uchar byte = n[31 - i];
+    for (j = 0; j < 8; ++j) {
+      if (byte & 0x80) {
+        fmonty(nqpqx2, nqpqz2,
+               nqx2, nqz2,
+               nqpqx, nqpqz,
+               nqx, nqz,
+               q);
+      } else {
+        fmonty(nqx2, nqz2,
+               nqpqx2, nqpqz2,
+               nqx, nqz,
+               nqpqx, nqpqz,
+               q);
+      }
+
+      t = nqx;
+      nqx = nqx2;
+      nqx2 = t;
+      t = nqz;
+      nqz = nqz2;
+      nqz2 = t;
+      t = nqpqx;
+      nqpqx = nqpqx2;
+      nqpqx2 = t;
+      t = nqpqz;
+      nqpqz = nqpqz2;
+      nqpqz2 = t;
+
+      byte <<= 1;
+    }
+  }
+
+  memcpy(resultx, nqx, sizeof(felem) * 10);
+  memcpy(resultz, nqz, sizeof(felem) * 10);
+}
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code
+// -----------------------------------------------------------------------------
+static void
+crecip(felem *out, felem *z) {
+  felem z2[10];
+  felem z9[10];
+  felem z11[10];
+  felem z2_5_0[10];
+  felem z2_10_0[10];
+  felem z2_20_0[10];
+  felem z2_50_0[10];
+  felem z2_100_0[10];
+  felem t0[10];
+  felem t1[10];
+  int i;
+
+  /* 2 */ fsquare(z2,z);
+  /* 4 */ fsquare(t1,z2);
+  /* 8 */ fsquare(t0,t1);
+  /* 9 */ fmul(z9,t0,z);
+  /* 11 */ fmul(z11,z9,z2);
+  /* 22 */ fsquare(t0,z11);
+  /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
+
+  /* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
+  /* 2^7 - 2^2 */ fsquare(t1,t0);
+  /* 2^8 - 2^3 */ fsquare(t0,t1);
+  /* 2^9 - 2^4 */ fsquare(t1,t0);
+  /* 2^10 - 2^5 */ fsquare(t0,t1);
+  /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
+
+  /* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
+  /* 2^12 - 2^2 */ fsquare(t1,t0);
+  /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+  /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
+
+  /* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
+  /* 2^22 - 2^2 */ fsquare(t1,t0);
+  /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+  /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
+
+  /* 2^41 - 2^1 */ fsquare(t1,t0);
+  /* 2^42 - 2^2 */ fsquare(t0,t1);
+  /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+  /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
+
+  /* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
+  /* 2^52 - 2^2 */ fsquare(t1,t0);
+  /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+  /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
+
+  /* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
+  /* 2^102 - 2^2 */ fsquare(t0,t1);
+  /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+  /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
+
+  /* 2^201 - 2^1 */ fsquare(t0,t1);
+  /* 2^202 - 2^2 */ fsquare(t1,t0);
+  /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+  /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
+
+  /* 2^251 - 2^1 */ fsquare(t1,t0);
+  /* 2^252 - 2^2 */ fsquare(t0,t1);
+  /* 2^253 - 2^3 */ fsquare(t1,t0);
+  /* 2^254 - 2^4 */ fsquare(t0,t1);
+  /* 2^255 - 2^5 */ fsquare(t1,t0);
+  /* 2^255 - 21 */ fmul(out,t1,z11);
+}
+
+void
+curve25519(uchar mypublic[32], uchar secret[32], uchar basepoint[32]) {
+  felem bp[10], x[10], z[10], zmone[10];
+  fexpand(bp, basepoint);
+  cmult(x, z, secret, bp);
+  crecip(zmone, z);
+  fmul(z, x, zmone);
+  fcontract(mypublic, z);
+}
--- /dev/null
+++ b/libsec/curve25519_dh.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+static uchar nine[32] = {9};
+
+void
+curve25519_dh_new(uchar x[32], uchar y[32])
+{
+	uchar b;
+
+	/* new public/private key pair */
+	genrandom(x, 32);
+	b = x[31];
+	x[0] &= ~7;			/* clear bit 0,1,2 */
+	x[31] = 0x40 | (b & 0x7f);	/* set bit 254, clear bit 255 */
+	curve25519(y, x, nine);
+
+	/* bit 255 is always 0, so make it random */
+	y[31] |= b & 0x80;
+}
+
+void
+curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32])
+{
+	/* remove the random bit */
+	y[31] &= 0x7f;
+
+	/* calculate dhx key */
+	curve25519(z, x, y);
+
+	memset(x, 0, 32);
+	memset(y, 0, 32);
+}
--- a/libsec/decodepem.c
+++ b/libsec/decodepem.c
@@ -6,12 +6,14 @@
 #define STRLEN(s)	(sizeof(s)-1)
 
 uchar*
-decodepem(char *s, char *type, int *len)
+decodePEM(char *s, char *type, int *len, char **new_s)
 {
 	uchar *d;
 	char *t, *e, *tt;
 	int n;
 
+	*len = 0;
+
 	/*
 	 * find the correct section of the file, stripping garbage at the beginning and end.
 	 * the data is delimited by -----BEGIN <type>-----\n and -----END <type>-----\n
@@ -42,6 +44,8 @@
 		return nil;
 	}
 
+	if(new_s)
+		*new_s = tt+1;
 	n = ((tt - t) * 6 + 7) / 8;
 	d = malloc(n);
 	if(d == nil){
@@ -56,4 +60,30 @@
 	}
 	*len = n;
 	return d;
+}
+
+PEMChain*
+decodepemchain(char *s, char *type)
+{
+	PEMChain *first = nil, *last = nil, *chp;
+	uchar *d;
+	char *e;
+	int n;
+
+	e = strchr(s, '\0');
+	while (s < e) {
+		d = decodePEM(s, type, &n, &s);
+		if(d == nil)
+			break;
+		chp = malloc(sizeof(PEMChain));
+		chp->next = nil;
+		chp->pem = d;
+		chp->pemlen = n;
+		if (first == nil)
+			first = chp;
+		else
+			last->next = chp;
+		last = chp;
+	}
+	return first;
 }
--- /dev/null
+++ b/libsec/dh.c
@@ -1,0 +1,74 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+mpint*
+dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g)
+{
+	mpint *pm1;
+	int n;
+
+	memset(dh, 0, sizeof(*dh));
+	if(mpcmp(g, mpone) <= 0)
+		return nil;
+
+	n = mpsignif(p);
+	pm1 = mpnew(n);
+	mpsub(p, mpone, pm1);
+	dh->p = mpcopy(p);
+	dh->g = mpcopy(g);
+	dh->q = mpcopy(q != nil ? q : pm1);
+	dh->x = mpnew(mpsignif(dh->q));
+	dh->y = mpnew(n);
+	for(;;){
+		mpnrand(dh->q, genrandom, dh->x);
+		mpexp(dh->g, dh->x, dh->p, dh->y);
+		if(mpcmp(dh->y, mpone) > 0 && mpcmp(dh->y, pm1) < 0)
+			break;
+	}
+	mpfree(pm1);
+
+	return dh->y;
+}
+
+mpint*
+dh_finish(DHstate *dh, mpint *y)
+{
+	mpint *k = nil;
+
+	if(y == nil || dh->x == nil || dh->p == nil || dh->q == nil)
+		goto Out;
+
+	/* y > 1 */
+	if(mpcmp(y, mpone) <= 0)
+		goto Out;
+
+	k = mpnew(mpsignif(dh->p));
+
+	/* y < p-1 */
+	mpsub(dh->p, mpone, k);
+	if(mpcmp(y, k) >= 0){
+Bad:
+		mpfree(k);
+		k = nil;
+		goto Out;
+	}
+
+	/* y**q % p == 1 if q < p-1 */
+	if(mpcmp(dh->q, k) < 0){
+		mpexp(y, dh->q, dh->p, k);
+		if(mpcmp(k, mpone) != 0)
+			goto Bad;
+	}
+
+	mpexp(y, dh->x, dh->p, k);
+
+Out:
+	mpfree(dh->p);
+	mpfree(dh->q);
+	mpfree(dh->g);
+	mpfree(dh->x);
+	mpfree(dh->y);
+	memset(dh, 0, sizeof(*dh));
+	return k;
+}
--- a/libsec/dsaalloc.c
+++ b/libsec/dsaalloc.c
@@ -22,6 +22,7 @@
 	mpfree(dsa->q);
 	mpfree(dsa->alpha);
 	mpfree(dsa->key);
+	free(dsa);
 }
 
 
@@ -46,6 +47,7 @@
 	mpfree(dsa->pub.alpha);
 	mpfree(dsa->pub.key);
 	mpfree(dsa->secret);
+	free(dsa);
 }
 
 DSAsig*
@@ -66,4 +68,5 @@
 		return;
 	mpfree(dsa->r);
 	mpfree(dsa->s);
+	free(dsa);
 }
--- a/libsec/dsagen.c
+++ b/libsec/dsagen.c
@@ -32,9 +32,6 @@
 	// find a generator alpha of the multiplicative
 	// group Z*p, i.e., of order n = p-1.  We use the
 	// fact that q divides p-1 to reduce the exponent.
-	//
-	// This isn't very efficient.  If anyone has a better
-	// idea, mail [email protected]
 	exp = mpnew(0);
 	g = mpnew(0);
 	r = mpnew(0);
--- a/libsec/dsaprimes.c
+++ b/libsec/dsaprimes.c
@@ -12,7 +12,7 @@
 static void
 Hrand(uchar *s)
 {
-	ulong *u = (ulong*)s;
+	u32int *u = (u32int*)s;
 	*u++ = fastrand();
 	*u++ = fastrand();
 	*u++ = fastrand();
--- a/libsec/dsasign.c
+++ b/libsec/dsasign.c
@@ -21,16 +21,16 @@
 	// find a k that has an inverse mod q
 	while(1){
 		mprand(qlen, genrandom, k);
-		if((mpcmp(mpone, k) > 0) || (mpcmp(k, qm1) >= 0))
+		if((mpcmp(mpone, k) > 0) || (mpcmp(k, pub->q) >= 0))
 			continue;
 		mpextendedgcd(k, q, r, kinv, s);
 		if(mpcmp(r, mpone) != 0)
-			continue;
+			sysfatal("dsasign: pub->q not prime");
 		break;
 	}
 
   	// make kinv positive
-	mpmod(kinv, qm1, kinv);
+	mpmod(kinv, pub->q, kinv);
 
 	// r = ((alpha**k) mod p) mod q
 	mpexp(alpha, k, p, r);
--- a/libsec/dsaverify.c
+++ b/libsec/dsaverify.c
@@ -8,9 +8,9 @@
 	int rv = -1;
 	mpint *u1, *u2, *v, *sinv;
 
-	if(sig->r->sign < 0 || mpcmp(sig->r, pub->q) >= 0)
+	if(mpcmp(sig->r, mpone) < 0 || mpcmp(sig->r, pub->q) >= 0)
 		return rv;
-	if(sig->s->sign < 0 || mpcmp(sig->s, pub->q) >= 0)
+	if(mpcmp(sig->s, mpone) < 0 || mpcmp(sig->s, pub->q) >= 0)
 		return rv;
 	u1 = mpnew(0);
 	u2 = mpnew(0);
--- /dev/null
+++ b/libsec/ecc.c
@@ -1,0 +1,620 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+#include <ctype.h>
+
+void
+ecassign(ECdomain *dom, ECpoint *a, ECpoint *b)
+{
+	USED(dom);
+	b->inf = a->inf;
+	mpassign(a->x, b->x);
+	mpassign(a->y, b->y);
+}
+
+void
+ecadd(ECdomain *dom, ECpoint *a, ECpoint *b, ECpoint *s)
+{
+	mpint *l, *k, *sx, *sy;
+
+	if(a->inf && b->inf){
+		s->inf = 1;
+		return;
+	}
+	if(a->inf){
+		ecassign(dom, b, s);
+		return;
+	}
+	if(b->inf){
+		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);
+		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);
+}
+
+void
+ecmul(ECdomain *dom, ECpoint *a, mpint *k, ECpoint *s)
+{
+	ECpoint ns, na;
+	mpint *l;
+
+	if(a->inf || mpcmp(k, mpzero) == 0){
+		s->inf = 1;
+		return;
+	}
+	ns.inf = 1;
+	ns.x = mpnew(0);
+	ns.y = mpnew(0);
+	na.x = mpnew(0);
+	na.y = mpnew(0);
+	ecassign(dom, a, &na);
+	l = mpcopy(k);
+	l->sign = 1;
+	while(mpcmp(l, mpzero) != 0){
+		if(l->p[0] & 1)
+			ecadd(dom, &na, &ns, &ns);
+		ecadd(dom, &na, &na, &na);
+		mpright(l, 1, l);
+	}
+	if(k->sign < 0){
+		ns.y->sign = -1;
+		mpmod(ns.y, dom->p, ns.y);
+	}
+	ecassign(dom, &ns, s);
+	mpfree(ns.x);
+	mpfree(ns.y);
+	mpfree(na.x);
+	mpfree(na.y);
+	mpfree(l);
+}
+
+int
+ecverify(ECdomain *dom, ECpoint *a)
+{
+	mpint *p, *q;
+	int r;
+
+	if(a->inf)
+		return 1;
+	
+	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);
+	r = mpcmp(p, q);
+	mpfree(p);
+	mpfree(q);
+	return r == 0;
+}
+
+int
+ecpubverify(ECdomain *dom, ECpub *a)
+{
+	ECpoint p;
+	int r;
+
+	if(a->inf)
+		return 0;
+	if(!ecverify(dom, a))
+		return 0;
+	p.x = mpnew(0);
+	p.y = mpnew(0);
+	ecmul(dom, a, dom->n, &p);
+	r = p.inf;
+	mpfree(p.x);
+	mpfree(p.y);
+	return r;
+}
+
+static void
+fixnibble(uchar *a)
+{
+	if(*a >= 'a')
+		*a -= 'a'-10;
+	else if(*a >= 'A')
+		*a -= 'A'-10;
+	else
+		*a -= '0';
+}
+
+static int
+octet(char **s)
+{
+	uchar c, d;
+	
+	c = *(*s)++;
+	if(!isxdigit(c))
+		return -1;
+	d = *(*s)++;
+	if(!isxdigit(d))
+		return -1;
+	fixnibble(&c);
+	fixnibble(&d);
+	return (c << 4) | d;
+}
+
+static mpint*
+halfpt(ECdomain *dom, char *s, char **rptr, mpint *out)
+{
+	char *buf, *r;
+	int n;
+	mpint *ret;
+	
+	n = ((mpsignif(dom->p)+7)/8)*2;
+	if(strlen(s) < n)
+		return 0;
+	buf = malloc(n+1);
+	buf[n] = 0;
+	memcpy(buf, s, n);
+	ret = strtomp(buf, &r, 16, out);
+	*rptr = s + (r - buf);
+	free(buf);
+	return ret;
+}
+
+static int
+mpleg(mpint *a, mpint *b)
+{
+	int r, k;
+	mpint *m, *n, *t;
+	
+	r = 1;
+	m = mpcopy(a);
+	n = mpcopy(b);
+	for(;;){
+		if(mpcmp(m, n) > 0)
+			mpmod(m, n, m);
+		if(mpcmp(m, mpzero) == 0){
+			r = 0;
+			break;
+		}
+		if(mpcmp(m, mpone) == 0)
+			break;
+		k = mplowbits0(m);
+		if(k > 0){
+			if(k & 1)
+				switch(n->p[0] & 15){
+				case 3: case 5: case 11: case 13:
+					r = -r;
+				}
+			mpright(m, k, m);
+		}
+		if((n->p[0] & 3) == 3 && (m->p[0] & 3) == 3)
+			r = -r;
+		t = m;
+		m = n;
+		n = t;
+	}
+	mpfree(m);
+	mpfree(n);
+	return r;
+}
+
+static int
+mpsqrt(mpint *n, mpint *p, mpint *r)
+{
+	mpint *a, *t, *s, *xp, *xq, *yp, *yq, *zp, *zq, *N;
+
+	if(mpleg(n, p) == -1)
+		return 0;
+	a = mpnew(0);
+	t = mpnew(0);
+	s = mpnew(0);
+	N = mpnew(0);
+	xp = mpnew(0);
+	xq = mpnew(0);
+	yp = mpnew(0);
+	yq = mpnew(0);
+	zp = mpnew(0);
+	zq = mpnew(0);
+	for(;;){
+		for(;;){
+			mprand(mpsignif(p), genrandom, a);
+			if(mpcmp(a, mpzero) > 0 && mpcmp(a, p) < 0)
+				break;
+		}
+		mpmul(a, a, t);
+		mpsub(t, n, t);
+		mpmod(t, p, t);
+		if(mpleg(t, p) == -1)
+			break;
+	}
+	mpadd(p, mpone, N);
+	mpright(N, 1, N);
+	mpmul(a, a, t);
+	mpsub(t, n, t);
+	mpassign(a, xp);
+	uitomp(1, xq);
+	uitomp(1, yp);
+	uitomp(0, yq);
+	while(mpcmp(N, mpzero) != 0){
+		if(N->p[0] & 1){
+			mpmul(xp, yp, zp);
+			mpmul(xq, yq, zq);
+			mpmul(zq, t, zq);
+			mpadd(zp, zq, zp);
+			mpmod(zp, p, zp);
+			mpmul(xp, yq, zq);
+			mpmul(xq, yp, s);
+			mpadd(zq, s, zq);
+			mpmod(zq, p, yq);
+			mpassign(zp, yp);
+		}
+		mpmul(xp, xp, zp);
+		mpmul(xq, xq, zq);
+		mpmul(zq, t, zq);
+		mpadd(zp, zq, zp);
+		mpmod(zp, p, zp);
+		mpmul(xp, xq, zq);
+		mpadd(zq, zq, zq);
+		mpmod(zq, p, xq);
+		mpassign(zp, xp);
+		mpright(N, 1, N);
+	}
+	if(mpcmp(yq, mpzero) != 0)
+		abort();
+	mpassign(yp, r);
+	mpfree(a);
+	mpfree(t);
+	mpfree(s);
+	mpfree(N);
+	mpfree(xp);
+	mpfree(xq);
+	mpfree(yp);
+	mpfree(yq);
+	mpfree(zp);
+	mpfree(zq);
+	return 1;
+}
+
+ECpoint*
+strtoec(ECdomain *dom, char *s, char **rptr, ECpoint *ret)
+{
+	int allocd, o;
+	mpint *r;
+
+	allocd = 0;
+	if(ret == nil){
+		allocd = 1;
+		ret = mallocz(sizeof(*ret), 1);
+		if(ret == nil)
+			return nil;
+		ret->x = mpnew(0);
+		ret->y = mpnew(0);
+	}
+	o = 0;
+	switch(octet(&s)){
+	case 0:
+		ret->inf = 1;
+		return ret;
+	case 3:
+		o = 1;
+	case 2:
+		if(halfpt(dom, s, &s, ret->x) == nil)
+			goto err;
+		r = mpnew(0);
+		mpmul(ret->x, ret->x, r);
+		mpadd(r, dom->a, r);
+		mpmul(r, ret->x, r);
+		mpadd(r, dom->b, r);
+		if(!mpsqrt(r, dom->p, r)){
+			mpfree(r);
+			goto err;
+		}
+		if((r->p[0] & 1) != o)
+			mpsub(dom->p, r, r);
+		mpassign(r, ret->y);
+		mpfree(r);
+		if(!ecverify(dom, ret))
+			goto err;
+		return ret;
+	case 4:
+		if(halfpt(dom, s, &s, ret->x) == nil)
+			goto err;
+		if(halfpt(dom, s, &s, ret->y) == nil)
+			goto err;
+		if(!ecverify(dom, ret))
+			goto err;
+		return ret;
+	}
+err:
+	if(rptr)
+		*rptr = s;
+	if(allocd){
+		mpfree(ret->x);
+		mpfree(ret->y);
+		free(ret);
+	}
+	return nil;
+}
+
+ECpriv*
+ecgen(ECdomain *dom, ECpriv *p)
+{
+	if(p == nil){
+		p = mallocz(sizeof(*p), 1);
+		if(p == nil)
+			return nil;
+		p->x = mpnew(0);
+		p->y = mpnew(0);
+		p->d = mpnew(0);
+	}
+	for(;;){
+		mprand(mpsignif(dom->n), genrandom, p->d);
+		if(mpcmp(p->d, mpzero) > 0 && mpcmp(p->d, dom->n) < 0)
+			break;
+	}
+	ecmul(dom, &dom->G, p->d, (ECpoint*)p);
+	return p;
+}
+
+void
+ecdsasign(ECdomain *dom, ECpriv *priv, uchar *dig, int len, mpint *r, mpint *s)
+{
+	ECpriv tmp;
+	mpint *E, *t;
+
+	tmp.x = mpnew(0);
+	tmp.y = mpnew(0);
+	tmp.d = mpnew(0);
+	E = betomp(dig, len, nil);
+	t = mpnew(0);
+	if(mpsignif(dom->n) < 8*len)
+		mpright(E, 8*len - mpsignif(dom->n), E);
+	for(;;){
+		ecgen(dom, &tmp);
+		mpmod(tmp.x, dom->n, r);
+		if(mpcmp(r, mpzero) == 0)
+			continue;
+		mpmul(r, priv->d, s);
+		mpadd(E, s, s);
+		mpinvert(tmp.d, dom->n, t);
+		mpmul(s, t, s);
+		mpmod(s, dom->n, s);
+		if(mpcmp(s, mpzero) != 0)
+			break;
+	}
+	mpfree(t);
+	mpfree(E);
+	mpfree(tmp.x);
+	mpfree(tmp.y);
+	mpfree(tmp.d);
+}
+
+int
+ecdsaverify(ECdomain *dom, ECpub *pub, uchar *dig, int len, mpint *r, mpint *s)
+{
+	mpint *E, *t, *u1, *u2;
+	ECpoint R, S;
+	int ret;
+
+	if(mpcmp(r, mpone) < 0 || mpcmp(s, mpone) < 0 || mpcmp(r, dom->n) >= 0 || mpcmp(r, dom->n) >= 0)
+		return 0;
+	E = betomp(dig, len, nil);
+	if(mpsignif(dom->n) < 8*len)
+		mpright(E, 8*len - mpsignif(dom->n), E);
+	t = mpnew(0);
+	u1 = mpnew(0);
+	u2 = mpnew(0);
+	R.x = mpnew(0);
+	R.y = mpnew(0);
+	S.x = mpnew(0);
+	S.y = 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);
+	ecmul(dom, &dom->G, u1, &R);
+	ecmul(dom, pub, u2, &S);
+	ecadd(dom, &R, &S, &R);
+	ret = 0;
+	if(!R.inf){
+		mpmod(R.x, dom->n, t);
+		ret = mpcmp(r, t) == 0;
+	}
+	mpfree(E);
+	mpfree(t);
+	mpfree(u1);
+	mpfree(u2);
+	mpfree(R.x);
+	mpfree(R.y);
+	mpfree(S.x);
+	mpfree(S.y);
+	return ret;
+}
+
+static char *code = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+
+void
+base58enc(uchar *src, char *dst, int len)
+{
+	mpint *n, *r, *b;
+	char *sdst, t;
+	
+	sdst = dst;
+	n = betomp(src, len, nil);
+	b = uitomp(58, nil);
+	r = mpnew(0);
+	while(mpcmp(n, mpzero) != 0){
+		mpdiv(n, b, n, r);
+		*dst++ = code[mptoui(r)];
+	}
+	for(; *src == 0; src++)
+		*dst++ = code[0];
+	dst--;
+	while(dst > sdst){
+		t = *sdst;
+		*sdst++ = *dst;
+		*dst-- = t;
+	}
+}
+
+int
+base58dec(char *src, uchar *dst, int len)
+{
+	mpint *n, *b, *r;
+	char *t;
+	
+	n = mpnew(0);
+	r = mpnew(0);
+	b = uitomp(58, nil);
+	for(; *src; src++){
+		t = strchr(code, *src);
+		if(t == nil){
+			mpfree(n);
+			mpfree(r);
+			mpfree(b);
+			werrstr("invalid base58 char");
+			return -1;
+		}
+		uitomp(t - code, r);
+		mpmul(n, b, n);
+		mpadd(n, r, n);
+	}
+	mptober(n, dst, len);
+	mpfree(n);
+	mpfree(r);
+	mpfree(b);
+	return 0;
+}
+
+void
+ecdominit(ECdomain *dom, void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h))
+{
+	memset(dom, 0, sizeof(*dom));
+	dom->p = mpnew(0);
+	dom->a = mpnew(0);
+	dom->b = mpnew(0);
+	dom->G.x = mpnew(0);
+	dom->G.y = mpnew(0);
+	dom->n = mpnew(0);
+	dom->h = mpnew(0);
+	if(init){
+		(*init)(dom->p, dom->a, dom->b, dom->G.x, dom->G.y, dom->n, dom->h);
+		dom->p = mpfield(dom->p);
+	}
+}
+
+void
+ecdomfree(ECdomain *dom)
+{
+	mpfree(dom->p);
+	mpfree(dom->a);
+	mpfree(dom->b);
+	mpfree(dom->G.x);
+	mpfree(dom->G.y);
+	mpfree(dom->n);
+	mpfree(dom->h);
+	memset(dom, 0, sizeof(*dom));
+}
+
+int
+ecencodepub(ECdomain *dom, ECpub *pub, uchar *data, int len)
+{
+	int n;
+
+	n = (mpsignif(dom->p)+7)/8;
+	if(len < 1 + 2*n)
+		return 0;
+	len = 1 + 2*n;
+	data[0] = 0x04;
+	mptober(pub->x, data+1, n);
+	mptober(pub->y, data+1+n, n);
+	return len;
+}
+
+ECpub*
+ecdecodepub(ECdomain *dom, uchar *data, int len)
+{
+	ECpub *pub;
+	int n;
+
+	n = (mpsignif(dom->p)+7)/8;
+	if(len != 1 + 2*n || data[0] != 0x04)
+		return nil;
+	pub = mallocz(sizeof(*pub), 1);
+	if(pub == nil)
+		return nil;
+	pub->x = betomp(data+1, n, nil);
+	pub->y = betomp(data+1+n, n, nil);
+	if(!ecpubverify(dom, pub)){
+		ecpubfree(pub);
+		pub = nil;
+	}
+	return pub;
+}
+
+void
+ecpubfree(ECpub *p)
+{
+	if(p == nil)
+		return;
+	mpfree(p->x);
+	mpfree(p->y);
+	free(p);
+}
--- a/libsec/egalloc.c
+++ b/libsec/egalloc.c
@@ -21,6 +21,7 @@
 	mpfree(eg->p);
 	mpfree(eg->alpha);
 	mpfree(eg->key);
+	free(eg);
 }
 
 
@@ -44,6 +45,7 @@
 	mpfree(eg->pub.alpha);
 	mpfree(eg->pub.key);
 	mpfree(eg->secret);
+	free(eg);
 }
 
 EGsig*
@@ -64,4 +66,5 @@
 		return;
 	mpfree(eg->r);
 	mpfree(eg->s);
+	free(eg);
 }
--- a/libsec/genprime.c
+++ b/libsec/genprime.c
@@ -17,6 +17,7 @@
 	p->p[p->top-1] &= (x-1);
 	p->p[p->top-1] |= x;
 	p->p[0] |= 1;
+	mpnorm(p);
 
 	// keep icrementing till it looks prime
 	for(;;){
--- /dev/null
+++ b/libsec/hkdf.c
@@ -1,0 +1,39 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+/* rfc5869 */
+void
+hkdf_x(salt, nsalt, info, ninfo, key, nkey, d, dlen, x, xlen)
+	uchar *salt, *info, *key, *d;
+	ulong nsalt, ninfo, nkey, dlen;
+	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+	int xlen;
+{
+	uchar prk[256], tmp[256], cnt;
+	DigestState *ds;
+
+	assert(xlen <= sizeof(tmp));
+
+	memset(tmp, 0, xlen);
+	if(nsalt == 0){
+		salt = tmp;
+		nsalt = xlen;
+	}
+	/* note that salt and key are swapped in this case */
+	(*x)(key, nkey, salt, nsalt, prk, nil);
+	ds = nil;
+	for(cnt=1;; cnt++) {
+		if(ninfo > 0)
+			ds = (*x)(info, ninfo, prk, xlen, nil, ds);
+		(*x)(&cnt, 1, prk, xlen, tmp, ds);
+		if(dlen <= xlen){
+			memmove(d, tmp, dlen);
+			break;
+		}
+		memmove(d, tmp, xlen);
+		dlen -= xlen;
+		d += xlen;
+		ds = (*x)(tmp, xlen, prk, xlen, nil, nil);
+	}
+}
--- a/libsec/hmac.c
+++ b/libsec/hmac.c
@@ -2,27 +2,30 @@
 #include <libsec.h>
 
 /* rfc2104 */
-static DigestState*
+DigestState*
 hmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
 	DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen)
 {
 	int i;
-	uchar pad[65], innerdigest[256];
+	uchar pad[Hmacblksz+1], innerdigest[256];
 
 	if(xlen > sizeof(innerdigest))
 		return nil;
+	if(klen > Hmacblksz){
+		if(xlen > Hmacblksz)
+			return nil;
+		(*x)(key, klen, innerdigest, nil);
+		key = innerdigest;
+		klen = xlen;
+	}
 
-	if(klen>64)
-		return nil;
-
 	/* first time through */
-	if(s == nil){
-		for(i=0; i<64; i++)
-			pad[i] = 0x36;
-		pad[64] = 0;
-		for(i=0; i<klen; i++)
+	if(s == nil || s->seeded == 0){
+		memset(pad, 0x36, Hmacblksz);
+		pad[Hmacblksz] = 0;
+		for(i = 0; i < klen; i++)
 			pad[i] ^= key[i];
-		s = (*x)(pad, 64, nil, nil);
+		s = (*x)(pad, Hmacblksz, nil, s);
 		if(s == nil)
 			return nil;
 	}
@@ -32,25 +35,12 @@
 		return s;
 
 	/* last time through */
-	for(i=0; i<64; i++)
-		pad[i] = 0x5c;
-	pad[64] = 0;
-	for(i=0; i<klen; i++)
+	memset(pad, 0x5c, Hmacblksz);
+	pad[Hmacblksz] = 0;
+	for(i = 0; i < klen; i++)
 		pad[i] ^= key[i];
 	(*x)(nil, 0, innerdigest, s);
-	s = (*x)(pad, 64, nil, nil);
+	s = (*x)(pad, Hmacblksz, nil, nil);
 	(*x)(innerdigest, xlen, digest, s);
 	return nil;
-}
-
-DigestState*
-hmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
-{
-	return hmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen);
-}
-
-DigestState*
-hmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
-{
-	return hmac_x(p, len, key, klen, digest, s, md5, MD5dlen);
 }
--- a/libsec/hmactest.c
+++ /dev/null
@@ -1,19 +1,0 @@
-#include "os.h"
-#include <mp.h>
-#include <libsec.h>
-
-uchar key[] = "Jefe";
-uchar data[] = "what do ya want for nothing?";
-
-void
-main(void)
-{
-	int i;
-	uchar hash[MD5dlen];
-
-	hmac_md5(data, strlen((char*)data), key, 4, hash, nil);
-	for(i=0; i<MD5dlen; i++)
-		print("%2.2x", hash[i]);
-	print("\n");
-	print("750c783e6ab0b503eaa86e310a5db738\n");
-}
--- a/libsec/md4test.c
+++ /dev/null
@@ -1,31 +1,0 @@
-#include "os.h"
-#include <mp.h>
-#include <libsec.h>
-
-char *tests[] = {
-	"",
-	"a",
-	"abc",
-	"message digest",
-	"abcdefghijklmnopqrstuvwxyz",
-	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-	"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
-	0
-};
-
-void
-main(void)
-{
-	char **pp;
-	uchar *p;
-	int i;
-	uchar digest[MD5dlen];
-
-	for(pp = tests; *pp; pp++){
-		p = (uchar*)*pp;
-		md4(p, strlen(*pp), digest, 0);
-		for(i = 0; i < MD5dlen; i++)
-			print("%2.2ux", digest[i]);
-		print("\n");
-	}
-}
--- a/libsec/md5.c
+++ b/libsec/md5.c
@@ -145,3 +145,10 @@
 		*output++ = x >> 24;
 	}
 }
+
+DigestState*
+hmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+	DigestState *s)
+{
+	return hmac_x(p, len, key, klen, digest, s, md5, MD5dlen);
+}
--- a/libsec/md5pickle.c
+++ b/libsec/md5pickle.c
@@ -7,11 +7,12 @@
 	char *p;
 	int m, n;
 
-	m = 4*9+4*((s->blen+3)/3);
+	m = 17+4*9+4*((s->blen+3)/3 + 1);
 	p = malloc(m);
 	if(p == nil)
 		return p;
-	n = sprint(p, "%8.8ux %8.8ux %8.8ux %8.8ux ",
+	n = sprint(p, "%16.16llux %8.8ux %8.8ux %8.8ux %8.8ux ",
+		s->len,
 		s->state[0], s->state[1], s->state[2],
 		s->state[3]);
 	enc64(p+n, m-n, s->buf, s->blen);
@@ -26,6 +27,7 @@
 	s = malloc(sizeof(*s));
 	if(s == nil)
 		return nil;
+	s->len = strtoull(p, &p, 16);
 	s->state[0] = strtoul(p, &p, 16);
 	s->state[1] = strtoul(p, &p, 16);
 	s->state[2] = strtoul(p, &p, 16);
--- a/libsec/nfastrand.c
+++ b/libsec/nfastrand.c
@@ -14,7 +14,7 @@
 	 * so we want a random number < m.
 	 */
 	if(n > Maxrand)
-		abort();
+		sysfatal("nfastrand: n too large");
 
 	m = Maxrand - Maxrand % n;
 	while((r = fastrand()) >= m)
--- /dev/null
+++ b/libsec/pbkdf2.c
@@ -1,0 +1,35 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+/* rfc2898 */
+void
+pbkdf2_x(p, plen, s, slen, rounds, d, dlen, x, xlen)
+	uchar *p, *s, *d;
+	ulong plen, slen, dlen, rounds;
+	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+	int xlen;
+{
+	uchar block[256], tmp[256];
+	ulong i, j, k, n;
+	DigestState *ds;
+
+	assert(xlen <= sizeof(tmp));
+
+	for(i = 1; dlen > 0; i++, d += n, dlen -= n){
+		tmp[3] = i;
+		tmp[2] = i >> 8;
+		tmp[1] = i >> 16;
+		tmp[0] = i >> 24;
+		ds = (*x)(s, slen, p, plen, nil, nil);
+		(*x)(tmp, 4, p, plen, block, ds);
+		memmove(tmp, block, xlen);
+		for(j = 1; j < rounds; j++){
+			(*x)(tmp, xlen, p, plen, tmp, nil);
+			for(k=0; k<xlen; k++)
+				block[k] ^= tmp[k];
+		}
+		n = dlen > xlen ? xlen : dlen;
+		memmove(d, block, n); 
+	}
+}
--- /dev/null
+++ b/libsec/poly1305.c
@@ -1,0 +1,196 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+/*
+	poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition
+
+	derived from http://github.com/floodberry/poly1305-donna
+*/
+
+#define U8TO32(p)	((u32int)(p)[0] | (u32int)(p)[1]<<8 | (u32int)(p)[2]<<16 | (u32int)(p)[3]<<24)
+#define U32TO8(p, v)	(p)[0]=(v), (p)[1]=(v)>>8, (p)[2]=(v)>>16, (p)[3]=(v)>>24
+
+/* (r,s) = (key[0:15],key[16:31]), the one time key */
+DigestState*
+poly1305(uchar *m, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
+{
+	u32int r0,r1,r2,r3,r4, s1,s2,s3,s4, h0,h1,h2,h3,h4, g0,g1,g2,g3,g4;
+	u64int d0,d1,d2,d3,d4, f;
+	u32int hibit, mask, c;
+
+	if(s == nil){
+		s = malloc(sizeof(*s));
+		if(s == nil)
+			return nil;
+		memset(s, 0, sizeof(*s));
+		s->malloced = 1;
+	}
+
+	if(s->seeded == 0){
+		assert(klen == 32);
+
+		/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+		s->state[0] = (U8TO32(&key[ 0])     ) & 0x3ffffff;
+		s->state[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
+		s->state[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
+		s->state[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
+		s->state[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
+
+		/* h = 0 */
+		s->state[5] = 0;
+		s->state[6] = 0;
+		s->state[7] = 0;
+		s->state[8] = 0;
+		s->state[9] = 0;
+
+		/* save pad for later */
+		s->state[10] = U8TO32(&key[16]);
+		s->state[11] = U8TO32(&key[20]);
+		s->state[12] = U8TO32(&key[24]);
+		s->state[13] = U8TO32(&key[28]);
+
+		s->seeded = 1;
+	}
+
+	if(s->blen){
+		c = 16 - s->blen;
+		if(c > len)
+			c = len;
+		memmove(s->buf + s->blen, m, c);
+		len -= c, m += c;
+		s->blen += c;
+		if(s->blen == 16){
+			s->blen = 0;
+			poly1305(s->buf, 16, key, klen, nil, s);
+		} else if(len == 0){
+			m = s->buf;
+			len = s->blen;
+			s->blen = 0;
+		}
+	}
+
+	r0 = s->state[0];
+	r1 = s->state[1];
+	r2 = s->state[2];
+	r3 = s->state[3];
+	r4 = s->state[4];
+
+	h0 = s->state[5];
+	h1 = s->state[6];
+	h2 = s->state[7];
+	h3 = s->state[8];
+	h4 = s->state[9];
+
+	s1 = r1 * 5;
+	s2 = r2 * 5;
+	s3 = r3 * 5;
+	s4 = r4 * 5;
+
+	hibit = 1<<24;	/* 1<<128 */
+
+	while(len >= 16){
+Block:
+		/* h += m[i] */
+		h0 += (U8TO32(&m[0])     ) & 0x3ffffff;
+		h1 += (U8TO32(&m[3]) >> 2) & 0x3ffffff;
+		h2 += (U8TO32(&m[6]) >> 4) & 0x3ffffff;
+		h3 += (U8TO32(&m[9]) >> 6) & 0x3ffffff;
+		h4 += (U8TO32(&m[12])>> 8) | hibit;
+
+		/* h *= r */
+		d0 = ((u64int)h0 * r0) + ((u64int)h1 * s4) + ((u64int)h2 * s3) + ((u64int)h3 * s2) + ((u64int)h4 * s1);
+		d1 = ((u64int)h0 * r1) + ((u64int)h1 * r0) + ((u64int)h2 * s4) + ((u64int)h3 * s3) + ((u64int)h4 * s2);
+		d2 = ((u64int)h0 * r2) + ((u64int)h1 * r1) + ((u64int)h2 * r0) + ((u64int)h3 * s4) + ((u64int)h4 * s3);
+		d3 = ((u64int)h0 * r3) + ((u64int)h1 * r2) + ((u64int)h2 * r1) + ((u64int)h3 * r0) + ((u64int)h4 * s4);
+		d4 = ((u64int)h0 * r4) + ((u64int)h1 * r3) + ((u64int)h2 * r2) + ((u64int)h3 * r1) + ((u64int)h4 * r0);
+
+		/* (partial) h %= p */
+		              c = (u32int)(d0 >> 26); h0 = (u32int)d0 & 0x3ffffff;
+		d1 += c;      c = (u32int)(d1 >> 26); h1 = (u32int)d1 & 0x3ffffff;
+		d2 += c;      c = (u32int)(d2 >> 26); h2 = (u32int)d2 & 0x3ffffff;
+		d3 += c;      c = (u32int)(d3 >> 26); h3 = (u32int)d3 & 0x3ffffff;
+		d4 += c;      c = (u32int)(d4 >> 26); h4 = (u32int)d4 & 0x3ffffff;
+		h0 += c * 5;  c = (h0 >> 26); h0 = h0 & 0x3ffffff;
+		h1 += c;
+
+		len -= 16, m += 16;
+	}
+
+	if(len){
+		s->blen = len;
+		memmove(s->buf, m, len);
+	}
+
+	if(digest == nil){
+		s->state[5] = h0;
+		s->state[6] = h1;
+		s->state[7] = h2;
+		s->state[8] = h3;
+		s->state[9] = h4;
+		return s;
+	}
+
+	if(len){
+		m = s->buf;
+		m[len++] = 1;
+		while(len < 16)
+			m[len++] = 0;
+		hibit = 0;
+		goto Block;
+	}
+
+	             c = h1 >> 26; h1 = h1 & 0x3ffffff;
+	h2 +=     c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
+	h3 +=     c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
+	h4 +=     c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
+	h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
+	h1 +=     c;
+
+	/* compute h + -p */
+	g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
+	g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
+	g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
+	g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
+	g4 = h4 + c - (1 << 26);
+
+	/* select h if h < p, or h + -p if h >= p */
+	mask = (g4 >> 31) - 1;
+	g0 &= mask;
+	g1 &= mask;
+	g2 &= mask;
+	g3 &= mask;
+	g4 &= mask;
+	mask = ~mask;
+	h0 = (h0 & mask) | g0;
+	h1 = (h1 & mask) | g1;
+	h2 = (h2 & mask) | g2;
+	h3 = (h3 & mask) | g3;
+	h4 = (h4 & mask) | g4;
+
+	/* h = h % (2^128) */
+	h0 = (h0      ) | (h1 << 26);
+	h1 = (h1 >>  6) | (h2 << 20);
+	h2 = (h2 >> 12) | (h3 << 14);
+	h3 = (h3 >> 18) | (h4 <<  8);
+	
+	/* digest = (h + pad) % (2^128) */
+	f = (u64int)h0 + s->state[10]            ; h0 = (u32int)f;
+	f = (u64int)h1 + s->state[11] + (f >> 32); h1 = (u32int)f;
+	f = (u64int)h2 + s->state[12] + (f >> 32); h2 = (u32int)f;
+	f = (u64int)h3 + s->state[13] + (f >> 32); h3 = (u32int)f;
+
+	U32TO8(&digest[0], h0);
+	U32TO8(&digest[4], h1);
+	U32TO8(&digest[8], h2);
+	U32TO8(&digest[12], h3);
+
+	if(s->malloced){
+		memset(s, 0, sizeof(*s));
+		free(s);
+		return nil;
+	}
+
+	memset(s, 0, sizeof(*s));
+	return nil;
+}
--- a/libsec/probably_prime.c
+++ b/libsec/probably_prime.c
@@ -2,14 +2,16 @@
 #include <mp.h>
 #include <libsec.h>
 
-// Miller-Rabin probabilistic primality testing
-//	Knuth (1981) Seminumerical Algorithms, p.379
-//	Menezes et al () Handbook, p.39
-// 0 if composite; 1 if almost surely prime, Pr(err)<1/4**nrep
+/*
+ * Miller-Rabin probabilistic primality testing
+ *	Knuth (1981) Seminumerical Algorithms, p.379
+ *	Menezes et al () Handbook, p.39
+ * 0 if composite; 1 if almost surely prime, Pr(err)<1/4**nrep
+ */
 int
 probably_prime(mpint *n, int nrep)
 {
-	int j, k, rep, nbits, isprime = 1;
+	int j, k, rep, nbits, isprime;
 	mpint *nm1, *q, *x, *y, *r;
 
 	if(n->sign < 0)
@@ -19,18 +21,18 @@
 		nrep = 18;
 
 	k = mptoi(n);
-	if(k == 2)		// 2 is prime
+	if(k < 2)		/* 1 is not prime */
+		return 0;
+	if(k == 2 || k == 3)	/* 2, 3 is prime */
 		return 1;
-	if(k < 2)		// 1 is not prime
+	if((n->p[0] & 1) == 0)	/* even is not prime */
 		return 0;
-	if((n->p[0] & 1) == 0)	// even is not prime
-		return 0;
 
-	// test against small prime numbers
+	/* test against small prime numbers */
 	if(smallprimetest(n) < 0)
 		return 0;
 
-	// fermat test, 2^n mod n == 2 if p is prime
+	/* fermat test, 2^n mod n == 2 if p is prime */
 	x = uitomp(2, nil);
 	y = mpnew(0);
 	mpexp(x, n, n, y);
@@ -43,38 +45,43 @@
 
 	nbits = mpsignif(n);
 	nm1 = mpnew(nbits);
-	mpsub(n, mpone, nm1);	// nm1 = n - 1 */
+	mpsub(n, mpone, nm1);	/* nm1 = n - 1 */
 	k = mplowbits0(nm1);
 	q = mpnew(0);
-	mpright(nm1, k, q);	// q = (n-1)/2**k
+	mpright(nm1, k, q);	/* q = (n-1)/2**k */
 
 	for(rep = 0; rep < nrep; rep++){
-		
-		// x = random in [2, n-2]
-		r = mprand(nbits, prng, nil);
-		mpmod(r, nm1, x);
-		mpfree(r);
-		if(mpcmp(x, mpone) <= 0)
-			continue;
+		for(;;){
+			/* find x = random in [2, n-2] */
+		 	r = mprand(nbits, prng, nil);
+		 	mpmod(r, nm1, x);
+		 	mpfree(r);
+		 	if(mpcmp(x, mpone) > 0)
+		 		break;
+		}
 
-		// y = x**q mod n
+		/* y = x**q mod n */
 		mpexp(x, q, n, y);
 
 		if(mpcmp(y, mpone) == 0 || mpcmp(y, nm1) == 0)
-			goto done;
+		 	continue;
 
-		for(j = 1; j < k; j++){
-			mpmul(y, y, x);
-			mpmod(x, n, y);	// y = y*y mod n
-			if(mpcmp(y, nm1) == 0)
-				goto done;
-			if(mpcmp(y, mpone) == 0){
-				isprime = 0;
-				goto done;
-			}
+		for(j = 1;; j++){
+		 	if(j >= k) {
+		 		isprime = 0;
+		 		goto done;
+		 	}
+		 	mpmul(y, y, x);
+		 	mpmod(x, n, y);	/* y = y*y mod n */
+		 	if(mpcmp(y, nm1) == 0)
+		 		break;
+		 	if(mpcmp(y, mpone) == 0){
+		 		isprime = 0;
+		 		goto done;
+		 	}
 		}
-		isprime = 0;
 	}
+	isprime = 1;
 done:
 	mpfree(y);
 	mpfree(x);
--- a/libsec/readcert.c
+++ b/libsec/readcert.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <auth.h>
 #include <mp.h>
 #include <libsec.h>
 
@@ -13,8 +14,10 @@
 	fd = open(name, OREAD);
 	if(fd < 0)
 		return nil;
-	if((d = dirfstat(fd)) == nil)
+	if((d = dirfstat(fd)) == nil) {
+		close(fd);
 		return nil;
+	}
 	s = malloc(d->length + 1);
 	if(s == nil || readn(fd, s, d->length) != d->length){
 		free(s);
@@ -36,10 +39,10 @@
 
 	pem = readfile(filename);
 	if(pem == nil){
-		werrstr("can't read %s", filename);
+		werrstr("can't read %s: %r", filename);
 		return nil;
 	}
-	binary = decodepem(pem, "CERTIFICATE", pcertlen);
+	binary = decodePEM(pem, "CERTIFICATE", pcertlen, nil);
 	free(pem);
 	if(binary == nil){
 		werrstr("can't parse %s", filename);
@@ -46,5 +49,18 @@
 		return nil;
 	}
 	return binary;
+}
+
+PEMChain *
+readcertchain(char *filename)
+{
+	char *chfile;
+
+	chfile = readfile(filename);
+	if (chfile == nil) {
+		werrstr("can't read %s: %r", filename);
+		return nil;
+	}
+	return decodepemchain(chfile, "CERTIFICATE");
 }
 
--- /dev/null
+++ b/libsec/ripemd.c
@@ -1,0 +1,383 @@
+#include "os.h"
+
+#include <libsec.h>
+
+#define BYTES_TO_DWORD(strptr)                    \
+            (((u32int) *((strptr)+3) << 24) | \
+             ((u32int) *((strptr)+2) << 16) | \
+             ((u32int) *((strptr)+1) <<  8) | \
+             ((u32int) *(strptr)))
+
+#define ROL(x, n)        (((x) << (n)) | ((x) >> (32-(n))))
+
+/* the five basic functions F(), G() and H() */
+#define F(x, y, z)        ((x) ^ (y) ^ (z)) 
+#define G(x, y, z)        (((x) & (y)) | (~(x) & (z))) 
+#define H(x, y, z)        (((x) | ~(y)) ^ (z))
+#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z))) 
+#define J(x, y, z)        ((x) ^ ((y) | ~(z)))
+  
+/* the ten basic operations FF() through III() */
+#define FF(a, b, c, d, e, x, s)        {\
+      (a) += F((b), (c), (d)) + (x);\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define GG(a, b, c, d, e, x, s)        {\
+      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define HH(a, b, c, d, e, x, s)        {\
+      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define II(a, b, c, d, e, x, s)        {\
+      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define JJ(a, b, c, d, e, x, s)        {\
+      (a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define FFF(a, b, c, d, e, x, s)        {\
+      (a) += F((b), (c), (d)) + (x);\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define GGG(a, b, c, d, e, x, s)        {\
+      (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define HHH(a, b, c, d, e, x, s)        {\
+      (a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define III(a, b, c, d, e, x, s)        {\
+      (a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+#define JJJ(a, b, c, d, e, x, s)        {\
+      (a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\
+      (a) = ROL((a), (s)) + (e);\
+      (c) = ROL((c), 10);\
+   }
+
+
+static void MDinit(u32int *MDbuf)
+{
+   MDbuf[0] = 0x67452301UL;
+   MDbuf[1] = 0xefcdab89UL;
+   MDbuf[2] = 0x98badcfeUL;
+   MDbuf[3] = 0x10325476UL;
+   MDbuf[4] = 0xc3d2e1f0UL;
+
+   return;
+}
+
+static void compress(u32int *MDbuf, u32int *X)
+{
+   u32int aa = MDbuf[0],  bb = MDbuf[1],  cc = MDbuf[2],
+         dd = MDbuf[3],  ee = MDbuf[4];
+   u32int aaa = MDbuf[0], bbb = MDbuf[1], ccc = MDbuf[2],
+         ddd = MDbuf[3], eee = MDbuf[4];
+
+   /* round 1 */
+   FF(aa, bb, cc, dd, ee, X[ 0], 11);
+   FF(ee, aa, bb, cc, dd, X[ 1], 14);
+   FF(dd, ee, aa, bb, cc, X[ 2], 15);
+   FF(cc, dd, ee, aa, bb, X[ 3], 12);
+   FF(bb, cc, dd, ee, aa, X[ 4],  5);
+   FF(aa, bb, cc, dd, ee, X[ 5],  8);
+   FF(ee, aa, bb, cc, dd, X[ 6],  7);
+   FF(dd, ee, aa, bb, cc, X[ 7],  9);
+   FF(cc, dd, ee, aa, bb, X[ 8], 11);
+   FF(bb, cc, dd, ee, aa, X[ 9], 13);
+   FF(aa, bb, cc, dd, ee, X[10], 14);
+   FF(ee, aa, bb, cc, dd, X[11], 15);
+   FF(dd, ee, aa, bb, cc, X[12],  6);
+   FF(cc, dd, ee, aa, bb, X[13],  7);
+   FF(bb, cc, dd, ee, aa, X[14],  9);
+   FF(aa, bb, cc, dd, ee, X[15],  8);
+                             
+   /* round 2 */
+   GG(ee, aa, bb, cc, dd, X[ 7],  7);
+   GG(dd, ee, aa, bb, cc, X[ 4],  6);
+   GG(cc, dd, ee, aa, bb, X[13],  8);
+   GG(bb, cc, dd, ee, aa, X[ 1], 13);
+   GG(aa, bb, cc, dd, ee, X[10], 11);
+   GG(ee, aa, bb, cc, dd, X[ 6],  9);
+   GG(dd, ee, aa, bb, cc, X[15],  7);
+   GG(cc, dd, ee, aa, bb, X[ 3], 15);
+   GG(bb, cc, dd, ee, aa, X[12],  7);
+   GG(aa, bb, cc, dd, ee, X[ 0], 12);
+   GG(ee, aa, bb, cc, dd, X[ 9], 15);
+   GG(dd, ee, aa, bb, cc, X[ 5],  9);
+   GG(cc, dd, ee, aa, bb, X[ 2], 11);
+   GG(bb, cc, dd, ee, aa, X[14],  7);
+   GG(aa, bb, cc, dd, ee, X[11], 13);
+   GG(ee, aa, bb, cc, dd, X[ 8], 12);
+
+   /* round 3 */
+   HH(dd, ee, aa, bb, cc, X[ 3], 11);
+   HH(cc, dd, ee, aa, bb, X[10], 13);
+   HH(bb, cc, dd, ee, aa, X[14],  6);
+   HH(aa, bb, cc, dd, ee, X[ 4],  7);
+   HH(ee, aa, bb, cc, dd, X[ 9], 14);
+   HH(dd, ee, aa, bb, cc, X[15],  9);
+   HH(cc, dd, ee, aa, bb, X[ 8], 13);
+   HH(bb, cc, dd, ee, aa, X[ 1], 15);
+   HH(aa, bb, cc, dd, ee, X[ 2], 14);
+   HH(ee, aa, bb, cc, dd, X[ 7],  8);
+   HH(dd, ee, aa, bb, cc, X[ 0], 13);
+   HH(cc, dd, ee, aa, bb, X[ 6],  6);
+   HH(bb, cc, dd, ee, aa, X[13],  5);
+   HH(aa, bb, cc, dd, ee, X[11], 12);
+   HH(ee, aa, bb, cc, dd, X[ 5],  7);
+   HH(dd, ee, aa, bb, cc, X[12],  5);
+
+   /* round 4 */
+   II(cc, dd, ee, aa, bb, X[ 1], 11);
+   II(bb, cc, dd, ee, aa, X[ 9], 12);
+   II(aa, bb, cc, dd, ee, X[11], 14);
+   II(ee, aa, bb, cc, dd, X[10], 15);
+   II(dd, ee, aa, bb, cc, X[ 0], 14);
+   II(cc, dd, ee, aa, bb, X[ 8], 15);
+   II(bb, cc, dd, ee, aa, X[12],  9);
+   II(aa, bb, cc, dd, ee, X[ 4],  8);
+   II(ee, aa, bb, cc, dd, X[13],  9);
+   II(dd, ee, aa, bb, cc, X[ 3], 14);
+   II(cc, dd, ee, aa, bb, X[ 7],  5);
+   II(bb, cc, dd, ee, aa, X[15],  6);
+   II(aa, bb, cc, dd, ee, X[14],  8);
+   II(ee, aa, bb, cc, dd, X[ 5],  6);
+   II(dd, ee, aa, bb, cc, X[ 6],  5);
+   II(cc, dd, ee, aa, bb, X[ 2], 12);
+
+   /* round 5 */
+   JJ(bb, cc, dd, ee, aa, X[ 4],  9);
+   JJ(aa, bb, cc, dd, ee, X[ 0], 15);
+   JJ(ee, aa, bb, cc, dd, X[ 5],  5);
+   JJ(dd, ee, aa, bb, cc, X[ 9], 11);
+   JJ(cc, dd, ee, aa, bb, X[ 7],  6);
+   JJ(bb, cc, dd, ee, aa, X[12],  8);
+   JJ(aa, bb, cc, dd, ee, X[ 2], 13);
+   JJ(ee, aa, bb, cc, dd, X[10], 12);
+   JJ(dd, ee, aa, bb, cc, X[14],  5);
+   JJ(cc, dd, ee, aa, bb, X[ 1], 12);
+   JJ(bb, cc, dd, ee, aa, X[ 3], 13);
+   JJ(aa, bb, cc, dd, ee, X[ 8], 14);
+   JJ(ee, aa, bb, cc, dd, X[11], 11);
+   JJ(dd, ee, aa, bb, cc, X[ 6],  8);
+   JJ(cc, dd, ee, aa, bb, X[15],  5);
+   JJ(bb, cc, dd, ee, aa, X[13],  6);
+
+   /* parallel round 1 */
+   JJJ(aaa, bbb, ccc, ddd, eee, X[ 5],  8);
+   JJJ(eee, aaa, bbb, ccc, ddd, X[14],  9);
+   JJJ(ddd, eee, aaa, bbb, ccc, X[ 7],  9);
+   JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11);
+   JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13);
+   JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15);
+   JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15);
+   JJJ(ddd, eee, aaa, bbb, ccc, X[ 4],  5);
+   JJJ(ccc, ddd, eee, aaa, bbb, X[13],  7);
+   JJJ(bbb, ccc, ddd, eee, aaa, X[ 6],  7);
+   JJJ(aaa, bbb, ccc, ddd, eee, X[15],  8);
+   JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11);
+   JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14);
+   JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14);
+   JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12);
+   JJJ(aaa, bbb, ccc, ddd, eee, X[12],  6);
+
+   /* parallel round 2 */
+   III(eee, aaa, bbb, ccc, ddd, X[ 6],  9); 
+   III(ddd, eee, aaa, bbb, ccc, X[11], 13);
+   III(ccc, ddd, eee, aaa, bbb, X[ 3], 15);
+   III(bbb, ccc, ddd, eee, aaa, X[ 7],  7);
+   III(aaa, bbb, ccc, ddd, eee, X[ 0], 12);
+   III(eee, aaa, bbb, ccc, ddd, X[13],  8);
+   III(ddd, eee, aaa, bbb, ccc, X[ 5],  9);
+   III(ccc, ddd, eee, aaa, bbb, X[10], 11);
+   III(bbb, ccc, ddd, eee, aaa, X[14],  7);
+   III(aaa, bbb, ccc, ddd, eee, X[15],  7);
+   III(eee, aaa, bbb, ccc, ddd, X[ 8], 12);
+   III(ddd, eee, aaa, bbb, ccc, X[12],  7);
+   III(ccc, ddd, eee, aaa, bbb, X[ 4],  6);
+   III(bbb, ccc, ddd, eee, aaa, X[ 9], 15);
+   III(aaa, bbb, ccc, ddd, eee, X[ 1], 13);
+   III(eee, aaa, bbb, ccc, ddd, X[ 2], 11);
+
+   /* parallel round 3 */
+   HHH(ddd, eee, aaa, bbb, ccc, X[15],  9);
+   HHH(ccc, ddd, eee, aaa, bbb, X[ 5],  7);
+   HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15);
+   HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11);
+   HHH(eee, aaa, bbb, ccc, ddd, X[ 7],  8);
+   HHH(ddd, eee, aaa, bbb, ccc, X[14],  6);
+   HHH(ccc, ddd, eee, aaa, bbb, X[ 6],  6);
+   HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14);
+   HHH(aaa, bbb, ccc, ddd, eee, X[11], 12);
+   HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13);
+   HHH(ddd, eee, aaa, bbb, ccc, X[12],  5);
+   HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14);
+   HHH(bbb, ccc, ddd, eee, aaa, X[10], 13);
+   HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13);
+   HHH(eee, aaa, bbb, ccc, ddd, X[ 4],  7);
+   HHH(ddd, eee, aaa, bbb, ccc, X[13],  5);
+
+   /* parallel round 4 */   
+   GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15);
+   GGG(bbb, ccc, ddd, eee, aaa, X[ 6],  5);
+   GGG(aaa, bbb, ccc, ddd, eee, X[ 4],  8);
+   GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11);
+   GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14);
+   GGG(ccc, ddd, eee, aaa, bbb, X[11], 14);
+   GGG(bbb, ccc, ddd, eee, aaa, X[15],  6);
+   GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14);
+   GGG(eee, aaa, bbb, ccc, ddd, X[ 5],  6);
+   GGG(ddd, eee, aaa, bbb, ccc, X[12],  9);
+   GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12);
+   GGG(bbb, ccc, ddd, eee, aaa, X[13],  9);
+   GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12);
+   GGG(eee, aaa, bbb, ccc, ddd, X[ 7],  5);
+   GGG(ddd, eee, aaa, bbb, ccc, X[10], 15);
+   GGG(ccc, ddd, eee, aaa, bbb, X[14],  8);
+
+   /* parallel round 5 */
+   FFF(bbb, ccc, ddd, eee, aaa, X[12] ,  8);
+   FFF(aaa, bbb, ccc, ddd, eee, X[15] ,  5);
+   FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12);
+   FFF(ddd, eee, aaa, bbb, ccc, X[ 4] ,  9);
+   FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12);
+   FFF(bbb, ccc, ddd, eee, aaa, X[ 5] ,  5);
+   FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14);
+   FFF(eee, aaa, bbb, ccc, ddd, X[ 7] ,  6);
+   FFF(ddd, eee, aaa, bbb, ccc, X[ 6] ,  8);
+   FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13);
+   FFF(bbb, ccc, ddd, eee, aaa, X[13] ,  6);
+   FFF(aaa, bbb, ccc, ddd, eee, X[14] ,  5);
+   FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15);
+   FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13);
+   FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11);
+   FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11);
+
+   /* combine results */
+   ddd += cc + MDbuf[1];               /* final result for MDbuf[0] */
+   MDbuf[1] = MDbuf[2] + dd + eee;
+   MDbuf[2] = MDbuf[3] + ee + aaa;
+   MDbuf[3] = MDbuf[4] + aa + bbb;
+   MDbuf[4] = MDbuf[0] + bb + ccc;
+   MDbuf[0] = ddd;
+
+   return;
+}
+
+static void MDfinish(u32int *MDbuf, uchar *strptr, u32int lswlen, u32int mswlen)
+{
+   unsigned int i;                                 /* counter       */
+   u32int        X[16];                             /* message words */
+
+   memset(X, 0, 16*sizeof(u32int));
+
+   /* put bytes from strptr into X */
+   for (i=0; i<(lswlen&63); i++) {
+      /* byte i goes into word X[i div 4] at pos.  8*(i mod 4)  */
+      X[i>>2] ^= (u32int) *strptr++ << (8 * (i&3));
+   }
+
+   /* append the bit m_n == 1 */
+   X[(lswlen>>2)&15] ^= (u32int)1 << (8*(lswlen&3) + 7);
+
+   if ((lswlen & 63) > 55) {
+      /* length goes to next block */
+      compress(MDbuf, X);
+      memset(X, 0, 16*sizeof(u32int));
+   }
+
+   /* append length in bits*/
+   X[14] = lswlen << 3;
+   X[15] = (lswlen >> 29) | (mswlen << 3);
+   compress(MDbuf, X);
+
+   return;
+}
+
+DigestState*
+ripemd160(uchar *p, ulong len, uchar *digest, DigestState *s)
+{
+	u32int x[16];
+	int i, j, k;
+
+	if(s == nil){
+		s = malloc(sizeof(*s));
+		if(s == nil)
+			return nil;
+		memset(s, 0, sizeof(*s));
+		s->malloced = 1;
+	}
+
+	if(s->seeded == 0){
+		MDinit(s->state);
+		s->seeded = 1;
+	}
+
+	/* fill out the partial 64 byte block from previous calls */
+	if(s->blen){
+		i = 64 - s->blen;
+		if(len < i)
+			i = len;
+		memmove(s->buf + s->blen, p, i);
+		len -= i;
+		s->blen += i;
+		p += i;
+		if(s->blen == 64){
+			for(i = 0; i < 16; i++)
+				x[i] = BYTES_TO_DWORD(s->buf + i * 4);
+			compress(s->state, x);
+			s->len += s->blen;
+			s->blen = 0;
+		}
+	}
+
+	/* do 64 byte blocks */
+	i = len & ~0x3f;
+	if(i){
+		for(j = 0; j < i; j += 64){
+			for(k = 0; k < 16; k++)
+				x[k] = BYTES_TO_DWORD(p + j + k * 4);
+			compress(s->state, x);
+		}
+		s->len += i;
+		len -= i;
+		p += i;
+	}
+
+	/* save the left overs if not last call */
+	if(digest == 0){
+		if(len){
+			memmove(s->buf, p, len);
+			s->blen += len;
+		}
+		return s;
+	}
+
+	MDfinish(s->state, p, s->len + len, 0);
+	for(i = 0; i < 5; i++){
+		digest[4 * i] = s->state[i];
+		digest[4 * i + 1] = s->state[i] >> 8;
+		digest[4 * i + 2] = s->state[i] >> 16;
+		digest[4 * i + 3] = s->state[i] >> 24;
+
+	}
+	if(s->malloced == 1)
+		free(s);
+	return nil;
+
+}
--- a/libsec/rsagen.c
+++ b/libsec/rsagen.c
@@ -2,21 +2,6 @@
 #include <mp.h>
 #include <libsec.h>
 
-static void
-genrand(mpint *p, int n)
-{
-	mpdigit x;
-
-	// generate n random bits with high set
-	mpbits(p, n);
-	genrandom((uchar*)p->p, (n+7)/8);
-	p->top = (n+Dbits-1)/Dbits;
-	x = 1;
-	x <<= ((n-1)%Dbits);
-	p->p[p->top-1] &= (x-1);
-	p->p[p->top-1] |= x;
-}
-
 RSApriv*
 rsagen(int nlen, int elen, int rounds)
 {
@@ -31,8 +16,8 @@
 	phi = mpnew(nlen);
 
 	// create the prime factors and euclid's function
-	genstrongprime(p, nlen/2, rounds);
-	genstrongprime(q, nlen - mpsignif(p) + 1, rounds);
+	genprime(p, nlen/2, rounds);
+	genprime(q, nlen - mpsignif(p) + 1, rounds);
 	mpmul(p, q, n);
 	mpsub(p, mpone, e);
 	mpsub(q, mpone, d);
@@ -41,18 +26,21 @@
 	// find an e relatively prime to phi
 	t1 = mpnew(0);
 	t2 = mpnew(0);
-	genrand(e, elen);
+	mprand(elen, genrandom, e);
+	if(mpcmp(e,mptwo) <= 0)
+		itomp(3, e);
+	// See Menezes et al. p.291 "8.8 Note (selecting primes)" for discussion
+	// of the merits of various choices of primes and exponents.  e=3 is a
+	// common and recommended exponent, but doesn't necessarily work here
+	// because we chose strong rather than safe primes.
 	for(;;){
-		mpextendedgcd(e, phi, d, t1, t2);
-		if(mpcmp(d, mpone) == 0)
+		mpextendedgcd(e, phi, t1, d, t2);
+		if(mpcmp(t1, mpone) == 0)
 			break;
 		mpadd(mpone, e, e);
 	}
 	mpfree(t1);
 	mpfree(t2);
-
-	// d = e**-1 mod phi
-	mpinvert(e, phi, d);
 
 	// compute chinese remainder coefficient
 	c2 = mpnew(0);
--- /dev/null
+++ b/libsec/salsa.c
@@ -1,0 +1,320 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+enum{
+	Blockwords=	SalsaBsize/sizeof(u32int)
+};
+
+/* little-endian data order */
+#define GET4(p)	((((((p)[3]<<8) | (p)[2])<<8) | (p)[1])<<8 | (p)[0])
+#define PUT4(p, v)	(((p)[0]=v), (v>>=8), ((p)[1]=v), (v>>=8), ((p)[2]=v), (v>>=8), ((p)[3]=v))
+
+#define ROTATE(v,c) (t = v, (u32int)(t << (c)) | (t >> (32 - (c))))
+
+#define ENCRYPT(s, x, y, d) {\
+	u32int v; \
+	uchar *sp, *dp; \
+	sp = (s); \
+	v = GET4(sp); \
+	v ^= (x)+(y); \
+	dp = (d); \
+	PUT4(dp, v); \
+}
+
+static uchar sigma[16] = "expand 32-byte k";
+static uchar tau[16] = "expand 16-byte k";
+
+static void
+load(u32int *d, uchar *s, int nw)
+{
+	int i;
+
+	for(i = 0; i < nw; i++, s+=4)
+		d[i] = GET4(s);
+}
+
+void
+setupSalsastate(Salsastate *s, uchar *key, ulong keylen, uchar *iv, ulong ivlen, int rounds)
+{
+	if(keylen != 256/8 && keylen != 128/8)
+		sysfatal("invalid salsa key length");
+	if(ivlen != 64/8 && ivlen != 128/8 && ivlen != 192/8)
+		sysfatal("invalid salsa iv length");
+	if(rounds == 0)
+		rounds = 20;
+	s->rounds = rounds;
+	if(keylen == 256/8) { /* recommended */
+		load(&s->input[0],  sigma+4*0, 1);
+		load(&s->input[1],  key +16*0, 4);
+		load(&s->input[5],  sigma+4*1, 1);
+		load(&s->input[10], sigma+4*2, 1);
+		load(&s->input[11], key +16*1, 4);
+		load(&s->input[15], sigma+4*3, 1);
+	}else{
+		load(&s->input[0],  tau +4*0, 1);
+		load(&s->input[1],  key, 4);
+		load(&s->input[5],  tau +4*1, 1);
+		load(&s->input[10], tau +4*2, 1);
+		load(&s->input[11], key, 4);
+		load(&s->input[15], tau +4*3, 1);
+	}
+	s->key[0] = s->input[1];
+	s->key[1] = s->input[2];
+	s->key[2] = s->input[3];
+	s->key[3] = s->input[4];
+	s->key[4] = s->input[11];
+	s->key[5] = s->input[12];
+	s->key[6] = s->input[13];
+	s->key[7] = s->input[14];
+
+	s->ivwords = ivlen/4;
+	s->input[8] = 0;
+	s->input[9] = 0;
+	if(iv == nil){
+		s->input[6] = 0;
+		s->input[7] = 0;
+	}else
+		salsa_setiv(s, iv);
+}
+
+static void
+hsalsablock(uchar h[32], Salsastate *s)
+{
+	u32int x[Blockwords], t;
+	int i, rounds;
+
+	rounds = s->rounds;
+	x[0] = s->input[0];
+	x[1] = s->input[1];
+	x[2] = s->input[2];
+	x[3] = s->input[3];
+	x[4] = s->input[4];
+	x[5] = s->input[5];
+	x[6] = s->input[6];
+	x[7] = s->input[7];
+	x[8] = s->input[8];
+	x[9] = s->input[9];
+	x[10] = s->input[10];
+	x[11] = s->input[11];
+	x[12] = s->input[12];
+	x[13] = s->input[13];
+	x[14] = s->input[14];
+	x[15] = s->input[15];
+
+	for(i = rounds; i > 0; i -= 2) {
+	     x[4] ^= ROTATE( x[0]+x[12], 7);
+	     x[8] ^= ROTATE( x[4]+ x[0], 9);
+	    x[12] ^= ROTATE( x[8]+ x[4],13);
+	     x[0] ^= ROTATE(x[12]+ x[8],18);
+	     x[9] ^= ROTATE( x[5]+ x[1], 7);
+	    x[13] ^= ROTATE( x[9]+ x[5], 9);
+	     x[1] ^= ROTATE(x[13]+ x[9],13);
+	     x[5] ^= ROTATE( x[1]+x[13],18);
+	    x[14] ^= ROTATE(x[10]+ x[6], 7);
+	     x[2] ^= ROTATE(x[14]+x[10], 9);
+	     x[6] ^= ROTATE( x[2]+x[14],13);
+	    x[10] ^= ROTATE( x[6]+ x[2],18);
+	     x[3] ^= ROTATE(x[15]+x[11], 7);
+	     x[7] ^= ROTATE( x[3]+x[15], 9);
+	    x[11] ^= ROTATE( x[7]+ x[3],13);
+	    x[15] ^= ROTATE(x[11]+ x[7],18);
+	     x[1] ^= ROTATE( x[0]+ x[3], 7);
+	     x[2] ^= ROTATE( x[1]+ x[0], 9);
+	     x[3] ^= ROTATE( x[2]+ x[1],13);
+	     x[0] ^= ROTATE( x[3]+ x[2],18);
+	     x[6] ^= ROTATE( x[5]+ x[4], 7);
+	     x[7] ^= ROTATE( x[6]+ x[5], 9);
+	     x[4] ^= ROTATE( x[7]+ x[6],13);
+	     x[5] ^= ROTATE( x[4]+ x[7],18);
+	    x[11] ^= ROTATE(x[10]+ x[9], 7);
+	     x[8] ^= ROTATE(x[11]+x[10], 9);
+	     x[9] ^= ROTATE( x[8]+x[11],13);
+	    x[10] ^= ROTATE( x[9]+ x[8],18);
+	    x[12] ^= ROTATE(x[15]+x[14], 7);
+	    x[13] ^= ROTATE(x[12]+x[15], 9);
+	    x[14] ^= ROTATE(x[13]+x[12],13);
+	    x[15] ^= ROTATE(x[14]+x[13],18);
+	}
+
+	PUT4(h+0*4, x[0]);
+	PUT4(h+1*4, x[5]);
+	PUT4(h+2*4, x[10]);
+	PUT4(h+3*4, x[15]);
+	PUT4(h+4*4, x[6]);
+	PUT4(h+5*4, x[7]);
+	PUT4(h+6*4, x[8]);
+	PUT4(h+7*4, x[9]);
+}
+
+void
+salsa_setiv(Salsastate *s, uchar *iv)
+{
+	if(s->ivwords == 128/32){
+		/* hsalsa 128-bit iv */
+		load(&s->input[6], iv, 4);
+		return;
+	}
+	if(s->ivwords == 192/32){
+		/* xsalsa with 192-bit iv */
+		u32int counter[2];
+		uchar h[32];
+
+		counter[0] = s->input[8];
+		counter[1] = s->input[9];
+
+		s->input[1] = s->key[0];
+		s->input[2] = s->key[1];
+		s->input[3] = s->key[2];
+		s->input[4] = s->key[3];
+		s->input[11] = s->key[4];
+		s->input[12] = s->key[5];
+		s->input[13] = s->key[6];
+		s->input[14] = s->key[7];
+
+		load(&s->input[6], iv, 4);
+
+		hsalsablock(h, s);
+		load(&s->input[1],  h+16*0, 4);
+		load(&s->input[11], h+16*1, 4);
+		memset(h, 0, 32);
+
+		s->input[8] = counter[0];
+		s->input[9] = counter[1];
+
+		iv += 16;
+	}
+	/* 64-bit iv */
+	load(&s->input[6], iv, 2);
+}
+
+void
+salsa_setblock(Salsastate *s, u64int blockno)
+{
+	s->input[8] = blockno;
+	s->input[9] = blockno>>32;
+}
+
+static void
+encryptblock(Salsastate *s, uchar *src, uchar *dst)
+{
+	u32int x[Blockwords], t;
+	int i, rounds;
+
+	rounds = s->rounds;
+	x[0] = s->input[0];
+	x[1] = s->input[1];
+	x[2] = s->input[2];
+	x[3] = s->input[3];
+	x[4] = s->input[4];
+	x[5] = s->input[5];
+	x[6] = s->input[6];
+	x[7] = s->input[7];
+	x[8] = s->input[8];
+	x[9] = s->input[9];
+	x[10] = s->input[10];
+	x[11] = s->input[11];
+	x[12] = s->input[12];
+	x[13] = s->input[13];
+	x[14] = s->input[14];
+	x[15] = s->input[15];
+
+	for(i = rounds; i > 0; i -= 2) {
+	     x[4] ^= ROTATE( x[0]+x[12], 7);
+	     x[8] ^= ROTATE( x[4]+ x[0], 9);
+	    x[12] ^= ROTATE( x[8]+ x[4],13);
+	     x[0] ^= ROTATE(x[12]+ x[8],18);
+	     x[9] ^= ROTATE( x[5]+ x[1], 7);
+	    x[13] ^= ROTATE( x[9]+ x[5], 9);
+	     x[1] ^= ROTATE(x[13]+ x[9],13);
+	     x[5] ^= ROTATE( x[1]+x[13],18);
+	    x[14] ^= ROTATE(x[10]+ x[6], 7);
+	     x[2] ^= ROTATE(x[14]+x[10], 9);
+	     x[6] ^= ROTATE( x[2]+x[14],13);
+	    x[10] ^= ROTATE( x[6]+ x[2],18);
+	     x[3] ^= ROTATE(x[15]+x[11], 7);
+	     x[7] ^= ROTATE( x[3]+x[15], 9);
+	    x[11] ^= ROTATE( x[7]+ x[3],13);
+	    x[15] ^= ROTATE(x[11]+ x[7],18);
+	     x[1] ^= ROTATE( x[0]+ x[3], 7);
+	     x[2] ^= ROTATE( x[1]+ x[0], 9);
+	     x[3] ^= ROTATE( x[2]+ x[1],13);
+	     x[0] ^= ROTATE( x[3]+ x[2],18);
+	     x[6] ^= ROTATE( x[5]+ x[4], 7);
+	     x[7] ^= ROTATE( x[6]+ x[5], 9);
+	     x[4] ^= ROTATE( x[7]+ x[6],13);
+	     x[5] ^= ROTATE( x[4]+ x[7],18);
+	    x[11] ^= ROTATE(x[10]+ x[9], 7);
+	     x[8] ^= ROTATE(x[11]+x[10], 9);
+	     x[9] ^= ROTATE( x[8]+x[11],13);
+	    x[10] ^= ROTATE( x[9]+ x[8],18);
+	    x[12] ^= ROTATE(x[15]+x[14], 7);
+	    x[13] ^= ROTATE(x[12]+x[15], 9);
+	    x[14] ^= ROTATE(x[13]+x[12],13);
+	    x[15] ^= ROTATE(x[14]+x[13],18);
+	}
+
+#ifdef FULL_UNROLL
+	ENCRYPT(src+0*4, x[0], s->input[0], dst+0*4);
+	ENCRYPT(src+1*4, x[1], s->input[1], dst+1*4);
+	ENCRYPT(src+2*4, x[2], s->input[2], dst+2*4);
+	ENCRYPT(src+3*4, x[3], s->input[3], dst+3*4);
+	ENCRYPT(src+4*4, x[4], s->input[4], dst+4*4);
+	ENCRYPT(src+5*4, x[5], s->input[5], dst+5*4);
+	ENCRYPT(src+6*4, x[6], s->input[6], dst+6*4);
+	ENCRYPT(src+7*4, x[7], s->input[7], dst+7*4);
+	ENCRYPT(src+8*4, x[8], s->input[8], dst+8*4);
+	ENCRYPT(src+9*4, x[9], s->input[9], dst+9*4);
+	ENCRYPT(src+10*4, x[10], s->input[10], dst+10*4);
+	ENCRYPT(src+11*4, x[11], s->input[11], dst+11*4);
+	ENCRYPT(src+12*4, x[12], s->input[12], dst+12*4);
+	ENCRYPT(src+13*4, x[13], s->input[13], dst+13*4);
+	ENCRYPT(src+14*4, x[14], s->input[14], dst+14*4);
+	ENCRYPT(src+15*4, x[15], s->input[15], dst+15*4);
+#else
+	for(i=0; i<nelem(x); i+=4){
+		ENCRYPT(src, x[i], s->input[i], dst);
+		ENCRYPT(src+4, x[i+1], s->input[i+1], dst+4);
+		ENCRYPT(src+8, x[i+2], s->input[i+2], dst+8);
+		ENCRYPT(src+12, x[i+3], s->input[i+3], dst+12);
+		src += 16;
+		dst += 16;
+	}
+#endif
+
+	if(++s->input[8] == 0)
+		s->input[9]++;
+}
+
+void
+salsa_encrypt2(uchar *src, uchar *dst, ulong bytes, Salsastate *s)
+{
+	uchar tmp[SalsaBsize];
+
+	for(; bytes >= SalsaBsize; bytes -= SalsaBsize){
+		encryptblock(s, src, dst);
+		src += SalsaBsize;
+		dst += SalsaBsize;
+	}
+	if(bytes > 0){
+		memmove(tmp, src, bytes);
+		encryptblock(s, tmp, tmp);
+		memmove(dst, tmp, bytes);
+	}
+}
+
+void
+salsa_encrypt(uchar *buf, ulong bytes, Salsastate *s)
+{
+	salsa_encrypt2(buf, buf, bytes, s);
+}
+
+void
+hsalsa(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds)
+{
+	Salsastate s[1];
+
+	setupSalsastate(s, key, keylen, nonce, 16, rounds);
+	hsalsablock(h, s);
+	memset(s, 0, sizeof(s));
+}
--- /dev/null
+++ b/libsec/secp256k1.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+void secp256k1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h){
+	strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", nil, 16, p);
+	mpassign(mpzero, a);
+	uitomp(7UL, b);
+	strtomp("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", nil, 16, x);
+	strtomp("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", nil, 16, y);
+	strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", nil, 16, n);
+	mpassign(mpone, h);
+	}
--- /dev/null
+++ b/libsec/secp256r1.c
@@ -1,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+void secp256r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h){
+	strtomp("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", nil, 16, p);
+	uitomp(3UL, a);
+	mpsub(p, a, a);
+	strtomp("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", nil, 16, b);
+	strtomp("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", nil, 16, x);
+	strtomp("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", nil, 16, y);
+	strtomp("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", nil, 16, n);
+	mpassign(mpone, h);
+	}
--- a/libsec/sha1.c
+++ b/libsec/sha1.c
@@ -125,3 +125,10 @@
 		*output++ = x;
 	}
 }
+
+DigestState*
+hmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+	DigestState *s)
+{
+	return hmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen);
+}
--- /dev/null
+++ b/libsec/sha2_128.c
@@ -1,0 +1,191 @@
+/*
+ * sha2 128-bit
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+static void encode64(uchar*, u64int*, ulong);
+static DigestState* sha2_128(uchar *, ulong, uchar *, SHA2_256state *, int);
+
+extern void _sha2block128(uchar*, ulong, u64int*);
+
+/*
+ *  for sha2_384 and sha2_512, len must be multiple of 128 for all but
+ *  the last call.  There must be room in the input buffer to pad.
+ *
+ *  Note: sha2_384 calls sha2_512block as sha2_384; it just uses a different
+ *  initial seed to produce a truncated 384b hash result.  otherwise
+ *  it's the same as sha2_512.
+ */
+SHA2_384state*
+sha2_384(uchar *p, ulong len, uchar *digest, SHA2_384state *s)
+{
+	if(s == nil) {
+		s = mallocz(sizeof(*s), 1);
+		if(s == nil)
+			return nil;
+		s->malloced = 1;
+	}
+	if(s->seeded == 0){
+		/*
+		 * seed the state with the first 64 bits of the fractional
+		 * parts of the square roots of the 9th thru 16th primes.
+		 */
+ 		s->bstate[0] = 0xcbbb9d5dc1059ed8LL;
+		s->bstate[1] = 0x629a292a367cd507LL;
+		s->bstate[2] = 0x9159015a3070dd17LL;
+		s->bstate[3] = 0x152fecd8f70e5939LL;
+		s->bstate[4] = 0x67332667ffc00b31LL;
+		s->bstate[5] = 0x8eb44a8768581511LL;
+		s->bstate[6] = 0xdb0c2e0d64f98fa7LL;
+		s->bstate[7] = 0x47b5481dbefa4fa4LL;
+		s->seeded = 1;
+	}
+	return sha2_128(p, len, digest, s, SHA2_384dlen);
+}
+
+SHA2_512state*
+sha2_512(uchar *p, ulong len, uchar *digest, SHA2_512state *s)
+{
+
+	if(s == nil) {
+		s = mallocz(sizeof(*s), 1);
+		if(s == nil)
+			return nil;
+		s->malloced = 1;
+	}
+	if(s->seeded == 0){
+		/*
+		 * seed the state with the first 64 bits of the fractional
+		 * parts of the square roots of the first 8 primes 2..19).
+		 */
+ 		s->bstate[0] = 0x6a09e667f3bcc908LL;
+		s->bstate[1] = 0xbb67ae8584caa73bLL;
+		s->bstate[2] = 0x3c6ef372fe94f82bLL;
+		s->bstate[3] = 0xa54ff53a5f1d36f1LL;
+		s->bstate[4] = 0x510e527fade682d1LL;
+		s->bstate[5] = 0x9b05688c2b3e6c1fLL;
+		s->bstate[6] = 0x1f83d9abfb41bd6bLL;
+		s->bstate[7] = 0x5be0cd19137e2179LL;
+		s->seeded = 1;
+	}
+	return sha2_128(p, len, digest, s, SHA2_512dlen);
+}
+
+/* common 128 byte block padding and count code for SHA2_384 and SHA2_512 */
+static DigestState*
+sha2_128(uchar *p, ulong len, uchar *digest, SHA2_512state *s, int dlen)
+{
+	int i;
+	u64int x[16];
+	uchar buf[256];
+	uchar *e;
+
+	/* fill out the partial 128 byte block from previous calls */
+	if(s->blen){
+		i = 128 - s->blen;
+		if(len < i)
+			i = len;
+		memmove(s->buf + s->blen, p, i);
+		len -= i;
+		s->blen += i;
+		p += i;
+		if(s->blen == 128){
+			_sha2block128(s->buf, s->blen, s->bstate);
+			s->len += s->blen;
+			s->blen = 0;
+		}
+	}
+
+	/* do 128 byte blocks */
+	i = len & ~(128-1);
+	if(i){
+		_sha2block128(p, i, s->bstate);
+		s->len += i;
+		len -= i;
+		p += i;
+	}
+
+	/* save the left overs if not last call */
+	if(digest == 0){
+		if(len){
+			memmove(s->buf, p, len);
+			s->blen += len;
+		}
+		return s;
+	}
+
+	/*
+	 *  this is the last time through, pad what's left with 0x80,
+	 *  0's, and the input count to create a multiple of 128 bytes.
+	 */
+	if(s->blen){
+		p = s->buf;
+		len = s->blen;
+	} else {
+		memmove(buf, p, len);
+		p = buf;
+	}
+	s->len += len;
+	e = p + len;
+	if(len < 112)
+		i = 112 - len;
+	else
+		i = 240 - len;
+	memset(e, 0, i);
+	*e = 0x80;
+	len += i;
+
+	/* append the count */
+	x[0] = 0;			/* assume 32b length, i.e. < 4GB */
+	x[1] = s->len<<3;
+	encode64(p+len, x, 16);
+
+	/* digest the last part */
+	_sha2block128(p, len+16, s->bstate);
+	s->len += len+16;
+
+	/* return result and free state */
+	encode64(digest, s->bstate, dlen);
+	if(s->malloced == 1)
+		free(s);
+	return nil;
+}
+
+/*
+ * Encodes input (ulong long) into output (uchar).
+ * Assumes len is a multiple of 8.
+ */
+static void
+encode64(uchar *output, u64int *input, ulong len)
+{
+	u64int x;
+	uchar *e;
+
+	for(e = output + len; output < e;) {
+		x = *input++;
+		*output++ = x >> 56;
+		*output++ = x >> 48;
+		*output++ = x >> 40;
+		*output++ = x >> 32;
+		*output++ = x >> 24;
+		*output++ = x >> 16;
+		*output++ = x >> 8;
+		*output++ = x;
+	}
+}
+
+DigestState*
+hmac_sha2_384(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+	DigestState *s)
+{
+	return hmac_x(p, len, key, klen, digest, s, sha2_384, SHA2_384dlen);
+}
+
+DigestState*
+hmac_sha2_512(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+	DigestState *s)
+{
+	return hmac_x(p, len, key, klen, digest, s, sha2_512, SHA2_512dlen);
+}
--- /dev/null
+++ b/libsec/sha2_64.c
@@ -1,0 +1,187 @@
+/*
+ * sha2 64-bit
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+static void encode32(uchar*, u32int*, ulong);
+static DigestState* sha2_64(uchar *, ulong, uchar *, SHA2_256state *, int);
+
+extern void _sha2block64(uchar*, ulong, u32int*);
+
+/*
+ *  for sha2_224 and sha2_256, len must be multiple of 64 for all but
+ *  the last call.  There must be room in the input buffer to pad.
+ *
+ *  Note: sha2_224 calls sha2_256block as sha2_224, just uses different
+ *  initial seed and produces a 224b hash result.  otherwise it's
+ *  the same as sha2_256.
+ */
+
+SHA2_224state*
+sha2_224(uchar *p, ulong len, uchar *digest, SHA2_224state *s)
+{
+	if(s == nil) {
+		s = mallocz(sizeof(*s), 1);
+		if(s == nil)
+			return nil;
+		s->malloced = 1;
+	}
+	if(s->seeded == 0){
+		/*
+		 * seed the state with the first 32 bits of the fractional
+		 * parts of the square roots of the first 8 primes 2..19).
+		 */
+		s->state[0] = 0xc1059ed8;
+		s->state[1] = 0x367cd507;
+		s->state[2] = 0x3070dd17;
+		s->state[3] = 0xf70e5939;
+		s->state[4] = 0xffc00b31;
+		s->state[5] = 0x68581511;
+		s->state[6] = 0x64f98fa7;
+		s->state[7] = 0xbefa4fa4;
+		s->seeded = 1;
+	}
+	return sha2_64(p, len, digest, s, SHA2_224dlen);
+}
+
+SHA2_256state*
+sha2_256(uchar *p, ulong len, uchar *digest, SHA2_256state *s)
+{
+	if(s == nil) {
+		s = mallocz(sizeof(*s), 1);
+		if(s == nil)
+			return nil;
+		s->malloced = 1;
+	}
+	if(s->seeded == 0){
+		/*
+		 * seed the state with the first 32 bits of the fractional
+		 * parts of the square roots of the first 8 primes 2..19).
+		 */
+		s->state[0] = 0x6a09e667;
+		s->state[1] = 0xbb67ae85;
+		s->state[2] = 0x3c6ef372;
+		s->state[3] = 0xa54ff53a;
+		s->state[4] = 0x510e527f;
+		s->state[5] = 0x9b05688c;
+		s->state[6] = 0x1f83d9ab;
+		s->state[7] = 0x5be0cd19;
+		s->seeded = 1;
+	}
+	return sha2_64(p, len, digest, s, SHA2_256dlen);
+}
+
+/* common 64 byte block padding and count code for SHA2_224 and SHA2_256 */
+static DigestState*
+sha2_64(uchar *p, ulong len, uchar *digest, SHA2_256state *s, int dlen)
+{
+	int i;
+	u32int x[16];
+	uchar buf[128];
+	uchar *e;
+
+	/* fill out the partial 64 byte block from previous calls */
+	if(s->blen){
+		i = 64 - s->blen;
+		if(len < i)
+			i = len;
+		memmove(s->buf + s->blen, p, i);
+		len -= i;
+		s->blen += i;
+		p += i;
+		if(s->blen == 64){
+			_sha2block64(s->buf, s->blen, s->state);
+			s->len += s->blen;
+			s->blen = 0;
+		}
+	}
+
+	/* do 64 byte blocks */
+	i = len & ~(64-1);
+	if(i){
+		_sha2block64(p, i, s->state);
+		s->len += i;
+		len -= i;
+		p += i;
+	}
+
+	/* save the left overs if not last call */
+	if(digest == 0){
+		if(len){
+			memmove(s->buf, p, len);
+			s->blen += len;
+		}
+		return s;
+	}
+
+	/*
+	 *  this is the last time through, pad what's left with 0x80,
+	 *  0's, and the input count to create a multiple of 64 bytes.
+	 */
+	if(s->blen){
+		p = s->buf;
+		len = s->blen;
+	} else {
+		memmove(buf, p, len);
+		p = buf;
+	}
+	s->len += len;
+	e = p + len;
+	if(len < 56)
+		i = 56 - len;
+	else
+		i = 120 - len;
+	memset(e, 0, i);
+	*e = 0x80;
+	len += i;
+
+	/* append the count */
+	x[0] = s->len>>29;
+	x[1] = s->len<<3;
+	encode32(p+len, x, 8);
+
+	/* digest the last part */
+	_sha2block64(p, len+8, s->state);
+	s->len += len+8;
+
+	/* return result and free state */
+	encode32(digest, s->state, dlen);
+	if(s->malloced == 1)
+		free(s);
+	return nil;
+}
+
+/*
+ * Encodes input (ulong) into output (uchar).
+ * Assumes len is a multiple of 4.
+ */
+static void
+encode32(uchar *output, u32int *input, ulong len)
+{
+	u32int x;
+	uchar *e;
+
+	for(e = output + len; output < e;) {
+		x = *input++;
+		*output++ = x >> 24;
+		*output++ = x >> 16;
+		*output++ = x >> 8;
+		*output++ = x;
+	}
+}
+
+DigestState*
+hmac_sha2_224(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+	DigestState *s)
+{
+	return hmac_x(p, len, key, klen, digest, s, sha2_224, SHA2_224dlen);
+}
+
+DigestState*
+hmac_sha2_256(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+	DigestState *s)
+{
+	return hmac_x(p, len, key, klen, digest, s, sha2_256, SHA2_256dlen);
+}
--- /dev/null
+++ b/libsec/sha2block128.c
@@ -1,0 +1,101 @@
+/*
+ * sha2_512 block cipher
+ *
+ * Implementation straight from Federal Information Processing Standards
+ * publication 180-2 (+Change Notice to include SHA-224) August 1, 2002
+ *   note: the following upper and lower case macro names are distinct
+ *	   and reflect the functions defined in FIPS pub. 180-2.
+ */
+#include <u.h>
+#include <libc.h>
+
+#define ROTR(x,n)	(((x) >> (n)) | ((x) << (64-(n))))
+#define sigma0(x)	(ROTR((x),1) ^ ROTR((x),8) ^ ((x) >> 7))
+#define sigma1(x)	(ROTR((x),19) ^ ROTR((x),61) ^ ((x) >> 6))
+#define SIGMA0(x)	(ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+#define SIGMA1(x)	(ROTR((x),14) ^ ROTR((x),18) ^ ROTR((x),41))
+#define Ch(x,y,z)	(((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z)	(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/*
+ * first 64 bits of the fractional parts of cube roots of
+ * first 80 primes (2..311).
+ */
+static u64int K512[80] = {
+	0x428a2f98d728ae22LL, 0x7137449123ef65cdLL, 0xb5c0fbcfec4d3b2fLL, 0xe9b5dba58189dbbcLL,
+	0x3956c25bf348b538LL, 0x59f111f1b605d019LL, 0x923f82a4af194f9bLL, 0xab1c5ed5da6d8118LL,
+	0xd807aa98a3030242LL, 0x12835b0145706fbeLL, 0x243185be4ee4b28cLL, 0x550c7dc3d5ffb4e2LL,
+	0x72be5d74f27b896fLL, 0x80deb1fe3b1696b1LL, 0x9bdc06a725c71235LL, 0xc19bf174cf692694LL,
+	0xe49b69c19ef14ad2LL, 0xefbe4786384f25e3LL, 0x0fc19dc68b8cd5b5LL, 0x240ca1cc77ac9c65LL,
+	0x2de92c6f592b0275LL, 0x4a7484aa6ea6e483LL, 0x5cb0a9dcbd41fbd4LL, 0x76f988da831153b5LL,
+	0x983e5152ee66dfabLL, 0xa831c66d2db43210LL, 0xb00327c898fb213fLL, 0xbf597fc7beef0ee4LL,
+	0xc6e00bf33da88fc2LL, 0xd5a79147930aa725LL, 0x06ca6351e003826fLL, 0x142929670a0e6e70LL,
+	0x27b70a8546d22ffcLL, 0x2e1b21385c26c926LL, 0x4d2c6dfc5ac42aedLL, 0x53380d139d95b3dfLL,
+	0x650a73548baf63deLL, 0x766a0abb3c77b2a8LL, 0x81c2c92e47edaee6LL, 0x92722c851482353bLL,
+	0xa2bfe8a14cf10364LL, 0xa81a664bbc423001LL, 0xc24b8b70d0f89791LL, 0xc76c51a30654be30LL,
+	0xd192e819d6ef5218LL, 0xd69906245565a910LL, 0xf40e35855771202aLL, 0x106aa07032bbd1b8LL,
+	0x19a4c116b8d2d0c8LL, 0x1e376c085141ab53LL, 0x2748774cdf8eeb99LL, 0x34b0bcb5e19b48a8LL,
+	0x391c0cb3c5c95a63LL, 0x4ed8aa4ae3418acbLL, 0x5b9cca4f7763e373LL, 0x682e6ff3d6b2b8a3LL,
+	0x748f82ee5defb2fcLL, 0x78a5636f43172f60LL, 0x84c87814a1f0ab72LL, 0x8cc702081a6439ecLL,
+	0x90befffa23631e28LL, 0xa4506cebde82bde9LL, 0xbef9a3f7b2c67915LL, 0xc67178f2e372532bLL,
+	0xca273eceea26619cLL, 0xd186b8c721c0c207LL, 0xeada7dd6cde0eb1eLL, 0xf57d4f7fee6ed178LL,
+	0x06f067aa72176fbaLL, 0x0a637dc5a2c898a6LL, 0x113f9804bef90daeLL, 0x1b710b35131c471bLL,
+	0x28db77f523047d84LL, 0x32caab7b40c72493LL, 0x3c9ebe0a15c9bebcLL, 0x431d67c49c100d4cLL,
+	0x4cc5d4becb3e42b6LL, 0x597f299cfc657e2aLL, 0x5fcb6fab3ad6faecLL, 0x6c44198c4a475817LL };
+
+void
+_sha2block128(uchar *p, ulong len, u64int *s)
+{
+	u64int a, b, c, d, e, f, g, h, t1, t2;
+	u64int *kp, *wp;
+	u64int w[80];
+	uchar *end;
+
+	/* at this point, we have a multiple of 64 bytes */
+	for(end = p+len; p < end;){
+		a = s[0];
+		b = s[1];
+		c = s[2];
+		d = s[3];
+		e = s[4];
+		f = s[5];
+		g = s[6];
+		h = s[7];
+
+		for(wp = w; wp < &w[16]; wp++, p += 8)
+			wp[0] = ((vlong)p[0])<<56 | ((vlong)p[1])<<48 |
+				((vlong)p[2])<<40 | ((vlong)p[3])<<32 |
+				p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7];
+		for(; wp < &w[80]; wp++) {
+			u64int s0, s1;
+
+			s0 = sigma0(wp[-15]);
+			s1 = sigma1(wp[-2]);
+//			wp[0] = sigma1(wp[-2]) + wp[-7] + sigma0(wp[-15]) + wp[-16];
+			wp[0] = s1 + wp[-7] + s0 + wp[-16];
+		}
+
+		for(kp = K512, wp = w; wp < &w[80]; ) {
+			t1 = h + SIGMA1(e) + Ch(e,f,g) + *kp++ + *wp++;
+			t2 = SIGMA0(a) + Maj(a,b,c);
+			h = g;
+			g = f;
+			f = e;
+			e = d + t1;
+			d = c;
+			c = b;
+			b = a;
+			a = t1 + t2;
+		}
+
+		/* save state */
+		s[0] += a;
+		s[1] += b;
+		s[2] += c;
+		s[3] += d;
+		s[4] += e;
+		s[5] += f;
+		s[6] += g;
+		s[7] += h;
+	}
+}
--- /dev/null
+++ b/libsec/sha2block64.c
@@ -1,0 +1,92 @@
+/*
+ * sha2_256 block cipher
+ *
+ * Implementation straight from Federal Information Processing Standards
+ * publication 180-2 (+Change Notice to include SHA-224) August 1, 2002
+ *   note: the following upper and lower case macro names are distinct
+ *	   and reflect the functions defined in FIPS pub. 180-2.
+ */
+
+#include <u.h>
+#include <libc.h>
+
+#define ROTR(x,n)	(((x) >> (n)) | ((x) << (32-(n))))
+#define sigma0(x)	(ROTR((x),7) ^ ROTR((x),18) ^ ((x) >> 3))
+#define sigma1(x)	(ROTR((x),17) ^ ROTR((x),19) ^ ((x) >> 10))
+#define SIGMA0(x)	(ROTR((x),2) ^ ROTR((x),13) ^ ROTR((x),22))
+#define SIGMA1(x)	(ROTR((x),6) ^ ROTR((x),11) ^ ROTR((x),25))
+#define Ch(x,y,z)	(((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z)	(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/*
+ * first 32 bits of the fractional parts of cube roots of
+ * first 64 primes (2..311).
+ */
+static u32int K256[64] = {
+	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,
+	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
+	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,
+	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,
+	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
+	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,
+	0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,
+	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
+	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2,
+};
+
+void
+_sha2block64(uchar *p, ulong len, u32int *s)
+{
+	u32int a, b, c, d, e, f, g, h, t1, t2;
+	u32int *kp, *wp;
+	u32int w[64];
+	uchar *end;
+
+	/* at this point, we have a multiple of 64 bytes */
+	for(end = p+len; p < end;){
+		a = s[0];
+		b = s[1];
+		c = s[2];
+		d = s[3];
+		e = s[4];
+		f = s[5];
+		g = s[6];
+		h = s[7];
+
+		for(wp = w; wp < &w[16]; wp++, p += 4)
+			wp[0] = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+		for(; wp < &w[64]; wp++)
+			wp[0] = sigma1(wp[-2]) + wp[-7] +
+				sigma0(wp[-15]) + wp[-16];
+
+		for(kp = K256, wp = w; wp < &w[64]; ) {
+			t1 = h + SIGMA1(e) + Ch(e,f,g) + *kp++ + *wp++;
+			t2 = SIGMA0(a) + Maj(a,b,c);
+			h = g;
+			g = f;
+			f = e;
+			e = d + t1;
+			d = c;
+			c = b;
+			b = a;
+			a = t1 + t2;
+		}
+
+		/* save state */
+		s[0] += a;
+		s[1] += b;
+		s[2] += c;
+		s[3] += d;
+		s[4] += e;
+		s[5] += f;
+		s[6] += g;
+		s[7] += h;
+	}
+}
--- a/libsec/smallprimetest.c
+++ /dev/null
@@ -1,1039 +1,0 @@
-#include "os.h"
-#include <mp.h>
-#include <libsec.h>
-
-static ulong smallprimes[] = {
-	2,	3,	5,	7,	11,	13,	17,	19,	23,	29,
-	31,	37,	41,	43,	47,	53,	59,	61,	67,	71,
-	73,	79,	83,	89,	97,	101,	103,	107,	109,	113,
-	127,	131,	137,	139,	149,	151,	157,	163,	167,	173,
-	179,	181,	191,	193,	197,	199,	211,	223,	227,	229,
-	233,	239,	241,	251,	257,	263,	269,	271,	277,	281,
-	283,	293,	307,	311,	313,	317,	331,	337,	347,	349,
-	353,	359,	367,	373,	379,	383,	389,	397,	401,	409,
-	419,	421,	431,	433,	439,	443,	449,	457,	461,	463,
-	467,	479,	487,	491,	499,	503,	509,	521,	523,	541,
-	547,	557,	563,	569,	571,	577,	587,	593,	599,	601,
-	607,	613,	617,	619,	631,	641,	643,	647,	653,	659,
-	661,	673,	677,	683,	691,	701,	709,	719,	727,	733,
-	739,	743,	751,	757,	761,	769,	773,	787,	797,	809,
-	811,	821,	823,	827,	829,	839,	853,	857,	859,	863,
-	877,	881,	883,	887,	907,	911,	919,	929,	937,	941,
-	947,	953,	967,	971,	977,	983,	991,	997,	1009,	1013,
-	1019,	1021,	1031,	1033,	1039,	1049,	1051,	1061,	1063,	1069,
-	1087,	1091,	1093,	1097,	1103,	1109,	1117,	1123,	1129,	1151,
-	1153,	1163,	1171,	1181,	1187,	1193,	1201,	1213,	1217,	1223,
-	1229,	1231,	1237,	1249,	1259,	1277,	1279,	1283,	1289,	1291,
-	1297,	1301,	1303,	1307,	1319,	1321,	1327,	1361,	1367,	1373,
-	1381,	1399,	1409,	1423,	1427,	1429,	1433,	1439,	1447,	1451,
-	1453,	1459,	1471,	1481,	1483,	1487,	1489,	1493,	1499,	1511,
-	1523,	1531,	1543,	1549,	1553,	1559,	1567,	1571,	1579,	1583,
-	1597,	1601,	1607,	1609,	1613,	1619,	1621,	1627,	1637,	1657,
-	1663,	1667,	1669,	1693,	1697,	1699,	1709,	1721,	1723,	1733,
-	1741,	1747,	1753,	1759,	1777,	1783,	1787,	1789,	1801,	1811,
-	1823,	1831,	1847,	1861,	1867,	1871,	1873,	1877,	1879,	1889,
-	1901,	1907,	1913,	1931,	1933,	1949,	1951,	1973,	1979,	1987,
-	1993,	1997,	1999,	2003,	2011,	2017,	2027,	2029,	2039,	2053,
-	2063,	2069,	2081,	2083,	2087,	2089,	2099,	2111,	2113,	2129,
-	2131,	2137,	2141,	2143,	2153,	2161,	2179,	2203,	2207,	2213,
-	2221,	2237,	2239,	2243,	2251,	2267,	2269,	2273,	2281,	2287,
-	2293,	2297,	2309,	2311,	2333,	2339,	2341,	2347,	2351,	2357,
-	2371,	2377,	2381,	2383,	2389,	2393,	2399,	2411,	2417,	2423,
-	2437,	2441,	2447,	2459,	2467,	2473,	2477,	2503,	2521,	2531,
-	2539,	2543,	2549,	2551,	2557,	2579,	2591,	2593,	2609,	2617,
-	2621,	2633,	2647,	2657,	2659,	2663,	2671,	2677,	2683,	2687,
-	2689,	2693,	2699,	2707,	2711,	2713,	2719,	2729,	2731,	2741,
-	2749,	2753,	2767,	2777,	2789,	2791,	2797,	2801,	2803,	2819,
-	2833,	2837,	2843,	2851,	2857,	2861,	2879,	2887,	2897,	2903,
-	2909,	2917,	2927,	2939,	2953,	2957,	2963,	2969,	2971,	2999,
-	3001,	3011,	3019,	3023,	3037,	3041,	3049,	3061,	3067,	3079,
-	3083,	3089,	3109,	3119,	3121,	3137,	3163,	3167,	3169,	3181,
-	3187,	3191,	3203,	3209,	3217,	3221,	3229,	3251,	3253,	3257,
-	3259,	3271,	3299,	3301,	3307,	3313,	3319,	3323,	3329,	3331,
-	3343,	3347,	3359,	3361,	3371,	3373,	3389,	3391,	3407,	3413,
-	3433,	3449,	3457,	3461,	3463,	3467,	3469,	3491,	3499,	3511,
-	3517,	3527,	3529,	3533,	3539,	3541,	3547,	3557,	3559,	3571,
-	3581,	3583,	3593,	3607,	3613,	3617,	3623,	3631,	3637,	3643,
-	3659,	3671,	3673,	3677,	3691,	3697,	3701,	3709,	3719,	3727,
-	3733,	3739,	3761,	3767,	3769,	3779,	3793,	3797,	3803,	3821,
-	3823,	3833,	3847,	3851,	3853,	3863,	3877,	3881,	3889,	3907,
-	3911,	3917,	3919,	3923,	3929,	3931,	3943,	3947,	3967,	3989,
-	4001,	4003,	4007,	4013,	4019,	4021,	4027,	4049,	4051,	4057,
-	4073,	4079,	4091,	4093,	4099,	4111,	4127,	4129,	4133,	4139,
-	4153,	4157,	4159,	4177,	4201,	4211,	4217,	4219,	4229,	4231,
-	4241,	4243,	4253,	4259,	4261,	4271,	4273,	4283,	4289,	4297,
-	4327,	4337,	4339,	4349,	4357,	4363,	4373,	4391,	4397,	4409,
-	4421,	4423,	4441,	4447,	4451,	4457,	4463,	4481,	4483,	4493,
-	4507,	4513,	4517,	4519,	4523,	4547,	4549,	4561,	4567,	4583,
-	4591,	4597,	4603,	4621,	4637,	4639,	4643,	4649,	4651,	4657,
-	4663,	4673,	4679,	4691,	4703,	4721,	4723,	4729,	4733,	4751,
-	4759,	4783,	4787,	4789,	4793,	4799,	4801,	4813,	4817,	4831,
-	4861,	4871,	4877,	4889,	4903,	4909,	4919,	4931,	4933,	4937,
-	4943,	4951,	4957,	4967,	4969,	4973,	4987,	4993,	4999,	5003,
-	5009,	5011,	5021,	5023,	5039,	5051,	5059,	5077,	5081,	5087,
-	5099,	5101,	5107,	5113,	5119,	5147,	5153,	5167,	5171,	5179,
-	5189,	5197,	5209,	5227,	5231,	5233,	5237,	5261,	5273,	5279,
-	5281,	5297,	5303,	5309,	5323,	5333,	5347,	5351,	5381,	5387,
-	5393,	5399,	5407,	5413,	5417,	5419,	5431,	5437,	5441,	5443,
-	5449,	5471,	5477,	5479,	5483,	5501,	5503,	5507,	5519,	5521,
-	5527,	5531,	5557,	5563,	5569,	5573,	5581,	5591,	5623,	5639,
-	5641,	5647,	5651,	5653,	5657,	5659,	5669,	5683,	5689,	5693,
-	5701,	5711,	5717,	5737,	5741,	5743,	5749,	5779,	5783,	5791,
-	5801,	5807,	5813,	5821,	5827,	5839,	5843,	5849,	5851,	5857,
-	5861,	5867,	5869,	5879,	5881,	5897,	5903,	5923,	5927,	5939,
-	5953,	5981,	5987,	6007,	6011,	6029,	6037,	6043,	6047,	6053,
-	6067,	6073,	6079,	6089,	6091,	6101,	6113,	6121,	6131,	6133,
-	6143,	6151,	6163,	6173,	6197,	6199,	6203,	6211,	6217,	6221,
-	6229,	6247,	6257,	6263,	6269,	6271,	6277,	6287,	6299,	6301,
-	6311,	6317,	6323,	6329,	6337,	6343,	6353,	6359,	6361,	6367,
-	6373,	6379,	6389,	6397,	6421,	6427,	6449,	6451,	6469,	6473,
-	6481,	6491,	6521,	6529,	6547,	6551,	6553,	6563,	6569,	6571,
-	6577,	6581,	6599,	6607,	6619,	6637,	6653,	6659,	6661,	6673,
-	6679,	6689,	6691,	6701,	6703,	6709,	6719,	6733,	6737,	6761,
-	6763,	6779,	6781,	6791,	6793,	6803,	6823,	6827,	6829,	6833,
-	6841,	6857,	6863,	6869,	6871,	6883,	6899,	6907,	6911,	6917,
-	6947,	6949,	6959,	6961,	6967,	6971,	6977,	6983,	6991,	6997,
-	7001,	7013,	7019,	7027,	7039,	7043,	7057,	7069,	7079,	7103,
-	7109,	7121,	7127,	7129,	7151,	7159,	7177,	7187,	7193,	7207,
-	7211,	7213,	7219,	7229,	7237,	7243,	7247,	7253,	7283,	7297,
-	7307,	7309,	7321,	7331,	7333,	7349,	7351,	7369,	7393,	7411,
-	7417,	7433,	7451,	7457,	7459,	7477,	7481,	7487,	7489,	7499,
-	7507,	7517,	7523,	7529,	7537,	7541,	7547,	7549,	7559,	7561,
-	7573,	7577,	7583,	7589,	7591,	7603,	7607,	7621,	7639,	7643,
-	7649,	7669,	7673,	7681,	7687,	7691,	7699,	7703,	7717,	7723,
-	7727,	7741,	7753,	7757,	7759,	7789,	7793,	7817,	7823,	7829,
-	7841,	7853,	7867,	7873,	7877,	7879,	7883,	7901,	7907,	7919,
-	7927,	7933,	7937,	7949,	7951,	7963,	7993,	8009,	8011,	8017,
-	8039,	8053,	8059,	8069,	8081,	8087,	8089,	8093,	8101,	8111,
-	8117,	8123,	8147,	8161,	8167,	8171,	8179,	8191,	8209,	8219,
-	8221,	8231,	8233,	8237,	8243,	8263,	8269,	8273,	8287,	8291,
-	8293,	8297,	8311,	8317,	8329,	8353,	8363,	8369,	8377,	8387,
-	8389,	8419,	8423,	8429,	8431,	8443,	8447,	8461,	8467,	8501,
-	8513,	8521,	8527,	8537,	8539,	8543,	8563,	8573,	8581,	8597,
-	8599,	8609,	8623,	8627,	8629,	8641,	8647,	8663,	8669,	8677,
-	8681,	8689,	8693,	8699,	8707,	8713,	8719,	8731,	8737,	8741,
-	8747,	8753,	8761,	8779,	8783,	8803,	8807,	8819,	8821,	8831,
-	8837,	8839,	8849,	8861,	8863,	8867,	8887,	8893,	8923,	8929,
-	8933,	8941,	8951,	8963,	8969,	8971,	8999,	9001,	9007,	9011,
-	9013,	9029,	9041,	9043,	9049,	9059,	9067,	9091,	9103,	9109,
-	9127,	9133,	9137,	9151,	9157,	9161,	9173,	9181,	9187,	9199,
-	9203,	9209,	9221,	9227,	9239,	9241,	9257,	9277,	9281,	9283,
-	9293,	9311,	9319,	9323,	9337,	9341,	9343,	9349,	9371,	9377,
-	9391,	9397,	9403,	9413,	9419,	9421,	9431,	9433,	9437,	9439,
-	9461,	9463,	9467,	9473,	9479,	9491,	9497,	9511,	9521,	9533,
-	9539,	9547,	9551,	9587,	9601,	9613,	9619,	9623,	9629,	9631,
-	9643,	9649,	9661,	9677,	9679,	9689,	9697,	9719,	9721,	9733,
-	9739,	9743,	9749,	9767,	9769,	9781,	9787,	9791,	9803,	9811,
-	9817,	9829,	9833,	9839,	9851,	9857,	9859,	9871,	9883,	9887,
-	9901,	9907,	9923,	9929,	9931,	9941,	9949,	9967,	9973,	10007,
-	10009,	10037,	10039,	10061,	10067,	10069,	10079,	10091,	10093,	10099,
-	10103,	10111,	10133,	10139,	10141,	10151,	10159,	10163,	10169,	10177,
-	10181,	10193,	10211,	10223,	10243,	10247,	10253,	10259,	10267,	10271,
-	10273,	10289,	10301,	10303,	10313,	10321,	10331,	10333,	10337,	10343,
-	10357,	10369,	10391,	10399,	10427,	10429,	10433,	10453,	10457,	10459,
-	10463,	10477,	10487,	10499,	10501,	10513,	10529,	10531,	10559,	10567,
-	10589,	10597,	10601,	10607,	10613,	10627,	10631,	10639,	10651,	10657,
-	10663,	10667,	10687,	10691,	10709,	10711,	10723,	10729,	10733,	10739,
-	10753,	10771,	10781,	10789,	10799,	10831,	10837,	10847,	10853,	10859,
-	10861,	10867,	10883,	10889,	10891,	10903,	10909,	10937,	10939,	10949,
-	10957,	10973,	10979,	10987,	10993,	11003,	11027,	11047,	11057,	11059,
-	11069,	11071,	11083,	11087,	11093,	11113,	11117,	11119,	11131,	11149,
-	11159,	11161,	11171,	11173,	11177,	11197,	11213,	11239,	11243,	11251,
-	11257,	11261,	11273,	11279,	11287,	11299,	11311,	11317,	11321,	11329,
-	11351,	11353,	11369,	11383,	11393,	11399,	11411,	11423,	11437,	11443,
-	11447,	11467,	11471,	11483,	11489,	11491,	11497,	11503,	11519,	11527,
-	11549,	11551,	11579,	11587,	11593,	11597,	11617,	11621,	11633,	11657,
-	11677,	11681,	11689,	11699,	11701,	11717,	11719,	11731,	11743,	11777,
-	11779,	11783,	11789,	11801,	11807,	11813,	11821,	11827,	11831,	11833,
-	11839,	11863,	11867,	11887,	11897,	11903,	11909,	11923,	11927,	11933,
-	11939,	11941,	11953,	11959,	11969,	11971,	11981,	11987,	12007,	12011,
-	12037,	12041,	12043,	12049,	12071,	12073,	12097,	12101,	12107,	12109,
-	12113,	12119,	12143,	12149,	12157,	12161,	12163,	12197,	12203,	12211,
-	12227,	12239,	12241,	12251,	12253,	12263,	12269,	12277,	12281,	12289,
-	12301,	12323,	12329,	12343,	12347,	12373,	12377,	12379,	12391,	12401,
-	12409,	12413,	12421,	12433,	12437,	12451,	12457,	12473,	12479,	12487,
-	12491,	12497,	12503,	12511,	12517,	12527,	12539,	12541,	12547,	12553,
-	12569,	12577,	12583,	12589,	12601,	12611,	12613,	12619,	12637,	12641,
-	12647,	12653,	12659,	12671,	12689,	12697,	12703,	12713,	12721,	12739,
-	12743,	12757,	12763,	12781,	12791,	12799,	12809,	12821,	12823,	12829,
-	12841,	12853,	12889,	12893,	12899,	12907,	12911,	12917,	12919,	12923,
-	12941,	12953,	12959,	12967,	12973,	12979,	12983,	13001,	13003,	13007,
-	13009,	13033,	13037,	13043,	13049,	13063,	13093,	13099,	13103,	13109,
-	13121,	13127,	13147,	13151,	13159,	13163,	13171,	13177,	13183,	13187,
-	13217,	13219,	13229,	13241,	13249,	13259,	13267,	13291,	13297,	13309,
-	13313,	13327,	13331,	13337,	13339,	13367,	13381,	13397,	13399,	13411,
-	13417,	13421,	13441,	13451,	13457,	13463,	13469,	13477,	13487,	13499,
-	13513,	13523,	13537,	13553,	13567,	13577,	13591,	13597,	13613,	13619,
-	13627,	13633,	13649,	13669,	13679,	13681,	13687,	13691,	13693,	13697,
-	13709,	13711,	13721,	13723,	13729,	13751,	13757,	13759,	13763,	13781,
-	13789,	13799,	13807,	13829,	13831,	13841,	13859,	13873,	13877,	13879,
-	13883,	13901,	13903,	13907,	13913,	13921,	13931,	13933,	13963,	13967,
-	13997,	13999,	14009,	14011,	14029,	14033,	14051,	14057,	14071,	14081,
-	14083,	14087,	14107,	14143,	14149,	14153,	14159,	14173,	14177,	14197,
-	14207,	14221,	14243,	14249,	14251,	14281,	14293,	14303,	14321,	14323,
-	14327,	14341,	14347,	14369,	14387,	14389,	14401,	14407,	14411,	14419,
-	14423,	14431,	14437,	14447,	14449,	14461,	14479,	14489,	14503,	14519,
-	14533,	14537,	14543,	14549,	14551,	14557,	14561,	14563,	14591,	14593,
-	14621,	14627,	14629,	14633,	14639,	14653,	14657,	14669,	14683,	14699,
-	14713,	14717,	14723,	14731,	14737,	14741,	14747,	14753,	14759,	14767,
-	14771,	14779,	14783,	14797,	14813,	14821,	14827,	14831,	14843,	14851,
-	14867,	14869,	14879,	14887,	14891,	14897,	14923,	14929,	14939,	14947,
-	14951,	14957,	14969,	14983,	15013,	15017,	15031,	15053,	15061,	15073,
-	15077,	15083,	15091,	15101,	15107,	15121,	15131,	15137,	15139,	15149,
-	15161,	15173,	15187,	15193,	15199,	15217,	15227,	15233,	15241,	15259,
-	15263,	15269,	15271,	15277,	15287,	15289,	15299,	15307,	15313,	15319,
-	15329,	15331,	15349,	15359,	15361,	15373,	15377,	15383,	15391,	15401,
-	15413,	15427,	15439,	15443,	15451,	15461,	15467,	15473,	15493,	15497,
-	15511,	15527,	15541,	15551,	15559,	15569,	15581,	15583,	15601,	15607,
-	15619,	15629,	15641,	15643,	15647,	15649,	15661,	15667,	15671,	15679,
-	15683,	15727,	15731,	15733,	15737,	15739,	15749,	15761,	15767,	15773,
-	15787,	15791,	15797,	15803,	15809,	15817,	15823,	15859,	15877,	15881,
-	15887,	15889,	15901,	15907,	15913,	15919,	15923,	15937,	15959,	15971,
-	15973,	15991,	16001,	16007,	16033,	16057,	16061,	16063,	16067,	16069,
-	16073,	16087,	16091,	16097,	16103,	16111,	16127,	16139,	16141,	16183,
-	16187,	16189,	16193,	16217,	16223,	16229,	16231,	16249,	16253,	16267,
-	16273,	16301,	16319,	16333,	16339,	16349,	16361,	16363,	16369,	16381,
-	16411,	16417,	16421,	16427,	16433,	16447,	16451,	16453,	16477,	16481,
-	16487,	16493,	16519,	16529,	16547,	16553,	16561,	16567,	16573,	16603,
-	16607,	16619,	16631,	16633,	16649,	16651,	16657,	16661,	16673,	16691,
-	16693,	16699,	16703,	16729,	16741,	16747,	16759,	16763,	16787,	16811,
-	16823,	16829,	16831,	16843,	16871,	16879,	16883,	16889,	16901,	16903,
-	16921,	16927,	16931,	16937,	16943,	16963,	16979,	16981,	16987,	16993,
-	17011,	17021,	17027,	17029,	17033,	17041,	17047,	17053,	17077,	17093,
-	17099,	17107,	17117,	17123,	17137,	17159,	17167,	17183,	17189,	17191,
-	17203,	17207,	17209,	17231,	17239,	17257,	17291,	17293,	17299,	17317,
-	17321,	17327,	17333,	17341,	17351,	17359,	17377,	17383,	17387,	17389,
-	17393,	17401,	17417,	17419,	17431,	17443,	17449,	17467,	17471,	17477,
-	17483,	17489,	17491,	17497,	17509,	17519,	17539,	17551,	17569,	17573,
-	17579,	17581,	17597,	17599,	17609,	17623,	17627,	17657,	17659,	17669,
-	17681,	17683,	17707,	17713,	17729,	17737,	17747,	17749,	17761,	17783,
-	17789,	17791,	17807,	17827,	17837,	17839,	17851,	17863,	17881,	17891,
-	17903,	17909,	17911,	17921,	17923,	17929,	17939,	17957,	17959,	17971,
-	17977,	17981,	17987,	17989,	18013,	18041,	18043,	18047,	18049,	18059,
-	18061,	18077,	18089,	18097,	18119,	18121,	18127,	18131,	18133,	18143,
-	18149,	18169,	18181,	18191,	18199,	18211,	18217,	18223,	18229,	18233,
-	18251,	18253,	18257,	18269,	18287,	18289,	18301,	18307,	18311,	18313,
-	18329,	18341,	18353,	18367,	18371,	18379,	18397,	18401,	18413,	18427,
-	18433,	18439,	18443,	18451,	18457,	18461,	18481,	18493,	18503,	18517,
-	18521,	18523,	18539,	18541,	18553,	18583,	18587,	18593,	18617,	18637,
-	18661,	18671,	18679,	18691,	18701,	18713,	18719,	18731,	18743,	18749,
-	18757,	18773,	18787,	18793,	18797,	18803,	18839,	18859,	18869,	18899,
-	18911,	18913,	18917,	18919,	18947,	18959,	18973,	18979,	19001,	19009,
-	19013,	19031,	19037,	19051,	19069,	19073,	19079,	19081,	19087,	19121,
-	19139,	19141,	19157,	19163,	19181,	19183,	19207,	19211,	19213,	19219,
-	19231,	19237,	19249,	19259,	19267,	19273,	19289,	19301,	19309,	19319,
-	19333,	19373,	19379,	19381,	19387,	19391,	19403,	19417,	19421,	19423,
-	19427,	19429,	19433,	19441,	19447,	19457,	19463,	19469,	19471,	19477,
-	19483,	19489,	19501,	19507,	19531,	19541,	19543,	19553,	19559,	19571,
-	19577,	19583,	19597,	19603,	19609,	19661,	19681,	19687,	19697,	19699,
-	19709,	19717,	19727,	19739,	19751,	19753,	19759,	19763,	19777,	19793,
-	19801,	19813,	19819,	19841,	19843,	19853,	19861,	19867,	19889,	19891,
-	19913,	19919,	19927,	19937,	19949,	19961,	19963,	19973,	19979,	19991,
-	19993,	19997,	20011,	20021,	20023,	20029,	20047,	20051,	20063,	20071,
-	20089,	20101,	20107,	20113,	20117,	20123,	20129,	20143,	20147,	20149,
-	20161,	20173,	20177,	20183,	20201,	20219,	20231,	20233,	20249,	20261,
-	20269,	20287,	20297,	20323,	20327,	20333,	20341,	20347,	20353,	20357,
-	20359,	20369,	20389,	20393,	20399,	20407,	20411,	20431,	20441,	20443,
-	20477,	20479,	20483,	20507,	20509,	20521,	20533,	20543,	20549,	20551,
-	20563,	20593,	20599,	20611,	20627,	20639,	20641,	20663,	20681,	20693,
-	20707,	20717,	20719,	20731,	20743,	20747,	20749,	20753,	20759,	20771,
-	20773,	20789,	20807,	20809,	20849,	20857,	20873,	20879,	20887,	20897,
-	20899,	20903,	20921,	20929,	20939,	20947,	20959,	20963,	20981,	20983,
-	21001,	21011,	21013,	21017,	21019,	21023,	21031,	21059,	21061,	21067,
-	21089,	21101,	21107,	21121,	21139,	21143,	21149,	21157,	21163,	21169,
-	21179,	21187,	21191,	21193,	21211,	21221,	21227,	21247,	21269,	21277,
-	21283,	21313,	21317,	21319,	21323,	21341,	21347,	21377,	21379,	21383,
-	21391,	21397,	21401,	21407,	21419,	21433,	21467,	21481,	21487,	21491,
-	21493,	21499,	21503,	21517,	21521,	21523,	21529,	21557,	21559,	21563,
-	21569,	21577,	21587,	21589,	21599,	21601,	21611,	21613,	21617,	21647,
-	21649,	21661,	21673,	21683,	21701,	21713,	21727,	21737,	21739,	21751,
-	21757,	21767,	21773,	21787,	21799,	21803,	21817,	21821,	21839,	21841,
-	21851,	21859,	21863,	21871,	21881,	21893,	21911,	21929,	21937,	21943,
-	21961,	21977,	21991,	21997,	22003,	22013,	22027,	22031,	22037,	22039,
-	22051,	22063,	22067,	22073,	22079,	22091,	22093,	22109,	22111,	22123,
-	22129,	22133,	22147,	22153,	22157,	22159,	22171,	22189,	22193,	22229,
-	22247,	22259,	22271,	22273,	22277,	22279,	22283,	22291,	22303,	22307,
-	22343,	22349,	22367,	22369,	22381,	22391,	22397,	22409,	22433,	22441,
-	22447,	22453,	22469,	22481,	22483,	22501,	22511,	22531,	22541,	22543,
-	22549,	22567,	22571,	22573,	22613,	22619,	22621,	22637,	22639,	22643,
-	22651,	22669,	22679,	22691,	22697,	22699,	22709,	22717,	22721,	22727,
-	22739,	22741,	22751,	22769,	22777,	22783,	22787,	22807,	22811,	22817,
-	22853,	22859,	22861,	22871,	22877,	22901,	22907,	22921,	22937,	22943,
-	22961,	22963,	22973,	22993,	23003,	23011,	23017,	23021,	23027,	23029,
-	23039,	23041,	23053,	23057,	23059,	23063,	23071,	23081,	23087,	23099,
-	23117,	23131,	23143,	23159,	23167,	23173,	23189,	23197,	23201,	23203,
-	23209,	23227,	23251,	23269,	23279,	23291,	23293,	23297,	23311,	23321,
-	23327,	23333,	23339,	23357,	23369,	23371,	23399,	23417,	23431,	23447,
-	23459,	23473,	23497,	23509,	23531,	23537,	23539,	23549,	23557,	23561,
-	23563,	23567,	23581,	23593,	23599,	23603,	23609,	23623,	23627,	23629,
-	23633,	23663,	23669,	23671,	23677,	23687,	23689,	23719,	23741,	23743,
-	23747,	23753,	23761,	23767,	23773,	23789,	23801,	23813,	23819,	23827,
-	23831,	23833,	23857,	23869,	23873,	23879,	23887,	23893,	23899,	23909,
-	23911,	23917,	23929,	23957,	23971,	23977,	23981,	23993,	24001,	24007,
-	24019,	24023,	24029,	24043,	24049,	24061,	24071,	24077,	24083,	24091,
-	24097,	24103,	24107,	24109,	24113,	24121,	24133,	24137,	24151,	24169,
-	24179,	24181,	24197,	24203,	24223,	24229,	24239,	24247,	24251,	24281,
-	24317,	24329,	24337,	24359,	24371,	24373,	24379,	24391,	24407,	24413,
-	24419,	24421,	24439,	24443,	24469,	24473,	24481,	24499,	24509,	24517,
-	24527,	24533,	24547,	24551,	24571,	24593,	24611,	24623,	24631,	24659,
-	24671,	24677,	24683,	24691,	24697,	24709,	24733,	24749,	24763,	24767,
-	24781,	24793,	24799,	24809,	24821,	24841,	24847,	24851,	24859,	24877,
-	24889,	24907,	24917,	24919,	24923,	24943,	24953,	24967,	24971,	24977,
-	24979,	24989,	25013,	25031,	25033,	25037,	25057,	25073,	25087,	25097,
-	25111,	25117,	25121,	25127,	25147,	25153,	25163,	25169,	25171,	25183,
-	25189,	25219,	25229,	25237,	25243,	25247,	25253,	25261,	25301,	25303,
-	25307,	25309,	25321,	25339,	25343,	25349,	25357,	25367,	25373,	25391,
-	25409,	25411,	25423,	25439,	25447,	25453,	25457,	25463,	25469,	25471,
-	25523,	25537,	25541,	25561,	25577,	25579,	25583,	25589,	25601,	25603,
-	25609,	25621,	25633,	25639,	25643,	25657,	25667,	25673,	25679,	25693,
-	25703,	25717,	25733,	25741,	25747,	25759,	25763,	25771,	25793,	25799,
-	25801,	25819,	25841,	25847,	25849,	25867,	25873,	25889,	25903,	25913,
-	25919,	25931,	25933,	25939,	25943,	25951,	25969,	25981,	25997,	25999,
-	26003,	26017,	26021,	26029,	26041,	26053,	26083,	26099,	26107,	26111,
-	26113,	26119,	26141,	26153,	26161,	26171,	26177,	26183,	26189,	26203,
-	26209,	26227,	26237,	26249,	26251,	26261,	26263,	26267,	26293,	26297,
-	26309,	26317,	26321,	26339,	26347,	26357,	26371,	26387,	26393,	26399,
-	26407,	26417,	26423,	26431,	26437,	26449,	26459,	26479,	26489,	26497,
-	26501,	26513,	26539,	26557,	26561,	26573,	26591,	26597,	26627,	26633,
-	26641,	26647,	26669,	26681,	26683,	26687,	26693,	26699,	26701,	26711,
-	26713,	26717,	26723,	26729,	26731,	26737,	26759,	26777,	26783,	26801,
-	26813,	26821,	26833,	26839,	26849,	26861,	26863,	26879,	26881,	26891,
-	26893,	26903,	26921,	26927,	26947,	26951,	26953,	26959,	26981,	26987,
-	26993,	27011,	27017,	27031,	27043,	27059,	27061,	27067,	27073,	27077,
-	27091,	27103,	27107,	27109,	27127,	27143,	27179,	27191,	27197,	27211,
-	27239,	27241,	27253,	27259,	27271,	27277,	27281,	27283,	27299,	27329,
-	27337,	27361,	27367,	27397,	27407,	27409,	27427,	27431,	27437,	27449,
-	27457,	27479,	27481,	27487,	27509,	27527,	27529,	27539,	27541,	27551,
-	27581,	27583,	27611,	27617,	27631,	27647,	27653,	27673,	27689,	27691,
-	27697,	27701,	27733,	27737,	27739,	27743,	27749,	27751,	27763,	27767,
-	27773,	27779,	27791,	27793,	27799,	27803,	27809,	27817,	27823,	27827,
-	27847,	27851,	27883,	27893,	27901,	27917,	27919,	27941,	27943,	27947,
-	27953,	27961,	27967,	27983,	27997,	28001,	28019,	28027,	28031,	28051,
-	28057,	28069,	28081,	28087,	28097,	28099,	28109,	28111,	28123,	28151,
-	28163,	28181,	28183,	28201,	28211,	28219,	28229,	28277,	28279,	28283,
-	28289,	28297,	28307,	28309,	28319,	28349,	28351,	28387,	28393,	28403,
-	28409,	28411,	28429,	28433,	28439,	28447,	28463,	28477,	28493,	28499,
-	28513,	28517,	28537,	28541,	28547,	28549,	28559,	28571,	28573,	28579,
-	28591,	28597,	28603,	28607,	28619,	28621,	28627,	28631,	28643,	28649,
-	28657,	28661,	28663,	28669,	28687,	28697,	28703,	28711,	28723,	28729,
-	28751,	28753,	28759,	28771,	28789,	28793,	28807,	28813,	28817,	28837,
-	28843,	28859,	28867,	28871,	28879,	28901,	28909,	28921,	28927,	28933,
-	28949,	28961,	28979,	29009,	29017,	29021,	29023,	29027,	29033,	29059,
-	29063,	29077,	29101,	29123,	29129,	29131,	29137,	29147,	29153,	29167,
-	29173,	29179,	29191,	29201,	29207,	29209,	29221,	29231,	29243,	29251,
-	29269,	29287,	29297,	29303,	29311,	29327,	29333,	29339,	29347,	29363,
-	29383,	29387,	29389,	29399,	29401,	29411,	29423,	29429,	29437,	29443,
-	29453,	29473,	29483,	29501,	29527,	29531,	29537,	29567,	29569,	29573,
-	29581,	29587,	29599,	29611,	29629,	29633,	29641,	29663,	29669,	29671,
-	29683,	29717,	29723,	29741,	29753,	29759,	29761,	29789,	29803,	29819,
-	29833,	29837,	29851,	29863,	29867,	29873,	29879,	29881,	29917,	29921,
-	29927,	29947,	29959,	29983,	29989,	30011,	30013,	30029,	30047,	30059,
-	30071,	30089,	30091,	30097,	30103,	30109,	30113,	30119,	30133,	30137,
-	30139,	30161,	30169,	30181,	30187,	30197,	30203,	30211,	30223,	30241,
-	30253,	30259,	30269,	30271,	30293,	30307,	30313,	30319,	30323,	30341,
-	30347,	30367,	30389,	30391,	30403,	30427,	30431,	30449,	30467,	30469,
-	30491,	30493,	30497,	30509,	30517,	30529,	30539,	30553,	30557,	30559,
-	30577,	30593,	30631,	30637,	30643,	30649,	30661,	30671,	30677,	30689,
-	30697,	30703,	30707,	30713,	30727,	30757,	30763,	30773,	30781,	30803,
-	30809,	30817,	30829,	30839,	30841,	30851,	30853,	30859,	30869,	30871,
-	30881,	30893,	30911,	30931,	30937,	30941,	30949,	30971,	30977,	30983,
-	31013,	31019,	31033,	31039,	31051,	31063,	31069,	31079,	31081,	31091,
-	31121,	31123,	31139,	31147,	31151,	31153,	31159,	31177,	31181,	31183,
-	31189,	31193,	31219,	31223,	31231,	31237,	31247,	31249,	31253,	31259,
-	31267,	31271,	31277,	31307,	31319,	31321,	31327,	31333,	31337,	31357,
-	31379,	31387,	31391,	31393,	31397,	31469,	31477,	31481,	31489,	31511,
-	31513,	31517,	31531,	31541,	31543,	31547,	31567,	31573,	31583,	31601,
-	31607,	31627,	31643,	31649,	31657,	31663,	31667,	31687,	31699,	31721,
-	31723,	31727,	31729,	31741,	31751,	31769,	31771,	31793,	31799,	31817,
-	31847,	31849,	31859,	31873,	31883,	31891,	31907,	31957,	31963,	31973,
-	31981,	31991,	32003,	32009,	32027,	32029,	32051,	32057,	32059,	32063,
-	32069,	32077,	32083,	32089,	32099,	32117,	32119,	32141,	32143,	32159,
-	32173,	32183,	32189,	32191,	32203,	32213,	32233,	32237,	32251,	32257,
-	32261,	32297,	32299,	32303,	32309,	32321,	32323,	32327,	32341,	32353,
-	32359,	32363,	32369,	32371,	32377,	32381,	32401,	32411,	32413,	32423,
-	32429,	32441,	32443,	32467,	32479,	32491,	32497,	32503,	32507,	32531,
-	32533,	32537,	32561,	32563,	32569,	32573,	32579,	32587,	32603,	32609,
-	32611,	32621,	32633,	32647,	32653,	32687,	32693,	32707,	32713,	32717,
-	32719,	32749,	32771,	32779,	32783,	32789,	32797,	32801,	32803,	32831,
-	32833,	32839,	32843,	32869,	32887,	32909,	32911,	32917,	32933,	32939,
-	32941,	32957,	32969,	32971,	32983,	32987,	32993,	32999,	33013,	33023,
-	33029,	33037,	33049,	33053,	33071,	33073,	33083,	33091,	33107,	33113,
-	33119,	33149,	33151,	33161,	33179,	33181,	33191,	33199,	33203,	33211,
-	33223,	33247,	33287,	33289,	33301,	33311,	33317,	33329,	33331,	33343,
-	33347,	33349,	33353,	33359,	33377,	33391,	33403,	33409,	33413,	33427,
-	33457,	33461,	33469,	33479,	33487,	33493,	33503,	33521,	33529,	33533,
-	33547,	33563,	33569,	33577,	33581,	33587,	33589,	33599,	33601,	33613,
-	33617,	33619,	33623,	33629,	33637,	33641,	33647,	33679,	33703,	33713,
-	33721,	33739,	33749,	33751,	33757,	33767,	33769,	33773,	33791,	33797,
-	33809,	33811,	33827,	33829,	33851,	33857,	33863,	33871,	33889,	33893,
-	33911,	33923,	33931,	33937,	33941,	33961,	33967,	33997,	34019,	34031,
-	34033,	34039,	34057,	34061,	34123,	34127,	34129,	34141,	34147,	34157,
-	34159,	34171,	34183,	34211,	34213,	34217,	34231,	34253,	34259,	34261,
-	34267,	34273,	34283,	34297,	34301,	34303,	34313,	34319,	34327,	34337,
-	34351,	34361,	34367,	34369,	34381,	34403,	34421,	34429,	34439,	34457,
-	34469,	34471,	34483,	34487,	34499,	34501,	34511,	34513,	34519,	34537,
-	34543,	34549,	34583,	34589,	34591,	34603,	34607,	34613,	34631,	34649,
-	34651,	34667,	34673,	34679,	34687,	34693,	34703,	34721,	34729,	34739,
-	34747,	34757,	34759,	34763,	34781,	34807,	34819,	34841,	34843,	34847,
-	34849,	34871,	34877,	34883,	34897,	34913,	34919,	34939,	34949,	34961,
-	34963,	34981,	35023,	35027,	35051,	35053,	35059,	35069,	35081,	35083,
-	35089,	35099,	35107,	35111,	35117,	35129,	35141,	35149,	35153,	35159,
-	35171,	35201,	35221,	35227,	35251,	35257,	35267,	35279,	35281,	35291,
-	35311,	35317,	35323,	35327,	35339,	35353,	35363,	35381,	35393,	35401,
-	35407,	35419,	35423,	35437,	35447,	35449,	35461,	35491,	35507,	35509,
-	35521,	35527,	35531,	35533,	35537,	35543,	35569,	35573,	35591,	35593,
-	35597,	35603,	35617,	35671,	35677,	35729,	35731,	35747,	35753,	35759,
-	35771,	35797,	35801,	35803,	35809,	35831,	35837,	35839,	35851,	35863,
-	35869,	35879,	35897,	35899,	35911,	35923,	35933,	35951,	35963,	35969,
-	35977,	35983,	35993,	35999,	36007,	36011,	36013,	36017,	36037,	36061,
-	36067,	36073,	36083,	36097,	36107,	36109,	36131,	36137,	36151,	36161,
-	36187,	36191,	36209,	36217,	36229,	36241,	36251,	36263,	36269,	36277,
-	36293,	36299,	36307,	36313,	36319,	36341,	36343,	36353,	36373,	36383,
-	36389,	36433,	36451,	36457,	36467,	36469,	36473,	36479,	36493,	36497,
-	36523,	36527,	36529,	36541,	36551,	36559,	36563,	36571,	36583,	36587,
-	36599,	36607,	36629,	36637,	36643,	36653,	36671,	36677,	36683,	36691,
-	36697,	36709,	36713,	36721,	36739,	36749,	36761,	36767,	36779,	36781,
-	36787,	36791,	36793,	36809,	36821,	36833,	36847,	36857,	36871,	36877,
-	36887,	36899,	36901,	36913,	36919,	36923,	36929,	36931,	36943,	36947,
-	36973,	36979,	36997,	37003,	37013,	37019,	37021,	37039,	37049,	37057,
-	37061,	37087,	37097,	37117,	37123,	37139,	37159,	37171,	37181,	37189,
-	37199,	37201,	37217,	37223,	37243,	37253,	37273,	37277,	37307,	37309,
-	37313,	37321,	37337,	37339,	37357,	37361,	37363,	37369,	37379,	37397,
-	37409,	37423,	37441,	37447,	37463,	37483,	37489,	37493,	37501,	37507,
-	37511,	37517,	37529,	37537,	37547,	37549,	37561,	37567,	37571,	37573,
-	37579,	37589,	37591,	37607,	37619,	37633,	37643,	37649,	37657,	37663,
-	37691,	37693,	37699,	37717,	37747,	37781,	37783,	37799,	37811,	37813,
-	37831,	37847,	37853,	37861,	37871,	37879,	37889,	37897,	37907,	37951,
-	37957,	37963,	37967,	37987,	37991,	37993,	37997,	38011,	38039,	38047,
-	38053,	38069,	38083,	38113,	38119,	38149,	38153,	38167,	38177,	38183,
-	38189,	38197,	38201,	38219,	38231,	38237,	38239,	38261,	38273,	38281,
-	38287,	38299,	38303,	38317,	38321,	38327,	38329,	38333,	38351,	38371,
-	38377,	38393,	38431,	38447,	38449,	38453,	38459,	38461,	38501,	38543,
-	38557,	38561,	38567,	38569,	38593,	38603,	38609,	38611,	38629,	38639,
-	38651,	38653,	38669,	38671,	38677,	38693,	38699,	38707,	38711,	38713,
-	38723,	38729,	38737,	38747,	38749,	38767,	38783,	38791,	38803,	38821,
-	38833,	38839,	38851,	38861,	38867,	38873,	38891,	38903,	38917,	38921,
-	38923,	38933,	38953,	38959,	38971,	38977,	38993,	39019,	39023,	39041,
-	39043,	39047,	39079,	39089,	39097,	39103,	39107,	39113,	39119,	39133,
-	39139,	39157,	39161,	39163,	39181,	39191,	39199,	39209,	39217,	39227,
-	39229,	39233,	39239,	39241,	39251,	39293,	39301,	39313,	39317,	39323,
-	39341,	39343,	39359,	39367,	39371,	39373,	39383,	39397,	39409,	39419,
-	39439,	39443,	39451,	39461,	39499,	39503,	39509,	39511,	39521,	39541,
-	39551,	39563,	39569,	39581,	39607,	39619,	39623,	39631,	39659,	39667,
-	39671,	39679,	39703,	39709,	39719,	39727,	39733,	39749,	39761,	39769,
-	39779,	39791,	39799,	39821,	39827,	39829,	39839,	39841,	39847,	39857,
-	39863,	39869,	39877,	39883,	39887,	39901,	39929,	39937,	39953,	39971,
-	39979,	39983,	39989,	40009,	40013,	40031,	40037,	40039,	40063,	40087,
-	40093,	40099,	40111,	40123,	40127,	40129,	40151,	40153,	40163,	40169,
-	40177,	40189,	40193,	40213,	40231,	40237,	40241,	40253,	40277,	40283,
-	40289,	40343,	40351,	40357,	40361,	40387,	40423,	40427,	40429,	40433,
-	40459,	40471,	40483,	40487,	40493,	40499,	40507,	40519,	40529,	40531,
-	40543,	40559,	40577,	40583,	40591,	40597,	40609,	40627,	40637,	40639,
-	40693,	40697,	40699,	40709,	40739,	40751,	40759,	40763,	40771,	40787,
-	40801,	40813,	40819,	40823,	40829,	40841,	40847,	40849,	40853,	40867,
-	40879,	40883,	40897,	40903,	40927,	40933,	40939,	40949,	40961,	40973,
-	40993,	41011,	41017,	41023,	41039,	41047,	41051,	41057,	41077,	41081,
-	41113,	41117,	41131,	41141,	41143,	41149,	41161,	41177,	41179,	41183,
-	41189,	41201,	41203,	41213,	41221,	41227,	41231,	41233,	41243,	41257,
-	41263,	41269,	41281,	41299,	41333,	41341,	41351,	41357,	41381,	41387,
-	41389,	41399,	41411,	41413,	41443,	41453,	41467,	41479,	41491,	41507,
-	41513,	41519,	41521,	41539,	41543,	41549,	41579,	41593,	41597,	41603,
-	41609,	41611,	41617,	41621,	41627,	41641,	41647,	41651,	41659,	41669,
-	41681,	41687,	41719,	41729,	41737,	41759,	41761,	41771,	41777,	41801,
-	41809,	41813,	41843,	41849,	41851,	41863,	41879,	41887,	41893,	41897,
-	41903,	41911,	41927,	41941,	41947,	41953,	41957,	41959,	41969,	41981,
-	41983,	41999,	42013,	42017,	42019,	42023,	42043,	42061,	42071,	42073,
-	42083,	42089,	42101,	42131,	42139,	42157,	42169,	42179,	42181,	42187,
-	42193,	42197,	42209,	42221,	42223,	42227,	42239,	42257,	42281,	42283,
-	42293,	42299,	42307,	42323,	42331,	42337,	42349,	42359,	42373,	42379,
-	42391,	42397,	42403,	42407,	42409,	42433,	42437,	42443,	42451,	42457,
-	42461,	42463,	42467,	42473,	42487,	42491,	42499,	42509,	42533,	42557,
-	42569,	42571,	42577,	42589,	42611,	42641,	42643,	42649,	42667,	42677,
-	42683,	42689,	42697,	42701,	42703,	42709,	42719,	42727,	42737,	42743,
-	42751,	42767,	42773,	42787,	42793,	42797,	42821,	42829,	42839,	42841,
-	42853,	42859,	42863,	42899,	42901,	42923,	42929,	42937,	42943,	42953,
-	42961,	42967,	42979,	42989,	43003,	43013,	43019,	43037,	43049,	43051,
-	43063,	43067,	43093,	43103,	43117,	43133,	43151,	43159,	43177,	43189,
-	43201,	43207,	43223,	43237,	43261,	43271,	43283,	43291,	43313,	43319,
-	43321,	43331,	43391,	43397,	43399,	43403,	43411,	43427,	43441,	43451,
-	43457,	43481,	43487,	43499,	43517,	43541,	43543,	43573,	43577,	43579,
-	43591,	43597,	43607,	43609,	43613,	43627,	43633,	43649,	43651,	43661,
-	43669,	43691,	43711,	43717,	43721,	43753,	43759,	43777,	43781,	43783,
-	43787,	43789,	43793,	43801,	43853,	43867,	43889,	43891,	43913,	43933,
-	43943,	43951,	43961,	43963,	43969,	43973,	43987,	43991,	43997,	44017,
-	44021,	44027,	44029,	44041,	44053,	44059,	44071,	44087,	44089,	44101,
-	44111,	44119,	44123,	44129,	44131,	44159,	44171,	44179,	44189,	44201,
-	44203,	44207,	44221,	44249,	44257,	44263,	44267,	44269,	44273,	44279,
-	44281,	44293,	44351,	44357,	44371,	44381,	44383,	44389,	44417,	44449,
-	44453,	44483,	44491,	44497,	44501,	44507,	44519,	44531,	44533,	44537,
-	44543,	44549,	44563,	44579,	44587,	44617,	44621,	44623,	44633,	44641,
-	44647,	44651,	44657,	44683,	44687,	44699,	44701,	44711,	44729,	44741,
-	44753,	44771,	44773,	44777,	44789,	44797,	44809,	44819,	44839,	44843,
-	44851,	44867,	44879,	44887,	44893,	44909,	44917,	44927,	44939,	44953,
-	44959,	44963,	44971,	44983,	44987,	45007,	45013,	45053,	45061,	45077,
-	45083,	45119,	45121,	45127,	45131,	45137,	45139,	45161,	45179,	45181,
-	45191,	45197,	45233,	45247,	45259,	45263,	45281,	45289,	45293,	45307,
-	45317,	45319,	45329,	45337,	45341,	45343,	45361,	45377,	45389,	45403,
-	45413,	45427,	45433,	45439,	45481,	45491,	45497,	45503,	45523,	45533,
-	45541,	45553,	45557,	45569,	45587,	45589,	45599,	45613,	45631,	45641,
-	45659,	45667,	45673,	45677,	45691,	45697,	45707,	45737,	45751,	45757,
-	45763,	45767,	45779,	45817,	45821,	45823,	45827,	45833,	45841,	45853,
-	45863,	45869,	45887,	45893,	45943,	45949,	45953,	45959,	45971,	45979,
-	45989,	46021,	46027,	46049,	46051,	46061,	46073,	46091,	46093,	46099,
-	46103,	46133,	46141,	46147,	46153,	46171,	46181,	46183,	46187,	46199,
-	46219,	46229,	46237,	46261,	46271,	46273,	46279,	46301,	46307,	46309,
-	46327,	46337,	46349,	46351,	46381,	46399,	46411,	46439,	46441,	46447,
-	46451,	46457,	46471,	46477,	46489,	46499,	46507,	46511,	46523,	46549,
-	46559,	46567,	46573,	46589,	46591,	46601,	46619,	46633,	46639,	46643,
-	46649,	46663,	46679,	46681,	46687,	46691,	46703,	46723,	46727,	46747,
-	46751,	46757,	46769,	46771,	46807,	46811,	46817,	46819,	46829,	46831,
-	46853,	46861,	46867,	46877,	46889,	46901,	46919,	46933,	46957,	46993,
-	46997,	47017,	47041,	47051,	47057,	47059,	47087,	47093,	47111,	47119,
-	47123,	47129,	47137,	47143,	47147,	47149,	47161,	47189,	47207,	47221,
-	47237,	47251,	47269,	47279,	47287,	47293,	47297,	47303,	47309,	47317,
-	47339,	47351,	47353,	47363,	47381,	47387,	47389,	47407,	47417,	47419,
-	47431,	47441,	47459,	47491,	47497,	47501,	47507,	47513,	47521,	47527,
-	47533,	47543,	47563,	47569,	47581,	47591,	47599,	47609,	47623,	47629,
-	47639,	47653,	47657,	47659,	47681,	47699,	47701,	47711,	47713,	47717,
-	47737,	47741,	47743,	47777,	47779,	47791,	47797,	47807,	47809,	47819,
-	47837,	47843,	47857,	47869,	47881,	47903,	47911,	47917,	47933,	47939,
-	47947,	47951,	47963,	47969,	47977,	47981,	48017,	48023,	48029,	48049,
-	48073,	48079,	48091,	48109,	48119,	48121,	48131,	48157,	48163,	48179,
-	48187,	48193,	48197,	48221,	48239,	48247,	48259,	48271,	48281,	48299,
-	48311,	48313,	48337,	48341,	48353,	48371,	48383,	48397,	48407,	48409,
-	48413,	48437,	48449,	48463,	48473,	48479,	48481,	48487,	48491,	48497,
-	48523,	48527,	48533,	48539,	48541,	48563,	48571,	48589,	48593,	48611,
-	48619,	48623,	48647,	48649,	48661,	48673,	48677,	48679,	48731,	48733,
-	48751,	48757,	48761,	48767,	48779,	48781,	48787,	48799,	48809,	48817,
-	48821,	48823,	48847,	48857,	48859,	48869,	48871,	48883,	48889,	48907,
-	48947,	48953,	48973,	48989,	48991,	49003,	49009,	49019,	49031,	49033,
-	49037,	49043,	49057,	49069,	49081,	49103,	49109,	49117,	49121,	49123,
-	49139,	49157,	49169,	49171,	49177,	49193,	49199,	49201,	49207,	49211,
-	49223,	49253,	49261,	49277,	49279,	49297,	49307,	49331,	49333,	49339,
-	49363,	49367,	49369,	49391,	49393,	49409,	49411,	49417,	49429,	49433,
-	49451,	49459,	49463,	49477,	49481,	49499,	49523,	49529,	49531,	49537,
-	49547,	49549,	49559,	49597,	49603,	49613,	49627,	49633,	49639,	49663,
-	49667,	49669,	49681,	49697,	49711,	49727,	49739,	49741,	49747,	49757,
-	49783,	49787,	49789,	49801,	49807,	49811,	49823,	49831,	49843,	49853,
-	49871,	49877,	49891,	49919,	49921,	49927,	49937,	49939,	49943,	49957,
-	49991,	49993,	49999,	50021,	50023,	50033,	50047,	50051,	50053,	50069,
-	50077,	50087,	50093,	50101,	50111,	50119,	50123,	50129,	50131,	50147,
-	50153,	50159,	50177,	50207,	50221,	50227,	50231,	50261,	50263,	50273,
-	50287,	50291,	50311,	50321,	50329,	50333,	50341,	50359,	50363,	50377,
-	50383,	50387,	50411,	50417,	50423,	50441,	50459,	50461,	50497,	50503,
-	50513,	50527,	50539,	50543,	50549,	50551,	50581,	50587,	50591,	50593,
-	50599,	50627,	50647,	50651,	50671,	50683,	50707,	50723,	50741,	50753,
-	50767,	50773,	50777,	50789,	50821,	50833,	50839,	50849,	50857,	50867,
-	50873,	50891,	50893,	50909,	50923,	50929,	50951,	50957,	50969,	50971,
-	50989,	50993,	51001,	51031,	51043,	51047,	51059,	51061,	51071,	51109,
-	51131,	51133,	51137,	51151,	51157,	51169,	51193,	51197,	51199,	51203,
-	51217,	51229,	51239,	51241,	51257,	51263,	51283,	51287,	51307,	51329,
-	51341,	51343,	51347,	51349,	51361,	51383,	51407,	51413,	51419,	51421,
-	51427,	51431,	51437,	51439,	51449,	51461,	51473,	51479,	51481,	51487,
-	51503,	51511,	51517,	51521,	51539,	51551,	51563,	51577,	51581,	51593,
-	51599,	51607,	51613,	51631,	51637,	51647,	51659,	51673,	51679,	51683,
-	51691,	51713,	51719,	51721,	51749,	51767,	51769,	51787,	51797,	51803,
-	51817,	51827,	51829,	51839,	51853,	51859,	51869,	51871,	51893,	51899,
-	51907,	51913,	51929,	51941,	51949,	51971,	51973,	51977,	51991,	52009,
-	52021,	52027,	52051,	52057,	52067,	52069,	52081,	52103,	52121,	52127,
-	52147,	52153,	52163,	52177,	52181,	52183,	52189,	52201,	52223,	52237,
-	52249,	52253,	52259,	52267,	52289,	52291,	52301,	52313,	52321,	52361,
-	52363,	52369,	52379,	52387,	52391,	52433,	52453,	52457,	52489,	52501,
-	52511,	52517,	52529,	52541,	52543,	52553,	52561,	52567,	52571,	52579,
-	52583,	52609,	52627,	52631,	52639,	52667,	52673,	52691,	52697,	52709,
-	52711,	52721,	52727,	52733,	52747,	52757,	52769,	52783,	52807,	52813,
-	52817,	52837,	52859,	52861,	52879,	52883,	52889,	52901,	52903,	52919,
-	52937,	52951,	52957,	52963,	52967,	52973,	52981,	52999,	53003,	53017,
-	53047,	53051,	53069,	53077,	53087,	53089,	53093,	53101,	53113,	53117,
-	53129,	53147,	53149,	53161,	53171,	53173,	53189,	53197,	53201,	53231,
-	53233,	53239,	53267,	53269,	53279,	53281,	53299,	53309,	53323,	53327,
-	53353,	53359,	53377,	53381,	53401,	53407,	53411,	53419,	53437,	53441,
-	53453,	53479,	53503,	53507,	53527,	53549,	53551,	53569,	53591,	53593,
-	53597,	53609,	53611,	53617,	53623,	53629,	53633,	53639,	53653,	53657,
-	53681,	53693,	53699,	53717,	53719,	53731,	53759,	53773,	53777,	53783,
-	53791,	53813,	53819,	53831,	53849,	53857,	53861,	53881,	53887,	53891,
-	53897,	53899,	53917,	53923,	53927,	53939,	53951,	53959,	53987,	53993,
-	54001,	54011,	54013,	54037,	54049,	54059,	54083,	54091,	54101,	54121,
-	54133,	54139,	54151,	54163,	54167,	54181,	54193,	54217,	54251,	54269,
-	54277,	54287,	54293,	54311,	54319,	54323,	54331,	54347,	54361,	54367,
-	54371,	54377,	54401,	54403,	54409,	54413,	54419,	54421,	54437,	54443,
-	54449,	54469,	54493,	54497,	54499,	54503,	54517,	54521,	54539,	54541,
-	54547,	54559,	54563,	54577,	54581,	54583,	54601,	54617,	54623,	54629,
-	54631,	54647,	54667,	54673,	54679,	54709,	54713,	54721,	54727,	54751,
-	54767,	54773,	54779,	54787,	54799,	54829,	54833,	54851,	54869,	54877,
-	54881,	54907,	54917,	54919,	54941,	54949,	54959,	54973,	54979,	54983,
-	55001,	55009,	55021,	55049,	55051,	55057,	55061,	55073,	55079,	55103,
-	55109,	55117,	55127,	55147,	55163,	55171,	55201,	55207,	55213,	55217,
-	55219,	55229,	55243,	55249,	55259,	55291,	55313,	55331,	55333,	55337,
-	55339,	55343,	55351,	55373,	55381,	55399,	55411,	55439,	55441,	55457,
-	55469,	55487,	55501,	55511,	55529,	55541,	55547,	55579,	55589,	55603,
-	55609,	55619,	55621,	55631,	55633,	55639,	55661,	55663,	55667,	55673,
-	55681,	55691,	55697,	55711,	55717,	55721,	55733,	55763,	55787,	55793,
-	55799,	55807,	55813,	55817,	55819,	55823,	55829,	55837,	55843,	55849,
-	55871,	55889,	55897,	55901,	55903,	55921,	55927,	55931,	55933,	55949,
-	55967,	55987,	55997,	56003,	56009,	56039,	56041,	56053,	56081,	56087,
-	56093,	56099,	56101,	56113,	56123,	56131,	56149,	56167,	56171,	56179,
-	56197,	56207,	56209,	56237,	56239,	56249,	56263,	56267,	56269,	56299,
-	56311,	56333,	56359,	56369,	56377,	56383,	56393,	56401,	56417,	56431,
-	56437,	56443,	56453,	56467,	56473,	56477,	56479,	56489,	56501,	56503,
-	56509,	56519,	56527,	56531,	56533,	56543,	56569,	56591,	56597,	56599,
-	56611,	56629,	56633,	56659,	56663,	56671,	56681,	56687,	56701,	56711,
-	56713,	56731,	56737,	56747,	56767,	56773,	56779,	56783,	56807,	56809,
-	56813,	56821,	56827,	56843,	56857,	56873,	56891,	56893,	56897,	56909,
-	56911,	56921,	56923,	56929,	56941,	56951,	56957,	56963,	56983,	56989,
-	56993,	56999,	57037,	57041,	57047,	57059,	57073,	57077,	57089,	57097,
-	57107,	57119,	57131,	57139,	57143,	57149,	57163,	57173,	57179,	57191,
-	57193,	57203,	57221,	57223,	57241,	57251,	57259,	57269,	57271,	57283,
-	57287,	57301,	57329,	57331,	57347,	57349,	57367,	57373,	57383,	57389,
-	57397,	57413,	57427,	57457,	57467,	57487,	57493,	57503,	57527,	57529,
-	57557,	57559,	57571,	57587,	57593,	57601,	57637,	57641,	57649,	57653,
-	57667,	57679,	57689,	57697,	57709,	57713,	57719,	57727,	57731,	57737,
-	57751,	57773,	57781,	57787,	57791,	57793,	57803,	57809,	57829,	57839,
-	57847,	57853,	57859,	57881,	57899,	57901,	57917,	57923,	57943,	57947,
-	57973,	57977,	57991,	58013,	58027,	58031,	58043,	58049,	58057,	58061,
-	58067,	58073,	58099,	58109,	58111,	58129,	58147,	58151,	58153,	58169,
-	58171,	58189,	58193,	58199,	58207,	58211,	58217,	58229,	58231,	58237,
-	58243,	58271,	58309,	58313,	58321,	58337,	58363,	58367,	58369,	58379,
-	58391,	58393,	58403,	58411,	58417,	58427,	58439,	58441,	58451,	58453,
-	58477,	58481,	58511,	58537,	58543,	58549,	58567,	58573,	58579,	58601,
-	58603,	58613,	58631,	58657,	58661,	58679,	58687,	58693,	58699,	58711,
-	58727,	58733,	58741,	58757,	58763,	58771,	58787,	58789,	58831,	58889,
-	58897,	58901,	58907,	58909,	58913,	58921,	58937,	58943,	58963,	58967,
-	58979,	58991,	58997,	59009,	59011,	59021,	59023,	59029,	59051,	59053,
-	59063,	59069,	59077,	59083,	59093,	59107,	59113,	59119,	59123,	59141,
-	59149,	59159,	59167,	59183,	59197,	59207,	59209,	59219,	59221,	59233,
-	59239,	59243,	59263,	59273,	59281,	59333,	59341,	59351,	59357,	59359,
-	59369,	59377,	59387,	59393,	59399,	59407,	59417,	59419,	59441,	59443,
-	59447,	59453,	59467,	59471,	59473,	59497,	59509,	59513,	59539,	59557,
-	59561,	59567,	59581,	59611,	59617,	59621,	59627,	59629,	59651,	59659,
-	59663,	59669,	59671,	59693,	59699,	59707,	59723,	59729,	59743,	59747,
-	59753,	59771,	59779,	59791,	59797,	59809,	59833,	59863,	59879,	59887,
-	59921,	59929,	59951,	59957,	59971,	59981,	59999,	60013,	60017,	60029,
-	60037,	60041,	60077,	60083,	60089,	60091,	60101,	60103,	60107,	60127,
-	60133,	60139,	60149,	60161,	60167,	60169,	60209,	60217,	60223,	60251,
-	60257,	60259,	60271,	60289,	60293,	60317,	60331,	60337,	60343,	60353,
-	60373,	60383,	60397,	60413,	60427,	60443,	60449,	60457,	60493,	60497,
-	60509,	60521,	60527,	60539,	60589,	60601,	60607,	60611,	60617,	60623,
-	60631,	60637,	60647,	60649,	60659,	60661,	60679,	60689,	60703,	60719,
-	60727,	60733,	60737,	60757,	60761,	60763,	60773,	60779,	60793,	60811,
-	60821,	60859,	60869,	60887,	60889,	60899,	60901,	60913,	60917,	60919,
-	60923,	60937,	60943,	60953,	60961,	61001,	61007,	61027,	61031,	61043,
-	61051,	61057,	61091,	61099,	61121,	61129,	61141,	61151,	61153,	61169,
-	61211,	61223,	61231,	61253,	61261,	61283,	61291,	61297,	61331,	61333,
-	61339,	61343,	61357,	61363,	61379,	61381,	61403,	61409,	61417,	61441,
-	61463,	61469,	61471,	61483,	61487,	61493,	61507,	61511,	61519,	61543,
-	61547,	61553,	61559,	61561,	61583,	61603,	61609,	61613,	61627,	61631,
-	61637,	61643,	61651,	61657,	61667,	61673,	61681,	61687,	61703,	61717,
-	61723,	61729,	61751,	61757,	61781,	61813,	61819,	61837,	61843,	61861,
-	61871,	61879,	61909,	61927,	61933,	61949,	61961,	61967,	61979,	61981,
-	61987,	61991,	62003,	62011,	62017,	62039,	62047,	62053,	62057,	62071,
-	62081,	62099,	62119,	62129,	62131,	62137,	62141,	62143,	62171,	62189,
-	62191,	62201,	62207,	62213,	62219,	62233,	62273,	62297,	62299,	62303,
-	62311,	62323,	62327,	62347,	62351,	62383,	62401,	62417,	62423,	62459,
-	62467,	62473,	62477,	62483,	62497,	62501,	62507,	62533,	62539,	62549,
-	62563,	62581,	62591,	62597,	62603,	62617,	62627,	62633,	62639,	62653,
-	62659,	62683,	62687,	62701,	62723,	62731,	62743,	62753,	62761,	62773,
-	62791,	62801,	62819,	62827,	62851,	62861,	62869,	62873,	62897,	62903,
-	62921,	62927,	62929,	62939,	62969,	62971,	62981,	62983,	62987,	62989,
-	63029,	63031,	63059,	63067,	63073,	63079,	63097,	63103,	63113,	63127,
-	63131,	63149,	63179,	63197,	63199,	63211,	63241,	63247,	63277,	63281,
-	63299,	63311,	63313,	63317,	63331,	63337,	63347,	63353,	63361,	63367,
-	63377,	63389,	63391,	63397,	63409,	63419,	63421,	63439,	63443,	63463,
-	63467,	63473,	63487,	63493,	63499,	63521,	63527,	63533,	63541,	63559,
-	63577,	63587,	63589,	63599,	63601,	63607,	63611,	63617,	63629,	63647,
-	63649,	63659,	63667,	63671,	63689,	63691,	63697,	63703,	63709,	63719,
-	63727,	63737,	63743,	63761,	63773,	63781,	63793,	63799,	63803,	63809,
-	63823,	63839,	63841,	63853,	63857,	63863,	63901,	63907,	63913,	63929,
-	63949,	63977,	63997,	64007,	64013,	64019,	64033,	64037,	64063,	64067,
-	64081,	64091,	64109,	64123,	64151,	64153,	64157,	64171,	64187,	64189,
-	64217,	64223,	64231,	64237,	64271,	64279,	64283,	64301,	64303,	64319,
-	64327,	64333,	64373,	64381,	64399,	64403,	64433,	64439,	64451,	64453,
-	64483,	64489,	64499,	64513,	64553,	64567,	64577,	64579,	64591,	64601,
-	64609,	64613,	64621,	64627,	64633,	64661,	64663,	64667,	64679,	64693,
-	64709,	64717,	64747,	64763,	64781,	64783,	64793,	64811,	64817,	64849,
-	64853,	64871,	64877,	64879,	64891,	64901,	64919,	64921,	64927,	64937,
-	64951,	64969,	64997,	65003,	65011,	65027,	65029,	65033,	65053,	65063,
-	65071,	65089,	65099,	65101,	65111,	65119,	65123,	65129,	65141,	65147,
-	65167,	65171,	65173,	65179,	65183,	65203,	65213,	65239,	65257,	65267,
-	65269,	65287,	65293,	65309,	65323,	65327,	65353,	65357,	65371,	65381,
-	65393,	65407,	65413,	65419,	65423,	65437,	65447,	65449,	65479,	65497,
-	65519,	65521,	65537,	65539,	65543,	65551,	65557,	65563,	65579,	65581,
-	65587,	65599,	65609,	65617,	65629,	65633,	65647,	65651,	65657,	65677,
-	65687,	65699,	65701,	65707,	65713,	65717,	65719,	65729,	65731,	65761,
-	65777,	65789,	65809,	65827,	65831,	65837,	65839,	65843,	65851,	65867,
-	65881,	65899,	65921,	65927,	65929,	65951,	65957,	65963,	65981,	65983,
-	65993,	66029,	66037,	66041,	66047,	66067,	66071,	66083,	66089,	66103,
-	66107,	66109,	66137,	66161,	66169,	66173,	66179,	66191,	66221,	66239,
-	66271,	66293,	66301,	66337,	66343,	66347,	66359,	66361,	66373,	66377,
-	66383,	66403,	66413,	66431,	66449,	66457,	66463,	66467,	66491,	66499,
-	66509,	66523,	66529,	66533,	66541,	66553,	66569,	66571,	66587,	66593,
-	66601,	66617,	66629,	66643,	66653,	66683,	66697,	66701,	66713,	66721,
-	66733,	66739,	66749,	66751,	66763,	66791,	66797,	66809,	66821,	66841,
-	66851,	66853,	66863,	66877,	66883,	66889,	66919,	66923,	66931,	66943,
-	66947,	66949,	66959,	66973,	66977,	67003,	67021,	67033,	67043,	67049,
-	67057,	67061,	67073,	67079,	67103,	67121,	67129,	67139,	67141,	67153,
-	67157,	67169,	67181,	67187,	67189,	67211,	67213,	67217,	67219,	67231,
-	67247,	67261,	67271,	67273,	67289,	67307,	67339,	67343,	67349,	67369,
-	67391,	67399,	67409,	67411,	67421,	67427,	67429,	67433,	67447,	67453,
-	67477,	67481,	67489,	67493,	67499,	67511,	67523,	67531,	67537,	67547,
-	67559,	67567,	67577,	67579,	67589,	67601,	67607,	67619,	67631,	67651,
-	67679,	67699,	67709,	67723,	67733,	67741,	67751,	67757,	67759,	67763,
-	67777,	67783,	67789,	67801,	67807,	67819,	67829,	67843,	67853,	67867,
-	67883,	67891,	67901,	67927,	67931,	67933,	67939,	67943,	67957,	67961,
-	67967,	67979,	67987,	67993,	68023,	68041,	68053,	68059,	68071,	68087,
-	68099,	68111,	68113,	68141,	68147,	68161,	68171,	68207,	68209,	68213,
-	68219,	68227,	68239,	68261,	68279,	68281,	68311,	68329,	68351,	68371,
-	68389,	68399,	68437,	68443,	68447,	68449,	68473,	68477,	68483,	68489,
-	68491,	68501,	68507,	68521,	68531,	68539,	68543,	68567,	68581,	68597,
-	68611,	68633,	68639,	68659,	68669,	68683,	68687,	68699,	68711,	68713,
-	68729,	68737,	68743,	68749,	68767,	68771,	68777,	68791,	68813,	68819,
-	68821,	68863,	68879,	68881,	68891,	68897,	68899,	68903,	68909,	68917,
-	68927,	68947,	68963,	68993,	69001,	69011,	69019,	69029,	69031,	69061,
-	69067,	69073,	69109,	69119,	69127,	69143,	69149,	69151,	69163,	69191,
-	69193,	69197,	69203,	69221,	69233,	69239,	69247,	69257,	69259,	69263,
-	69313,	69317,	69337,	69341,	69371,	69379,	69383,	69389,	69401,	69403,
-	69427,	69431,	69439,	69457,	69463,	69467,	69473,	69481,	69491,	69493,
-	69497,	69499,	69539,	69557,	69593,	69623,	69653,	69661,	69677,	69691,
-	69697,	69709,	69737,	69739,	69761,	69763,	69767,	69779,	69809,	69821,
-	69827,	69829,	69833,	69847,	69857,	69859,	69877,	69899,	69911,	69929,
-	69931,	69941,	69959,	69991,	69997,	70001,	70003,	70009,	70019,	70039,
-	70051,	70061,	70067,	70079,	70099,	70111,	70117,	70121,	70123,	70139,
-	70141,	70157,	70163,	70177,	70181,	70183,	70199,	70201,	70207,	70223,
-	70229,	70237,	70241,	70249,	70271,	70289,	70297,	70309,	70313,	70321,
-	70327,	70351,	70373,	70379,	70381,	70393,	70423,	70429,	70439,	70451,
-	70457,	70459,	70481,	70487,	70489,	70501,	70507,	70529,	70537,	70549,
-	70571,	70573,	70583,	70589,	70607,	70619,	70621,	70627,	70639,	70657,
-	70663,	70667,	70687,	70709,	70717,	70729,	70753,	70769,	70783,	70793,
-	70823,	70841,	70843,	70849,	70853,	70867,	70877,	70879,	70891,	70901,
-	70913,	70919,	70921,	70937,	70949,	70951,	70957,	70969,	70979,	70981,
-	70991,	70997,	70999,	71011,	71023,	71039,	71059,	71069,	71081,	71089,
-	71119,	71129,	71143,	71147,	71153,	71161,	71167,	71171,	71191,	71209,
-	71233,	71237,	71249,	71257,	71261,	71263,	71287,	71293,	71317,	71327,
-	71329,	71333,	71339,	71341,	71347,	71353,	71359,	71363,	71387,	71389,
-	71399,	71411,	71413,	71419,	71429,	71437,	71443,	71453,	71471,	71473,
-	71479,	71483,	71503,	71527,	71537,	71549,	71551,	71563,	71569,	71593,
-	71597,	71633,	71647,	71663,	71671,	71693,	71699,	71707,	71711,	71713,
-	71719,	71741,	71761,	71777,	71789,	71807,	71809,	71821,	71837,	71843,
-	71849,	71861,	71867,	71879,	71881,	71887,	71899,	71909,	71917,	71933,
-	71941,	71947,	71963,	71971,	71983,	71987,	71993,	71999,	72019,	72031,
-	72043,	72047,	72053,	72073,	72077,	72089,	72091,	72101,	72103,	72109,
-	72139,	72161,	72167,	72169,	72173,	72211,	72221,	72223,	72227,	72229,
-	72251,	72253,	72269,	72271,	72277,	72287,	72307,	72313,	72337,	72341,
-	72353,	72367,	72379,	72383,	72421,	72431,	72461,	72467,	72469,	72481,
-	72493,	72497,	72503,	72533,	72547,	72551,	72559,	72577,	72613,	72617,
-	72623,	72643,	72647,	72649,	72661,	72671,	72673,	72679,	72689,	72701,
-	72707,	72719,	72727,	72733,	72739,	72763,	72767,	72797,	72817,	72823,
-	72859,	72869,	72871,	72883,	72889,	72893,	72901,	72907,	72911,	72923,
-	72931,	72937,	72949,	72953,	72959,	72973,	72977,	72997,	73009,	73013,
-	73019,	73037,	73039,	73043,	73061,	73063,	73079,	73091,	73121,	73127,
-	73133,	73141,	73181,	73189,	73237,	73243,	73259,	73277,	73291,	73303,
-	73309,	73327,	73331,	73351,	73361,	73363,	73369,	73379,	73387,	73417,
-	73421,	73433,	73453,	73459,	73471,	73477,	73483,	73517,	73523,	73529,
-	73547,	73553,	73561,	73571,	73583,	73589,	73597,	73607,	73609,	73613,
-	73637,	73643,	73651,	73673,	73679,	73681,	73693,	73699,	73709,	73721,
-	73727,	73751,	73757,	73771,	73783,	73819,	73823,	73847,	73849,	73859,
-	73867,	73877,	73883,	73897,	73907,	73939,	73943,	73951,	73961,	73973,
-	73999,	74017,	74021,	74027,	74047,	74051,	74071,	74077,	74093,	74099,
-	74101,	74131,	74143,	74149,	74159,	74161,	74167,	74177,	74189,	74197,
-	74201,	74203,	74209,	74219,	74231,	74257,	74279,	74287,	74293,	74297,
-	74311,	74317,	74323,	74353,	74357,	74363,	74377,	74381,	74383,	74411,
-	74413,	74419,	74441,	74449,	74453,	74471,	74489,	74507,	74509,	74521,
-	74527,	74531,	74551,	74561,	74567,	74573,	74587,	74597,	74609,	74611,
-	74623,	74653,	74687,	74699,	74707,	74713,	74717,	74719,	74729,	74731,
-	74747,	74759,	74761,	74771,	74779,	74797,	74821,	74827,	74831,	74843,
-	74857,	74861,	74869,	74873,	74887,	74891,	74897,	74903,	74923,	74929,
-	74933,	74941,	74959,	75011,	75013,	75017,	75029,	75037,	75041,	75079,
-	75083,	75109,	75133,	75149,	75161,	75167,	75169,	75181,	75193,	75209,
-	75211,	75217,	75223,	75227,	75239,	75253,	75269,	75277,	75289,	75307,
-	75323,	75329,	75337,	75347,	75353,	75367,	75377,	75389,	75391,	75401,
-	75403,	75407,	75431,	75437,	75479,	75503,	75511,	75521,	75527,	75533,
-	75539,	75541,	75553,	75557,	75571,	75577,	75583,	75611,	75617,	75619,
-	75629,	75641,	75653,	75659,	75679,	75683,	75689,	75703,	75707,	75709,
-	75721,	75731,	75743,	75767,	75773,	75781,	75787,	75793,	75797,	75821,
-	75833,	75853,	75869,	75883,	75913,	75931,	75937,	75941,	75967,	75979,
-	75983,	75989,	75991,	75997,	76001,	76003,	76031,	76039,	76079,	76081,
-	76091,	76099,	76103,	76123,	76129,	76147,	76157,	76159,	76163,	76207,
-	76213,	76231,	76243,	76249,	76253,	76259,	76261,	76283,	76289,	76303,
-	76333,	76343,	76367,	76369,	76379,	76387,	76403,	76421,	76423,	76441,
-	76463,	76471,	76481,	76487,	76493,	76507,	76511,	76519,	76537,	76541,
-	76543,	76561,	76579,	76597,	76603,	76607,	76631,	76649,	76651,	76667,
-	76673,	76679,	76697,	76717,	76733,	76753,	76757,	76771,	76777,	76781,
-	76801,	76819,	76829,	76831,	76837,	76847,	76871,	76873,	76883,	76907,
-	76913,	76919,	76943,	76949,	76961,	76963,	76991,	77003,	77017,	77023,
-	77029,	77041,	77047,	77069,	77081,	77093,	77101,	77137,	77141,	77153,
-	77167,	77171,	77191,	77201,	77213,	77237,	77239,	77243,	77249,	77261,
-	77263,	77267,	77269,	77279,	77291,	77317,	77323,	77339,	77347,	77351,
-	77359,	77369,	77377,	77383,	77417,	77419,	77431,	77447,	77471,	77477,
-	77479,	77489,	77491,	77509,	77513,	77521,	77527,	77543,	77549,	77551,
-	77557,	77563,	77569,	77573,	77587,	77591,	77611,	77617,	77621,	77641,
-	77647,	77659,	77681,	77687,	77689,	77699,	77711,	77713,	77719,	77723,
-	77731,	77743,	77747,	77761,	77773,	77783,	77797,	77801,	77813,	77839,
-	77849,	77863,	77867,	77893,	77899,	77929,	77933,	77951,	77969,	77977,
-	77983,	77999,	78007,	78017,	78031,	78041,	78049,	78059,	78079,	78101,
-	78121,	78137,	78139,	78157,	78163,	78167,	78173,	78179,	78191,	78193,
-	78203,	78229,	78233,	78241,	78259,	78277,	78283,	78301,	78307,	78311,
-	78317,	78341,	78347,	78367,	78401,	78427,	78437,	78439,	78467,	78479,
-	78487,	78497,	78509,	78511,	78517,	78539,	78541,	78553,	78569,	78571,
-	78577,	78583,	78593,	78607,	78623,	78643,	78649,	78653,	78691,	78697,
-	78707,	78713,	78721,	78737,	78779,	78781,	78787,	78791,	78797,	78803,
-	78809,	78823,	78839,	78853,	78857,	78877,	78887,	78889,	78893,	78901,
-	78919,	78929,	78941,	78977,	78979,	78989,	79031,	79039,	79043,	79063,
-	79087,	79103,	79111,	79133,	79139,	79147,	79151,	79153,	79159,	79181,
-	79187,	79193,	79201,	79229,	79231,	79241,	79259,	79273,	79279,	79283,
-	79301,	79309,	79319,	79333,	79337,	79349,	79357,	79367,	79379,	79393,
-	79397,	79399,	79411,	79423,	79427,	79433,	79451,	79481,	79493,	79531,
-	79537,	79549,	79559,	79561,	79579,	79589,	79601,	79609,	79613,	79621,
-	79627,	79631,	79633,	79657,	79669,	79687,	79691,	79693,	79697,	79699,
-	79757,	79769,	79777,	79801,	79811,	79813,	79817,	79823,	79829,	79841,
-	79843,	79847,	79861,	79867,	79873,	79889,	79901,	79903,	79907,	79939,
-	79943,	79967,	79973,	79979,	79987,	79997,	79999,	80021,	80039,	80051,
-	80071,	80077,	80107,	80111,	80141,	80147,	80149,	80153,	80167,	80173,
-	80177,	80191,	80207,	80209,	80221,	80231,	80233,	80239,	80251,	80263,
-	80273,	80279,	80287,	80309,	80317,	80329,	80341,	80347,	80363,	80369,
-	80387,	80407,	80429,	80447,	80449,	80471,	80473,	80489,	80491,	80513,
-	80527,	80537,	80557,	80567,	80599,	80603,	80611,	80621,	80627,	80629,
-	80651,	80657,	80669,	80671,	80677,	80681,	80683,	80687,	80701,	80713,
-	80737,	80747,	80749,	80761,	80777,	80779,	80783,	80789,	80803,	80809,
-	80819,	80831,	80833,	80849,	80863,	80897,	80909,	80911,	80917,	80923,
-	80929,	80933,	80953,	80963,	80989,	81001,	81013,	81017,	81019,	81023,
-	81031,	81041,	81043,	81047,	81049,	81071,	81077,	81083,	81097,	81101,
-	81119,	81131,	81157,	81163,	81173,	81181,	81197,	81199,	81203,	81223,
-	81233,	81239,	81281,	81283,	81293,	81299,	81307,	81331,	81343,	81349,
-	81353,	81359,	81371,	81373,	81401,	81409,	81421,	81439,	81457,	81463,
-	81509,	81517,	81527,	81533,	81547,	81551,	81553,	81559,	81563,	81569,
-	81611,	81619,	81629,	81637,	81647,	81649,	81667,	81671,	81677,	81689,
-	81701,	81703,	81707,	81727,	81737,	81749,	81761,	81769,	81773,	81799,
-	81817,	81839,	81847,	81853,	81869,	81883,	81899,	81901,	81919,	81929,
-	81931,	81937,	81943,	81953,	81967,	81971,	81973,	82003,	82007,	82009,
-	82013,	82021,	82031,	82037,	82039,	82051,	82067,	82073,	82129,	82139,
-	82141,	82153,	82163,	82171,	82183,	82189,	82193,	82207,	82217,	82219,
-	82223,	82231,	82237,	82241,	82261,	82267,	82279,	82301,	82307,	82339,
-	82349,	82351,	82361,	82373,	82387,	82393,	82421,	82457,	82463,	82469,
-	82471,	82483,	82487,	82493,	82499,	82507,	82529,	82531,	82549,	82559,
-	82561,	82567,	82571,	82591,	82601,	82609,	82613,	82619,	82633,	82651,
-	82657,	82699,	82721,	82723,	82727,	82729,	82757,	82759,	82763,	82781,
-	82787,	82793,	82799,	82811,	82813,	82837,	82847,	82883,	82889,	82891,
-	82903,	82913,	82939,	82963,	82981,	82997,	83003,	83009,	83023,	83047,
-	83059,	83063,	83071,	83077,	83089,	83093,	83101,	83117,	83137,	83177,
-	83203,	83207,	83219,	83221,	83227,	83231,	83233,	83243,	83257,	83267,
-	83269,	83273,	83299,	83311,	83339,	83341,	83357,	83383,	83389,	83399,
-	83401,	83407,	83417,	83423,	83431,	83437,	83443,	83449,	83459,	83471,
-	83477,	83497,	83537,	83557,	83561,	83563,	83579,	83591,	83597,	83609,
-	83617,	83621,	83639,	83641,	83653,	83663,	83689,	83701,	83717,	83719,
-	83737,	83761,	83773,	83777,	83791,	83813,	83833,	83843,	83857,	83869,
-	83873,	83891,	83903,	83911,	83921,	83933,	83939,	83969,	83983,	83987,
-	84011,	84017,	84047,	84053,	84059,	84061,	84067,	84089,	84121,	84127,
-	84131,	84137,	84143,	84163,	84179,	84181,	84191,	84199,	84211,	84221,
-	84223,	84229,	84239,	84247,	84263,	84299,	84307,	84313,	84317,	84319,
-	84347,	84349,	84377,	84389,	84391,	84401,	84407,	84421,	84431,	84437,
-	84443,	84449,	84457,	84463,	84467,	84481,	84499,	84503,	84509,	84521,
-	84523,	84533,	84551,	84559,	84589,	84629,	84631,	84649,	84653,	84659,
-	84673,	84691,	84697,	84701,	84713,	84719,	84731,	84737,	84751,	84761,
-	84787,	84793,	84809,	84811,	84827,	84857,	84859,	84869,	84871,	84913,
-	84919,	84947,	84961,	84967,	84977,	84979,	84991,	85009,	85021,	85027,
-	85037,	85049,	85061,	85081,	85087,	85091,	85093,	85103,	85109,	85121,
-	85133,	85147,	85159,	85193,	85199,	85201,	85213,	85223,	85229,	85237,
-	85243,	85247,	85259,	85297,	85303,	85313,	85331,	85333,	85361,	85363,
-	85369,	85381,	85411,	85427,	85429,	85439,	85447,	85451,	85453,	85469,
-	85487,	85513,	85517,	85523,	85531,	85549,	85571,	85577,	85597,	85601,
-	85607,	85619,	85621,	85627,	85639,	85643,	85661,	85667,	85669,	85691,
-	85703,	85711,	85717,	85733,	85751,	85781,	85793,	85817,	85819,	85829,
-	85831,	85837,	85843,	85847,	85853,	85889,	85903,	85909,	85931,	85933,
-	85991,	85999,	86011,	86017,	86027,	86029,	86069,	86077,	86083,	86111,
-	86113,	86117,	86131,	86137,	86143,	86161,	86171,	86179,	86183,	86197,
-	86201,	86209,	86239,	86243,	86249,	86257,	86263,	86269,	86287,	86291,
-	86293,	86297,	86311,	86323,	86341,	86351,	86353,	86357,	86369,	86371,
-	86381,	86389,	86399,	86413,	86423,	86441,	86453,	86461,	86467,	86477,
-	86491,	86501,	86509,	86531,	86533,	86539,	86561,	86573,	86579,	86587,
-	86599,	86627,	86629,	86677,	86689,	86693,	86711,	86719,	86729,	86743,
-	86753,	86767,	86771,	86783,	86813,	86837,	86843,	86851,	86857,	86861,
-	86869,	86923,	86927,	86929,	86939,	86951,	86959,	86969,	86981,	86993,
-	87011,	87013,	87037,	87041,	87049,	87071,	87083,	87103,	87107,	87119,
-	87121,	87133,	87149,	87151,	87179,	87181,	87187,	87211,	87221,	87223,
-	87251,	87253,	87257,	87277,	87281,	87293,	87299,	87313,	87317,	87323,
-	87337,	87359,	87383,	87403,	87407,	87421,	87427,	87433,	87443,	87473,
-	87481,	87491,	87509,	87511,	87517,	87523,	87539,	87541,	87547,	87553,
-	87557,	87559,	87583,	87587,	87589,	87613,	87623,	87629,	87631,	87641,
-	87643,	87649,	87671,	87679,	87683,	87691,	87697,	87701,	87719,	87721,
-	87739,	87743,	87751,	87767,	87793,	87797,	87803,	87811,	87833,	87853,
-	87869,	87877,	87881,	87887,	87911,	87917,	87931,	87943,	87959,	87961,
-	87973,	87977,	87991,	88001,	88003,	88007,	88019,	88037,	88069,	88079,
-	88093,	88117,	88129,	88169,	88177,	88211,	88223,	88237,	88241,	88259,
-	88261,	88289,	88301,	88321,	88327,	88337,	88339,	88379,	88397,	88411,
-	88423,	88427,	88463,	88469,	88471,	88493,	88499,	88513,	88523,	88547,
-	88589,	88591,	88607,	88609,	88643,	88651,	88657,	88661,	88663,	88667,
-	88681,	88721,	88729,	88741,	88747,	88771,	88789,	88793,	88799,	88801,
-	88807,	88811,	88813,	88817,	88819,	88843,	88853,	88861,	88867,	88873,
-	88883,	88897,	88903,	88919,	88937,	88951,	88969,	88993,	88997,	89003,
-	89009,	89017,	89021,	89041,	89051,	89057,	89069,	89071,	89083,	89087,
-	89101,	89107,	89113,	89119,	89123,	89137,	89153,	89189,	89203,	89209,
-	89213,	89227,	89231,	89237,	89261,	89269,	89273,	89293,	89303,	89317,
-	89329,	89363,	89371,	89381,	89387,	89393,	89399,	89413,	89417,	89431,
-	89443,	89449,	89459,	89477,	89491,	89501,	89513,	89519,	89521,	89527,
-	89533,	89561,	89563,	89567,	89591,	89597,	89599,	89603,	89611,	89627,
-	89633,	89653,	89657,	89659,	89669,	89671,	89681,	89689,	89753,	89759,
-	89767,	89779,	89783,	89797,	89809,	89819,	89821,	89833,	89839,	89849,
-	89867,	89891,	89897,	89899,	89909,	89917,	89923,	89939,	89959,	89963,
-	89977,	89983,	89989,	90001,	90007,	90011,	90017,	90019,	90023,	90031,
-	90053,	90059,	90067,	90071,	90073,	90089,	90107,	90121,	90127,	90149,
-	90163,	90173,	90187,	90191,	90197,	90199,	90203,	90217,	90227,	90239,
-	90247,	90263,	90271,	90281,	90289,	90313,	90353,	90359,	90371,	90373,
-	90379,	90397,	90401,	90403,	90407,	90437,	90439,	90469,	90473,	90481,
-	90499,	90511,	90523,	90527,	90529,	90533,	90547,	90583,	90599,	90617,
-	90619,	90631,	90641,	90647,	90659,	90677,	90679,	90697,	90703,	90709,
-	90731,	90749,	90787,	90793,	90803,	90821,	90823,	90833,	90841,	90847,
-	90863,	90887,	90901,	90907,	90911,	90917,	90931,	90947,	90971,	90977,
-	90989,	90997,	91009,	91019,	91033,	91079,	91081,	91097,	91099,	91121,
-	91127,	91129,	91139,	91141,	91151,	91153,	91159,	91163,	91183,	91193,
-	91199,	91229,	91237,	91243,	91249,	91253,	91283,	91291,	91297,	91303,
-	91309,	91331,	91367,	91369,	91373,	91381,	91387,	91393,	91397,	91411,
-	91423,	91433,	91453,	91457,	91459,	91463,	91493,	91499,	91513,	91529,
-	91541,	91571,	91573,	91577,	91583,	91591,	91621,	91631,	91639,	91673,
-	91691,	91703,	91711,	91733,	91753,	91757,	91771,	91781,	91801,	91807,
-	91811,	91813,	91823,	91837,	91841,	91867,	91873,	91909,	91921,	91939,
-	91943,	91951,	91957,	91961,	91967,	91969,	91997,	92003,	92009,	92033,
-	92041,	92051,	92077,	92083,	92107,	92111,	92119,	92143,	92153,	92173,
-	92177,	92179,	92189,	92203,	92219,	92221,	92227,	92233,	92237,	92243,
-	92251,	92269,	92297,	92311,	92317,	92333,	92347,	92353,	92357,	92363,
-	92369,	92377,	92381,	92383,	92387,	92399,	92401,	92413,	92419,	92431,
-	92459,	92461,	92467,	92479,	92489,	92503,	92507,	92551,	92557,	92567,
-	92569,	92581,	92593,	92623,	92627,	92639,	92641,	92647,	92657,	92669,
-	92671,	92681,	92683,	92693,	92699,	92707,	92717,	92723,	92737,	92753,
-	92761,	92767,	92779,	92789,	92791,	92801,	92809,	92821,	92831,	92849,
-	92857,	92861,	92863,	92867,	92893,	92899,	92921,	92927,	92941,	92951,
-	92957,	92959,	92987,	92993,	93001,	93047,	93053,	93059,	93077,	93083,
-	93089,	93097,	93103,	93113,	93131,	93133,	93139,	93151,	93169,	93179,
-	93187,	93199,	93229,	93239,	93241,	93251,	93253,	93257,	93263,	93281,
-	93283,	93287,	93307,	93319,	93323,	93329,	93337,	93371,	93377,	93383,
-	93407,	93419,	93427,	93463,	93479,	93481,	93487,	93491,	93493,	93497,
-	93503,	93523,	93529,	93553,	93557,	93559,	93563,	93581,	93601,	93607,
-	93629,	93637,	93683,	93701,	93703,	93719,	93739,	93761,	93763,	93787,
-	93809,	93811,	93827,	93851,	93871,	93887,	93889,	93893,	93901,	93911,
-	93913,	93923,	93937,	93941,	93949,	93967,	93971,	93979,	93983,	93997,
-	94007,	94009,	94033,	94049,	94057,	94063,	94079,	94099,	94109,	94111,
-	94117,	94121,	94151,	94153,	94169,	94201,	94207,	94219,	94229,	94253,
-	94261,	94273,	94291,	94307,	94309,	94321,	94327,	94331,	94343,	94349,
-	94351,	94379,	94397,	94399,	94421,	94427,	94433,	94439,	94441,	94447,
-	94463,	94477,	94483,	94513,	94529,	94531,	94541,	94543,	94547,	94559,
-	94561,	94573,	94583,	94597,	94603,	94613,	94621,	94649,	94651,	94687,
-	94693,	94709,	94723,	94727,	94747,	94771,	94777,	94781,	94789,	94793,
-	94811,	94819,	94823,	94837,	94841,	94847,	94849,	94873,	94889,	94903,
-	94907,	94933,	94949,	94951,	94961,	94993,	94999,	95003,	95009,	95021,
-	95027,	95063,	95071,	95083,	95087,	95089,	95093,	95101,	95107,	95111,
-	95131,	95143,	95153,	95177,	95189,	95191,	95203,	95213,	95219,	95231,
-	95233,	95239,	95257,	95261,	95267,	95273,	95279,	95287,	95311,	95317,
-	95327,	95339,	95369,	95383,	95393,	95401,	95413,	95419,	95429,	95441,
-	95443,	95461,	95467,	95471,	95479,	95483,	95507,	95527,	95531,	95539,
-	95549,	95561,	95569,	95581,	95597,	95603,	95617,	95621,	95629,	95633,
-	95651,	95701,	95707,	95713,	95717,	95723,	95731,	95737,	95747,	95773,
-	95783,	95789,	95791,	95801,	95803,	95813,	95819,	95857,	95869,	95873,
-	95881,	95891,	95911,	95917,	95923,	95929,	95947,	95957,	95959,	95971,
-	95987,	95989,	96001,	96013,	96017,	96043,	96053,	96059,	96079,	96097,
-	96137,	96149,	96157,	96167,	96179,	96181,	96199,	96211,	96221,	96223,
-	96233,	96259,	96263,	96269,	96281,	96289,	96293,	96323,	96329,	96331,
-	96337,	96353,	96377,	96401,	96419,	96431,	96443,	96451,	96457,	96461,
-	96469,	96479,	96487,	96493,	96497,	96517,	96527,	96553,	96557,	96581,
-	96587,	96589,	96601,	96643,	96661,	96667,	96671,	96697,	96703,	96731,
-	96737,	96739,	96749,	96757,	96763,	96769,	96779,	96787,	96797,	96799,
-	96821,	96823,	96827,	96847,	96851,	96857,	96893,	96907,	96911,	96931,
-	96953,	96959,	96973,	96979,	96989,	96997,	97001,	97003,	97007,	97021,
-	97039,	97073,	97081,	97103,	97117,	97127,	97151,	97157,	97159,	97169,
-	97171,	97177,	97187,	97213,	97231,	97241,	97259,	97283,	97301,	97303,
-	97327,	97367,	97369,	97373,	97379,	97381,	97387,	97397,	97423,	97429,
-	97441,	97453,	97459,	97463,	97499,	97501,	97511,	97523,	97547,	97549,
-	97553,	97561,	97571,	97577,	97579,	97583,	97607,	97609,	97613,	97649,
-	97651,	97673,	97687,	97711,	97729,	97771,	97777,	97787,	97789,	97813,
-	97829,	97841,	97843,	97847,	97849,	97859,	97861,	97871,	97879,	97883,
-	97919,	97927,	97931,	97943,	97961,	97967,	97973,	97987,	98009,	98011,
-	98017,	98041,	98047,	98057,	98081,	98101,	98123,	98129,	98143,	98179,
-	98207,	98213,	98221,	98227,	98251,	98257,	98269,	98297,	98299,	98317,
-	98321,	98323,	98327,	98347,	98369,	98377,	98387,	98389,	98407,	98411,
-	98419,	98429,	98443,	98453,	98459,	98467,	98473,	98479,	98491,	98507,
-	98519,	98533,	98543,	98561,	98563,	98573,	98597,	98621,	98627,	98639,
-	98641,	98663,	98669,	98689,	98711,	98713,	98717,	98729,	98731,	98737,
-	98773,	98779,	98801,	98807,	98809,	98837,	98849,	98867,	98869,	98873,
-	98887,	98893,	98897,	98899,	98909,	98911,	98927,	98929,	98939,	98947,
-	98953,	98963,	98981,	98993,	98999,	99013,	99017,	99023,	99041,	99053,
-	99079,	99083,	99089,	99103,	99109,	99119,	99131,	99133,	99137,	99139,
-	99149,	99173,	99181,	99191,	99223,	99233,	99241,	99251,	99257,	99259,
-	99277,	99289,	99317,	99347,	99349,	99367,	99371,	99377,	99391,	99397,
-	99401,	99409,	99431,	99439,	99469,	99487,	99497,	99523,	99527,	99529,
-	99551,	99559,	99563,	99571,	99577,	99581,	99607,	99611,	99623,	99643,
-	99661,	99667,	99679,	99689,	99707,	99709,	99713,	99719,	99721,	99733,
-	99761,	99767,	99787,	99793,	99809,	99817,	99823,	99829,	99833,	99839,
-	99859,	99871,	99877,	99881,	99901,	99907,	99923,	99929,	99961,	99971,
-	99989,	99991,	100003,	100019,	100043,	100049,	100057,	100069,	100103,	100109,
-	100129,	100151,	100153,	100169,	100183,	100189,	100193,	100207,	100213,	100237,
-	100267,	100271,	100279,	100291,	100297,	100313,	100333,	100343,	100357,	100361,
-	100363,	100379,	100391,	100393,	100403,	100411,	100417,	100447,	100459,	100469,
-	100483,	100493,	100501,	100511,	100517,	100519,	100523,	100537,	100547,	100549,
-	100559,	100591,	100609,	100613,	100621,	100649,	100669,	100673,	100693,	100699,
-	100703,	100733,	100741,	100747,	100769,	100787,	100799,	100801,	100811,	100823,
-	100829,	100847,	100853,	100907,	100913,	100927,	100931,	100937,	100943,	100957,
-	100981,	100987,	100999,	101009,	101021,	101027,	101051,	101063,	101081,	101089,
-	101107,	101111,	101113,	101117,	101119,	101141,	101149,	101159,	101161,	101173,
-	101183,	101197,	101203,	101207,	101209,	101221,	101267,	101273,	101279,	101281,
-	101287,	101293,	101323,	101333,	101341,	101347,	101359,	101363,	101377,	101383,
-	101399,	101411,	101419,	101429,	101449,	101467,	101477,	101483,	101489,	101501,
-	101503,	101513,	101527,	101531,	101533,	101537,	101561,	101573,	101581,	101599,
-	101603,	101611,	101627,	101641,	101653,	101663,	101681,	101693,	101701,	101719,
-	101723,	101737,	101741,	101747,	101749,	101771,	101789,	101797,	101807,	101833,
-	101837,	101839,	101863,	101869,	101873,	101879,	101891,	101917,	101921,	101929,
-	101939,	101957,	101963,	101977,	101987,	101999,	102001,	102013,	102019,	102023,
-	102031,	102043,	102059,	102061,	102071,	102077,	102079,	102101,	102103,	102107,
-	102121,	102139,	102149,	102161,	102181,	102191,	102197,	102199,	102203,	102217,
-	102229,	102233,	102241,	102251,	102253,	102259,	102293,	102299,	102301,	102317,
-	102329,	102337,	102359,	102367,	102397,	102407,	102409,	102433,	102437,	102451,
-	102461,	102481,	102497,	102499,	102503,	102523,	102533,	102539,	102547,	102551,
-	102559,	102563,	102587,	102593,	102607,	102611,	102643,	102647,	102653,	102667,
-	102673,	102677,	102679,	102701,	102761,	102763,	102769,	102793,	102797,	102811,
-	102829,	102841,	102859,	102871,	102877,	102881,	102911,	102913,	102929,	102931,
-	102953,	102967,	102983,	103001,	103007,	103043,	103049,	103067,	103069,	103079,
-	103087,	103091,	103093,	103099,	103123,	103141,	103171,	103177,	103183,	103217,
-	103231,	103237,	103289,	103291,	103307,	103319,	103333,	103349,	103357,	103387,
-	103391,	103393,	103399,	103409,	103421,	103423,	103451,	103457,	103471,	103483,
-	103511,	103529,	103549,	103553,	103561,	103567,	103573,	103577,	103583,	103591,
-	103613,	103619,	103643,	103651,	103657,	103669,	103681,	103687,	103699,	103703,
-	103723,	103769,	103787,	103801,	103811,	103813,	103837,	103841,	103843,	103867,
-	103889,	103903,	103913,	103919,	103951,	103963,	103967,	103969,	103979,	103981,
-	103991,	103993,	103997,	104003,	104009,	104021,	104033,	104047,	104053,	104059,
-	104087,	104089,	104107,	104113,	104119,	104123,	104147,	104149,	104161,	104173,
-	104179,	104183,	104207,	104231,	104233,	104239,	104243,	104281,	104287,	104297,
-	104309,	104311,	104323,	104327,	104347,	104369,	104381,	104383,	104393,	104399,
-	104417,	104459,	104471,	104473,	104479,	104491,	104513,	104527,	104537,	104543,
-	104549,	104551,	104561,	104579,	104593,	104597,	104623,	104639,	104651,	104659,
-	104677,	104681,	104683,	104693,	104701,	104707,	104711,	104717,	104723,	104729,
-};
-
-//  return 1 if p is divisable by sp, 0 otherwise
-static int
-divides(mpint *dividend, ulong divisor)
-{
-	mpdigit d[2], q;
-	int i;
-
-	d[1] = 0;
-	for(i = dividend->top-1; i >= 0; i--){
-		d[0] = dividend->p[i];
-		mpdigdiv(d, divisor, &q);
-		d[1] = d[0] - divisor*q;
-	}
-	return d[1] == 0;
-}
-
-//  return -1 if p is divisable by one of the small primes, 0 otherwise
-int
-smallprimetest(mpint *p)
-{
-	int i;
-	ulong sp;
-
-	for(i = 0; i < nelem(smallprimes); i++){
-		sp = smallprimes[i];
-		if(p->top == 1 && p->p[0] <= sp)
-			break;
-		if(divides(p, sp))
-			return -1;
-	}
-	return 0;
-}
--- a/libsec/thumb.c
+++ b/libsec/thumb.c
@@ -7,18 +7,10 @@
 
 enum{ ThumbTab = 1<<10 };
 
-static void *
-emalloc(int n)
+static Thumbprint*
+tablehead(uchar *sum, Thumbprint *table)
 {
-	void *p;
-	if(n==0)
-		n=1;
-	p = malloc(n);
-	if(p == nil){
-		exits("out of memory");
-	}
-	memset(p, 0, n);
-	return p;
+	return &table[((sum[0]<<8) + sum[1]) & (ThumbTab-1)];
 }
 
 void
@@ -25,8 +17,11 @@
 freeThumbprints(Thumbprint *table)
 {
 	Thumbprint *hd, *p, *q;
+
+	if(table == nil)
+		return;
 	for(hd = table; hd < table+ThumbTab; hd++){
-		for(p = hd->next; p; p = q){
+		for(p = hd->next; p && p != hd; p = q){
 			q = p->next;
 			free(p);
 		}
@@ -37,61 +32,89 @@
 int
 okThumbprint(uchar *sum, Thumbprint *table)
 {
-	Thumbprint *p;
-	int i = ((sum[0]<<8) + sum[1]) & (ThumbTab-1);
+	Thumbprint *hd, *p;
 
-	for(p = table[i].next; p; p = p->next)
+	if(table == nil)
+		return 0;
+	hd = tablehead(sum, table);
+	for(p = hd->next; p; p = p->next){
 		if(memcmp(sum, p->sha1, SHA1dlen) == 0)
 			return 1;
+		if(p == hd)
+			break;
+	}
 	return 0;
 }
 
-static void
+static int
 loadThumbprints(char *file, Thumbprint *table, Thumbprint *crltab)
 {
-	Thumbprint *entry;
-	Biobuf *bin;
+	Thumbprint *hd, *entry;
 	char *line, *field[50];
 	uchar sum[SHA1dlen];
-	int i;
+	Biobuf *bin;
 
-	bin = Bopen(file, OREAD);
-	if(bin == nil)
-		return;
-	for(; (line = Brdstr(bin, '\n', 1)) != 0; free(line)){
+	if(access(file, AEXIST) < 0)
+		return 0;	/* not an error */
+	if((bin = Bopen(file, OREAD)) == nil)
+		return -1;
+	for(; (line = Brdstr(bin, '\n', 1)) != nil; free(line)){
 		if(tokenize(line, field, nelem(field)) < 2)
 			continue;
 		if(strcmp(field[0], "#include") == 0){
-			loadThumbprints(field[1], table, crltab);
+			if(loadThumbprints(field[1], table, crltab) < 0)
+				goto err;
 			continue;
 		}
-		if(strcmp(field[0], "x509") != 0 || strncmp(field[1], "sha1=", strlen("sha1=")) != 0)
+		if(strcmp(field[0], "x509") != 0 || strncmp(field[1], "sha1=", 5) != 0)
 			continue;
-		field[1] += strlen("sha1=");
-		dec16(sum, sizeof(sum), field[1], strlen(field[1]));
+		field[1] += 5;
+		if(dec16(sum, SHA1dlen, field[1], strlen(field[1])) != SHA1dlen){
+			werrstr("malformed x509 entry in %s: %s", file, field[1]);
+			goto err;
+		}
 		if(crltab && okThumbprint(sum, crltab))
 			continue;
-		entry = (Thumbprint*)emalloc(sizeof(*entry));
+		hd = tablehead(sum, table);
+		if(hd->next == nil)
+			entry = hd;
+		else {
+			if((entry = malloc(sizeof(*entry))) == nil)
+				goto err;
+			entry->next = hd->next;
+		}
+		hd->next = entry;
 		memcpy(entry->sha1, sum, SHA1dlen);
-		i = ((sum[0]<<8) + sum[1]) & (ThumbTab-1);
-		entry->next = table[i].next;
-		table[i].next = entry;
 	}
 	Bterm(bin);
+	return 0;
+err:
+	free(line);
+	Bterm(bin);
+	return -1;
 }
 
 Thumbprint *
 initThumbprints(char *ok, char *crl)
 {
-	Thumbprint *table, *crltab = nil;
+	Thumbprint *table, *crltab;
 
+	table = crltab = nil;
 	if(crl){
-		crltab = emalloc(ThumbTab * sizeof(*table));
-		loadThumbprints(crl, crltab, nil);
+		if((crltab = malloc(ThumbTab * sizeof(*crltab))) == nil)
+			goto err;
+		memset(crltab, 0, ThumbTab * sizeof(*crltab));
+		if(loadThumbprints(crl, crltab, nil) < 0)
+			goto err;
 	}
-	table = emalloc(ThumbTab * sizeof(*table));
-	loadThumbprints(ok, table, crltab);
-	free(crltab);
+	if((table = malloc(ThumbTab * sizeof(*table))) == nil)
+		goto err;
+	memset(table, 0, ThumbTab * sizeof(*table));
+	if(loadThumbprints(ok, table, crltab) < 0){
+		freeThumbprints(table);
+		table = nil;
+	}
+err:
+	freeThumbprints(crltab);
 	return table;
 }
-
--- a/libsec/tlshand.c
+++ b/libsec/tlshand.c
@@ -1,6 +1,5 @@
 #include <u.h>
 #include <libc.h>
-#include <bio.h>
 #include <auth.h>
 #include <mp.h>
 #include <libsec.h>
@@ -17,8 +16,9 @@
 enum {
 	TLSFinishedLen = 12,
 	SSL3FinishedLen = MD5dlen+SHA1dlen,
-	MaxKeyData = 104,	// amount of secret we may need
-	MaxChunk = 1<<14,
+	MaxKeyData = 160,	// amount of secret we may need
+	MaxChunk = 1<<15,
+	MAXdlen = SHA2_512dlen,
 	RandomSize = 32,
 	SidSize = 32,
 	MasterSecretSize = 48,
@@ -46,11 +46,22 @@
 	int ok;
 } Algs;
 
+typedef struct Namedcurve{
+	int tlsid;
+	void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+} Namedcurve;
+
 typedef struct Finished{
 	uchar verify[SSL3FinishedLen];
 	int n;
 } Finished;
 
+typedef struct HandshakeHash {
+	MD5state	md5;
+	SHAstate	sha1;
+	SHA2_256state	sha2_256;
+} HandshakeHash;
+
 typedef struct TlsConnection{
 	TlsSec *sec;	// security management goo
 	int hand, ctl;	// record layer file descriptors
@@ -67,19 +78,23 @@
 	int state;		// must be set using setstate
 
 	// input buffer for handshake messages
-	uchar buf[MaxChunk+2048];
+	uchar recvbuf[MaxChunk];
 	uchar *rp, *ep;
 
+	// output buffer
+	uchar sendbuf[MaxChunk];
+	uchar *sendp;
+
 	uchar crandom[RandomSize];	// client random
 	uchar srandom[RandomSize];	// server random
 	int clientVersion;	// version in ClientHello
+	int cipher;
 	char *digest;	// name of digest algorithm to use
 	char *enc;		// name of encryption algorithm to use
 	int nsecret;	// amount of secret data to init keys
 
 	// for finished messages
-	MD5state	hsmd5;	// handshake hash
-	SHAstate	hssha1;	// handshake hash
+	HandshakeHash	handhash;
 	Finished	finished;
 } TlsConnection;
 
@@ -92,13 +107,15 @@
 			Bytes*	sid;
 			Ints*	ciphers;
 			Bytes*	compressors;
+			Bytes*	extensions;
 		} clientHello;
 		struct {
 			int version;
-			uchar 	random[RandomSize];
+			uchar	random[RandomSize];
 			Bytes*	sid;
-			int cipher;
-			int compressor;
+			int	cipher;
+			int	compressor;
+			Bytes*	extensions;
 		} serverHello;
 		struct {
 			int ncert;
@@ -106,12 +123,28 @@
 		} certificate;
 		struct {
 			Bytes *types;
+			Ints *sigalgs;
 			int nca;
 			Bytes **cas;
 		} certificateRequest;
 		struct {
+			Bytes *pskid;
 			Bytes *key;
 		} clientKeyExchange;
+		struct {
+			Bytes *pskid;
+			Bytes *dh_p;
+			Bytes *dh_g;
+			Bytes *dh_Ys;
+			Bytes *dh_parameters;
+			Bytes *dh_signature;
+			int sigalg;
+			int curve;
+		} serverKeyExchange;
+		struct {
+			int sigalg;
+			Bytes *signature;
+		} certificateVerify;		
 		Finished finished;
 	} u;
 } Msg;
@@ -118,9 +151,11 @@
 
 typedef struct TlsSec{
 	char *server;	// name of remote; nil for server
-	int ok;	// <0 killed; ==0 in progress; >0 reusable
+	int ok;	// <0 killed; == 0 in progress; >0 reusable
 	RSApub *rsapub;
 	AuthRpc *rpc;	// factotum for rsa private key
+	uchar *psk;	// pre-shared key
+	int psklen;
 	uchar sec[MasterSecretSize];	// master secret
 	uchar crandom[RandomSize];	// client random
 	uchar srandom[RandomSize];	// server random
@@ -128,16 +163,18 @@
 	int vers;			// final version
 	// byte generation and handshake checksum
 	void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int);
-	void (*setFinished)(TlsSec*, MD5state, SHAstate, uchar*, int);
+	void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int);
 	int nfin;
 } TlsSec;
 
 
 enum {
-	TLSVersion = 0x0301,
-	SSL3Version = 0x0300,
-	ProtocolVersion = 0x0301,	// maximum version we speak
-	MinProtoVersion = 0x0300,	// limits on version we accept
+	SSL3Version	= 0x0300,
+	TLS10Version	= 0x0301,
+	TLS11Version	= 0x0302,
+	TLS12Version	= 0x0303,
+	ProtocolVersion	= TLS12Version,	// maximum version we speak
+	MinProtoVersion	= 0x0300,	// limits on version we accept
 	MaxProtoVersion	= 0x03ff,
 };
 
@@ -183,19 +220,20 @@
 	EInternalError = 80,
 	EUserCanceled = 90,
 	ENoRenegotiation = 100,
+	EUnknownPSKidentity = 115,
 	EMax = 256
 };
 
 // cipher suites
 enum {
-	TLS_NULL_WITH_NULL_NULL	 		= 0x0000,
-	TLS_RSA_WITH_NULL_MD5 			= 0x0001,
-	TLS_RSA_WITH_NULL_SHA 			= 0x0002,
-	TLS_RSA_EXPORT_WITH_RC4_40_MD5 		= 0x0003,
-	TLS_RSA_WITH_RC4_128_MD5 		= 0x0004,
-	TLS_RSA_WITH_RC4_128_SHA 		= 0x0005,
+	TLS_NULL_WITH_NULL_NULL			= 0x0000,
+	TLS_RSA_WITH_NULL_MD5			= 0x0001,
+	TLS_RSA_WITH_NULL_SHA			= 0x0002,
+	TLS_RSA_EXPORT_WITH_RC4_40_MD5		= 0x0003,
+	TLS_RSA_WITH_RC4_128_MD5		= 0x0004,
+	TLS_RSA_WITH_RC4_128_SHA		= 0x0005,
 	TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5	= 0X0006,
-	TLS_RSA_WITH_IDEA_CBC_SHA 		= 0X0007,
+	TLS_RSA_WITH_IDEA_CBC_SHA		= 0X0007,
 	TLS_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X0008,
 	TLS_RSA_WITH_DES_CBC_SHA		= 0X0009,
 	TLS_RSA_WITH_3DES_EDE_CBC_SHA		= 0X000A,
@@ -212,12 +250,11 @@
 	TLS_DHE_RSA_WITH_DES_CBC_SHA		= 0X0015,
 	TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA	= 0X0016,
 	TLS_DH_anon_EXPORT_WITH_RC4_40_MD5	= 0x0017,
-	TLS_DH_anon_WITH_RC4_128_MD5 		= 0x0018,
+	TLS_DH_anon_WITH_RC4_128_MD5		= 0x0018,
 	TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA	= 0X0019,
 	TLS_DH_anon_WITH_DES_CBC_SHA		= 0X001A,
 	TLS_DH_anon_WITH_3DES_EDE_CBC_SHA	= 0X001B,
-
-	TLS_RSA_WITH_AES_128_CBC_SHA		= 0X002f,	// aes, aka rijndael with 128 bit blocks
+	TLS_RSA_WITH_AES_128_CBC_SHA		= 0X002F,	// aes, aka rijndael with 128 bit blocks
 	TLS_DH_DSS_WITH_AES_128_CBC_SHA		= 0X0030,
 	TLS_DH_RSA_WITH_AES_128_CBC_SHA		= 0X0031,
 	TLS_DHE_DSS_WITH_AES_128_CBC_SHA	= 0X0032,
@@ -229,7 +266,25 @@
 	TLS_DHE_DSS_WITH_AES_256_CBC_SHA	= 0X0038,
 	TLS_DHE_RSA_WITH_AES_256_CBC_SHA	= 0X0039,
 	TLS_DH_anon_WITH_AES_256_CBC_SHA	= 0X003A,
-	CipherMax
+	TLS_RSA_WITH_AES_128_CBC_SHA256		= 0X003C,
+	TLS_RSA_WITH_AES_256_CBC_SHA256		= 0X003D,
+	TLS_DHE_RSA_WITH_AES_128_CBC_SHA256	= 0X0067,
+	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA	= 0XC013,
+	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA	= 0XC014,
+	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256	= 0xC027,
+	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256	= 0xC023,
+
+	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305	= 0xCCA8,
+	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305	= 0xCCA9,
+	TLS_DHE_RSA_WITH_CHACHA20_POLY1305	= 0xCCAA,
+
+	GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305		= 0xCC13,
+	GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305	= 0xCC14,
+	GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305		= 0xCC15,
+
+	TLS_PSK_WITH_CHACHA20_POLY1305		= 0xCCAB,
+	TLS_PSK_WITH_AES_128_CBC_SHA256		= 0x00AE,
+	TLS_PSK_WITH_AES_128_CBC_SHA		= 0x008C,
 };
 
 // compression methods
@@ -239,9 +294,32 @@
 };
 
 static Algs cipherAlgs[] = {
-	{"rc4_128", "md5",	2 * (16 + MD5dlen), TLS_RSA_WITH_RC4_128_MD5},
-	{"rc4_128", "sha1",	2 * (16 + SHA1dlen), TLS_RSA_WITH_RC4_128_SHA},
-	{"3des_ede_cbc","sha1",2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+	{"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+	{"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_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_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},
+	{"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},
+	{"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
+	{"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},
 };
 
 static uchar compressors[] = {
@@ -248,41 +326,82 @@
 	CompressionNull,
 };
 
-static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...));
-static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...));
+static Namedcurve namedcurves[] = {
+	0x0017, secp256r1,
+};
 
+static uchar pointformats[] = {
+	CompressionNull /* support of uncompressed point format is mandatory */
+};
+
+static struct {
+	DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*);
+	int len;
+} hashfun[] = {
+	[0x01]	{md5,		MD5dlen},
+	[0x02]	{sha1,		SHA1dlen},
+	[0x03]	{sha2_224,	SHA2_224dlen},
+	[0x04]	{sha2_256,	SHA2_256dlen},
+	[0x05]	{sha2_384,	SHA2_384dlen},
+	[0x06]	{sha2_512,	SHA2_512dlen},
+};
+
+// signature algorithms (only RSA and ECDSA at the moment)
+static int sigalgs[] = {
+	0x0603,		/* SHA512 ECDSA */
+	0x0503,		/* SHA384 ECDSA */
+	0x0403,		/* SHA256 ECDSA */
+	0x0203,		/* SHA1 ECDSA */
+
+	0x0601,		/* SHA512 RSA */
+	0x0501,		/* SHA384 RSA */
+	0x0401,		/* SHA256 RSA */
+	0x0201,		/* SHA1 RSA */
+};
+
+static TlsConnection *tlsServer2(int ctl, int hand,
+	uchar *cert, int certlen,
+	char *pskid, uchar *psk, int psklen,
+	int (*trace)(char*fmt, ...), PEMChain *chain);
+static TlsConnection *tlsClient2(int ctl, int hand,
+	uchar *csid, int ncsid,
+	uchar *cert, int certlen,
+	char *pskid, uchar *psk, int psklen,
+	uchar *ext, int extlen, int (*trace)(char*fmt, ...));
 static void	msgClear(Msg *m);
 static char* msgPrint(char *buf, int n, Msg *m);
 static int	msgRecv(TlsConnection *c, Msg *m);
 static int	msgSend(TlsConnection *c, Msg *m, int act);
 static void	tlsError(TlsConnection *c, int err, char *msg, ...);
-#pragma	varargck argpos	tlsError 3
 static int setVersion(TlsConnection *c, int version);
 static int finishedMatch(TlsConnection *c, Finished *f);
 static void tlsConnectionFree(TlsConnection *c);
 
 static int setAlgs(TlsConnection *c, int a);
-static int okCipher(Ints *cv);
+static int okCipher(Ints *cv, int ispsk);
 static int okCompression(Bytes *cv);
 static int initCiphers(void);
-static Ints* makeciphers(void);
+static Ints* makeciphers(int ispsk);
 
-static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom);
-static int	tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd);
+static TlsSec*	tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom);
+static int	tlsSecRSAs(TlsSec *sec, int vers, Bytes *epm);
+static int	tlsSecPSKs(TlsSec *sec, int vers);
 static TlsSec*	tlsSecInitc(int cvers, uchar *crandom);
-static int	tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd);
-static int	tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient);
+static Bytes*	tlsSecRSAc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers);
+static int	tlsSecPSKc(TlsSec *sec, uchar *srandom, int vers);
+static Bytes*	tlsSecDHEc(TlsSec *sec, uchar *srandom, int vers, Bytes *p, Bytes *g, Bytes *Ys);
+static Bytes*	tlsSecECDHEc(TlsSec *sec, uchar *srandom, int vers, int curve, Bytes *Ys);
+static int	tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient);
 static void	tlsSecOk(TlsSec *sec);
 static void	tlsSecKill(TlsSec *sec);
 static void	tlsSecClose(TlsSec *sec);
 static void	setMasterSecret(TlsSec *sec, Bytes *pm);
-static void	serverMasterSecret(TlsSec *sec, uchar *epm, int nepm);
 static void	setSecrets(TlsSec *sec, uchar *kd, int nkd);
-static int	clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm);
-static Bytes *pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype);
-static Bytes *pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm);
-static void	tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient);
-static void	sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient);
+static Bytes*	pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype);
+static Bytes*	pkcs1_decrypt(TlsSec *sec, Bytes *cipher);
+static void	tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient);
+static void	tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient);
+static void	sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient);
 static void	sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label,
 			uchar *seed0, int nseed0, uchar *seed1, int nseed1);
 static int setVers(TlsSec *sec, int version);
@@ -301,11 +420,16 @@
 static int get16(uchar *p);
 static Bytes* newbytes(int len);
 static Bytes* makebytes(uchar* buf, int len);
+static Bytes* mptobytes(mpint* big);
+static mpint* bytestomp(Bytes* bytes);
 static void freebytes(Bytes* b);
 static Ints* newints(int len);
-static Ints* makeints(int* buf, int len);
 static void freeints(Ints* b);
 
+/* x509.c */
+extern mpint*	pkcs1padbuf(uchar *buf, int len, mpint *modulus);
+extern int	asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len);
+
 //================= client/server ========================
 
 //	push TLS onto fd, returning new (application) file descriptor
@@ -329,8 +453,8 @@
 		return -1;
 	}
 	buf[n] = 0;
-	sprint(conn->dir, "#a/tls/%s", buf);
-	sprint(dname, "#a/tls/%s/hand", buf);
+	snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
+	snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
 	hand = open(dname, ORDWR);
 	if(hand < 0){
 		close(ctl);
@@ -337,30 +461,108 @@
 		return -1;
 	}
 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
-	tls = tlsServer2(ctl, hand, conn->cert, conn->certlen, conn->trace);
-	sprint(dname, "#a/tls/%s/data", buf);
+	tls = tlsServer2(ctl, hand,
+		conn->cert, conn->certlen,
+		conn->pskID, conn->psk, conn->psklen,
+		conn->trace, conn->chain);
+	snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
 	data = open(dname, ORDWR);
-	close(fd);
 	close(hand);
 	close(ctl);
-	if(data < 0){
+	if(data < 0 || tls == nil){
+		if(tls != nil)
+			tlsConnectionFree(tls);
 		return -1;
 	}
-	if(tls == nil){
-		close(data);
-		return -1;
-	}
-	if(conn->cert)
-		free(conn->cert);
-	conn->cert = 0;  // client certificates are not yet implemented
+	free(conn->cert);
+	conn->cert = nil;  // client certificates are not yet implemented
 	conn->certlen = 0;
 	conn->sessionIDlen = tls->sid->len;
 	conn->sessionID = emalloc(conn->sessionIDlen);
 	memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen);
+	if(conn->sessionKey != nil
+	&& conn->sessionType != nil
+	&& strcmp(conn->sessionType, "ttls") == 0)
+		tls->sec->prf(
+			conn->sessionKey, conn->sessionKeylen,
+			tls->sec->sec, MasterSecretSize,
+			conn->sessionConst, 
+			tls->sec->crandom, RandomSize,
+			tls->sec->srandom, RandomSize);
 	tlsConnectionFree(tls);
+	close(fd);
 	return data;
 }
 
+static uchar*
+tlsClientExtensions(TLSconn *conn, int *plen)
+{
+	uchar *b, *p;
+	int i, n, m;
+
+	p = b = nil;
+
+	// RFC6066 - Server Name Identification
+	if(conn->serverName != nil){
+		n = strlen(conn->serverName);
+
+		m = p - b;
+		b = erealloc(b, m + 2+2+2+1+2+n);
+		p = b + m;
+
+		put16(p, 0), p += 2;		/* Type: server_name */
+		put16(p, 2+1+2+n), p += 2;	/* Length */
+		put16(p, 1+2+n), p += 2;	/* Server Name list length */
+		*p++ = 0;			/* Server Name Type: host_name */
+		put16(p, n), p += 2;		/* Server Name length */
+		memmove(p, conn->serverName, n);
+		p += n;
+	}
+
+	// ECDHE
+	if(1){
+		m = p - b;
+		b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats));
+		p = b + m;
+
+		n = nelem(namedcurves);
+		put16(p, 0x000a), p += 2;	/* Type: elliptic_curves */
+		put16(p, (n+1)*2), p += 2;	/* Length */
+		put16(p, n*2), p += 2;		/* Elliptic Curves Length */
+		for(i=0; i < n; i++){		/* Elliptic curves */
+			put16(p, namedcurves[i].tlsid);
+			p += 2;
+		}
+
+		n = nelem(pointformats);
+		put16(p, 0x000b), p += 2;	/* Type: ec_point_formats */
+		put16(p, n+1), p += 2;		/* Length */
+		*p++ = n;			/* EC point formats Length */
+		for(i=0; i < n; i++)		/* Elliptic curves point formats */
+			*p++ = pointformats[i];
+	}
+
+	// signature algorithms
+	if(ProtocolVersion >= TLS12Version){
+		n = nelem(sigalgs);
+
+		m = p - b;
+		b = erealloc(b, m + 2+2+2+n*2);
+		p = b + m;
+
+		put16(p, 0x000d), p += 2;
+		put16(p, n*2 + 2), p += 2;
+		put16(p, n*2), p += 2;
+		for(i=0; i < n; i++){
+			put16(p, sigalgs[i]);
+			p += 2;
+		}
+	}
+	
+	*plen = p - b;
+	return b;
+}
+
 //	push TLS onto fd, returning new (application) file descriptor
 //		or -1 if error.
 int
@@ -370,8 +572,9 @@
 	char dname[64];
 	int n, data, ctl, hand;
 	TlsConnection *tls;
+	uchar *ext;
 
-	if(!conn)
+	if(conn == nil)
 		return -1;
 	ctl = open("#a/tls/clone", ORDWR);
 	if(ctl < 0)
@@ -382,20 +585,28 @@
 		return -1;
 	}
 	buf[n] = 0;
-	sprint(conn->dir, "#a/tls/%s", buf);
-	sprint(dname, "#a/tls/%s/hand", buf);
+	snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
+	snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
 	hand = open(dname, ORDWR);
 	if(hand < 0){
 		close(ctl);
 		return -1;
 	}
-	sprint(dname, "#a/tls/%s/data", buf);
+	snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
 	data = open(dname, ORDWR);
-	if(data < 0)
+	if(data < 0){
+		close(hand);
+		close(ctl);
 		return -1;
+	}
 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
-	tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->trace);
-	close(fd);
+	ext = tlsClientExtensions(conn, &n);
+	tls = tlsClient2(ctl, hand,
+		conn->sessionID, conn->sessionIDlen,
+		conn->cert, conn->certlen, 
+		conn->pskID, conn->psk, conn->psklen,
+		ext, n, conn->trace);
+	free(ext);
 	close(hand);
 	close(ctl);
 	if(tls == nil){
@@ -402,18 +613,48 @@
 		close(data);
 		return -1;
 	}
-	conn->certlen = tls->cert->len;
-	conn->cert = emalloc(conn->certlen);
-	memcpy(conn->cert, tls->cert->data, conn->certlen);
+	if(tls->cert != nil){
+		conn->certlen = tls->cert->len;
+		conn->cert = emalloc(conn->certlen);
+		memcpy(conn->cert, tls->cert->data, conn->certlen);
+	} else {
+		conn->certlen = 0;
+		conn->cert = nil;
+	}
 	conn->sessionIDlen = tls->sid->len;
 	conn->sessionID = emalloc(conn->sessionIDlen);
 	memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen);
+	if(conn->sessionKey != nil
+	&& conn->sessionType != nil
+	&& strcmp(conn->sessionType, "ttls") == 0)
+		tls->sec->prf(
+			conn->sessionKey, conn->sessionKeylen,
+			tls->sec->sec, MasterSecretSize,
+			conn->sessionConst, 
+			tls->sec->crandom, RandomSize,
+			tls->sec->srandom, RandomSize);
 	tlsConnectionFree(tls);
+	close(fd);
 	return data;
 }
 
+static int
+countchain(PEMChain *p)
+{
+	int i = 0;
+
+	while (p) {
+		i++;
+		p = p->next;
+	}
+	return i;
+}
+
 static TlsConnection *
-tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...))
+tlsServer2(int ctl, int hand,
+	uchar *cert, int certlen,
+	char *pskid, uchar *psk, int psklen,
+	int (*trace)(char*fmt, ...), PEMChain *chp)
 {
 	TlsConnection *c;
 	Msg m;
@@ -420,7 +661,7 @@
 	Bytes *csid;
 	uchar sid[SidSize], kd[MaxKeyData];
 	char *secrets;
-	int cipher, compressor, nsid, rv;
+	int cipher, compressor, nsid, rv, numcerts, i;
 
 	if(trace)
 		trace("tlsServer2\n");
@@ -445,13 +686,13 @@
 	c->clientVersion = m.u.clientHello.version;
 	if(trace)
 		trace("ClientHello version %x\n", c->clientVersion);
-	if(setVersion(c, m.u.clientHello.version) < 0) {
+	if(setVersion(c, c->clientVersion) < 0) {
 		tlsError(c, EIllegalParameter, "incompatible version");
 		goto Err;
 	}
 
 	memmove(c->crandom, m.u.clientHello.random, RandomSize);
-	cipher = okCipher(m.u.clientHello.ciphers);
+	cipher = okCipher(m.u.clientHello.ciphers, psklen > 0);
 	if(cipher < 0) {
 		// reply with EInsufficientSecurity if we know that's the case
 		if(cipher == -2)
@@ -472,18 +713,28 @@
 
 	csid = m.u.clientHello.sid;
 	if(trace)
-		trace("  cipher %d, compressor %d, csidlen %d\n", cipher, compressor, csid->len);
+		trace("  cipher %x, compressor %x, csidlen %d\n", cipher, compressor, csid->len);
 	c->sec = tlsSecInits(c->clientVersion, csid->data, csid->len, c->crandom, sid, &nsid, c->srandom);
 	if(c->sec == nil){
 		tlsError(c, EHandshakeFailure, "can't initialize security: %r");
 		goto Err;
 	}
-	c->sec->rpc = factotum_rsa_open(cert, ncert);
-	if(c->sec->rpc == nil){
-		tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
-		goto Err;
+	if(psklen > 0){
+		c->sec->psk = psk;
+		c->sec->psklen = psklen;
 	}
-	c->sec->rsapub = X509toRSApub(cert, ncert, nil, 0);
+	if(certlen > 0){
+		c->sec->rpc = factotum_rsa_open(cert, certlen);
+		if(c->sec->rpc == nil){
+			tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
+			goto Err;
+		}
+		c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+		if(c->sec->rsapub == nil){
+			tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate");
+			goto Err;
+		}
+	}
 	msgClear(&m);
 
 	m.tag = HServerHello;
@@ -497,13 +748,18 @@
 		goto Err;
 	msgClear(&m);
 
-	m.tag = HCertificate;
-	m.u.certificate.ncert = 1;
-	m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes));
-	m.u.certificate.certs[0] = makebytes(cert, ncert);
-	if(!msgSend(c, &m, AQueue))
-		goto Err;
-	msgClear(&m);
+	if(certlen > 0){
+		m.tag = HCertificate;
+		numcerts = countchain(chp);
+		m.u.certificate.ncert = 1 + numcerts;
+		m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
+		m.u.certificate.certs[0] = makebytes(cert, certlen);
+		for (i = 0; i < numcerts && chp; i++, chp = chp->next)
+			m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen);
+		if(!msgSend(c, &m, AQueue))
+			goto Err;
+		msgClear(&m);
+	}
 
 	m.tag = HServerHelloDone;
 	if(!msgSend(c, &m, AFlush))
@@ -516,10 +772,30 @@
 		tlsError(c, EUnexpectedMessage, "expected a client key exchange");
 		goto Err;
 	}
-	if(tlsSecSecrets(c->sec, c->version, m.u.clientKeyExchange.key->data, m.u.clientKeyExchange.key->len, kd, c->nsecret) < 0){
-		tlsError(c, EHandshakeFailure, "couldn't set secrets: %r");
+	if(pskid != nil){
+		if(m.u.clientKeyExchange.pskid == nil
+		|| m.u.clientKeyExchange.pskid->len != strlen(pskid)
+		|| memcmp(pskid, m.u.clientKeyExchange.pskid->data, m.u.clientKeyExchange.pskid->len) != 0){
+			tlsError(c, EUnknownPSKidentity, "unknown or missing pskid");
+			goto Err;
+		}
+	}
+	if(certlen > 0){
+		if(tlsSecRSAs(c->sec, c->version, m.u.clientKeyExchange.key) < 0){
+			tlsError(c, EHandshakeFailure, "couldn't set secrets: %r");
+			goto Err;
+		}
+	} else if(psklen > 0){
+		if(tlsSecPSKs(c->sec, c->version) < 0){
+			tlsError(c, EHandshakeFailure, "couldn't set secrets: %r");
+			goto Err;
+		}
+	} else {
+		tlsError(c, EInternalError, "no psk or certificate");
 		goto Err;
 	}
+
+	setSecrets(c->sec, kd, c->nsecret);
 	if(trace)
 		trace("tls secrets\n");
 	secrets = (char*)emalloc(2*c->nsecret);
@@ -535,7 +811,7 @@
 	msgClear(&m);
 
 	/* no CertificateVerify; skip to Finished */
-	if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){
+	if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){
 		tlsError(c, EInternalError, "can't set finished: %r");
 		goto Err;
 	}
@@ -557,7 +833,7 @@
 		goto Err;
 	}
 
-	if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){
+	if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){
 		tlsError(c, EInternalError, "can't set finished: %r");
 		goto Err;
 	}
@@ -580,14 +856,235 @@
 	return 0;
 }
 
+static int
+isDHE(int tlsid)
+{
+	switch(tlsid){
+	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_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, uchar *srandom, int vers, 
+	Bytes *p, Bytes *g, Bytes *Ys)
+{
+	mpint *G, *P, *Y, *K;
+	Bytes *epm;
+	DHstate dh;
+
+	if(p == nil || g == nil || Ys == nil)
+		return nil;
+
+	memmove(sec->srandom, srandom, RandomSize);
+	if(setVers(sec, vers) < 0)
+		return nil;
+
+	epm = 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)
+		goto Out;
+	epm = mptobytes(dh.y);
+	K = dh_finish(&dh, Y);
+	if(K == nil){
+		freebytes(epm);
+		epm = nil;
+		goto Out;
+	}
+	setMasterSecret(sec, mptobytes(K));
+
+Out:
+	mpfree(K);
+	mpfree(Y);
+	mpfree(G);
+	mpfree(P);
+
+	return epm;
+}
+
+static Bytes*
+tlsSecECDHEc(TlsSec *sec, uchar *srandom, int vers, int curve, Bytes *Ys)
+{
+	Namedcurve *nc, *enc;
+	Bytes *epm;
+	ECdomain dom;
+	ECpub *pub;
+	ECpoint K;
+	ECpriv Q;
+
+	if(Ys == nil)
+		return nil;
+
+	enc = &namedcurves[nelem(namedcurves)];
+	for(nc = namedcurves; nc != enc; nc++)
+		if(nc->tlsid == curve)
+			break;
+
+	if(nc == enc)
+		return nil;
+		
+	memmove(sec->srandom, srandom, RandomSize);
+	if(setVers(sec, vers) < 0)
+		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.x = mpnew(0);
+	Q.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);
+	}
+
+	mpfree(K.x);
+	mpfree(K.y);
+	mpfree(Q.x);
+	mpfree(Q.y);
+	mpfree(Q.d);
+
+	ecpubfree(pub);
+	ecdomfree(&dom);
+
+	return epm;
+}
+
+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->crandom, RandomSize);
+	memmove(blob->data+1*RandomSize, c->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);
+	} 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 *csid, int ncsid, int (*trace)(char*fmt, ...))
+tlsClient2(int ctl, int hand,
+	uchar *csid, int ncsid, 
+	uchar *cert, int certlen,
+	char *pskid, uchar *psk, int psklen,
+	uchar *ext, int extlen,
+	int (*trace)(char*fmt, ...))
 {
 	TlsConnection *c;
 	Msg m;
-	uchar kd[MaxKeyData], *epm;
+	uchar kd[MaxKeyData];
 	char *secrets;
-	int creq, nepm, rv;
+	int creq, dhx, rv, cipher;
+	Bytes *epm;
 
 	if(!initCiphers())
 		return nil;
@@ -594,16 +1091,23 @@
 	epm = nil;
 	c = emalloc(sizeof(TlsConnection));
 	c->version = ProtocolVersion;
+
 	c->ctl = ctl;
 	c->hand = hand;
 	c->trace = trace;
 	c->isClient = 1;
 	c->clientVersion = c->version;
+	c->cert = nil;
 
 	c->sec = tlsSecInitc(c->clientVersion, c->crandom);
 	if(c->sec == nil)
 		goto Err;
 
+	if(psklen > 0){
+		c->sec->psk = psk;
+		c->sec->psklen = psklen;
+	}
+
 	/* client hello */
 	memset(&m, 0, sizeof(m));
 	m.tag = HClientHello;
@@ -610,8 +1114,9 @@
 	m.u.clientHello.version = c->clientVersion;
 	memmove(m.u.clientHello.random, c->crandom, RandomSize);
 	m.u.clientHello.sid = makebytes(csid, ncsid);
-	m.u.clientHello.ciphers = makeciphers();
+	m.u.clientHello.ciphers = makeciphers(psklen > 0);
 	m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors));
+	m.u.clientHello.extensions = makebytes(ext, extlen);
 	if(!msgSend(c, &m, AFlush))
 		goto Err;
 	msgClear(&m);
@@ -624,7 +1129,7 @@
 		goto Err;
 	}
 	if(setVersion(c, m.u.serverHello.version) < 0) {
-		tlsError(c, EIllegalParameter, "incompatible version %r");
+		tlsError(c, EIllegalParameter, "incompatible version: %r");
 		goto Err;
 	}
 	memmove(c->srandom, m.u.serverHello.random, RandomSize);
@@ -633,7 +1138,8 @@
 		tlsError(c, EIllegalParameter, "invalid server session identifier");
 		goto Err;
 	}
-	if(!setAlgs(c, m.u.serverHello.cipher)) {
+	cipher = m.u.serverHello.cipher;
+	if((psklen > 0) != isPSK(cipher) || !setAlgs(c, cipher)) {
 		tlsError(c, EIllegalParameter, "invalid cipher suite");
 		goto Err;
 	}
@@ -643,26 +1149,53 @@
 	}
 	msgClear(&m);
 
-	/* certificate */
-	if(!msgRecv(c, &m) || m.tag != HCertificate) {
+	dhx = isDHE(cipher) || isECDHE(cipher);
+	if(!msgRecv(c, &m))
+		goto Err;
+	if(m.tag == HCertificate){
+		if(m.u.certificate.ncert < 1) {
+			tlsError(c, EIllegalParameter, "runt certificate");
+			goto Err;
+		}
+		c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len);
+		msgClear(&m);
+		if(!msgRecv(c, &m))
+			goto Err;
+	} else if(psklen == 0) {
 		tlsError(c, EUnexpectedMessage, "expected a certificate");
 		goto Err;
 	}
-	if(m.u.certificate.ncert < 1) {
-		tlsError(c, EIllegalParameter, "runt certificate");
-		goto Err;
-	}
-	c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len);
-	msgClear(&m);
-
-	/* server key exchange (optional) */
-	if(!msgRecv(c, &m))
-		goto Err;
 	if(m.tag == HServerKeyExchange) {
-		tlsError(c, EUnexpectedMessage, "got an server key exchange");
+		if(dhx){
+			char *err = verifyDHparams(c,
+				m.u.serverKeyExchange.dh_parameters,
+				m.u.serverKeyExchange.dh_signature,
+				m.u.serverKeyExchange.sigalg);
+			if(err != nil){
+				tlsError(c, EBadCertificate, "can't verify dh parameters: %s", err);
+				goto Err;
+			}
+			if(isECDHE(cipher))
+				epm = tlsSecECDHEc(c->sec, c->srandom, c->version,
+					m.u.serverKeyExchange.curve,
+					m.u.serverKeyExchange.dh_Ys);
+			else
+				epm = tlsSecDHEc(c->sec, c->srandom, c->version,
+					m.u.serverKeyExchange.dh_p, 
+					m.u.serverKeyExchange.dh_g,
+					m.u.serverKeyExchange.dh_Ys);
+			if(epm == nil)
+				goto Badcert;
+		} else if(psklen == 0){
+			tlsError(c, EUnexpectedMessage, "got an server key exchange");
+			goto Err;
+		}
+		msgClear(&m);
+		if(!msgRecv(c, &m))
+			goto Err;
+	} else if(dhx){
+		tlsError(c, EUnexpectedMessage, "expected server key exchange");
 		goto Err;
-		// If implementing this later, watch out for rollback attack
-		// described in Wagner Schneier 1996, section 4.4.
 	}
 
 	/* certificate request (optional) */
@@ -680,12 +1213,25 @@
 	}
 	msgClear(&m);
 
-	if(tlsSecSecretc(c->sec, c->sid->data, c->sid->len, c->srandom,
-			c->cert->data, c->cert->len, c->version, &epm, &nepm,
-			kd, c->nsecret) < 0){
-		tlsError(c, EBadCertificate, "invalid x509/rsa certificate");
-		goto Err;
+	if(!dhx){
+		if(c->cert != nil){
+			epm = tlsSecRSAc(c->sec, c->sid->data, c->sid->len, c->srandom,
+				c->cert->data, c->cert->len, c->version);
+			if(epm == nil){
+			Badcert:
+				tlsError(c, EBadCertificate, "bad certificate: %r");
+				goto Err;
+			}
+		} else if(psklen > 0) {
+			if(tlsSecPSKc(c->sec, c->srandom, c->version) < 0)
+				goto Badcert;
+		} else {
+			tlsError(c, EInternalError, "no psk or certificate");
+			goto Err;
+		}
 	}
+
+	setSecrets(c->sec, kd, c->nsecret);
 	secrets = (char*)emalloc(2*c->nsecret);
 	enc64(secrets, 2*c->nsecret, kd, c->nsecret);
 	rv = fprint(c->ctl, "secret %s %s 1 %s", c->digest, c->enc, secrets);
@@ -698,7 +1244,11 @@
 	}
 
 	if(creq) {
-		/* send a zero length certificate */
+		if(cert != nil && certlen > 0){
+			m.u.certificate.ncert = 1;
+			m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
+			m.u.certificate.certs[0] = makebytes(cert, certlen);
+		}		
 		m.tag = HCertificate;
 		if(!msgSend(c, &m, AFlush))
 			goto Err;
@@ -707,17 +1257,71 @@
 
 	/* client key exchange */
 	m.tag = HClientKeyExchange;
-	m.u.clientKeyExchange.key = makebytes(epm, nepm);
-	free(epm);
-	epm = nil;
-	if(m.u.clientKeyExchange.key == nil) {
-		tlsError(c, EHandshakeFailure, "can't set secret: %r");
-		goto Err;
+	if(psklen > 0){
+		if(pskid == nil)
+			pskid = "";
+		m.u.clientKeyExchange.pskid = makebytes((uchar*)pskid, strlen(pskid));
 	}
+	m.u.clientKeyExchange.key = epm;
+	epm = nil;
+	 
 	if(!msgSend(c, &m, AFlush))
 		goto Err;
 	msgClear(&m);
 
+	/* certificate verify */
+	if(creq && cert != nil && certlen > 0) {
+		mpint *signedMP, *paddedHashes;
+		HandshakeHash hsave;
+		uchar buf[512];
+		int buflen;
+
+		c->sec->rpc = factotum_rsa_open(cert, certlen);
+		if(c->sec->rpc == nil){
+			tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
+			goto Err;
+		}
+		c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+		if(c->sec->rsapub == nil){
+			tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate");
+			goto Err;
+		}
+
+		/* save the state for the Finish message */
+		hsave = c->handhash;
+		if(c->version >= TLS12Version){
+			uchar digest[SHA2_256dlen];
+
+			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;
+		}
+		c->handhash = hsave;
+
+		if(buflen <= 0){
+			tlsError(c, EInternalError, "can't encode handshake hashes");
+			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))
+			goto Err;
+		msgClear(&m);
+	} 
+
 	/* change cipher spec */
 	if(fprint(c->ctl, "changecipher") < 0){
 		tlsError(c, EInternalError, "can't enable cipher: %r");
@@ -726,32 +1330,27 @@
 
 	// Cipherchange must occur immediately before Finished to avoid
 	// potential hole;  see section 4.3 of Wagner Schneier 1996.
-	if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){
+	if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){
 		tlsError(c, EInternalError, "can't set finished 1: %r");
 		goto Err;
 	}
 	m.tag = HFinished;
 	m.u.finished = c->finished;
-
 	if(!msgSend(c, &m, AFlush)) {
-		fprint(2, "tlsClient nepm=%d\n", nepm);
 		tlsError(c, EInternalError, "can't flush after client Finished: %r");
 		goto Err;
 	}
 	msgClear(&m);
 
-	if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){
-		fprint(2, "tlsClient nepm=%d\n", nepm);
+	if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){
 		tlsError(c, EInternalError, "can't set finished 0: %r");
 		goto Err;
 	}
 	if(!msgRecv(c, &m)) {
-		fprint(2, "tlsClient nepm=%d\n", nepm);
 		tlsError(c, EInternalError, "can't read server Finished: %r");
 		goto Err;
 	}
 	if(m.tag != HFinished) {
-		fprint(2, "tlsClient nepm=%d\n", nepm);
 		tlsError(c, EUnexpectedMessage, "expected a Finished msg from server");
 		goto Err;
 	}
@@ -780,7 +1379,14 @@
 
 //================= message functions ========================
 
-static uchar sendbuf[9000], *sendp;
+static void
+msgHash(TlsConnection *c, uchar *p, int n)
+{
+	md5(p, n, 0, &c->handhash.md5);
+	sha1(p, n, 0, &c->handhash.sha1);
+	if(c->version >= TLS12Version)
+		sha2_256(p, n, 0, &c->handhash.sha2_256);
+}
 
 static int
 msgSend(TlsConnection *c, Msg *m, int act)
@@ -788,11 +1394,11 @@
 	uchar *p; // sendp = start of new message;  p = write pointer
 	int nn, n, i;
 
-	if(sendp == nil)
-		sendp = sendbuf;
-	p = sendp;
+	if(c->sendp == nil)
+		c->sendp = c->sendbuf;
+	p = c->sendp;
 	if(c->trace)
-		c->trace("send %s", msgPrint((char*)p, (sizeof sendbuf) - (p-sendbuf), m));
+		c->trace("send %s", msgPrint((char*)p, (sizeof(c->sendbuf)) - (p - c->sendbuf), m));
 
 	p[0] = m->tag;	// header - fill in size later
 	p += 4;
@@ -831,6 +1437,15 @@
 		p[0] = n;
 		memmove(p+1, m->u.clientHello.compressors->data, n);
 		p += n+1;
+
+		if(m->u.clientHello.extensions == nil)
+			break;
+		n = m->u.clientHello.extensions->len;
+		if(n == 0)
+			break;
+		put16(p, n);
+		memmove(p+2, m->u.clientHello.extensions->data, n);
+		p += n+2;
 		break;
 	case HServerHello:
 		put16(p, m->u.serverHello.version);
@@ -851,6 +1466,15 @@
 		p += 2;
 		p[0] = m->u.serverHello.compressor;
 		p += 1;
+
+		if(m->u.serverHello.extensions == nil)
+			break;
+		n = m->u.serverHello.extensions->len;
+		if(n == 0)
+			break;
+		put16(p, n);
+		memmove(p+2, m->u.serverHello.extensions->data, n);
+		p += n+2;
 		break;
 	case HServerHelloDone:
 		break;
@@ -858,7 +1482,7 @@
 		nn = 0;
 		for(i = 0; i < m->u.certificate.ncert; i++)
 			nn += 3 + m->u.certificate.certs[i]->len;
-		if(p + 3 + nn - sendbuf > sizeof(sendbuf)) {
+		if(p + 3 + nn - c->sendbuf > sizeof(c->sendbuf)) {
 			tlsError(c, EInternalError, "output buffer too small for certificate");
 			goto Err;
 		}
@@ -871,12 +1495,33 @@
 			p += m->u.certificate.certs[i]->len;
 		}
 		break;
+	case HCertificateVerify:
+		if(m->u.certificateVerify.sigalg != 0){
+			put16(p, m->u.certificateVerify.sigalg);
+			p += 2;
+		}
+		put16(p, m->u.certificateVerify.signature->len);
+		p += 2;
+		memmove(p, m->u.certificateVerify.signature->data, m->u.certificateVerify.signature->len);
+		p += m->u.certificateVerify.signature->len;
+		break;
 	case HClientKeyExchange:
-		n = m->u.clientKeyExchange.key->len;
-		if(c->version != SSL3Version){
+		if(m->u.clientKeyExchange.pskid != nil){
+			n = m->u.clientKeyExchange.pskid->len;
 			put16(p, n);
 			p += 2;
+			memmove(p, m->u.clientKeyExchange.pskid->data, n);
+			p += n;
 		}
+		if(m->u.clientKeyExchange.key == nil)
+			break;
+		n = m->u.clientKeyExchange.key->len;
+		if(c->version != SSL3Version){
+			if(isECDHE(c->cipher))
+				*p++ = n;
+			else
+				put16(p, n), p += 2;
+		}
 		memmove(p, m->u.clientKeyExchange.key->data, n);
 		p += n;
 		break;
@@ -887,20 +1532,18 @@
 	}
 
 	// go back and fill in size
-	n = p-sendp;
-	assert(p <= sendbuf+sizeof(sendbuf));
-	put24(sendp+1, n-4);
+	n = p - c->sendp;
+	assert(p <= c->sendbuf + sizeof(c->sendbuf));
+	put24(c->sendp+1, n-4);
 
 	// remember hash of Handshake messages
-	if(m->tag != HHelloRequest) {
-		md5(sendp, n, 0, &c->hsmd5);
-		sha1(sendp, n, 0, &c->hssha1);
-	}
+	if(m->tag != HHelloRequest)
+		msgHash(c, c->sendp, n);
 
-	sendp = p;
+	c->sendp = p;
 	if(act == AFlush){
-		sendp = sendbuf;
-		if(write(c->hand, sendbuf, p-sendbuf) < 0){
+		c->sendp = c->sendbuf;
+		if(write(c->hand, c->sendbuf, p - c->sendbuf) < 0){
 			fprint(2, "write error: %r\n");
 			goto Err;
 		}
@@ -920,10 +1563,10 @@
 
 	nn = c->ep - c->rp;
 	if(nn < n){
-		if(c->rp != c->buf){
-			memmove(c->buf, c->rp, nn);
-			c->rp = c->buf;
-			c->ep = &c->buf[nn];
+		if(c->rp != c->recvbuf){
+			memmove(c->recvbuf, c->rp, nn);
+			c->rp = c->recvbuf;
+			c->ep = &c->recvbuf[nn];
 		}
 		for(; nn < n; nn += nr) {
 			nr = read(c->hand, &c->rp[nn], n - nn);
@@ -940,7 +1583,7 @@
 static int
 msgRecv(TlsConnection *c, Msg *m)
 {
-	uchar *p;
+	uchar *p, *s;
 	int type, n, nn, i, nsid, nrandom, nciph;
 
 	for(;;) {
@@ -958,8 +1601,8 @@
 		}
 	}
 
-	if(n > sizeof(c->buf)) {
-		tlsError(c, EDecodeError, "handshake message too long %d %d", n, sizeof(c->buf));
+	if(n > sizeof(c->recvbuf)) {
+		tlsError(c, EDecodeError, "handshake message too long %d %d", n, sizeof(c->recvbuf));
 		return 0;
 	}
 
@@ -970,8 +1613,7 @@
 		p = tlsReadN(c, n);
 		if(p == nil)
 			return 0;
-		md5(p, n, 0, &c->hsmd5);
-		sha1(p, n, 0, &c->hssha1);
+		msgHash(c, p, n);
 		m->tag = HClientHello;
 		if(n < 22)
 			goto Short;
@@ -1008,16 +1650,13 @@
 		m->u.clientHello.compressors->data[0] = CompressionNull;
 		goto Ok;
 	}
+	msgHash(c, p, 4);
 
-	md5(p, 4, 0, &c->hsmd5);
-	sha1(p, 4, 0, &c->hssha1);
-
 	p = tlsReadN(c, n);
 	if(p == nil)
 		return 0;
 
-	md5(p, n, 0, &c->hsmd5);
-	sha1(p, n, 0, &c->hssha1);
+	msgHash(c, p, n);
 
 	m->tag = type;
 
@@ -1060,9 +1699,17 @@
 		if(n < 1 || n < p[0]+1 || p[0] == 0)
 			goto Short;
 		nn = p[0];
-		m->u.clientHello.compressors = newbytes(nn);
-		memmove(m->u.clientHello.compressors->data, p+1, nn);
+		m->u.clientHello.compressors = makebytes(p+1, nn);
+		p += nn + 1;
 		n -= nn + 1;
+
+		if(n < 2)
+			break;
+		nn = get16(p);
+		if(nn > n-2)
+			goto Short;
+		m->u.clientHello.extensions = makebytes(p+2, nn);
+		n -= nn + 2;
 		break;
 	case HServerHello:
 		if(n < 2)
@@ -1087,7 +1734,16 @@
 			goto Short;
 		m->u.serverHello.cipher = get16(p);
 		m->u.serverHello.compressor = p[2];
+		p += 3;
 		n -= 3;
+
+		if(n < 2)
+			break;
+		nn = get16(p);
+		if(nn > n-2)
+			goto Short;
+		m->u.serverHello.extensions = makebytes(p+2, nn);
+		n -= nn + 2;
 		break;
 	case HCertificate:
 		if(n < 3)
@@ -1095,7 +1751,7 @@
 		nn = get24(p);
 		p += 3;
 		n -= 3;
-		if(n != nn)
+		if(nn == 0 && n > 0)
 			goto Short;
 		/* certs */
 		i = 0;
@@ -1108,7 +1764,7 @@
 			if(nn > n)
 				goto Short;
 			m->u.certificate.ncert = i+1;
-			m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes));
+			m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes*));
 			m->u.certificate.certs[i] = makebytes(p, nn);
 			p += nn;
 			n -= nn;
@@ -1116,19 +1772,39 @@
 		}
 		break;
 	case HCertificateRequest:
+		if(n < 1)
+			goto Short;
+		nn = p[0];
+		p += 1;
+		n -= 1;
+		if(nn > n)
+			goto Short;
+		m->u.certificateRequest.types = makebytes(p, nn);
+		p += nn;
+		n -= nn;
+		if(c->version >= TLS12Version){
+			if(n < 2)
+				goto Short;
+			nn = get16(p);
+			p += 2;
+			n -= 2;
+			if(nn & 1)
+				goto Short;
+			m->u.certificateRequest.sigalgs = newints(nn>>1);
+			for(i = 0; i < nn; i += 2)
+				m->u.certificateRequest.sigalgs->data[i >> 1] = get16(&p[i]);
+			p += nn;
+			n -= nn;
+
+		}
 		if(n < 2)
 			goto Short;
 		nn = get16(p);
 		p += 2;
 		n -= 2;
-		if(nn < 1 || nn > n)
+		/* nn == 0 can happen; yahoo's servers do it */
+		if(nn != n)
 			goto Short;
-		m->u.certificateRequest.types = makebytes(p, nn);
-		nn = get24(p);
-		p += 3;
-		n -= 3;
-		if(nn == 0 || n != nn)
-			goto Short;
 		/* cas */
 		i = 0;
 		while(n > 0) {
@@ -1140,7 +1816,8 @@
 			if(nn < 1 || nn > n)
 				goto Short;
 			m->u.certificateRequest.nca = i+1;
-			m->u.certificateRequest.cas = erealloc(m->u.certificateRequest.cas, (i+1)*sizeof(Bytes));
+			m->u.certificateRequest.cas = erealloc(
+				m->u.certificateRequest.cas, (i+1)*sizeof(Bytes*));
 			m->u.certificateRequest.cas[i] = makebytes(p, nn);
 			p += nn;
 			n -= nn;
@@ -1149,11 +1826,99 @@
 		break;
 	case HServerHelloDone:
 		break;
+	case HServerKeyExchange:
+		if(isPSK(c->cipher)){
+			if(n < 2)
+				goto Short;
+			nn = get16(p);
+			p += 2, n -= 2;
+			if(nn > n)
+				goto Short;
+			m->u.serverKeyExchange.pskid = makebytes(p, nn);
+			p += nn, n -= nn;
+			if(n == 0)
+				break;
+		}
+		if(n < 2)
+			goto Short;
+		s = p;
+		if(isECDHE(c->cipher)){
+			nn = *p;
+			p++, n--;
+			if(nn != 3 || nn > n) /* not a named curve */
+				goto Short;
+			nn = get16(p);
+			p += 2, n -= 2;
+			m->u.serverKeyExchange.curve = nn;
+
+			nn = *p++, n--;
+			if(nn < 1 || nn > n)
+				goto Short;
+			m->u.serverKeyExchange.dh_Ys = makebytes(p, nn);
+			p += nn, n -= nn;
+		}else if(isDHE(c->cipher)){
+			nn = get16(p);
+			p += 2, n -= 2;
+			if(nn < 1 || nn > n)
+				goto Short;
+			m->u.serverKeyExchange.dh_p = makebytes(p, nn);
+			p += nn, n -= nn;
+	
+			if(n < 2)
+				goto Short;
+			nn = get16(p);
+			p += 2, n -= 2;
+			if(nn < 1 || nn > n)
+				goto Short;
+			m->u.serverKeyExchange.dh_g = makebytes(p, nn);
+			p += nn, n -= nn;
+	
+			if(n < 2)
+				goto Short;
+			nn = get16(p);
+			p += 2, n -= 2;
+			if(nn < 1 || nn > n)
+				goto Short;
+			m->u.serverKeyExchange.dh_Ys = makebytes(p, nn);
+			p += nn, n -= nn;
+		} else {
+			/* should not happen */
+			goto Short;
+		}
+		m->u.serverKeyExchange.dh_parameters = makebytes(s, p - s);
+		if(n >= 2){
+			m->u.serverKeyExchange.sigalg = 0;
+			if(c->version >= TLS12Version){
+				m->u.serverKeyExchange.sigalg = get16(p);
+				p += 2, n -= 2;
+				if(n < 2)
+					goto Short;
+			}
+			nn = get16(p);
+			p += 2, n -= 2;
+			if(nn > 0 && nn <= n){
+				m->u.serverKeyExchange.dh_signature = makebytes(p, nn);
+				n -= nn;
+			}
+		}
+		break;		
 	case HClientKeyExchange:
 		/*
 		 * this message depends upon the encryption selected
 		 * assume rsa.
 		 */
+		if(isPSK(c->cipher)){
+			if(n < 2)
+				goto Short;
+			nn = get16(p);
+			p += 2, n -= 2;
+			if(nn > n)
+				goto Short;
+			m->u.clientKeyExchange.pskid = makebytes(p, nn);
+			p += nn, n -= nn;
+			if(n == 0)
+				break;
+		}
 		if(c->version == SSL3Version)
 			nn = n;
 		else{
@@ -1177,16 +1942,18 @@
 		break;
 	}
 
-	if(type != HClientHello && n != 0)
+	if(type != HClientHello && type != HServerHello && n != 0)
 		goto Short;
 Ok:
 	if(c->trace){
-		char buf[8000];
-		c->trace("recv %s", msgPrint(buf, sizeof buf, m));
+		char *buf;
+		buf = emalloc(8000);
+		c->trace("recv %s", msgPrint(buf, 8000, m));
+		free(buf);
 	}
 	return 1;
 Short:
-	tlsError(c, EDecodeError, "handshake message has invalid length");
+	tlsError(c, EDecodeError, "handshake message (%d) has invalid length", type);
 Err:
 	msgClear(m);
 	return 0;
@@ -1206,9 +1973,11 @@
 		freebytes(m->u.clientHello.sid);
 		freeints(m->u.clientHello.ciphers);
 		freebytes(m->u.clientHello.compressors);
+		freebytes(m->u.clientHello.extensions);
 		break;
 	case HServerHello:
-		freebytes(m->u.clientHello.sid);
+		freebytes(m->u.serverHello.sid);
+		freebytes(m->u.serverHello.extensions);
 		break;
 	case HCertificate:
 		for(i=0; i<m->u.certificate.ncert; i++)
@@ -1217,13 +1986,26 @@
 		break;
 	case HCertificateRequest:
 		freebytes(m->u.certificateRequest.types);
+		freeints(m->u.certificateRequest.sigalgs);
 		for(i=0; i<m->u.certificateRequest.nca; i++)
 			freebytes(m->u.certificateRequest.cas[i]);
 		free(m->u.certificateRequest.cas);
 		break;
+	case HCertificateVerify:
+		freebytes(m->u.certificateVerify.signature);
+		break;
 	case HServerHelloDone:
 		break;
+	case HServerKeyExchange:
+		freebytes(m->u.serverKeyExchange.pskid);
+		freebytes(m->u.serverKeyExchange.dh_p);
+		freebytes(m->u.serverKeyExchange.dh_g);
+		freebytes(m->u.serverKeyExchange.dh_Ys);
+		freebytes(m->u.serverKeyExchange.dh_parameters);
+		freebytes(m->u.serverKeyExchange.dh_signature);
+		break;
 	case HClientKeyExchange:
+		freebytes(m->u.clientKeyExchange.pskid);
 		freebytes(m->u.clientKeyExchange.key);
 		break;
 	case HFinished:
@@ -1239,12 +2021,13 @@
 
 	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, "<%d> [", b->len);
 		for(i=0; i<b->len; i++)
 			bs = seprint(bs, be, "%.2x ", b->data[i]);
+	}
 	bs = seprint(bs, be, "]");
 	if(s1)
 		bs = seprint(bs, be, "%s", s1);
@@ -1290,6 +2073,8 @@
 		bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n");
 		bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n");
 		bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n");
+		if(m->u.clientHello.extensions != nil)
+			bs = bytesPrint(bs, be, "\textensions: ", m->u.clientHello.extensions, "\n");
 		break;
 	case HServerHello:
 		bs = seprint(bs, be, "ServerHello\n");
@@ -1301,6 +2086,8 @@
 		bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n");
 		bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher);
 		bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor);
+		if(m->u.serverHello.extensions != nil)
+			bs = bytesPrint(bs, be, "\textensions: ", m->u.serverHello.extensions, "\n");
 		break;
 	case HCertificate:
 		bs = seprint(bs, be, "Certificate\n");
@@ -1310,16 +2097,45 @@
 	case HCertificateRequest:
 		bs = seprint(bs, be, "CertificateRequest\n");
 		bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n");
+		if(m->u.certificateRequest.sigalgs != nil)
+			bs = intsPrint(bs, be, "\tsigalgs: ", m->u.certificateRequest.sigalgs, "\n");
 		bs = seprint(bs, be, "\tcertificateauthorities\n");
 		for(i=0; i<m->u.certificateRequest.nca; i++)
 			bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n");
 		break;
+	case HCertificateVerify:
+		bs = seprint(bs, be, "HCertificateVerify\n");
+		if(m->u.certificateVerify.sigalg != 0)
+			bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.certificateVerify.sigalg);
+		bs = bytesPrint(bs, be, "\tsignature: ", m->u.certificateVerify.signature,"\n");
+		break;	
 	case HServerHelloDone:
 		bs = seprint(bs, be, "ServerHelloDone\n");
 		break;
+	case HServerKeyExchange:
+		bs = seprint(bs, be, "HServerKeyExchange\n");
+		if(m->u.serverKeyExchange.pskid != nil)
+			bs = bytesPrint(bs, be, "\tpskid: ", m->u.serverKeyExchange.pskid, "\n");
+		if(m->u.serverKeyExchange.dh_parameters == nil)
+			break;
+		if(m->u.serverKeyExchange.curve != 0){
+			bs = seprint(bs, be, "\tcurve: %.4x\n", m->u.serverKeyExchange.curve);
+		} else {
+			bs = bytesPrint(bs, be, "\tdh_p: ", m->u.serverKeyExchange.dh_p, "\n");
+			bs = bytesPrint(bs, be, "\tdh_g: ", m->u.serverKeyExchange.dh_g, "\n");
+		}
+		bs = bytesPrint(bs, be, "\tdh_Ys: ", m->u.serverKeyExchange.dh_Ys, "\n");
+		if(m->u.serverKeyExchange.sigalg != 0)
+			bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.serverKeyExchange.sigalg);
+		bs = bytesPrint(bs, be, "\tdh_parameters: ", m->u.serverKeyExchange.dh_parameters, "\n");
+		bs = bytesPrint(bs, be, "\tdh_signature: ", m->u.serverKeyExchange.dh_signature, "\n");
+		break;
 	case HClientKeyExchange:
 		bs = seprint(bs, be, "HClientKeyExchange\n");
-		bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n");
+		if(m->u.clientKeyExchange.pskid != nil)
+			bs = bytesPrint(bs, be, "\tpskid: ", m->u.clientKeyExchange.pskid, "\n");
+		if(m->u.clientKeyExchange.key != nil)
+			bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n");
 		break;
 	case HFinished:
 		bs = seprint(bs, be, "HFinished\n");
@@ -1362,11 +2178,10 @@
 	if(version == SSL3Version) {
 		c->version = version;
 		c->finished.n = SSL3FinishedLen;
-	}else if(version == TLSVersion){
+	}else {
 		c->version = version;
 		c->finished.n = TLSFinishedLen;
-	}else
-		return -1;
+	}
 	c->verset = 1;
 	return fprint(c->ctl, "version 0x%x", version);
 }
@@ -1375,7 +2190,7 @@
 static int
 finishedMatch(TlsConnection *c, Finished *f)
 {
-	return memcmp(f->verify, c->finished.verify, f->n) == 0;
+	return tsmemcmp(f->verify, c->finished.verify, f->n) == 0;
 }
 
 // free memory associated with TlsConnection struct
@@ -1386,7 +2201,7 @@
 	tlsSecClose(c->sec);
 	freebytes(c->sid);
 	freebytes(c->cert);
-	memset(c, 0, sizeof(c));
+	memset(c, 0, sizeof(*c));
 	free(c);
 }
 
@@ -1393,36 +2208,36 @@
 
 //================= cipher choices ========================
 
-static int weakCipher[CipherMax] =
+static char weakCipher[] =
 {
-	1,	/* TLS_NULL_WITH_NULL_NULL */
-	1,	/* TLS_RSA_WITH_NULL_MD5 */
-	1,	/* TLS_RSA_WITH_NULL_SHA */
-	1,	/* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
-	0,	/* TLS_RSA_WITH_RC4_128_MD5 */
-	0,	/* TLS_RSA_WITH_RC4_128_SHA */
-	1,	/* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */
-	0,	/* TLS_RSA_WITH_IDEA_CBC_SHA */
-	1,	/* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */
-	0,	/* TLS_RSA_WITH_DES_CBC_SHA */
-	0,	/* TLS_RSA_WITH_3DES_EDE_CBC_SHA */
-	1,	/* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */
-	0,	/* TLS_DH_DSS_WITH_DES_CBC_SHA */
-	0,	/* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */
-	1,	/* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */
-	0,	/* TLS_DH_RSA_WITH_DES_CBC_SHA */
-	0,	/* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */
-	1,	/* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */
-	0,	/* TLS_DHE_DSS_WITH_DES_CBC_SHA */
-	0,	/* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */
-	1,	/* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */
-	0,	/* TLS_DHE_RSA_WITH_DES_CBC_SHA */
-	0,	/* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */
-	1,	/* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */
-	1,	/* TLS_DH_anon_WITH_RC4_128_MD5 */
-	1,	/* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */
-	1,	/* TLS_DH_anon_WITH_DES_CBC_SHA */
-	1,	/* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */
+[TLS_NULL_WITH_NULL_NULL]		1,
+[TLS_RSA_WITH_NULL_MD5]			1,
+[TLS_RSA_WITH_NULL_SHA]			1,
+[TLS_RSA_EXPORT_WITH_RC4_40_MD5]	1,
+[TLS_RSA_WITH_RC4_128_MD5]		1,
+[TLS_RSA_WITH_RC4_128_SHA]		1,
+[TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5]	1,
+[TLS_RSA_WITH_IDEA_CBC_SHA]		0,
+[TLS_RSA_EXPORT_WITH_DES40_CBC_SHA]	1,
+[TLS_RSA_WITH_DES_CBC_SHA]		0,
+[TLS_RSA_WITH_3DES_EDE_CBC_SHA]		0,
+[TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA]	1,
+[TLS_DH_DSS_WITH_DES_CBC_SHA]		0,
+[TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA]	0,
+[TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA]	1,
+[TLS_DH_RSA_WITH_DES_CBC_SHA]		0,
+[TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA]	0,
+[TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA]	1,
+[TLS_DHE_DSS_WITH_DES_CBC_SHA]		0,
+[TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA]	0,
+[TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA]	1,
+[TLS_DHE_RSA_WITH_DES_CBC_SHA]		0,
+[TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA]	0,
+[TLS_DH_anon_EXPORT_WITH_RC4_40_MD5]	1,
+[TLS_DH_anon_WITH_RC4_128_MD5]		1,
+[TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA]	1,
+[TLS_DH_anon_WITH_DES_CBC_SHA]		1,
+[TLS_DH_anon_WITH_3DES_EDE_CBC_SHA]	1,
 };
 
 static int
@@ -1432,6 +2247,7 @@
 
 	for(i = 0; i < nelem(cipherAlgs); i++){
 		if(cipherAlgs[i].tlsid == a){
+			c->cipher = a;
 			c->enc = cipherAlgs[i].enc;
 			c->digest = cipherAlgs[i].digest;
 			c->nsecret = cipherAlgs[i].nsecret;
@@ -1444,7 +2260,7 @@
 }
 
 static int
-okCipher(Ints *cv)
+okCipher(Ints *cv, int ispsk)
 {
 	int weak, i, j, c;
 
@@ -1451,10 +2267,14 @@
 	weak = 1;
 	for(i = 0; i < cv->len; i++) {
 		c = cv->data[i];
-		if(c >= CipherMax)
+		if(c >= nelem(weakCipher))
 			weak = 0;
 		else
 			weak &= weakCipher[c];
+		if(isPSK(c) != ispsk)
+			continue;
+		if(isDHE(c) || isECDHE(c))
+			continue;	/* TODO: not implemented for server */
 		for(j = 0; j < nelem(cipherAlgs); j++)
 			if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c)
 				return c;
@@ -1497,13 +2317,13 @@
 	j = open("#a/tls/encalgs", OREAD);
 	if(j < 0){
 		werrstr("can't open #a/tls/encalgs: %r");
-		return 0;
+		goto out;
 	}
 	n = read(j, s, MaxAlgF-1);
 	close(j);
 	if(n <= 0){
 		werrstr("nothing in #a/tls/encalgs: %r");
-		return 0;
+		goto out;
 	}
 	s[n] = 0;
 	n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
@@ -1521,13 +2341,13 @@
 	j = open("#a/tls/hashalgs", OREAD);
 	if(j < 0){
 		werrstr("can't open #a/tls/hashalgs: %r");
-		return 0;
+		goto out;
 	}
 	n = read(j, s, MaxAlgF-1);
 	close(j);
 	if(n <= 0){
 		werrstr("nothing in #a/tls/hashalgs: %r");
-		return 0;
+		goto out;
 	}
 	s[n] = 0;
 	n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
@@ -1543,12 +2363,13 @@
 		if(cipherAlgs[i].ok)
 			nciphers++;
 	}
+out:
 	unlock(&ciphLock);
 	return nciphers;
 }
 
 static Ints*
-makeciphers(void)
+makeciphers(int ispsk)
 {
 	Ints *is;
 	int i, j;
@@ -1555,10 +2376,10 @@
 
 	is = newints(nciphers);
 	j = 0;
-	for(i = 0; i < nelem(cipherAlgs); i++){
-		if(cipherAlgs[i].ok)
+	for(i = 0; i < nelem(cipherAlgs); i++)
+		if(cipherAlgs[i].ok && isPSK(cipherAlgs[i].tlsid) == ispsk)
 			is->data[j++] = cipherAlgs[i].tlsid;
-	}
+	is->len = j;
 	return is;
 }
 
@@ -1615,20 +2436,21 @@
 	char *p;
 	int rv;
 
-	if((p = mptoa(cipher, 16, nil, 0)) == nil)
+	p = mptoa(cipher, 16, nil, 0);
+	mpfree(cipher);
+	if(p == nil)
 		return nil;
 	rv = auth_rpc(rpc, "write", p, strlen(p));
 	free(p);
 	if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok)
 		return nil;
-	mpfree(cipher);
 	return strtomp(rpc->arg, nil, 16, nil);
 }
 
 static void
-factotum_rsa_close(AuthRpc*rpc)
+factotum_rsa_close(AuthRpc *rpc)
 {
-	if(!rpc)
+	if(rpc == nil)
 		return;
 	close(rpc->afd);
 	auth_freerpc(rpc);
@@ -1692,20 +2514,55 @@
 	}
 }
 
+static void
+p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed)
+{
+	uchar ai[SHA2_256dlen], tmp[SHA2_256dlen];
+	SHAstate *s;
+	int n;
+
+	// generate a1
+	s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil);
+	hmac_sha2_256(seed, nseed, key, nkey, ai, s);
+
+	while(nbuf > 0) {
+		s = hmac_sha2_256(ai, SHA2_256dlen, key, nkey, nil, nil);
+		s = hmac_sha2_256(label, nlabel, key, nkey, nil, s);
+		hmac_sha2_256(seed, nseed, key, nkey, tmp, s);
+		n = SHA2_256dlen;
+		if(n > nbuf)
+			n = nbuf;
+		memmove(buf, tmp, n);
+		buf += n;
+		nbuf -= n;
+		hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil);
+		memmove(ai, tmp, SHA2_256dlen);
+	}
+}
+
 // fill buf with md5(args)^sha1(args)
 static void
-tlsPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
+tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
 {
-	int i;
 	int nlabel = strlen(label);
 	int n = (nkey + 1) >> 1;
 
-	for(i = 0; i < nbuf; i++)
-		buf[i] = 0;
+	memset(buf, 0, nbuf);
 	tlsPmd5(buf, nbuf, key, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1);
 	tlsPsha1(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1);
 }
 
+static void
+tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
+{
+	uchar seed[2*RandomSize];
+
+	assert(nseed0+nseed1 <= sizeof(seed));
+	memmove(seed, seed0, nseed0);
+	memmove(seed+nseed0, seed1, nseed1);
+	p_sha256(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed0+nseed1);
+}
+
 /*
  * for setting server session id's
  */
@@ -1742,17 +2599,29 @@
 }
 
 static int
-tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd)
+tlsSecRSAs(TlsSec *sec, int vers, Bytes *epm)
 {
-	if(epm != nil){
-		if(setVers(sec, vers) < 0)
-			goto Err;
-		serverMasterSecret(sec, epm, nepm);
-	}else if(sec->vers != vers){
-		werrstr("mismatched session versions");
+	Bytes *pm;
+
+	if(setVers(sec, vers) < 0)
 		goto Err;
+	if(epm == nil){
+		werrstr("no encrypted premaster secret");
+		goto Err;
 	}
-	setSecrets(sec, kd, nkd);
+	// if the client messed up, just continue as if everything is ok,
+	// to prevent attacks to check for correctly formatted messages.
+	// Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client.
+	pm = pkcs1_decrypt(sec, epm);
+	if(sec->ok < 0 || pm == nil || pm->len != MasterSecretSize || get16(pm->data) != sec->clientVers){
+		fprint(2, "tlsSecRSAs failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n",
+			sec->ok, pm, pm != nil ? get16(pm->data) : -1, sec->clientVers, epm->len);
+		sec->ok = -1;
+		freebytes(pm);
+		pm = newbytes(MasterSecretSize);
+		genrandom(pm->data, MasterSecretSize);
+	}
+	setMasterSecret(sec, pm);
 	return 0;
 Err:
 	sec->ok = -1;
@@ -1759,6 +2628,17 @@
 	return -1;
 }
 
+static int
+tlsSecPSKs(TlsSec *sec, int vers)
+{
+	if(setVers(sec, vers) < 0){
+		sec->ok = -1;
+		return -1;
+	}
+	setMasterSecret(sec, newbytes(sec->psklen));
+	return 0;
+}
+
 static TlsSec*
 tlsSecInitc(int cvers, uchar *crandom)
 {
@@ -1771,40 +2651,49 @@
 }
 
 static int
-tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd)
+tlsSecPSKc(TlsSec *sec, uchar *srandom, int vers)
 {
+	memmove(sec->srandom, srandom, RandomSize);
+	if(setVers(sec, vers) < 0){
+		sec->ok = -1;
+		return -1;
+	}
+	setMasterSecret(sec, newbytes(sec->psklen));
+	return 0;
+}
+
+static Bytes*
+tlsSecRSAc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers)
+{
 	RSApub *pub;
+	Bytes *pm, *epm;
 
-	pub = nil;
-
 	USED(sid);
 	USED(nsid);
 	
 	memmove(sec->srandom, srandom, RandomSize);
-
 	if(setVers(sec, vers) < 0)
 		goto Err;
-
 	pub = X509toRSApub(cert, ncert, nil, 0);
 	if(pub == nil){
 		werrstr("invalid x509/rsa certificate");
 		goto Err;
 	}
-	if(clientMasterSecret(sec, pub, epm, nepm) < 0)
-		goto Err;
+	pm = newbytes(MasterSecretSize);
+	put16(pm->data, sec->clientVers);
+	genrandom(pm->data+2, MasterSecretSize - 2);
+	epm = pkcs1_encrypt(pm, pub, 2);
+	setMasterSecret(sec, pm);
 	rsapubfree(pub);
-	setSecrets(sec, kd, nkd);
-	return 0;
-
+	if(epm != nil)
+		return epm;
 Err:
-	if(pub != nil)
-		rsapubfree(pub);
 	sec->ok = -1;
-	return -1;
+	return nil;
 }
 
 static int
-tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient)
+tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient)
 {
 	if(sec->nfin != nfin){
 		sec->ok = -1;
@@ -1811,9 +2700,10 @@
 		werrstr("invalid finished exchange");
 		return -1;
 	}
-	md5.malloced = 0;
-	sha1.malloced = 0;
-	(*sec->setFinished)(sec, md5, sha1, fin, isclient);
+	hsh.md5.malloced = 0;
+	hsh.sha1.malloced = 0;
+	hsh.sha2_256.malloced = 0;
+	(*sec->setFinished)(sec, hsh, fin, isclient);
 	return 1;
 }
 
@@ -1836,7 +2726,7 @@
 static void
 tlsSecClose(TlsSec *sec)
 {
-	if(!sec)
+	if(sec == nil)
 		return;
 	factotum_rsa_close(sec->rpc);
 	free(sec->server);
@@ -1850,13 +2740,14 @@
 		sec->setFinished = sslSetFinished;
 		sec->nfin = SSL3FinishedLen;
 		sec->prf = sslPRF;
-	}else if(v == TLSVersion){
-		sec->setFinished = tlsSetFinished;
+	}else if(v < TLS12Version) {
+		sec->setFinished = tls10SetFinished;
 		sec->nfin = TLSFinishedLen;
-		sec->prf = tlsPRF;
-	}else{
-		werrstr("invalid version");
-		return -1;
+		sec->prf = tls10PRF;
+	}else {
+		sec->setFinished = tls12SetFinished;
+		sec->nfin = TLSFinishedLen;
+		sec->prf = tls12PRF;
 	}
 	sec->vers = v;
 	return 0;
@@ -1877,74 +2768,37 @@
 }
 
 /*
- * set the master secret from the pre-master secret.
+ * set the master secret from the pre-master secret,
+ * destroys premaster.
  */
 static void
 setMasterSecret(TlsSec *sec, Bytes *pm)
 {
-	(*sec->prf)(sec->sec, MasterSecretSize, pm->data, MasterSecretSize, "master secret",
-			sec->crandom, RandomSize, sec->srandom, RandomSize);
-}
+	if(sec->psklen > 0){
+		Bytes *opm = pm;
+		uchar *p;
 
-static void
-serverMasterSecret(TlsSec *sec, uchar *epm, int nepm)
-{
-	Bytes *pm;
+		/* concatenate psk to pre-master secret */
+		pm = newbytes(4 + opm->len + sec->psklen);
+		p = pm->data;
+		put16(p, opm->len), p += 2;
+		memmove(p, opm->data, opm->len), p += opm->len;
+		put16(p, sec->psklen), p += 2;
+		memmove(p, sec->psk, sec->psklen);
 
-	pm = pkcs1_decrypt(sec, epm, nepm);
-
-	// if the client messed up, just continue as if everything is ok,
-	// to prevent attacks to check for correctly formatted messages.
-	// Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client.
-	if(sec->ok < 0 || pm == nil || get16(pm->data) != sec->clientVers){
-		fprint(2, "serverMasterSecret failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n",
-			sec->ok, pm, pm ? get16(pm->data) : -1, sec->clientVers, nepm);
-		sec->ok = -1;
-		if(pm != nil)
-			freebytes(pm);
-		pm = newbytes(MasterSecretSize);
-		genrandom(pm->data, MasterSecretSize);
+		memset(opm->data, 0, opm->len);
+		freebytes(opm);
 	}
-	setMasterSecret(sec, pm);
-	memset(pm->data, 0, pm->len);	
-	freebytes(pm);
-}
 
-static int
-clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm)
-{
-	Bytes *pm, *key;
+	(*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret",
+			sec->crandom, RandomSize, sec->srandom, RandomSize);
 
-	pm = newbytes(MasterSecretSize);
-	put16(pm->data, sec->clientVers);
-	genrandom(pm->data+2, MasterSecretSize - 2);
-
-	setMasterSecret(sec, pm);
-
-	key = pkcs1_encrypt(pm, pub, 2);
-	memset(pm->data, 0, pm->len);
+	memset(pm->data, 0, pm->len);	
 	freebytes(pm);
-	if(key == nil){
-		werrstr("tls pkcs1_encrypt failed");
-		return -1;
-	}
-
-	*nepm = key->len;
-	*epm = malloc(*nepm);
-	if(*epm == nil){
-		freebytes(key);
-		werrstr("out of memory");
-		return -1;
-	}
-	memmove(*epm, key->data, *nepm);
-
-	freebytes(key);
-
-	return 1;
 }
 
 static void
-sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient)
+sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient)
 {
 	DigestState *s;
 	uchar h0[MD5dlen], h1[SHA1dlen], pad[48];
@@ -1955,21 +2809,21 @@
 	else
 		label = "SRVR";
 
-	md5((uchar*)label, 4, nil, &hsmd5);
-	md5(sec->sec, MasterSecretSize, nil, &hsmd5);
+	md5((uchar*)label, 4, nil, &hsh.md5);
+	md5(sec->sec, MasterSecretSize, nil, &hsh.md5);
 	memset(pad, 0x36, 48);
-	md5(pad, 48, nil, &hsmd5);
-	md5(nil, 0, h0, &hsmd5);
+	md5(pad, 48, nil, &hsh.md5);
+	md5(nil, 0, h0, &hsh.md5);
 	memset(pad, 0x5C, 48);
 	s = md5(sec->sec, MasterSecretSize, nil, nil);
 	s = md5(pad, 48, nil, s);
 	md5(h0, MD5dlen, finished, s);
 
-	sha1((uchar*)label, 4, nil, &hssha1);
-	sha1(sec->sec, MasterSecretSize, nil, &hssha1);
+	sha1((uchar*)label, 4, nil, &hsh.sha1);
+	sha1(sec->sec, MasterSecretSize, nil, &hsh.sha1);
 	memset(pad, 0x36, 40);
-	sha1(pad, 40, nil, &hssha1);
-	sha1(nil, 0, h1, &hssha1);
+	sha1(pad, 40, nil, &hsh.sha1);
+	sha1(nil, 0, h1, &hsh.sha1);
 	memset(pad, 0x5C, 40);
 	s = sha1(sec->sec, MasterSecretSize, nil, nil);
 	s = sha1(pad, 40, nil, s);
@@ -1978,27 +2832,43 @@
 
 // fill "finished" arg with md5(args)^sha1(args)
 static void
-tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient)
+tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient)
 {
 	uchar h0[MD5dlen], h1[SHA1dlen];
 	char *label;
 
 	// get current hash value, but allow further messages to be hashed in
-	md5(nil, 0, h0, &hsmd5);
-	sha1(nil, 0, h1, &hssha1);
+	md5(nil, 0, h0, &hsh.md5);
+	sha1(nil, 0, h1, &hsh.sha1);
 
 	if(isClient)
 		label = "client finished";
 	else
 		label = "server finished";
-	tlsPRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen);
+	tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen);
 }
 
 static void
+tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient)
+{
+	uchar seed[SHA2_256dlen];
+	char *label;
+
+	// get current hash value, but allow further messages to be hashed in
+	sha2_256(nil, 0, seed, &hsh.sha2_256);
+
+	if(isClient)
+		label = "client finished";
+	else
+		label = "server finished";
+	p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen);
+}
+
+static void
 sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
 {
-	DigestState *s;
 	uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26];
+	DigestState *s;
 	int i, n, len;
 
 	USED(label);
@@ -2027,10 +2897,7 @@
 static mpint*
 bytestomp(Bytes* bytes)
 {
-	mpint* ans;
-
-	ans = betomp(bytes->data, bytes->len, nil);
-	return ans;
+	return betomp(bytes->data, bytes->len, nil);
 }
 
 /*
@@ -2039,13 +2906,13 @@
 static Bytes*
 mptobytes(mpint* big)
 {
-	int n, m;
-	uchar *a;
 	Bytes* ans;
+	int n;
 
 	n = (mpsignif(big)+7)/8;
-	m = mptobe(big, nil, n, &a);
-	ans = makebytes(a, m);
+	if(n == 0) n = 1;
+	ans = newbytes(n);
+	mptober(big, ans->data, ans->len);
 	return ans;
 }
 
@@ -2063,6 +2930,7 @@
 	mpfree(x);
 	ybytes = mptobytes(y);
 	ylen = ybytes->len;
+	mpfree(y);
 
 	if(ylen < modlen) {
 		a = newbytes(modlen);
@@ -2078,7 +2946,6 @@
 		freebytes(ybytes);
 		ybytes = a;
 	}
-	mpfree(y);
 	return ybytes;
 }
 
@@ -2119,36 +2986,34 @@
 // decrypt data according to PKCS#1, with given key.
 // expect a block type of 2.
 static Bytes*
-pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm)
+pkcs1_decrypt(TlsSec *sec, Bytes *cipher)
 {
-	Bytes *eb, *ans = nil;
+	Bytes *eb;
 	int i, modlen;
 	mpint *x, *y;
 
 	modlen = (mpsignif(sec->rsapub->n)+7)/8;
-	if(nepm != modlen)
+	if(cipher->len != modlen)
 		return nil;
-	x = betomp(epm, nepm, nil);
+	x = bytestomp(cipher);
 	y = factotum_rsa_decrypt(sec->rpc, x);
 	if(y == nil)
 		return nil;
-	eb = mptobytes(y);
-	if(eb->len < modlen){ // pad on left with zeros
-		ans = newbytes(modlen);
-		memset(ans->data, 0, modlen-eb->len);
-		memmove(ans->data+modlen-eb->len, eb->data, eb->len);
-		freebytes(eb);
-		eb = ans;
-	}
+	eb = newbytes(modlen);
+	mptober(y, eb->data, eb->len);
+	mpfree(y);
 	if(eb->data[0] == 0 && eb->data[1] == 2) {
-		for(i = 2; i < modlen; i++)
+		for(i = 2; i < eb->len; i++)
 			if(eb->data[i] == 0)
 				break;
-		if(i < modlen - 1)
-			ans = makebytes(eb->data+i+1, modlen-(i+1));
+		if(++i < eb->len){
+			eb->len -= i;
+			memmove(eb->data, eb->data+i, eb->len);
+			return eb;
+		}
 	}
 	freebytes(eb);
-	return ans;
+	return nil;
 }
 
 
@@ -2161,10 +3026,10 @@
 	if(n==0)
 		n=1;
 	p = malloc(n);
-	if(p == nil){
-		exits("out of memory");
-	}
+	if(p == nil)
+		sysfatal("out of memory");
 	memset(p, 0, n);
+	setmalloctag(p, getcallerpc(&n));
 	return p;
 }
 
@@ -2173,11 +3038,11 @@
 {
 	if(ReallocN == 0)
 		ReallocN = 1;
-	if(!ReallocP)
+	if(ReallocP == nil)
 		ReallocP = emalloc(ReallocN);
-	else if(!(ReallocP = realloc(ReallocP, ReallocN))){
-		exits("out of memory");
-	}
+	else if((ReallocP = realloc(ReallocP, ReallocN)) == nil)
+		sysfatal("out of memory");
+	setrealloctag(ReallocP, getcallerpc(&ReallocP));
 	return(ReallocP);
 }
 
@@ -2223,20 +3088,14 @@
 	return (p[0]<<8)|p[1];
 }
 
-/* ANSI offsetof() */
-#define OFFSET(x, s) ((int)(&(((s*)0)->x)))
-
-/*
- * malloc and return a new Bytes structure capable of
- * holding len bytes. (len >= 0)
- * Used to use crypt_malloc, which aborts if malloc fails.
- */
 static Bytes*
 newbytes(int len)
 {
 	Bytes* ans;
 
-	ans = (Bytes*)malloc(OFFSET(data[0], Bytes) + len);
+	if(len < 0)
+		abort();
+	ans = emalloc(sizeof(Bytes) + len);
 	ans->len = len;
 	return ans;
 }
@@ -2257,8 +3116,7 @@
 static void
 freebytes(Bytes* b)
 {
-	if(b != nil)
-		free(b);
+	free(b);
 }
 
 /* len is number of ints */
@@ -2267,25 +3125,15 @@
 {
 	Ints* ans;
 
-	ans = (Ints*)malloc(OFFSET(data[0], Ints) + len*sizeof(int));
+	if(len < 0 || len > ((uint)-1>>1)/sizeof(int))
+		abort();
+	ans = emalloc(sizeof(Ints) + len*sizeof(int));
 	ans->len = len;
 	return ans;
 }
 
-static Ints*
-makeints(int* buf, int len)
-{
-	Ints* ans;
-
-	ans = newints(len);
-	if(len > 0)
-		memmove(ans->data, buf, len*sizeof(int));
-	return ans;
-}
-
 static void
 freeints(Ints* b)
 {
-	if(b != nil)
-		free(b);
+	free(b);
 }
--- /dev/null
+++ b/libsec/tsmemcmp.c
@@ -1,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+/*
+ * timing safe memcmp()
+ */
+int
+tsmemcmp(void *a1, void *a2, ulong n)
+{
+	int lt, gt, c1, c2, r, m;
+	uchar *s1, *s2;
+
+	r = m = 0;
+	s1 = a1;
+	s2 = a2;
+	while(n--){
+		c1 = *s1++;
+		c2 = *s2++;
+		lt = (c1 - c2) >> 8;
+		gt = (c2 - c1) >> 8;
+		r |= (lt - gt) & ~m;
+		m |= lt | gt;
+	}
+	return r;
+}
--- a/libsec/x509.c
+++ b/libsec/x509.c
@@ -3,11 +3,6 @@
 #include <mp.h>
 #include <libsec.h>
 
-typedef DigestState*(*DigestFun)(uchar*,ulong,uchar*,DigestState*);
-
-/* ANSI offsetof, backwards. */
-#define	OFFSETOF(a, b)	offsetof(b, a)
-
 /*=============================================================*/
 /*  general ASN1 declarations and parsing
  *
@@ -40,6 +35,7 @@
 #define REAL 9
 #define ENUMERATED 10
 #define EMBEDDED_PDV 11
+#define UTF8String 12
 #define SEQUENCE 16		/* also SEQUENCE OF */
 #define SETOF 17				/* also SETOF OF */
 #define NumericString 18
@@ -62,13 +58,13 @@
 
 struct Ints {
 	int	len;
-	int	data[1];
+	int	data[];
 };
 
 struct Bits {
 	int	len;		/* number of bytes */
 	int	unusedbits;	/* unused bits in last byte */
-	uchar	data[1];	/* most-significant bit first */
+	uchar	data[];		/* most-significant bit first */
 };
 
 struct Tag {
@@ -134,15 +130,12 @@
 static int	is_string(Elem* pe, char** pstring);
 static int	is_time(Elem* pe, char** ptime);
 static int	decode(uchar* a, int alen, Elem* pelem);
-static int	decode_seq(uchar* a, int alen, Elist** pelist);
-static int	decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval);
 static int	encode(Elem e, Bytes** pbytes);
 static int	oid_lookup(Ints* o, Ints** tab);
 static void	freevalfields(Value* v);
 static mpint	*asn1mpint(Elem *e);
+static void	edump(Elem);
 
-
-
 #define TAG_MASK 0x1F
 #define CONSTR_MASK 0x20
 #define CLASS_MASK 0xC0
@@ -168,10 +161,10 @@
 	if(n==0)
 		n=1;
 	p = malloc(n);
-	if(p == nil){
-		exits("out of memory");
-	}
+	if(p == nil)
+		sysfatal("out of memory");
 	memset(p, 0, n);
+	setmalloctag(p, getcallerpc(&n));
 	return p;
 }
 
@@ -178,14 +171,13 @@
 static char*
 estrdup(char *s)
 {
-	char *d, *d0;
+	char *d;
+	int n;
 
-	if(!s)
-		return 0;
-	d = d0 = emalloc(strlen(s)+1);
-	while(*d++ = *s++)
-		;
-	return d0;
+	n = strlen(s)+1;
+	d = emalloc(n);
+	memmove(d, s, n);
+	return d;
 }
 
 
@@ -199,40 +191,15 @@
 decode(uchar* a, int alen, Elem* pelem)
 {
 	uchar* p = a;
+	int err;
 
-	return  ber_decode(&p, &a[alen], pelem);
+	err = ber_decode(&p, &a[alen], pelem);
+	if(err == ASN_OK && p != &a[alen])
+		err = ASN_EVALLEN;
+	return err;
 }
 
 /*
- * Like decode, but continue decoding after first element
- * of array ends.
- */
-static int
-decode_seq(uchar* a, int alen, Elist** pelist)
-{
-	uchar* p = a;
-
-	return seq_decode(&p, &a[alen], -1, 1, pelist);
-}
-
-/*
- * Decode the whole array as a BER encoding of an ASN1 value,
- * (i.e., the part after the tag and length).
- * Assume the value is encoded as universal tag "kind".
- * The constr arg is 1 if the value is constructed, 0 if primitive.
- * If there's an error, the return string will contain the error.
- * Depending on the error, the returned value may or may not
- * be nil.
- */
-static int
-decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval)
-{
-	uchar* p = a;
-
-	return value_decode(&p, &a[alen], alen, kind, isconstr, pval);
-}
-
-/*
  * All of the following decoding routines take arguments:
  *	uchar **pp;
  *	uchar *pend;
@@ -255,6 +222,7 @@
 	Tag tag;
 	Value val;
 
+	memset(pelem, 0, sizeof(*pelem));
 	err = tag_decode(pp, pend, &tag, &isconstr);
 	if(err == ASN_OK) {
 		err = length_decode(pp, pend, &length);
@@ -316,8 +284,6 @@
 		v = *p++;
 		if(v&0x80)
 			err = int_decode(&p, pend, v&0x7F, 1, &num);
-		else if(v == 0x80)
-			num = -1;
 		else
 			num = v;
 	}
@@ -398,8 +364,7 @@
 				pval->u.bitstringval = makebits(0, 0, 0);
 				p += 2;
 			}
-			else
-				/* TODO: recurse and concat results */
+			else	/* TODO: recurse and concat results */
 				err = ASN_EUNIMPL;
 		}
 		else {
@@ -513,7 +478,7 @@
 			pval->u.setval = vl;
 		}
 		break;
-
+	case UTF8String:
 	case NumericString:
 	case PrintableString:
 	case TeletexString:
@@ -573,7 +538,7 @@
 			err = ASN_ETOOBIG;
 		else {
 			if(!unsgned && count > 0 && count < 4 && (*p&0x80))
-				num = -1;		// set all bits, initially
+				num = -1;	/* set all bits, initially */
 			while(count--)
 				num = (num << 8)|(*p++);
 		}
@@ -661,18 +626,17 @@
 			switch(elem.val.tag) {
 			case VOctets:
 				newans = catbytes(ans, elem.val.u.octetsval);
+				freevalfields(&elem.val);
 				freebytes(ans);
 				ans = newans;
 				break;
 
 			case VEOC:
-				if(length != -1) {
-					p = pold;
-					err = ASN_EINVAL;
-				}
-				goto cloop_done;
-
+				if(length == -1)
+					goto cloop_done;
+				/* no break */
 			default:
+				freevalfields(&elem.val);
 				p = pold;
 				err = ASN_EINVAL;
 				goto cloop_done;
@@ -679,7 +643,10 @@
 			}
 		}
 cloop_done:
-		;
+		if(err != ASN_OK){
+			freebytes(ans);
+			ans = nil;
+		}
 	}
 	*pp = p;
 	*pbytes = ans;
@@ -732,7 +699,9 @@
 			else
 				lve = mkel(elem, lve);
 		}
-		if(err == ASN_OK) {
+		if(err != ASN_OK)
+			freeelist(lve);
+		else {
 			/* reverse back to original order */
 			while(lve != nil) {
 				lveold = lve;
@@ -972,8 +941,8 @@
 				memmove(p, bb->data, bb->len);
 			p += bb->len;
 		}
-			else
-				err = ASN_EINVAL;
+		else
+			err = ASN_EINVAL;
 		break;
 
 	case NULLTAG:
@@ -1014,6 +983,7 @@
 		}
 		break;
 
+	case UTF8String:
 	case NumericString:
 	case PrintableString:
 	case TeletexString:
@@ -1189,21 +1159,8 @@
 static int
 is_bigint(Elem* pe, Bytes** pbigint)
 {
-	int v, n, i;
-
-	if(pe->tag.class == Universal && pe->tag.num == INTEGER) {
-		if(pe->val.tag == VBigInt)
-			*pbigint = pe->val.u.bigintval;
-		else if(pe->val.tag == VInt){
-			v = pe->val.u.intval;
-			for(n = 1; n < 4; n++)
-				if((1 << (8 * n)) > v)
-					break;
-			*pbigint = newbytes(n);
-			for(i = 0; i < n; i++)
-				(*pbigint)->data[i] = (v >> ((n - 1 - i) * 8));
-		}else
-			return 0;
+	if(pe->tag.class == Universal && pe->tag.num == INTEGER && pe->val.tag == VBigInt) {
+		*pbigint = pe->val.u.bigintval;
 		return 1;
 	}
 	return 0;
@@ -1244,6 +1201,7 @@
 {
 	if(pe->tag.class == Universal) {
 		switch(pe->tag.num) {
+		case UTF8String:
 		case NumericString:
 		case PrintableString:
 		case TeletexString:
@@ -1285,7 +1243,9 @@
 {
 	Bytes* ans;
 
-	ans = (Bytes*)emalloc(OFFSETOF(data[0], Bytes) + len);
+	if(len < 0)
+		abort();
+	ans = emalloc(sizeof(Bytes) + len);
 	ans->len = len;
 	return ans;
 }
@@ -1306,8 +1266,7 @@
 static void
 freebytes(Bytes* b)
 {
-	if(b != nil)
-		free(b);
+	free(b);
 }
 
 /*
@@ -1345,7 +1304,9 @@
 {
 	Ints* ans;
 
-	ans = (Ints*)emalloc(OFFSETOF(data[0], Ints) + len*sizeof(int));
+	if(len < 0 || len > ((uint)-1>>1)/sizeof(int))
+		abort();
+	ans = emalloc(sizeof(Ints) + len*sizeof(int));
 	ans->len = len;
 	return ans;
 }
@@ -1356,8 +1317,7 @@
 	Ints* ans;
 
 	ans = newints(len);
-	if(len > 0)
-		memmove(ans->data, buf, len*sizeof(int));
+	memmove(ans->data, buf, len*sizeof(int));
 	return ans;
 }
 
@@ -1364,8 +1324,7 @@
 static void
 freeints(Ints* b)
 {
-	if(b != nil)
-		free(b);
+	free(b);
 }
 
 /* len is number of bytes */
@@ -1374,7 +1333,9 @@
 {
 	Bits* ans;
 
-	ans = (Bits*)emalloc(OFFSETOF(data[0], Bits) + len);
+	if(len < 0)
+		abort();
+	ans = emalloc(sizeof(Bits) + len);
 	ans->len = len;
 	ans->unusedbits = 0;
 	return ans;
@@ -1394,8 +1355,7 @@
 static void
 freebits(Bits* b)
 {
-	if(b != nil)
-		free(b);
+	free(b);
 }
 
 static Elist*
@@ -1404,6 +1364,7 @@
 	Elist* el;
 
 	el = (Elist*)emalloc(sizeof(Elist));
+	setmalloctag(el, getcallerpc(&e));
 	el->hd = e;
 	el->tl = tail;
 	return el;
@@ -1461,7 +1422,7 @@
 		freeints(v->u.objidval);
 		break;
 	case VString:
-		if (v->u.stringval)
+		if(v->u.stringval)
 			free(v->u.stringval);
 		break;
 	case VSeq:
@@ -1468,15 +1429,13 @@
 		el = v->u.seqval;
 		for(l = el; l != nil; l = l->tl)
 			freevalfields(&l->hd.val);
-		if (el)
-			freeelist(el);
+		freeelist(el);
 		break;
 	case VSet:
 		el = v->u.setval;
 		for(l = el; l != nil; l = l->tl)
 			freevalfields(&l->hd.val);
-		if (el)
-			freeelist(el);
+		freeelist(el);
 		break;
 	}
 }
@@ -1564,6 +1523,7 @@
 	Bytes*	publickey;
 	int	signature_alg;
 	Bytes*	signature;
+	int	curve;
 } CertX509;
 
 /* Algorithm object-ids */
@@ -1572,45 +1532,140 @@
 	ALG_md2WithRSAEncryption,
 	ALG_md4WithRSAEncryption,
 	ALG_md5WithRSAEncryption,
+
 	ALG_sha1WithRSAEncryption,
+	ALG_sha1WithRSAEncryptionOiw,
+
+	ALG_sha256WithRSAEncryption,
+	ALG_sha384WithRSAEncryption,
+	ALG_sha512WithRSAEncryption,
+	ALG_sha224WithRSAEncryption,
+
+	ALG_ecPublicKey,
+	ALG_sha1WithECDSA,
+	ALG_sha256WithECDSA,
+	ALG_sha384WithECDSA,
+	ALG_sha512WithECDSA,
+
 	ALG_md5,
+	ALG_sha1,
+	ALG_sha256,
+	ALG_sha384,
+	ALG_sha512,
+	ALG_sha224,
+
 	NUMALGS
 };
-typedef struct Ints7 {
+
+typedef struct Ints15 {
 	int		len;
-	int		data[7];
-} Ints7;
-static Ints7 oid_rsaEncryption = {7, 1, 2, 840, 113549, 1, 1, 1 };
-static Ints7 oid_md2WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 2 };
-static Ints7 oid_md4WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 3 };
-static Ints7 oid_md5WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 4 };
-static Ints7 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 };
-static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 };
+	int		data[15];
+} Ints15;
+
+typedef struct DigestAlg {
+	int		alg;
+	DigestState*	(*fun)(uchar*,ulong,uchar*,DigestState*);
+	int		len;
+} DigestAlg;
+
+static DigestAlg alg_md5 = { ALG_md5, md5, MD5dlen};
+static DigestAlg alg_sha1 = { ALG_sha1, sha1, SHA1dlen };
+static DigestAlg alg_sha256 = { ALG_sha256, sha2_256, SHA2_256dlen };
+static DigestAlg alg_sha384 = { ALG_sha384, sha2_384, SHA2_384dlen };
+static DigestAlg alg_sha512 = { ALG_sha512, sha2_512, SHA2_512dlen };
+static DigestAlg alg_sha224 = { ALG_sha224, sha2_224, SHA2_224dlen };
+
+/* maximum length of digest output of the digest algs above */
+enum {
+	MAXdlen = SHA2_512dlen,
+};
+
+static Ints15 oid_rsaEncryption = {7, 1, 2, 840, 113549, 1, 1, 1 };
+
+static Ints15 oid_md2WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 2 };
+static Ints15 oid_md4WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 3 };
+static Ints15 oid_md5WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 4 };
+static Ints15 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 };
+static Ints15 oid_sha1WithRSAEncryptionOiw ={6, 1, 3, 14, 3, 2, 29 };
+static Ints15 oid_sha256WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 11 };
+static Ints15 oid_sha384WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 12 };
+static Ints15 oid_sha512WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 13 };
+static Ints15 oid_sha224WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 14 };
+
+static Ints15 oid_ecPublicKey = {6, 1, 2, 840, 10045, 2, 1 };
+static Ints15 oid_sha1WithECDSA = {6, 1, 2, 840, 10045, 4, 1 };
+static Ints15 oid_sha256WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 2 };
+static Ints15 oid_sha384WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 3 };
+static Ints15 oid_sha512WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 4 };
+
+static Ints15 oid_md5 = {6, 1, 2, 840, 113549, 2, 5 };
+static Ints15 oid_sha1 = {6, 1, 3, 14, 3, 2, 26 };
+static Ints15 oid_sha256= {9, 2, 16, 840, 1, 101, 3, 4, 2, 1 };
+static Ints15 oid_sha384= {9, 2, 16, 840, 1, 101, 3, 4, 2, 2 };
+static Ints15 oid_sha512= {9, 2, 16, 840, 1, 101, 3, 4, 2, 3 };
+static Ints15 oid_sha224= {9, 2, 16, 840, 1, 101, 3, 4, 2, 4 };
+
 static Ints *alg_oid_tab[NUMALGS+1] = {
 	(Ints*)&oid_rsaEncryption,
 	(Ints*)&oid_md2WithRSAEncryption,
 	(Ints*)&oid_md4WithRSAEncryption,
 	(Ints*)&oid_md5WithRSAEncryption,
+
 	(Ints*)&oid_sha1WithRSAEncryption,
+	(Ints*)&oid_sha1WithRSAEncryptionOiw,
+
+	(Ints*)&oid_sha256WithRSAEncryption,
+	(Ints*)&oid_sha384WithRSAEncryption,
+	(Ints*)&oid_sha512WithRSAEncryption,
+	(Ints*)&oid_sha224WithRSAEncryption,
+
+	(Ints*)&oid_ecPublicKey,
+	(Ints*)&oid_sha1WithECDSA,
+	(Ints*)&oid_sha256WithECDSA,
+	(Ints*)&oid_sha384WithECDSA,
+	(Ints*)&oid_sha512WithECDSA,
+
 	(Ints*)&oid_md5,
+	(Ints*)&oid_sha1,
+	(Ints*)&oid_sha256,
+	(Ints*)&oid_sha384,
+	(Ints*)&oid_sha512,
+	(Ints*)&oid_sha224,
 	nil
 };
-static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, md5, nil };
 
+static DigestAlg *digestalg[NUMALGS+1] = {
+	&alg_md5, &alg_md5, &alg_md5, &alg_md5,
+	&alg_sha1, &alg_sha1,
+	&alg_sha256, &alg_sha384, &alg_sha512, &alg_sha224,
+	&alg_sha256, &alg_sha1, &alg_sha256, &alg_sha384, &alg_sha512,
+	&alg_md5, &alg_sha1, &alg_sha256, &alg_sha384, &alg_sha512, &alg_sha224,
+	nil
+};
+
+static Ints15 oid_secp256r1 = {7, 1, 2, 840, 10045, 3, 1, 7};
+
+static Ints *namedcurves_oid_tab[] = {
+	(Ints*)&oid_secp256r1,
+	nil,
+};
+static void (*namedcurves[])(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h) = {
+	secp256r1,
+	nil,
+};
+
 static void
 freecert(CertX509* c)
 {
-	if (!c) return;
-	if(c->issuer != nil)
-		free(c->issuer);
-	if(c->validity_start != nil)
-		free(c->validity_start);
-	if(c->validity_end != nil)
-		free(c->validity_end);
-	if(c->subject != nil)
-		free(c->subject);
+	if(c == nil)
+		return;
+	free(c->issuer);
+	free(c->validity_start);
+	free(c->validity_end);
+	free(c->subject);
 	freebytes(c->publickey);
 	freebytes(c->signature);
+	free(c);
 }
 
 /*
@@ -1690,6 +1745,17 @@
 	return oid_lookup(oid, alg_oid_tab);
 }
 
+static int
+parse_curve(Elem* e)
+{
+	Elist* el;
+	Ints* oid;
+
+	if(!is_seq(e, &el) || elistlen(el)<2 || !is_oid(&el->tl->hd, &oid))
+		return -1;
+	return oid_lookup(oid, namedcurves_oid_tab);
+}
+
 static CertX509*
 decode_cert(Bytes* a)
 {
@@ -1792,7 +1858,7 @@
 		goto errret;
 
 	/* SubjectPublicKeyInfo */
- 	if(!is_seq(epubkey, &elpubkey))
+	if(!is_seq(epubkey, &elpubkey))
 		goto errret;
 	if(elistlen(elpubkey) != 2)
 		goto errret;
@@ -1800,6 +1866,12 @@
 	c->publickey_alg = parse_alg(&elpubkey->hd);
 	if(c->publickey_alg < 0)
 		goto errret;
+	c->curve = -1;
+	if(c->publickey_alg == ALG_ecPublicKey){
+		c->curve = parse_curve(&elpubkey->hd);
+		if(c->curve < 0)
+			goto errret;
+	}
   	if(!is_bitstring(&elpubkey->tl->hd, &bits))
 		goto errret;
 	if(bits->unusedbits != 0)
@@ -1824,7 +1896,7 @@
 }
 
 /*
- *	RSAPublickKey :: SEQUENCE {
+ *	RSAPublickKey ::= SEQUENCE {
  *		modulus INTEGER,
  *		publicExponent INTEGER
  *	}
@@ -1834,7 +1906,6 @@
 {
 	Elem e;
 	Elist *el;
-	mpint *mp;
 	RSApub* key;
 
 	key = rsapuballoc();
@@ -1842,17 +1913,15 @@
 		goto errret;
 	if(!is_seq(&e, &el) || elistlen(el) != 2)
 		goto errret;
-
-	key->n = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->n = asn1mpint(&el->hd)) == nil)
 		goto errret;
-
 	el = el->tl;
-	key->ek = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->ek = asn1mpint(&el->hd)) == nil)
 		goto errret;
+	freevalfields(&e.val);
 	return key;
 errret:
+	freevalfields(&e.val);
 	rsapubfree(key);
 	return nil;
 }
@@ -1875,7 +1944,6 @@
 	int version;
 	Elem e;
 	Elist *el;
-	mpint *mp;
 	RSApriv* key;
 
 	key = rsaprivalloc();
@@ -1887,70 +1955,115 @@
 		goto errret;
 
 	el = el->tl;
-	key->pub.n = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->pub.n = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->pub.ek = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->pub.ek = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->dk = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->dk = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->q = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->q = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->p = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->p = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->kq = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->kq = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->kp = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->kp = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
 	el = el->tl;
-	key->c2 = mp = asn1mpint(&el->hd);
-	if(mp == nil)
+	if((key->c2 = asn1mpint(&el->hd)) == nil)
 		goto errret;
 
+	freevalfields(&e.val);
 	return key;
 errret:
+	freevalfields(&e.val);
 	rsaprivfree(key);
 	return nil;
 }
 
+/*
+ * 	DSAPrivateKey ::= SEQUENCE{
+ *		version Version,
+ *		p INTEGER,
+ *		q INTEGER,
+ *		g INTEGER, -- alpha
+ *		pub_key INTEGER, -- key
+ *		priv_key INTEGER, -- secret
+ *	}
+ */
+static DSApriv*
+decode_dsaprivkey(Bytes* a)
+{
+	int version;
+	Elem e;
+	Elist *el;
+	DSApriv* key;
+
+	key = dsaprivalloc();
+	if(decode(a->data, a->len, &e) != ASN_OK)
+		goto errret;
+	if(!is_seq(&e, &el) || elistlen(el) != 6)
+		goto errret;
+	version = -1;
+	if(!is_int(&el->hd, &version) || version != 0)
+		goto errret;
+
+	el = el->tl;
+	if((key->pub.p = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->pub.q = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->pub.alpha = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->pub.key = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->secret = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	freevalfields(&e.val);
+	return key;
+errret:
+	freevalfields(&e.val);
+	dsaprivfree(key);
+	return nil;
+}
+
 static mpint*
 asn1mpint(Elem *e)
 {
 	Bytes *b;
-	mpint *mp;
 	int v;
 
 	if(is_int(e, &v))
 		return itomp(v, nil);
-	if(is_bigint(e, &b)) {
-		mp = betomp(b->data, b->len, nil);
-		freebytes(b);
-		return mp;
-	}
+	if(is_bigint(e, &b))
+		return betomp(b->data, b->len, nil);
 	return nil;
 }
 
-static mpint*
-pkcs1pad(Bytes *b, mpint *modulus)
+mpint*
+pkcs1padbuf(uchar *buf, int len, mpint *modulus)
 {
 	int n = (mpsignif(modulus)+7)/8;
 	int pm1, i;
@@ -1957,7 +2070,7 @@
 	uchar *p;
 	mpint *mp;
 
-	pm1 = n - 1 - b->len;
+	pm1 = n - 1 - len;
 	p = (uchar*)emalloc(n);
 	p[0] = 0;
 	p[1] = 1;
@@ -1964,12 +2077,18 @@
 	for(i = 2; i < pm1; i++)
 		p[i] = 0xFF;
 	p[pm1] = 0;
-	memcpy(&p[pm1+1], b->data, b->len);
+	memcpy(&p[pm1+1], buf, len);
 	mp = betomp(p, n, nil);
 	free(p);
 	return mp;
 }
 
+static mpint*
+pkcs1pad(Bytes *b, mpint *modulus)
+{
+	return pkcs1padbuf(b->data, b->len, modulus);
+}
+
 RSApriv*
 asn1toRSApriv(uchar *kd, int kn)
 {
@@ -1982,13 +2101,25 @@
 	return key;
 }
 
+DSApriv*
+asn1toDSApriv(uchar *kd, int kn)
+{
+	Bytes *b;
+	DSApriv *key;
+
+	b = makebytes(kd, kn);
+	key = decode_dsaprivkey(b);
+	freebytes(b);
+	return key;
+}
+
 /*
  * digest(CertificateInfo)
  * Our ASN.1 library doesn't return pointers into the original
  * data array, so we need to do a little hand decoding.
  */
-static void
-digest_certinfo(Bytes *cert, DigestFun digestfun, uchar *digest)
+static int
+digest_certinfo(Bytes *cert, DigestAlg *da, uchar *digest)
 {
 	uchar *info, *p, *pend;
 	ulong infolen;
@@ -1999,50 +2130,178 @@
 	p = cert->data;
 	pend = cert->data + cert->len;
 	if(tag_decode(&p, pend, &tag, &isconstr) != ASN_OK ||
-			tag.class != Universal || tag.num != SEQUENCE ||
-			length_decode(&p, pend, &length) != ASN_OK ||
-			p+length > pend)
-		return;
+	   tag.class != Universal || tag.num != SEQUENCE ||
+	   length_decode(&p, pend, &length) != ASN_OK ||
+	   p+length > pend ||
+	   p+length < p)
+		return -1;
 	info = p;
-	if(ber_decode(&p, pend, &elem) != ASN_OK || elem.tag.num != SEQUENCE)
-		return;
+	if(ber_decode(&p, pend, &elem) != ASN_OK)
+		return -1;
+	freevalfields(&elem.val);
+	if(elem.tag.num != SEQUENCE)
+		return -1;
 	infolen = p - info;
-	(*digestfun)(info, infolen, digest, nil);
+	(*da->fun)(info, infolen, digest, nil);
+	return da->len;
 }
 
-static char*
-verify_signature(Bytes* signature, RSApub *pk, uchar *edigest, Elem **psigalg)
+static int
+pkcs1decryptsignature(uchar *sig, int siglen, RSApub *pk, uchar **pbuf)
 {
-	Elem e;
-	Elist *el;
-	Bytes *digest;
-	uchar *pkcs1buf, *buf;
-	int buflen;
+	int nlen, buflen;
 	mpint *pkcs1;
+	uchar *buf;
 
+	*pbuf = nil;
+
+	/* one less than the byte length of the modulus */
+	nlen = (mpsignif(pk->n)-1)/8;
+
 	/* see 9.2.1 of rfc2437 */
-	pkcs1 = betomp(signature->data, signature->len, nil);
+	pkcs1 = betomp(sig, siglen, nil);
 	mpexp(pkcs1, pk->ek, pk->n, pkcs1);
-	buflen = mptobe(pkcs1, nil, 0, &pkcs1buf);
-	buf = pkcs1buf;
-	if(buflen < 4 || buf[0] != 1)
-		return "expected 1";
-	buf++;
-	while(buf[0] == 0xff)
-		buf++;
-	if(buf[0] != 0)
-		return "expected 0";
-	buf++;
-	buflen -= buf-pkcs1buf;
-	if(decode(buf, buflen, &e) != ASN_OK || !is_seq(&e, &el) || elistlen(el) != 2 ||
-			!is_octetstring(&el->tl->hd, &digest))
-		return "signature parse error";
-	*psigalg = &el->hd;
-	if(memcmp(digest->data, edigest, digest->len) == 0)
+	buflen = mptobe(pkcs1, nil, 0, pbuf);
+	mpfree(pkcs1);
+
+	buf = *pbuf;
+	if(buflen != nlen || buf[0] != 1)
+		goto bad;
+	buf++, buflen--;
+	while(buflen > 0 && buf[0] == 0xff)
+		buf++, buflen--;
+	if(buflen < 1 || buf[0] != 0)
+		goto bad;
+	buf++, buflen--;
+	memmove(*pbuf, buf, buflen);
+	return buflen;
+bad:
+	free(*pbuf);
+	*pbuf = nil;
+	return -1;
+}
+
+char*
+X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk)
+{
+	Elem e;
+	Elist *el;
+	Bytes *digest;
+	uchar *buf;
+	int alg, buflen;
+	char *err;
+
+	buflen = pkcs1decryptsignature(sig, siglen, pk, &buf);
+	if(buflen == edigestlen && tsmemcmp(buf, edigest, edigestlen) == 0){
+		free(buf);
 		return nil;
-	return "digests did not match";
+	}
+	el = nil;
+	memset(&e, 0, sizeof(e));
+	if(buflen < 0 || decode(buf, buflen, &e) != ASN_OK
+	|| !is_seq(&e, &el) || elistlen(el) != 2 || !is_octetstring(&el->tl->hd, &digest)) {
+		err = "signature parse error";
+		goto end;
+	}
+	alg = parse_alg(&el->hd);
+	if(alg < 0){
+		err = "unknown signature algorithm";
+		goto end;
+	}
+	if(digest->len != edigestlen || digest->len != digestalg[alg]->len){
+		err = "bad digest length";
+		goto end;
+	}
+	if(tsmemcmp(digest->data, edigest, edigestlen) != 0){
+		err = "digest did not match";
+		goto end;
+	}
+	err = nil;
+end:
+	freevalfields(&e.val);
+	free(buf);
+	return err;
 }
-	
+
+char*
+X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub)
+{
+	Elem e;
+	Elist *el;
+	mpint *r, *s;
+	char *err;
+
+	r = s = nil;
+	err = "bad signature";
+	if(decode(sig, siglen, &e) != ASN_OK)
+		goto end;
+	if(!is_seq(&e, &el) || elistlen(el) != 2)
+		goto end;
+	r = asn1mpint(&el->hd);
+	if(r == nil)
+		goto end;
+	el = el->tl;
+	s = asn1mpint(&el->hd);
+	if(s == nil)
+		goto end;
+	if(ecdsaverify(dom, pub, edigest, edigestlen, r, s))
+		err = nil;
+end:
+	freevalfields(&e.val);
+	mpfree(s);
+	mpfree(r);
+	return err;
+}
+
+ECpub*
+X509toECpub(uchar *cert, int ncert, ECdomain *dom)
+{
+	CertX509 *c;
+	ECpub *pub;
+	Bytes *b;
+
+	b = makebytes(cert, ncert);
+	c = decode_cert(b);
+	freebytes(b);
+	if(c == nil)
+		return nil;
+	pub = nil;
+	if(c->publickey_alg == ALG_ecPublicKey){
+		ecdominit(dom, namedcurves[c->curve]);
+		pub = ecdecodepub(dom, c->publickey->data, c->publickey->len);
+		if(pub == nil)
+			ecdomfree(dom);
+	}
+	freecert(c);
+	return pub;
+}
+
+char*
+X509ecdsaverify(uchar *cert, int ncert, ECdomain *dom, ECpub *pk)
+{
+	char *e;
+	Bytes *b;
+	CertX509 *c;
+	int digestlen;
+	uchar digest[MAXdlen];
+
+	b = makebytes(cert, ncert);
+	c = decode_cert(b);
+	if(c == nil){
+		freebytes(b);
+		return "cannot decode cert";
+	}
+	digestlen = digest_certinfo(b, digestalg[c->signature_alg], digest);
+	freebytes(b);
+	if(digestlen <= 0){
+		freecert(c);
+		return "cannot decode certinfo";
+	}
+	e = X509ecdsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, dom, pk);
+	freecert(c);
+	return e;
+}
+
 RSApub*
 X509toRSApub(uchar *cert, int ncert, char *name, int nname)
 {
@@ -2049,7 +2308,7 @@
 	char *e;
 	Bytes *b;
 	CertX509 *c;
-	RSApub *pk;
+	RSApub *pub;
 
 	b = makebytes(cert, ncert);
 	c = decode_cert(b);
@@ -2059,31 +2318,38 @@
 	if(name != nil && c->subject != nil){
 		e = strchr(c->subject, ',');
 		if(e != nil)
-			*e = 0;  // take just CN part of Distinguished Name
+			*e = 0;	/* take just CN part of Distinguished Name */
 		strncpy(name, c->subject, nname);
 	}
-	pk = decode_rsapubkey(c->publickey);
+	pub = nil;
+	if(c->publickey_alg == ALG_rsaEncryption)
+		pub = decode_rsapubkey(c->publickey);
 	freecert(c);
-	return pk;
+	return pub;
 }
 
 char*
-X509verify(uchar *cert, int ncert, RSApub *pk)
+X509rsaverify(uchar *cert, int ncert, RSApub *pk)
 {
 	char *e;
 	Bytes *b;
 	CertX509 *c;
-	uchar digest[SHA1dlen];
-	Elem *sigalg;
+	int digestlen;
+	uchar digest[MAXdlen];
 
 	b = makebytes(cert, ncert);
 	c = decode_cert(b);
-	if(c != nil)
-		digest_certinfo(b, digestalg[c->signature_alg], digest);
-	freebytes(b);
-	if(c == nil)
+	if(c == nil){
+		freebytes(b);
 		return "cannot decode cert";
-	e = verify_signature(c->signature, pk, digest, &sigalg);
+	}
+	digestlen = digest_certinfo(b, digestalg[c->signature_alg], digest);
+	freebytes(b);
+	if(digestlen <= 0){
+		freecert(c);
+		return "cannot decode certinfo";
+	}
+	e = X509rsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
 	freecert(c);
 	return e;
 }
@@ -2128,13 +2394,33 @@
 	return e;
 }
 
+static int
+printable(char *s)
+{
+	int c;
+
+	while((c = (uchar)*s++) != 0){
+		if((c >= 'a' && c <= 'z')
+		|| (c >= 'A' && c <= 'Z')
+		|| (c >= '0' && c <= '9')
+		|| strchr("'=()+,-./:? ", c) != nil)
+			continue;
+		return 0;
+	}
+	return 1;
+}
+
+#define DirectoryString 0
+
 static Elem
-mkstring(char *s)
+mkstring(char *s, int t)
 {
 	Elem e;
 
+	if(t == DirectoryString)
+		t = printable(s) ? PrintableString : UTF8String;
 	e.tag.class = Universal;
-	e.tag.num = IA5String;
+	e.tag.num = t;
 	e.val.tag = VString;
 	e.val.u.stringval = estrdup(s);
 	return e;
@@ -2174,7 +2460,7 @@
 	e.tag.class = Universal;
 	e.tag.num = UTCTime;
 	e.val.tag = VString;
-	snprint(utc, 50, "%.2d%.2d%.2d%.2d%.2d%.2dZ",
+	snprint(utc, sizeof(utc), "%.2d%.2d%.2d%.2d%.2d%.2dZ",
 		tm->year % 100, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);
 	e.val.u.stringval = estrdup(utc);
 	return e;
@@ -2223,24 +2509,26 @@
 }
 
 typedef struct Ints7pref {
-	int		len;
-	int		data[7];
+	int	len;
+	int	data[7];
 	char	prefix[4];
+	int	stype;
 } Ints7pref;
 Ints7pref DN_oid[] = {
-	{4, 2, 5, 4, 6, 0, 0, 0,  "C="},
-	{4, 2, 5, 4, 8, 0, 0, 0,  "ST="},
-	{4, 2, 5, 4, 7, 0, 0, 0,  "L="},
-	{4, 2, 5, 4, 10, 0, 0, 0, "O="},
-	{4, 2, 5, 4, 11, 0, 0, 0, "OU="},
-	{4, 2, 5, 4, 3, 0, 0, 0,  "CN="},
- 	{7, 1,2,840,113549,1,9,1, "E="},
+	{4, 2, 5, 4, 6, 0, 0, 0,        "C=", PrintableString},
+	{4, 2, 5, 4, 8, 0, 0, 0,        "ST=",DirectoryString},
+	{4, 2, 5, 4, 7, 0, 0, 0,        "L=", DirectoryString},
+	{4, 2, 5, 4, 10, 0, 0, 0,       "O=", DirectoryString},
+	{4, 2, 5, 4, 11, 0, 0, 0,       "OU=",DirectoryString},
+	{4, 2, 5, 4, 3, 0, 0, 0,        "CN=",DirectoryString},
+	{7, 1,2,840,113549,1,9,1,       "E=", IA5String},
+	{7, 0,9,2342,19200300,100,1,25,	"DC=",IA5String},
 };
 
 static Elem
 mkname(Ints7pref *oid, char *subj)
 {
-	return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj), nil))), nil));
+	return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj, oid->stype), nil))), nil));
 }
 
 static Elem
@@ -2264,61 +2552,106 @@
 	return mkseq(el);
 }
 
+/*
+ * DigestInfo ::= SEQUENCE {
+ *	digestAlgorithm AlgorithmIdentifier,
+ *	digest OCTET STRING }
+ */
+static Bytes*
+encode_digest(DigestAlg *da, uchar *digest)
+{
+	Bytes *ans;
+	int err;
+	Elem e;
 
+	e = mkseq(
+		mkel(mkalg(da->alg),
+		mkel(mkoctet(digest, da->len),
+		nil)));
+	err = encode(e, &ans);
+	freevalfields(&e.val);
+	if(err != ASN_OK)
+		return nil;
+
+	return ans;
+}
+
+int
+asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len)
+{
+	Bytes *bytes;
+	DigestAlg **dp;
+
+	for(dp = digestalg; *dp != nil; dp++){
+		if((*dp)->fun != fun)
+			continue;
+		bytes = encode_digest(*dp, digest);
+		if(bytes == nil)
+			break;
+		if(bytes->len > len){
+			freebytes(bytes);
+			break;
+		}
+		len = bytes->len;
+		memmove(buf, bytes->data, len);
+		freebytes(bytes);
+		return len;
+	}
+	return -1;
+}
+
 uchar*
-X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen)
+X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen)
 {
-	int serial = 0;
+	int serial = 0, sigalg = ALG_sha256WithRSAEncryption;
 	uchar *cert = nil;
 	RSApub *pk = rsaprivtopub(priv);
 	Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
-	Elem e, certinfo, issuer, subject, pubkey, validity, sig;
-	uchar digest[MD5dlen], *buf;
+	Elem e, certinfo;
+	DigestAlg *da;
+	uchar digest[MAXdlen], *buf;
 	int buflen;
 	mpint *pkcs1;
 
-	e.val.tag = VInt;  /* so freevalfields at errret is no-op */
-	issuer = mkDN(subj);
-	subject = mkDN(subj);
-	pubkey = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil)));
-	if(encode(pubkey, &pkbytes) != ASN_OK)
+	e = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil)));
+	if(encode(e, &pkbytes) != ASN_OK)
 		goto errret;
-	freevalfields(&pubkey.val);
-	pubkey = mkseq(
-		mkel(mkalg(ALG_rsaEncryption),
-		mkel(mkbits(pkbytes->data, pkbytes->len),
-		nil)));
-	freebytes(pkbytes);
-	validity = mkseq(
-		mkel(mkutc(valid[0]),
-		mkel(mkutc(valid[1]),
-		nil)));
-	certinfo = mkseq(
+	freevalfields(&e.val);
+	e = mkseq(
 		mkel(mkint(serial),
-		mkel(mkalg(ALG_md5WithRSAEncryption),
-		mkel(issuer,
-		mkel(validity,
-		mkel(subject,
-		mkel(pubkey,
+		mkel(mkalg(sigalg),
+		mkel(mkDN(subj),
+		mkel(mkseq(
+			mkel(mkutc(valid[0]),
+			mkel(mkutc(valid[1]),
+			nil))),
+		mkel(mkDN(subj),
+		mkel(mkseq(
+			mkel(mkalg(ALG_rsaEncryption),
+			mkel(mkbits(pkbytes->data, pkbytes->len),
+			nil))),
 		nil)))))));
-	if(encode(certinfo, &certinfobytes) != ASN_OK)
+	freebytes(pkbytes);
+	if(encode(e, &certinfobytes) != ASN_OK)
 		goto errret;
-	md5(certinfobytes->data, certinfobytes->len, digest, 0);
+
+	da = digestalg[sigalg];
+	(*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
 	freebytes(certinfobytes);
-	sig = mkseq(
-		mkel(mkalg(ALG_md5),
-		mkel(mkoctet(digest, MD5dlen),
-		nil)));
-	if(encode(sig, &sigbytes) != ASN_OK)
+	certinfo = e;
+
+	sigbytes = encode_digest(da, digest);
+	if(sigbytes == nil)
 		goto errret;
 	pkcs1 = pkcs1pad(sigbytes, pk->n);
 	freebytes(sigbytes);
+
 	rsadecrypt(priv, pkcs1, pkcs1);
 	buflen = mptobe(pkcs1, nil, 0, &buf);
 	mpfree(pkcs1);
 	e = mkseq(
 		mkel(certinfo,
-		mkel(mkalg(ALG_md5WithRSAEncryption),
+		mkel(mkalg(sigalg),
 		mkel(mkbits(buf, buflen),
 		nil))));
 	free(buf);
@@ -2326,7 +2659,10 @@
 		goto errret;
 	if(certlen)
 		*certlen = certbytes->len;
-	cert = certbytes->data;
+	cert = malloc(certbytes->len);
+	if(cert != nil)
+		memmove(cert, certbytes->data, certbytes->len);
+	freebytes(certbytes);
 errret:
 	freevalfields(&e.val);
 	return cert;
@@ -2333,52 +2669,51 @@
 }
 
 uchar*
-X509req(RSApriv *priv, char *subj, int *certlen)
+X509rsareq(RSApriv *priv, char *subj, int *certlen)
 {
 	/* RFC 2314, PKCS #10 Certification Request Syntax */
-	int version = 0;
+	int version = 0, sigalg = ALG_sha256WithRSAEncryption;
 	uchar *cert = nil;
 	RSApub *pk = rsaprivtopub(priv);
 	Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
-	Elem e, certinfo, subject, pubkey, sig;
-	uchar digest[MD5dlen], *buf;
+	Elem e, certinfo;
+	DigestAlg *da;
+	uchar digest[MAXdlen], *buf;
 	int buflen;
 	mpint *pkcs1;
 
-	e.val.tag = VInt;  /* so freevalfields at errret is no-op */
-	subject = mkDN(subj);
-	pubkey = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil)));
-	if(encode(pubkey, &pkbytes) != ASN_OK)
+	e = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil)));
+	if(encode(e, &pkbytes) != ASN_OK)
 		goto errret;
-	freevalfields(&pubkey.val);
-	pubkey = mkseq(
-		mkel(mkalg(ALG_rsaEncryption),
-		mkel(mkbits(pkbytes->data, pkbytes->len),
-		nil)));
-	freebytes(pkbytes);
-	certinfo = mkseq(
+	freevalfields(&e.val);
+	e = mkseq(
 		mkel(mkint(version),
-		mkel(subject,
-		mkel(pubkey,
+		mkel(mkDN(subj),
+		mkel(mkseq(
+			mkel(mkalg(ALG_rsaEncryption),
+			mkel(mkbits(pkbytes->data, pkbytes->len),
+			nil))),
 		nil))));
-	if(encode(certinfo, &certinfobytes) != ASN_OK)
+	freebytes(pkbytes);
+	if(encode(e, &certinfobytes) != ASN_OK)
 		goto errret;
-	md5(certinfobytes->data, certinfobytes->len, digest, 0);
+	da = digestalg[sigalg];
+	(*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
 	freebytes(certinfobytes);
-	sig = mkseq(
-		mkel(mkalg(ALG_md5),
-		mkel(mkoctet(digest, MD5dlen),
-		nil)));
-	if(encode(sig, &sigbytes) != ASN_OK)
+	certinfo = e;
+
+	sigbytes = encode_digest(da, digest);
+	if(sigbytes == nil)
 		goto errret;
 	pkcs1 = pkcs1pad(sigbytes, pk->n);
 	freebytes(sigbytes);
+
 	rsadecrypt(priv, pkcs1, pkcs1);
 	buflen = mptobe(pkcs1, nil, 0, &buf);
 	mpfree(pkcs1);
 	e = mkseq(
 		mkel(certinfo,
-		mkel(mkalg(ALG_md5),
+		mkel(mkalg(sigalg),
 		mkel(mkbits(buf, buflen),
 		nil))));
 	free(buf);
@@ -2386,7 +2721,10 @@
 		goto errret;
 	if(certlen)
 		*certlen = certbytes->len;
-	cert = certbytes->data;
+	cert = malloc(certbytes->len);
+	if(cert != nil)
+		memmove(cert, certbytes->data, certbytes->len);
+	freebytes(certbytes);
 errret:
 	freevalfields(&e.val);
 	return cert;
@@ -2398,33 +2736,34 @@
 	if(tag.class != Universal)
 		return smprint("class%d,num%d", tag.class, tag.num);
 	switch(tag.num){
-		case BOOLEAN: return "BOOLEAN"; break;
-		case INTEGER: return "INTEGER"; break;
-		case BIT_STRING: return "BIT STRING"; break;
-		case OCTET_STRING: return "OCTET STRING"; break;
-		case NULLTAG: return "NULLTAG"; break;
-		case OBJECT_ID: return "OID"; break;
-		case ObjectDescriptor: return "OBJECT_DES"; break;
-		case EXTERNAL: return "EXTERNAL"; break;
-		case REAL: return "REAL"; break;
-		case ENUMERATED: return "ENUMERATED"; break;
-		case EMBEDDED_PDV: return "EMBEDDED PDV"; break;
-		case SEQUENCE: return "SEQUENCE"; break;
-		case SETOF: return "SETOF"; break;
-		case NumericString: return "NumericString"; break;
-		case PrintableString: return "PrintableString"; break;
-		case TeletexString: return "TeletexString"; break;
-		case VideotexString: return "VideotexString"; break;
-		case IA5String: return "IA5String"; break;
-		case UTCTime: return "UTCTime"; break;
-		case GeneralizedTime: return "GeneralizedTime"; break;
-		case GraphicString: return "GraphicString"; break;
-		case VisibleString: return "VisibleString"; break;
-		case GeneralString: return "GeneralString"; break;
-		case UniversalString: return "UniversalString"; break;
-		case BMPString: return "BMPString"; break;
-		default:
-			return smprint("Universal,num%d", tag.num);
+	case BOOLEAN: return "BOOLEAN";
+	case INTEGER: return "INTEGER";
+	case BIT_STRING: return "BIT STRING";
+	case OCTET_STRING: return "OCTET STRING";
+	case NULLTAG: return "NULLTAG";
+	case OBJECT_ID: return "OID";
+	case ObjectDescriptor: return "OBJECT_DES";
+	case EXTERNAL: return "EXTERNAL";
+	case REAL: return "REAL";
+	case ENUMERATED: return "ENUMERATED";
+	case EMBEDDED_PDV: return "EMBEDDED PDV";
+	case SEQUENCE: return "SEQUENCE";
+	case SETOF: return "SETOF";
+	case UTF8String: return "UTF8String";
+	case NumericString: return "NumericString";
+	case PrintableString: return "PrintableString";
+	case TeletexString: return "TeletexString";
+	case VideotexString: return "VideotexString";
+	case IA5String: return "IA5String";
+	case UTCTime: return "UTCTime";
+	case GeneralizedTime: return "GeneralizedTime";
+	case GraphicString: return "GraphicString";
+	case VisibleString: return "VisibleString";
+	case GeneralString: return "GeneralString";
+	case UniversalString: return "UniversalString";
+	case BMPString: return "BMPString";
+	default:
+		return smprint("Universal,num%d", tag.num);
 	}
 }
 
@@ -2482,39 +2821,62 @@
 	char *e;
 	Bytes *b;
 	CertX509 *c;
-	RSApub *pk;
-	uchar digest[SHA1dlen];
-	Elem *sigalg;
+	RSApub *rsapub;
+	ECpub *ecpub;
+	ECdomain ecdom;
+	int digestlen;
+	uchar digest[MAXdlen];
 
 	print("begin X509dump\n");
 	b = makebytes(cert, ncert);
 	c = decode_cert(b);
-	if(c != nil)
-		digest_certinfo(b, digestalg[c->signature_alg], digest);
-	freebytes(b);
 	if(c == nil){
-		print("cannot decode cert");
+		freebytes(b);
+		print("cannot decode cert\n");
 		return;
 	}
+	digestlen = digest_certinfo(b, digestalg[c->signature_alg], digest);
+	freebytes(b);
+	if(digestlen <= 0){
+		freecert(c);
+		print("cannot decode certinfo\n");
+		return;
+	}
 
 	print("serial %d\n", c->serial);
 	print("issuer %s\n", c->issuer);
 	print("validity %s %s\n", c->validity_start, c->validity_end);
 	print("subject %s\n", c->subject);
-	pk = decode_rsapubkey(c->publickey);
-	print("pubkey e=%B n(%d)=%B\n", pk->ek, mpsignif(pk->n), pk->n);
 
-	print("sigalg=%d digest=%.*H\n", c->signature_alg, MD5dlen, digest);
-	e = verify_signature(c->signature, pk, digest, &sigalg);
-	if(e==nil){
-		e = "nil (meaning ok)";
-		print("sigalg=\n");
-		if(sigalg)
-			edump(*sigalg);
-	}
-	print("self-signed verify_signature returns: %s\n", e);
+	print("sigalg=%d digest=%.*H\n", c->signature_alg, digestlen, digest);
+	print("publickey_alg=%d pubkey[%d] %.*H\n", c->publickey_alg, c->publickey->len,
+		c->publickey->len, c->publickey->data);
 
-	rsapubfree(pk);
+	switch(c->publickey_alg){
+	case ALG_rsaEncryption:
+		rsapub = decode_rsapubkey(c->publickey);
+		if(rsapub != nil){
+			print("rsa pubkey e=%B n(%d)=%B\n", rsapub->ek, mpsignif(rsapub->n), rsapub->n);
+			e = X509rsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, rsapub);
+			if(e==nil)
+				e = "nil (meaning ok)";
+			print("self-signed X509rsaverifydigest returns: %s\n", e);
+			rsapubfree(rsapub);
+		}
+		break;
+	case ALG_ecPublicKey:
+		ecdominit(&ecdom, namedcurves[c->curve]);
+		ecpub = ecdecodepub(&ecdom, c->publickey->data, c->publickey->len);
+		if(ecpub != nil){
+			e = X509ecdsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, &ecdom, ecpub);
+			if(e==nil)
+				e = "nil (meaning ok)";
+			print("self-signed X509ecdsaverifydigest returns: %s\n", e);
+			ecpubfree(ecpub);
+		}
+		ecdomfree(&ecdom);
+		break;
+	}
 	freecert(c);
 	print("end X509dump\n");
 }