shithub: tlsclient

Download patch

ref: f6e1c9f713929b2585edc6fca46c3ea46f872f72
author: Jacob Moody <moody@posixcafe.org>
date: Sun Feb 14 10:03:05 EST 2021

Initial commit

diff: cannot open b/include//null: file does not exist: 'b/include//null' diff: cannot open b/libauthsrv//null: file does not exist: 'b/libauthsrv//null' diff: cannot open b/libc//null: file does not exist: 'b/libc//null' diff: cannot open b/libmp//null: file does not exist: 'b/libmp//null' diff: cannot open b/libsec//null: file does not exist: 'b/libsec//null'
--- /dev/null
+++ b/9cpu
@@ -1,0 +1,43 @@
+#!/bin/sh
+
+auth=$AUTH
+user=$USER
+cpu=$CPU
+
+while :; do
+	case $1 in
+		-a)
+			auth=$2
+			shift
+			;;
+		-u)
+			user=$2
+			shift
+			;;
+		-h)
+			cpu=$2
+			shift
+			;;
+		*)
+			break;
+	esac
+	shift
+done
+
+cmd="rc -i"
+
+if [ "$#" -ne 0 ]; then
+	cmd=$*
+fi
+
+len=`echo $cmd | wc -c`
+
+USER=$user AUTH=$auth CPU=$cpu tlsclient -f -p 17019 sh <(cat <<EOF
+consin=\`head -c 1\`
+consout=\`head -c 1\`
+printf '%7ld\n' $len
+echo $cmd
+cat <&\$consin 2>/dev/null &
+cat >&\$consout 2>/dev/null 
+EOF
+)
--- /dev/null
+++ b/Make.config
@@ -1,0 +1,9 @@
+AR=ar
+RANLIB=ranlib
+CC=gcc
+CFLAGS=-Wall -Wno-missing-braces -Wno-parentheses -ggdb -I$(ROOT) -I$(ROOT)/include -c -D_THREAD_SAFE -O2
+O=o
+LDADD=
+TARG=tlsclient
+
+all: default
--- /dev/null
+++ b/Makefile
@@ -1,0 +1,38 @@
+ROOT=.
+
+include ./Make.config
+
+LIBS=\
+	libauthsrv/libauthsrv.a\
+	libmp/libmp.a\
+	libc/libc.a\
+	libsec/libsec.a\
+
+OFILES=cpu.$O
+
+default: $(TARG)
+$(TARG): $(LIBS) $(OFILES)
+	$(CC) `pkg-config gnutls --libs` $(LDFLAGS) -o $(TARG) $(OFILES) $(LIBS) $(LDADD)
+
+cpu.$O: cpu.c
+	$(CC) `pkg-config gnutls --cflags` $(CFLAGS) cpu.c -o cpu.o
+
+.PHONY: clean
+clean:
+	rm -f *.o */*.o */*.a *.a $(TARG)
+
+.PHONY: libauthsrv/libauthsrv.a
+libauthsrv/libauthsrv.a:
+	(cd libauthsrv; $(MAKE))
+
+.PHONY: libmp/libmp.a
+libmp/libmp.a:
+	(cd libmp; $(MAKE))
+
+.PHONY: libc/libc.a
+libc/libc.a:
+	(cd libc; $(MAKE))
+
+.PHONY: libsec/libsec.a
+libsec/libsec.a:
+	(cd libsec; $(MAKE))
--- /dev/null
+++ b/README
@@ -1,0 +1,18 @@
+tlsclient: tlsclient(1) for unix
+
+tlsclient comes with two programs, tlsclient itself and 9cpu which uses tlsclient
+to connect to a 9front cpu server and acts as rcpu(1).
+
+Due to the lack of /dev/cons on unix, an additional flag '-f' is added which
+causes tlsclient to dup additional copies of it's own stdin and stdout and place
+them on additional file descriptors for the child process. The additional file
+descriptor numbers are sent as the first two bytes to the child process.
+
+Most of this code is pillaged from jsdrawterm: https://github.com/aiju/jsdrawterm
+
+Usage:
+	tlsclient [-f] [ -u user] [ -h host ] [ -a auth ] -p port cmd...
+	9cpu [ -u user ] [ -h host ] [ -a auth ] cmd...
+
+Bugs:
+	Currently 9cpu uses gnutls, a better alternative is welcome.
--- /dev/null
+++ b/cpu.c
@@ -1,0 +1,530 @@
+/*
+ * cpu.c - Make a connection to a cpu server
+ */
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <gnutls/gnutls.h>
+
+#include <u.h>
+#include <args.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+
+#define MaxStr 128
+
+static void	usage(void);
+static int	readstr(int, char*, int);
+static AuthInfo *p9any(int);
+static int	getkey(Authkey*, char*, char*, char*, char*);
+static int	p9authtls(int);
+
+static char	*host;
+
+char *argv0;
+
+char *authserver;
+char *secstore;
+char *user, *pass;
+char secstorebuf[65536];
+char *geometry;
+
+gnutls_session_t session;
+
+void errstr(char *s){}
+
+int
+unix_dial(char *host, char *port)
+{
+	int fd;
+	struct sockaddr_in server;
+	struct hostent *he;
+	struct in_addr **addr_list;
+
+	he = gethostbyname(host);
+	if(he == nil){
+		sysfatal("could not resolve %s", host);
+	}
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+	addr_list = (struct in_addr **) he->h_addr_list;
+	server.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
+	server.sin_family = AF_INET;
+	server.sin_port = htons(atoi(port));
+	if(connect(fd, (struct sockaddr*)&server, sizeof(server)) < 0)
+		return -1;
+	return fd;
+}
+
+
+char*
+estrdup(char *s)
+{
+	s = strdup(s);
+	if(s == nil)
+		sysfatal("out of memory");
+	return s;
+}
+
+typedef size_t (*iofunc)(int, void*, size_t);
+size_t tls_send(int f, void *b, size_t n) { return gnutls_record_send(session, b, n); }
+size_t tls_recv(int f, void *b, size_t n) { return gnutls_record_recv(session, b, n); }
+size_t s_send(int f, void *b, size_t n) { return write(f, b, n); }
+size_t s_recv(int f, void *b, size_t n) { return read(f, b, n); }
+
+void
+xfer(int from, int to, iofunc recvf, iofunc sendf)
+{
+	char buf[12*1024];
+	size_t n;
+	
+	while((n = recvf(from, buf, sizeof buf)) > 0){
+		if(sendf(to, buf, n) < 0)
+			break;
+	}
+
+}
+
+void
+usage(void)
+{
+	fprint(2, "Usage: %s [-f] [ -u user ] [ -h host ] [ -a authserver ] -p port cmd...\n", argv0);
+	exits("usage");
+}
+
+int
+main(int argc, char **argv)
+{
+	int fd, res;
+	char *cmd;
+	char buf[1024];
+	size_t n;
+	char *port;
+	int pin[2];
+	int pout[2];
+	int infd, outfd;
+	pid_t execc, xferc;
+	int consin, consout, consflag;
+
+	execc = xferc = 0;
+	consflag = 0;
+	infd = 0;
+	outfd = 1;
+	user = getenv("USER");	
+	host = getenv("CPU");
+	authserver = getenv("AUTH");
+	pass = getenv("PASS");
+	port = nil;
+
+	ARGBEGIN {
+		case 'u': user = EARGF(usage()); break;
+		case 'h': host = EARGF(usage()); break;
+		case 'a': authserver = EARGF(usage()); break;
+		case 'p': port = EARGF(usage()); break;
+		case 'f': consflag++; break;
+	} ARGEND
+
+	if(user == nil || host == nil || authserver == nil || port == nil)
+		usage();
+
+	if(pass == nil)
+		pass = getpass("password:");
+
+	gnutls_global_init();
+	res = gnutls_init(&session, GNUTLS_CLIENT);
+	if(res != GNUTLS_E_SUCCESS){
+		sysfatal("could not init session");
+	}
+
+	if(*argv){
+		pipe(pin);
+		pipe(pout);
+		if(consflag){
+			/*
+			 * Unix has no /dev/cons, so there is no way to read
+			 * and write from the terminal if stdin and stdout
+			 * are dup'd over with the socket. This gives a bit
+			 * of a back door in to the orginal stdin and stdout.
+			 */
+			consin = dup(0);
+			consout = dup(1);
+		}
+		switch((execc = fork())){
+		case -1:
+			sysfatal("fork");
+		case 0:
+			close(pin[1]);
+			close(pout[0]);
+			dup2(pin[0], 0);
+			dup2(pout[1], 1);
+			execvp(argv[0], argv);
+			sysfatal("exec");
+		}
+		close(pout[1]);
+		close(pin[0]);
+		infd = pout[0];
+		outfd = pin[1];
+		if(consflag){
+			/*
+			 * For the sake of portability,
+			 * send the "cons" fds as the first
+			 * to lines to the child process to avoid
+			 * having to assume the next two fds
+			 */
+			n = sprint(buf, "%d%d", consin, consout);
+			write(pin[1], buf, n);
+		}
+	}
+
+	fd = unix_dial(host, port);
+	if(fd < 0){
+		sysfatal("Failed to connect to the client");
+	}
+
+	p9authtls(fd);
+
+	switch((xferc = fork())){
+	case -1:
+		sysfatal("fork");
+	case 0:
+		xfer(infd, -1, s_recv, tls_send);
+		break;
+	default:
+		xfer(-1, outfd, tls_recv, s_send);
+		break;
+	}
+	
+	if(xferc)
+		kill(xferc, SIGTERM);
+	if(execc)
+		kill(execc, SIGTERM);
+}
+
+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;
+}
+
+
+/*
+ * p9any authentication followed by tls-psk encryption
+ */
+static int
+p9authtls(int fd)
+{
+	AuthInfo *ai;
+	gnutls_psk_client_credentials_t cred;
+	gnutls_datum_t key;
+	const char *error = NULL;
+	int res;
+
+	ai = p9any(fd);
+	if(ai == nil)
+		sysfatal("can't authenticate: %r");
+
+	if(gnutls_psk_allocate_client_credentials(&cred) != 0)
+		sysfatal("can't allocate client creds");
+
+	key.size = ai->nsecret;
+	key.data = ai->secret;
+
+	if(gnutls_psk_set_client_credentials(cred, "p9secret", &key, GNUTLS_PSK_KEY_RAW) != 0)
+		sysfatal("can't set creds");
+	if(gnutls_credentials_set(session, GNUTLS_CRD_PSK, cred) != 0)
+		sysfatal("can't set creds 2");
+	res = gnutls_priority_set_direct(
+		session,
+		"NONE:+VERS-TLS1.2:+SIGN-ALL:+MAC-ALL:+CHACHA20-POLY1305:+PSK:+CTYPE-ALL",
+		&error
+	);
+	if (res != GNUTLS_E_SUCCESS) {
+		sysfatal("gnutls_priority_set_direct() failed: %s", error);
+	}
+	gnutls_transport_set_int(session, fd);
+	do {
+		res = gnutls_handshake(session);
+	} while ( res != 0 && !gnutls_error_is_fatal(res) );
+
+	if (gnutls_error_is_fatal(res)) {
+		sysfatal("Fatal error during handshake");
+	}
+
+	return fd;
+}
+
+int
+authdial(char *net, char *dom)
+{
+	return unix_dial(authserver, "567");
+}
+
+static int
+getastickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
+	int asfd, rv;
+	char *dom;
+
+	dom = tr->authdom;
+	asfd = authdial(nil, dom);
+	if(asfd < 0)
+		return -1;
+	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
+mkservertickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
+	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);
+	genrandom((uchar*)t.key, sizeof(t.key));
+	t.num = AuthTc;
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
+	t.num = AuthTs;
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
+	memset(&t, 0, sizeof(t));
+
+	return ret;
+}
+
+static int
+gettickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
+	int ret;
+	ret = getastickets(key, tr, y, tbuf, tbuflen);
+	if(ret > 0)
+		return ret;
+	return mkservertickets(key, tr, y, tbuf, tbuflen);
+}
+
+AuthInfo*
+p9any(int fd)
+{
+	char buf[1024], buf2[1024], *bbuf, *p, *proto, *dom;
+	uchar crand[2*NONCELEN], cchal[CHALLEN], y[PAKYLEN];
+	char tbuf[2*MAXTICKETLEN+MAXAUTHENTLEN+PAKYLEN], trbuf[TICKREQLEN+PAKYLEN];
+	Authkey authkey;
+	Authenticator auth;
+	int i, n, m, v2, dp9ik;
+	Ticketreq tr;
+	Ticket t;
+	AuthInfo *ai;
+
+	if(readstr(fd, buf, sizeof buf) < 0)
+		sysfatal("cannot read p9any negotiation: %r");
+	bbuf = buf;
+	v2 = 0;
+	if(strncmp(buf, "v.2 ", 4) == 0){
+		v2 = 1;
+		bbuf += 4;
+	}
+	dp9ik = 0;
+	proto = nil;
+	while(bbuf != nil){
+		if((p = strchr(bbuf, ' ')))
+			*p++ = 0;
+		if((dom = strchr(bbuf, '@')) == nil)
+			sysfatal("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)
+		sysfatal("server did not offer p9sk1 or dp9ik");
+	proto = estrdup(proto);
+	sprint(buf2, "%s %s", proto, dom);
+	if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1)
+		sysfatal("cannot write user/domain choice in p9any");
+	if(v2){
+		if(readstr(fd, buf, sizeof buf) < 0)
+			sysfatal("cannot read OK in p9any: %r");
+		if(memcmp(buf, "OK\0", 3) != 0)
+			sysfatal("did not get OK in p9any: got %s", buf);
+	}
+	genrandom(crand, 2*NONCELEN);
+	genrandom(cchal, CHALLEN);
+	if(write(fd, cchal, CHALLEN) != CHALLEN)
+		sysfatal("cannot write p9sk1 challenge: %r");
+
+	n = TICKREQLEN;
+	if(dp9ik)
+		n += PAKYLEN;
+
+	if(readn(fd, trbuf, n) != n || convM2TR(trbuf, TICKREQLEN, &tr) <= 0)
+		sysfatal("cannot read ticket request in p9sk1: %r");
+
+again:
+	if(!getkey(&authkey, user, tr.authdom, proto, pass))
+		sysfatal("no password");
+
+	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, user);
+	strecpy(tr.uid, tr.uid+sizeof tr.uid, user);
+
+	if(dp9ik){
+		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)
+		sysfatal("cannot get auth tickets in p9sk1: %r");
+
+	m = convM2T(tbuf, n, &t, &authkey);
+	if(m <= 0 || t.num != AuthTc){
+		print("?password mismatch with auth server\n");
+		if(pass != nil && *pass)
+			sysfatal("wrong password");
+		goto again;
+	}
+	n -= m;
+	memmove(tbuf, tbuf+m, n);
+
+	if(dp9ik && write(fd, y, PAKYLEN) != PAKYLEN)
+		sysfatal("cannot send authpak public key back: %r");
+
+	auth.num = AuthAc;
+	memmove(auth.rand, crand, NONCELEN);
+	memmove(auth.chal, tr.chal, CHALLEN);
+	m = convA2M(&auth, tbuf+n, sizeof(tbuf)-n, &t);
+	n += m;
+
+	if(write(fd, tbuf, n) != n)
+		sysfatal("cannot send ticket and authenticator back: %r");
+
+	if((n=read(fd, tbuf, m)) != m || memcmp(tbuf, "cpu:", 4) == 0){
+		if(n <= 4)
+			sysfatal("cannot read authenticator");
+
+		/*
+		 * didn't send back authenticator:
+		 * sent back fatal error message.
+		 */
+		memmove(buf, tbuf, n);
+		i = readn(fd, buf+n, sizeof buf-n-1);
+		if(i > 0)
+			n += i;
+		buf[n] = 0;
+		sysfatal("server says: %s", buf);
+	}
+	
+	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");
+		sysfatal("server lies");
+	}
+	memmove(crand+NONCELEN, auth.rand, NONCELEN);
+
+	// print("i am %s there.\n", t.suid);
+
+	ai = mallocz(sizeof(AuthInfo), 1);
+	ai->suid = estrdup(t.suid);
+	ai->cuid = estrdup(t.cuid);
+	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);
+	}
+
+	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));
+	free(proto);
+
+	return ai;
+}
+
+static int
+getkey(Authkey *key, char *user, char *dom, char *proto, char *pass)
+{
+	if(pass != nil && *pass)
+		pass = estrdup(pass);
+	else {
+		sysfatal("getkey: no password");
+	}
+	if(pass != nil){
+		memset(key, 0, sizeof(*key));
+		passtokey(key, pass);
+		if(strcmp(proto, "dp9ik") == 0) {
+			authpak_hash(key, user);
+		}
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/include/9windows.h
@@ -1,0 +1,21 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <io.h>
+#include <setjmp.h>
+#include <direct.h>
+#include <process.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+
+/* disable various silly warnings */
+#ifdef MSVC
+#pragma warning( disable : 4245 4305 4244 4102 4761 4090 4028 4024)
+#endif
+
+typedef __int64		p9_vlong;
+typedef	unsigned __int64 p9_uvlong;
+typedef unsigned uintptr;
--- /dev/null
+++ b/include/args.h
@@ -1,0 +1,20 @@
+extern char *argv0;
+#define	ARGBEGIN	for((argv0? 0: (argv0=*argv)),argv++,argc--;\
+			    argv[0] && argv[0][0]=='-' && argv[0][1];\
+			    argc--, argv++) {\
+				char *_args, *_argt;\
+				Rune _argc;\
+				_args = &argv[0][1];\
+				if(_args[0]=='-' && _args[1]==0){\
+					argc--; argv++; break;\
+				}\
+				_argc = 0;\
+				while(*_args && (_args += chartorune(&_argc, _args)))\
+				switch(_argc)
+#define	ARGEND		SET(_argt);USED(_argt); USED(_argc); USED(_args);}USED(argv); USED(argc);
+#define	ARGF()		(_argt=_args, _args="",\
+				(*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define	ARGC()		_argc
+
+#define	EARGF(x)		(_argt=_args, _args="",\
+				(*_argt? _argt: argv[1]? (argc--, *++argv): (x, (char*)0)))
--- /dev/null
+++ b/include/auth.h
@@ -1,0 +1,146 @@
+#ifdef PLAN9
+#pragma	src	"/sys/src/libauth"
+#pragma	lib	"libauth.a"
+#endif
+
+/*
+ * Interface for typical callers.
+ */
+
+typedef struct	AuthInfo	AuthInfo;
+typedef struct	Chalstate	Chalstate;
+typedef struct	Chapreply	Chapreply;
+typedef struct	MSchapreply	MSchapreply;
+typedef struct	UserPasswd	UserPasswd;
+typedef struct	AuthRpc		AuthRpc;
+
+enum
+{
+	MAXCHLEN=	256,		/* max challenge length	*/
+	MAXNAMELEN=	256,		/* maximum name length */
+	MD5LEN=		16,
+
+	ARok = 0,			/* rpc return values */
+	ARdone,
+	ARerror,
+	ARneedkey,
+	ARbadkey,
+	ARwritenext,
+	ARtoosmall,
+	ARtoobig,
+	ARrpcfailure,
+	ARphase,
+
+	AuthRpcMax = 4096,
+};
+
+struct AuthRpc
+{
+	int afd;
+	char ibuf[AuthRpcMax+1];	/* +1 for NUL in auth_rpc.c */
+	char obuf[AuthRpcMax];
+	char *arg;
+	uint narg;
+};
+
+struct AuthInfo
+{
+	char	*cuid;		/* caller id */
+	char	*suid;		/* server id */
+	char	*cap;		/* capability (only valid on server side) */
+	int	nsecret;	/* length of secret */
+	uchar	*secret;	/* secret */
+};
+
+struct Chalstate
+{
+	char	*user;
+	char	chal[MAXCHLEN];
+	int	nchal;
+	void	*resp;
+	int	nresp;
+
+/* for implementation only */
+	int	afd;			/* to factotum */
+	AuthRpc	*rpc;			/* to factotum */
+	char	userbuf[MAXNAMELEN];	/* temp space if needed */
+	int	userinchal;		/* user was sent to obtain challenge */
+};
+
+struct	Chapreply		/* for protocol "chap" */
+{
+	uchar	id;
+	char	resp[MD5LEN];
+};
+
+struct	MSchapreply	/* for protocol "mschap" */
+{
+	char	LMresp[24];		/* Lan Manager response */
+	char	NTresp[24];		/* NT response */
+};
+
+struct	UserPasswd
+{
+	char	*user;
+	char	*passwd;
+};
+
+extern	int	newns(char*, char*);
+extern	int	addns(char*, char*);
+
+extern	int	noworld(char*);
+extern	int	amount(int, char*, int, char*);
+
+extern	int	login(char*, char*, char*);
+
+typedef struct Attr Attr;
+enum {
+	AttrNameval,		/* name=val -- when matching, must have name=val */
+	AttrQuery,		/* name? -- when matching, must be present */
+	AttrDefault,		/* name=val -- when matching, if present must match INTERNAL */
+};
+struct Attr
+{
+	int type;
+	Attr *next;
+	char *name;
+	char *val;
+};
+
+typedef int AuthGetkey(char*);
+
+int	_attrfmt(Fmt*);
+Attr	*_copyattr(Attr*);
+Attr	*_delattr(Attr*, char*);
+Attr	*_findattr(Attr*, char*);
+void	_freeattr(Attr*);
+Attr	*_mkattr(int, char*, char*, Attr*);
+Attr	*_parseattr(char*);
+char	*_strfindattr(Attr*, char*);
+#ifdef VARARGCK
+#pragma varargck type "A" Attr*
+#endif
+
+extern AuthInfo*	fauth_proxy(int, AuthRpc *rpc, AuthGetkey *getkey, char *params);
+extern AuthInfo*	auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...);
+extern int		auth_getkey(char*);
+extern int		(*amount_getkey)(char*);
+extern void		auth_freeAI(AuthInfo *ai);
+extern int		auth_chuid(AuthInfo *ai, char *ns);
+extern Chalstate	*auth_challenge(char*, ...);
+extern AuthInfo*	auth_response(Chalstate*);
+extern int		auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...);
+extern void		auth_freechal(Chalstate*);
+extern AuthInfo*	auth_userpasswd(char *user, char *passwd);
+extern UserPasswd*	auth_getuserpasswd(AuthGetkey *getkey, char*, ...);
+extern AuthInfo*	auth_getinfo(AuthRpc *rpc);
+extern AuthRpc*		auth_allocrpc(int afd);
+extern Attr*		auth_attr(AuthRpc *rpc);
+extern void		auth_freerpc(AuthRpc *rpc);
+extern uint		auth_rpc(AuthRpc *rpc, char *verb, void *a, int n);
+#ifdef VARARGCK
+#pragma varargck argpos auth_proxy 3
+#pragma varargck argpos auth_challenge 1
+#pragma varargck argpos auth_respond 8
+#pragma varargck argpos auth_getuserpasswd 2
+#endif
--- /dev/null
+++ b/include/authsrv.h
@@ -1,0 +1,217 @@
+/*
+ * Interface for talking to authentication server.
+ */
+typedef struct	Ticket		Ticket;
+typedef struct	Ticketreq	Ticketreq;
+typedef struct	Authenticator	Authenticator;
+typedef struct	Nvrsafe		Nvrsafe;
+typedef struct	Passwordreq	Passwordreq;
+typedef struct	OChapreply	OChapreply;
+typedef struct	OMSchapreply	OMSchapreply;
+
+typedef struct	Authkey		Authkey;
+
+enum
+{
+	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,	/* secret max size */
+	PASSWDLEN=	28,	/* password max size */
+
+	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) */
+enum
+{
+	AuthTreq=1,	/* ticket request */
+	AuthChal=2,	/* challenge box request */
+	AuthPass=3,	/* change password */
+	AuthOK=4,	/* fixed length reply follows */
+	AuthErr=5,	/* error follows */
+	AuthMod=6,	/* modify user */
+	AuthApop=7,	/* apop authentication for pop3 */
+	AuthOKvar=9,	/* variable length reply follows */
+	AuthChap=10,	/* chap authentication for ppp */
+	AuthMSchap=11,	/* MS chap authentication for ppp */
+	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 */
+	AuthAc,		/* client generated authenticator */
+	AuthTp,		/* ticket encrypted with client's key for password change */
+	AuthHr,		/* http reply */
+};
+
+struct Ticketreq
+{
+	char	type;
+	char	authid[ANAMELEN];	/* server's encryption id */
+	char	authdom[DOMLEN];	/* server's authentication domain */
+	char	chal[CHALLEN];		/* challenge from server */
+	char	hostid[ANAMELEN];	/* host's encryption id */
+	char	uid[ANAMELEN];		/* uid of requesting user on host */
+};
+#define	TICKREQLEN	(3*ANAMELEN+CHALLEN+DOMLEN+1)
+
+struct Ticket
+{
+	char	num;			/* replay protection */
+	char	chal[CHALLEN];		/* server challenge */
+	char	cuid[ANAMELEN];		/* uid on client */
+	char	suid[ANAMELEN];		/* uid on server */
+	uchar	key[NONCELEN];		/* nonce key */
+
+	char	form;			/* (not transmitted) format (0 = des, 1 = ccpoly) */
+};
+#define	MAXTICKETLEN	(12+CHALLEN+2*ANAMELEN+NONCELEN+16)
+
+struct Authenticator
+{
+	char	num;			/* replay protection */
+	char	chal[CHALLEN];		/* server/client challenge */
+	uchar	rand[NONCELEN];		/* server/client nonce */
+};
+#define	MAXAUTHENTLEN	(12+CHALLEN+NONCELEN+16)
+
+struct Passwordreq
+{
+	char	num;
+	char	old[PASSWDLEN];
+	char	new[PASSWDLEN];
+	char	changesecret;
+	char	secret[SECRETLEN];	/* new secret */
+};
+#define	MAXPASSREQLEN	(12+2*PASSWDLEN+1+SECRETLEN+16)
+
+struct	OChapreply
+{
+	uchar	id;
+	char	uid[ANAMELEN];
+	char	resp[OMD5LEN];
+};
+#define OCHAPREPLYLEN	(1+ANAMELEN+OMD5LEN)
+
+struct	OMSchapreply
+{
+	char	uid[ANAMELEN];
+	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*, 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 auth key
+ */
+extern	void	passtokey(Authkey*, char*);
+
+extern	void	passtodeskey(char key[DESKEYLEN], char *p);
+extern	void	passtoaeskey(uchar key[AESKEYLEN], char *p);
+
+/*
+ *  Nvram interface
+ */
+enum {
+	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];	/* file server's authid's des key */
+	uchar	machsum;
+	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];	/* auth userid, e.g., bootes */
+	uchar	authidsum;
+	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	char*	readcons(char*, char*, int);
+
+/*
+ *  call up auth server
+ */
+extern	int	authdial(char *netroot, char *authdom);
+
+/*
+ *  exchange messages with auth server
+ */
+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);
+
+/*
+ *  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]);
--- /dev/null
+++ b/include/cursor.h
@@ -1,0 +1,6 @@
+struct	Cursor
+{
+	Point	offset;
+	uchar	clr[2*16];
+	uchar	set[2*16];
+};
--- /dev/null
+++ b/include/draw.h
@@ -1,0 +1,512 @@
+typedef struct	Cachefont Cachefont;
+typedef struct	Cacheinfo Cacheinfo;
+typedef struct	Cachesubf Cachesubf;
+typedef struct	Display Display;
+typedef struct	Font Font;
+typedef struct	Fontchar Fontchar;
+typedef struct	Image Image;
+typedef struct	Mouse Mouse;
+typedef struct	Point Point;
+typedef struct	Rectangle Rectangle;
+typedef struct	RGB RGB;
+typedef struct	Screen Screen;
+typedef struct	Subfont Subfont;
+
+extern	int	Rfmt(Fmt*);
+extern	int	Pfmt(Fmt*);
+
+enum
+{
+	DOpaque		= 0xFFFFFFFF,
+	DTransparent	= 0x00000000,		/* only useful for allocimage, memfillcolor */
+	DBlack		= 0x000000FF,
+	DWhite		= 0xFFFFFFFF,
+	DRed		= 0xFF0000FF,
+	DGreen		= 0x00FF00FF,
+	DBlue		= 0x0000FFFF,
+	DCyan		= 0x00FFFFFF,
+	DMagenta		= 0xFF00FFFF,
+	DYellow		= 0xFFFF00FF,
+	DPaleyellow	= 0xFFFFAAFF,
+	DDarkyellow	= 0xEEEE9EFF,
+	DDarkgreen	= 0x448844FF,
+	DPalegreen	= 0xAAFFAAFF,
+	DMedgreen	= 0x88CC88FF,
+	DDarkblue	= 0x000055FF,
+	DPalebluegreen= 0xAAFFFFFF,
+	DPaleblue		= 0x0000BBFF,
+	DBluegreen	= 0x008888FF,
+	DGreygreen	= 0x55AAAAFF,
+	DPalegreygreen	= 0x9EEEEEFF,
+	DYellowgreen	= 0x99994CFF,
+	DMedblue		= 0x000099FF,
+	DGreyblue	= 0x005DBBFF,
+	DPalegreyblue	= 0x4993DDFF,
+	DPurpleblue	= 0x8888CCFF,
+
+	DNotacolor	= 0xFFFFFF00,
+	DNofill		= DNotacolor,
+	
+};
+
+enum
+{
+	Displaybufsize	= 8000,
+	ICOSSCALE	= 1024,
+	Borderwidth =	4,
+};
+
+enum
+{
+	/* refresh methods */
+	Refbackup	= 0,
+	Refnone		= 1,
+	Refmesg		= 2
+};
+#define	NOREFRESH	((void*)-1)
+
+enum
+{
+	/* line ends */
+	Endsquare	= 0,
+	Enddisc		= 1,
+	Endarrow	= 2,
+	Endmask		= 0x1F
+};
+
+#define	ARROW(a, b, c)	(Endarrow|((a)<<5)|((b)<<14)|((c)<<23))
+
+typedef enum
+{
+	/* Porter-Duff compositing operators */
+	Clear	= 0,
+
+	SinD	= 8,
+	DinS	= 4,
+	SoutD	= 2,
+	DoutS	= 1,
+
+	S		= SinD|SoutD,
+	SoverD	= SinD|SoutD|DoutS,
+	SatopD	= SinD|DoutS,
+	SxorD	= SoutD|DoutS,
+
+	D		= DinS|DoutS,
+	DoverS	= DinS|DoutS|SoutD,
+	DatopS	= DinS|SoutD,
+	DxorS	= DoutS|SoutD,	/* == SxorD */
+
+	Ncomp = 12,
+} Drawop;
+
+/*
+ * image channel descriptors 
+ */
+enum {
+	CRed = 0,
+	CGreen,
+	CBlue,
+	CGrey,
+	CAlpha,
+	CMap,
+	CIgnore,
+	NChan,
+};
+
+#define __DC(type, nbits)	((((type)&15)<<4)|((nbits)&15))
+#define CHAN1(a,b)	__DC(a,b)
+#define CHAN2(a,b,c,d)	(CHAN1((a),(b))<<8|__DC((c),(d)))
+#define CHAN3(a,b,c,d,e,f)	(CHAN2((a),(b),(c),(d))<<8|__DC((e),(f)))
+#define CHAN4(a,b,c,d,e,f,g,h)	(CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h)))
+
+#define NBITS(c) ((c)&15)
+#define TYPE(c) (((c)>>4)&15)
+
+enum {
+	GREY1	= CHAN1(CGrey, 1),
+	GREY2	= CHAN1(CGrey, 2),
+	GREY4	= CHAN1(CGrey, 4),
+	GREY8	= CHAN1(CGrey, 8),
+	CMAP8	= CHAN1(CMap, 8),
+	RGB15	= CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5),
+	RGB16	= CHAN3(CRed, 5, CGreen, 6, CBlue, 5),
+	RGB24	= CHAN3(CRed, 8, CGreen, 8, CBlue, 8),
+	RGBA32	= CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8),
+	ARGB32	= CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8),	/* stupid VGAs */
+	XRGB32	= CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8),
+	BGR24	= CHAN3(CBlue, 8, CGreen, 8, CRed, 8),
+	ABGR32	= CHAN4(CAlpha, 8, CBlue, 8, CGreen, 8, CRed, 8),
+	XBGR32	= CHAN4(CIgnore, 8, CBlue, 8, CGreen, 8, CRed, 8),
+};
+
+extern	char*	chantostr(char*, ulong);
+extern	ulong	strtochan(char*);
+extern	int		chantodepth(ulong);
+
+struct	Point
+{
+	int	x;
+	int	y;
+};
+
+struct Rectangle
+{
+	Point	min;
+	Point	max;
+};
+
+typedef void	(*Reffn)(Image*, Rectangle, void*);
+
+struct Screen
+{
+	Display	*display;	/* display holding data */
+	int	id;		/* id of system-held Screen */
+	Image	*image;		/* unused; for reference only */
+	Image	*fill;		/* color to paint behind windows */
+};
+
+struct Display
+{
+	QLock		qlock;
+	int		locking;	/*program is using lockdisplay */
+	int		dirno;
+	int		fd;
+	int		reffd;
+	int		ctlfd;
+	int		imageid;
+	int		local;
+	void		(*error)(Display*, char*);
+	char		*devdir;
+	char		*windir;
+	char		oldlabel[64];
+	ulong		dataqid;
+	Image		*white;
+	Image		*black;
+	Image		*opaque;
+	Image		*transparent;
+	Image		*image;
+	uchar		*buf;
+	int		bufsize;
+	uchar		*bufp;
+	Font		*defaultfont;
+	Subfont		*defaultsubfont;
+	Image		*windows;
+	Image		*screenimage;
+	int		_isnewdisplay;
+};
+
+struct Image
+{
+	Display		*display;	/* display holding data */
+	int		id;		/* id of system-held Image */
+	Rectangle	r;		/* rectangle in data area, local coords */
+	Rectangle 	clipr;		/* clipping region */
+	int		depth;		/* number of bits per pixel */
+	ulong		chan;
+	int		repl;		/* flag: data replicates to tile clipr */
+	Screen		*screen;	/* 0 if not a window */
+	Image		*next;	/* next in list of windows */
+};
+
+struct RGB
+{
+	ulong	red;
+	ulong	green;
+	ulong	blue;
+};
+
+/*
+ * Subfonts
+ *
+ * given char c, Subfont *f, Fontchar *i, and Point p, one says
+ *	i = f->info+c;
+ *	draw(b, Rect(p.x+i->left, p.y+i->top,
+ *		p.x+i->left+((i+1)->x-i->x), p.y+i->bottom),
+ *		color, f->bits, Pt(i->x, i->top));
+ *	p.x += i->width;
+ * to draw characters in the specified color (itself an Image) in Image b.
+ */
+
+struct	Fontchar
+{
+	int		x;		/* left edge of bits */
+	uchar		top;		/* first non-zero scan-line */
+	uchar		bottom;		/* last non-zero scan-line + 1 */
+	char		left;		/* offset of baseline */
+	uchar		width;		/* width of baseline */
+};
+
+struct	Subfont
+{
+	char		*name;
+	short		n;		/* number of chars in font */
+	uchar		height;		/* height of image */
+	char		ascent;		/* top of image to baseline */
+	Fontchar 	*info;		/* n+1 character descriptors */
+	Image		*bits;		/* of font */
+	int		ref;
+};
+
+enum
+{
+	/* starting values */
+	LOG2NFCACHE =	6,
+	NFCACHE =	(1<<LOG2NFCACHE),	/* #chars cached */
+	NFLOOK =	5,			/* #chars to scan in cache */
+	NFSUBF =	2,			/* #subfonts to cache */
+	/* max value */
+	MAXFCACHE =	1024+NFLOOK,		/* upper limit */
+	MAXSUBF =	50,			/* generous upper limit */
+	/* deltas */
+	DSUBF = 	4,
+	/* expiry ages */
+	SUBFAGE	=	10000,
+	CACHEAGE =	10000
+};
+
+struct Cachefont
+{
+	Rune		min;	/* lowest rune value to be taken from subfont */
+	Rune		max;	/* highest rune value+1 to be taken from subfont */
+	int		offset;	/* position in subfont of character at min */
+	char		*name;			/* stored in font */
+	char		*subfontname;		/* to access subfont */
+};
+
+struct Cacheinfo
+{
+	ushort		x;		/* left edge of bits */
+	uchar		width;		/* width of baseline */
+	schar		left;		/* offset of baseline */
+	Rune		value;	/* value of character at this slot in cache */
+	ushort		age;
+};
+
+struct Cachesubf
+{
+	ulong		age;	/* for replacement */
+	Cachefont	*cf;	/* font info that owns us */
+	Subfont		*f;	/* attached subfont */
+};
+
+struct Font
+{
+	char		*name;
+	Display		*display;
+	short		height;	/* max height of image, interline spacing */
+	short		ascent;	/* top of image to baseline */
+	short		width;	/* widest so far; used in caching only */	
+	short		nsub;	/* number of subfonts */
+	ulong		age;	/* increasing counter; used for LRU */
+	int		maxdepth;	/* maximum depth of all loaded subfonts */
+	int		ncache;	/* size of cache */
+	int		nsubf;	/* size of subfont list */
+	Cacheinfo	*cache;
+	Cachesubf	*subf;
+	Cachefont	**sub;	/* as read from file */
+	Image		*cacheimage;
+};
+
+#define	Dx(r)	((r).max.x-(r).min.x)
+#define	Dy(r)	((r).max.y-(r).min.y)
+
+/*
+ * Image management
+ */
+extern Image*	_allocimage(Image*, Display*, Rectangle, ulong, int, ulong, int, int);
+extern Image*	allocimage(Display*, Rectangle, ulong, int, ulong);
+extern uchar*	bufimage(Display*, int);
+extern int	bytesperline(Rectangle, int);
+extern void	closedisplay(Display*);
+extern void	drawerror(Display*, char*);
+extern int	flushimage(Display*, int);
+extern int	freeimage(Image*);
+extern int	_freeimage1(Image*);
+extern int	geninitdraw(char*, void(*)(Display*, char*), char*, char*, char*, int);
+extern int	initdraw(void(*)(Display*, char*), char*, char*);
+extern int	newwindow(char*);
+extern Display*	initdisplay(char*, char*, void(*)(Display*, char*));
+extern int	loadimage(Image*, Rectangle, uchar*, int);
+extern int	cloadimage(Image*, Rectangle, uchar*, int);
+extern int	getwindow(Display*, int);
+extern int	gengetwindow(Display*, char*, Image**, Screen**, int);
+extern Image* readimage(Display*, int, int);
+extern Image* creadimage(Display*, int, int);
+extern int	unloadimage(Image*, Rectangle, uchar*, int);
+extern int	wordsperline(Rectangle, int);
+extern int	writeimage(int, Image*, int);
+extern Image*	namedimage(Display*, char*);
+extern int	nameimage(Image*, char*, int);
+extern Image* allocimagemix(Display*, ulong, ulong);
+
+/*
+ * Colors
+ */
+extern	void	readcolmap(Display*, RGB*);
+extern	void	writecolmap(Display*, RGB*);
+extern	ulong	setalpha(ulong, uchar);
+
+/*
+ * Windows
+ */
+extern Screen*	allocscreen(Image*, Image*, int);
+extern Image*	_allocwindow(Image*, Screen*, Rectangle, int, ulong);
+extern Image*	allocwindow(Screen*, Rectangle, int, ulong);
+extern void	bottomnwindows(Image**, int);
+extern void	bottomwindow(Image*);
+extern int	freescreen(Screen*);
+extern Screen*	publicscreen(Display*, int, ulong);
+extern void	topnwindows(Image**, int);
+extern void	topwindow(Image*);
+extern int	originwindow(Image*, Point, Point);
+
+/*
+ * Geometry
+ */
+extern Point		Pt(int, int);
+extern Rectangle	Rect(int, int, int, int);
+extern Rectangle	Rpt(Point, Point);
+extern Point		addpt(Point, Point);
+extern Point		subpt(Point, Point);
+extern Point		divpt(Point, int);
+extern Point		mulpt(Point, int);
+extern int		eqpt(Point, Point);
+extern int		eqrect(Rectangle, Rectangle);
+extern Rectangle	insetrect(Rectangle, int);
+extern Rectangle	rectaddpt(Rectangle, Point);
+extern Rectangle	rectsubpt(Rectangle, Point);
+extern Rectangle	canonrect(Rectangle);
+extern int		rectXrect(Rectangle, Rectangle);
+extern int		rectinrect(Rectangle, Rectangle);
+extern void		combinerect(Rectangle*, Rectangle);
+extern int		rectclip(Rectangle*, Rectangle);
+extern int		ptinrect(Point, Rectangle);
+extern void		replclipr(Image*, int, Rectangle);
+extern int		drawreplxy(int, int, int);	/* used to be drawsetxy */
+extern Point	drawrepl(Rectangle, Point);
+extern int		rgb2cmap(int, int, int);
+extern int		cmap2rgb(int);
+extern int		cmap2rgba(int);
+extern void		icossin(int, int*, int*);
+extern void		icossin2(int, int, int*, int*);
+extern int		badrect(Rectangle);
+
+/*
+ * Graphics
+ */
+extern void	draw(Image*, Rectangle, Image*, Image*, Point);
+extern void	drawop(Image*, Rectangle, Image*, Image*, Point, Drawop);
+extern void	gendraw(Image*, Rectangle, Image*, Point, Image*, Point);
+extern void	gendrawop(Image*, Rectangle, Image*, Point, Image*, Point, Drawop);
+extern void	line(Image*, Point, Point, int, int, int, Image*, Point);
+extern void	lineop(Image*, Point, Point, int, int, int, Image*, Point, Drawop);
+extern void	poly(Image*, Point*, int, int, int, int, Image*, Point);
+extern void	polyop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern void	fillpoly(Image*, Point*, int, int, Image*, Point);
+extern void	fillpolyop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern Point	string(Image*, Point, Image*, Point, Font*, char*);
+extern Point	stringop(Image*, Point, Image*, Point, Font*, char*, Drawop);
+extern Point	stringn(Image*, Point, Image*, Point, Font*, char*, int);
+extern Point	stringnop(Image*, Point, Image*, Point, Font*, char*, int, Drawop);
+extern Point	runestring(Image*, Point, Image*, Point, Font*, Rune*);
+extern Point	runestringop(Image*, Point, Image*, Point, Font*, Rune*, Drawop);
+extern Point	runestringn(Image*, Point, Image*, Point, Font*, Rune*, int);
+extern Point	runestringnop(Image*, Point, Image*, Point, Font*, Rune*, int, Drawop);
+extern Point	stringbg(Image*, Point, Image*, Point, Font*, char*, Image*, Point);
+extern Point	stringbgop(Image*, Point, Image*, Point, Font*, char*, Image*, Point, Drawop);
+extern Point	stringnbg(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point);
+extern Point	stringnbgop(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point, Drawop);
+extern Point	runestringbg(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point);
+extern Point	runestringbgop(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point, Drawop);
+extern Point	runestringnbg(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point);
+extern Point	runestringnbgop(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point, Drawop);
+extern Point	_string(Image*, Point, Image*, Point, Font*, char*, Rune*, int, Rectangle, Image*, Point, Drawop);
+extern Point	stringsubfont(Image*, Point, Image*, Subfont*, char*);
+extern int		bezier(Image*, Point, Point, Point, Point, int, int, int, Image*, Point);
+extern int		bezierop(Image*, Point, Point, Point, Point, int, int, int, Image*, Point, Drawop);
+extern int		bezspline(Image*, Point*, int, int, int, int, Image*, Point);
+extern int		bezsplineop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern int		bezsplinepts(Point*, int, Point**);
+extern int		fillbezier(Image*, Point, Point, Point, Point, int, Image*, Point);
+extern int		fillbezierop(Image*, Point, Point, Point, Point, int, Image*, Point, Drawop);
+extern int		fillbezspline(Image*, Point*, int, int, Image*, Point);
+extern int		fillbezsplineop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern void	ellipse(Image*, Point, int, int, int, Image*, Point);
+extern void	ellipseop(Image*, Point, int, int, int, Image*, Point, Drawop);
+extern void	fillellipse(Image*, Point, int, int, Image*, Point);
+extern void	fillellipseop(Image*, Point, int, int, Image*, Point, Drawop);
+extern void	arc(Image*, Point, int, int, int, Image*, Point, int, int);
+extern void	arcop(Image*, Point, int, int, int, Image*, Point, int, int, Drawop);
+extern void	fillarc(Image*, Point, int, int, Image*, Point, int, int);
+extern void	fillarcop(Image*, Point, int, int, Image*, Point, int, int, Drawop);
+extern void	border(Image*, Rectangle, int, Image*, Point);
+extern void	borderop(Image*, Rectangle, int, Image*, Point, Drawop);
+
+/*
+ * Font management
+ */
+extern Font*	openfont(Display*, char*);
+extern Font*	buildfont(Display*, char*, char*);
+extern void	freefont(Font*);
+extern Font*	mkfont(Subfont*, Rune);
+extern int	cachechars(Font*, char**, Rune**, ushort*, int, int*, char**);
+extern void	agefont(Font*);
+extern Subfont*	allocsubfont(char*, int, int, int, Fontchar*, Image*);
+extern Subfont*	lookupsubfont(Display*, char*);
+extern void	installsubfont(char*, Subfont*);
+extern void	uninstallsubfont(Subfont*);
+extern void	freesubfont(Subfont*);
+extern Subfont*	readsubfont(Display*, char*, int, int);
+extern Subfont*	readsubfonti(Display*, char*, int, Image*, int);
+extern int	writesubfont(int, Subfont*);
+extern void	_unpackinfo(Fontchar*, uchar*, int);
+extern Point	stringsize(Font*, char*);
+extern int	stringwidth(Font*, char*);
+extern int	stringnwidth(Font*, char*, int);
+extern Point	runestringsize(Font*, Rune*);
+extern int	runestringwidth(Font*, Rune*);
+extern int	runestringnwidth(Font*, Rune*, int);
+extern Point	strsubfontwidth(Subfont*, char*);
+extern int	loadchar(Font*, Rune, Cacheinfo*, int, int, char**);
+extern char*	subfontname(char*, char*, int);
+extern Subfont*	_getsubfont(Display*, char*);
+extern Subfont*	getdefont(Display*);
+extern void		lockdisplay(Display*);
+extern void	unlockdisplay(Display*);
+
+/*
+ * Predefined 
+ */
+extern	uchar	defontdata[];
+extern	int		sizeofdefont;
+extern	Point		ZP;
+extern	Rectangle	ZR;
+
+/*
+ * Set up by initdraw()
+ */
+extern	Display	*display;
+extern	Font		*font;
+// extern	Image	*screen;
+extern	Screen	*_screen;
+extern	int	_cursorfd;
+extern	void	_setdrawop(Display*, Drawop);
+
+#define	BGSHORT(p)		(((p)[0]<<0) | ((p)[1]<<8))
+#define	BGLONG(p)		((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16))
+#define	BPSHORT(p, v)		((p)[0]=(v), (p)[1]=((v)>>8))
+#define	BPLONG(p, v)		(BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16))
+
+/*
+ * Compressed image file parameters and helper routines
+ */
+#define	NMATCH	3		/* shortest match possible */
+#define	NRUN	(NMATCH+31)	/* longest match possible */
+#define	NMEM	1024		/* window size */
+#define	NDUMP	128		/* maximum length of dump */
+#define	NCBLOCK	6000		/* size of compressed blocks */
+extern	void	_twiddlecompressed(uchar*, int);
+extern	int	_compblocksize(Rectangle, int);
+
+extern	ulong	drawld2chan[];
+extern	void	drawsetdebug(int);
--- /dev/null
+++ b/include/dtos.h
@@ -1,0 +1,15 @@
+#if defined(linux) || defined(IRIX) || defined(SOLARIS) || defined(OSF1) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__) || defined(__sun) || defined(sun) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__EMSCRIPTEN__)
+#	include "unix.h"
+#	ifdef __APPLE__
+#		define panic dt_panic
+#	endif
+#elif defined(WINDOWS)
+#	include "9windows.h"
+#	define main mymain
+#else
+#	error "Define an OS"
+#endif
+
+#ifdef IRIX
+typedef int socklen_t;
+#endif
--- /dev/null
+++ b/include/fcall.h
@@ -1,0 +1,110 @@
+#define	VERSION9P	"9P2000"
+
+#define	MAXWELEM	16
+
+typedef
+struct	Fcall
+{
+	uchar	type;
+	u32int	fid;
+	ushort	tag;
+	u32int	msize;		/* Tversion, Rversion */
+	char	*version;	/* Tversion, Rversion */
+	ushort	oldtag;		/* Tflush */
+	char	*ename;		/* Rerror */
+	Qid	qid;		/* Rattach, Ropen, Rcreate */
+	u32int	iounit;		/* Ropen, Rcreate */
+	Qid	aqid;		/* Rauth */
+	u32int	afid;		/* Tauth, Tattach */
+	char	*uname;		/* Tauth, Tattach */
+	char	*aname;		/* Tauth, Tattach */
+	u32int	perm;		/* Tcreate */ 
+	char	*name;		/* Tcreate */
+	uchar	mode;		/* Tcreate, Topen */
+	u32int	newfid;		/* Twalk */
+	ushort	nwname;		/* Twalk */
+	char	*wname[MAXWELEM];	/* Twalk */
+	ushort	nwqid;		/* Rwalk */
+	Qid	wqid[MAXWELEM];		/* Rwalk */
+	vlong	offset;		/* Tread, Twrite */
+	u32int	count;		/* Tread, Twrite, Rread */
+	char	*data;		/* Twrite, Rread */
+	ushort	nstat;		/* Twstat, Rstat */
+	uchar	*stat;		/* Twstat, Rstat */
+} Fcall;
+
+
+#define	GBIT8(p)	((p)[0])
+#define	GBIT16(p)	((p)[0]|((p)[1]<<8))
+#define	GBIT32(p)	((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define	GBIT64(p)	((u32int)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\
+				((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32))
+
+#define	PBIT8(p,v)	(p)[0]=(v)
+#define	PBIT16(p,v)	(p)[0]=(v);(p)[1]=(v)>>8
+#define	PBIT32(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define	PBIT64(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\
+			(p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56
+
+#define	BIT8SZ		1
+#define	BIT16SZ		2
+#define	BIT32SZ		4
+#define	BIT64SZ		8
+#define	QIDSZ	(BIT8SZ+BIT32SZ+BIT64SZ)
+
+/* STATFIXLEN includes leading 16-bit count */
+/* The count, however, excludes itself; total size is BIT16SZ+count */
+#define STATFIXLEN	(BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ)	/* amount of fixed length data in a stat buffer */
+
+#define	NOTAG		(ushort)~0U	/* Dummy tag */
+#define	NOFID		(u32int)~0U	/* Dummy fid */
+#define	IOHDRSZ		24	/* ample room for Twrite/Rread header (iounit) */
+
+enum
+{
+	Tversion =	100,
+	Rversion,
+	Tauth =		102,
+	Rauth,
+	Tattach =	104,
+	Rattach,
+	Terror =	106,	/* illegal */
+	Rerror,
+	Tflush =	108,
+	Rflush,
+	Twalk =		110,
+	Rwalk,
+	Topen =		112,
+	Ropen,
+	Tcreate =	114,
+	Rcreate,
+	Tread =		116,
+	Rread,
+	Twrite =	118,
+	Rwrite,
+	Tclunk =	120,
+	Rclunk,
+	Tremove =	122,
+	Rremove,
+	Tstat =		124,
+	Rstat,
+	Twstat =	126,
+	Rwstat,
+	Tmax,
+};
+
+uint	convM2S(uchar*, uint, Fcall*);
+uint	convS2M(Fcall*, uchar*, uint);
+uint	sizeS2M(Fcall*);
+
+int	statcheck(uchar *abuf, uint nbuf);
+uint	convM2D(uchar*, uint, Dir*, char*);
+uint	convD2M(Dir*, uchar*, uint);
+uint	sizeD2M(Dir*);
+
+int	fcallfmt(Fmt*);
+int	dirfmt(Fmt*);
+int	dirmodefmt(Fmt*);
+
+int	read9pmsg(int, void*, uint);
+
--- /dev/null
+++ b/include/ip.h
@@ -1,0 +1,38 @@
+enum 
+{
+	IPaddrlen=	16,
+	IPv4addrlen=	4,
+	IPv4off=	12,
+};
+
+uchar*	defmask(uchar*);
+void	maskip(uchar*, uchar*, uchar*);
+int	eipfmt(Fmt*);
+int	isv4(uchar*);
+vlong	parseip(uchar*, char*);
+vlong	parseipmask(uchar*, char*, int);
+vlong	parseipandmask(uchar*, uchar*, char*, char*);
+char*	v4parseip(uchar*, char*);
+
+void	hnputv(void*, uvlong);
+void	hnputl(void*, uint);
+void	hnputs(void*, ushort);
+uvlong	nhgetv(void*);
+uint	nhgetl(void*);
+ushort	nhgets(void*);
+
+int	v6tov4(uchar*, uchar*);
+void	v4tov6(uchar*, uchar*);
+
+#define	ipcmp(x, y) memcmp(x, y, IPaddrlen)
+#define	ipmove(x, y) memmove(x, y, IPaddrlen)
+
+extern uchar IPv4bcast[IPaddrlen];
+extern uchar IPv4bcastobs[IPaddrlen];
+extern uchar IPv4allsys[IPaddrlen];
+extern uchar IPv4allrouter[IPaddrlen];
+extern uchar IPnoaddr[IPaddrlen];
+extern uchar v4prefix[IPaddrlen];
+extern uchar IPallbits[IPaddrlen];
+
+#define CLASS(p) ((*(uchar*)(p))>>6)
--- /dev/null
+++ b/include/keyboard.h
@@ -1,0 +1,62 @@
+typedef struct 	Keyboardctl Keyboardctl;
+typedef struct	Channel	Channel;
+
+struct	Keyboardctl
+{
+	Channel	*c;	/* chan(Rune)[20] */
+
+	char		*file;
+	int		consfd;		/* to cons file */
+	int		ctlfd;		/* to ctl file */
+	int		pid;		/* of slave proc */
+};
+
+
+extern	Keyboardctl*	initkeyboard(char*);
+extern	int		ctlkeyboard(Keyboardctl*, char*);
+extern	void		closekeyboard(Keyboardctl*);
+
+enum {
+	KF=	0xF000,	/* Rune: beginning of private Unicode space */
+	Spec=	0xF800,
+	PF=	Spec|0x20,	/* num pad function key */
+	Kview=	Spec|0x00,	/* view (shift window up) */
+	/* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+	Khome=	KF|0x0D,
+	Kup=	KF|0x0E,
+	Kdown=	Kview,
+	Kpgup=	KF|0x0F,
+	Kprint=	KF|0x10,
+	Kleft=	KF|0x11,
+	Kright=	KF|0x12,
+	Kpgdown=	KF|0x13,
+	Kins=	KF|0x14,
+
+	Kalt=	KF|0x15,
+	Kshift=	KF|0x16,
+	Kctl=	KF|0x17,
+
+	Kend=	KF|0x18,
+	Kscroll=	KF|0x19,
+	Kscrolloneup=	KF|0x20,
+	Kscrollonedown=	KF|0x21,
+
+	Ksoh=	0x01,
+	Kstx=	0x02,
+	Ketx=	0x03,
+	Keof=	0x04,
+	Kenq=	0x05,
+	Kack=	0x06,
+	Kbs=	0x08,
+	Knack=	0x15,
+	Ketb=	0x17,
+	Kdel=	0x7f,
+	Kesc=	0x1b,
+
+	Kbreak=	Spec|0x61,
+	Kcaps=	Spec|0x64,
+	Knum=	Spec|0x65,
+	Kmiddle=	Spec|0x66,
+	Kaltgr=	Spec|0x67,
+	Kmouse=	Spec|0x100,
+};
--- /dev/null
+++ b/include/lib.h
@@ -1,0 +1,313 @@
+/* avoid name conflicts */
+#define accept	pm_accept
+#define listen  pm_listen
+#define sleep	ksleep
+#define wakeup	kwakeup
+#ifdef strtod
+#undef strtod
+#endif
+#define strtod		fmtstrtod
+
+/* conflicts on some os's */
+#define encrypt	libencrypt
+#define decrypt libdecrypt
+#define oserror	liboserror
+#define clone	libclone
+#define atexit	libatexit
+#define log2	liblog2
+#define log	liblog
+#define reboot	libreboot
+#define strtoll libstrtoll
+#undef timeradd
+#define timeradd	xtimeradd
+#define gmtime	libgmtime
+
+
+#define	nil	((void*)0)
+
+typedef unsigned char	p9_uchar;
+typedef unsigned int	p9_uint;
+typedef unsigned int	p9_ulong;
+typedef int		p9_long;
+typedef signed char	p9_schar;
+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 */
+#define schar	p9_schar
+#define uchar	p9_uchar
+#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
+#define ulong	p9_ulong
+#define vlong	p9_vlong
+#define uvlong	p9_uvlong
+
+#define	nelem(x)	(sizeof(x)/sizeof((x)[0]))
+#define SET(x)		((x)=0)
+#define	USED(x)		if(x);else
+
+enum
+{
+	UTFmax		= 4,		/* maximum bytes per rune */
+	Runesync	= 0x80,		/* cannot represent part of a UTF sequence (<) */
+	Runeself	= 0x80,		/* rune and UTF sequences are the same (<) */
+	Runeerror	= 0xFFFD,	/* decoding error in UTF */
+	Runemax		= 0x10FFFF,	/* 21-bit rune */
+	Runemask	= 0x1FFFFF,	/* bits used by runes (see grep) */
+};
+
+/*
+ * new rune routines
+ */
+extern	int	runetochar(char*, Rune*);
+extern	int	chartorune(Rune*, char*);
+extern	int	runelen(long);
+extern	int	fullrune(char*, int);
+
+extern  int	wstrtoutf(char*, Rune*, int);
+extern  int	wstrutflen(Rune*);
+
+/*
+ * rune routines from converted str routines
+ */
+extern	long	utflen(char*);
+extern	char*	utfrune(char*, long);
+extern	char*	utfrrune(char*, long);
+
+/*
+ * Syscall data structures
+ */
+#define	MORDER	0x0003	/* mask for bits defining order of mounting */
+#define	MREPL	0x0000	/* mount replaces object */
+#define	MBEFORE	0x0001	/* mount goes before others in union directory */
+#define	MAFTER	0x0002	/* mount goes after others in union directory */
+#define	MCREATE	0x0004	/* permit creation in mounted directory */
+#define	MCACHE	0x0010	/* cache some data */
+#define	MMASK	0x0017	/* all bits on */
+
+#define	OREAD	0	/* open for read */
+#define	OWRITE	1	/* write */
+#define	ORDWR	2	/* read and write */
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+#define	OEXCL   0x1000	/* or'ed in, exclusive create */
+
+#define	NCONT	0	/* continue after note */
+#define	NDFLT	1	/* terminate after note */
+#define	NSAVE	2	/* clear note but hold state */
+#define	NRSTR	3	/* restore saved state */
+
+#define	ERRMAX			128	/* max length of error string */
+#define	KNAMELEN		28	/* max length of name held in kernel */
+
+/* bits in Qid.type */
+#define QTDIR		0x80		/* type bit for directories */
+#define QTAPPEND	0x40		/* type bit for append only files */
+#define QTEXCL		0x20		/* type bit for exclusive use files */
+#define QTMOUNT		0x10		/* type bit for mounted channel */
+#define QTAUTH		0x08		/* type bit for authentication file */
+#define QTFILE		0x00		/* plain file */
+
+/* bits in Dir.mode */
+#define DMDIR		0x80000000	/* mode bit for directories */
+#define DMAPPEND		0x40000000	/* mode bit for append only files */
+#define DMEXCL		0x20000000	/* mode bit for exclusive use files */
+#define DMMOUNT		0x10000000	/* mode bit for mounted channel */
+#define DMAUTH		0x08000000	/* mode bit for authentication files */
+#define DMREAD		0x4		/* mode bit for read permission */
+#define DMWRITE		0x2		/* mode bit for write permission */
+#define DMEXEC		0x1		/* mode bit for execute permission */
+
+typedef struct Lock
+{
+#ifdef PTHREAD
+	int init;
+	pthread_mutex_t mutex;
+#else
+	int key;
+#endif
+} Lock;
+
+typedef struct QLock
+{
+	Lock	lk;
+	struct Proc	*hold;
+	struct Proc	*first;
+	struct Proc	*last;
+} QLock;
+
+typedef
+struct Qid
+{
+	uvlong	path;
+	ulong	vers;
+	uchar	type;
+} Qid;
+
+typedef
+struct Dir {
+	/* system-modified data */
+	ushort	type;	/* server type */
+	uint	dev;	/* server subtype */
+	/* file data */
+	Qid	qid;	/* unique id from server */
+	ulong	mode;	/* permissions */
+	ulong	atime;	/* last read time */
+	ulong	mtime;	/* last write time */
+	vlong	length;	/* file length */
+	char	*name;	/* last element of path */
+	char	*uid;	/* owner name */
+	char	*gid;	/* group name */
+	char	*muid;	/* last modifier name */
+} Dir;
+
+typedef
+struct Waitmsg
+{
+	int pid;	/* of loved one */
+	ulong time[3];	/* of loved one & descendants */
+	char	*msg;
+} Waitmsg;
+
+/*
+ * print routines
+ */
+typedef struct Fmt	Fmt;
+struct Fmt{
+	uchar	runes;			/* output buffer is runes or chars? */
+	void	*start;			/* of buffer */
+	void	*to;			/* current place in the buffer */
+	void	*stop;			/* end of the buffer; overwritten if flush fails */
+	int	(*flush)(Fmt *);	/* called when to == stop */
+	void	*farg;			/* to make flush a closure */
+	int	nfmt;			/* num chars formatted so far */
+	va_list	args;			/* args passed to dofmt */
+	int	r;			/* % format Rune */
+	int	width;
+	int	prec;
+	ulong	flags;
+};
+
+enum{
+	FmtWidth	= 1,
+	FmtLeft		= FmtWidth << 1,
+	FmtPrec		= FmtLeft << 1,
+	FmtSharp	= FmtPrec << 1,
+	FmtSpace	= FmtSharp << 1,
+	FmtSign		= FmtSpace << 1,
+	FmtZero		= FmtSign << 1,
+	FmtUnsigned	= FmtZero << 1,
+	FmtShort	= FmtUnsigned << 1,
+	FmtLong		= FmtShort << 1,
+	FmtVLong	= FmtLong << 1,
+	FmtComma	= FmtVLong << 1,
+	FmtByte	= FmtComma << 1,
+
+	FmtFlag		= FmtByte << 1,
+	FmtLDouble	= FmtFlag << 1
+};
+
+extern	int	print(char*, ...);
+extern	char*	seprint(char*, char*, char*, ...);
+extern	char*	vseprint(char*, char*, char*, va_list);
+extern	int	snprint(char*, int, char*, ...);
+extern	int	vsnprint(char*, int, char*, va_list);
+extern	char*	smprint(char*, ...);
+extern	char*	vsmprint(char*, va_list);
+extern	int	sprint(char*, char*, ...);
+extern	int	fprint(int, char*, ...);
+extern	int	vfprint(int, char*, va_list);
+
+extern	int	(*doquote)(int);
+extern	int	runesprint(Rune*, char*, ...);
+extern	int	runesnprint(Rune*, int, char*, ...);
+extern	int	runevsnprint(Rune*, int, char*, va_list);
+extern	Rune*	runeseprint(Rune*, Rune*, char*, ...);
+extern	Rune*	runevseprint(Rune*, Rune*, char*, va_list);
+extern	Rune*	runesmprint(char*, ...);
+extern	Rune*	runevsmprint(char*, va_list);
+
+extern       Rune*	runestrchr(Rune*, Rune);
+extern       long	runestrlen(Rune*);
+extern       Rune*	runestrstr(Rune*, Rune*);
+
+extern	int	fmtfdinit(Fmt*, int, char*, int);
+extern	int	fmtfdflush(Fmt*);
+extern	int	fmtstrinit(Fmt*);
+extern	int	fmtinstall(int, int (*)(Fmt*));
+extern	char*	fmtstrflush(Fmt*);
+extern	int	runefmtstrinit(Fmt*);
+extern	Rune*	runefmtstrflush(Fmt*);
+extern	int	fmtstrcpy(Fmt*, char*);
+extern	int	fmtprint(Fmt*, char*, ...);
+extern	int	fmtvprint(Fmt*, char*, va_list);
+extern	void*	mallocz(ulong, int);
+
+#define getcallerpc(v) 0
+extern	char*	cleanname(char*);
+extern	void	sysfatal(char*, ...);
+extern	char*	strecpy(char*, char*, char*);
+
+extern	int	tokenize(char*, char**, int);
+extern	int	getfields(char*, char**, int, int, char*);
+extern	char*	utfecpy(char*, char*, char*);
+extern	int	tas(int*);
+extern	void	quotefmtinstall(void);
+extern	int	dec64(uchar*, int, char*, int);
+extern	int	enc64(char*, int, uchar*, int);
+extern	int	dec32(uchar*, int, char*, int);
+extern	int	enc32(char*, int, uchar*, int);
+extern	int	dec16(uchar*, int, char*, int);
+extern	int	enc16(char*, int, uchar*, int);
+extern	int	dec64chr(int);
+extern	int	enc64chr(int);
+extern	int	dec32chr(int);
+extern	int	enc32chr(int);
+extern	int	dec16chr(int);
+extern	int	enc16chr(int);
+extern	int	encodefmt(Fmt*);
+void		hnputs(void *p, unsigned short v);
+extern	int	dofmt(Fmt*, char*);
+extern	double	__NaN(void);
+extern	int	__isNaN(double);
+extern	double	strtod(const char*, char**);
+extern	vlong	strtoll(const char *, char **, int);
+extern	int	utfnlen(char*, long);
+extern	double	__Inf(int);
+extern	int	__isInf(double, int);
+
+extern int (*fmtdoquote)(int);
+
+extern void exits(char*);
+extern long readn(int, void*, long);
+
+
+/*
+ * 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);
--- /dev/null
+++ b/include/libc.h
@@ -1,0 +1,6 @@
+#include "lib.h"
+#define setmalloctag(a,b)
+#define qlock(x) ((void)(x))
+#define qunlock(x) ((void)(x))
+#define lock(x) ((void)(x))
+#define unlock(x) ((void)(x))
--- /dev/null
+++ b/include/libsec.h
@@ -1,0 +1,572 @@
+#ifndef _MPINT
+typedef struct mpint mpint;
+#endif
+
+/*
+ * AES definitions
+ */
+
+enum
+{
+	AESbsize=	16,
+	AESmaxkey=	32,
+	AESmaxrounds=	14
+};
+
+typedef struct AESstate AESstate;
+struct AESstate
+{
+	ulong	setup;
+	ulong	offset;
+	int	rounds;
+	int	keybytes;
+	void	*ekey;				/* expanded encryption round key */
+	void	*dkey;				/* expanded decryption round key */
+	uchar	key[AESmaxkey];			/* unexpanded key */
+	uchar	ivec[AESbsize];			/* initialization vector */
+	uchar	storage[512];			/* storage for expanded keys */
+};
+
+/* block ciphers */
+extern void (*aes_encrypt)(ulong rk[], int Nr, uchar pt[16], uchar ct[16]);
+extern void (*aes_decrypt)(ulong rk[], int Nr, uchar ct[16], uchar pt[16]);
+
+void	setupAESstate(AESstate *s, uchar key[], int nkey, uchar *ivec);
+
+void	aesCBCencrypt(uchar *p, int len, AESstate *s);
+void	aesCBCdecrypt(uchar *p, int len, AESstate *s);
+void	aesCFBencrypt(uchar *p, int len, AESstate *s);
+void	aesCFBdecrypt(uchar *p, int len, AESstate *s);
+void	aesOFBencrypt(uchar *p, int len, AESstate *s);
+
+typedef struct AESGCMstate AESGCMstate;
+struct AESGCMstate
+{
+	AESstate a;
+
+	ulong	H[4];
+	ulong	M[16][256][4];
+};
+
+void	setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen);
+void	aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen);
+void	aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s);
+int	aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s);
+
+/*
+ * Blowfish Definitions
+ */
+
+enum
+{
+	BFbsize	= 8,
+	BFrounds= 16
+};
+
+/* 16-round Blowfish */
+typedef struct BFstate BFstate;
+struct BFstate
+{
+	ulong	setup;
+
+	uchar	key[56];
+	uchar	ivec[8];
+
+	u32int 	pbox[BFrounds+2];
+	u32int	sbox[1024];
+};
+
+void	setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec);
+void	bfCBCencrypt(uchar*, int, BFstate*);
+void	bfCBCdecrypt(uchar*, int, BFstate*);
+void	bfECBencrypt(uchar*, int, BFstate*);
+void	bfECBdecrypt(uchar*, int, BFstate*);
+
+/*
+ * Chacha definitions
+ */
+
+enum
+{
+	ChachaBsize=	64,
+	ChachaKeylen=	256/8,
+	ChachaIVlen=	96/8,
+	XChachaIVlen=	192/8,
+};
+
+typedef struct Chachastate Chachastate;
+struct Chachastate
+{
+	union{
+		u32int	input[16];
+		struct {
+			u32int	constant[4];
+			u32int	key[8];
+			u32int	counter;
+			u32int	iv[3];
+		};
+	};
+	u32int	xkey[8];
+	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	hchacha(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds);
+
+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	xkey[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	salsa_core(u32int in[16], u32int out[16], int rounds);
+
+void	hsalsa(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds);
+
+/*
+ * DES definitions
+ */
+
+enum
+{
+	DESbsize=	8
+};
+
+/* single des */
+typedef struct DESstate DESstate;
+struct DESstate
+{
+	ulong	setup;
+	uchar	key[8];		/* unexpanded key */
+	ulong	expanded[32];	/* expanded key */
+	uchar	ivec[8];	/* initialization vector */
+};
+
+void	setupDESstate(DESstate *s, uchar key[8], uchar *ivec);
+void	des_key_setup(uchar[8], ulong[32]);
+void	block_cipher(ulong*, uchar*, int);
+void	desCBCencrypt(uchar*, int, DESstate*);
+void	desCBCdecrypt(uchar*, int, DESstate*);
+void	desECBencrypt(uchar*, int, DESstate*);
+void	desECBdecrypt(uchar*, int, DESstate*);
+
+/* 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 */
+enum {
+	DES3E=		0,
+	DES3D=		1,
+	DES3EEE=	0,
+	DES3EDE=	2,
+	DES3DED=	5,
+	DES3DDD=	7
+};
+
+typedef struct DES3state DES3state;
+struct DES3state
+{
+	ulong	setup;
+	uchar	key[3][8];		/* unexpanded key */
+	ulong	expanded[3][32];	/* expanded key */
+	uchar	ivec[8];		/* initialization vector */
+};
+
+void	setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec);
+void	triple_block_cipher(ulong keys[3][32], uchar*, int);
+void	des3CBCencrypt(uchar*, int, DES3state*);
+void	des3CBCdecrypt(uchar*, int, DES3state*);
+void	des3ECBencrypt(uchar*, int, DES3state*);
+void	des3ECBdecrypt(uchar*, int, DES3state*);
+
+/*
+ * 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 */
+	Poly1305dlen=	16,	/* Poly1305 digest length */
+
+	Hmacblksz	= 64,	/* in bytes; from rfc2104 */
+};
+
+typedef struct DigestState DigestState;
+struct DigestState
+{
+	uvlong	len;
+	union {
+		u32int	state[16];
+		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*	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*);
+
+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 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 */
+
+/*
+ * rc4
+ */
+typedef struct RC4state RC4state;
+struct RC4state
+{
+	 uchar	state[256];
+	 uchar	x;
+	 uchar	y;
+};
+
+void	setupRC4state(RC4state*, uchar*, int);
+void	rc4(RC4state*, uchar*, int);
+void	rc4skip(RC4state*, int);
+void	rc4back(RC4state*, int);
+
+/*
+ * rsa
+ */
+typedef struct RSApub RSApub;
+typedef struct RSApriv RSApriv;
+typedef struct PEMChain PEMChain;
+
+/* public/encryption key */
+struct RSApub
+{
+	mpint	*n;	/* modulus */
+	mpint	*ek;	/* exp (encryption key) */
+};
+
+/* private/decryption key */
+struct RSApriv
+{
+	RSApub	pub;
+
+	mpint	*dk;	/* exp (decryption key) */
+
+	/* 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 */
+};
+
+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);
+mpint*		rsadecrypt(RSApriv *k, mpint *in, mpint *out);
+RSApub*		rsapuballoc(void);
+void		rsapubfree(RSApub*);
+RSApriv*	rsaprivalloc(void);
+void		rsaprivfree(RSApriv*);
+RSApub*		rsaprivtopub(RSApriv*);
+RSApub*		X509toRSApub(uchar*, int, char*, int);
+RSApub*		asn1toRSApub(uchar*, int);
+RSApriv*	asn1toRSApriv(uchar*, int);
+void		asn1dump(uchar *der, int len);
+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*		X509rsaverify(uchar *cert, int ncert, RSApub *pk);
+char*		X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk);
+
+void		X509dump(uchar *cert, int ncert);
+
+mpint*		pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+int		pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+int		asn1encodeRSApub(RSApub *pk, uchar *buf, int len);
+int		asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*),
+			uchar *digest, uchar *buf, int len);
+
+
+/*
+ * elgamal
+ */
+typedef struct EGpub EGpub;
+typedef struct EGpriv EGpriv;
+typedef struct EGsig EGsig;
+
+/* public/encryption key */
+struct EGpub
+{
+	mpint	*p;	/* modulus */
+	mpint	*alpha;	/* generator */
+	mpint	*key;	/* (encryption key) alpha**secret mod p */
+};
+
+/* private/decryption key */
+struct EGpriv
+{
+	EGpub	pub;
+	mpint	*secret;	/* (decryption key) */
+};
+
+/* signature */
+struct EGsig
+{
+	mpint	*r, *s;
+};
+
+EGpriv*		eggen(int nlen, int rounds);
+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);
+EGpub*		egpuballoc(void);
+void		egpubfree(EGpub*);
+EGpriv*		egprivalloc(void);
+void		egprivfree(EGpriv*);
+EGsig*		egsigalloc(void);
+void		egsigfree(EGsig*);
+EGpub*		egprivtopub(EGpriv*);
+
+/*
+ * dsa
+ */
+typedef struct DSApub DSApub;
+typedef struct DSApriv DSApriv;
+typedef struct DSAsig DSAsig;
+
+/* 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 */
+};
+
+/* private/decryption key */
+struct DSApriv
+{
+	DSApub	pub;
+	mpint	*secret;	/* (decryption key) */
+};
+
+/* signature */
+struct DSAsig
+{
+	mpint	*r, *s;
+};
+
+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);
+void		dsapubfree(DSApub*);
+DSApriv*	dsaprivalloc(void);
+void		dsaprivfree(DSApriv*);
+DSAsig*		dsasigalloc(void);
+void		dsasigfree(DSAsig*);
+DSApub*		dsaprivtopub(DSApriv*);
+
+/*
+ * TLS
+ */
+typedef struct Thumbprint{
+	struct Thumbprint *next;
+	uchar	hash[SHA2_256dlen];
+	uchar	len;
+} Thumbprint;
+
+/* thumb.c */
+Thumbprint* initThumbprints(char *ok, char *crl, char *tag);
+void	freeThumbprints(Thumbprint *ok);
+int	okThumbprint(uchar *hash, int len, Thumbprint *ok);
+int	okCertificate(uchar *cert, int len, Thumbprint *ok);
+
+/* readcert.c */
+uchar	*readcert(char *filename, int *pcertlen);
+PEMChain*readcertchain(char *filename);
+
+/* aes_xts.c */
+void aes_xts_encrypt(AESstate *tweak, AESstate *ecb, uvlong sectorNumber, uchar *input, uchar *output, ulong len);
+void aes_xts_decrypt(AESstate *tweak, AESstate *ecb, uvlong sectorNumber, uchar *input, uchar *output, ulong len);
+
+typedef struct ECpoint{
+	int inf;
+	mpint *x;
+	mpint *y;
+	mpint *z;	/* nil when using affine coordinates */
+} ECpoint;
+
+typedef ECpoint ECpub;
+typedef struct ECpriv{
+	ECpoint a;
+	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, char *name, int nname, ECdomain *dom);
+char*	X509ecdsaverify(uchar *cert, int ncert, ECdomain *dom, ECpub *pub);
+char*	X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, 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);
+void	secp384r1(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);
+
+/* scrypt password-based key derivation function */
+char* scrypt(uchar *p, ulong plen, uchar *s, ulong slen,
+	ulong N, ulong R, ulong P,
+	uchar *d, ulong dlen);
+
+/* 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);
--- /dev/null
+++ b/include/memdraw.h
@@ -1,0 +1,171 @@
+typedef struct	Memimage Memimage;
+typedef struct	Memdata Memdata;
+typedef struct	Memsubfont Memsubfont;
+typedef struct	Memlayer Memlayer;
+typedef struct	Memcmap Memcmap;
+typedef struct	Memdrawparam	Memdrawparam;
+
+/*
+ * Memdata is allocated from main pool, but .data from the image pool.
+ * Memdata is allocated separately to permit patching its pointer after
+ * compaction when windows share the image data.
+ * The first word of data is a back pointer to the Memdata, to find
+ * The word to patch.
+ */
+
+struct Memdata
+{
+	ulong	*base;	/* allocated data pointer */
+	uchar	*bdata;	/* pointer to first byte of actual data; word-aligned */
+	int		ref;		/* number of Memimages using this data */
+	void*	imref;
+	int		allocd;	/* is this malloc'd? */
+};
+
+enum {
+	Frepl		= 1<<0,	/* is replicated */
+	Fsimple	= 1<<1,	/* is 1x1 */
+	Fgrey	= 1<<2,	/* is grey */
+	Falpha	= 1<<3,	/* has explicit alpha */
+	Fcmap	= 1<<4,	/* has cmap channel */
+	Fbytes	= 1<<5,	/* has only 8-bit channels */
+};
+
+struct Memimage
+{
+	Rectangle	r;		/* rectangle in data area, local coords */
+	Rectangle	clipr;		/* clipping region */
+	int		depth;	/* number of bits of storage per pixel */
+	int		nchan;	/* number of channels */
+	ulong	chan;	/* channel descriptions */
+	Memcmap	*cmap;
+
+	Memdata	*data;	/* pointer to data; shared by windows in this image */
+	int		zero;		/* data->bdata+zero==&byte containing (0,0) */
+	ulong	width;	/* width in words of a single scan line */
+	Memlayer	*layer;	/* nil if not a layer*/
+	ulong	flags;
+
+	int		shift[NChan];
+	int		mask[NChan];
+	int		nbits[NChan];
+};
+
+struct Memcmap
+{
+	uchar	cmap2rgb[3*256];
+	uchar	rgb2cmap[16*16*16];
+};
+
+/*
+ * Subfonts
+ *
+ * given char c, Subfont *f, Fontchar *i, and Point p, one says
+ *	i = f->info+c;
+ *	draw(b, Rect(p.x+i->left, p.y+i->top,
+ *		p.x+i->left+((i+1)->x-i->x), p.y+i->bottom),
+ *		color, f->bits, Pt(i->x, i->top));
+ *	p.x += i->width;
+ * to draw characters in the specified color (itself a Memimage) in Memimage b.
+ */
+
+struct	Memsubfont
+{
+	char		*name;
+	short	n;		/* number of chars in font */
+	uchar	height;		/* height of bitmap */
+	char	ascent;		/* top of bitmap to baseline */
+	Fontchar *info;		/* n+1 character descriptors */
+	Memimage	*bits;		/* of font */
+};
+
+/*
+ * Encapsulated parameters and information for sub-draw routines.
+ */
+enum {
+	Simplesrc=1<<0,
+	Simplemask=1<<1,
+	Replsrc=1<<2,
+	Replmask=1<<3,
+	Fullmask=1<<4,
+};
+struct	Memdrawparam
+{
+	Memimage *dst;
+	Rectangle	r;
+	Memimage *src;
+	Rectangle sr;
+	Memimage *mask;
+	Rectangle mr;
+	int op;
+
+	ulong state;
+	ulong mval;	/* if Simplemask, the mask pixel in mask format */
+	ulong mrgba;	/* mval in rgba */
+	ulong sval;	/* if Simplesrc, the source pixel in src format */
+	ulong srgba;	/* sval in rgba */
+	ulong sdval;	/* sval in dst format */
+};
+
+/*
+ * Memimage management
+ */
+
+extern Memimage*	allocmemimage(Rectangle, ulong);
+extern Memimage*	allocmemimaged(Rectangle, ulong, Memdata*);
+extern Memimage*	readmemimage(int);
+extern Memimage*	creadmemimage(int);
+extern int	writememimage(int, Memimage*);
+extern void	freememimage(Memimage*);
+extern int		loadmemimage(Memimage*, Rectangle, uchar*, int);
+extern int		cloadmemimage(Memimage*, Rectangle, uchar*, int);
+extern int		unloadmemimage(Memimage*, Rectangle, uchar*, int);
+extern ulong*	wordaddr(Memimage*, Point);
+extern uchar*	byteaddr(Memimage*, Point);
+extern int		drawclipnorepl(Memimage*, Rectangle*, Memimage*, Point*, Memimage*, Point*, Rectangle*, Rectangle*);
+extern int		drawclip(Memimage*, Rectangle*, Memimage*, Point*, Memimage*, Point*, Rectangle*, Rectangle*);
+extern void	memfillcolor(Memimage*, ulong);
+extern int		memsetchan(Memimage*, ulong);
+
+/*
+ * Graphics
+ */
+extern void	memdraw(Memimage*, Rectangle, Memimage*, Point, Memimage*, Point, int);
+extern void	memline(Memimage*, Point, Point, int, int, int, Memimage*, Point, int);
+extern void	mempoly(Memimage*, Point*, int, int, int, int, Memimage*, Point, int);
+extern void	memfillpoly(Memimage*, Point*, int, int, Memimage*, Point, int);
+extern void	_memfillpolysc(Memimage*, Point*, int, int, Memimage*, Point, int, int, int, int);
+extern void	memimagedraw(Memimage*, Rectangle, Memimage*, Point, Memimage*, Point, int);
+extern int	hwdraw(Memdrawparam*);
+extern void	memimageline(Memimage*, Point, Point, int, int, int, Memimage*, Point, int);
+extern void	_memimageline(Memimage*, Point, Point, int, int, int, Memimage*, Point, Rectangle, int);
+extern Point	memimagestring(Memimage*, Point, Memimage*, Point, Memsubfont*, char*);
+extern void	memellipse(Memimage*, Point, int, int, int, Memimage*, Point, int);
+extern void	memarc(Memimage*, Point, int, int, int, Memimage*, Point, int, int, int);
+extern Rectangle	memlinebbox(Point, Point, int, int, int);
+extern int	memlineendsize(int);
+extern void	_memmkcmap(void);
+extern int	memimageinit(void);
+
+/*
+ * Subfont management
+ */
+extern Memsubfont*	allocmemsubfont(char*, int, int, int, Fontchar*, Memimage*);
+extern Memsubfont*	openmemsubfont(char*);
+extern void	freememsubfont(Memsubfont*);
+extern Point	memsubfontwidth(Memsubfont*, char*);
+extern Memsubfont*	getmemdefont(void);
+
+/*
+ * Predefined 
+ */
+extern	Memimage*	memwhite;
+extern	Memimage*	memblack;
+extern	Memimage*	memopaque;
+extern	Memimage*	memtransparent;
+extern	Memcmap	*memdefcmap;
+
+/*
+ * Kernel interface
+ */
+void		memimagemove(void*, void*);
--- /dev/null
+++ b/include/memlayer.h
@@ -1,0 +1,53 @@
+#ifdef PLAN9
+#pragma src "/sys/src/libmemlayer"
+#pragma lib "libmemlayer.a"
+#endif
+
+typedef struct Memscreen Memscreen;
+typedef void (*Refreshfn)(Memimage*, Rectangle, void*);
+
+struct Memscreen
+{
+	Memimage	*frontmost;	/* frontmost layer on screen */
+	Memimage	*rearmost;	/* rearmost layer on screen */
+	Memimage	*image;		/* upon which all layers are drawn */
+	Memimage	*fill;			/* if non-zero, picture to use when repainting */
+};
+
+struct Memlayer
+{
+	Rectangle		screenr;	/* true position of layer on screen */
+	Point			delta;	/* add delta to go from image coords to screen */
+	Memscreen	*screen;	/* screen this layer belongs to */
+	Memimage	*front;	/* window in front of this one */
+	Memimage	*rear;	/* window behind this one*/
+	int		clear;	/* layer is fully visible */
+	Memimage	*save;	/* save area for obscured parts */
+	Refreshfn	refreshfn;		/* function to call to refresh obscured parts if save==nil */
+	void		*refreshptr;	/* argument to refreshfn */
+};
+
+/*
+ * These functions accept local coordinates
+ */
+int			memload(Memimage*, Rectangle, uchar*, int, int);
+int			memunload(Memimage*, Rectangle, uchar*, int);
+
+/*
+ * All these functions accept screen coordinates, not local ones.
+ */
+void			_memlayerop(void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), Memimage*, Rectangle, Rectangle, void*);
+Memimage*	memlalloc(Memscreen*, Rectangle, Refreshfn, void*, ulong);
+void			memldelete(Memimage*);
+void			memlfree(Memimage*);
+void			memltofront(Memimage*);
+void			memltofrontn(Memimage**, int);
+void			_memltofrontfill(Memimage*, int);
+void			memltorear(Memimage*);
+void			memltorearn(Memimage**, int);
+int			memlsetrefresh(Memimage*, Refreshfn, void*);
+void			memlhide(Memimage*, Rectangle);
+void			memlexpose(Memimage*, Rectangle);
+void			_memlsetclear(Memscreen*);
+int			memlorigin(Memimage*, Point, Point);
+void			memlnorefresh(Memimage*, Rectangle, void*);
--- /dev/null
+++ b/include/mp.h
@@ -1,0 +1,176 @@
+#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
+ */
+typedef struct mpint mpint;
+
+struct mpint
+{
+	int	sign;	/* +1 or -1 */
+	int	size;	/* allocated digits */
+	int	top;	/* significant digits */
+	mpdigit	*p;
+	char	flags;
+};
+
+enum
+{
+	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 */
+void	mpfree(mpint *b);
+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 */
+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 */
+int	mpfmt(Fmt*);
+char*	mptoa(mpint*, int, char*, int);
+mpint*	letomp(uchar*, uint, mpint*);	/* byte array, little-endian */
+int	mptole(mpint*, uchar*, uint, uchar**);
+void	mptolel(mpint *b, uchar *p, int n);
+mpint*	betomp(uchar*, uint, mpint*);	/* byte array, big-endian */
+int	mptobe(mpint*, uchar*, uint, uchar**);
+void	mptober(mpint *b, uchar *p, int n);
+uint	mptoui(mpint*);			/* unsigned int */
+mpint*	uitomp(uint, mpint*);
+int	mptoi(mpint*);			/* int */
+mpint*	itomp(int, mpint*);
+uvlong	mptouv(mpint*);			/* unsigned vlong */
+mpint*	uvtomp(uvlong, mpint*);
+vlong	mptov(mpint*);			/* vlong */
+mpint*	vtomp(vlong, mpint*);
+
+/* 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 */
+
+/* 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);
+void	mpasr(mpint *b, int shift, 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 */
+int	mpcmp(mpint *b1, mpint *b2);
+
+/* 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 */
+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 */
+
+/* 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 */
+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 */
+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 */
+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 */
+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 */
+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 */
+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 */
+void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
+
+/* 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 */
+
+/* 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 */
+};
+
+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	m;
+	int	(*reduce)(Mfield*, mpint*, mpint*);
+};
+
+mpint *mpfield(mpint*);
+
+Mfield *gmfield(mpint*);
+Mfield *cnfield(mpint*);
--- /dev/null
+++ b/include/u.h
@@ -1,0 +1,29 @@
+#include "dtos.h"
+
+/* avoid name conflicts */
+#undef accept
+#undef listen
+
+/* sys calls */
+#undef bind
+#undef chdir
+#undef close
+#undef create
+#undef dup
+#undef export
+#undef fstat
+#undef fwstat
+#undef mount
+#undef open
+#undef start
+#undef read
+#undef remove
+#undef seek
+#undef stat
+#undef write
+#undef wstat
+#undef unmount
+#undef pipe
+#undef iounit
+
+#define EXPORT EMSCRIPTEN_KEEPALIVE
--- /dev/null
+++ b/include/unix.h
@@ -1,0 +1,42 @@
+#undef _FORTIFY_SOURCE	/* stupid ubuntu warnings */
+#define __BSD_VISIBLE 1 /* FreeBSD 5.x */
+#define _BSD_SOURCE 1
+#define _NETBSD_SOURCE 1	/* NetBSD */
+#define _SVID_SOURCE 1
+#define _DEFAULT_SOURCE 1
+#if !defined(__APPLE__) && !defined(__OpenBSD__)
+#	define _XOPEN_SOURCE 1000
+#	define _XOPEN_SOURCE_EXTENDED 1
+#endif
+#define _LARGEFILE64_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <time.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#ifdef PTHREAD
+#include <pthread.h>
+#endif
+
+#ifdef TRACE_MALLOC
+#define malloc malloc0
+#define free free0
+void *malloc(size_t);
+void free(void *);
+#endif
+
+typedef long long		p9_vlong;
+typedef unsigned long long p9_uvlong;
+typedef uintptr_t uintptr;
--- /dev/null
+++ b/libauthsrv/Makefile
@@ -1,0 +1,36 @@
+ROOT=..
+include ../Make.config
+LIB=libauthsrv.a
+
+OFILES=\
+	authpak.$O\
+	passtokey.$O\
+	form1.$O\
+	hkdf_x_plan9.$O\
+	convT2M.$O\
+	convM2T.$O\
+	convA2M.$O\
+	convM2A.$O\
+	_asgetticket.$O\
+	_asrdresp.$O\
+	_asrequest.$O\
+	convM2PR.$O\
+	convM2TR.$O\
+	convPR2M.$O\
+	convTR2M.$O\
+	_asgetpakkey.$O\
+	_asgetresp.$O\
+#	nvcsum.$O\
+#	readcons.$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;
+}
--- /dev/null
+++ b/libauthsrv/_asgetticket.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asgetticket(int fd, Ticketreq *tr, char *tbuf, int tbuflen)
+{
+	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;
+	}
+
+	errstr(err, ERRMAX);
+
+	return r;
+}
--- /dev/null
+++ b/libauthsrv/_asrdresp.c
@@ -1,0 +1,56 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+static char *pbmsg = "AS protocol botch";
+
+int
+_asrdresp(int fd, char *buf, int len)
+{
+	int n;
+	char error[64];
+
+	if(read(fd, buf, 1) != 1){
+		werrstr(pbmsg);
+		return -1;
+	}
+
+	n = len;
+	switch(buf[0]){
+	case AuthOK:
+		if(readn(fd, buf, len) != len){
+			werrstr(pbmsg);
+			return -1;
+		}
+		break;
+	case AuthErr:
+		if(readn(fd, error, sizeof error) != sizeof error){
+			werrstr(pbmsg);
+			return -1;
+		}
+		error[sizeof error-1] = '\0';
+		werrstr("remote: %s", error);
+		return -1;
+	case AuthOKvar:
+		if(readn(fd, error, 5) != 5){
+			werrstr(pbmsg);
+			return -1;
+		}
+		error[5] = 0;
+		n = atoi(error);
+		if(n <= 0 || n > len){
+			werrstr(pbmsg);
+			return -1;
+		}
+		memset(buf, 0, len);
+		if(readn(fd, buf, n) != n){
+			werrstr(pbmsg);
+			return -1;
+		}
+		break;
+	default:
+		werrstr(pbmsg);
+		return -1;
+	}
+	return n;
+}
--- /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;
+}
--- /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	lk;
+	mpint	*P;
+	mpint	*A;
+	mpint	*D;
+	mpint	*X;
+	mpint	*Y;
+};
+
+static PAKcurve*
+authpak_curve(void)
+{
+	static PAKcurve a;
+
+	lock(&a.lk);
+	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.lk);
+	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;
+}
--- /dev/null
+++ b/libauthsrv/convA2M.c
@@ -1,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1B2M(char *ap, int n, uchar key[32]);
+
+int
+convA2M(Authenticator *f, char *ap, int n, Ticket *t)
+{
+	uchar *p;
+
+	if(n < 1+CHALLEN)
+		return 0;
+
+	p = (uchar*)ap;
+	*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;
+}
--- /dev/null
+++ b/libauthsrv/convM2A.c
@@ -1,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1M2B(char *ap, int n, uchar key[32]);
+
+int
+convM2A(char *ap, int n, Authenticator *f, Ticket *t)
+{
+	uchar buf[MAXAUTHENTLEN], *p;
+	int m;
+
+	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;
+}
--- /dev/null
+++ b/libauthsrv/convM2PR.c
@@ -1,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1M2B(char *ap, int n, uchar key[32]);
+
+int
+convM2PR(char *ap, int n, Passwordreq *f, Ticket *t)
+{
+	uchar *p, buf[MAXPASSREQLEN];
+	int m;
+
+	memset(f, 0, sizeof(Passwordreq));
+	if(t->form == 0){
+		m = 1+2*PASSWDLEN+1+SECRETLEN;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		decrypt(t->key, buf, m);
+	} else {
+		m = 12+2*PASSWDLEN+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, PASSWDLEN), p += PASSWDLEN;
+	memmove(f->new, p, PASSWDLEN), p += PASSWDLEN;
+	f->changesecret = *p++;
+	memmove(f->secret, p, SECRETLEN);
+	f->old[PASSWDLEN-1] = 0;
+	f->new[PASSWDLEN-1] = 0;
+	f->secret[SECRETLEN-1] = 0;
+
+	return m;
+}
--- /dev/null
+++ b/libauthsrv/convM2T.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1check(char *ap, int n);
+extern int form1M2B(char *ap, int n, uchar key[32]);
+
+int
+convM2T(char *ap, int n, Ticket *f, Authkey *k)
+{
+	uchar buf[MAXTICKETLEN], *p;
+	int m;
+
+	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;
+	f->suid[ANAMELEN-1] = 0;
+
+	return m;
+}
--- /dev/null
+++ b/libauthsrv/convM2TR.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+convM2TR(char *ap, int n, Ticketreq *f)
+{
+	uchar *p;
+
+	memset(f, 0, sizeof(Ticketreq));
+	if(n < TICKREQLEN)
+		return -TICKREQLEN;
+
+	p = (uchar*)ap;
+	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;
+	f->authdom[DOMLEN-1] = 0;
+	f->hostid[ANAMELEN-1] = 0;
+	f->uid[ANAMELEN-1] = 0;
+	n = p - (uchar*)ap;
+
+	return n;
+}
--- /dev/null
+++ b/libauthsrv/convPR2M.c
@@ -1,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1B2M(char *ap, int n, uchar key[32]);
+
+int
+convPR2M(Passwordreq *f, char *ap, int n, Ticket *t)
+{
+	uchar *p;
+
+	if(n < 1+2*PASSWDLEN+1+SECRETLEN)
+		return 0;
+
+	p = (uchar*)ap;
+	*p++ = f->num;
+	memmove(p, f->old, PASSWDLEN), p += PASSWDLEN;
+	memmove(p, f->new, PASSWDLEN), p += PASSWDLEN;
+	*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*PASSWDLEN+1+SECRETLEN+16)
+			return 0;
+		return form1B2M(ap, p - (uchar*)ap, t->key);
+	}
+
+	return 0;
+}
+
--- /dev/null
+++ b/libauthsrv/convT2M.c
@@ -1,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+extern int form1B2M(char *ap, int n, uchar key[32]);
+
+int
+convT2M(Ticket *f, char *ap, int n, Authkey *key)
+{
+	uchar *p;
+
+	if(n < 1+CHALLEN+2*ANAMELEN)
+		return 0;
+
+	p = (uchar*)ap;
+	*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;
+}
--- /dev/null
+++ b/libauthsrv/convTR2M.c
@@ -1,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+convTR2M(Ticketreq *f, char *ap, int n)
+{
+	uchar *p;
+
+	if(n < TICKREQLEN)
+		return 0;
+
+	p = (uchar*)ap;
+	*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/hkdf_x_plan9.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <authsrv.h>
+
+void
+hkdf_x_plan9(uchar crand[2*NONCELEN], uchar key[NONCELEN], uchar secret[256])
+{
+	static char info[] = "Plan 9 session secret";
+	
+	hkdf_x(crand, 2*NONCELEN,
+		(uchar*)info, sizeof(info)-1,
+		key, NONCELEN,
+		secret, 256,
+		hmac_sha2_256, SHA2_256dlen);
+}
--- /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);
+	}
--- /dev/null
+++ b/libauthsrv/passtokey.c
@@ -1,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+void
+passtodeskey(char key[DESKEYLEN], char *p)
+{
+	uchar buf[ANAMELEN], *t;
+	int i, n;
+
+	n = strlen(p);
+	if(n >= ANAMELEN)
+		n = ANAMELEN-1;
+	memset(buf, ' ', 8);
+	t = buf;
+	strncpy((char*)t, p, n);
+	t[n] = 0;
+	memset(key, 0, DESKEYLEN);
+	for(;;){
+		for(i = 0; i < DESKEYLEN; i++)
+			key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1)));
+		if(n <= 8)
+			return;
+		n -= 8;
+		t += 8;
+		if(n < 8){
+			t -= 8 - n;
+			n = 8;
+		}
+		encrypt(key, t, 8);
+	}
+}
+
+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);
+}
--- /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);
+	}
--- /dev/null
+++ b/libc/Makefile
@@ -1,0 +1,95 @@
+ROOT=..
+include ../Make.config
+LIB=libc.a
+
+OFILES=\
+	u16.$O\
+	mallocz.$O\
+	u32.$O\
+	u64.$O\
+	genrandom.$O\
+	sysfatal.$O\
+	fmt.$O\
+	fmtfd.$O\
+	fmtfdflush.$O\
+	fmtlock.$O\
+	fmtprint.$O\
+	fmtquote.$O\
+	fmtrune.$O\
+	fmtstr.$O\
+	fmtvprint.$O\
+	vseprint.$O\
+	dofmt.$O\
+	dorfmt.$O\
+	encodefmt.$O\
+	fcallfmt.$O\
+	fltfmt.$O\
+	rune.$O\
+	runefmtstr.$O\
+	runeseprint.$O\
+	runesmprint.$O\
+	runesnprint.$O\
+	runesprint.$O\
+	runestrchr.$O\
+	runestrlen.$O\
+	runestrstr.$O\
+	runetype.$O\
+	runevseprint.$O\
+	runevsmprint.$O\
+	runevsnprint.$O\
+	seprint.$O\
+	smprint.$O\
+	snprint.$O\
+	sprint.$O\
+	strecpy.$O\
+	strtod.$O\
+	strtoll.$O\
+	tokenize.$O\
+	utfecpy.$O\
+	utflen.$O\
+	utfnlen.$O\
+	utfrrune.$O\
+	utfrune.$O\
+	utfutf.$O\
+	vfprint.$O\
+	vsmprint.$O\
+	vsnprint.$O\
+	nan64.$O\
+	atexit.$O\
+	charstod.$O\
+	cleanname.$O\
+	convD2M.$O\
+	convM2D.$O\
+	convM2S.$O\
+	convS2M.$O\
+	ctime.$O\
+	crypt.$O\
+	dirfstat.$O\
+	dirfwstat.$O\
+	dirmodefmt.$O\
+	dirstat.$O\
+	dirwstat.$O\
+	fprint.$O\
+	getfields.$O\
+	getpid.$O\
+	netmkaddr.$O\
+	nsec.$O\
+	pow10.$O\
+	pushssl.$O\
+	pushtls.$O\
+	read9pmsg.$O\
+	readn.$O\
+	truerand.$O\
+	print.$O\
+	write.$O\
+	tas.$O\
+	werrstr.$O\
+
+default: $(LIB)
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libc/atexit.c
@@ -1,0 +1,46 @@
+#include <u.h>
+#include <libc.h>
+
+#define	NEXIT	33
+
+typedef struct Onex Onex;
+struct Onex{
+	void	(*f)(void);
+	int	pid;
+};
+
+static Lock onexlock;
+Onex onex[NEXIT];
+
+int
+atexit(void (*f)(void))
+{
+	int i;
+
+	lock(&onexlock);
+	for(i=0; i<NEXIT; i++)
+		if(onex[i].f == 0) {
+			onex[i].pid = getpid();
+			onex[i].f = f;
+			unlock(&onexlock);
+			return 1;
+		}
+	unlock(&onexlock);
+	return 0;
+}
+
+void
+exits(char *s)
+{
+	int i, pid;
+	void (*f)(void);
+
+	pid = getpid();
+	for(i = NEXIT-1; i >= 0; i--)
+		if((f = onex[i].f) && pid == onex[i].pid) {
+			onex[i].f = 0;
+			(*f)();
+		}
+	exit(s && *s);
+}
+
--- /dev/null
+++ b/libc/charstod.c
@@ -1,0 +1,70 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * Reads a floating-point number by interpreting successive characters
+ * returned by (*f)(vp).  The last call it makes to f terminates the
+ * scan, so is not a character in the number.  It may therefore be
+ * necessary to back up the input stream up one byte after calling charstod.
+ */
+
+double
+fmtcharstod(int(*f)(void*), void *vp)
+{
+	double num, dem;
+	int neg, eneg, dig, exp, c;
+
+	num = 0;
+	neg = 0;
+	dig = 0;
+	exp = 0;
+	eneg = 0;
+
+	c = (*f)(vp);
+	while(c == ' ' || c == '\t')
+		c = (*f)(vp);
+	if(c == '-' || c == '+'){
+		if(c == '-')
+			neg = 1;
+		c = (*f)(vp);
+	}
+	while(c >= '0' && c <= '9'){
+		num = num*10 + c-'0';
+		c = (*f)(vp);
+	}
+	if(c == '.')
+		c = (*f)(vp);
+	while(c >= '0' && c <= '9'){
+		num = num*10 + c-'0';
+		dig++;
+		c = (*f)(vp);
+	}
+	if(c == 'e' || c == 'E'){
+		c = (*f)(vp);
+		if(c == '-' || c == '+'){
+			if(c == '-'){
+				dig = -dig;
+				eneg = 1;
+			}
+			c = (*f)(vp);
+		}
+		while(c >= '0' && c <= '9'){
+			exp = exp*10 + c-'0';
+			c = (*f)(vp);
+		}
+	}
+	exp -= dig;
+	if(exp < 0){
+		exp = -exp;
+		eneg = !eneg;
+	}
+	dem = __fmtpow10(exp);
+	if(eneg)
+		num /= dem;
+	else
+		num *= dem;
+	if(neg)
+		return -num;
+	return num;
+}
--- /dev/null
+++ b/libc/cleanname.c
@@ -1,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+#define SEP(x)	((x)=='/' || (x) == 0)
+char*
+cleanname(char *name)
+{
+	char *p, *q, *dotdot;
+	int rooted, erasedprefix;
+
+	rooted = name[0] == '/';
+	erasedprefix = 0;
+
+	/*
+	 * invariants:
+	 *	p points at beginning of path element we're considering.
+	 *	q points just past the last path element we wrote (no slash).
+	 *	dotdot points just past the point where .. cannot backtrack
+	 *		any further (no slash).
+	 */
+	p = q = dotdot = name+rooted;
+	while(*p) {
+		if(p[0] == '/')	/* null element */
+			p++;
+		else if(p[0] == '.' && SEP(p[1])) {
+			if(p == name)
+				erasedprefix = 1;
+			p += 1;	/* don't count the separator in case it is nul */
+		} else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
+			p += 2;
+			if(q > dotdot) {	/* can backtrack */
+				while(--q > dotdot && *q != '/')
+					;
+			} else if(!rooted) {	/* /.. is / but ./../ is .. */
+				if(q != name)
+					*q++ = '/';
+				*q++ = '.';
+				*q++ = '.';
+				dotdot = q;
+			}
+			if(q == name)
+				erasedprefix = 1;	/* erased entire path via dotdot */
+		} else {	/* real path element */
+			if(q != name+rooted)
+				*q++ = '/';
+			while((*q = *p) != '/' && *q != 0)
+				p++, q++;
+		}
+	}
+	if(q == name)	/* empty string is really ``.'' */
+		*q++ = '.';
+	*q = '\0';
+	if(erasedprefix && name[0] == '#'){
+		/* this was not a #x device path originally - make it not one now */
+		memmove(name+2, name, strlen(name)+1);
+		name[0] = '.';
+		name[1] = '/';
+	}
+	return name;
+}
--- /dev/null
+++ b/libc/convD2M.c
@@ -1,0 +1,95 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<fcall.h>
+
+uint
+sizeD2M(Dir *d)
+{
+	char *sv[4];
+	int i, ns;
+
+	sv[0] = d->name;
+	sv[1] = d->uid;
+	sv[2] = d->gid;
+	sv[3] = d->muid;
+
+	ns = 0;
+	for(i = 0; i < 4; i++)
+		if(sv[i])
+			ns += strlen(sv[i]);
+
+	return STATFIXLEN + ns;
+}
+
+uint
+convD2M(Dir *d, uchar *buf, uint nbuf)
+{
+	uchar *p, *ebuf;
+	char *sv[4];
+	int i, ns, nsv[4], ss;
+
+	if(nbuf < BIT16SZ)
+		return 0;
+
+	p = buf;
+	ebuf = buf + nbuf;
+
+	sv[0] = d->name;
+	sv[1] = d->uid;
+	sv[2] = d->gid;
+	sv[3] = d->muid;
+
+	ns = 0;
+	for(i = 0; i < 4; i++){
+		if(sv[i])
+			nsv[i] = strlen(sv[i]);
+		else
+			nsv[i] = 0;
+		ns += nsv[i];
+	}
+
+	ss = STATFIXLEN + ns;
+
+	/* set size befor erroring, so user can know how much is needed */
+	/* note that length excludes count field itself */
+	PBIT16(p, ss-BIT16SZ);
+	p += BIT16SZ;
+
+	if(ss > nbuf)
+		return BIT16SZ;
+
+	PBIT16(p, d->type);
+	p += BIT16SZ;
+	PBIT32(p, d->dev);
+	p += BIT32SZ;
+	PBIT8(p, d->qid.type);
+	p += BIT8SZ;
+	PBIT32(p, d->qid.vers);
+	p += BIT32SZ;
+	PBIT64(p, d->qid.path);
+	p += BIT64SZ;
+	PBIT32(p, d->mode);
+	p += BIT32SZ;
+	PBIT32(p, d->atime);
+	p += BIT32SZ;
+	PBIT32(p, d->mtime);
+	p += BIT32SZ;
+	PBIT64(p, d->length);
+	p += BIT64SZ;
+
+	for(i = 0; i < 4; i++){
+		ns = nsv[i];
+		if(p + ns + BIT16SZ > ebuf)
+			return 0;
+		PBIT16(p, ns);
+		p += BIT16SZ;
+		if(ns)
+			memmove(p, sv[i], ns);
+		p += ns;
+	}
+
+	if(ss != p - buf)
+		return 0;
+
+	return p - buf;
+}
--- /dev/null
+++ b/libc/convM2D.c
@@ -1,0 +1,94 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<fcall.h>
+
+int
+statcheck(uchar *buf, uint nbuf)
+{
+	uchar *ebuf;
+	int i;
+
+	ebuf = buf + nbuf;
+
+	if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
+		return -1;
+
+	buf += STATFIXLEN - 4 * BIT16SZ;
+
+	for(i = 0; i < 4; i++){
+		if(buf + BIT16SZ > ebuf)
+			return -1;
+		buf += BIT16SZ + GBIT16(buf);
+	}
+
+	if(buf != ebuf)
+		return -1;
+
+	return 0;
+}
+
+static char nullstring[] = "";
+
+uint
+convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
+{
+	uchar *p, *ebuf;
+	char *sv[4];
+	int i, ns;
+
+	if(nbuf < STATFIXLEN)
+		return 0; 
+
+	p = buf;
+	ebuf = buf + nbuf;
+
+	p += BIT16SZ;	/* ignore size */
+	d->type = GBIT16(p);
+	p += BIT16SZ;
+	d->dev = GBIT32(p);
+	p += BIT32SZ;
+	d->qid.type = GBIT8(p);
+	p += BIT8SZ;
+	d->qid.vers = GBIT32(p);
+	p += BIT32SZ;
+	d->qid.path = GBIT64(p);
+	p += BIT64SZ;
+	d->mode = GBIT32(p);
+	p += BIT32SZ;
+	d->atime = GBIT32(p);
+	p += BIT32SZ;
+	d->mtime = GBIT32(p);
+	p += BIT32SZ;
+	d->length = GBIT64(p);
+	p += BIT64SZ;
+
+	for(i = 0; i < 4; i++){
+		if(p + BIT16SZ > ebuf)
+			return 0;
+		ns = GBIT16(p);
+		p += BIT16SZ;
+		if(p + ns > ebuf)
+			return 0;
+		if(strs){
+			sv[i] = strs;
+			memmove(strs, p, ns);
+			strs += ns;
+			*strs++ = '\0';
+		}
+		p += ns;
+	}
+
+	if(strs){
+		d->name = sv[0];
+		d->uid = sv[1];
+		d->gid = sv[2];
+		d->muid = sv[3];
+	}else{
+		d->name = nullstring;
+		d->uid = nullstring;
+		d->gid = nullstring;
+		d->muid = nullstring;
+	}
+	
+	return p - buf;
+}
--- /dev/null
+++ b/libc/convM2S.c
@@ -1,0 +1,315 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<fcall.h>
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+	uint n;
+
+	if(p+BIT16SZ > ep)
+		return nil;
+	n = GBIT16(p);
+	p += BIT16SZ - 1;
+	if(p+n+1 > ep)
+		return nil;
+	/* move it down, on top of count, to make room for '\0' */
+	memmove(p, p + 1, n);
+	p[n] = '\0';
+	*s = (char*)p;
+	p += n+1;
+	return p;
+}
+
+static
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+	if(p+QIDSZ > ep)
+		return nil;
+	q->type = GBIT8(p);
+	p += BIT8SZ;
+	q->vers = GBIT32(p);
+	p += BIT32SZ;
+	q->path = GBIT64(p);
+	p += BIT64SZ;
+	return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ *  1. message size field is incorrect
+ *  2. input buffer too short for its own data (counts too long, etc.)
+ *  3. too many names or qids
+ * gqid() and gstring() return nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+	uchar *p, *ep;
+	uint i, size;
+
+	p = ap;
+	ep = p + nap;
+
+	if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+		return 0;
+	size = GBIT32(p);
+	p += BIT32SZ;
+
+	if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+		return 0;
+
+	f->type = GBIT8(p);
+	p += BIT8SZ;
+	f->tag = GBIT16(p);
+	p += BIT16SZ;
+
+	switch(f->type)
+	{
+	default:
+		return 0;
+
+	case Tversion:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->msize = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->version);
+		break;
+
+	case Tflush:
+		if(p+BIT16SZ > ep)
+			return 0;
+		f->oldtag = GBIT16(p);
+		p += BIT16SZ;
+		break;
+
+	case Tauth:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->afid = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->uname);
+		if(p == nil)
+			break;
+		p = gstring(p, ep, &f->aname);
+		if(p == nil)
+			break;
+		break;
+
+	case Tattach:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->afid = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->uname);
+		if(p == nil)
+			break;
+		p = gstring(p, ep, &f->aname);
+		if(p == nil)
+			break;
+		break;
+
+	case Twalk:
+		if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->newfid = GBIT32(p);
+		p += BIT32SZ;
+		f->nwname = GBIT16(p);
+		p += BIT16SZ;
+		if(f->nwname > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwname; i++){
+			p = gstring(p, ep, &f->wname[i]);
+			if(p == nil)
+				break;
+		}
+		break;
+
+	case Topen:
+		if(p+BIT32SZ+BIT8SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->mode = GBIT8(p);
+		p += BIT8SZ;
+		break;
+
+	case Tcreate:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->name);
+		if(p == nil)
+			break;
+		if(p+BIT32SZ+BIT8SZ > ep)
+			return 0;
+		f->perm = GBIT32(p);
+		p += BIT32SZ;
+		f->mode = GBIT8(p);
+		p += BIT8SZ;
+		break;
+
+	case Tread:
+		if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->offset = GBIT64(p);
+		p += BIT64SZ;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Twrite:
+		if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->offset = GBIT64(p);
+		p += BIT64SZ;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		if(p+f->count > ep)
+			return 0;
+		f->data = (char*)p;
+		p += f->count;
+		break;
+
+	case Tclunk:
+	case Tremove:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Tstat:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Twstat:
+		if(p+BIT32SZ+BIT16SZ > ep)
+			return 0;
+		f->fid = GBIT32(p);
+		p += BIT32SZ;
+		f->nstat = GBIT16(p);
+		p += BIT16SZ;
+		if(p+f->nstat > ep)
+			return 0;
+		f->stat = p;
+		p += f->nstat;
+		break;
+
+/*
+ */
+	case Rversion:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->msize = GBIT32(p);
+		p += BIT32SZ;
+		p = gstring(p, ep, &f->version);
+		break;
+
+	case Rerror:
+		p = gstring(p, ep, &f->ename);
+		break;
+
+	case Rflush:
+		break;
+
+	case Rauth:
+		p = gqid(p, ep, &f->aqid);
+		if(p == nil)
+			break;
+		break;
+
+	case Rattach:
+		p = gqid(p, ep, &f->qid);
+		if(p == nil)
+			break;
+		break;
+
+	case Rwalk:
+		if(p+BIT16SZ > ep)
+			return 0;
+		f->nwqid = GBIT16(p);
+		p += BIT16SZ;
+		if(f->nwqid > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwqid; i++){
+			p = gqid(p, ep, &f->wqid[i]);
+			if(p == nil)
+				break;
+		}
+		break;
+
+	case Ropen:
+	case Rcreate:
+		p = gqid(p, ep, &f->qid);
+		if(p == nil)
+			break;
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->iounit = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Rread:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		if(p+f->count > ep)
+			return 0;
+		f->data = (char*)p;
+		p += f->count;
+		break;
+
+	case Rwrite:
+		if(p+BIT32SZ > ep)
+			return 0;
+		f->count = GBIT32(p);
+		p += BIT32SZ;
+		break;
+
+	case Rclunk:
+	case Rremove:
+		break;
+
+	case Rstat:
+		if(p+BIT16SZ > ep)
+			return 0;
+		f->nstat = GBIT16(p);
+		p += BIT16SZ;
+		if(p+f->nstat > ep)
+			return 0;
+		f->stat = p;
+		p += f->nstat;
+		break;
+
+	case Rwstat:
+		break;
+	}
+
+	if(p==nil || p>ep)
+		return 0;
+	if(ap+size == p)
+		return size;
+	return 0;
+}
--- /dev/null
+++ b/libc/convS2M.c
@@ -1,0 +1,386 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<fcall.h>
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+	uint n;
+
+	if(s == nil){
+		PBIT16(p, 0);
+		p += BIT16SZ;
+		return p;
+	}
+
+	n = strlen(s);
+	PBIT16(p, n);
+	p += BIT16SZ;
+	memmove(p, s, n);
+	p += n;
+	return p;
+}
+
+static
+uchar*
+pqid(uchar *p, Qid *q)
+{
+	PBIT8(p, q->type);
+	p += BIT8SZ;
+	PBIT32(p, q->vers);
+	p += BIT32SZ;
+	PBIT64(p, q->path);
+	p += BIT64SZ;
+	return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+	if(s == nil)
+		return BIT16SZ;
+
+	return BIT16SZ+strlen(s);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+	uint n;
+	int i;
+
+	n = 0;
+	n += BIT32SZ;	/* size */
+	n += BIT8SZ;	/* type */
+	n += BIT16SZ;	/* tag */
+
+	switch(f->type)
+	{
+	default:
+		return 0;
+
+	case Tversion:
+		n += BIT32SZ;
+		n += stringsz(f->version);
+		break;
+
+	case Tflush:
+		n += BIT16SZ;
+		break;
+
+	case Tauth:
+		n += BIT32SZ;
+		n += stringsz(f->uname);
+		n += stringsz(f->aname);
+		break;
+
+	case Tattach:
+		n += BIT32SZ;
+		n += BIT32SZ;
+		n += stringsz(f->uname);
+		n += stringsz(f->aname);
+		break;
+
+	case Twalk:
+		n += BIT32SZ;
+		n += BIT32SZ;
+		n += BIT16SZ;
+		for(i=0; i<f->nwname; i++)
+			n += stringsz(f->wname[i]);
+		break;
+
+	case Topen:
+		n += BIT32SZ;
+		n += BIT8SZ;
+		break;
+
+	case Tcreate:
+		n += BIT32SZ;
+		n += stringsz(f->name);
+		n += BIT32SZ;
+		n += BIT8SZ;
+		break;
+
+	case Tread:
+		n += BIT32SZ;
+		n += BIT64SZ;
+		n += BIT32SZ;
+		break;
+
+	case Twrite:
+		n += BIT32SZ;
+		n += BIT64SZ;
+		n += BIT32SZ;
+		n += f->count;
+		break;
+
+	case Tclunk:
+	case Tremove:
+		n += BIT32SZ;
+		break;
+
+	case Tstat:
+		n += BIT32SZ;
+		break;
+
+	case Twstat:
+		n += BIT32SZ;
+		n += BIT16SZ;
+		n += f->nstat;
+		break;
+/*
+ */
+
+	case Rversion:
+		n += BIT32SZ;
+		n += stringsz(f->version);
+		break;
+
+	case Rerror:
+		n += stringsz(f->ename);
+		break;
+
+	case Rflush:
+		break;
+
+	case Rauth:
+		n += QIDSZ;
+		break;
+
+	case Rattach:
+		n += QIDSZ;
+		break;
+
+	case Rwalk:
+		n += BIT16SZ;
+		n += f->nwqid*QIDSZ;
+		break;
+
+	case Ropen:
+	case Rcreate:
+		n += QIDSZ;
+		n += BIT32SZ;
+		break;
+
+	case Rread:
+		n += BIT32SZ;
+		n += f->count;
+		break;
+
+	case Rwrite:
+		n += BIT32SZ;
+		break;
+
+	case Rclunk:
+		break;
+
+	case Rremove:
+		break;
+
+	case Rstat:
+		n += BIT16SZ;
+		n += f->nstat;
+		break;
+
+	case Rwstat:
+		break;
+	}
+	return n;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+	uchar *p;
+	uint i, size;
+
+	size = sizeS2M(f);
+	if(size == 0)
+		return 0;
+	if(size > nap)
+		return 0;
+
+	p = (uchar*)ap;
+
+	PBIT32(p, size);
+	p += BIT32SZ;
+	PBIT8(p, f->type);
+	p += BIT8SZ;
+	PBIT16(p, f->tag);
+	p += BIT16SZ;
+
+	switch(f->type)
+	{
+	default:
+		return 0;
+
+	case Tversion:
+		PBIT32(p, f->msize);
+		p += BIT32SZ;
+		p = pstring(p, f->version);
+		break;
+
+	case Tflush:
+		PBIT16(p, f->oldtag);
+		p += BIT16SZ;
+		break;
+
+	case Tauth:
+		PBIT32(p, f->afid);
+		p += BIT32SZ;
+		p  = pstring(p, f->uname);
+		p  = pstring(p, f->aname);
+		break;
+
+	case Tattach:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT32(p, f->afid);
+		p += BIT32SZ;
+		p  = pstring(p, f->uname);
+		p  = pstring(p, f->aname);
+		break;
+
+	case Twalk:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT32(p, f->newfid);
+		p += BIT32SZ;
+		PBIT16(p, f->nwname);
+		p += BIT16SZ;
+		if(f->nwname > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwname; i++)
+			p = pstring(p, f->wname[i]);
+		break;
+
+	case Topen:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT8(p, f->mode);
+		p += BIT8SZ;
+		break;
+
+	case Tcreate:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		p = pstring(p, f->name);
+		PBIT32(p, f->perm);
+		p += BIT32SZ;
+		PBIT8(p, f->mode);
+		p += BIT8SZ;
+		break;
+
+	case Tread:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT64(p, f->offset);
+		p += BIT64SZ;
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		break;
+
+	case Twrite:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT64(p, f->offset);
+		p += BIT64SZ;
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		memmove(p, f->data, f->count);
+		p += f->count;
+		break;
+
+	case Tclunk:
+	case Tremove:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		break;
+
+	case Tstat:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		break;
+
+	case Twstat:
+		PBIT32(p, f->fid);
+		p += BIT32SZ;
+		PBIT16(p, f->nstat);
+		p += BIT16SZ;
+		memmove(p, f->stat, f->nstat);
+		p += f->nstat;
+		break;
+/*
+ */
+
+	case Rversion:
+		PBIT32(p, f->msize);
+		p += BIT32SZ;
+		p = pstring(p, f->version);
+		break;
+
+	case Rerror:
+		p = pstring(p, f->ename);
+		break;
+
+	case Rflush:
+		break;
+
+	case Rauth:
+		p = pqid(p, &f->aqid);
+		break;
+
+	case Rattach:
+		p = pqid(p, &f->qid);
+		break;
+
+	case Rwalk:
+		PBIT16(p, f->nwqid);
+		p += BIT16SZ;
+		if(f->nwqid > MAXWELEM)
+			return 0;
+		for(i=0; i<f->nwqid; i++)
+			p = pqid(p, &f->wqid[i]);
+		break;
+
+	case Ropen:
+	case Rcreate:
+		p = pqid(p, &f->qid);
+		PBIT32(p, f->iounit);
+		p += BIT32SZ;
+		break;
+
+	case Rread:
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		memmove(p, f->data, f->count);
+		p += f->count;
+		break;
+
+	case Rwrite:
+		PBIT32(p, f->count);
+		p += BIT32SZ;
+		break;
+
+	case Rclunk:
+		break;
+
+	case Rremove:
+		break;
+
+	case Rstat:
+		PBIT16(p, f->nstat);
+		p += BIT16SZ;
+		memmove(p, f->stat, f->nstat);
+		p += f->nstat;
+		break;
+
+	case Rwstat:
+		break;
+	}
+	if(size != p-ap)
+		return 0;
+	return size;
+}
--- /dev/null
+++ b/libc/crypt.c
@@ -1,0 +1,67 @@
+/*
+ *	Data Encryption Standard
+ *	D.P.Mitchell  83/06/08.
+ *
+ *	block_cipher(key, block, decrypting)
+ *
+ *	these routines use the non-standard 7 byte format
+ *	for DES keys.
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+/*
+ * destructively encrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+int
+encrypt(void *key, void *vbuf, int n)
+{
+	ulong ekey[32];
+	uchar *buf;
+	int i, r;
+
+	if(n < 8)
+		return 0;
+	key_setup(key, ekey);
+	buf = vbuf;
+	n--;
+	r = n % 7;
+	n /= 7;
+	for(i = 0; i < n; i++){
+		block_cipher(ekey, buf, 0);
+		buf += 7;
+	}
+	if(r)
+		block_cipher(ekey, buf - 7 + r, 0);
+	return 1;
+}
+
+/*
+ * destructively decrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+int
+decrypt(void *key, void *vbuf, int n)
+{
+	ulong ekey[128];
+	uchar *buf;
+	int i, r;
+
+	if(n < 8)
+		return 0;
+	key_setup(key, ekey);
+	buf = vbuf;
+	n--;
+	r = n % 7;
+	n /= 7;
+	buf += n * 7;
+	if(r)
+		block_cipher(ekey, buf - 7 + r, 1);
+	for(i = 0; i < n; i++){
+		buf -= 7;
+		block_cipher(ekey, buf, 1);
+	}
+	return 1;
+}
--- /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.
+ */
+
+#define dysize _dysize	/* conflicts on unix */
+
+static int
+dysize(int y)
+{
+
+	if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
+		return 366;
+	return 365;
+}
+
+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;
+}
+
--- /dev/null
+++ b/libc/dirfstat.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+enum
+{
+	DIRSIZE	= STATFIXLEN + 16 * 4		/* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirfstat(int fd)
+{
+	Dir *d;
+	uchar *buf;
+	int n, nd, i;
+
+	nd = DIRSIZE;
+	for(i=0; i<2; i++){	/* should work by the second try */
+		d = malloc(sizeof(Dir) + BIT16SZ + nd);
+		if(d == nil)
+			return nil;
+		buf = (uchar*)&d[1];
+		n = fstat(fd, buf, BIT16SZ+nd);
+		if(n < BIT16SZ){
+			free(d);
+			return nil;
+		}
+		nd = GBIT16(buf);	/* upper bound on size of Dir + strings */
+		if(nd <= n){
+			convM2D(buf, n, d, (char*)&d[1]);
+			return d;
+		}
+		/* else sizeof(Dir)+BIT16SZ+nd is plenty */
+		free(d);
+	}
+	return nil;
+}
--- /dev/null
+++ b/libc/dirfwstat.c
@@ -1,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+dirfwstat(int fd, Dir *d)
+{
+	uchar *buf;
+	int r;
+
+	r = sizeD2M(d);
+	buf = malloc(r);
+	if(buf == nil)
+		return -1;
+	convD2M(d, buf, r);
+	r = fwstat(fd, buf, r);
+	free(buf);
+	return r;
+}
--- /dev/null
+++ b/libc/dirmodefmt.c
@@ -1,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static char *modes[] =
+{
+	"---",
+	"--x",
+	"-w-",
+	"-wx",
+	"r--",
+	"r-x",
+	"rw-",
+	"rwx",
+};
+
+static void
+rwx(long m, char *s)
+{
+	strncpy(s, modes[m], 3);
+}
+
+int
+dirmodefmt(Fmt *f)
+{
+	static char buf[16];
+	ulong m;
+
+	m = va_arg(f->args, ulong);
+
+	if(m & DMDIR)
+		buf[0]='d';
+	else if(m & DMAPPEND)
+		buf[0]='a';
+	else if(m & DMAUTH)
+		buf[0]='A';
+	else
+		buf[0]='-';
+	if(m & DMEXCL)
+		buf[1]='l';
+	else
+		buf[1]='-';
+	rwx((m>>6)&7, buf+2);
+	rwx((m>>3)&7, buf+5);
+	rwx((m>>0)&7, buf+8);
+	buf[11] = 0;
+	return fmtstrcpy(f, buf);
+}
--- /dev/null
+++ b/libc/dirstat.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+enum
+{
+	DIRSIZE	= STATFIXLEN + 16 * 4		/* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirstat(char *name)
+{
+	Dir *d;
+	uchar *buf;
+	int n, nd, i;
+
+	nd = DIRSIZE;
+	for(i=0; i<2; i++){	/* should work by the second try */
+		d = malloc(sizeof(Dir) + BIT16SZ + nd);
+		if(d == nil)
+			return nil;
+		buf = (uchar*)&d[1];
+		n = stat(name, buf, BIT16SZ+nd);
+		if(n < BIT16SZ){
+			free(d);
+			return nil;
+		}
+		nd = GBIT16((uchar*)buf);	/* upper bound on size of Dir + strings */
+		if(nd <= n){
+			convM2D(buf, n, d, (char*)&d[1]);
+			return d;
+		}
+		/* else sizeof(Dir)+BIT16SZ+nd is plenty */
+		free(d);
+	}
+	return nil;
+}
--- /dev/null
+++ b/libc/dirwstat.c
@@ -1,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+dirwstat(char *name, Dir *d)
+{
+	uchar *buf;
+	int r;
+
+	r = sizeD2M(d);
+	buf = malloc(r);
+	if(buf == nil)
+		return -1;
+	convD2M(d, buf, r);
+	r = wstat(name, buf, r);
+	free(buf);
+	return r;
+}
--- /dev/null
+++ b/libc/dofmt.c
@@ -1,0 +1,539 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted  */
+int
+dofmt(Fmt *f, char *fmt)
+{
+	Rune rune, *rt, *rs;
+	int r;
+	char *t, *s;
+	int n, nfmt;
+
+	nfmt = f->nfmt;
+	for(;;){
+		if(f->runes){
+			rt = (Rune*)f->to;
+			rs = (Rune*)f->stop;
+			while((r = *(uchar*)fmt) && r != '%'){
+				if(r < Runeself)
+					fmt++;
+				else{
+					fmt += chartorune(&rune, fmt);
+					r = rune;
+				}
+				FMTRCHAR(f, rt, rs, r);
+			}
+			fmt++;
+			f->nfmt += rt - (Rune *)f->to;
+			f->to = rt;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = rs;
+		}else{
+			t = (char*)f->to;
+			s = (char*)f->stop;
+			while((r = *(uchar*)fmt) && r != '%'){
+				if(r < Runeself){
+					FMTCHAR(f, t, s, r);
+					fmt++;
+				}else{
+					n = chartorune(&rune, fmt);
+					if(t + n > s){
+						t = (char*)__fmtflush(f, t, n);
+						if(t != nil)
+							s = (char*)f->stop;
+						else
+							return -1;
+					}
+					while(n--)
+						*t++ = *fmt++;
+				}
+			}
+			fmt++;
+			f->nfmt += t - (char *)f->to;
+			f->to = t;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = s;
+		}
+
+		fmt = (char*)__fmtdispatch(f, fmt, 0);
+		if(fmt == nil)
+			return -1;
+	}
+}
+
+void *
+__fmtflush(Fmt *f, void *t, int len)
+{
+	if(f->runes)
+		f->nfmt += (Rune*)t - (Rune*)f->to;
+	else
+		f->nfmt += (char*)t - (char *)f->to;
+	f->to = t;
+	if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
+		f->stop = f->to;
+		return nil;
+	}
+	return f->to;
+}
+
+/*
+ * put a formatted block of memory sz bytes long of n runes into the output buffer,
+ * left/right justified in a field of at least f->width charactes
+ */
+int
+__fmtpad(Fmt *f, int n)
+{
+	char *t, *s;
+	int i;
+
+	t = (char*)f->to;
+	s = (char*)f->stop;
+	for(i = 0; i < n; i++)
+		FMTCHAR(f, t, s, ' ');
+	f->nfmt += t - (char *)f->to;
+	f->to = t;
+	return 0;
+}
+
+int
+__rfmtpad(Fmt *f, int n)
+{
+	Rune *t, *s;
+	int i;
+
+	t = (Rune*)f->to;
+	s = (Rune*)f->stop;
+	for(i = 0; i < n; i++)
+		FMTRCHAR(f, t, s, ' ');
+	f->nfmt += t - (Rune *)f->to;
+	f->to = t;
+	return 0;
+}
+
+int
+__fmtcpy(Fmt *f, const void *vm, int n, int sz)
+{
+	Rune *rt, *rs, r;
+	char *t, *s, *m, *me;
+	ulong fl;
+	int nc, w;
+
+	m = (char*)vm;
+	me = m + sz;
+	w = f->width;
+	fl = f->flags;
+	if((fl & FmtPrec) && n > f->prec)
+		n = f->prec;
+	if(f->runes){
+		if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+			return -1;
+		rt = (Rune*)f->to;
+		rs = (Rune*)f->stop;
+		for(nc = n; nc > 0; nc--){
+			r = *(uchar*)m;
+			if(r < Runeself)
+				m++;
+			else if((me - m) >= UTFmax || fullrune(m, me-m))
+				m += chartorune(&r, m);
+			else
+				break;
+			FMTRCHAR(f, rt, rs, r);
+		}
+		f->nfmt += rt - (Rune *)f->to;
+		f->to = rt;
+		if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+			return -1;
+	}else{
+		if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+			return -1;
+		t = (char*)f->to;
+		s = (char*)f->stop;
+		for(nc = n; nc > 0; nc--){
+			r = *(uchar*)m;
+			if(r < Runeself)
+				m++;
+			else if((me - m) >= UTFmax || fullrune(m, me-m))
+				m += chartorune(&r, m);
+			else
+				break;
+			FMTRUNE(f, t, s, r);
+		}
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+		if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+int
+__fmtrcpy(Fmt *f, const void *vm, int n)
+{
+	Rune r, *m, *me, *rt, *rs;
+	char *t, *s;
+	ulong fl;
+	int w;
+
+	m = (Rune*)vm;
+	w = f->width;
+	fl = f->flags;
+	if((fl & FmtPrec) && n > f->prec)
+		n = f->prec;
+	if(f->runes){
+		if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+			return -1;
+		rt = (Rune*)f->to;
+		rs = (Rune*)f->stop;
+		for(me = m + n; m < me; m++)
+			FMTRCHAR(f, rt, rs, *m);
+		f->nfmt += rt - (Rune *)f->to;
+		f->to = rt;
+		if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+			return -1;
+	}else{
+		if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+			return -1;
+		t = (char*)f->to;
+		s = (char*)f->stop;
+		for(me = m + n; m < me; m++){
+			r = *m;
+			FMTRUNE(f, t, s, r);
+		}
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+		if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/* fmt out one character */
+int
+__charfmt(Fmt *f)
+{
+	char x[1];
+
+	x[0] = va_arg(f->args, int);
+	f->prec = 1;
+	return __fmtcpy(f, (const char*)x, 1, 1);
+}
+
+/* fmt out one rune */
+int
+__runefmt(Fmt *f)
+{
+	Rune x[1];
+
+	x[0] = va_arg(f->args, int);
+	return __fmtrcpy(f, (const void*)x, 1);
+}
+
+/* public helper routine: fmt out a null terminated string already in hand */
+int
+fmtstrcpy(Fmt *f, char *s)
+{
+	int i, j;
+	Rune r;
+
+	if(!s)
+		return __fmtcpy(f, "<nil>", 5, 5);
+	/* if precision is specified, make sure we don't wander off the end */
+	if(f->flags & FmtPrec){
+		i = 0;
+		for(j=0; j<f->prec && s[i]; j++)
+			i += chartorune(&r, s+i);
+		return __fmtcpy(f, s, j, i);
+	}
+	return __fmtcpy(f, s, utflen(s), strlen(s));
+}
+
+/* fmt out a null terminated utf string */
+int
+__strfmt(Fmt *f)
+{
+	char *s;
+
+	s = va_arg(f->args, char *);
+	return fmtstrcpy(f, s);
+}
+
+/* public helper routine: fmt out a null terminated rune string already in hand */
+int
+fmtrunestrcpy(Fmt *f, Rune *s)
+{
+	Rune *e;
+	int n, p;
+
+	if(!s)
+		return __fmtcpy(f, "<nil>", 5, 5);
+	/* if precision is specified, make sure we don't wander off the end */
+	if(f->flags & FmtPrec){
+		p = f->prec;
+		for(n = 0; n < p; n++)
+			if(s[n] == 0)
+				break;
+	}else{
+		for(e = s; *e; e++)
+			;
+		n = e - s;
+	}
+	return __fmtrcpy(f, s, n);
+}
+
+/* fmt out a null terminated rune string */
+int
+__runesfmt(Fmt *f)
+{
+	Rune *s;
+
+	s = va_arg(f->args, Rune *);
+	return fmtrunestrcpy(f, s);
+}
+
+/* fmt a % */
+int
+__percentfmt(Fmt *f)
+{
+	Rune x[1];
+
+	x[0] = f->r;
+	f->prec = 1;
+	return __fmtrcpy(f, (const void*)x, 1);
+}
+
+/* fmt an integer */
+int
+__ifmt(Fmt *f)
+{
+	char buf[70], *p, *conv;
+	uvlong vu;
+	ulong u;
+	int neg, base, i, n, fl, w, isv;
+
+	neg = 0;
+	fl = f->flags;
+	isv = 0;
+	vu = 0;
+	u = 0;
+	/*
+	 * Unsigned verbs for ANSI C
+	 */
+	switch(f->r){
+	case 'x':
+	case 'X':
+	case 'o':
+	case 'u':
+	case 'p':
+		fl |= FmtUnsigned;
+		fl &= ~(FmtSign|FmtSpace);
+		break;
+	}
+	if(f->r == 'p'){
+		if(sizeof(void*) == sizeof(uvlong)){
+			isv = 1;
+			vu = (uvlong)va_arg(f->args, uvlong);
+		}else
+			u = (ulong)va_arg(f->args, ulong);
+		f->r = 'x';
+		fl |= FmtUnsigned;
+	}else if(fl & FmtVLong){
+		isv = 1;
+		if(fl & FmtUnsigned)
+			vu = va_arg(f->args, uvlong);
+		else
+			vu = va_arg(f->args, vlong);
+	}else if(fl & FmtLong){
+		if(fl & FmtUnsigned)
+			u = va_arg(f->args, ulong);
+		else
+			u = va_arg(f->args, long);
+	}else if(fl & FmtByte){
+		if(fl & FmtUnsigned)
+			u = (uchar)va_arg(f->args, int);
+		else
+			u = (char)va_arg(f->args, int);
+	}else if(fl & FmtShort){
+		if(fl & FmtUnsigned)
+			u = (ushort)va_arg(f->args, int);
+		else
+			u = (short)va_arg(f->args, int);
+	}else{
+		if(fl & FmtUnsigned)
+			u = va_arg(f->args, uint);
+		else
+			u = va_arg(f->args, int);
+	}
+	conv = "0123456789abcdef";
+	switch(f->r){
+	case 'd':
+	case 'i':
+	case 'u':
+		base = 10;
+		break;
+	case 'x':
+		base = 16;
+		break;
+	case 'X':
+		base = 16;
+		conv = "0123456789ABCDEF";
+		break;
+	case 'b':
+		base = 2;
+		break;
+	case 'o':
+		base = 8;
+		break;
+	default:
+		return -1;
+	}
+	if(!(fl & FmtUnsigned)){
+		if(isv && (vlong)vu < 0){
+			vu = -(vlong)vu;
+			neg = 1;
+		}else if(!isv && (long)u < 0){
+			u = -(long)u;
+			neg = 1;
+		}
+	}
+	p = buf + sizeof buf - 1;
+	n = 0;
+	if(isv){
+		while(vu){
+			i = vu % base;
+			vu /= base;
+			if((fl & FmtComma) && n % 4 == 3){
+				*p-- = ',';
+				n++;
+			}
+			*p-- = conv[i];
+			n++;
+		}
+	}else{
+		while(u){
+			i = u % base;
+			u /= base;
+			if((fl & FmtComma) && n % 4 == 3){
+				*p-- = ',';
+				n++;
+			}
+			*p-- = conv[i];
+			n++;
+		}
+	}
+	if(n == 0){
+		*p-- = '0';
+		n = 1;
+	}
+	for(w = f->prec; n < w && p > buf+3; n++)
+		*p-- = '0';
+	if(neg || (fl & (FmtSign|FmtSpace)))
+		n++;
+	if(fl & FmtSharp){
+		if(base == 16)
+			n += 2;
+		else if(base == 8){
+			if(p[1] == '0')
+				fl &= ~FmtSharp;
+			else
+				n++;
+		}
+	}
+	if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
+		for(w = f->width; n < w && p > buf+3; n++)
+			*p-- = '0';
+		f->width = 0;
+	}
+	if(fl & FmtSharp){
+		if(base == 16)
+			*p-- = f->r;
+		if(base == 16 || base == 8)
+			*p-- = '0';
+	}
+	if(neg)
+		*p-- = '-';
+	else if(fl & FmtSign)
+		*p-- = '+';
+	else if(fl & FmtSpace)
+		*p-- = ' ';
+	f->flags &= ~FmtPrec;
+	return __fmtcpy(f, p + 1, n, n);
+}
+
+int
+__countfmt(Fmt *f)
+{
+	void *p;
+	ulong fl;
+
+	fl = f->flags;
+	p = va_arg(f->args, void*);
+	if(fl & FmtVLong){
+		*(vlong*)p = f->nfmt;
+	}else if(fl & FmtLong){
+		*(long*)p = f->nfmt;
+	}else if(fl & FmtByte){
+		*(char*)p = f->nfmt;
+	}else if(fl & FmtShort){
+		*(short*)p = f->nfmt;
+	}else{
+		*(int*)p = f->nfmt;
+	}
+	return 0;
+}
+
+int
+__flagfmt(Fmt *f)
+{
+	switch(f->r){
+	case ',':
+		f->flags |= FmtComma;
+		break;
+	case '-':
+		f->flags |= FmtLeft;
+		break;
+	case '+':
+		f->flags |= FmtSign;
+		break;
+	case '#':
+		f->flags |= FmtSharp;
+		break;
+	case ' ':
+		f->flags |= FmtSpace;
+		break;
+	case 'u':
+		f->flags |= FmtUnsigned;
+		break;
+	case 'h':
+		if(f->flags & FmtShort)
+			f->flags |= FmtByte;
+		f->flags |= FmtShort;
+		break;
+	case 'L':
+		f->flags |= FmtLDouble;
+		break;
+	case 'l':
+		if(f->flags & FmtLong)
+			f->flags |= FmtVLong;
+		f->flags |= FmtLong;
+		break;
+	}
+	return 1;
+}
+
+/* default error format */
+int
+__badfmt(Fmt *f)
+{
+	char x[3];
+
+	x[0] = '%';
+	x[1] = f->r;
+	x[2] = '%';
+	f->prec = 3;
+	__fmtcpy(f, (const void*)x, 3, 3);
+	return 0;
+}
--- /dev/null
+++ b/libc/dorfmt.c
@@ -1,0 +1,46 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted  */
+
+int
+dorfmt(Fmt *f, const Rune *fmt)
+{
+	Rune *rt, *rs;
+	int r;
+	char *t, *s;
+	int nfmt;
+
+	nfmt = f->nfmt;
+	for(;;){
+		if(f->runes){
+			rt = f->to;
+			rs = f->stop;
+			while((r = *fmt++) && r != '%'){
+				FMTRCHAR(f, rt, rs, r);
+			}
+			f->nfmt += rt - (Rune *)f->to;
+			f->to = rt;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = rs;
+		}else{
+			t = f->to;
+			s = f->stop;
+			while((r = *fmt++) && r != '%'){
+				FMTRUNE(f, t, f->stop, r);
+			}
+			f->nfmt += t - (char *)f->to;
+			f->to = t;
+			if(!r)
+				return f->nfmt - nfmt;
+			f->stop = s;
+		}
+
+		fmt = __fmtdispatch(f, (Rune*)fmt, 1);
+		if(fmt == nil)
+			return -1;
+	}
+	return 0;		/* not reached */
+}
--- /dev/null
+++ b/libc/encodefmt.c
@@ -1,0 +1,78 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+int
+encodefmt(Fmt *f)
+{
+	char *out;
+	char *buf;
+	int len;
+	int ilen;
+	int rv;
+	uchar *b;
+	char *p;
+	char obuf[64];	// rsc optimization
+
+	if(!(f->flags&FmtPrec) || f->prec < 1)
+		goto error;
+
+	b = va_arg(f->args, uchar*);
+	if(b == 0)
+		return fmtstrcpy(f, "<nil>");
+
+	ilen = f->prec;
+	f->prec = 0;
+	f->flags &= ~FmtPrec;
+	switch(f->r){
+	case '<':
+		len = (8*ilen+4)/5 + 3;
+		break;
+	case '[':
+		len = (8*ilen+5)/6 + 4;
+		break;
+	case 'H':
+		len = 2*ilen + 1;
+		break;
+	default:
+		goto error;
+	}
+
+	if(len > sizeof(obuf)){
+		buf = malloc(len);
+		if(buf == nil)
+			goto error;
+	} else
+		buf = obuf;
+
+	// convert
+	out = buf;
+	switch(f->r){
+	case '[':
+		rv = enc64(out, len, b, ilen);
+		break;
+	case '<':
+		rv = enc32(out, len, b, ilen);
+		break;
+	case 'H':
+		rv = enc16(out, len, b, ilen);
+		break;
+	default:
+		rv = -1;
+		break;
+	}
+	if(rv < 0)
+		goto error;
+
+	if((f->flags & FmtLong) != 0 && f->r != '[')
+		for(p = buf; *p; p++)
+			*p = tolower(*p);
+
+	fmtstrcpy(f, buf);
+	if(buf != obuf)
+		free(buf);
+	return 0;
+
+error:
+	return fmtstrcpy(f, "<encodefmt>");
+}
--- /dev/null
+++ b/libc/fcallfmt.c
@@ -1,0 +1,234 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static uint dumpsome(char*, char*, char*, long);
+static void fdirconv(char*, char*, Dir*);
+static char *qidtype(char*, uchar);
+
+#define	QIDFMT	"(%.16llux %lud %s)"
+
+int
+fcallfmt(Fmt *fmt)
+{
+	Fcall *f;
+	int fid, type, tag, i;
+	char buf[512], tmp[200];
+	char *p, *e;
+	Dir *d;
+	Qid *q;
+
+	e = buf+sizeof(buf);
+	f = va_arg(fmt->args, Fcall*);
+	type = f->type;
+	fid = f->fid;
+	tag = f->tag;
+	switch(type){
+	case Tversion:	/* 100 */
+		seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+		break;
+	case Rversion:
+		seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+		break;
+	case Tauth:	/* 102 */
+		seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag,
+			f->afid, f->uname, f->aname);
+		break;
+	case Rauth:
+		seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag,
+			f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
+		break;
+	case Tattach:	/* 104 */
+		seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+			fid, f->afid, f->uname, f->aname);
+		break;
+	case Rattach:
+		seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag,
+			f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
+		break;
+	case Rerror:	/* 107; 106 (Terror) illegal */
+		seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename);
+		break;
+	case Tflush:	/* 108 */
+		seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+		break;
+	case Rflush:
+		seprint(buf, e, "Rflush tag %ud", tag);
+		break;
+	case Twalk:	/* 110 */
+		p = seprint(buf, e, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname);
+		if(f->nwname <= MAXWELEM)
+			for(i=0; i<f->nwname; i++)
+				p = seprint(p, e, "%d:%s ", i, f->wname[i]);
+		break;
+	case Rwalk:
+		p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+		if(f->nwqid <= MAXWELEM)
+			for(i=0; i<f->nwqid; i++){
+				q = &f->wqid[i];
+				p = seprint(p, e, "%d:" QIDFMT " ", i,
+					q->path, q->vers, qidtype(tmp, q->type));
+			}
+		break;
+	case Topen:	/* 112 */
+		seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
+		break;
+	case Ropen:
+		seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
+			f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+		break;
+	case Tcreate:	/* 114 */
+		seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
+		break;
+	case Rcreate:
+		seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag,
+			f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+		break;
+	case Tread:	/* 116 */
+		seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud",
+			tag, fid, f->offset, f->count);
+		break;
+	case Rread:
+		p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count);
+			dumpsome(p, e, f->data, f->count);
+		break;
+	case Twrite:	/* 118 */
+		p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ",
+			tag, fid, f->offset, f->count);
+		dumpsome(p, e, f->data, f->count);
+		break;
+	case Rwrite:
+		seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count);
+		break;
+	case Tclunk:	/* 120 */
+		seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid);
+		break;
+	case Rclunk:
+		seprint(buf, e, "Rclunk tag %ud", tag);
+		break;
+	case Tremove:	/* 122 */
+		seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid);
+		break;
+	case Rremove:
+		seprint(buf, e, "Rremove tag %ud", tag);
+		break;
+	case Tstat:	/* 124 */
+		seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid);
+		break;
+	case Rstat:
+		p = seprint(buf, e, "Rstat tag %ud ", tag);
+		if(f->nstat > sizeof tmp)
+			seprint(p, e, " stat(%d bytes)", f->nstat);
+		else{
+			d = (Dir*)tmp;
+			convM2D(f->stat, f->nstat, d, (char*)(d+1));
+			seprint(p, e, " stat ");
+			fdirconv(p+6, e, d);
+		}
+		break;
+	case Twstat:	/* 126 */
+		p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid);
+		if(f->nstat > sizeof tmp)
+			seprint(p, e, " stat(%d bytes)", f->nstat);
+		else{
+			d = (Dir*)tmp;
+			convM2D(f->stat, f->nstat, d, (char*)(d+1));
+			seprint(p, e, " stat ");
+			fdirconv(p+6, e, d);
+		}
+		break;
+	case Rwstat:
+		seprint(buf, e, "Rwstat tag %ud", tag);
+		break;
+	default:
+		seprint(buf, e,  "unknown type %d", type);
+	}
+	return fmtstrcpy(fmt, buf);
+}
+
+static char*
+qidtype(char *s, uchar t)
+{
+	char *p;
+
+	p = s;
+	if(t & QTDIR)
+		*p++ = 'd';
+	if(t & QTAPPEND)
+		*p++ = 'a';
+	if(t & QTEXCL)
+		*p++ = 'l';
+	if(t & QTAUTH)
+		*p++ = 'A';
+	*p = '\0';
+	return s;
+}
+
+int
+dirfmt(Fmt *fmt)
+{
+	char buf[160];
+
+	fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*));
+	return fmtstrcpy(fmt, buf);
+}
+
+static void
+fdirconv(char *buf, char *e, Dir *d)
+{
+	char tmp[16];
+
+	seprint(buf, e, "'%s' '%s' '%s' '%s' "
+		"q " QIDFMT " m %#luo "
+		"at %ld mt %ld l %lld "
+		"t %d d %d",
+			d->name, d->uid, d->gid, d->muid,
+			d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
+			d->atime, d->mtime, d->length,
+			d->type, d->dev);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static uint
+dumpsome(char *ans, char *e, char *buf, long count)
+{
+	int i, printable;
+	char *p;
+
+	if(buf == nil){
+		seprint(ans, e, "<no data>");
+		return strlen(ans);
+	}
+	printable = 1;
+	if(count > DUMPL)
+		count = DUMPL;
+	for(i=0; i<count && printable; i++)
+		if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
+			printable = 0;
+	p = ans;
+	*p++ = '\'';
+	if(printable){
+		if(count > e-p-2)
+			count = e-p-2;
+		memmove(p, buf, count);
+		p += count;
+	}else{
+		if(2*count > e-p-2)
+			count = (e-p-2)/2;
+		for(i=0; i<count; i++){
+			if(i>0 && i%4==0)
+				*p++ = ' ';
+			sprint(p, "%2.2ux", buf[i]);
+			p += 2;
+		}
+	}
+	*p++ = '\'';
+	*p = 0;
+	return p - ans;
+}
--- /dev/null
+++ b/libc/fltfmt.c
@@ -1,0 +1,375 @@
+#include <u.h>
+#include <libc.h>
+#include <float.h>
+#include <ctype.h>
+#include "fmtdef.h"
+
+enum
+{
+	FDIGIT	= 30,
+	FDEFLT	= 6,
+	NSIGNIF	= 17
+};
+
+/*
+ * first few powers of 10, enough for about 1/2 of the
+ * total space for doubles.
+ */
+static double pows10[] =
+{
+	  1e0,   1e1,   1e2,   1e3,   1e4,   1e5,   1e6,   1e7,   1e8,   1e9,  
+	 1e10,  1e11,  1e12,  1e13,  1e14,  1e15,  1e16,  1e17,  1e18,  1e19,  
+	 1e20,  1e21,  1e22,  1e23,  1e24,  1e25,  1e26,  1e27,  1e28,  1e29,  
+	 1e30,  1e31,  1e32,  1e33,  1e34,  1e35,  1e36,  1e37,  1e38,  1e39,  
+	 1e40,  1e41,  1e42,  1e43,  1e44,  1e45,  1e46,  1e47,  1e48,  1e49,  
+	 1e50,  1e51,  1e52,  1e53,  1e54,  1e55,  1e56,  1e57,  1e58,  1e59,  
+	 1e60,  1e61,  1e62,  1e63,  1e64,  1e65,  1e66,  1e67,  1e68,  1e69,  
+	 1e70,  1e71,  1e72,  1e73,  1e74,  1e75,  1e76,  1e77,  1e78,  1e79,  
+	 1e80,  1e81,  1e82,  1e83,  1e84,  1e85,  1e86,  1e87,  1e88,  1e89,  
+	 1e90,  1e91,  1e92,  1e93,  1e94,  1e95,  1e96,  1e97,  1e98,  1e99,  
+	1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 
+	1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 
+	1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 
+	1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 
+	1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 
+	1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 
+};
+
+#undef pow10
+#define  pow10(x)  fmtpow10(x)
+
+static double
+pow10(int n)
+{
+	double d;
+	int neg;
+
+	neg = 0;
+	if(n < 0){
+		if(n < DBL_MIN_10_EXP){
+			return 0.;
+		}
+		neg = 1;
+		n = -n;
+	}else if(n > DBL_MAX_10_EXP){
+		return HUGE_VAL;
+	}
+	if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
+		d = pows10[n];
+	else{
+		d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
+		for(;;){
+			n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
+			if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
+				d *= pows10[n];
+				break;
+			}
+			d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
+		}
+	}
+	if(neg){
+		return 1./d;
+	}
+	return d;
+}
+
+static int
+xadd(char *a, int n, int v)
+{
+	char *b;
+	int c;
+
+	if(n < 0 || n >= NSIGNIF)
+		return 0;
+	for(b = a+n; b >= a; b--) {
+		c = *b + v;
+		if(c <= '9') {
+			*b = c;
+			return 0;
+		}
+		*b = '0';
+		v = 1;
+	}
+	*a = '1';	/* overflow adding */
+	return 1;
+}
+
+static int
+xsub(char *a, int n, int v)
+{
+	char *b;
+	int c;
+
+	for(b = a+n; b >= a; b--) {
+		c = *b - v;
+		if(c >= '0') {
+			*b = c;
+			return 0;
+		}
+		*b = '9';
+		v = 1;
+	}
+	*a = '9';	/* underflow subtracting */
+	return 1;
+}
+
+static void
+xdtoa(Fmt *fmt, char *s2, double f)
+{
+	char s1[NSIGNIF+10];
+	double g, h;
+	int e, d, i, n;
+	int c1, c2, c3, c4, ucase, sign, chr, prec;
+
+	prec = FDEFLT;
+	if(fmt->flags & FmtPrec)
+		prec = fmt->prec;
+	if(prec > FDIGIT)
+		prec = FDIGIT;
+	if(__isNaN(f)) {
+		strcpy(s2, "NaN");
+		return;
+	}
+	if(__isInf(f, 1)) {
+		strcpy(s2, "+Inf");
+		return;
+	}
+	if(__isInf(f, -1)) {
+		strcpy(s2, "-Inf");
+		return;
+	}
+	sign = 0;
+	if(f < 0) {
+		f = -f;
+		sign++;
+	}
+	ucase = 0;
+	chr = fmt->r;
+	if(isupper(chr)) {
+		ucase = 1;
+		chr = tolower(chr);
+	}
+
+	e = 0;
+	g = f;
+	if(g != 0) {
+		frexp(f, &e);
+		e = e * .301029995664;
+		if(e >= -150 && e <= +150) {
+			d = 0;
+			h = f;
+		} else {
+			d = e/2;
+			h = f * pow10(-d);
+		}
+		g = h * pow10(d-e);
+		while(g < 1) {
+			e--;
+			g = h * pow10(d-e);
+		}
+		while(g >= 10) {
+			e++;
+			g = h * pow10(d-e);
+		}
+	}
+
+	/*
+	 * convert NSIGNIF digits and convert
+	 * back to get accuracy.
+	 */
+	for(i=0; i<NSIGNIF; i++) {
+		d = g;
+		s1[i] = d + '0';
+		g = (g - d) * 10;
+	}
+	s1[i] = 0;
+
+	/*
+	 * try decimal rounding to eliminate 9s
+	 */
+	c2 = prec + 1;
+	if(chr == 'f')
+		c2 += e;
+	if(c2 >= NSIGNIF-2) {
+		strcpy(s2, s1);
+		d = e;
+		s1[NSIGNIF-2] = '0';
+		s1[NSIGNIF-1] = '0';
+		sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+		g = strtod(s1, nil);
+		if(g == f)
+			goto found;
+		if(xadd(s1, NSIGNIF-3, 1)) {
+			e++;
+			sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+		}
+		g = strtod(s1, nil);
+		if(g == f)
+			goto found;
+		strcpy(s1, s2);
+		e = d;
+	}
+
+	/*
+	 * convert back so s1 gets exact answer
+	 */
+	for(;;) {
+		sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+		g = strtod(s1, nil);
+		if(f > g) {
+			if(xadd(s1, NSIGNIF-1, 1))
+				e--;
+			continue;
+		}
+		if(f < g) {
+			if(xsub(s1, NSIGNIF-1, 1))
+				e++;
+			continue;
+		}
+		break;
+	}
+
+found:
+	/*
+	 * sign
+	 */
+	d = 0;
+	i = 0;
+	if(sign)
+		s2[d++] = '-';
+	else if(fmt->flags & FmtSign)
+		s2[d++] = '+';
+	else if(fmt->flags & FmtSpace)
+		s2[d++] = ' ';
+
+	/*
+	 * copy into final place
+	 * c1 digits of leading '0'
+	 * c2 digits from conversion
+	 * c3 digits of trailing '0'
+	 * c4 digits after '.'
+	 */
+	c1 = 0;
+	c2 = prec + 1;
+	c3 = 0;
+	c4 = prec;
+	switch(chr) {
+	default:
+		if(xadd(s1, c2, 5))
+			e++;
+		break;
+	case 'g':
+		/*
+		 * decide on 'e' of 'f' style convers
+		 */
+		if(xadd(s1, c2, 5))
+			e++;
+		if(e >= -5 && e <= prec) {
+			c1 = -e - 1;
+			c4 = prec - e;
+			chr = 'h';	// flag for 'f' style
+		}
+		break;
+	case 'f':
+		if(xadd(s1, c2+e, 5))
+			e++;
+		c1 = -e;
+		if(c1 > prec)
+			c1 = c2;
+		c2 += e;
+		break;
+	}
+
+	/*
+	 * clean up c1 c2 and c3
+	 */
+	if(c1 < 0)
+		c1 = 0;
+	if(c2 < 0)
+		c2 = 0;
+	if(c2 > NSIGNIF) {
+		c3 = c2-NSIGNIF;
+		c2 = NSIGNIF;
+	}
+
+	/*
+	 * copy digits
+	 */
+	while(c1 > 0) {
+		if(c1+c2+c3 == c4)
+			s2[d++] = '.';
+		s2[d++] = '0';
+		c1--;
+	}
+	while(c2 > 0) {
+		if(c2+c3 == c4)
+			s2[d++] = '.';
+		s2[d++] = s1[i++];
+		c2--;
+	}
+	while(c3 > 0) {
+		if(c3 == c4)
+			s2[d++] = '.';
+		s2[d++] = '0';
+		c3--;
+	}
+
+	/*
+	 * strip trailing '0' on g conv
+	 */
+	if(fmt->flags & FmtSharp) {
+		if(0 == c4)
+			s2[d++] = '.';
+	} else
+	if(chr == 'g' || chr == 'h') {
+		for(n=d-1; n>=0; n--)
+			if(s2[n] != '0')
+				break;
+		for(i=n; i>=0; i--)
+			if(s2[i] == '.') {
+				d = n;
+				if(i != n)
+					d++;
+				break;
+			}
+	}
+	if(chr == 'e' || chr == 'g') {
+		if(ucase)
+			s2[d++] = 'E';
+		else
+			s2[d++] = 'e';
+		c1 = e;
+		if(c1 < 0) {
+			s2[d++] = '-';
+			c1 = -c1;
+		} else
+			s2[d++] = '+';
+		if(c1 >= 100) {
+			s2[d++] = c1/100 + '0';
+			c1 = c1%100;
+		}
+		s2[d++] = c1/10 + '0';
+		s2[d++] = c1%10 + '0';
+	}
+	s2[d] = 0;
+}
+
+static int
+floatfmt(Fmt *fmt, double f)
+{
+	char s[341];		/* precision+exponent+sign+'.'+null */
+
+	xdtoa(fmt, s, f);
+	fmt->flags &= FmtWidth|FmtLeft;
+	__fmtcpy(fmt, s, strlen(s), strlen(s));
+	return 0;
+}
+
+int
+__efgfmt(Fmt *f)
+{
+	double d;
+
+	d = va_arg(f->args, double);
+	return floatfmt(f, d);
+}
--- /dev/null
+++ b/libc/fmt.c
@@ -1,0 +1,216 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+enum
+{
+	Maxfmt = 64
+};
+
+typedef struct Convfmt Convfmt;
+struct Convfmt
+{
+	int	c;
+	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
+};
+
+struct
+{
+	/* lock by calling __fmtlock, __fmtunlock */
+	int	nfmt;
+	Convfmt	fmt[Maxfmt];
+} fmtalloc;
+
+static Convfmt knownfmt[] = {
+	' ',	__flagfmt,
+	'#',	__flagfmt,
+	'%',	__percentfmt,
+	'+',	__flagfmt,
+	',',	__flagfmt,
+	'-',	__flagfmt,
+	'C',	__runefmt,	/* Plan 9 addition */
+	'E',	__efgfmt,
+#ifndef PLAN9PORT
+	'F',	__efgfmt,	/* ANSI only */
+#endif
+	'G',	__efgfmt,
+#ifndef PLAN9PORT
+	'L',	__flagfmt,	/* ANSI only */
+#endif
+	'S',	__runesfmt,	/* Plan 9 addition */
+	'X',	__ifmt,
+	'b',	__ifmt,		/* Plan 9 addition */
+	'c',	__charfmt,
+	'd',	__ifmt,
+	'e',	__efgfmt,
+	'f',	__efgfmt,
+	'g',	__efgfmt,
+	'h',	__flagfmt,
+#ifndef PLAN9PORT
+	'i',	__ifmt,		/* ANSI only */
+#endif
+	'l',	__flagfmt,
+	'n',	__countfmt,
+	'o',	__ifmt,
+	'p',	__ifmt,
+//	'r',	__errfmt,
+	's',	__strfmt,
+#ifdef PLAN9PORT
+	'u',	__flagfmt,
+#else
+	'u',	__ifmt,
+#endif
+	'x',	__ifmt,
+	0,	0,
+};
+
+
+int	(*fmtdoquote)(int);
+
+/*
+ * __fmtlock() must be set
+ */
+static int
+__fmtinstall(int c, Fmts f)
+{
+	Convfmt *p, *ep;
+
+	if(c<=0 || c>=65536)
+		return -1;
+	if(!f)
+		f = __badfmt;
+
+	ep = &fmtalloc.fmt[fmtalloc.nfmt];
+	for(p=fmtalloc.fmt; p<ep; p++)
+		if(p->c == c)
+			break;
+
+	if(p == &fmtalloc.fmt[Maxfmt])
+		return -1;
+
+	p->fmt = f;
+	if(p == ep){	/* installing a new format character */
+		fmtalloc.nfmt++;
+		p->c = c;
+	}
+
+	return 0;
+}
+
+int
+fmtinstall(int c, int (*f)(Fmt*))
+{
+	int ret;
+
+	__fmtlock();
+	ret = __fmtinstall(c, f);
+	__fmtunlock();
+	return ret;
+}
+
+static Fmts
+fmtfmt(int c)
+{
+	Convfmt *p, *ep;
+
+	ep = &fmtalloc.fmt[fmtalloc.nfmt];
+	for(p=fmtalloc.fmt; p<ep; p++)
+		if(p->c == c){
+			while(p->fmt == 0)	/* loop until value is updated */
+				;
+			return p->fmt;
+		}
+
+	/* is this a predefined format char? */
+	__fmtlock();
+	for(p=knownfmt; p->c; p++)
+		if(p->c == c){
+			__fmtinstall(p->c, p->fmt);
+			__fmtunlock();
+			return p->fmt;
+		}
+	__fmtunlock();
+
+	return __badfmt;
+}
+
+void*
+__fmtdispatch(Fmt *f, void *fmt, int isrunes)
+{
+	Rune rune, r;
+	int i, n;
+
+	f->flags = 0;
+	f->width = f->prec = 0;
+
+	for(;;){
+		if(isrunes){
+			r = *(Rune*)fmt;
+			fmt = (Rune*)fmt + 1;
+		}else{
+			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
+			r = rune;
+		}
+		f->r = r;
+		switch(r){
+		case '\0':
+			return nil;
+		case '.':
+			f->flags |= FmtWidth|FmtPrec;
+			continue;
+		case '0':
+			if(!(f->flags & FmtWidth)){
+				f->flags |= FmtZero;
+				continue;
+			}
+			/* fall through */
+		case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			i = 0;
+			while(r >= '0' && r <= '9'){
+				i = i * 10 + r - '0';
+				if(isrunes){
+					r = *(Rune*)fmt;
+					fmt = (Rune*)fmt + 1;
+				}else{
+					r = *(char*)fmt;
+					fmt = (char*)fmt + 1;
+				}
+			}
+			if(isrunes)
+				fmt = (Rune*)fmt - 1;
+			else
+				fmt = (char*)fmt - 1;
+		numflag:
+			if(f->flags & FmtWidth){
+				f->flags |= FmtPrec;
+				f->prec = i;
+			}else{
+				f->flags |= FmtWidth;
+				f->width = i;
+			}
+			continue;
+		case '*':
+			i = va_arg(f->args, int);
+			if(i < 0){
+				/*
+				 * negative precision =>
+				 * ignore the precision.
+				 */
+				if(f->flags & FmtPrec){
+					f->flags &= ~FmtPrec;
+					f->prec = 0;
+					continue;
+				}
+				i = -i;
+				f->flags |= FmtLeft;
+			}
+			goto numflag;
+		}
+		n = (*fmtfmt(r))(f);
+		if(n < 0)
+			return nil;
+		if(n == 0)
+			return fmt;
+	}
+}
--- /dev/null
+++ b/libc/fmtdef.h
@@ -1,0 +1,103 @@
+/*
+ * dofmt -- format to a buffer
+ * the number of characters formatted is returned,
+ * or -1 if there was an error.
+ * if the buffer is ever filled, flush is called.
+ * it should reset the buffer and return whether formatting should continue.
+ */
+
+typedef int (*Fmts)(Fmt*);
+
+typedef struct Quoteinfo Quoteinfo;
+struct Quoteinfo
+{
+	int	quoted;		/* if set, string must be quoted */
+	int	nrunesin;	/* number of input runes that can be accepted */
+	int	nbytesin;	/* number of input bytes that can be accepted */
+	int	nrunesout;	/* number of runes that will be generated */
+	int	nbytesout;	/* number of bytes that will be generated */
+};
+
+/* Edit .+1,/^$/ |cfn |grep -v static | grep __ */
+double       __Inf(int sign);
+double       __NaN(void);
+int          __badfmt(Fmt *f);
+int          __charfmt(Fmt *f);
+int          __countfmt(Fmt *f);
+int          __efgfmt(Fmt *fmt);
+int          __errfmt(Fmt *f);
+int          __flagfmt(Fmt *f);
+int          __fmtFdFlush(Fmt *f);
+int          __fmtcpy(Fmt *f, const void *vm, int n, int sz);
+void*        __fmtdispatch(Fmt *f, void *fmt, int isrunes);
+void *       __fmtflush(Fmt *f, void *t, int len);
+void         __fmtlock(void);
+int          __fmtpad(Fmt *f, int n);
+double       __fmtpow10(int n);
+int          __fmtrcpy(Fmt *f, const void *vm, int n);
+void         __fmtunlock(void);
+int          __ifmt(Fmt *f);
+int          __isInf(double d, int sign);
+int          __isNaN(double d);
+int          __needsquotes(char *s, int *quotelenp);
+int          __percentfmt(Fmt *f);
+void         __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout);
+int          __quotestrfmt(int runesin, Fmt *f);
+int          __rfmtpad(Fmt *f, int n);
+int          __runefmt(Fmt *f);
+int          __runeneedsquotes(Rune *r, int *quotelenp);
+int          __runesfmt(Fmt *f);
+int          __strfmt(Fmt *f);
+
+#define FMTCHAR(f, t, s, c)\
+	do{\
+	if(t + 1 > (char*)s){\
+		t = __fmtflush(f, t, 1);\
+		if(t != nil)\
+			s = f->stop;\
+		else\
+			return -1;\
+	}\
+	*t++ = c;\
+	}while(0)
+
+#define FMTRCHAR(f, t, s, c)\
+	do{\
+	if(t + 1 > (Rune*)s){\
+		t = __fmtflush(f, t, sizeof(Rune));\
+		if(t != nil)\
+			s = f->stop;\
+		else\
+			return -1;\
+	}\
+	*t++ = c;\
+	}while(0)
+
+#define FMTRUNE(f, t, s, r)\
+	do{\
+	Rune _rune;\
+	int _runelen;\
+	if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\
+		t = __fmtflush(f, t, _runelen);\
+		if(t != nil)\
+			s = f->stop;\
+		else\
+			return -1;\
+	}\
+	if(r < Runeself)\
+		*t++ = r;\
+	else{\
+		_rune = r;\
+		t += runetochar(t, &_rune);\
+	}\
+	}while(0)
+
+#ifdef va_copy
+#	define VA_COPY(a,b) va_copy(a,b)
+#	define VA_END(a) va_end(a)
+#else
+#	define VA_COPY(a,b) (a) = (b)
+#	define VA_END(a)
+#endif
+
+#define PLAN9PORT
--- /dev/null
+++ b/libc/fmtfd.c
@@ -1,0 +1,32 @@
+#include <inttypes.h>
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * public routine for final flush of a formatting buffer
+ * to a file descriptor; returns total char count.
+ */
+int
+fmtfdflush(Fmt *f)
+{
+	if(__fmtFdFlush(f) <= 0)
+		return -1;
+	return f->nfmt;
+}
+
+/*
+ * initialize an output buffer for buffered printing
+ */
+int
+fmtfdinit(Fmt *f, int fd, char *buf, int size)
+{
+	f->runes = 0;
+	f->start = buf;
+	f->to = buf;
+	f->stop = buf + size;
+	f->flush = __fmtFdFlush;
+	f->farg = (void*)(uintptr_t)fd;
+	f->nfmt = 0;
+	return 0;
+}
--- /dev/null
+++ b/libc/fmtfdflush.c
@@ -1,0 +1,20 @@
+#include <inttypes.h>
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * generic routine for flushing a formatting buffer
+ * to a file descriptor
+ */
+int
+__fmtFdFlush(Fmt *f)
+{
+	int n;
+
+	n = (char*)f->to - (char*)f->start;
+	if(n && write((uintptr_t)f->farg, f->start, n) != n)
+		return 0;
+	f->to = f->start;
+	return 1;
+}
--- /dev/null
+++ b/libc/fmtlock.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+
+static Lock fmtl;
+
+void
+__fmtlock(void)
+{
+	lock(&fmtl);
+}
+
+void
+__fmtunlock(void)
+{
+	unlock(&fmtl);
+}
--- /dev/null
+++ b/libc/fmtprint.c
@@ -1,0 +1,33 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtprint(Fmt *f, char *fmt, ...)
+{
+	va_list va;
+	int n;
+
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	VA_COPY(va, f->args);
+	VA_END(f->args);
+	va_start(f->args, fmt);
+	n = dofmt(f, fmt);
+	va_end(f->args);
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	VA_COPY(f->args,va);
+	VA_END(va);
+	if(n >= 0)
+		return 0;
+	return n;
+}
+
--- /dev/null
+++ b/libc/fmtquote.c
@@ -1,0 +1,249 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by __quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ *	NUL in input
+ *	count exceeded in input
+ *	count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+__quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
+{
+	int w;
+	Rune c;
+
+	q->quoted = 0;
+	q->nbytesout = 0;
+	q->nrunesout = 0;
+	q->nbytesin = 0;
+	q->nrunesin = 0;
+	if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
+		if(nout < 2)
+			return;
+		q->quoted = 1;
+		q->nbytesout = 2;
+		q->nrunesout = 2;
+	}
+	for(; nin!=0; nin--){
+		if(s)
+			w = chartorune(&c, s);
+		else{
+			c = *r;
+			w = runelen(c);
+		}
+
+		if(c == '\0')
+			break;
+		if(runesout){
+			if(q->nrunesout+1 > nout)
+				break;
+		}else{
+			if(q->nbytesout+w > nout)
+				break;
+		}
+
+		if((c <= L' ') || (c == L'\'') || (fmtdoquote!=0 && fmtdoquote(c))){
+			if(!q->quoted){
+				if(runesout){
+					if(1+q->nrunesout+1+1 > nout)	/* no room for quotes */
+						break;
+				}else{
+					if(1+q->nbytesout+w+1 > nout)	/* no room for quotes */
+						break;
+				}
+				q->nrunesout += 2;	/* include quotes */
+				q->nbytesout += 2;	/* include quotes */
+				q->quoted = 1;
+			}
+			if(c == '\'')	{
+				if(runesout){
+					if(1+q->nrunesout+1 > nout)	/* no room for quotes */
+						break;
+				}else{
+					if(1+q->nbytesout+w > nout)	/* no room for quotes */
+						break;
+				}
+				q->nbytesout++;
+				q->nrunesout++;	/* quotes reproduce as two characters */
+			}
+		}
+
+		/* advance input */
+		if(s)
+			s += w;
+		else
+			r++;
+		q->nbytesin += w;
+		q->nrunesin++;
+
+		/* advance output */
+		q->nbytesout += w;
+		q->nrunesout++;
+	}
+}
+
+static int
+qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
+{
+	Rune r, *rm, *rme;
+	char *t, *s, *m, *me;
+	Rune *rt, *rs;
+	ulong fl;
+	int nc, w;
+
+	m = sin;
+	me = m + q->nbytesin;
+	rm = rin;
+	rme = rm + q->nrunesin;
+
+	w = f->width;
+	fl = f->flags;
+	if(f->runes){
+		if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0)
+			return -1;
+	}else{
+		if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
+			return -1;
+	}
+	t = (char*)f->to;
+	s = (char*)f->stop;
+	rt = (Rune*)f->to;
+	rs = (Rune*)f->stop;
+	if(f->runes)
+		FMTRCHAR(f, rt, rs, '\'');
+	else
+		FMTRUNE(f, t, s, '\'');
+	for(nc = q->nrunesin; nc > 0; nc--){
+		if(sin){
+			r = *(uchar*)m;
+			if(r < Runeself)
+				m++;
+			else if((me - m) >= UTFmax || fullrune(m, me-m))
+				m += chartorune(&r, m);
+			else
+				break;
+		}else{
+			if(rm >= rme)
+				break;
+			r = *(uchar*)rm++;
+		}
+		if(f->runes){
+			FMTRCHAR(f, rt, rs, r);
+			if(r == '\'')
+				FMTRCHAR(f, rt, rs, r);
+		}else{
+			FMTRUNE(f, t, s, r);
+			if(r == '\'')
+				FMTRUNE(f, t, s, r);
+		}
+	}
+
+	if(f->runes){
+		FMTRCHAR(f, rt, rs, '\'');
+		USED(rs);
+		f->nfmt += rt - (Rune *)f->to;
+		f->to = rt;
+		if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
+			return -1;
+	}else{
+		FMTRUNE(f, t, s, '\'');
+		USED(s);
+		f->nfmt += t - (char *)f->to;
+		f->to = t;
+		if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+int
+__quotestrfmt(int runesin, Fmt *f)
+{
+	int nin, outlen;
+	Rune *r;
+	char *s;
+	Quoteinfo q;
+
+	nin = -1;
+	if(f->flags&FmtPrec)
+		nin = f->prec;
+	if(runesin){
+		r = va_arg(f->args, Rune *);
+		s = nil;
+	}else{
+		s = va_arg(f->args, char *);
+		r = nil;
+	}
+	if(!s && !r)
+		return __fmtcpy(f, (void*)"<nil>", 5, 5);
+
+	if(f->flush)
+		outlen = 0x7FFFFFFF;	/* if we can flush, no output limit */
+	else if(f->runes)
+		outlen = (Rune*)f->stop - (Rune*)f->to;
+	else
+		outlen = (char*)f->stop - (char*)f->to;
+
+	__quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
+//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
+
+	if(runesin){
+		if(!q.quoted)
+			return __fmtrcpy(f, r, q.nrunesin);
+		return qstrfmt(nil, r, &q, f);
+	}
+
+	if(!q.quoted)
+		return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
+	return qstrfmt(s, nil, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+	return __quotestrfmt(0, f);
+}
+
+int
+quoterunestrfmt(Fmt *f)
+{
+	return __quotestrfmt(1, f);
+}
+
+void
+quotefmtinstall(void)
+{
+	fmtinstall('q', quotestrfmt);
+	fmtinstall('Q', quoterunestrfmt);
+}
+
+int
+__needsquotes(char *s, int *quotelenp)
+{
+	Quoteinfo q;
+
+	__quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
+	*quotelenp = q.nbytesout;
+
+	return q.quoted;
+}
+
+int
+__runeneedsquotes(Rune *r, int *quotelenp)
+{
+	Quoteinfo q;
+
+	__quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
+	*quotelenp = q.nrunesout;
+
+	return q.quoted;
+}
--- /dev/null
+++ b/libc/fmtrune.c
@@ -1,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+fmtrune(Fmt *f, int r)
+{
+	Rune *rt;
+	char *t;
+	int n;
+
+	if(f->runes){
+		rt = (Rune*)f->to;
+		FMTRCHAR(f, rt, f->stop, r);
+		f->to = rt;
+		n = 1;
+	}else{
+		t = (char*)f->to;
+		FMTRUNE(f, t, f->stop, r);
+		n = t - (char*)f->to;
+		f->to = t;
+	}
+	f->nfmt += n;
+	return 0;
+}
--- /dev/null
+++ b/libc/fmtstr.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+char*
+fmtstrflush(Fmt *f)
+{
+	if(f->start == nil)
+		return nil;
+	*(char*)f->to = '\0';
+	return (char*)f->start;
+}
--- /dev/null
+++ b/libc/fmtvprint.c
@@ -1,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtvprint(Fmt *f, char *fmt, va_list args)
+{
+	va_list va;
+	int n;
+
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	VA_COPY(va,f->args);
+	VA_END(f->args);
+	VA_COPY(f->args,args);
+	n = dofmt(f, fmt);
+	f->flags = 0;
+	f->width = 0;
+	f->prec = 0;
+	VA_END(f->args);
+	VA_COPY(f->args,va);
+	VA_END(va);
+	if(n >= 0)
+		return 0;
+	return n;
+}
+
--- /dev/null
+++ b/libc/fprint.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+fprint(int fd, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vfprint(fd, fmt, args);
+	va_end(args);
+	return n;
+}
--- /dev/null
+++ b/libc/frand.c
@@ -1,0 +1,17 @@
+#include	<u.h>
+#include	<libc.h>
+
+#define	MASK	0x7fffffffL
+#define	NORM	(1.0/(1.0+MASK))
+
+double
+frand(void)
+{
+	double x;
+
+	do {
+		x = lrand() * NORM;
+		x = (x + lrand()) * NORM;
+	} while(x >= 1);
+	return x;
+}
--- /dev/null
+++ b/libc/genrandom.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+
+#undef long
+#undef ulong
+#include <sys/random.h>
+
+void
+genrandom(uchar *buf, int nbytes)
+{
+	getrandom(buf, nbytes, 0);
+}
--- /dev/null
+++ b/libc/getfields.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+
+int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+	Rune r;
+	int nr, intok, narg;
+
+	if(max <= 0)
+		return 0;
+
+	narg = 0;
+	args[narg] = str;
+	if(!mflag)
+		narg++;
+	intok = 0;
+	for(;; str += nr) {
+		nr = chartorune(&r, str);
+		if(r == 0)
+			break;
+		if(utfrune(set, r)) {
+			if(narg >= max)
+				break;
+			*str = 0;
+			intok = 0;
+			args[narg] = str + nr;
+			if(!mflag)
+				narg++;
+		} else {
+			if(!intok && mflag)
+				narg++;
+			intok = 1;
+		}
+	}
+	return narg;
+}
--- /dev/null
+++ b/libc/getpid.c
@@ -1,0 +1,17 @@
+#include	<u.h>
+#include	<libc.h>
+
+int
+getpid(void)
+{
+	char b[20];
+	int f;
+
+	memset(b, 0, sizeof(b));
+	f = open("#c/pid", 0);
+	if(f >= 0) {
+		read(f, b, sizeof(b));
+		close(f);
+	}
+	return atol(b);
+}
--- /dev/null
+++ b/libc/lnrand.c
@@ -1,0 +1,18 @@
+#include	<u.h>
+#include	<libc.h>
+
+#define	MASK	0x7fffffffL
+
+long
+lnrand(long n)
+{
+	long slop, v;
+
+	if(n < 0)
+		return n;
+	slop = MASK % n;
+	do
+		v = lrand();
+	while(v <= slop);
+	return v % n;
+}
--- /dev/null
+++ b/libc/lrand.c
@@ -1,0 +1,83 @@
+#include	<u.h>
+#include	<libc.h>
+
+/*
+ *	algorithm by
+ *	D. P. Mitchell & J. A. Reeds
+ */
+
+#define	LEN	607
+#define	TAP	273
+#define	MASK	0x7fffffffL
+#define	A	48271
+#define	M	2147483647
+#define	Q	44488
+#define	R	3399
+#define	NORM	(1.0/(1.0+MASK))
+
+static	ulong	rng_vec[LEN];
+static	ulong*	rng_tap = rng_vec;
+static	ulong*	rng_feed = 0;
+static	Lock	lk;
+
+static void
+isrand(long seed)
+{
+	long lo, hi, x;
+	int i;
+
+	rng_tap = rng_vec;
+	rng_feed = rng_vec+LEN-TAP;
+	seed = seed%M;
+	if(seed < 0)
+		seed += M;
+	if(seed == 0)
+		seed = 89482311;
+	x = seed;
+	/*
+	 *	Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1)
+	 */
+	for(i = -20; i < LEN; i++) {
+		hi = x / Q;
+		lo = x % Q;
+		x = A*lo - R*hi;
+		if(x < 0)
+			x += M;
+		if(i >= 0)
+			rng_vec[i] = x;
+	}
+}
+
+void
+srand(long seed)
+{
+	lock(&lk);
+	isrand(seed);
+	unlock(&lk);
+}
+
+long
+lrand(void)
+{
+	ulong x;
+
+	lock(&lk);
+
+	rng_tap--;
+	if(rng_tap < rng_vec) {
+		if(rng_feed == 0) {
+			isrand(1);
+			rng_tap--;
+		}
+		rng_tap += LEN;
+	}
+	rng_feed--;
+	if(rng_feed < rng_vec)
+		rng_feed += LEN;
+	x = (*rng_feed + *rng_tap) & MASK;
+	*rng_feed = x;
+
+	unlock(&lk);
+
+	return x;
+}
--- /dev/null
+++ b/libc/mallocz.c
@@ -1,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+void*
+mallocz(ulong n, int clr)
+{
+	void *v;
+
+	v = malloc(n);
+	if(v && clr)
+		memset(v, 0, n);
+	return v;
+}
--- /dev/null
+++ b/libc/nan.h
@@ -1,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern int __isNaN(double);
+extern int __isInf(double, int);
--- /dev/null
+++ b/libc/nan64.c
@@ -1,0 +1,67 @@
+/*
+ * 64-bit IEEE not-a-number routines.
+ * This is big/little-endian portable assuming that 
+ * the 64-bit doubles and 64-bit integers have the
+ * same byte ordering.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+#if defined (__APPLE__) || (__powerpc__)
+#define _NEEDLL
+#endif
+
+static uvlong uvnan    = ((uvlong)0x7FF00000<<32)|0x00000001;
+static uvlong uvinf    = ((uvlong)0x7FF00000<<32)|0x00000000;
+static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000;
+
+double
+__NaN(void)
+{
+	uvlong *p;
+
+	/* gcc complains about "return *(double*)&uvnan;" */
+	p = &uvnan;
+	return *(double*)p;
+}
+
+int
+__isNaN(double d)
+{
+	uvlong x;
+	double *p;
+
+	p = &d;
+	x = *(uvlong*)p;
+	return (ulong)(x>>32)==0x7FF00000 && !__isInf(d, 0);
+}
+
+double
+__Inf(int sign)
+{
+	uvlong *p;
+
+	if(sign < 0)
+		p = &uvinf;
+	else
+		p = &uvneginf;
+	return *(double*)p;
+}
+
+int
+__isInf(double d, int sign)
+{
+	uvlong x;
+	double *p;
+
+	p = &d;
+	x = *(uvlong*)p;
+	if(sign == 0)
+		return x==uvinf || x==uvneginf;
+	else if(sign > 0)
+		return x==uvinf;
+	else
+		return x==uvneginf;
+}
--- /dev/null
+++ b/libc/netmkaddr.c
@@ -1,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/*
+ *  make an address, add the defaults
+ */
+char *
+netmkaddr(char *linear, char *defnet, char *defsrv)
+{
+	static char addr[256];
+	char *cp;
+
+	/*
+	 *  dump network name
+	 */
+	cp = strchr(linear, '!');
+	if(cp == 0){
+		if(defnet==0){
+			if(defsrv)
+				snprint(addr, sizeof(addr), "net!%s!%s",
+					linear, defsrv);
+			else
+				snprint(addr, sizeof(addr), "net!%s", linear);
+		}
+		else {
+			if(defsrv)
+				snprint(addr, sizeof(addr), "%s!%s!%s", defnet,
+					linear, defsrv);
+			else
+				snprint(addr, sizeof(addr), "%s!%s", defnet,
+					linear);
+		}
+		return addr;
+	}
+
+	/*
+	 *  if there is already a service, use it
+	 */
+	cp = strchr(cp+1, '!');
+	if(cp)
+		return linear;
+
+	/*
+	 *  add default service
+	 */
+	if(defsrv == 0)
+		return linear;
+	snprint(addr, sizeof(addr), "%s!%s", linear, defsrv);
+
+	return addr;
+}
--- /dev/null
+++ b/libc/nrand.c
@@ -1,0 +1,21 @@
+#include	<u.h>
+#include	<libc.h>
+
+#define	MASK	0x7fffffffL
+
+int
+nrand(int n)
+{
+	long slop, v;
+
+	if(n < 0)
+		return n;
+	if(n == 1)
+		return 0;
+	/* and if n == 0, you deserve what you get */
+	slop = MASK % n;
+	do
+		v = lrand();
+	while(v <= slop);
+	return v % n;
+}
--- /dev/null
+++ b/libc/nsec.c
@@ -1,0 +1,65 @@
+#include <u.h>
+#include <libc.h>
+
+static uvlong order = (uvlong) 0x0001020304050607ULL;
+
+static void
+be2vlong(vlong *to, uchar *f)
+{
+	uchar *t, *o;
+	int i;
+
+	t = (uchar*)to;
+	o = (uchar*)&order;
+	for(i = 0; i < 8; i++)
+		t[o[i]] = f[i];
+}
+
+/*
+ *  After a fork with fd's copied, both fd's are pointing to
+ *  the same Chan structure.  Since the offset is kept in the Chan
+ *  structure, the seek's and read's in the two processes can
+ *  compete at moving the offset around.  Hence the retry loop.
+ *
+ *  Since the bintime version doesn't need a seek, it doesn't
+ *  have the loop.
+ */
+vlong
+nsec(void)
+{
+	char b[12+1];
+	static int f = -1;
+	static int usebintime;
+	int retries;
+	vlong t;
+
+	if(f < 0){
+		usebintime = 1;
+		f = open("/dev/bintime", OREAD|OCEXEC);
+		if(f < 0){
+			usebintime = 0;
+			f = open("/dev/nsec", OREAD|OCEXEC);
+			if(f < 0)
+				return 0;
+		}
+	}
+
+	if(usebintime){
+		if(read(f, b, sizeof(uvlong)) < 0)
+			goto error;
+		be2vlong(&t, (uchar*)b);
+		return t;
+	} else {
+		for(retries = 0; retries < 100; retries++){
+			if(seek(f, 0, 0) >= 0 && read(f, b, sizeof(b)-1) >= 0){
+				b[sizeof(b)-1] = 0;
+				return strtoll(b, 0, 0);
+			}
+		}
+	}
+
+error:
+	close(f);
+	f = -1;
+	return 0;
+}
--- /dev/null
+++ b/libc/pow10.c
@@ -1,0 +1,42 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * this table might overflow 127-bit exponent representations.
+ * in that case, truncate it after 1.0e38.
+ * it is important to get all one can from this
+ * routine since it is used in atof to scale numbers.
+ * the presumption is that C converts fp numbers better
+ * than multipication of lower powers of 10.
+ */
+
+static
+double	tab[] =
+{
+	1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
+	1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19,
+	1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29,
+	1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39,
+	1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49,
+	1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59,
+	1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69,
+};
+
+double
+__fmtpow10(int n)
+{
+	int m;
+
+	if(n < 0) {
+		n = -n;
+		if(n < (int)(sizeof(tab)/sizeof(tab[0])))
+			return 1/tab[n];
+		m = n/2;
+		return __fmtpow10(-m) * __fmtpow10(m-n);
+	}
+	if(n < (int)(sizeof(tab)/sizeof(tab[0])))
+		return tab[n];
+	m = n/2;
+	return __fmtpow10(m) * __fmtpow10(n-m);
+}
--- /dev/null
+++ b/libc/print.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+print(char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vfprint(1, fmt, args);
+	va_end(args);
+	return n;
+}
--- /dev/null
+++ b/libc/pushssl.c
@@ -1,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Since the SSL device uses decimal file descriptors to name channels,
+ * it is impossible for a user-level file server to stand in for the kernel device.
+ * Thus we hard-code #D rather than use /net/ssl.
+ */
+
+int
+pushssl(int fd, char *alg, char *secin, char *secout, int *cfd)
+{
+	char buf[8];
+	char dname[64];
+	int n, data, ctl;
+
+	ctl = open("#D/ssl/clone", ORDWR);
+	if(ctl < 0)
+		return -1;
+	n = read(ctl, buf, sizeof(buf)-1);
+	if(n < 0)
+		goto error;
+	buf[n] = 0;
+	sprint(dname, "#D/ssl/%s/data", buf);
+	data = open(dname, ORDWR);
+	if(data < 0)
+		goto error;
+	if(fprint(ctl, "fd %d", fd) < 0 ||
+	   fprint(ctl, "secretin %s", secin) < 0 ||
+	   fprint(ctl, "secretout %s", secout) < 0 ||
+	   fprint(ctl, "alg %s", alg) < 0){
+		close(data);
+		goto error;
+	}
+	close(fd);
+	if(cfd != 0)
+		*cfd = ctl;
+	else
+		close(ctl);
+	return data;
+error:
+	close(ctl);
+	return -1;
+}
--- /dev/null
+++ b/libc/pushtls.c
@@ -1,0 +1,99 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+enum {
+	TLSFinishedLen = 12,
+	HFinished = 20,
+};
+
+static int
+finished(int hand, int isclient)
+{
+	int i, n;
+	uchar buf[500], buf2[500];
+
+	buf[0] = HFinished;
+	buf[1] = TLSFinishedLen>>16;
+	buf[2] = TLSFinishedLen>>8;
+	buf[3] = TLSFinishedLen;
+	n = TLSFinishedLen+4;
+
+	for(i=0; i<2; i++){
+		if(i==0)
+			memmove(buf+4, "client finished", TLSFinishedLen);
+		else
+			memmove(buf+4, "server finished", TLSFinishedLen);
+		if(isclient == 1-i){
+			if(write(hand, buf, n) != n)
+				return -1;
+		}else{
+			if(readn(hand, buf2, n) != n || memcmp(buf,buf2,n) != 0)
+				return -1;
+		}
+	}
+	return 1;
+}
+
+
+// given a plain fd and secrets established beforehand, return encrypted connection
+int
+pushtls(int fd, char *hashalg, char *encalg, int isclient, char *secret, char *dir)
+{
+	char buf[8];
+	char dname[64];
+	int n, data, ctl, hand;
+
+	// open a new filter; get ctl fd
+	data = hand = -1;
+	// /net/tls uses decimal file descriptors to name channels, hence a
+	// user-level file server can't stand in for #a; may as well hard-code it.
+	ctl = open("#a/tls/clone", ORDWR);
+	if(ctl < 0)
+		goto error;
+	n = read(ctl, buf, sizeof(buf)-1);
+	if(n < 0)
+		goto error;
+	buf[n] = 0;
+	if(dir)
+		sprint(dir, "#a/tls/%s", buf);
+
+	// get application fd
+	sprint(dname, "#a/tls/%s/data", buf);
+	data = open(dname, ORDWR);
+	if(data < 0)
+		goto error;
+
+	// get handshake fd
+	sprint(dname, "#a/tls/%s/hand", buf);
+	hand = open(dname, ORDWR);
+	if(hand < 0)
+		goto error;
+
+	// speak a minimal handshake
+	if(fprint(ctl, "fd %d 0x301", fd) < 0 ||
+	   fprint(ctl, "version 0x301") < 0 ||
+	   fprint(ctl, "secret %s %s %d %s", hashalg, encalg, isclient, secret) < 0 ||
+	   fprint(ctl, "changecipher") < 0 ||
+	   finished(hand, isclient) < 0 ||
+	   fprint(ctl, "opened") < 0){
+		close(hand);
+		hand = -1;
+		goto error;
+	}
+	close(ctl);
+	close(hand);
+	close(fd);
+	return data;
+
+error:
+	if(data>=0)
+		close(data);
+	if(ctl>=0)
+		close(ctl);
+	if(hand>=0)
+		close(hand);
+	return -1;
+}
--- /dev/null
+++ b/libc/rand.c
@@ -1,0 +1,8 @@
+#include	<u.h>
+#include	<libc.h>
+
+int
+rand(void)
+{
+	return lrand() & 0x7fff;
+}
--- /dev/null
+++ b/libc/read9pmsg.c
@@ -1,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+read9pmsg(int fd, void *abuf, uint n)
+{
+	int m, len;
+	uchar *buf;
+
+	buf = abuf;
+
+	/* read count */
+	m = readn(fd, buf, BIT32SZ);
+	if(m != BIT32SZ){
+		if(m < 0)
+			return -1;
+		return 0;
+	}
+
+	len = GBIT32(buf);
+	if(len <= BIT32SZ || len > n){
+		werrstr("bad length in 9P2000 message header");
+		return -1;
+	}
+	len -= BIT32SZ;
+	m = readn(fd, buf+BIT32SZ, len);
+	if(m < len)
+		return 0;
+	return BIT32SZ+m;
+}
--- /dev/null
+++ b/libc/readn.c
@@ -1,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+long
+readn(int f, void *av, long n)
+{
+	char *a;
+	long m, t;
+
+	a = av;
+	t = 0;
+	while(t < n){
+		m = read(f, a+t, n-t);
+		if(m <= 0){
+			if(t == 0)
+				return m;
+			break;
+		}
+		t += m;
+	}
+	return t;
+}
--- /dev/null
+++ b/libc/rune.c
@@ -1,0 +1,204 @@
+#include	<u.h>
+#include	<libc.h>
+
+enum
+{
+	Bit1	= 7,
+	Bitx	= 6,
+	Bit2	= 5,
+	Bit3	= 4,
+	Bit4	= 3,
+	Bit5	= 2,
+
+	T1	= ((1<<(Bit1+1))-1) ^ 0xFF,	/* 0000 0000 */
+	Tx	= ((1<<(Bitx+1))-1) ^ 0xFF,	/* 1000 0000 */
+	T2	= ((1<<(Bit2+1))-1) ^ 0xFF,	/* 1100 0000 */
+	T3	= ((1<<(Bit3+1))-1) ^ 0xFF,	/* 1110 0000 */
+	T4	= ((1<<(Bit4+1))-1) ^ 0xFF,	/* 1111 0000 */
+	T5	= ((1<<(Bit5+1))-1) ^ 0xFF,	/* 1111 1000 */
+
+	Rune1	= (1<<(Bit1+0*Bitx))-1,		/* 0000 0000 0000 0000 0111 1111 */
+	Rune2	= (1<<(Bit2+1*Bitx))-1,		/* 0000 0000 0000 0111 1111 1111 */
+	Rune3	= (1<<(Bit3+2*Bitx))-1,		/* 0000 0000 1111 1111 1111 1111 */
+	Rune4	= (1<<(Bit4+3*Bitx))-1,		/* 0011 1111 1111 1111 1111 1111 */
+
+	Maskx	= (1<<Bitx)-1,			/* 0011 1111 */
+	Testx	= Maskx ^ 0xFF,			/* 1100 0000 */
+
+	Bad	= Runeerror,
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+	int c, c1, c2, c3;
+	long l;
+
+	/*
+	 * one character sequence
+	 *	00000-0007F => T1
+	 */
+	c = *(uchar*)str;
+	if(c < Tx) {
+		*rune = c;
+		return 1;
+	}
+
+	/*
+	 * two character sequence
+	 *	0080-07FF => T2 Tx
+	 */
+	c1 = *(uchar*)(str+1) ^ Tx;
+	if(c1 & Testx)
+		goto bad;
+	if(c < T3) {
+		if(c < T2)
+			goto bad;
+		l = ((c << Bitx) | c1) & Rune2;
+		if(l <= Rune1)
+			goto bad;
+		*rune = l;
+		return 2;
+	}
+
+	/*
+	 * three character sequence
+	 *	0800-FFFF => T3 Tx Tx
+	 */
+	c2 = *(uchar*)(str+2) ^ Tx;
+	if(c2 & Testx)
+		goto bad;
+	if(c < T4) {
+		l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+		if(l <= Rune2)
+			goto bad;
+		*rune = l;
+		return 3;
+	}
+
+ 	/*
+	 * four character sequence
+	 *	10000-10FFFF => T4 Tx Tx Tx
+	 */
+	if(UTFmax >= 4) {
+		c3 = *(uchar*)(str+3) ^ Tx;
+		if(c3 & Testx)
+			goto bad;
+		if(c < T5) {
+			l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
+			if(l <= Rune3)
+				goto bad;
+			if(l > Runemax)
+				goto bad;
+			*rune = l;
+			return 4;
+		}
+	}
+
+	/*
+	 * bad decoding
+	 */
+bad:
+	*rune = Bad;
+	return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+	long c;
+
+	c = *rune;
+	if(c > Runemax)
+		c = Runeerror;
+
+	/*
+	 * one character sequence
+	 *	00000-0007F => 00-7F
+	 */
+	if(c <= Rune1) {
+		str[0] = c;
+		return 1;
+	}
+
+	/*
+	 * two character sequence
+	 *	0080-07FF => T2 Tx
+	 */
+	if(c <= Rune2) {
+		str[0] = T2 | (c >> 1*Bitx);
+		str[1] = Tx | (c & Maskx);
+		return 2;
+	}
+
+	/*
+	 * three character sequence
+	 *	0800-FFFF => T3 Tx Tx
+	 */
+	if(c <= Rune3) {
+		str[0] = T3 |  (c >> 2*Bitx);
+		str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+		str[2] = Tx |  (c & Maskx);
+		return 3;
+	}
+
+	/*
+	 * four character sequence
+	 *	10000-1FFFFF => T4 Tx Tx Tx
+	 */
+	str[0] = T4 |  (c >> 3*Bitx);
+	str[1] = Tx | ((c >> 2*Bitx) & Maskx);
+	str[2] = Tx | ((c >> 1*Bitx) & Maskx);
+	str[3] = Tx |  (c & Maskx);
+	return 4;
+}
+
+int
+runelen(long c)
+{
+	Rune rune;
+	char str[UTFmax];
+
+	rune = c;
+	return runetochar(str, &rune);
+}
+
+int
+runenlen(Rune *r, int nrune)
+{
+	int nb, c;
+
+	nb = 0;
+	while(nrune--) {
+		c = *r++;
+		if(c <= Rune1)
+			nb++;
+		else
+		if(c <= Rune2)
+			nb += 2;
+		else
+		if(c <= Rune3 || c > Runemax)
+			nb += 3;
+		else
+			nb += 4;
+	}
+	return nb;
+}
+
+int
+fullrune(char *str, int n)
+{
+	int c;
+
+	if(n <= 0)
+		return 0;
+	c = *(uchar*)str;
+	if(c < Tx)
+		return 1;
+	if(c < T3)
+		return n >= 2;
+	if(UTFmax == 3 || c < T4)
+		return n >= 3;
+	return n >= 4;
+}
+
--- /dev/null
+++ b/libc/runefmtstr.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+Rune*
+runefmtstrflush(Fmt *f)
+{
+	if(f->start == nil)
+		return nil;
+	*(Rune*)f->to = '\0';
+	return f->start;
+}
--- /dev/null
+++ b/libc/runeseprint.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+Rune*
+runeseprint(Rune *buf, Rune *e, char *fmt, ...)
+{
+	Rune *p;
+	va_list args;
+
+	va_start(args, fmt);
+	p = runevseprint(buf, e, fmt, args);
+	va_end(args);
+	return p;
+}
--- /dev/null
+++ b/libc/runesmprint.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+Rune*
+runesmprint(char *fmt, ...)
+{
+	va_list args;
+	Rune *p;
+
+	va_start(args, fmt);
+	p = runevsmprint(fmt, args);
+	va_end(args);
+	return p;
+}
--- /dev/null
+++ b/libc/runesnprint.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+runesnprint(Rune *buf, int len, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = runevsnprint(buf, len, fmt, args);
+	va_end(args);
+	return n;
+}
+
--- /dev/null
+++ b/libc/runesprint.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+runesprint(Rune *buf, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = runevsnprint(buf, 256, fmt, args);
+	va_end(args);
+	return n;
+}
--- /dev/null
+++ b/libc/runestrcat.c
@@ -1,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrcat(Rune *s1, Rune *s2)
+{
+
+	runestrcpy(runestrchr(s1, 0), s2);
+	return s1;
+}
--- /dev/null
+++ b/libc/runestrchr.c
@@ -1,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrchr(Rune *s, Rune c)
+{
+	Rune c0 = c;
+	Rune c1;
+
+	if(c == 0) {
+		while(*s++)
+			;
+		return s-1;
+	}
+
+	while(c1 = *s++)
+		if(c1 == c0)
+			return s-1;
+	return 0;
+}
--- /dev/null
+++ b/libc/runestrcmp.c
@@ -1,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runestrcmp(Rune *s1, Rune *s2)
+{
+	Rune c1, c2;
+
+	for(;;) {
+		c1 = *s1++;
+		c2 = *s2++;
+		if(c1 != c2) {
+			if(c1 > c2)
+				return 1;
+			return -1;
+		}
+		if(c1 == 0)
+			return 0;
+	}
+}
--- /dev/null
+++ b/libc/runestrcpy.c
@@ -1,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrcpy(Rune *s1, Rune *s2)
+{
+	Rune *os1;
+
+	os1 = s1;
+	while(*s1++ = *s2++)
+		;
+	return os1;
+}
--- /dev/null
+++ b/libc/runestrdup.c
@@ -1,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrdup(Rune *s)
+{
+	Rune *ns;
+
+	ns = malloc(sizeof(Rune)*(runestrlen(s) + 1));
+	if(ns == 0)
+		return 0;
+	setmalloctag(ns, getcallerpc(&s));
+	return runestrcpy(ns, s);
+}
--- /dev/null
+++ b/libc/runestrecpy.c
@@ -1,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrecpy(Rune *s1, Rune *es1, Rune *s2)
+{
+	if(s1 >= es1)
+		return s1;
+
+	while(*s1++ = *s2++){
+		if(s1 == es1){
+			s1[-1] = '\0';
+			break;
+		}
+	}
+	return s1-1;
+}
--- /dev/null
+++ b/libc/runestrlen.c
@@ -1,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+long
+runestrlen(Rune *s)
+{
+
+	return runestrchr(s, 0) - s;
+}
--- /dev/null
+++ b/libc/runestrncat.c
@@ -1,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrncat(Rune *s1, Rune *s2, long n)
+{
+	Rune *os1;
+
+	os1 = s1;
+	s1 = runestrchr(s1, 0);
+	while(*s1++ = *s2++)
+		if(--n < 0) {
+			s1[-1] = 0;
+			break;
+		}
+	return os1;
+}
--- /dev/null
+++ b/libc/runestrncmp.c
@@ -1,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runestrncmp(Rune *s1, Rune *s2, long n)
+{
+	Rune c1, c2;
+
+	while(n > 0) {
+		c1 = *s1++;
+		c2 = *s2++;
+		n--;
+		if(c1 != c2) {
+			if(c1 > c2)
+				return 1;
+			return -1;
+		}
+		if(c1 == 0)
+			break;
+	}
+	return 0;
+}
--- /dev/null
+++ b/libc/runestrncpy.c
@@ -1,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrncpy(Rune *s1, Rune *s2, long n)
+{
+	int i;
+	Rune *os1;
+
+	os1 = s1;
+	for(i = 0; i < n; i++)
+		if((*s1++ = *s2++) == 0) {
+			while(++i < n)
+				*s1++ = 0;
+			return os1;
+		}
+	return os1;
+}
--- /dev/null
+++ b/libc/runestrrchr.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrrchr(Rune *s, Rune c)
+{
+	Rune *r;
+
+	if(c == 0)
+		return runestrchr(s, 0);
+	r = 0;
+	while(s = runestrchr(s, c))
+		r = s++;
+	return r;
+}
--- /dev/null
+++ b/libc/runestrstr.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+Rune*
+runestrstr(Rune *s1, Rune *s2)
+{
+	Rune *p, *pa, *pb;
+	int c0, c;
+
+	c0 = *s2;
+	if(c0 == 0)
+		return s1;
+	s2++;
+	for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) {
+		pa = p;
+		for(pb=s2;; pb++) {
+			c = *pb;
+			if(c == 0)
+				return p;
+			if(c != *++pa)
+				break;
+		}
+	}
+	return 0;
+}
--- /dev/null
+++ b/libc/runetype.c
@@ -1,0 +1,1182 @@
+#include	<u.h>
+#include	<libc.h>
+
+/*
+ * alpha ranges -
+ *	only covers ranges not in lower||upper
+ */
+static
+Rune	_alpha2[] =
+{
+	0x00d8,	0x00f6,	/* Ø - ö */
+	0x00f8,	0x01f5,	/* ø - ǵ */
+	0x0250,	0x02a8,	/* ɐ - ʨ */
+	0x038e,	0x03a1,	/* Ύ - Ρ */
+	0x03a3,	0x03ce,	/* Σ - ώ */
+	0x03d0,	0x03d6,	/* ϐ - ϖ */
+	0x03e2,	0x03f3,	/* Ϣ - ϳ */
+	0x0490,	0x04c4,	/* Ґ - ӄ */
+	0x0561,	0x0587,	/* ա - և */
+	0x05d0,	0x05ea,	/* א - ת */
+	0x05f0,	0x05f2,	/* װ - ײ */
+	0x0621,	0x063a,	/* ء - غ */
+	0x0640,	0x064a,	/* ـ - ي */
+	0x0671,	0x06b7,	/* ٱ - ڷ */
+	0x06ba,	0x06be,	/* ں - ھ */
+	0x06c0,	0x06ce,	/* ۀ - ێ */
+	0x06d0,	0x06d3,	/* ې - ۓ */
+	0x0905,	0x0939,	/* अ - ह */
+	0x0958,	0x0961,	/* क़ - ॡ */
+	0x0985,	0x098c,	/* অ - ঌ */
+	0x098f,	0x0990,	/* এ - ঐ */
+	0x0993,	0x09a8,	/* ও - ন */
+	0x09aa,	0x09b0,	/* প - র */
+	0x09b6,	0x09b9,	/* শ - হ */
+	0x09dc,	0x09dd,	/* ড় - ঢ় */
+	0x09df,	0x09e1,	/* য় - ৡ */
+	0x09f0,	0x09f1,	/* ৰ - ৱ */
+	0x0a05,	0x0a0a,	/* ਅ - ਊ */
+	0x0a0f,	0x0a10,	/* ਏ - ਐ */
+	0x0a13,	0x0a28,	/* ਓ - ਨ */
+	0x0a2a,	0x0a30,	/* ਪ - ਰ */
+	0x0a32,	0x0a33,	/* ਲ - ਲ਼ */
+	0x0a35,	0x0a36,	/* ਵ - ਸ਼ */
+	0x0a38,	0x0a39,	/* ਸ - ਹ */
+	0x0a59,	0x0a5c,	/* ਖ਼ - ੜ */
+	0x0a85,	0x0a8b,	/* અ - ઋ */
+	0x0a8f,	0x0a91,	/* એ - ઑ */
+	0x0a93,	0x0aa8,	/* ઓ - ન */
+	0x0aaa,	0x0ab0,	/* પ - ર */
+	0x0ab2,	0x0ab3,	/* લ - ળ */
+	0x0ab5,	0x0ab9,	/* વ - હ */
+	0x0b05,	0x0b0c,	/* ଅ - ଌ */
+	0x0b0f,	0x0b10,	/* ଏ - ଐ */
+	0x0b13,	0x0b28,	/* ଓ - ନ */
+	0x0b2a,	0x0b30,	/* ପ - ର */
+	0x0b32,	0x0b33,	/* ଲ - ଳ */
+	0x0b36,	0x0b39,	/* ଶ - ହ */
+	0x0b5c,	0x0b5d,	/* ଡ଼ - ଢ଼ */
+	0x0b5f,	0x0b61,	/* ୟ - ୡ */
+	0x0b85,	0x0b8a,	/* அ - ஊ */
+	0x0b8e,	0x0b90,	/* எ - ஐ */
+	0x0b92,	0x0b95,	/* ஒ - க */
+	0x0b99,	0x0b9a,	/* ங - ச */
+	0x0b9e,	0x0b9f,	/* ஞ - ட */
+	0x0ba3,	0x0ba4,	/* ண - த */
+	0x0ba8,	0x0baa,	/* ந - ப */
+	0x0bae,	0x0bb5,	/* ம - வ */
+	0x0bb7,	0x0bb9,	/* ஷ - ஹ */
+	0x0c05,	0x0c0c,	/* అ - ఌ */
+	0x0c0e,	0x0c10,	/* ఎ - ఐ */
+	0x0c12,	0x0c28,	/* ఒ - న */
+	0x0c2a,	0x0c33,	/* ప - ళ */
+	0x0c35,	0x0c39,	/* వ - హ */
+	0x0c60,	0x0c61,	/* ౠ - ౡ */
+	0x0c85,	0x0c8c,	/* ಅ - ಌ */
+	0x0c8e,	0x0c90,	/* ಎ - ಐ */
+	0x0c92,	0x0ca8,	/* ಒ - ನ */
+	0x0caa,	0x0cb3,	/* ಪ - ಳ */
+	0x0cb5,	0x0cb9,	/* ವ - ಹ */
+	0x0ce0,	0x0ce1,	/* ೠ - ೡ */
+	0x0d05,	0x0d0c,	/* അ - ഌ */
+	0x0d0e,	0x0d10,	/* എ - ഐ */
+	0x0d12,	0x0d28,	/* ഒ - ന */
+	0x0d2a,	0x0d39,	/* പ - ഹ */
+	0x0d60,	0x0d61,	/* ൠ - ൡ */
+	0x0e01,	0x0e30,	/* ก - ะ */
+	0x0e32,	0x0e33,	/* า - ำ */
+	0x0e40,	0x0e46,	/* เ - ๆ */
+	0x0e5a,	0x0e5b,	/* ๚ - ๛ */
+	0x0e81,	0x0e82,	/* ກ - ຂ */
+	0x0e87,	0x0e88,	/* ງ - ຈ */
+	0x0e94,	0x0e97,	/* ດ - ທ */
+	0x0e99,	0x0e9f,	/* ນ - ຟ */
+	0x0ea1,	0x0ea3,	/* ມ - ຣ */
+	0x0eaa,	0x0eab,	/* ສ - ຫ */
+	0x0ead,	0x0eae,	/* ອ - ຮ */
+	0x0eb2,	0x0eb3,	/* າ - ຳ */
+	0x0ec0,	0x0ec4,	/* ເ - ໄ */
+	0x0edc,	0x0edd,	/* ໜ - ໝ */
+	0x0f18,	0x0f19,	/* ༘ - ༙ */
+	0x0f40,	0x0f47,	/* ཀ - ཇ */
+	0x0f49,	0x0f69,	/* ཉ - ཀྵ */
+	0x10d0,	0x10f6,	/* ა - ჶ */
+	0x1100,	0x1159,	/* ᄀ - ᅙ */
+	0x115f,	0x11a2,	/* ᅟ - ᆢ */
+	0x11a8,	0x11f9,	/* ᆨ - ᇹ */
+	0x1e00,	0x1e9b,	/* Ḁ - ẛ */
+	0x1f50,	0x1f57,	/* ὐ - ὗ */
+	0x1f80,	0x1fb4,	/* ᾀ - ᾴ */
+	0x1fb6,	0x1fbc,	/* ᾶ - ᾼ */
+	0x1fc2,	0x1fc4,	/* ῂ - ῄ */
+	0x1fc6,	0x1fcc,	/* ῆ - ῌ */
+	0x1fd0,	0x1fd3,	/* ῐ - ΐ */
+	0x1fd6,	0x1fdb,	/* ῖ - Ί */
+	0x1fe0,	0x1fec,	/* ῠ - Ῥ */
+	0x1ff2,	0x1ff4,	/* ῲ - ῴ */
+	0x1ff6,	0x1ffc,	/* ῶ - ῼ */
+	0x210a,	0x2113,	/* ℊ - ℓ */
+	0x2115,	0x211d,	/* ℕ - ℝ */
+	0x2120,	0x2122,	/* ℠ - ™ */
+	0x212a,	0x2131,	/* K - ℱ */
+	0x2133,	0x2138,	/* ℳ - ℸ */
+	0x3041,	0x3094,	/* ぁ - ゔ */
+	0x30a1,	0x30fa,	/* ァ - ヺ */
+	0x3105,	0x312c,	/* ㄅ - ㄬ */
+	0x3131,	0x318e,	/* ㄱ - ㆎ */
+	0x3192,	0x319f,	/* ㆒ - ㆟ */
+	0x3260,	0x327b,	/* ㉠ - ㉻ */
+	0x328a,	0x32b0,	/* ㊊ - ㊰ */
+	0x32d0,	0x32fe,	/* ㋐ - ㋾ */
+	0x3300,	0x3357,	/* ㌀ - ㍗ */
+	0x3371,	0x3376,	/* ㍱ - ㍶ */
+	0x337b,	0x3394,	/* ㍻ - ㎔ */
+	0x3399,	0x339e,	/* ㎙ - ㎞ */
+	0x33a9,	0x33ad,	/* ㎩ - ㎭ */
+	0x33b0,	0x33c1,	/* ㎰ - ㏁ */
+	0x33c3,	0x33c5,	/* ㏃ - ㏅ */
+	0x33c7,	0x33d7,	/* ㏇ - ㏗ */
+	0x33d9,	0x33dd,	/* ㏙ - ㏝ */
+	0x4e00,	0x9fff,	/* 一 - 鿿 */
+	0xac00,	0xd7a3,	/* 가 - 힣 */
+	0xf900,	0xfb06,	/* 豈 - st */
+	0xfb13,	0xfb17,	/* ﬓ - ﬗ */
+	0xfb1f,	0xfb28,	/* ײַ - ﬨ */
+	0xfb2a,	0xfb36,	/* שׁ - זּ */
+	0xfb38,	0xfb3c,	/* טּ - לּ */
+	0xfb40,	0xfb41,	/* נּ - סּ */
+	0xfb43,	0xfb44,	/* ףּ - פּ */
+	0xfb46,	0xfbb1,	/* צּ - ﮱ */
+	0xfbd3,	0xfd3d,	/* ﯓ - ﴽ */
+	0xfd50,	0xfd8f,	/* ﵐ - ﶏ */
+	0xfd92,	0xfdc7,	/* ﶒ - ﷇ */
+	0xfdf0,	0xfdf9,	/* ﷰ - ﷹ */
+	0xfe70,	0xfe72,	/* ﹰ - ﹲ */
+	0xfe76,	0xfefc,	/* ﹶ - ﻼ */
+	0xff66,	0xff6f,	/* ヲ - ッ */
+	0xff71,	0xff9d,	/* ア - ン */
+	0xffa0,	0xffbe,	/* ᅠ - ᄒ */
+	0xffc2,	0xffc7,	/* ᅡ - ᅦ */
+	0xffca,	0xffcf,	/* ᅧ - ᅬ */
+	0xffd2,	0xffd7,	/* ᅭ - ᅲ */
+	0xffda,	0xffdc,	/* ᅳ - ᅵ */
+};
+
+/*
+ * alpha singlets -
+ *	only covers ranges not in lower||upper
+ */
+static
+Rune	_alpha1[] =
+{
+	0x00aa,	/* ª */
+	0x00b5,	/* µ */
+	0x00ba,	/* º */
+	0x03da,	/* Ϛ */
+	0x03dc,	/* Ϝ */
+	0x03de,	/* Ϟ */
+	0x03e0,	/* Ϡ */
+	0x06d5,	/* ە */
+	0x09b2,	/* ল */
+	0x0a5e,	/* ਫ਼ */
+	0x0a8d,	/* ઍ */
+	0x0ae0,	/* ૠ */
+	0x0b9c,	/* ஜ */
+	0x0cde,	/* ೞ */
+	0x0e4f,	/* ๏ */
+	0x0e84,	/* ຄ */
+	0x0e8a,	/* ຊ */
+	0x0e8d,	/* ຍ */
+	0x0ea5,	/* ລ */
+	0x0ea7,	/* ວ */
+	0x0eb0,	/* ະ */
+	0x0ebd,	/* ຽ */
+	0x1fbe,	/* ι */
+	0x207f,	/* ⁿ */
+	0x20a8,	/* ₨ */
+	0x2102,	/* ℂ */
+	0x2107,	/* ℇ */
+	0x2124,	/* ℤ */
+	0x2126,	/* Ω */
+	0x2128,	/* ℨ */
+	0xfb3e,	/* מּ */
+	0xfe74,	/* ﹴ */
+};
+
+/*
+ * space ranges
+ */
+static
+Rune	_space2[] =
+{
+	0x0009,	0x000a,	/* tab and newline */
+	0x0020,	0x0020,	/* space */
+	0x0085, 0x0085,
+	0x00a0,	0x00a0,	/*   */
+	0x1680, 0x1680,
+	0x180e, 0x180e,
+	0x2000,	0x200b,	/*   - ​ */
+	0x2028,	0x2029,	/* 
 - 
 */
+	0x202f, 0x202f,
+	0x205f, 0x205f,
+	0x3000,	0x3000,	/*   */
+	0xfeff,	0xfeff,	/*  */
+};
+
+/*
+ * lower case ranges
+ *	3rd col is conversion excess 500
+ */
+static
+Rune	_toupper2[] =
+{
+	0x0061,	0x007a, 468,	/* a-z A-Z */
+	0x00e0,	0x00f6, 468,	/* à-ö À-Ö */
+	0x00f8,	0x00fe, 468,	/* ø-þ Ø-Þ */
+	0x0256,	0x0257, 295,	/* ɖ-ɗ Ɖ-Ɗ */
+	0x0258,	0x0259, 298,	/* ɘ-ə Ǝ-Ə */
+	0x028a,	0x028b, 283,	/* ʊ-ʋ Ʊ-Ʋ */
+	0x03ad,	0x03af, 463,	/* έ-ί Έ-Ί */
+	0x03b1,	0x03c1, 468,	/* α-ρ Α-Ρ */
+	0x03c3,	0x03cb, 468,	/* σ-ϋ Σ-Ϋ */
+	0x03cd,	0x03ce, 437,	/* ύ-ώ Ύ-Ώ */
+	0x0430,	0x044f, 468,	/* а-я А-Я */
+	0x0451,	0x045c, 420,	/* ё-ќ Ё-Ќ */
+	0x045e,	0x045f, 420,	/* ў-џ Ў-Џ */
+	0x0561,	0x0586, 452,	/* ա-ֆ Ա-Ֆ */
+	0x1f00,	0x1f07, 508,	/* ἀ-ἇ Ἀ-Ἇ */
+	0x1f10,	0x1f15, 508,	/* ἐ-ἕ Ἐ-Ἕ */
+	0x1f20,	0x1f27, 508,	/* ἠ-ἧ Ἠ-Ἧ */
+	0x1f30,	0x1f37, 508,	/* ἰ-ἷ Ἰ-Ἷ */
+	0x1f40,	0x1f45, 508,	/* ὀ-ὅ Ὀ-Ὅ */
+	0x1f60,	0x1f67, 508,	/* ὠ-ὧ Ὠ-Ὧ */
+	0x1f70,	0x1f71, 574,	/* ὰ-ά Ὰ-Ά */
+	0x1f72,	0x1f75, 586,	/* ὲ-ή Ὲ-Ή */
+	0x1f76,	0x1f77, 600,	/* ὶ-ί Ὶ-Ί */
+	0x1f78,	0x1f79, 628,	/* ὸ-ό Ὸ-Ό */
+	0x1f7a,	0x1f7b, 612,	/* ὺ-ύ Ὺ-Ύ */
+	0x1f7c,	0x1f7d, 626,	/* ὼ-ώ Ὼ-Ώ */
+	0x1f80,	0x1f87, 508,	/* ᾀ-ᾇ ᾈ-ᾏ */
+	0x1f90,	0x1f97, 508,	/* ᾐ-ᾗ ᾘ-ᾟ */
+	0x1fa0,	0x1fa7, 508,	/* ᾠ-ᾧ ᾨ-ᾯ */
+	0x1fb0,	0x1fb1, 508,	/* ᾰ-ᾱ Ᾰ-Ᾱ */
+	0x1fd0,	0x1fd1, 508,	/* ῐ-ῑ Ῐ-Ῑ */
+	0x1fe0,	0x1fe1, 508,	/* ῠ-ῡ Ῠ-Ῡ */
+	0x2170,	0x217f, 484,	/* ⅰ-ⅿ Ⅰ-Ⅿ */
+	0x24d0,	0x24e9, 474,	/* ⓐ-ⓩ Ⓐ-Ⓩ */
+	0xff41,	0xff5a, 468,	/* a-z A-Z */
+};
+
+/*
+ * lower case singlets
+ *	2nd col is conversion excess 500
+ */
+static
+Rune	_toupper1[] =
+{
+	0x00ff, 621,	/* ÿ Ÿ */
+	0x0101, 499,	/* ā Ā */
+	0x0103, 499,	/* ă Ă */
+	0x0105, 499,	/* ą Ą */
+	0x0107, 499,	/* ć Ć */
+	0x0109, 499,	/* ĉ Ĉ */
+	0x010b, 499,	/* ċ Ċ */
+	0x010d, 499,	/* č Č */
+	0x010f, 499,	/* ď Ď */
+	0x0111, 499,	/* đ Đ */
+	0x0113, 499,	/* ē Ē */
+	0x0115, 499,	/* ĕ Ĕ */
+	0x0117, 499,	/* ė Ė */
+	0x0119, 499,	/* ę Ę */
+	0x011b, 499,	/* ě Ě */
+	0x011d, 499,	/* ĝ Ĝ */
+	0x011f, 499,	/* ğ Ğ */
+	0x0121, 499,	/* ġ Ġ */
+	0x0123, 499,	/* ģ Ģ */
+	0x0125, 499,	/* ĥ Ĥ */
+	0x0127, 499,	/* ħ Ħ */
+	0x0129, 499,	/* ĩ Ĩ */
+	0x012b, 499,	/* ī Ī */
+	0x012d, 499,	/* ĭ Ĭ */
+	0x012f, 499,	/* į Į */
+	0x0131, 268,	/* ı I */
+	0x0133, 499,	/* ij IJ */
+	0x0135, 499,	/* ĵ Ĵ */
+	0x0137, 499,	/* ķ Ķ */
+	0x013a, 499,	/* ĺ Ĺ */
+	0x013c, 499,	/* ļ Ļ */
+	0x013e, 499,	/* ľ Ľ */
+	0x0140, 499,	/* ŀ Ŀ */
+	0x0142, 499,	/* ł Ł */
+	0x0144, 499,	/* ń Ń */
+	0x0146, 499,	/* ņ Ņ */
+	0x0148, 499,	/* ň Ň */
+	0x014b, 499,	/* ŋ Ŋ */
+	0x014d, 499,	/* ō Ō */
+	0x014f, 499,	/* ŏ Ŏ */
+	0x0151, 499,	/* ő Ő */
+	0x0153, 499,	/* œ Œ */
+	0x0155, 499,	/* ŕ Ŕ */
+	0x0157, 499,	/* ŗ Ŗ */
+	0x0159, 499,	/* ř Ř */
+	0x015b, 499,	/* ś Ś */
+	0x015d, 499,	/* ŝ Ŝ */
+	0x015f, 499,	/* ş Ş */
+	0x0161, 499,	/* š Š */
+	0x0163, 499,	/* ţ Ţ */
+	0x0165, 499,	/* ť Ť */
+	0x0167, 499,	/* ŧ Ŧ */
+	0x0169, 499,	/* ũ Ũ */
+	0x016b, 499,	/* ū Ū */
+	0x016d, 499,	/* ŭ Ŭ */
+	0x016f, 499,	/* ů Ů */
+	0x0171, 499,	/* ű Ű */
+	0x0173, 499,	/* ų Ų */
+	0x0175, 499,	/* ŵ Ŵ */
+	0x0177, 499,	/* ŷ Ŷ */
+	0x017a, 499,	/* ź Ź */
+	0x017c, 499,	/* ż Ż */
+	0x017e, 499,	/* ž Ž */
+	0x017f, 200,	/* ſ S */
+	0x0183, 499,	/* ƃ Ƃ */
+	0x0185, 499,	/* ƅ Ƅ */
+	0x0188, 499,	/* ƈ Ƈ */
+	0x018c, 499,	/* ƌ Ƌ */
+	0x0192, 499,	/* ƒ Ƒ */
+	0x0199, 499,	/* ƙ Ƙ */
+	0x01a1, 499,	/* ơ Ơ */
+	0x01a3, 499,	/* ƣ Ƣ */
+	0x01a5, 499,	/* ƥ Ƥ */
+	0x01a8, 499,	/* ƨ Ƨ */
+	0x01ad, 499,	/* ƭ Ƭ */
+	0x01b0, 499,	/* ư Ư */
+	0x01b4, 499,	/* ƴ Ƴ */
+	0x01b6, 499,	/* ƶ Ƶ */
+	0x01b9, 499,	/* ƹ Ƹ */
+	0x01bd, 499,	/* ƽ Ƽ */
+	0x01c5, 499,	/* Dž DŽ */
+	0x01c6, 498,	/* dž DŽ */
+	0x01c8, 499,	/* Lj LJ */
+	0x01c9, 498,	/* lj LJ */
+	0x01cb, 499,	/* Nj NJ */
+	0x01cc, 498,	/* nj NJ */
+	0x01ce, 499,	/* ǎ Ǎ */
+	0x01d0, 499,	/* ǐ Ǐ */
+	0x01d2, 499,	/* ǒ Ǒ */
+	0x01d4, 499,	/* ǔ Ǔ */
+	0x01d6, 499,	/* ǖ Ǖ */
+	0x01d8, 499,	/* ǘ Ǘ */
+	0x01da, 499,	/* ǚ Ǚ */
+	0x01dc, 499,	/* ǜ Ǜ */
+	0x01df, 499,	/* ǟ Ǟ */
+	0x01e1, 499,	/* ǡ Ǡ */
+	0x01e3, 499,	/* ǣ Ǣ */
+	0x01e5, 499,	/* ǥ Ǥ */
+	0x01e7, 499,	/* ǧ Ǧ */
+	0x01e9, 499,	/* ǩ Ǩ */
+	0x01eb, 499,	/* ǫ Ǫ */
+	0x01ed, 499,	/* ǭ Ǭ */
+	0x01ef, 499,	/* ǯ Ǯ */
+	0x01f2, 499,	/* Dz DZ */
+	0x01f3, 498,	/* dz DZ */
+	0x01f5, 499,	/* ǵ Ǵ */
+	0x01fb, 499,	/* ǻ Ǻ */
+	0x01fd, 499,	/* ǽ Ǽ */
+	0x01ff, 499,	/* ǿ Ǿ */
+	0x0201, 499,	/* ȁ Ȁ */
+	0x0203, 499,	/* ȃ Ȃ */
+	0x0205, 499,	/* ȅ Ȅ */
+	0x0207, 499,	/* ȇ Ȇ */
+	0x0209, 499,	/* ȉ Ȉ */
+	0x020b, 499,	/* ȋ Ȋ */
+	0x020d, 499,	/* ȍ Ȍ */
+	0x020f, 499,	/* ȏ Ȏ */
+	0x0211, 499,	/* ȑ Ȑ */
+	0x0213, 499,	/* ȓ Ȓ */
+	0x0215, 499,	/* ȕ Ȕ */
+	0x0217, 499,	/* ȗ Ȗ */
+	0x0253, 290,	/* ɓ Ɓ */
+	0x0254, 294,	/* ɔ Ɔ */
+	0x025b, 297,	/* ɛ Ɛ */
+	0x0260, 295,	/* ɠ Ɠ */
+	0x0263, 293,	/* ɣ Ɣ */
+	0x0268, 291,	/* ɨ Ɨ */
+	0x0269, 289,	/* ɩ Ɩ */
+	0x026f, 289,	/* ɯ Ɯ */
+	0x0272, 287,	/* ɲ Ɲ */
+	0x0283, 282,	/* ʃ Ʃ */
+	0x0288, 282,	/* ʈ Ʈ */
+	0x0292, 281,	/* ʒ Ʒ */
+	0x03ac, 462,	/* ά Ά */
+	0x03cc, 436,	/* ό Ό */
+	0x03d0, 438,	/* ϐ Β */
+	0x03d1, 443,	/* ϑ Θ */
+	0x03d5, 453,	/* ϕ Φ */
+	0x03d6, 446,	/* ϖ Π */
+	0x03e3, 499,	/* ϣ Ϣ */
+	0x03e5, 499,	/* ϥ Ϥ */
+	0x03e7, 499,	/* ϧ Ϧ */
+	0x03e9, 499,	/* ϩ Ϩ */
+	0x03eb, 499,	/* ϫ Ϫ */
+	0x03ed, 499,	/* ϭ Ϭ */
+	0x03ef, 499,	/* ϯ Ϯ */
+	0x03f0, 414,	/* ϰ Κ */
+	0x03f1, 420,	/* ϱ Ρ */
+	0x0461, 499,	/* ѡ Ѡ */
+	0x0463, 499,	/* ѣ Ѣ */
+	0x0465, 499,	/* ѥ Ѥ */
+	0x0467, 499,	/* ѧ Ѧ */
+	0x0469, 499,	/* ѩ Ѩ */
+	0x046b, 499,	/* ѫ Ѫ */
+	0x046d, 499,	/* ѭ Ѭ */
+	0x046f, 499,	/* ѯ Ѯ */
+	0x0471, 499,	/* ѱ Ѱ */
+	0x0473, 499,	/* ѳ Ѳ */
+	0x0475, 499,	/* ѵ Ѵ */
+	0x0477, 499,	/* ѷ Ѷ */
+	0x0479, 499,	/* ѹ Ѹ */
+	0x047b, 499,	/* ѻ Ѻ */
+	0x047d, 499,	/* ѽ Ѽ */
+	0x047f, 499,	/* ѿ Ѿ */
+	0x0481, 499,	/* ҁ Ҁ */
+	0x0491, 499,	/* ґ Ґ */
+	0x0493, 499,	/* ғ Ғ */
+	0x0495, 499,	/* ҕ Ҕ */
+	0x0497, 499,	/* җ Җ */
+	0x0499, 499,	/* ҙ Ҙ */
+	0x049b, 499,	/* қ Қ */
+	0x049d, 499,	/* ҝ Ҝ */
+	0x049f, 499,	/* ҟ Ҟ */
+	0x04a1, 499,	/* ҡ Ҡ */
+	0x04a3, 499,	/* ң Ң */
+	0x04a5, 499,	/* ҥ Ҥ */
+	0x04a7, 499,	/* ҧ Ҧ */
+	0x04a9, 499,	/* ҩ Ҩ */
+	0x04ab, 499,	/* ҫ Ҫ */
+	0x04ad, 499,	/* ҭ Ҭ */
+	0x04af, 499,	/* ү Ү */
+	0x04b1, 499,	/* ұ Ұ */
+	0x04b3, 499,	/* ҳ Ҳ */
+	0x04b5, 499,	/* ҵ Ҵ */
+	0x04b7, 499,	/* ҷ Ҷ */
+	0x04b9, 499,	/* ҹ Ҹ */
+	0x04bb, 499,	/* һ Һ */
+	0x04bd, 499,	/* ҽ Ҽ */
+	0x04bf, 499,	/* ҿ Ҿ */
+	0x04c2, 499,	/* ӂ Ӂ */
+	0x04c4, 499,	/* ӄ Ӄ */
+	0x04c8, 499,	/* ӈ Ӈ */
+	0x04cc, 499,	/* ӌ Ӌ */
+	0x04d1, 499,	/* ӑ Ӑ */
+	0x04d3, 499,	/* ӓ Ӓ */
+	0x04d5, 499,	/* ӕ Ӕ */
+	0x04d7, 499,	/* ӗ Ӗ */
+	0x04d9, 499,	/* ә Ә */
+	0x04db, 499,	/* ӛ Ӛ */
+	0x04dd, 499,	/* ӝ Ӝ */
+	0x04df, 499,	/* ӟ Ӟ */
+	0x04e1, 499,	/* ӡ Ӡ */
+	0x04e3, 499,	/* ӣ Ӣ */
+	0x04e5, 499,	/* ӥ Ӥ */
+	0x04e7, 499,	/* ӧ Ӧ */
+	0x04e9, 499,	/* ө Ө */
+	0x04eb, 499,	/* ӫ Ӫ */
+	0x04ef, 499,	/* ӯ Ӯ */
+	0x04f1, 499,	/* ӱ Ӱ */
+	0x04f3, 499,	/* ӳ Ӳ */
+	0x04f5, 499,	/* ӵ Ӵ */
+	0x04f9, 499,	/* ӹ Ӹ */
+	0x1e01, 499,	/* ḁ Ḁ */
+	0x1e03, 499,	/* ḃ Ḃ */
+	0x1e05, 499,	/* ḅ Ḅ */
+	0x1e07, 499,	/* ḇ Ḇ */
+	0x1e09, 499,	/* ḉ Ḉ */
+	0x1e0b, 499,	/* ḋ Ḋ */
+	0x1e0d, 499,	/* ḍ Ḍ */
+	0x1e0f, 499,	/* ḏ Ḏ */
+	0x1e11, 499,	/* ḑ Ḑ */
+	0x1e13, 499,	/* ḓ Ḓ */
+	0x1e15, 499,	/* ḕ Ḕ */
+	0x1e17, 499,	/* ḗ Ḗ */
+	0x1e19, 499,	/* ḙ Ḙ */
+	0x1e1b, 499,	/* ḛ Ḛ */
+	0x1e1d, 499,	/* ḝ Ḝ */
+	0x1e1f, 499,	/* ḟ Ḟ */
+	0x1e21, 499,	/* ḡ Ḡ */
+	0x1e23, 499,	/* ḣ Ḣ */
+	0x1e25, 499,	/* ḥ Ḥ */
+	0x1e27, 499,	/* ḧ Ḧ */
+	0x1e29, 499,	/* ḩ Ḩ */
+	0x1e2b, 499,	/* ḫ Ḫ */
+	0x1e2d, 499,	/* ḭ Ḭ */
+	0x1e2f, 499,	/* ḯ Ḯ */
+	0x1e31, 499,	/* ḱ Ḱ */
+	0x1e33, 499,	/* ḳ Ḳ */
+	0x1e35, 499,	/* ḵ Ḵ */
+	0x1e37, 499,	/* ḷ Ḷ */
+	0x1e39, 499,	/* ḹ Ḹ */
+	0x1e3b, 499,	/* ḻ Ḻ */
+	0x1e3d, 499,	/* ḽ Ḽ */
+	0x1e3f, 499,	/* ḿ Ḿ */
+	0x1e41, 499,	/* ṁ Ṁ */
+	0x1e43, 499,	/* ṃ Ṃ */
+	0x1e45, 499,	/* ṅ Ṅ */
+	0x1e47, 499,	/* ṇ Ṇ */
+	0x1e49, 499,	/* ṉ Ṉ */
+	0x1e4b, 499,	/* ṋ Ṋ */
+	0x1e4d, 499,	/* ṍ Ṍ */
+	0x1e4f, 499,	/* ṏ Ṏ */
+	0x1e51, 499,	/* ṑ Ṑ */
+	0x1e53, 499,	/* ṓ Ṓ */
+	0x1e55, 499,	/* ṕ Ṕ */
+	0x1e57, 499,	/* ṗ Ṗ */
+	0x1e59, 499,	/* ṙ Ṙ */
+	0x1e5b, 499,	/* ṛ Ṛ */
+	0x1e5d, 499,	/* ṝ Ṝ */
+	0x1e5f, 499,	/* ṟ Ṟ */
+	0x1e61, 499,	/* ṡ Ṡ */
+	0x1e63, 499,	/* ṣ Ṣ */
+	0x1e65, 499,	/* ṥ Ṥ */
+	0x1e67, 499,	/* ṧ Ṧ */
+	0x1e69, 499,	/* ṩ Ṩ */
+	0x1e6b, 499,	/* ṫ Ṫ */
+	0x1e6d, 499,	/* ṭ Ṭ */
+	0x1e6f, 499,	/* ṯ Ṯ */
+	0x1e71, 499,	/* ṱ Ṱ */
+	0x1e73, 499,	/* ṳ Ṳ */
+	0x1e75, 499,	/* ṵ Ṵ */
+	0x1e77, 499,	/* ṷ Ṷ */
+	0x1e79, 499,	/* ṹ Ṹ */
+	0x1e7b, 499,	/* ṻ Ṻ */
+	0x1e7d, 499,	/* ṽ Ṽ */
+	0x1e7f, 499,	/* ṿ Ṿ */
+	0x1e81, 499,	/* ẁ Ẁ */
+	0x1e83, 499,	/* ẃ Ẃ */
+	0x1e85, 499,	/* ẅ Ẅ */
+	0x1e87, 499,	/* ẇ Ẇ */
+	0x1e89, 499,	/* ẉ Ẉ */
+	0x1e8b, 499,	/* ẋ Ẋ */
+	0x1e8d, 499,	/* ẍ Ẍ */
+	0x1e8f, 499,	/* ẏ Ẏ */
+	0x1e91, 499,	/* ẑ Ẑ */
+	0x1e93, 499,	/* ẓ Ẓ */
+	0x1e95, 499,	/* ẕ Ẕ */
+	0x1ea1, 499,	/* ạ Ạ */
+	0x1ea3, 499,	/* ả Ả */
+	0x1ea5, 499,	/* ấ Ấ */
+	0x1ea7, 499,	/* ầ Ầ */
+	0x1ea9, 499,	/* ẩ Ẩ */
+	0x1eab, 499,	/* ẫ Ẫ */
+	0x1ead, 499,	/* ậ Ậ */
+	0x1eaf, 499,	/* ắ Ắ */
+	0x1eb1, 499,	/* ằ Ằ */
+	0x1eb3, 499,	/* ẳ Ẳ */
+	0x1eb5, 499,	/* ẵ Ẵ */
+	0x1eb7, 499,	/* ặ Ặ */
+	0x1eb9, 499,	/* ẹ Ẹ */
+	0x1ebb, 499,	/* ẻ Ẻ */
+	0x1ebd, 499,	/* ẽ Ẽ */
+	0x1ebf, 499,	/* ế Ế */
+	0x1ec1, 499,	/* ề Ề */
+	0x1ec3, 499,	/* ể Ể */
+	0x1ec5, 499,	/* ễ Ễ */
+	0x1ec7, 499,	/* ệ Ệ */
+	0x1ec9, 499,	/* ỉ Ỉ */
+	0x1ecb, 499,	/* ị Ị */
+	0x1ecd, 499,	/* ọ Ọ */
+	0x1ecf, 499,	/* ỏ Ỏ */
+	0x1ed1, 499,	/* ố Ố */
+	0x1ed3, 499,	/* ồ Ồ */
+	0x1ed5, 499,	/* ổ Ổ */
+	0x1ed7, 499,	/* ỗ Ỗ */
+	0x1ed9, 499,	/* ộ Ộ */
+	0x1edb, 499,	/* ớ Ớ */
+	0x1edd, 499,	/* ờ Ờ */
+	0x1edf, 499,	/* ở Ở */
+	0x1ee1, 499,	/* ỡ Ỡ */
+	0x1ee3, 499,	/* ợ Ợ */
+	0x1ee5, 499,	/* ụ Ụ */
+	0x1ee7, 499,	/* ủ Ủ */
+	0x1ee9, 499,	/* ứ Ứ */
+	0x1eeb, 499,	/* ừ Ừ */
+	0x1eed, 499,	/* ử Ử */
+	0x1eef, 499,	/* ữ Ữ */
+	0x1ef1, 499,	/* ự Ự */
+	0x1ef3, 499,	/* ỳ Ỳ */
+	0x1ef5, 499,	/* ỵ Ỵ */
+	0x1ef7, 499,	/* ỷ Ỷ */
+	0x1ef9, 499,	/* ỹ Ỹ */
+	0x1f51, 508,	/* ὑ Ὑ */
+	0x1f53, 508,	/* ὓ Ὓ */
+	0x1f55, 508,	/* ὕ Ὕ */
+	0x1f57, 508,	/* ὗ Ὗ */
+	0x1fb3, 509,	/* ᾳ ᾼ */
+	0x1fc3, 509,	/* ῃ ῌ */
+	0x1fe5, 507,	/* ῥ Ῥ */
+	0x1ff3, 509,	/* ῳ ῼ */
+};
+
+static Rune __isdigitr[] = {
+	0x0030, 0x0039,
+	0x0660, 0x0669,
+	0x06f0, 0x06f9,
+	0x07c0, 0x07c9,
+	0x0966, 0x096f,
+	0x09e6, 0x09ef,
+	0x0a66, 0x0a6f,
+	0x0ae6, 0x0aef,
+	0x0b66, 0x0b6f,
+	0x0be6, 0x0bef,
+	0x0c66, 0x0c6f,
+	0x0ce6, 0x0cef,
+	0x0d66, 0x0d6f,
+	0x0e50, 0x0e59,
+	0x0ed0, 0x0ed9,
+	0x0f20, 0x0f29,
+	0x1040, 0x1049,
+	0x17e0, 0x17e9,
+	0x1810, 0x1819,
+	0x1946, 0x194f,
+	0x19d0, 0x19d9,
+	0x1b50, 0x1b59,
+	0xff10, 0xff19,
+	0x104a0, 0x104a9,
+	0x1d7ce, 0x1d7ff,
+};
+
+/*
+ * upper case ranges
+ *	3rd col is conversion excess 500
+ */
+static
+Rune	_tolower2[] =
+{
+	0x0041,	0x005a, 532,	/* A-Z a-z */
+	0x00c0,	0x00d6, 532,	/* À-Ö à-ö */
+	0x00d8,	0x00de, 532,	/* Ø-Þ ø-þ */
+	0x0189,	0x018a, 705,	/* Ɖ-Ɗ ɖ-ɗ */
+	0x018e,	0x018f, 702,	/* Ǝ-Ə ɘ-ə */
+	0x01b1,	0x01b2, 717,	/* Ʊ-Ʋ ʊ-ʋ */
+	0x0388,	0x038a, 537,	/* Έ-Ί έ-ί */
+	0x038e,	0x038f, 563,	/* Ύ-Ώ ύ-ώ */
+	0x0391,	0x03a1, 532,	/* Α-Ρ α-ρ */
+	0x03a3,	0x03ab, 532,	/* Σ-Ϋ σ-ϋ */
+	0x0401,	0x040c, 580,	/* Ё-Ќ ё-ќ */
+	0x040e,	0x040f, 580,	/* Ў-Џ ў-џ */
+	0x0410,	0x042f, 532,	/* А-Я а-я */
+	0x0531,	0x0556, 548,	/* Ա-Ֆ ա-ֆ */
+	0x10a0,	0x10c5, 548,	/* Ⴀ-Ⴥ ა-ჵ */
+	0x1f08,	0x1f0f, 492,	/* Ἀ-Ἇ ἀ-ἇ */
+	0x1f18,	0x1f1d, 492,	/* Ἐ-Ἕ ἐ-ἕ */
+	0x1f28,	0x1f2f, 492,	/* Ἠ-Ἧ ἠ-ἧ */
+	0x1f38,	0x1f3f, 492,	/* Ἰ-Ἷ ἰ-ἷ */
+	0x1f48,	0x1f4d, 492,	/* Ὀ-Ὅ ὀ-ὅ */
+	0x1f68,	0x1f6f, 492,	/* Ὠ-Ὧ ὠ-ὧ */
+	0x1f88,	0x1f8f, 492,	/* ᾈ-ᾏ ᾀ-ᾇ */
+	0x1f98,	0x1f9f, 492,	/* ᾘ-ᾟ ᾐ-ᾗ */
+	0x1fa8,	0x1faf, 492,	/* ᾨ-ᾯ ᾠ-ᾧ */
+	0x1fb8,	0x1fb9, 492,	/* Ᾰ-Ᾱ ᾰ-ᾱ */
+	0x1fba,	0x1fbb, 426,	/* Ὰ-Ά ὰ-ά */
+	0x1fc8,	0x1fcb, 414,	/* Ὲ-Ή ὲ-ή */
+	0x1fd8,	0x1fd9, 492,	/* Ῐ-Ῑ ῐ-ῑ */
+	0x1fda,	0x1fdb, 400,	/* Ὶ-Ί ὶ-ί */
+	0x1fe8,	0x1fe9, 492,	/* Ῠ-Ῡ ῠ-ῡ */
+	0x1fea,	0x1feb, 388,	/* Ὺ-Ύ ὺ-ύ */
+	0x1ff8,	0x1ff9, 372,	/* Ὸ-Ό ὸ-ό */
+	0x1ffa,	0x1ffb, 374,	/* Ὼ-Ώ ὼ-ώ */
+	0x2160,	0x216f, 516,	/* Ⅰ-Ⅿ ⅰ-ⅿ */
+	0x24b6,	0x24cf, 526,	/* Ⓐ-Ⓩ ⓐ-ⓩ */
+	0xff21,	0xff3a, 532,	/* A-Z a-z */
+};
+
+/*
+ * upper case singlets
+ *	2nd col is conversion excess 500
+ */
+static
+Rune	_tolower1[] =
+{
+	0x0100, 501,	/* Ā ā */
+	0x0102, 501,	/* Ă ă */
+	0x0104, 501,	/* Ą ą */
+	0x0106, 501,	/* Ć ć */
+	0x0108, 501,	/* Ĉ ĉ */
+	0x010a, 501,	/* Ċ ċ */
+	0x010c, 501,	/* Č č */
+	0x010e, 501,	/* Ď ď */
+	0x0110, 501,	/* Đ đ */
+	0x0112, 501,	/* Ē ē */
+	0x0114, 501,	/* Ĕ ĕ */
+	0x0116, 501,	/* Ė ė */
+	0x0118, 501,	/* Ę ę */
+	0x011a, 501,	/* Ě ě */
+	0x011c, 501,	/* Ĝ ĝ */
+	0x011e, 501,	/* Ğ ğ */
+	0x0120, 501,	/* Ġ ġ */
+	0x0122, 501,	/* Ģ ģ */
+	0x0124, 501,	/* Ĥ ĥ */
+	0x0126, 501,	/* Ħ ħ */
+	0x0128, 501,	/* Ĩ ĩ */
+	0x012a, 501,	/* Ī ī */
+	0x012c, 501,	/* Ĭ ĭ */
+	0x012e, 501,	/* Į į */
+	0x0130, 301,	/* İ i */
+	0x0132, 501,	/* IJ ij */
+	0x0134, 501,	/* Ĵ ĵ */
+	0x0136, 501,	/* Ķ ķ */
+	0x0139, 501,	/* Ĺ ĺ */
+	0x013b, 501,	/* Ļ ļ */
+	0x013d, 501,	/* Ľ ľ */
+	0x013f, 501,	/* Ŀ ŀ */
+	0x0141, 501,	/* Ł ł */
+	0x0143, 501,	/* Ń ń */
+	0x0145, 501,	/* Ņ ņ */
+	0x0147, 501,	/* Ň ň */
+	0x014a, 501,	/* Ŋ ŋ */
+	0x014c, 501,	/* Ō ō */
+	0x014e, 501,	/* Ŏ ŏ */
+	0x0150, 501,	/* Ő ő */
+	0x0152, 501,	/* Œ œ */
+	0x0154, 501,	/* Ŕ ŕ */
+	0x0156, 501,	/* Ŗ ŗ */
+	0x0158, 501,	/* Ř ř */
+	0x015a, 501,	/* Ś ś */
+	0x015c, 501,	/* Ŝ ŝ */
+	0x015e, 501,	/* Ş ş */
+	0x0160, 501,	/* Š š */
+	0x0162, 501,	/* Ţ ţ */
+	0x0164, 501,	/* Ť ť */
+	0x0166, 501,	/* Ŧ ŧ */
+	0x0168, 501,	/* Ũ ũ */
+	0x016a, 501,	/* Ū ū */
+	0x016c, 501,	/* Ŭ ŭ */
+	0x016e, 501,	/* Ů ů */
+	0x0170, 501,	/* Ű ű */
+	0x0172, 501,	/* Ų ų */
+	0x0174, 501,	/* Ŵ ŵ */
+	0x0176, 501,	/* Ŷ ŷ */
+	0x0178, 379,	/* Ÿ ÿ */
+	0x0179, 501,	/* Ź ź */
+	0x017b, 501,	/* Ż ż */
+	0x017d, 501,	/* Ž ž */
+	0x0181, 710,	/* Ɓ ɓ */
+	0x0182, 501,	/* Ƃ ƃ */
+	0x0184, 501,	/* Ƅ ƅ */
+	0x0186, 706,	/* Ɔ ɔ */
+	0x0187, 501,	/* Ƈ ƈ */
+	0x018b, 501,	/* Ƌ ƌ */
+	0x0190, 703,	/* Ɛ ɛ */
+	0x0191, 501,	/* Ƒ ƒ */
+	0x0193, 705,	/* Ɠ ɠ */
+	0x0194, 707,	/* Ɣ ɣ */
+	0x0196, 711,	/* Ɩ ɩ */
+	0x0197, 709,	/* Ɨ ɨ */
+	0x0198, 501,	/* Ƙ ƙ */
+	0x019c, 711,	/* Ɯ ɯ */
+	0x019d, 713,	/* Ɲ ɲ */
+	0x01a0, 501,	/* Ơ ơ */
+	0x01a2, 501,	/* Ƣ ƣ */
+	0x01a4, 501,	/* Ƥ ƥ */
+	0x01a7, 501,	/* Ƨ ƨ */
+	0x01a9, 718,	/* Ʃ ʃ */
+	0x01ac, 501,	/* Ƭ ƭ */
+	0x01ae, 718,	/* Ʈ ʈ */
+	0x01af, 501,	/* Ư ư */
+	0x01b3, 501,	/* Ƴ ƴ */
+	0x01b5, 501,	/* Ƶ ƶ */
+	0x01b7, 719,	/* Ʒ ʒ */
+	0x01b8, 501,	/* Ƹ ƹ */
+	0x01bc, 501,	/* Ƽ ƽ */
+	0x01c4, 502,	/* DŽ dž */
+	0x01c5, 501,	/* Dž dž */
+	0x01c7, 502,	/* LJ lj */
+	0x01c8, 501,	/* Lj lj */
+	0x01ca, 502,	/* NJ nj */
+	0x01cb, 501,	/* Nj nj */
+	0x01cd, 501,	/* Ǎ ǎ */
+	0x01cf, 501,	/* Ǐ ǐ */
+	0x01d1, 501,	/* Ǒ ǒ */
+	0x01d3, 501,	/* Ǔ ǔ */
+	0x01d5, 501,	/* Ǖ ǖ */
+	0x01d7, 501,	/* Ǘ ǘ */
+	0x01d9, 501,	/* Ǚ ǚ */
+	0x01db, 501,	/* Ǜ ǜ */
+	0x01de, 501,	/* Ǟ ǟ */
+	0x01e0, 501,	/* Ǡ ǡ */
+	0x01e2, 501,	/* Ǣ ǣ */
+	0x01e4, 501,	/* Ǥ ǥ */
+	0x01e6, 501,	/* Ǧ ǧ */
+	0x01e8, 501,	/* Ǩ ǩ */
+	0x01ea, 501,	/* Ǫ ǫ */
+	0x01ec, 501,	/* Ǭ ǭ */
+	0x01ee, 501,	/* Ǯ ǯ */
+	0x01f1, 502,	/* DZ dz */
+	0x01f2, 501,	/* Dz dz */
+	0x01f4, 501,	/* Ǵ ǵ */
+	0x01fa, 501,	/* Ǻ ǻ */
+	0x01fc, 501,	/* Ǽ ǽ */
+	0x01fe, 501,	/* Ǿ ǿ */
+	0x0200, 501,	/* Ȁ ȁ */
+	0x0202, 501,	/* Ȃ ȃ */
+	0x0204, 501,	/* Ȅ ȅ */
+	0x0206, 501,	/* Ȇ ȇ */
+	0x0208, 501,	/* Ȉ ȉ */
+	0x020a, 501,	/* Ȋ ȋ */
+	0x020c, 501,	/* Ȍ ȍ */
+	0x020e, 501,	/* Ȏ ȏ */
+	0x0210, 501,	/* Ȑ ȑ */
+	0x0212, 501,	/* Ȓ ȓ */
+	0x0214, 501,	/* Ȕ ȕ */
+	0x0216, 501,	/* Ȗ ȗ */
+	0x0386, 538,	/* Ά ά */
+	0x038c, 564,	/* Ό ό */
+	0x03e2, 501,	/* Ϣ ϣ */
+	0x03e4, 501,	/* Ϥ ϥ */
+	0x03e6, 501,	/* Ϧ ϧ */
+	0x03e8, 501,	/* Ϩ ϩ */
+	0x03ea, 501,	/* Ϫ ϫ */
+	0x03ec, 501,	/* Ϭ ϭ */
+	0x03ee, 501,	/* Ϯ ϯ */
+	0x0460, 501,	/* Ѡ ѡ */
+	0x0462, 501,	/* Ѣ ѣ */
+	0x0464, 501,	/* Ѥ ѥ */
+	0x0466, 501,	/* Ѧ ѧ */
+	0x0468, 501,	/* Ѩ ѩ */
+	0x046a, 501,	/* Ѫ ѫ */
+	0x046c, 501,	/* Ѭ ѭ */
+	0x046e, 501,	/* Ѯ ѯ */
+	0x0470, 501,	/* Ѱ ѱ */
+	0x0472, 501,	/* Ѳ ѳ */
+	0x0474, 501,	/* Ѵ ѵ */
+	0x0476, 501,	/* Ѷ ѷ */
+	0x0478, 501,	/* Ѹ ѹ */
+	0x047a, 501,	/* Ѻ ѻ */
+	0x047c, 501,	/* Ѽ ѽ */
+	0x047e, 501,	/* Ѿ ѿ */
+	0x0480, 501,	/* Ҁ ҁ */
+	0x0490, 501,	/* Ґ ґ */
+	0x0492, 501,	/* Ғ ғ */
+	0x0494, 501,	/* Ҕ ҕ */
+	0x0496, 501,	/* Җ җ */
+	0x0498, 501,	/* Ҙ ҙ */
+	0x049a, 501,	/* Қ қ */
+	0x049c, 501,	/* Ҝ ҝ */
+	0x049e, 501,	/* Ҟ ҟ */
+	0x04a0, 501,	/* Ҡ ҡ */
+	0x04a2, 501,	/* Ң ң */
+	0x04a4, 501,	/* Ҥ ҥ */
+	0x04a6, 501,	/* Ҧ ҧ */
+	0x04a8, 501,	/* Ҩ ҩ */
+	0x04aa, 501,	/* Ҫ ҫ */
+	0x04ac, 501,	/* Ҭ ҭ */
+	0x04ae, 501,	/* Ү ү */
+	0x04b0, 501,	/* Ұ ұ */
+	0x04b2, 501,	/* Ҳ ҳ */
+	0x04b4, 501,	/* Ҵ ҵ */
+	0x04b6, 501,	/* Ҷ ҷ */
+	0x04b8, 501,	/* Ҹ ҹ */
+	0x04ba, 501,	/* Һ һ */
+	0x04bc, 501,	/* Ҽ ҽ */
+	0x04be, 501,	/* Ҿ ҿ */
+	0x04c1, 501,	/* Ӂ ӂ */
+	0x04c3, 501,	/* Ӄ ӄ */
+	0x04c7, 501,	/* Ӈ ӈ */
+	0x04cb, 501,	/* Ӌ ӌ */
+	0x04d0, 501,	/* Ӑ ӑ */
+	0x04d2, 501,	/* Ӓ ӓ */
+	0x04d4, 501,	/* Ӕ ӕ */
+	0x04d6, 501,	/* Ӗ ӗ */
+	0x04d8, 501,	/* Ә ә */
+	0x04da, 501,	/* Ӛ ӛ */
+	0x04dc, 501,	/* Ӝ ӝ */
+	0x04de, 501,	/* Ӟ ӟ */
+	0x04e0, 501,	/* Ӡ ӡ */
+	0x04e2, 501,	/* Ӣ ӣ */
+	0x04e4, 501,	/* Ӥ ӥ */
+	0x04e6, 501,	/* Ӧ ӧ */
+	0x04e8, 501,	/* Ө ө */
+	0x04ea, 501,	/* Ӫ ӫ */
+	0x04ee, 501,	/* Ӯ ӯ */
+	0x04f0, 501,	/* Ӱ ӱ */
+	0x04f2, 501,	/* Ӳ ӳ */
+	0x04f4, 501,	/* Ӵ ӵ */
+	0x04f8, 501,	/* Ӹ ӹ */
+	0x1e00, 501,	/* Ḁ ḁ */
+	0x1e02, 501,	/* Ḃ ḃ */
+	0x1e04, 501,	/* Ḅ ḅ */
+	0x1e06, 501,	/* Ḇ ḇ */
+	0x1e08, 501,	/* Ḉ ḉ */
+	0x1e0a, 501,	/* Ḋ ḋ */
+	0x1e0c, 501,	/* Ḍ ḍ */
+	0x1e0e, 501,	/* Ḏ ḏ */
+	0x1e10, 501,	/* Ḑ ḑ */
+	0x1e12, 501,	/* Ḓ ḓ */
+	0x1e14, 501,	/* Ḕ ḕ */
+	0x1e16, 501,	/* Ḗ ḗ */
+	0x1e18, 501,	/* Ḙ ḙ */
+	0x1e1a, 501,	/* Ḛ ḛ */
+	0x1e1c, 501,	/* Ḝ ḝ */
+	0x1e1e, 501,	/* Ḟ ḟ */
+	0x1e20, 501,	/* Ḡ ḡ */
+	0x1e22, 501,	/* Ḣ ḣ */
+	0x1e24, 501,	/* Ḥ ḥ */
+	0x1e26, 501,	/* Ḧ ḧ */
+	0x1e28, 501,	/* Ḩ ḩ */
+	0x1e2a, 501,	/* Ḫ ḫ */
+	0x1e2c, 501,	/* Ḭ ḭ */
+	0x1e2e, 501,	/* Ḯ ḯ */
+	0x1e30, 501,	/* Ḱ ḱ */
+	0x1e32, 501,	/* Ḳ ḳ */
+	0x1e34, 501,	/* Ḵ ḵ */
+	0x1e36, 501,	/* Ḷ ḷ */
+	0x1e38, 501,	/* Ḹ ḹ */
+	0x1e3a, 501,	/* Ḻ ḻ */
+	0x1e3c, 501,	/* Ḽ ḽ */
+	0x1e3e, 501,	/* Ḿ ḿ */
+	0x1e40, 501,	/* Ṁ ṁ */
+	0x1e42, 501,	/* Ṃ ṃ */
+	0x1e44, 501,	/* Ṅ ṅ */
+	0x1e46, 501,	/* Ṇ ṇ */
+	0x1e48, 501,	/* Ṉ ṉ */
+	0x1e4a, 501,	/* Ṋ ṋ */
+	0x1e4c, 501,	/* Ṍ ṍ */
+	0x1e4e, 501,	/* Ṏ ṏ */
+	0x1e50, 501,	/* Ṑ ṑ */
+	0x1e52, 501,	/* Ṓ ṓ */
+	0x1e54, 501,	/* Ṕ ṕ */
+	0x1e56, 501,	/* Ṗ ṗ */
+	0x1e58, 501,	/* Ṙ ṙ */
+	0x1e5a, 501,	/* Ṛ ṛ */
+	0x1e5c, 501,	/* Ṝ ṝ */
+	0x1e5e, 501,	/* Ṟ ṟ */
+	0x1e60, 501,	/* Ṡ ṡ */
+	0x1e62, 501,	/* Ṣ ṣ */
+	0x1e64, 501,	/* Ṥ ṥ */
+	0x1e66, 501,	/* Ṧ ṧ */
+	0x1e68, 501,	/* Ṩ ṩ */
+	0x1e6a, 501,	/* Ṫ ṫ */
+	0x1e6c, 501,	/* Ṭ ṭ */
+	0x1e6e, 501,	/* Ṯ ṯ */
+	0x1e70, 501,	/* Ṱ ṱ */
+	0x1e72, 501,	/* Ṳ ṳ */
+	0x1e74, 501,	/* Ṵ ṵ */
+	0x1e76, 501,	/* Ṷ ṷ */
+	0x1e78, 501,	/* Ṹ ṹ */
+	0x1e7a, 501,	/* Ṻ ṻ */
+	0x1e7c, 501,	/* Ṽ ṽ */
+	0x1e7e, 501,	/* Ṿ ṿ */
+	0x1e80, 501,	/* Ẁ ẁ */
+	0x1e82, 501,	/* Ẃ ẃ */
+	0x1e84, 501,	/* Ẅ ẅ */
+	0x1e86, 501,	/* Ẇ ẇ */
+	0x1e88, 501,	/* Ẉ ẉ */
+	0x1e8a, 501,	/* Ẋ ẋ */
+	0x1e8c, 501,	/* Ẍ ẍ */
+	0x1e8e, 501,	/* Ẏ ẏ */
+	0x1e90, 501,	/* Ẑ ẑ */
+	0x1e92, 501,	/* Ẓ ẓ */
+	0x1e94, 501,	/* Ẕ ẕ */
+	0x1ea0, 501,	/* Ạ ạ */
+	0x1ea2, 501,	/* Ả ả */
+	0x1ea4, 501,	/* Ấ ấ */
+	0x1ea6, 501,	/* Ầ ầ */
+	0x1ea8, 501,	/* Ẩ ẩ */
+	0x1eaa, 501,	/* Ẫ ẫ */
+	0x1eac, 501,	/* Ậ ậ */
+	0x1eae, 501,	/* Ắ ắ */
+	0x1eb0, 501,	/* Ằ ằ */
+	0x1eb2, 501,	/* Ẳ ẳ */
+	0x1eb4, 501,	/* Ẵ ẵ */
+	0x1eb6, 501,	/* Ặ ặ */
+	0x1eb8, 501,	/* Ẹ ẹ */
+	0x1eba, 501,	/* Ẻ ẻ */
+	0x1ebc, 501,	/* Ẽ ẽ */
+	0x1ebe, 501,	/* Ế ế */
+	0x1ec0, 501,	/* Ề ề */
+	0x1ec2, 501,	/* Ể ể */
+	0x1ec4, 501,	/* Ễ ễ */
+	0x1ec6, 501,	/* Ệ ệ */
+	0x1ec8, 501,	/* Ỉ ỉ */
+	0x1eca, 501,	/* Ị ị */
+	0x1ecc, 501,	/* Ọ ọ */
+	0x1ece, 501,	/* Ỏ ỏ */
+	0x1ed0, 501,	/* Ố ố */
+	0x1ed2, 501,	/* Ồ ồ */
+	0x1ed4, 501,	/* Ổ ổ */
+	0x1ed6, 501,	/* Ỗ ỗ */
+	0x1ed8, 501,	/* Ộ ộ */
+	0x1eda, 501,	/* Ớ ớ */
+	0x1edc, 501,	/* Ờ ờ */
+	0x1ede, 501,	/* Ở ở */
+	0x1ee0, 501,	/* Ỡ ỡ */
+	0x1ee2, 501,	/* Ợ ợ */
+	0x1ee4, 501,	/* Ụ ụ */
+	0x1ee6, 501,	/* Ủ ủ */
+	0x1ee8, 501,	/* Ứ ứ */
+	0x1eea, 501,	/* Ừ ừ */
+	0x1eec, 501,	/* Ử ử */
+	0x1eee, 501,	/* Ữ ữ */
+	0x1ef0, 501,	/* Ự ự */
+	0x1ef2, 501,	/* Ỳ ỳ */
+	0x1ef4, 501,	/* Ỵ ỵ */
+	0x1ef6, 501,	/* Ỷ ỷ */
+	0x1ef8, 501,	/* Ỹ ỹ */
+	0x1f59, 492,	/* Ὑ ὑ */
+	0x1f5b, 492,	/* Ὓ ὓ */
+	0x1f5d, 492,	/* Ὕ ὕ */
+	0x1f5f, 492,	/* Ὗ ὗ */
+	0x1fbc, 491,	/* ᾼ ᾳ */
+	0x1fcc, 491,	/* ῌ ῃ */
+	0x1fec, 493,	/* Ῥ ῥ */
+	0x1ffc, 491,	/* ῼ ῳ */
+};
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+static
+Rune	_totitle1[] =
+{
+	0x01c4, 501,	/* DŽ Dž */
+	0x01c6, 499,	/* dž Dž */
+	0x01c7, 501,	/* LJ Lj */
+	0x01c9, 499,	/* lj Lj */
+	0x01ca, 501,	/* NJ Nj */
+	0x01cc, 499,	/* nj Nj */
+	0x01f1, 501,	/* DZ Dz */
+	0x01f3, 499,	/* dz Dz */
+};
+
+#define bsearch xbsearch
+static
+Rune*
+bsearch(Rune c, Rune *t, int n, int ne)
+{
+	Rune *p;
+	int m;
+
+	while(n > 1) {
+		m = n/2;
+		p = t + m*ne;
+		if(c >= p[0]) {
+			t = p;
+			n = n-m;
+		} else
+			n = m;
+	}
+	if(n && c >= t[0])
+		return t;
+	return 0;
+}
+
+Rune
+tolowerrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, _tolower2, nelem(_tolower2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return c + p[2] - 500;
+	p = bsearch(c, _tolower1, nelem(_tolower1)/2, 2);
+	if(p && c == p[0])
+		return c + p[1] - 500;
+	return c;
+}
+
+Rune
+toupperrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, _toupper2, nelem(_toupper2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return c + p[2] - 500;
+	p = bsearch(c, _toupper1, nelem(_toupper1)/2, 2);
+	if(p && c == p[0])
+		return c + p[1] - 500;
+	return c;
+}
+
+Rune
+totitlerune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, _totitle1, nelem(_totitle1)/2, 2);
+	if(p && c == p[0])
+		return c + p[1] - 500;
+	return c;
+}
+
+int
+islowerrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, _toupper2, nelem(_toupper2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	p = bsearch(c, _toupper1, nelem(_toupper1)/2, 2);
+	if(p && c == p[0])
+		return 1;
+	return 0;
+}
+
+int
+isupperrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, _tolower2, nelem(_tolower2)/3, 3);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	p = bsearch(c, _tolower1, nelem(_tolower1)/2, 2);
+	if(p && c == p[0])
+		return 1;
+	return 0;
+}
+
+int
+isalpharune(Rune c)
+{
+	Rune *p;
+
+	if(isupperrune(c) || islowerrune(c))
+		return 1;
+	p = bsearch(c, _alpha2, nelem(_alpha2)/2, 2);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	p = bsearch(c, _alpha1, nelem(_alpha1), 1);
+	if(p && c == p[0])
+		return 1;
+	return 0;
+}
+
+int
+istitlerune(Rune c)
+{
+	return isupperrune(c) && islowerrune(c);
+}
+
+int
+isspacerune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, _space2, nelem(_space2)/2, 2);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	return 0;
+}
+
+int
+isdigitrune(Rune c)
+{
+	Rune *p;
+
+	p = bsearch(c, __isdigitr, nelem(__isdigitr)/2, 2);
+	if(p && c >= p[0] && c <= p[1])
+		return 1;
+	return 0;
+}
--- /dev/null
+++ b/libc/runevseprint.c
@@ -1,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+Rune*
+runevseprint(Rune *buf, Rune *e, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(e <= buf)
+		return nil;
+	f.runes = 1;
+	f.start = buf;
+	f.to = buf;
+	f.stop = e - 1;
+	f.flush = 0;
+	f.farg = nil;
+	f.nfmt = 0;
+	VA_COPY(f.args,args);
+	dofmt(&f, fmt);
+	VA_END(f.args);
+	*(Rune*)f.to = '\0';
+	return (Rune*)f.to;
+}
+
--- /dev/null
+++ b/libc/runevsmprint.c
@@ -1,0 +1,71 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+static int
+runeFmtStrFlush(Fmt *f)
+{
+	Rune *s;
+	int n;
+
+	if(f->start == nil)
+		return 0;
+	n = (uintptr)f->farg;
+	n *= 2;
+	s = (Rune*)f->start;
+	f->start = realloc(s, sizeof(Rune)*n);
+	if(f->start == nil){
+		f->farg = nil;
+		f->to = nil;
+		f->stop = nil;
+		free(s);
+		return 0;
+	}
+	f->farg = (void*)(uintptr)n;
+	f->to = (Rune*)f->start + ((Rune*)f->to - s);
+	f->stop = (Rune*)f->start + n - 1;
+	return 1;
+}
+
+int
+runefmtstrinit(Fmt *f)
+{
+	int n;
+
+	memset(f, 0, sizeof *f);
+	f->runes = 1;
+	n = 32;
+	f->start = malloc(sizeof(Rune)*n);
+	if(f->start == nil)
+		return -1;
+	f->to = f->start;
+	f->stop = (Rune*)f->start + n - 1;
+	f->flush = runeFmtStrFlush;
+	f->farg = (void*)(uintptr)n;
+	f->nfmt = 0;
+	return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+Rune*
+runevsmprint(char *fmt, va_list args)
+{
+	Fmt f;
+	int n;
+
+	if(runefmtstrinit(&f) < 0)
+		return nil;
+	VA_COPY(f.args,args);
+	n = dofmt(&f, fmt);
+	VA_END(f.args);
+	if(f.start == nil)
+		return nil;
+	if(n < 0){
+		free(f.start);
+		return nil;
+	}
+	*(Rune*)f.to = '\0';
+	return (Rune*)f.start;
+}
--- /dev/null
+++ b/libc/runevsnprint.c
@@ -1,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+runevsnprint(Rune *buf, int len, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(len <= 0)
+		return -1;
+	f.runes = 1;
+	f.start = buf;
+	f.to = buf;
+	f.stop = buf + len - 1;
+	f.flush = 0;
+	f.farg = nil;
+	f.nfmt = 0;
+	VA_COPY(f.args,args);
+	dofmt(&f, fmt);
+	VA_END(f.args);
+	*(Rune*)f.to = '\0';
+	return (Rune*)f.to - buf;
+}
--- /dev/null
+++ b/libc/seprint.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+	char *p;
+	va_list args;
+
+	va_start(args, fmt);
+	p = vseprint(buf, e, fmt, args);
+	va_end(args);
+	return p;
+}
--- /dev/null
+++ b/libc/smprint.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+char*
+smprint(char *fmt, ...)
+{
+	va_list args;
+	char *p;
+
+	va_start(args, fmt);
+	p = vsmprint(fmt, args);
+	va_end(args);
+	return p;
+}
--- /dev/null
+++ b/libc/snprint.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+	int n;
+	va_list args;
+
+	va_start(args, fmt);
+	n = vsnprint(buf, len, fmt, args);
+	va_end(args);
+	return n;
+}
+
--- /dev/null
+++ b/libc/sprint.c
@@ -1,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+	int n;
+	uint len;
+	va_list args;
+
+	len = 1<<30;  /* big number, but sprint is deprecated anyway */
+	/*
+	 * on PowerPC, the stack is near the top of memory, so
+	 * we must be sure not to overflow a 32-bit pointer.
+	 */
+	if((uintptr)buf+len < (uintptr)buf)
+		len = -(uintptr)buf-1;
+
+	va_start(args, fmt);
+	n = vsnprint(buf, len, fmt, args);
+	va_end(args);
+	return n;
+}
--- /dev/null
+++ b/libc/strecpy.c
@@ -1,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strecpy(char *to, char *e, char *from)
+{
+	if(to >= e)
+		return to;
+	to = memccpy(to, from, '\0', e - to);
+	if(to == nil){
+		to = e - 1;
+		*to = '\0';
+	}else{
+		to--;
+	}
+	return to;
+}
--- /dev/null
+++ b/libc/strtod.c
@@ -1,0 +1,542 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * This routine will convert to arbitrary precision
+ * floating point entirely in multi-precision fixed.
+ * The answer is the closest floating point number to
+ * the given decimal number. Exactly half way are
+ * rounded ala ieee rules.
+ * Method is to scale input decimal between .500 and .999...
+ * with external power of 2, then binary search for the
+ * closest mantissa to this decimal number.
+ * Nmant is is the required precision. (53 for ieee dp)
+ * Nbits is the max number of bits/word. (must be <= 28)
+ * Prec is calculated - the number of words of fixed mantissa.
+ */
+enum
+{
+	Nbits	= 28,				/* bits safely represented in a ulong */
+	Nmant	= 53,				/* bits of precision required */
+	Bias	= 1022,
+	Prec	= (Nmant+Nbits+1)/Nbits,	/* words of Nbits each to represent mantissa */
+	Sigbit	= 1<<(Prec*Nbits-Nmant),	/* first significant bit of Prec-th word */
+	Ndig	= 1500,
+	One	= (ulong)(1<<Nbits),
+	Half	= (ulong)(One>>1),
+	Maxe	= 310,
+
+	S0	= 0,		/* _		_S0	+S1	#S2	.S3 */
+	S1,			/* _+		#S2	.S3 */
+	S2,			/* _+#		#S2	.S4	eS5 */
+	S3,			/* _+.		#S4 */
+	S4,			/* _+#.#	#S4	eS5 */
+	S5,			/* _+#.#e	+S6	#S7 */
+	S6,			/* _+#.#e+	#S7 */
+	S7,			/* _+#.#e+#	#S7 */
+
+	Fsign	= 1<<0,		/* found - */
+	Fesign	= 1<<1,		/* found e- */
+	Fdpoint	= 1<<2,		/* found . */
+};
+
+static	int	xcmp(char*, char*);
+static	int	fpcmp(char*, ulong*);
+static	void	frnorm(ulong*);
+static	void	divascii(char*, int*, int*, int*);
+static	void	mulascii(char*, int*, int*, int*);
+static	void	divby(char*, int*, int);
+static ulong	umuldiv(ulong, ulong, ulong);
+
+typedef	struct	Tab	Tab;
+struct	Tab
+{
+	int	bp;
+	int	siz;
+	char*	cmp;
+};
+
+#ifndef ERANGE
+#define ERANGE 12345
+#endif
+
+double
+fmtstrtod(const char *as, char **aas)
+{
+	int na, ona, ex, dp, bp, c, i, flag, state;
+	ulong low[Prec], hig[Prec], mid[Prec], num, den;
+	double d;
+	char *s, a[Ndig];
+
+	flag = 0;	/* Fsign, Fesign, Fdpoint */
+	na = 0;		/* number of digits of a[] */
+	dp = 0;		/* na of decimal point */
+	ex = 0;		/* exonent */
+
+	state = S0;
+	for(s=(char*)as;; s++) {
+		c = *s;
+		if(c >= '0' && c <= '9') {
+			switch(state) {
+			case S0:
+			case S1:
+			case S2:
+				state = S2;
+				break;
+			case S3:
+			case S4:
+				state = S4;
+				break;
+
+			case S5:
+			case S6:
+			case S7:
+				state = S7;
+				ex = ex*10 + (c-'0');
+				continue;
+			}
+			if(na == 0 && c == '0') {
+				dp--;
+				continue;
+			}
+			if(na < Ndig-50)
+				a[na++] = c;
+			continue;
+		}
+		switch(c) {
+		case '\t':
+		case '\n':
+		case '\v':
+		case '\f':
+		case '\r':
+		case ' ':
+			if(state == S0)
+				continue;
+			break;
+		case '-':
+			if(state == S0)
+				flag |= Fsign;
+			else
+				flag |= Fesign;
+		case '+':
+			if(state == S0)
+				state = S1;
+			else
+			if(state == S5)
+				state = S6;
+			else
+				break;	/* syntax */
+			continue;
+		case '.':
+			flag |= Fdpoint;
+			dp = na;
+			if(state == S0 || state == S1) {
+				state = S3;
+				continue;
+			}
+			if(state == S2) {
+				state = S4;
+				continue;
+			}
+			break;
+		case 'e':
+		case 'E':
+			if(state == S2 || state == S4) {
+				state = S5;
+				continue;
+			}
+			break;
+		}
+		break;
+	}
+
+	/*
+	 * clean up return char-pointer
+	 */
+	switch(state) {
+	case S0:
+		if(xcmp(s, "nan") == 0) {
+			if(aas != nil)
+				*aas = s+3;
+			goto retnan;
+		}
+	case S1:
+		if(xcmp(s, "infinity") == 0) {
+			if(aas != nil)
+				*aas = s+8;
+			goto retinf;
+		}
+		if(xcmp(s, "inf") == 0) {
+			if(aas != nil)
+				*aas = s+3;
+			goto retinf;
+		}
+	case S3:
+		if(aas != nil)
+			*aas = (char*)as;
+		goto ret0;	/* no digits found */
+	case S6:
+		s--;		/* back over +- */
+	case S5:
+		s--;		/* back over e */
+		break;
+	}
+	if(aas != nil)
+		*aas = s;
+
+	if(flag & Fdpoint)
+	while(na > 0 && a[na-1] == '0')
+		na--;
+	if(na == 0)
+		goto ret0;	/* zero */
+	a[na] = 0;
+	if(!(flag & Fdpoint))
+		dp = na;
+	if(flag & Fesign)
+		ex = -ex;
+	dp += ex;
+	if(dp < -Maxe-Nmant/3){ /* actually -Nmant*log(2)/log(10), but Nmant/3 close enough */
+		errno = ERANGE;
+		goto ret0;	/* underflow by exp */
+	} else
+	if(dp > +Maxe)
+		goto retinf;	/* overflow by exp */
+
+	/*
+	 * normalize the decimal ascii number
+	 * to range .[5-9][0-9]* e0
+	 */
+	bp = 0;		/* binary exponent */
+	while(dp > 0)
+		divascii(a, &na, &dp, &bp);
+	while(dp < 0 || a[0] < '5')
+		mulascii(a, &na, &dp, &bp);
+	a[na] = 0;
+
+	/*
+	 * very small numbers are represented using
+	 * bp = -Bias+1.  adjust accordingly.
+	 */
+	if(bp < -Bias+1){
+		ona = na;
+		divby(a, &na, -bp-Bias+1);
+		if(na < ona){
+			memmove(a+ona-na, a, na);
+			memset(a, '0', ona-na);
+			na = ona;
+		}
+		a[na] = 0;
+		bp = -Bias+1;
+	}
+
+	/* close approx by naive conversion */
+	num = 0;
+	den = 1;
+	for(i=0; i<9 && (c=a[i]); i++) {
+		num = num*10 + (c-'0');
+		den *= 10;
+	}
+	low[0] = umuldiv(num, One, den);
+	hig[0] = umuldiv(num+1, One, den);
+	for(i=1; i<Prec; i++) {
+		low[i] = 0;
+		hig[i] = One-1;
+	}
+
+	/* binary search for closest mantissa */
+	for(;;) {
+		/* mid = (hig + low) / 2 */
+		c = 0;
+		for(i=0; i<Prec; i++) {
+			mid[i] = hig[i] + low[i];
+			if(c)
+				mid[i] += One;
+			c = mid[i] & 1;
+			mid[i] >>= 1;
+		}
+		frnorm(mid);
+
+		/* compare */
+		c = fpcmp(a, mid);
+		if(c > 0) {
+			c = 1;
+			for(i=0; i<Prec; i++)
+				if(low[i] != mid[i]) {
+					c = 0;
+					low[i] = mid[i];
+				}
+			if(c)
+				break;	/* between mid and hig */
+			continue;
+		}
+		if(c < 0) {
+			for(i=0; i<Prec; i++)
+				hig[i] = mid[i];
+			continue;
+		}
+
+		/* only hard part is if even/odd roundings wants to go up */
+		c = mid[Prec-1] & (Sigbit-1);
+		if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
+			mid[Prec-1] -= c;
+		break;	/* exactly mid */
+	}
+
+	/* normal rounding applies */
+	c = mid[Prec-1] & (Sigbit-1);
+	mid[Prec-1] -= c;
+	if(c >= Sigbit/2) {
+		mid[Prec-1] += Sigbit;
+		frnorm(mid);
+	}
+	goto out;
+
+ret0:
+	return 0;
+
+retnan:
+	return __NaN();
+
+retinf:
+	/*
+	 * Unix strtod requires these.  Plan 9 would return Inf(0) or Inf(-1). */
+	errno = ERANGE;
+	if(flag & Fsign)
+		return -HUGE_VAL;
+	return HUGE_VAL;
+
+out:
+	d = 0;
+	for(i=0; i<Prec; i++)
+		d = d*One + mid[i];
+	if(flag & Fsign)
+		d = -d;
+	d = ldexp(d, bp - Prec*Nbits);
+	if(d == 0){	/* underflow */
+		errno = ERANGE;
+	}
+	return d;
+}
+
+static void
+frnorm(ulong *f)
+{
+	int i, c;
+
+	c = 0;
+	for(i=Prec-1; i>0; i--) {
+		f[i] += c;
+		c = f[i] >> Nbits;
+		f[i] &= One-1;
+	}
+	f[0] += c;
+}
+
+static int
+fpcmp(char *a, ulong* f)
+{
+	ulong tf[Prec];
+	int i, d, c;
+
+	for(i=0; i<Prec; i++)
+		tf[i] = f[i];
+
+	for(;;) {
+		/* tf *= 10 */
+		for(i=0; i<Prec; i++)
+			tf[i] = tf[i]*10;
+		frnorm(tf);
+		d = (tf[0] >> Nbits) + '0';
+		tf[0] &= One-1;
+
+		/* compare next digit */
+		c = *a;
+		if(c == 0) {
+			if('0' < d)
+				return -1;
+			if(tf[0] != 0)
+				goto cont;
+			for(i=1; i<Prec; i++)
+				if(tf[i] != 0)
+					goto cont;
+			return 0;
+		}
+		if(c > d)
+			return +1;
+		if(c < d)
+			return -1;
+		a++;
+	cont:;
+	}
+}
+
+static void
+_divby(char *a, int *na, int b)
+{
+	int n, c;
+	char *p;
+
+	p = a;
+	n = 0;
+	while(n>>b == 0) {
+		c = *a++;
+		if(c == 0) {
+			while(n) {
+				c = n*10;
+				if(c>>b)
+					break;
+				n = c;
+			}
+			goto xx;
+		}
+		n = n*10 + c-'0';
+		(*na)--;
+	}
+	for(;;) {
+		c = n>>b;
+		n -= c<<b;
+		*p++ = c + '0';
+		c = *a++;
+		if(c == 0)
+			break;
+		n = n*10 + c-'0';
+	}
+	(*na)++;
+xx:
+	while(n) {
+		n = n*10;
+		c = n>>b;
+		n -= c<<b;
+		*p++ = c + '0';
+		(*na)++;
+	}
+	*p = 0;
+}
+
+static void
+divby(char *a, int *na, int b)
+{
+	while(b > 9){
+		_divby(a, na, 9);
+		a[*na] = 0;
+		b -= 9;
+	}
+	if(b > 0)
+		_divby(a, na, b);
+}
+
+static	Tab	tab1[] =
+{
+	 1,  0, "",
+	 3,  1, "7",
+	 6,  2, "63",
+	 9,  3, "511",
+	13,  4, "8191",
+	16,  5, "65535",
+	19,  6, "524287",
+	23,  7, "8388607",
+	26,  8, "67108863",
+	27,  9, "134217727",
+};
+
+static void
+divascii(char *a, int *na, int *dp, int *bp)
+{
+	int b, d;
+	Tab *t;
+
+	d = *dp;
+	if(d >= (int)(nelem(tab1)))
+		d = (int)(nelem(tab1))-1;
+	t = tab1 + d;
+	b = t->bp;
+	if(memcmp(a, t->cmp, t->siz) > 0)
+		d--;
+	*dp -= d;
+	*bp += b;
+	divby(a, na, b);
+}
+
+static void
+mulby(char *a, char *p, char *q, int b)
+{
+	int n, c;
+
+	n = 0;
+	*p = 0;
+	for(;;) {
+		q--;
+		if(q < a)
+			break;
+		c = *q - '0';
+		c = (c<<b) + n;
+		n = c/10;
+		c -= n*10;
+		p--;
+		*p = c + '0';
+	}
+	while(n) {
+		c = n;
+		n = c/10;
+		c -= n*10;
+		p--;
+		*p = c + '0';
+	}
+}
+
+static	Tab	tab2[] =
+{
+	 1,  1, "",				/* dp = 0-0 */
+	 3,  3, "125",
+	 6,  5, "15625",
+	 9,  7, "1953125",
+	13, 10, "1220703125",
+	16, 12, "152587890625",
+	19, 14, "19073486328125",
+	23, 17, "11920928955078125",
+	26, 19, "1490116119384765625",
+	27, 19, "7450580596923828125",		/* dp 8-9 */
+};
+
+static void
+mulascii(char *a, int *na, int *dp, int *bp)
+{
+	char *p;
+	int d, b;
+	Tab *t;
+
+	d = -*dp;
+	if(d >= (int)(nelem(tab2)))
+		d = (int)(nelem(tab2))-1;
+	t = tab2 + d;
+	b = t->bp;
+	if(memcmp(a, t->cmp, t->siz) < 0)
+		d--;
+	p = a + *na;
+	*bp -= b;
+	*dp += d;
+	*na += d;
+	mulby(a, p+d, p, b);
+}
+
+static int
+xcmp(char *a, char *b)
+{
+	int c1, c2;
+
+	while(c1 = *b++) {
+		c2 = *a++;
+		if(isupper(c2))
+			c2 = tolower(c2);
+		if(c1 != c2)
+			return 1;
+	}
+	return 0;
+}
+
+static ulong
+umuldiv(ulong a, ulong b, ulong c)
+{
+	return ((uvlong)a * (uvlong)b) / c;
+}
--- /dev/null
+++ b/libc/strtod.h
@@ -1,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern double __isNaN(double);
+extern double __isInf(double, int);
--- /dev/null
+++ b/libc/strtoll.c
@@ -1,0 +1,93 @@
+#include <u.h>
+#include <libc.h>
+#define VLONG_MAX	((vlong)~(((uvlong)1)<<63))
+#define VLONG_MIN	((vlong)(((uvlong)1)<<63))
+vlong
+strtoll(const char *nptr, char **endptr, int base)
+{
+	char *p;
+	vlong n, nn, m;
+	int c, ovfl, v, neg, ndig;
+	p = (char*)nptr;
+	neg = 0;
+	n = 0;
+	ndig = 0;
+	ovfl = 0;
+	/*
+	 * White space
+	 */
+	for(;; p++) {
+		switch(*p) {
+		case ' ':
+		case '\t':
+		case '\n':
+		case '\f':
+		case '\r':
+		case '\v':
+			continue;
+		}
+		break;
+	}
+	/*
+	 * Sign
+	 */
+	if(*p=='-' || *p=='+')
+		if(*p++ == '-')
+			neg = 1;
+	/*
+	 * Base
+	 */
+	if(base==0){
+		base = 10;
+		if(*p == '0') {
+			base = 8;
+			if(p[1]=='x' || p[1]=='X') {
+				p += 2;
+				base = 16;
+			}
+		}
+	} else
+	if(base==16 && *p=='0') {
+		if(p[1]=='x' || p[1]=='X')
+			p += 2;
+	} else
+	if(base<0 || 36<base)
+		goto Return;
+	/*
+	 * Non-empty sequence of digits
+	 */
+	m = VLONG_MAX/base;
+	for(;; p++,ndig++) {
+		c = *p;
+		v = base;
+		if('0'<=c && c<='9')
+			v = c - '0';
+		else
+		if('a'<=c && c<='z')
+			v = c - 'a' + 10;
+		else
+		if('A'<=c && c<='Z')
+			v = c - 'A' + 10;
+		if(v >= base)
+			break;
+		if(n > m)
+			ovfl = 1;
+		nn = n*base + v;
+		if(nn < n)
+			ovfl = 1;
+		n = nn;
+	}
+Return:
+	if(ndig == 0)
+		p = (char*)nptr;
+	if(endptr)
+		*endptr = p;
+	if(ovfl){
+		if(neg)
+			return VLONG_MIN;
+		return VLONG_MAX;
+	}
+	if(neg)
+		return -n;
+	return n;
+}
--- /dev/null
+++ b/libc/sysfatal.c
@@ -1,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+
+#include <stdlib.h>
+
+static void
+_sysfatalimpl(char *fmt, va_list arg)
+{
+	vfprint(2, fmt, arg);
+	fprint(2, "\n");
+	exit(1);
+}
+
+void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl;
+
+void
+sysfatal(char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	(*_sysfatal)(fmt, arg);
+	va_end(arg);
+}
--- /dev/null
+++ b/libc/tas.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+
+int
+tas(int *p)
+{
+	int old;
+
+	old = *p;
+	*p = 1;
+	return old;
+}
--- /dev/null
+++ b/libc/tokenize.c
@@ -1,0 +1,107 @@
+#include <u.h>
+#include <libc.h>
+
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s, char *sep)
+{
+	int quoting;
+	char *t;
+
+	quoting = 0;
+	t = s;	/* s is output string, t is input string */
+	while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+		if(*t != '\''){
+			*s++ = *t++;
+			continue;
+		}
+		/* *t is a quote */
+		if(!quoting){
+			quoting = 1;
+			t++;
+			continue;
+		}
+		/* quoting and we're on a quote */
+		if(t[1] != '\''){
+			/* end of quoted section; absorb closing quote */
+			t++;
+			quoting = 0;
+			continue;
+		}
+		/* doubled quote; fold one quote into two */
+		t++;
+		*s++ = *t++;
+	}
+	if(*s != '\0'){
+		*s = '\0';
+		if(t == s)
+			t++;
+	}
+	return t;
+}
+
+static char*
+etoken(char *t, char *sep)
+{
+	int quoting;
+
+	/* move to end of next token */
+	quoting = 0;
+	while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+		if(*t != '\''){
+			t++;
+			continue;
+		}
+		/* *t is a quote */
+		if(!quoting){
+			quoting = 1;
+			t++;
+			continue;
+		}
+		/* quoting and we're on a quote */
+		if(t[1] != '\''){
+			/* end of quoted section; absorb closing quote */
+			t++;
+			quoting = 0;
+			continue;
+		}
+		/* doubled quote; fold one quote into two */
+		t += 2;
+	}
+	return t;
+}
+
+int
+gettokens(char *s, char **args, int maxargs, char *sep)
+{
+	int nargs;
+
+	for(nargs=0; nargs<maxargs; nargs++){
+		while(*s!='\0' && utfrune(sep, *s)!=nil)
+			*s++ = '\0';
+		if(*s == '\0')
+			break;
+		args[nargs] = s;
+		s = etoken(s, sep);
+	}
+
+	return nargs;
+}
+
+int
+tokenize(char *s, char **args, int maxargs)
+{
+	int nargs;
+
+	for(nargs=0; nargs<maxargs; nargs++){
+		while(*s!='\0' && utfrune(qsep, *s)!=nil)
+			s++;
+		if(*s == '\0')
+			break;
+		args[nargs] = s;
+		s = qtoken(s, qsep);
+	}
+
+	return nargs;
+}
--- /dev/null
+++ b/libc/truerand.c
@@ -1,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+ulong
+truerand(void)
+{
+	ulong x;
+	static int randfd = -1;
+
+	if(randfd < 0)
+		randfd = open("/dev/random", OREAD|OCEXEC);
+	if(randfd < 0)
+		sysfatal("can't open /dev/random");
+	if(read(randfd, &x, sizeof(x)) != sizeof(x))
+		sysfatal("can't read /dev/random");
+	return x;
+}
--- /dev/null
+++ b/libc/u16.c
@@ -1,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+
+#define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
+
+int
+enc16chr(int o)
+{
+	int c;
+
+	c  = between(o,  0,  9) & ('0'+o);
+	c |= between(o, 10, 15) & ('A'+(o-10));
+	return c;
+}
+
+int
+dec16chr(int c)
+{
+	int o;
+
+	o  = between(c, '0', '9') & (1+(c-'0'));
+	o |= between(c, 'A', 'F') & (1+10+(c-'A'));
+	o |= between(c, 'a', 'f') & (1+10+(c-'a'));
+	return o-1;
+}
+
+int
+dec16(uchar *out, int lim, char *in, int n)
+{
+	int c, w = 0, i = 0;
+	uchar *start = out;
+	uchar *eout = out + lim;
+
+	while(n-- > 0){
+		c = dec16chr(*in++);
+		if(c < 0)
+			continue;
+		w = (w<<4) + c;
+		i++;
+		if(i == 2){
+			if(out + 1 > eout)
+				goto exhausted;
+			*out++ = w;
+			w = 0;
+			i = 0;
+		}
+	}
+exhausted:
+	return out - start;
+}
+
+int
+enc16(char *out, int lim, uchar *in, int n)
+{
+	uint c;
+	char *eout = out + lim;
+	char *start = out;
+
+	while(n-- > 0){
+		c = *in++;
+		if(out + 2 >= eout)
+			goto exhausted;
+		*out++ = enc16chr(c>>4);
+		*out++ = enc16chr(c&15);
+	}
+exhausted:
+	*out = 0;
+	return out - start;
+}
--- /dev/null
+++ b/libc/u32.c
@@ -1,0 +1,132 @@
+#include <u.h>
+#include <libc.h>
+
+#define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
+
+int
+enc32chr(int o)
+{
+	int c;
+
+	c  = between(o,  0, 25) & ('A'+o);
+	c |= between(o, 26, 31) & ('2'+(o-26));
+	return c;
+}
+
+int
+dec32chr(int c)
+{
+	int o;
+
+	o  = between(c, 'A', 'Z') & (1+(c-'A'));
+	o |= between(c, 'a', 'z') & (1+(c-'a'));
+	o |= between(c, '2', '7') & (1+26+(c-'2'));
+	return o-1;
+}
+
+int
+dec32(uchar *dest, int ndest, char *src, int nsrc)
+{
+	uchar *start;
+	int i, j, u[8];
+
+	if(ndest+1 < (5*nsrc+7)/8)
+		return -1;
+	start = dest;
+	while(nsrc>=8){
+		for(i=0; i<8; i++){
+			j = dec32chr(src[i]);
+			if(j < 0)
+				j = 0;
+			u[i] = j;
+		}
+		*dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+		*dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+		*dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+		*dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+		*dest++ = ((0x7 & u[6])<<5) | u[7];
+		src  += 8;
+		nsrc -= 8;
+	}
+	if(nsrc > 0){
+		if(nsrc == 1 || nsrc == 3 || nsrc == 6)
+			return -1;
+		for(i=0; i<nsrc; i++){
+			j = dec32chr(src[i]);
+			if(j < 0)
+				j = 0;
+			u[i] = j;
+		}
+		*dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+		if(nsrc == 2)
+			goto out;
+		*dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+		if(nsrc == 4)
+			goto out;
+		*dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+		if(nsrc == 5)
+			goto out;
+		*dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+	}
+out:
+	return dest-start;
+}
+
+int
+enc32(char *dest, int ndest, uchar *src, int nsrc)
+{
+	char *start;
+	int j;
+
+	if(ndest <= (8*nsrc+4)/5)
+		return -1;
+	start = dest;
+	while(nsrc>=5){
+		j = (0x1f & (src[0]>>3));
+		*dest++ = enc32chr(j);
+		j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6));
+		*dest++ = enc32chr(j);
+		j = (0x1f & (src[1]>>1));
+		*dest++ = enc32chr(j);
+		j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4));
+		*dest++ = enc32chr(j);
+		j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7));
+		*dest++ = enc32chr(j);
+		j = (0x1f & (src[3]>>2));
+		*dest++ = enc32chr(j);
+		j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5));
+		*dest++ = enc32chr(j);
+		j = (0x1f & (src[4]));
+		*dest++ = enc32chr(j);
+		src  += 5;
+		nsrc -= 5;
+	}
+	if(nsrc){
+		j = (0x1f & (src[0]>>3));
+		*dest++ = enc32chr(j);
+		j = (0x1c & (src[0]<<2));
+		if(nsrc == 1)
+			goto out;
+		j |= (0x03 & (src[1]>>6));
+		*dest++ = enc32chr(j);
+		j = (0x1f & (src[1]>>1));
+		*dest++ = enc32chr(j);
+		j = (0x10 & (src[1]<<4));
+		if(nsrc == 2)
+			goto out;
+		j |= (0x0f & (src[2]>>4));
+		*dest++ = enc32chr(j);
+		j = (0x1e & (src[2]<<1));
+		if(nsrc == 3)
+			goto out;
+		j |= (0x01 & (src[3]>>7));
+		*dest++ = enc32chr(j);
+		j = (0x1f & (src[3]>>2));
+		*dest++ = enc32chr(j);
+		j = (0x18 & (src[3]<<3));
+out:
+		*dest++ = enc32chr(j);
+	}
+	*dest = 0;
+	return dest-start;
+}
--- /dev/null
+++ b/libc/u64.c
@@ -1,0 +1,130 @@
+#include <u.h>
+#include <libc.h>
+
+#define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
+
+int
+enc64chr(int o)
+{
+	int c;
+
+	c  = between(o,  0, 25) & ('A'+o);
+	c |= between(o, 26, 51) & ('a'+(o-26));
+	c |= between(o, 52, 61) & ('0'+(o-52));
+	c |= between(o, 62, 62) & ('+');
+	c |= between(o, 63, 63) & ('/');
+	return c;
+}
+
+int
+dec64chr(int c)
+{
+	int o;
+
+	o  = between(c, 'A', 'Z') & (1+(c-'A'));
+	o |= between(c, 'a', 'z') & (1+26+(c-'a'));
+	o |= between(c, '0', '9') & (1+52+(c-'0'));
+	o |= between(c, '+', '+') & (1+62);
+	o |= between(c, '/', '/') & (1+63);
+	return o-1;
+}
+
+int
+dec64(uchar *out, int lim, char *in, int n)
+{
+	ulong b24;
+	uchar *start = out;
+	uchar *e = out + lim;
+	int i, c;
+
+	b24 = 0;
+	i = 0;
+	while(n-- > 0){
+		c = dec64chr(*in++);
+		if(c < 0)
+			continue;
+		switch(i){
+		case 0:
+			b24 = c<<18;
+			break;
+		case 1:
+			b24 |= c<<12;
+			break;
+		case 2:
+			b24 |= c<<6;
+			break;
+		case 3:
+			if(out + 3 > e)
+				goto exhausted;
+
+			b24 |= c;
+			*out++ = b24>>16;
+			*out++ = b24>>8;
+			*out++ = b24;
+			i = 0;
+			continue;
+		}
+		i++;
+	}
+	switch(i){
+	case 2:
+		if(out + 1 > e)
+			goto exhausted;
+		*out++ = b24>>16;
+		break;
+	case 3:
+		if(out + 2 > e)
+			goto exhausted;
+		*out++ = b24>>16;
+		*out++ = b24>>8;
+		break;
+	}
+exhausted:
+	return out - start;
+}
+
+int
+enc64(char *out, int lim, uchar *in, int n)
+{
+	int i;
+	ulong b24;
+	char *start = out;
+	char *e = out + lim;
+
+	for(i = n/3; i > 0; i--){
+		b24 = *in++<<16;
+		b24 |= *in++<<8;
+		b24 |= *in++;
+		if(out + 4 >= e)
+			goto exhausted;
+		*out++ = enc64chr(b24>>18);
+		*out++ = enc64chr((b24>>12)&0x3f);
+		*out++ = enc64chr((b24>>6)&0x3f);
+		*out++ = enc64chr(b24&0x3f);
+	}
+
+	switch(n%3){
+	case 2:
+		b24 = *in++<<16;
+		b24 |= *in<<8;
+		if(out + 4 >= e)
+			goto exhausted;
+		*out++ = enc64chr(b24>>18);
+		*out++ = enc64chr((b24>>12)&0x3f);
+		*out++ = enc64chr((b24>>6)&0x3f);
+		*out++ = '=';
+		break;
+	case 1:
+		b24 = *in<<16;
+		if(out + 4 >= e)
+			goto exhausted;
+		*out++ = enc64chr(b24>>18);
+		*out++ = enc64chr((b24>>12)&0x3f);
+		*out++ = '=';
+		*out++ = '=';
+		break;
+	}
+exhausted:
+	*out = 0;
+	return out - start;
+}
--- /dev/null
+++ b/libc/utf.h
@@ -1,0 +1,53 @@
+#ifndef _UTFH_
+#define _UTFH_ 1
+
+typedef unsigned int Rune;	/* 32 bits */
+
+enum
+{
+	UTFmax		= 4,		/* maximum bytes per rune */
+	Runesync	= 0x80,		/* cannot represent part of a UTF sequence (<) */
+	Runeself	= 0x80,		/* rune and UTF sequences are the same (<) */
+	Runeerror	= 0xFFFD,	/* decoding error in UTF */
+	Runemax		= 0x10FFFF,	/* 21-bit rune */
+	Runemask	= 0x1FFFFF,	/* bits used by runes (see grep) */
+};
+
+/*
+ * rune routines
+ */
+extern	int	runetochar(char*, Rune*);
+extern	int	chartorune(Rune*, char*);
+extern	int	runelen(long);
+extern	int	runenlen(Rune*, int);
+extern	int	fullrune(char*, int);
+extern	int	utflen(char*);
+extern	int	utfnlen(char*, long);
+extern	char*	utfrune(char*, long);
+extern	char*	utfrrune(char*, long);
+extern	char*	utfutf(char*, char*);
+extern	char*	utfecpy(char*, char*, char*);
+
+extern	Rune*	runestrcat(Rune*, Rune*);
+extern	Rune*	runestrchr(Rune*, Rune);
+extern	int	runestrcmp(Rune*, Rune*);
+extern	Rune*	runestrcpy(Rune*, Rune*);
+extern	Rune*	runestrncpy(Rune*, Rune*, long);
+extern	Rune*	runestrecpy(Rune*, Rune*, Rune*);
+extern	Rune*	runestrdup(Rune*);
+extern	Rune*	runestrncat(Rune*, Rune*, long);
+extern	int	runestrncmp(Rune*, Rune*, long);
+extern	Rune*	runestrrchr(Rune*, Rune);
+extern	long	runestrlen(Rune*);
+extern	Rune*	runestrstr(Rune*, Rune*);
+
+extern	Rune	tolowerrune(Rune);
+extern	Rune	totitlerune(Rune);
+extern	Rune	toupperrune(Rune);
+extern	int	isalpharune(Rune);
+extern	int	islowerrune(Rune);
+extern	int	isspacerune(Rune);
+extern	int	istitlerune(Rune);
+extern	int	isupperrune(Rune);
+
+#endif
--- /dev/null
+++ b/libc/utfdef.h
@@ -1,0 +1,14 @@
+#define uchar _utfuchar
+#define ushort _utfushort
+#define uint _utfuint
+#define ulong _utfulong
+#define vlong _utfvlong
+#define uvlong _utfuvlong
+
+typedef unsigned char		uchar;
+typedef unsigned short		ushort;
+typedef unsigned int		uint;
+typedef unsigned long		ulong;
+
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+#define nil ((void*)0)
--- /dev/null
+++ b/libc/utfecpy.c
@@ -1,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+utfecpy(char *to, char *e, char *from)
+{
+	char *end;
+
+	if(to >= e)
+		return to;
+	end = memccpy(to, from, '\0', e - to);
+	if(end == nil){
+		end = e;
+		while(end>to && (*--end&0xC0)==0x80)
+			;
+		*end = '\0';
+	}else{
+		end--;
+	}
+	return end;
+}
--- /dev/null
+++ b/libc/utflen.c
@@ -1,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+
+int
+utflen(char *s)
+{
+	int c;
+	long n;
+	Rune rune;
+
+	n = 0;
+	for(;;) {
+		c = *(uchar*)s;
+		if(c < Runeself) {
+			if(c == 0)
+				return n;
+			s++;
+		} else
+			s += chartorune(&rune, s);
+		n++;
+	}
+	return 0;
+}
--- /dev/null
+++ b/libc/utfnlen.c
@@ -1,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+int
+utfnlen(char *s, long m)
+{
+	int c;
+	long n;
+	Rune rune;
+	char *es;
+
+	es = s + m;
+	for(n = 0; s < es; n++) {
+		c = *(uchar*)s;
+		if(c < Runeself){
+			if(c == '\0')
+				break;
+			s++;
+			continue;
+		}
+		if(!fullrune(s, es-s))
+			break;
+		s += chartorune(&rune, s);
+	}
+	return n;
+}
--- /dev/null
+++ b/libc/utfrrune.c
@@ -1,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+utfrrune(char *s, long c)
+{
+	long c1;
+	Rune r;
+	char *s1;
+
+	if(c < Runesync)		/* not part of utf sequence */
+		return strrchr(s, c);
+
+	s1 = 0;
+	for(;;) {
+		c1 = *(uchar*)s;
+		if(c1 < Runeself) {	/* one byte rune */
+			if(c1 == 0)
+				return s1;
+			if(c1 == c)
+				s1 = s;
+			s++;
+			continue;
+		}
+		c1 = chartorune(&r, s);
+		if(r == c)
+			s1 = s;
+		s += c1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/libc/utfrune.c
@@ -1,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+utfrune(char *s, long c)
+{
+	long c1;
+	Rune r;
+	int n;
+
+	if(c < Runesync)		/* not part of utf sequence */
+		return strchr(s, c);
+
+	for(;;) {
+		c1 = *(uchar*)s;
+		if(c1 < Runeself) {	/* one byte rune */
+			if(c1 == 0)
+				return 0;
+			if(c1 == c)
+				return s;
+			s++;
+			continue;
+		}
+		n = chartorune(&r, s);
+		if(r == c)
+			return s;
+		s += n;
+	}
+	return 0;
+}
--- /dev/null
+++ b/libc/utfutf.c
@@ -1,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+char*
+utfutf(char *s1, char *s2)
+{
+	char *p;
+	long f, n1, n2;
+	Rune r;
+
+	n1 = chartorune(&r, s2);
+	f = r;
+	if(f <= Runesync)		/* represents self */
+		return strstr(s1, s2);
+
+	n2 = strlen(s2);
+	for(p=s1; (p=utfrune(p, f)); p+=n1)
+		if(strncmp(p, s2, n2) == 0)
+			return p;
+	return 0;
+}
--- /dev/null
+++ b/libc/vfprint.c
@@ -1,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+vfprint(int fd, char *fmt, va_list args)
+{
+	Fmt f;
+	char buf[256];
+	int n;
+
+	fmtfdinit(&f, fd, buf, sizeof(buf));
+	VA_COPY(f.args,args);
+	n = dofmt(&f, fmt);
+	VA_END(f.args);
+	if(n > 0 && __fmtFdFlush(&f) == 0)
+		return -1;
+	return n;
+}
--- /dev/null
+++ b/libc/vseprint.c
@@ -1,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+char*
+vseprint(char *buf, char *e, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(e <= buf)
+		return nil;
+	f.runes = 0;
+	f.start = buf;
+	f.to = buf;
+	f.stop = e - 1;
+	f.flush = 0;
+	f.farg = nil;
+	f.nfmt = 0;
+	VA_COPY(f.args,args);
+	dofmt(&f, fmt);
+	VA_END(f.args);
+	*(char*)f.to = '\0';
+	return (char*)f.to;
+}
+
--- /dev/null
+++ b/libc/vsmprint.c
@@ -1,0 +1,68 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+static int
+fmtStrFlush(Fmt *f)
+{
+	char *s;
+	int n;
+
+	if(f->start == nil)
+		return 0;
+	n = (uintptr)f->farg;
+	n *= 2;
+	s = (char*)f->start;
+	f->start = realloc(s, n);
+	if(f->start == nil){
+		f->farg = nil;
+		f->to = nil;
+		f->stop = nil;
+		free(s);
+		return 0;
+	}
+	f->farg = (void*)(uintptr)n;
+	f->to = (char*)f->start + ((char*)f->to - s);
+	f->stop = (char*)f->start + n - 1;
+	return 1;
+}
+
+int
+fmtstrinit(Fmt *f)
+{
+	int n;
+
+	memset(f, 0, sizeof *f);
+	f->runes = 0;
+	n = 32;
+	f->start = malloc(n);
+	if(f->start == nil)
+		return -1;
+	f->to = f->start;
+	f->stop = (char*)f->start + n - 1;
+	f->flush = fmtStrFlush;
+	f->farg = (void*)(uintptr)n;
+	f->nfmt = 0;
+	return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+char*
+vsmprint(char *fmt, va_list args)
+{
+	Fmt f;
+	int n;
+
+	if(fmtstrinit(&f) < 0)
+		return nil;
+	VA_COPY(f.args,args);
+	n = dofmt(&f, fmt);
+	VA_END(f.args);
+	if(n < 0){
+		free(f.start);
+		return nil;
+	}
+	return fmtstrflush(&f);
+}
--- /dev/null
+++ b/libc/vsnprint.c
@@ -1,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+vsnprint(char *buf, int len, char *fmt, va_list args)
+{
+	Fmt f;
+
+	if(len <= 0)
+		return -1;
+	f.runes = 0;
+	f.start = buf;
+	f.to = buf;
+	f.stop = buf + len - 1;
+	f.flush = 0;
+	f.farg = nil;
+	f.nfmt = 0;
+	VA_COPY(f.args,args);
+	dofmt(&f, fmt);
+	VA_END(f.args);
+	*(char*)f.to = '\0';
+	return (char*)f.to - buf;
+}
--- /dev/null
+++ b/libc/werrstr.c
@@ -1,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+char errbuf[ERRMAX];
+
+int
+rerrstr(char *buf, uint n)
+{
+	utfecpy(buf, buf+n, errbuf);
+	return utflen(buf);
+}
+
+void
+werrstr(char *f, ...)
+{
+	va_list arg;
+
+	va_start(arg, f);
+	vsnprint(errbuf, sizeof errbuf, f, arg);
+	va_end(arg);
+}
+
--- /dev/null
+++ b/libmp/Makefile
@@ -1,0 +1,55 @@
+ROOT=..
+include ../Make.config
+
+LIB=libmp.a
+
+OFILES=\
+	betomp.$O\
+	cnfield.$O\
+	crt.$O\
+	gmfield.$O\
+	letomp.$O\
+	mpadd.$O\
+	mpaux.$O\
+	mpcmp.$O\
+	mpdigdiv.$O\
+	mpdiv.$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\
+	mpvecadd.$O\
+	mpveccmp.$O\
+	mpvecdigmuladd.$O\
+	mpvecsub.$O\
+	mpvectscmp.$O\
+	strtomp.$O
+
+default: $(LIB)
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libmp/betomp.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// convert a big-endian byte array (most significant byte first) to an mpint
+mpint*
+betomp(uchar *p, uint n, mpint *b)
+{
+	int m, s;
+	mpdigit x;
+
+	if(b == nil){
+		b = mpnew(0);
+		setmalloctag(b, getcallerpc(&p));
+	}
+	mpbits(b, n*8);
+
+	m = DIGITS(n*8);
+	b->top = m--;
+	b->sign = 1;
+
+	s = ((n-1)*8)%Dbits;
+	x = 0;
+	for(; n > 0; n--){
+		x |= ((mpdigit)(*p++)) << s;
+		s -= 8;
+		if(s < 0){
+			b->p[m--] = x;
+			s = Dbits-8;
+			x = 0;
+		}
+	}
+	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 f;	
+
+	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->f.m.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->f.reduce = cnreduce;
+	f->f.m.flags |= MPfield;
+out:
+	mpfree(M);
+	mpfree(C);
+
+	return (Mfield*)f;
+}
--- /dev/null
+++ b/libmp/crt.c
@@ -1,0 +1,121 @@
+#include "os.h"
+#include <mp.h>
+
+// chinese remainder theorem
+//
+// handbook of applied cryptography, menezes et al, 1997, pp 610 - 613
+
+struct CRTpre
+{
+	int	n;		// number of moduli
+	mpint	**m;		// pointer to moduli
+	mpint	**c;		// precomputed coefficients
+	mpint	**p;		// precomputed products
+	mpint	*a[1];		// local storage
+};
+
+// setup crt info, returns a newly created structure
+CRTpre*
+crtpre(int n, mpint **m)
+{
+	CRTpre *crt;
+	int i, j;
+	mpint *u;
+
+	crt = malloc(sizeof(CRTpre)+sizeof(mpint)*3*n);
+	if(crt == nil)
+		sysfatal("crtpre: %r");
+	crt->m = crt->a;
+	crt->c = crt->a+n;
+	crt->p = crt->c+n;
+	crt->n = n;
+
+	// make a copy of the moduli
+	for(i = 0; i < n; i++)
+		crt->m[i] = mpcopy(m[i]);
+
+	// precompute the products
+	u = mpcopy(mpone);
+	for(i = 0; i < n; i++){
+		mpmul(u, m[i], u);
+		crt->p[i] = mpcopy(u);
+	}
+
+	// precompute the coefficients
+	for(i = 1; i < n; i++){
+		crt->c[i] = mpcopy(mpone);
+		for(j = 0; j < i; j++){
+			mpinvert(m[j], m[i], u);
+			mpmul(u, crt->c[i], u);
+			mpmod(u, m[i], crt->c[i]);
+		}
+	}
+
+	mpfree(u);
+
+	return crt;		
+}
+
+void
+crtprefree(CRTpre *crt)
+{
+	int i;
+
+	for(i = 0; i < crt->n; i++){
+		if(i != 0)
+			mpfree(crt->c[i]);
+		mpfree(crt->p[i]);
+		mpfree(crt->m[i]);
+	}
+	free(crt);
+}
+
+// convert to residues, returns a newly created structure
+CRTres*
+crtin(CRTpre *crt, mpint *x)
+{
+	int i;
+	CRTres *res;
+
+	res = malloc(sizeof(CRTres)+sizeof(mpint)*crt->n);
+	if(res == nil)
+		sysfatal("crtin: %r");
+	res->n = crt->n;
+	for(i = 0; i < res->n; i++){
+		res->r[i] = mpnew(0);
+		mpmod(x, crt->m[i], res->r[i]);
+	}
+	return res;
+}
+
+// garners algorithm for converting residue form to linear
+void
+crtout(CRTpre *crt, CRTres *res, mpint *x)
+{
+	mpint *u;
+	int i;
+
+	u = mpnew(0);
+	mpassign(res->r[0], x);
+
+	for(i = 1; i < crt->n; i++){
+		mpsub(res->r[i], x, u);
+		mpmul(u, crt->c[i], u);
+		mpmod(u, crt->m[i], u);
+		mpmul(u, crt->p[i-1], u);
+		mpadd(x, u, x);
+	}
+
+	mpfree(u);
+}
+
+// free the residue
+void
+crtresfree(CRTres *res)
+{
+	int i;
+
+	for(i = 0; i < res->n; i++)
+		mpfree(res->r[i]);
+	free(res);
+}
--- /dev/null
+++ b/libmp/crttest.c
@@ -1,0 +1,52 @@
+#include "os.h"
+#include <mp.h>
+
+void
+testcrt(mpint **p)
+{
+	CRTpre *crt;
+	CRTres *res;
+	mpint *m, *x, *y;
+
+	fmtinstall('B', mpfmt);
+
+	// get a modulus and a test number
+	m = mpnew(1024+160);
+	mpmul(p[0], p[1], m);
+	x = mpnew(1024+160);
+	mpadd(m, mpone, x);
+
+	// do the precomputation for crt conversion
+	crt = crtpre(2, p);
+
+	// convert x to residues
+	res = crtin(crt, x);
+
+	// convert back
+	y = mpnew(1024+160);
+	crtout(crt, res, y);
+	print("x %B\ny %B\n", x, y);
+	mpfree(m);
+	mpfree(x);
+	mpfree(y);
+}
+
+void
+main(void)
+{
+	int i;
+	mpint *p[2];
+	long start;
+
+	start = time(0);
+	for(i = 0; i < 10; i++){
+		p[0] = mpnew(1024);
+		p[1] = mpnew(1024);
+		DSAprimes(p[0], p[1], nil);
+		testcrt(p);
+		mpfree(p[0]);
+		mpfree(p[1]);
+	}
+	print("%ld secs with more\n", time(0)-start);
+	exits(0);
+}
--- /dev/null
+++ b/libmp/dat.h
@@ -1,0 +1,12 @@
+#define	mpdighi  (mpdigit)(1<<(Dbits-1))
+#define DIGITS(x) ((Dbits - 1 + (x))/Dbits)
+
+// for converting between int's and mpint's
+#define MAXUINT ((uint)-1)
+#define MAXINT (MAXUINT>>1)
+#define MININT (MAXINT+1)
+
+// for converting between vlongs's and mpint's
+#define MAXUVLONG (~0ULL)
+#define MAXVLONG (MAXUVLONG>>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 f;	
+
+	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->f.m.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->f.m.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->f.m.p, d, r->p[d], r->p);
+	r->p[d] = 0;
+
+	mpvecsub(r->p, d+1, g->f.m.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->f.reduce = gmreduce;
+	g->f.m.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;
+}
+
--- /dev/null
+++ b/libmp/letomp.c
@@ -1,0 +1,31 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// convert a little endian byte array (least significant byte first) to an mpint
+mpint*
+letomp(uchar *s, uint n, mpint *b)
+{
+	int i=0, m = 0;
+	mpdigit x=0;
+
+	if(b == nil){
+		b = mpnew(0);
+		setmalloctag(b, getcallerpc(&s));
+	}
+	mpbits(b, 8*n);
+	for(; n > 0; n--){
+		x |= ((mpdigit)(*s++)) << i;
+		i += 8;
+		if(i == Dbits){
+			b->p[m++] = x;
+			i = 0;
+			x = 0;
+		}
+	}
+	if(i > 0)
+		b->p[m++] = x;
+	b->top = m;
+	b->sign = 1;
+	return mpnorm(b);
+}
--- /dev/null
+++ b/libmp/mpadd.c
@@ -1,0 +1,58 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// sum = abs(b1) + abs(b2), i.e., add the magnitudes
+void
+mpmagadd(mpint *b1, mpint *b2, mpint *sum)
+{
+	int m, n;
+	mpint *t;
+
+	sum->flags |= (b1->flags | b2->flags) & MPtimesafe;
+
+	// get the sizes right
+	if(b2->top > b1->top){
+		t = b1;
+		b1 = b2;
+		b2 = t;
+	}
+	n = b1->top;
+	m = b2->top;
+	if(n == 0){
+		mpassign(mpzero, sum);
+		return;
+	}
+	if(m == 0){
+		mpassign(b1, sum);
+		sum->sign = 1;
+		return;
+	}
+	mpbits(sum, (n+1)*Dbits);
+	sum->top = n+1;
+
+	mpvecadd(b1->p, n, b2->p, m, sum->p);
+	sum->sign = 1;
+
+	mpnorm(sum);
+}
+
+// sum = b1 + b2
+void
+mpadd(mpint *b1, mpint *b2, mpint *sum)
+{
+	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
+			mpmagsub(b1, b2, sum);
+	} else {
+		sign = b1->sign;
+		mpmagadd(b1, b2, sum);
+		if(sum->top != 0)
+			sum->sign = sign;
+	}
+}
--- /dev/null
+++ b/libmp/mpaux.c
@@ -1,0 +1,205 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+static mpdigit _mptwodata[1] = { 2 };
+static mpint _mptwo =
+{
+	1, 1, 1,
+	_mptwodata,
+	MPstatic|MPnorm
+};
+mpint *mptwo = &_mptwo;
+
+static mpdigit _mponedata[1] = { 1 };
+static mpint _mpone =
+{
+	1, 1, 1,
+	_mponedata,
+	MPstatic|MPnorm
+};
+mpint *mpone = &_mpone;
+
+static mpdigit _mpzerodata[1] = { 0 };
+static mpint _mpzero =
+{
+	1, 1, 0,
+	_mpzerodata,
+	MPstatic|MPnorm
+};
+mpint *mpzero = &_mpzero;
+
+static int mpmindigits = 33;
+
+// set minimum digit allocation
+void
+mpsetminbits(int n)
+{
+	if(n < 0)
+		sysfatal("mpsetminbits: n < 0");
+	if(n == 0)
+		n = 1;
+	mpmindigits = DIGITS(n);
+}
+
+// allocate an n bit 0'd number 
+mpint*
+mpnew(int n)
+{
+	mpint *b;
+
+	if(n < 0)
+		sysfatal("mpsetminbits: n < 0");
+
+	n = DIGITS(n);
+	if(n < mpmindigits)
+		n = mpmindigits;
+	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;
+}
+
+// guarantee at least n significant bits
+void
+mpbits(mpint *b, int m)
+{
+	int n;
+
+	n = DIGITS(m);
+	if(b->size >= n){
+		if(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;
+	}
+	memset(&b->p[b->top], 0, Dbytes*(n - b->top));
+	b->top = n;
+	b->flags &= ~MPnorm;
+}
+
+void
+mpfree(mpint *b)
+{
+	if(b == nil)
+		return;
+	if(b->flags & MPstatic)
+		sysfatal("freeing mp constant");
+	memset(b->p, 0, b->size*Dbytes);
+	if(b->p != (mpdigit*)&b[1])
+		free(b->p);
+	free(b);
+}
+
+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;
+	b->top = i+1;
+	if(b->top == 0)
+		b->sign = 1;
+	b->flags |= MPnorm;
+	return b;
+}
+
+mpint*
+mpcopy(mpint *old)
+{
+	mpint *new;
+
+	new = mpnew(Dbits*old->size);
+	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;
+}
+
+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);
+}
+
+// number of significant bits in mantissa
+int
+mpsignif(mpint *n)
+{
+	int i, j;
+	mpdigit d;
+
+	if(n->top == 0)
+		return 0;
+	for(i = n->top-1; i >= 0; i--){
+		d = n->p[i];
+		for(j = Dbits-1; j >= 0; j--){
+			if(d & (((mpdigit)1)<<j))
+				return i*Dbits + j + 1;
+		}
+	}
+	return 0;
+}
+
+// k, where n = 2**k * q for odd q
+int
+mplowbits0(mpint *n)
+{
+	int k, bit, digit;
+	mpdigit d;
+
+	assert(n->flags & MPnorm);
+	if(n->top==0)
+		return 0;
+	k = 0;
+	bit = 0;
+	digit = 0;
+	d = n->p[0];
+	for(;;){
+		if(d & (1<<bit))
+			break;
+		k++;
+		bit++;
+		if(bit==Dbits){
+			if(++digit >= n->top)
+				return 0;
+			d = n->p[digit];
+			bit = 0;
+		}
+	}
+	return k;
+}
--- /dev/null
+++ b/libmp/mpcmp.c
@@ -1,0 +1,30 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// return neg, 0, pos as abs(b1)-abs(b2) is neg, 0, pos
+int
+mpmagcmp(mpint *b1, mpint *b2)
+{
+	int 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);
+}
+
+// return neg, 0, pos as b1-b2 is neg, 0, pos
+int
+mpcmp(mpint *b1, mpint *b2)
+{
+	int sign;
+
+	sign = (b1->sign - b2->sign) >> 1;	// -1, 0, 1
+	return sign | (sign&1)-1 & mpmagcmp(b1, b2)*b1->sign;
+}
--- /dev/null
+++ b/libmp/mpdigdiv.c
@@ -1,0 +1,56 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+//
+//	divide two digits by one and return quotient
+//
+void
+mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient)
+{
+	mpdigit hi, lo, q, x, y;
+	int i;
+
+	hi = dividend[1];
+	lo = dividend[0];
+
+	// return highest digit value if the result >= 2**32
+	if(hi >= divisor || divisor == 0){
+		divisor = 0;
+		*quotient = ~divisor;
+		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;
+	x = divisor;
+	for(i = Dbits-1; hi > 0 && i >= 0; i--){
+		x >>= 1;
+		if(x > hi)
+			continue;
+		y = divisor<<i;
+		if(x == hi && y > lo)
+			continue;
+		if(y > lo)
+			hi--;
+		lo -= y;
+		hi -= x;
+		q |= 1<<i;
+	}
+	q += lo/divisor;
+	*quotient = q;
+}
--- /dev/null
+++ b/libmp/mpdiv.c
@@ -1,0 +1,142 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// division ala knuth, seminumerical algorithms, pp 237-238
+// the numbers are stored backwards to what knuth expects so j
+// counts down rather than up.
+
+void
+mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder)
+{
+	int j, s, vn, sign, qsign, rsign;
+	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 = 0;
+		if(dividend->top > 0)
+			r = (vlong)dividend->sign * (dividend->p[0] & divisor->p[0]-1);
+		if(quotient != nil){
+			sign = divisor->sign;
+			for(s = 0; ((divisor->p[0] >> s) & 1) == 0; s++)
+				;
+			mpright(dividend, s, quotient);
+			if(sign < 0)
+				quotient->sign ^= (-mpmagcmp(quotient, mpzero) >> 31) << 1;
+		}
+		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)
+			mpassign(dividend, remainder);
+		if(quotient != nil)
+			mpassign(mpzero, quotient);
+		return;
+	}
+	
+	qsign = divisor->sign * dividend->sign;
+	rsign = dividend->sign;
+
+	// D1: shift until divisor, v, has hi bit set (needed to make trial
+	//     divisor accurate)
+	qd = divisor->p[divisor->top-1];
+	for(s = 0; (qd & mpdighi) == 0; s++)
+		qd <<= 1;
+	u = mpnew((dividend->top+2)*Dbits + s);
+	if(s == 0 && divisor != quotient && divisor != remainder) {
+		mpassign(dividend, u);
+		v = divisor;
+	} else {
+		mpleft(dividend, s, u);
+		v = mpnew(divisor->top*Dbits);
+		mpleft(divisor, s, v);
+	}
+	up = u->p+u->top-1;
+	vp = v->p+v->top-1;
+	vn = v->top;
+
+	// D1a: make sure high digit of dividend is less than high digit of divisor
+	if(*up >= *vp){
+		*++up = 0;
+		u->top++;
+	}
+
+	// storage for multiplies
+	t = mpnew(4*Dbits);
+
+	qp = nil;
+	if(quotient != nil){
+		mpbits(quotient, (u->top - v->top)*Dbits);
+		quotient->top = u->top - v->top;
+		qp = quotient->p+quotient->top-1;
+	}
+
+	// D2, D7: loop on length of dividend
+	for(j = u->top; j > vn; j--){
+
+		// D3: calculate trial divisor
+		mpdigdiv(up-1, *vp, &qd);
+
+		// D3a: rule out trial divisors 2 greater than real divisor
+		if(vn > 1) for(;;){
+			memset(t->p, 0, 3*Dbytes);	// mpvecdigmuladd adds to what's there
+			mpvecdigmuladd(vp-1, 2, qd, t->p);
+			if(mpveccmp(t->p, 3, up-2, 3) > 0)
+				qd--;
+			else
+				break;
+		}
+
+		// D4: u -= v*qd << j*Dbits
+		sign = mpvecdigmulsub(v->p, vn, qd, up-vn);
+		if(sign < 0){
+
+			// D6: trial divisor was too high, add back borrowed
+			//     value and decrease divisor
+			mpvecadd(up-vn, vn+1, v->p, vn, up-vn);
+			qd--;
+		}
+
+		// D5: save quotient digit
+		if(qp != nil)
+			*qp-- = qd;
+
+		// push top of u down one
+		u->top--;
+		*up-- = 0;
+	}
+	if(qp != nil){
+		assert((quotient->flags & MPtimesafe) == 0);
+		mpnorm(quotient);
+		if(quotient->top != 0)
+			quotient->sign = qsign;
+	}
+
+	if(remainder != nil){
+		assert((remainder->flags & MPtimesafe) == 0);
+		mpright(u, s, remainder);	// u is the remainder shifted
+		if(remainder->top != 0)
+			remainder->sign = rsign;
+	}
+
+	mpfree(t);
+	mpfree(u);
+	if(v != divisor)
+		mpfree(v);
+}
--- /dev/null
+++ b/libmp/mpexp.c
@@ -1,0 +1,96 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = b**e
+//
+// knuth, vol 2, pp 398-400
+
+enum {
+	Freeb=	0x1,
+	Freee=	0x2,
+	Freem=	0x4,
+};
+
+//int expdebug;
+
+void
+mpexp(mpint *b, mpint *e, mpint *m, mpint *res)
+{
+	mpint *t[2];
+	int tofree;
+	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;
+
+	tofree = 0;
+	if(res == b){
+		b = mpcopy(b);
+		tofree |= Freeb;
+	}
+	if(res == e){
+		e = mpcopy(e);
+		tofree |= Freee;
+	}
+	if(res == m){
+		m = mpcopy(m);
+		tofree |= Freem;
+	}
+
+	// skip first bit
+	i = e->top-1;
+	d = e->p[i];
+	for(bit = mpdighi; (bit & d) == 0; bit >>= 1)
+		;
+	bit >>= 1;
+
+	j = 0;
+	for(;;){
+		for(; bit != 0; bit >>= 1){
+			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(--i < 0)
+			break;
+		bit = mpdighi;
+		d = e->p[i];
+	}
+	if(t[j] == res){
+		mpfree(t[j^1]);
+	} else {
+		mpassign(t[j], res);
+		mpfree(t[j]);
+	}
+
+	if(tofree){
+		if(tofree & Freeb)
+			mpfree(b);
+		if(tofree & Freee)
+			mpfree(e);
+		if(tofree & Freem)
+			mpfree(m);
+	}
+}
--- /dev/null
+++ b/libmp/mpextendedgcd.c
@@ -1,0 +1,115 @@
+#include "os.h"
+#include <mp.h>
+
+#define iseven(a)	(((a)->p[0] & 1) == 0)
+
+// extended binary gcd
+//
+// 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.  
+void
+mpextendedgcd(mpint *a, mpint *b, mpint *v, mpint *x, mpint *y)
+{
+	mpint *u, *A, *B, *C, *D;
+	int g;
+
+	if(v == nil){
+		v = mpnew(0);
+		mpextendedgcd(a, b, v, x, y);
+		mpfree(v);
+		return;
+	}
+	assert(x == nil || (x->flags & MPtimesafe) == 0);
+	assert(y == nil || (y->flags & MPtimesafe) == 0);
+	assert((a->flags&b->flags) & MPnorm);
+	assert(((a->flags|b->flags|v->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);
+		mpassign(mpzero, x);
+		return;
+	}
+	if(b->top == 0){
+		mpassign(a, v);
+		mpassign(mpone, x);
+		mpassign(mpzero, y);
+		return;
+	}
+
+	g = 0;
+	a = mpcopy(a);
+	b = mpcopy(b);
+
+	while(iseven(a) && iseven(b)){
+		mpright(a, 1, a);
+		mpright(b, 1, b);
+		g++;
+	}
+
+	u = mpcopy(a);
+	mpassign(b, v);
+	A = mpcopy(mpone);
+	B = mpcopy(mpzero);
+	C = mpcopy(mpzero);
+	D = mpcopy(mpone);
+
+	for(;;) {
+//		print("%B %B %B %B %B %B\n", u, v, A, B, C, D);
+		while(iseven(u)){
+			mpright(u, 1, u);
+			if(!iseven(A) || !iseven(B)) {
+				mpadd(A, b, A);
+				mpsub(B, a, B);
+			}
+			mpright(A, 1, A);
+			mpright(B, 1, B);
+		}
+	
+//		print("%B %B %B %B %B %B\n", u, v, A, B, C, D);
+		while(iseven(v)){
+			mpright(v, 1, v);
+			if(!iseven(C) || !iseven(D)) {
+				mpadd(C, b, C);
+				mpsub(D, a, D);
+			}
+			mpright(C, 1, C);
+			mpright(D, 1, D);
+		}
+	
+//		print("%B %B %B %B %B %B\n", u, v, A, B, C, D);
+		if(mpcmp(u, v) >= 0){
+			mpsub(u, v, u);
+			mpsub(A, C, A);
+			mpsub(B, D, B);
+		} else {
+			mpsub(v, u, v);
+			mpsub(C, A, C);
+			mpsub(D, B, D);
+		}
+
+		if(u->top == 0)
+			break;
+
+	}
+	mpassign(C, x);
+	mpassign(D, y);
+	mpleft(v, g, v);
+
+	mpfree(A);
+	mpfree(B);
+	mpfree(C);
+	mpfree(D);
+	mpfree(u);
+	mpfree(a);
+	mpfree(b);
+}
--- /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;
+}
--- /dev/null
+++ b/libmp/mpfmt.c
@@ -1,0 +1,254 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+static int
+toencx(mpint *b, char *buf, int len, int (*enc)(char*, int, uchar*, int))
+{
+	uchar *p;
+	int n, rv;
+
+	p = nil;
+	n = mptobe(b, nil, 0, &p);
+	if(n < 0)
+		return -1;
+	rv = (*enc)(buf, len, p, n);
+	free(p);
+	return rv;
+}
+
+static int
+topow2(mpint *b, char *buf, int len, int s)
+{
+	mpdigit *p, x;
+	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-s; i >= 0; i -= s){
+			j = x >> i & sn - 1;
+			if(j != 0 || out != buf){
+				if(out >= eout)
+					return -1;
+				*out++ = enc16chr(j);
+			}
+		}
+	}
+	if(out == buf)
+		*out++ = '0';
+	if(out >= eout)
+		return -1;
+	*out = 0;
+	return 0;
+}
+
+static char*
+modbillion(int rem, ulong r, char *out, char *buf)
+{
+	ulong rr;
+	int i;
+
+	for(i = 0; i < 9; i++){
+		rr = r%10;
+		r /= 10;
+		if(out <= buf)
+			return nil;
+		*--out = '0' + rr;
+		if(rem == 0 && r == 0)
+			break;
+	}
+	return out;
+}
+
+static int
+to10(mpint *b, char *buf, int len)
+{
+	mpint *d, *r, *billion;
+	char *out;
+
+	if(len < 1)
+		return -1;
+
+	d = mpcopy(b);
+	d->flags &= ~MPtimesafe;
+	mpnorm(d);
+	r = mpnew(0);
+	billion = uitomp(1000000000, nil);
+	out = buf+len;
+	*--out = 0;
+	do {
+		mpdiv(d, billion, d, r);
+		out = modbillion(d->top, r->p[0], out, buf);
+		if(out == nil)
+			break;
+	} while(d->top != 0);
+	mpfree(d);
+	mpfree(r);
+	mpfree(billion);
+
+	if(out == nil)
+		return -1;
+	len -= out-buf;
+	if(out != buf)
+		memmove(buf, out, len);
+	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 *x, *p;
+	int base;
+
+	b = va_arg(fmt->args, mpint*);
+	if(b == nil)
+		return fmtstrcpy(fmt, "*");
+
+	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{
+		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;
+	}
+}
+
+char*
+mptoa(mpint *b, int base, char *buf, int len)
+{
+	char *out;
+	int rv, alloced;
+
+	if(base == 0)
+		base = 16;	/* default */
+	alloced = 0;
+	if(buf == nil){
+		/* rv <= log₂(base) */
+		for(rv=1; (base >> rv) > 1; rv++)
+			;
+		len = 10 + (b->top*Dbits / rv);
+		buf = malloc(len);
+		if(buf == nil)
+			return nil;
+		alloced = 1;
+	}
+
+	if(len < 2)
+		return nil;
+
+	out = buf;
+	if(b->sign < 0){
+		*out++ = '-';
+		len--;
+	}
+	switch(base){
+	case 64:
+		rv = toencx(b, out, len, enc64);
+		break;
+	case 32:
+		rv = toencx(b, out, len, enc32);
+		break;
+	case 16:
+		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)
+			free(buf);
+		return nil;
+	}
+	return buf;
+}
--- /dev/null
+++ b/libmp/mpinvert.c
@@ -1,0 +1,17 @@
+#include "os.h"
+#include <mp.h>
+
+// use extended gcd to find the multiplicative inverse
+// res = b**-1 mod m
+void
+mpinvert(mpint *b, mpint *m, mpint *res)
+{
+	mpint *v;
+
+	v = mpnew(0);
+	mpextendedgcd(b, m, v, res, nil);
+	if(mpcmp(v, mpone) != 0)
+		abort();
+	mpfree(v);
+	mpmod(res, m, res);
+}
--- /dev/null
+++ b/libmp/mpleft.c
@@ -1,0 +1,51 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = b << shift
+void
+mpleft(mpint *b, int shift, mpint *res)
+{
+	int d, l, r, i, otop;
+	mpdigit this, last;
+
+	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;
+	}
+
+	// b and res may be the same so remember the old top
+	otop = b->top;
+
+	// shift
+	mpbits(res, otop*Dbits + shift);	// overkill
+	res->top = DIGITS(otop*Dbits + shift);
+	d = shift/Dbits;
+	l = shift - d*Dbits;
+	r = Dbits - l;
+
+	if(l == 0){
+		for(i = otop-1; i >= 0; i--)
+			res->p[i+d] = b->p[i];
+	} else {
+		last = 0;
+		for(i = otop-1; i >= 0; i--) {
+			this = b->p[i];
+			res->p[i+d+1] = (last<<l) | (this>>r);
+			last = this;
+		}
+		res->p[d] = last<<l;
+	}
+	for(i = 0; i < d; i++)
+		res->p[i] = 0;
+
+	res->flags |= b->flags & MPtimesafe;
+	mpnorm(res);
+}
--- /dev/null
+++ b/libmp/mplogic.c
@@ -1,0 +1,212 @@
+#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+1);
+	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;
+	if(co)
+		dpo[sum->top++] = co;
+	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);
+	if(r->top != 0)
+		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 | 1;
+	mpbits(sum, b1->top*Dbits+1);
+	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;
+	if(co)
+		dpo[sum->top++] = co;
+	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);
+			mpnorm(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);
+}
+
+void
+mpasr(mpint *b, int n, mpint *r)
+{
+	if(b->sign > 0 || n <= 0){
+		mpright(b, n, r);
+		return;
+	}
+	mpadd(b, mpone, r);
+	mpright(r, n, r);
+	mpsub(r, mpone, r);
+}
--- /dev/null
+++ b/libmp/mpmod.c
@@ -1,0 +1,20 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+void
+mpmod(mpint *x, mpint *n, mpint *r)
+{
+	int sign;
+	mpint *ns;
+
+	sign = x->sign;
+	ns = sign < 0 && n == r ? mpcopy(n) : n;
+	if((n->flags & MPfield) == 0
+	|| ((Mfield*)n)->reduce((Mfield*)n, x, r) != 0)
+		mpdiv(x, n, nil, r);
+	if(sign < 0){
+		mpmagsub(ns, r, r);
+		if(ns != n) mpfree(ns);
+	}
+}
--- /dev/null
+++ b/libmp/mpmodop.c
@@ -1,0 +1,95 @@
+#include "os.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);
+}
--- /dev/null
+++ b/libmp/mpmul.c
@@ -1,0 +1,176 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+//
+//  from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
+//
+//  mpvecmul is an assembly language routine that performs the inner
+//  loop.
+//
+//  the karatsuba trade off is set empiricly by measuring the algs on
+//  a 400 MHz Pentium II.
+//
+
+// karatsuba like (see knuth pg 258)
+// prereq: p is already zeroed
+static void
+mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{
+	mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod;
+	int u0len, u1len, v0len, v1len, reslen;
+	int sign, n;
+
+	// divide each piece in half
+	n = alen/2;
+	if(alen&1)
+		n++;
+	u0len = n;
+	u1len = alen-n;
+	if(blen > n){
+		v0len = n;
+		v1len = blen-n;
+	} else {
+		v0len = blen;
+		v1len = 0;
+	}
+	u0 = a;
+	u1 = a + u0len;
+	v0 = b;
+	v1 = b + v0len;
+
+	// room for the partial products
+	t = mallocz(Dbytes*5*(2*n+1), 1);
+	if(t == nil)
+		sysfatal("mpkaratsuba: %r");
+	u0v0 = t;
+	u1v1 = t + (2*n+1);
+	diffprod = t + 2*(2*n+1);
+	res = t + 3*(2*n+1);
+	reslen = 4*n+1;
+
+	// t[0] = (u1-u0)
+	sign = 1;
+	if(mpveccmp(u1, u1len, u0, u0len) < 0){
+		sign = -1;
+		mpvecsub(u0, u0len, u1, u1len, u0v0);
+	} else
+		mpvecsub(u1, u1len, u0, u1len, u0v0);
+
+	// t[1] = (v0-v1)
+	if(mpveccmp(v0, v0len, v1, v1len) < 0){
+		sign *= -1;
+		mpvecsub(v1, v1len, v0, v1len, u1v1);
+	} else
+		mpvecsub(v0, v0len, v1, v1len, u1v1);
+
+	// t[4:5] = (u1-u0)*(v0-v1)
+	mpvecmul(u0v0, u0len, u1v1, v0len, diffprod);
+
+	// t[0:1] = u1*v1
+	memset(t, 0, 2*(2*n+1)*Dbytes);
+	if(v1len > 0)
+		mpvecmul(u1, u1len, v1, v1len, u1v1);
+
+	// t[2:3] = u0v0
+	mpvecmul(u0, u0len, v0, v0len, u0v0);
+
+	// res = u0*v0<<n + u0*v0
+	mpvecadd(res, reslen, u0v0, u0len+v0len, res);
+	mpvecadd(res+n, reslen-n, u0v0, u0len+v0len, res+n);
+
+	// res += u1*v1<<n + u1*v1<<2*n
+	if(v1len > 0){
+		mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n);
+		mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n);
+	}
+
+	// res += (u1-u0)*(v0-v1)<<n
+	if(sign < 0)
+		mpvecsub(res+n, reslen-n, diffprod, u0len+v0len, res+n);
+	else
+		mpvecadd(res+n, reslen-n, diffprod, u0len+v0len, res+n);
+	memmove(p, res, (alen+blen)*Dbytes);
+
+	free(t);
+}
+
+#define KARATSUBAMIN 32
+
+void
+mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{
+	int i;
+	mpdigit d;
+	mpdigit *t;
+
+	// both mpvecdigmuladd and karatsuba are fastest when a is the longer vector
+	if(alen < blen){
+		i = alen;
+		alen = blen;
+		blen = i;
+		t = a;
+		a = b;
+		b = t;
+	}
+
+	if(alen >= KARATSUBAMIN && blen > 1){
+		// O(n^1.585)
+		mpkaratsuba(a, alen, b, blen, p);
+	} else {
+		// O(n^2)
+		for(i = 0; i < blen; i++){
+			d = b[i];
+			if(d != 0)
+				mpvecdigmuladd(a, alen, d, &p[i]);
+		}
+	}
+}
+
+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 = prod;
+	if(prod == b1 || prod == b2){
+		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);
+	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 != prod){
+		mpassign(prod, oprod);
+		mpfree(prod);
+	}
+}
--- /dev/null
+++ b/libmp/mpnrand.c
@@ -1,0 +1,23 @@
+#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)
+{
+	int bits;
+
+	bits = mpsignif(n);
+	if(bits == 0)
+		abort();
+	if(b == nil){
+		b = mpnew(bits);
+		setmalloctag(b, getcallerpc(&n));
+	}
+	do {
+		mprand(bits, gen, b);
+	} while(mpmagcmp(b, n) >= 0);
+
+	return b;
+}
--- /dev/null
+++ b/libmp/mprand.c
@@ -1,0 +1,25 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+mpint*
+mprand(int bits, void (*gen)(uchar*, int), mpint *b)
+{
+	mpdigit mask;
+
+	if(b == nil){
+		b = mpnew(bits);
+		setmalloctag(b, getcallerpc(&bits));
+	}else
+		mpbits(b, bits);
+
+	b->sign = 1;
+	b->top = DIGITS(bits);
+	(*gen)((uchar*)b->p, b->top*Dbytes);
+
+	mask = ((mpdigit)1 << (bits%Dbits))-1;
+	if(mask != 0)
+		b->p[b->top-1] &= mask;
+
+	return mpnorm(b);
+}
--- /dev/null
+++ b/libmp/mpright.c
@@ -1,0 +1,57 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = b >> shift
+void
+mpright(mpint *b, int shift, mpint *res)
+{
+	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);
+		return;
+	}
+
+	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++)
+			res->p[i] = b->p[i+d];
+	} else {
+		last = b->p[d];
+		for(i = 0; i < b->top-d-1; i++){
+			this = b->p[i+d+1];
+			res->p[i] = (this<<l) | (last>>r);
+			last = this;
+		}
+		res->p[i++] = last>>r;
+	}
+
+	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++;
+	}
+}
--- /dev/null
+++ b/libmp/mpsub.c
@@ -1,0 +1,56 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// diff = abs(b1) - abs(b2), i.e., subtract the magnitudes
+void
+mpmagsub(mpint *b1, mpint *b2, mpint *diff)
+{
+	int n, m, sign;
+	mpint *t;
+
+	// 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 {
+		diff->flags |= (b1->flags | b2->flags) & MPtimesafe;
+		sign = 1;
+	}
+	n = b1->top;
+	m = b2->top;
+	if(m == 0){
+		mpassign(b1, diff);
+		diff->sign = sign;
+		return;
+	}
+	mpbits(diff, n*Dbits);
+
+	mpvecsub(b1->p, n, b2->p, m, diff->p);
+	diff->sign = sign;
+	diff->top = n;
+	mpnorm(diff);
+}
+
+// diff = b1 - b2
+void
+mpsub(mpint *b1, mpint *b2, mpint *diff)
+{
+	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;
+		return;
+	}
+
+	sign = b1->sign;
+	mpmagsub(b1, b2, diff);
+	if(diff->top != 0)
+		diff->sign *= sign;
+}
--- /dev/null
+++ b/libmp/mptobe.c
@@ -1,0 +1,32 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// 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 m;
+
+	m = (mpsignif(b)+7)/8;
+	if(m == 0)
+		m++;
+	if(p == nil){
+		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(pp != nil)
+		*pp = p;
+	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;
+		}
+	}
+}
--- /dev/null
+++ b/libmp/mptoi.c
@@ -1,0 +1,44 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ *  this code assumes that mpdigit is at least as
+ *  big as an int.
+ */
+
+mpint*
+itomp(int i, mpint *b)
+{
+	if(b == nil){
+		b = mpnew(0);
+		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
+mptoi(mpint *b)
+{
+	uint x;
+
+	if(b->top==0)
+		return 0;
+	x = *b->p;
+	if(b->sign > 0){
+		if(b->top > 1 || (x > MAXINT))
+			x = (int)MAXINT;
+		else
+			x = (int)x;
+	} else {
+		if(b->top > 1 || x > MAXINT+1)
+			x = (int)MININT;
+		else
+			x = -(int)x;
+	}
+	return x;
+}
--- /dev/null
+++ b/libmp/mptole.c
@@ -1,0 +1,28 @@
+#include "os.h"
+#include <mp.h>
+#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 m;
+
+	m = (mpsignif(b)+7)/8;
+	if(m == 0)
+		m++;
+	if(p == nil){
+		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;
+	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;
+		}
+	}
+}
--- /dev/null
+++ b/libmp/mptoui.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ *  this code assumes that mpdigit is at least as
+ *  big as an int.
+ */
+
+mpint*
+uitomp(uint i, mpint *b)
+{
+	if(b == nil){
+		b = mpnew(0);
+		setmalloctag(b, getcallerpc(&i));
+	}
+	*b->p = i;
+	b->top = 1;
+	b->sign = 1;
+	return mpnorm(b);
+}
+
+uint
+mptoui(mpint *b)
+{
+	uint x;
+
+	x = *b->p;
+	if(b->sign < 0)
+		x = 0;
+	else if(b->top > 1 || (sizeof(mpdigit) > sizeof(uint) && x > MAXUINT))
+		x =  MAXUINT;
+	return x;
+}
--- /dev/null
+++ b/libmp/mptouv.c
@@ -1,0 +1,47 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+#define VLDIGITS (sizeof(vlong)/sizeof(mpdigit))
+
+/*
+ *  this code assumes that a vlong is an integral number of
+ *  mpdigits long.
+ */
+mpint*
+uvtomp(uvlong v, mpint *b)
+{
+	int s;
+
+	if(b == nil){
+		b = mpnew(VLDIGITS*Dbits);
+		setmalloctag(b, getcallerpc(&v));
+	}else
+		mpbits(b, VLDIGITS*Dbits);
+	b->sign = 1;
+	for(s = 0; s < VLDIGITS; s++){
+		b->p[s] = v;
+		v >>= sizeof(mpdigit)*8;
+	}
+	b->top = s;
+	return mpnorm(b);
+}
+
+uvlong
+mptouv(mpint *b)
+{
+	uvlong v;
+	int s;
+
+	if(b->top == 0 || b->sign < 0)
+		return 0LL;
+
+	if(b->top > VLDIGITS)
+		return -1LL;
+
+	v = 0ULL;
+	for(s = 0; s < b->top; s++)
+		v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
+
+	return v;
+}
--- /dev/null
+++ b/libmp/mptov.c
@@ -1,0 +1,63 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+#define VLDIGITS (sizeof(vlong)/sizeof(mpdigit))
+
+/*
+ *  this code assumes that a vlong is an integral number of
+ *  mpdigits long.
+ */
+mpint*
+vtomp(vlong v, mpint *b)
+{
+	int s;
+	uvlong uv;
+
+	if(b == nil){
+		b = mpnew(VLDIGITS*Dbits);
+		setmalloctag(b, getcallerpc(&v));
+	}else
+		mpbits(b, VLDIGITS*Dbits);
+	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 mpnorm(b);
+}
+
+vlong
+mptov(mpint *b)
+{
+	uvlong v;
+	int s;
+
+	if(b->top == 0)
+		return 0LL;
+
+	if(b->top > VLDIGITS){
+		if(b->sign > 0)
+			return (vlong)MAXVLONG;
+		else
+			return (vlong)MINVLONG;
+	}
+
+	v = 0ULL;
+	for(s = 0; s < b->top; s++)
+		v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
+
+	if(b->sign > 0){
+		if(v > MAXVLONG)
+			v = MAXVLONG;
+	} else {
+		if(v > MINVLONG)
+			v = MINVLONG;
+		else
+			v = -(vlong)v;
+	}
+
+	return (vlong)v;
+}
--- /dev/null
+++ b/libmp/mpvecadd.c
@@ -1,0 +1,35 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// prereq: alen >= blen, sum has at least blen+1 digits
+void
+mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum)
+{
+	int i, carry;
+	mpdigit x, y;
+
+	carry = 0;
+	for(i = 0; i < blen; i++){
+		x = *a++;
+		y = *b++;
+		x += carry;
+		if(x < carry)
+			carry = 1;
+		else
+			carry = 0;
+		x += y;
+		if(x < y)
+			carry++;
+		*sum++ = x;
+	}
+	for(; i < alen; i++){
+		x = *a++ + carry;
+		if(x < carry)
+			carry = 1;
+		else
+			carry = 0;
+		*sum++ = x;
+	}
+	*sum = carry;
+}
--- /dev/null
+++ b/libmp/mpveccmp.c
@@ -1,0 +1,27 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+int
+mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen)
+{
+	mpdigit x;
+
+	while(alen > blen)
+		if(a[--alen] != 0)
+			return 1;
+	while(blen > alen)
+		if(b[--blen] != 0)
+			return -1;
+	while(alen > 0){
+		--alen;
+		x = a[alen] - b[alen];
+		if(x == 0)
+			continue;
+		if(x > a[alen])
+			return -1;
+		else
+			return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/libmp/mpvecdigmuladd.c
@@ -1,0 +1,103 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+#define LO(x) ((x) & ((1<<(Dbits/2))-1))
+#define HI(x) ((x) >> (Dbits/2))
+
+static void
+mpdigmul(mpdigit a, mpdigit b, mpdigit *p)
+{
+	mpdigit x, ah, al, bh, bl, p1, p2, p3, p4;
+	int carry;
+
+	// half digits
+	ah = HI(a);
+	al = LO(a);
+	bh = HI(b);
+	bl = LO(b);
+
+	// partial products
+	p1 = ah*bl;
+	p2 = bh*al;
+	p3 = bl*al;
+	p4 = ah*bh;
+
+	// p = ((p1+p2)<<(Dbits/2)) + (p4<<Dbits) + p3
+	carry = 0;
+	x = p1<<(Dbits/2);
+	p3 += x;
+	if(p3 < x)
+		carry++;
+	x = p2<<(Dbits/2);
+	p3 += x;
+	if(p3 < x)
+		carry++;
+	p4 += carry + HI(p1) + HI(p2);	// can't carry out of the high digit
+	p[0] = p3;
+	p[1] = p4;
+}
+
+// prereq: p must have room for n+1 digits
+void
+mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p)
+{
+	int i;
+	mpdigit carry, x, y, part[2];
+
+	carry = 0;
+	part[1] = 0;
+	for(i = 0; i < n; i++){
+		x = part[1] + carry;
+		if(x < carry)
+			carry = 1;
+		else
+			carry = 0;
+		y = *p;
+		mpdigmul(*b++, m, part);
+		x += part[0];
+		if(x < part[0])
+			carry++;
+		x += y;
+		if(x < y)
+			carry++;
+		*p++ = x;
+	}
+	*p = part[1] + carry;
+}
+
+// prereq: p must have room for n+1 digits
+int
+mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p)
+{
+	int i;
+	mpdigit x, y, part[2], borrow;
+
+	borrow = 0;
+	part[1] = 0;
+	for(i = 0; i < n; i++){
+		x = *p;
+		y = x - borrow;
+		if(y > x)
+			borrow = 1;
+		else
+			borrow = 0;
+		x = part[1];
+		mpdigmul(*b++, m, part);
+		x += part[0];
+		if(x < part[0])
+			borrow++;
+		x = y - x;
+		if(x > y)
+			borrow++;
+		*p++ = x;
+	}
+
+	x = *p;
+	y = x - borrow - part[1];
+	*p = y;
+	if(y > x)
+		return -1;
+	else
+		return 1;
+}
--- /dev/null
+++ b/libmp/mpvecsub.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// prereq: a >= b, alen >= blen, diff has at least alen digits
+void
+mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff)
+{
+	int i, borrow;
+	mpdigit x, y;
+
+	borrow = 0;
+	for(i = 0; i < blen; i++){
+		x = *a++;
+		y = *b++;
+		y += borrow;
+		if(y < borrow)
+			borrow = 1;
+		else
+			borrow = 0;
+		if(x < y)
+			borrow++;
+		*diff++ = x - y;
+	}
+	for(; i < alen; i++){
+		x = *a++;
+		y = x - borrow;
+		if(y > x)
+			borrow = 1;
+		else
+			borrow = 0;
+		*diff++ = y;
+	}
+}
--- /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;
+}
--- /dev/null
+++ b/libmp/os.h
@@ -1,0 +1,3 @@
+#include <u.h>
+#include <libc.h>
+
--- /dev/null
+++ b/libmp/reduce
@@ -1,0 +1,16 @@
+O=$1
+shift
+objtype=$1
+shift
+
+ls -p ../$objtype/*.[cs] >[2]/dev/null | sed 's/..$//' > /tmp/reduce.$pid
+#
+#	if empty directory, just return the input files
+#
+if (! ~ $status '|') {
+	echo $*
+	rm /tmp/reduce.$pid
+	exit 0
+}
+echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' '
+rm /tmp/reduce.$pid
--- /dev/null
+++ b/libmp/strtomp.c
@@ -1,0 +1,206 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+static char*
+frompow2(char *a, mpint *b, int s)
+{
+	char *p, *next;
+	mpdigit x;
+	int i;
+
+	i = 1<<s;
+	for(p = a; (dec16chr(*p) & 255) < i; p++)
+		;
+
+	mpbits(b, (p-a)*s);
+	b->top = 0;
+	next = p;
+
+	while(p > a){
+		x = 0;
+		for(i = 0; i < Dbits; i += s){
+			if(p <= a)
+				break;
+			x |= dec16chr(*--p)<<i;
+		}
+		b->p[b->top++] = x;
+	}
+	return next;
+}
+
+static char*
+from8(char *a, mpint *b)
+{
+	char *p, *next;
+	mpdigit x, y;
+	int i;
+
+	for(p = a; ((*p - '0') & 255) < 8; p++)
+		;
+
+	mpbits(b, (p-a)*3);
+	b->top = 0;
+	next = p;
+
+	i = 0;
+	x = y = 0;
+	while(p > a){
+		y = *--p - '0';
+		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
+};
+
+static char*
+from10(char *a, mpint *b)
+{
+	ulong x, y;
+	mpint *pow, *r;
+	int i;
+
+	pow = mpnew(0);
+	r = mpnew(0);
+
+	b->top = 0;
+	for(;;){
+		// do a billion at a time in native arithmetic
+		x = 0;
+		for(i = 0; i < 9; i++){
+			y = *a - '0';
+			if(y > 9)
+				break;
+			a++;
+			x *= 10;
+			x += y;
+		}
+		if(i == 0)
+			break;
+
+		// accumulate into mpint
+		uitomp(mppow10[i], pow);
+		uitomp(x, r);
+		mpmul(b, pow, b);
+		mpadd(b, r, b);
+		if(i < 9)
+			break;
+	}
+	mpfree(pow);
+	mpfree(r);
+	return a;
+}
+
+static char*
+fromdecx(char *a, mpint *b, int (*chr)(int), int (*dec)(uchar*, int, char*, int))
+{
+	char *buf = a;
+	uchar *p;
+	int n, m;
+
+	b->top = 0;
+	for(; (*chr)(*a) >= 0; a++)
+		;
+	n = a-buf;
+	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;
+}
+
+mpint*
+strtomp(char *a, char **pp, int base, mpint *b)
+{
+	int sign;
+	char *e;
+
+	if(b == nil){
+		b = mpnew(0);
+		setmalloctag(b, getcallerpc(&a));
+	}
+
+	while(*a==' ' || *a=='\t')
+		a++;
+
+	sign = 1;
+	for(;; a++){
+		switch(*a){
+		case '-':
+			sign *= -1;
+			continue;
+		}
+		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;
+	case 16:
+		e = frompow2(a, b, 4);
+		break;
+	case 32:
+		e = fromdecx(a, b, dec32chr, dec32);
+		break;
+	case 64:
+		e = fromdecx(a, b, dec64chr, dec64);
+		break;
+	default:
+		abort();
+		return nil;
+	}
+
+	if(pp != nil)
+		*pp = e;
+
+	// if no characters parsed, there wasn't a number to convert
+	if(e == a)
+		return nil;
+
+	b->sign = sign;
+	return mpnorm(b);
+}
--- /dev/null
+++ b/libsec/Makefile
@@ -1,0 +1,84 @@
+ROOT=..
+include ../Make.config
+LIB=libsec.a
+
+OFILES=\
+	aes.$O aesni.$O aesCBC.$O aesCFB.$O aesOFB.$O\
+	aes_gcm.$O\
+	aes_xts.$O\
+	blowfish.$O\
+	ccpoly.$O\
+	chacha.$O chachablock.$O\
+	curve25519.$O\
+	curve25519_dh.$O\
+	decodepem.$O\
+	des.$O\
+	des3CBC.$O\
+	des3ECB.$O\
+	desCBC.$O\
+	desECB.$O\
+	desmodes.$O\
+	dh.$O\
+	dsaalloc.$O\
+	dsagen.$O\
+	dsaprimes.$O\
+	dsaprivtopub.$O\
+	dsasign.$O\
+	dsaverify.$O\
+	ecc.$O\
+	jacobian.$O\
+	egalloc.$O\
+	egdecrypt.$O\
+	egencrypt.$O\
+	eggen.$O\
+	egprivtopub.$O\
+	egsign.$O\
+	egverify.$O\
+	fastrand.$O\
+	genprime.$O\
+	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\
+	rsafill.$O\
+	rsagen.$O\
+	rsaprivtopub.$O\
+	salsa.$O\
+	scrypt.$O\
+	secp256k1.$O\
+	secp256r1.$O\
+	secp384r1.$O\
+	sha1.$O\
+	sha1pickle.$O\
+	sha2_128.$O\
+	sha2_64.$O\
+	sha1block.$O\
+	sha2block128.$O\
+	sha2block64.$O\
+	smallprimes.$O\
+	tsmemcmp.$O\
+	x509.$O\
+	p_sha256.$O
+
+default: $(LIB)
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libsec/aes.c
@@ -1,0 +1,1257 @@
+/*
+ * this code is derived from the following source,
+ * and modified to fit into the plan 9 libsec interface.
+ * most of the changes are confined to the top section,
+ * with the exception of converting Te4 and Td4 into u8 rather than u32 arrays.
+ *
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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.
+ */
+#include "os.h"
+#include <libsec.h>
+
+typedef uchar	u8;
+typedef ulong	u32;
+
+#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); }
+
+#define FULL_UNROLL
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x]
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x]
+*/
+
+static u32 Te0[256] = {
+	 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+	 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+	 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+	 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+	 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+	 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+	 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+	 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+	 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+	 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+	 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+	 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+	 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+	 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+	 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+	 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+	 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+	 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+	 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+	 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+	 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+	 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+	 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+	 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+	 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+	 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+	 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+	 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+	 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+	 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+	 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+	 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+	 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+	 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+	 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+	 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+	 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+	 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+	 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+	 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+	 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+	 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+	 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+	 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+	 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+	 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+	 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+	 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+	 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+	 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+	 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+	 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+	 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+	 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+	 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+	 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+	 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+	 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+	 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+	 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+	 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+	 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+	 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+	 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static u32 Te1[256] = {
+	 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+	 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+	 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+	 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+	 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+	 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+	 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+	 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+	 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+	 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+	 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+	 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+	 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+	 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+	 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+	 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+	 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+	 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+	 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+	 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+	 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+	 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+	 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+	 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+	 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+	 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+	 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+	 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+	 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+	 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+	 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+	 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+	 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+	 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+	 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+	 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+	 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+	 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+	 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+	 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+	 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+	 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+	 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+	 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+	 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+	 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+	 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+	 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+	 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+	 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+	 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+	 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+	 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+	 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+	 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+	 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+	 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+	 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+	 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+	 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+	 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+	 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+	 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+	 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static u32 Te2[256] = {
+	 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+	 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+	 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+	 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+	 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+	 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+	 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+	 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+	 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+	 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+	 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+	 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+	 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+	 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+	 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+	 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+	 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+	 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+	 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+	 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+	 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+	 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+	 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+	 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+	 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+	 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+	 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+	 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+	 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+	 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+	 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+	 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+	 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+	 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+	 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+	 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+	 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+	 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+	 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+	 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+	 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+	 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+	 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+	 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+	 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+	 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+	 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+	 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+	 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+	 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+	 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+	 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+	 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+	 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+	 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+	 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+	 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+	 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+	 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+	 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+	 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+	 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+	 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+	 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static u32 Te3[256] = {
+
+	 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+	 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+	 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+	 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+	 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+	 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+	 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+	 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+	 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+	 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+	 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+	 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+	 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+	 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+	 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+	 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+	 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+	 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+	 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+	 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+	 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+	 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+	 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+	 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+	 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+	 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+	 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+	 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+	 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+	 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+	 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+	 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+	 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+	 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+	 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+	 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+	 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+	 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+	 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+	 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+	 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+	 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+	 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+	 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+	 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+	 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+	 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+	 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+	 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+	 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+	 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+	 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+	 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+	 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+	 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+	 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+	 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+	 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+	 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+	 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+	 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+	 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+	 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+	 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static u8 Te4[256] = {
+	 0x63U, 0x7cU, 0x77U, 0x7bU,
+	 0xf2U, 0x6bU, 0x6fU, 0xc5U,
+	 0x30U, 0x01U, 0x67U, 0x2bU,
+	 0xfeU, 0xd7U, 0xabU, 0x76U,
+	 0xcaU, 0x82U, 0xc9U, 0x7dU,
+	 0xfaU, 0x59U, 0x47U, 0xf0U,
+	 0xadU, 0xd4U, 0xa2U, 0xafU,
+	 0x9cU, 0xa4U, 0x72U, 0xc0U,
+	 0xb7U, 0xfdU, 0x93U, 0x26U,
+	 0x36U, 0x3fU, 0xf7U, 0xccU,
+	 0x34U, 0xa5U, 0xe5U, 0xf1U,
+	 0x71U, 0xd8U, 0x31U, 0x15U,
+	 0x04U, 0xc7U, 0x23U, 0xc3U,
+	 0x18U, 0x96U, 0x05U, 0x9aU,
+	 0x07U, 0x12U, 0x80U, 0xe2U,
+	 0xebU, 0x27U, 0xb2U, 0x75U,
+	 0x09U, 0x83U, 0x2cU, 0x1aU,
+	 0x1bU, 0x6eU, 0x5aU, 0xa0U,
+	 0x52U, 0x3bU, 0xd6U, 0xb3U,
+	 0x29U, 0xe3U, 0x2fU, 0x84U,
+	 0x53U, 0xd1U, 0x00U, 0xedU,
+	 0x20U, 0xfcU, 0xb1U, 0x5bU,
+	 0x6aU, 0xcbU, 0xbeU, 0x39U,
+	 0x4aU, 0x4cU, 0x58U, 0xcfU,
+	 0xd0U, 0xefU, 0xaaU, 0xfbU,
+	 0x43U, 0x4dU, 0x33U, 0x85U,
+	 0x45U, 0xf9U, 0x02U, 0x7fU,
+	 0x50U, 0x3cU, 0x9fU, 0xa8U,
+	 0x51U, 0xa3U, 0x40U, 0x8fU,
+	 0x92U, 0x9dU, 0x38U, 0xf5U,
+	 0xbcU, 0xb6U, 0xdaU, 0x21U,
+	 0x10U, 0xffU, 0xf3U, 0xd2U,
+	 0xcdU, 0x0cU, 0x13U, 0xecU,
+	 0x5fU, 0x97U, 0x44U, 0x17U,
+	 0xc4U, 0xa7U, 0x7eU, 0x3dU,
+	 0x64U, 0x5dU, 0x19U, 0x73U,
+	 0x60U, 0x81U, 0x4fU, 0xdcU,
+	 0x22U, 0x2aU, 0x90U, 0x88U,
+	 0x46U, 0xeeU, 0xb8U, 0x14U,
+	 0xdeU, 0x5eU, 0x0bU, 0xdbU,
+	 0xe0U, 0x32U, 0x3aU, 0x0aU,
+	 0x49U, 0x06U, 0x24U, 0x5cU,
+	 0xc2U, 0xd3U, 0xacU, 0x62U,
+	 0x91U, 0x95U, 0xe4U, 0x79U,
+	 0xe7U, 0xc8U, 0x37U, 0x6dU,
+	 0x8dU, 0xd5U, 0x4eU, 0xa9U,
+	 0x6cU, 0x56U, 0xf4U, 0xeaU,
+	 0x65U, 0x7aU, 0xaeU, 0x08U,
+	 0xbaU, 0x78U, 0x25U, 0x2eU,
+	 0x1cU, 0xa6U, 0xb4U, 0xc6U,
+	 0xe8U, 0xddU, 0x74U, 0x1fU,
+	 0x4bU, 0xbdU, 0x8bU, 0x8aU,
+	 0x70U, 0x3eU, 0xb5U, 0x66U,
+	 0x48U, 0x03U, 0xf6U, 0x0eU,
+	 0x61U, 0x35U, 0x57U, 0xb9U,
+	 0x86U, 0xc1U, 0x1dU, 0x9eU,
+	 0xe1U, 0xf8U, 0x98U, 0x11U,
+	 0x69U, 0xd9U, 0x8eU, 0x94U,
+	 0x9bU, 0x1eU, 0x87U, 0xe9U,
+	 0xceU, 0x55U, 0x28U, 0xdfU,
+	 0x8cU, 0xa1U, 0x89U, 0x0dU,
+	 0xbfU, 0xe6U, 0x42U, 0x68U,
+	 0x41U, 0x99U, 0x2dU, 0x0fU,
+	 0xb0U, 0x54U, 0xbbU, 0x16U,
+};
+static u32 Td0[256] = {
+	 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+	 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+	 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+	 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+	 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+	 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+	 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+	 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+	 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+	 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+	 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+	 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+	 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+	 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+	 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+	 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+	 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+	 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+	 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+	 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+	 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+	 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+	 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+	 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+	 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+	 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+	 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+	 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+	 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+	 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+	 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+	 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+	 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+	 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+	 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+	 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+	 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+	 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+	 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+	 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+	 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+	 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+	 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+	 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+	 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+	 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+	 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+	 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+	 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+	 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+	 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+	 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+	 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+	 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+	 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+	 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+	 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+	 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+	 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+	 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+	 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+	 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+	 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+	 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static u32 Td1[256] = {
+	 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+	 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+	 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+	 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+	 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+	 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+	 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+	 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+	 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+	 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+	 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+	 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+	 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+	 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+	 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+	 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+	 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+	 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+	 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+	 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+	 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+	 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+	 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+	 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+	 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+	 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+	 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+	 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+	 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+	 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+	 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+	 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+	 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+	 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+	 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+	 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+	 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+	 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+	 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+	 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+	 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+	 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+	 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+	 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+	 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+	 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+	 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+	 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+	 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+	 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+	 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+	 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+	 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+	 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+	 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+	 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+	 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+	 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+	 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+	 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+	 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+	 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+	 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+	 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static u32 Td2[256] = {
+	 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+	 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+	 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+	 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+	 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+	 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+	 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+	 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+	 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+	 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+	 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+	 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+	 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+	 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+	 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+	 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+	 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+	 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+	 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+	 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+	 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+	 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+	 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+	 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+	 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+	 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+	 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+	 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+	 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+	 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+	 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+	 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+	 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+	 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+	 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+	 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+	 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+	 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+	 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+	 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+	 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+	 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+	 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+	 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+	 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+	 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+	 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+	 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+	 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+	 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+	 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+	 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+	 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+	 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+	 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+	 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+	 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+	 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+	 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+	 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+	 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+	 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+	 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+	 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static u32 Td3[256] = {
+	 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+	 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+	 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+	 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+	 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+	 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+	 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+	 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+	 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+	 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+	 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+	 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+	 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+	 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+	 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+	 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+	 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+	 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+	 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+	 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+	 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+	 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+	 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+	 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+	 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+	 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+	 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+	 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+	 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+	 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+	 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+	 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+	 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+	 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+	 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+	 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+	 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+	 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+	 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+	 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+	 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+	 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+	 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+	 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+	 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+	 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+	 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+	 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+	 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+	 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+	 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+	 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+	 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+	 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+	 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+	 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+	 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+	 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+	 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+	 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+	 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+	 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+	 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+	 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static u8 Td4[256] = {
+	 0x52U, 0x09U, 0x6aU, 0xd5U,
+	 0x30U, 0x36U, 0xa5U, 0x38U,
+	 0xbfU, 0x40U, 0xa3U, 0x9eU,
+	 0x81U, 0xf3U, 0xd7U, 0xfbU,
+	 0x7cU, 0xe3U, 0x39U, 0x82U,
+	 0x9bU, 0x2fU, 0xffU, 0x87U,
+	 0x34U, 0x8eU, 0x43U, 0x44U,
+	 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+	 0x54U, 0x7bU, 0x94U, 0x32U,
+	 0xa6U, 0xc2U, 0x23U, 0x3dU,
+	 0xeeU, 0x4cU, 0x95U, 0x0bU,
+	 0x42U, 0xfaU, 0xc3U, 0x4eU,
+	 0x08U, 0x2eU, 0xa1U, 0x66U,
+	 0x28U, 0xd9U, 0x24U, 0xb2U,
+	 0x76U, 0x5bU, 0xa2U, 0x49U,
+	 0x6dU, 0x8bU, 0xd1U, 0x25U,
+	 0x72U, 0xf8U, 0xf6U, 0x64U,
+	 0x86U, 0x68U, 0x98U, 0x16U,
+	 0xd4U, 0xa4U, 0x5cU, 0xccU,
+	 0x5dU, 0x65U, 0xb6U, 0x92U,
+	 0x6cU, 0x70U, 0x48U, 0x50U,
+	 0xfdU, 0xedU, 0xb9U, 0xdaU,
+	 0x5eU, 0x15U, 0x46U, 0x57U,
+	 0xa7U, 0x8dU, 0x9dU, 0x84U,
+	 0x90U, 0xd8U, 0xabU, 0x00U,
+	 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+	 0xf7U, 0xe4U, 0x58U, 0x05U,
+	 0xb8U, 0xb3U, 0x45U, 0x06U,
+	 0xd0U, 0x2cU, 0x1eU, 0x8fU,
+	 0xcaU, 0x3fU, 0x0fU, 0x02U,
+	 0xc1U, 0xafU, 0xbdU, 0x03U,
+	 0x01U, 0x13U, 0x8aU, 0x6bU,
+	 0x3aU, 0x91U, 0x11U, 0x41U,
+	 0x4fU, 0x67U, 0xdcU, 0xeaU,
+	 0x97U, 0xf2U, 0xcfU, 0xceU,
+	 0xf0U, 0xb4U, 0xe6U, 0x73U,
+	 0x96U, 0xacU, 0x74U, 0x22U,
+	 0xe7U, 0xadU, 0x35U, 0x85U,
+	 0xe2U, 0xf9U, 0x37U, 0xe8U,
+	 0x1cU, 0x75U, 0xdfU, 0x6eU,
+	 0x47U, 0xf1U, 0x1aU, 0x71U,
+	 0x1dU, 0x29U, 0xc5U, 0x89U,
+	 0x6fU, 0xb7U, 0x62U, 0x0eU,
+	 0xaaU, 0x18U, 0xbeU, 0x1bU,
+	 0xfcU, 0x56U, 0x3eU, 0x4bU,
+	 0xc6U, 0xd2U, 0x79U, 0x20U,
+	 0x9aU, 0xdbU, 0xc0U, 0xfeU,
+	 0x78U, 0xcdU, 0x5aU, 0xf4U,
+	 0x1fU, 0xddU, 0xa8U, 0x33U,
+	 0x88U, 0x07U, 0xc7U, 0x31U,
+	 0xb1U, 0x12U, 0x10U, 0x59U,
+	 0x27U, 0x80U, 0xecU, 0x5fU,
+	 0x60U, 0x51U, 0x7fU, 0xa9U,
+	 0x19U, 0xb5U, 0x4aU, 0x0dU,
+	 0x2dU, 0xe5U, 0x7aU, 0x9fU,
+	 0x93U, 0xc9U, 0x9cU, 0xefU,
+	 0xa0U, 0xe0U, 0x3bU, 0x4dU,
+	 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+	 0xc8U, 0xebU, 0xbbU, 0x3cU,
+	 0x83U, 0x53U, 0x99U, 0x61U,
+	 0x17U, 0x2bU, 0x04U, 0x7eU,
+	 0xbaU, 0x77U, 0xd6U, 0x26U,
+	 0xe1U, 0x69U, 0x14U, 0x63U,
+	 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+static u32 rcon[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000,
+	/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/*
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return	the number of rounds for the given cipher key size.
+ */
+static int
+setupEnc(ulong rk[/*4*(Nr + 1)*/], uchar key[], int nkey)
+{
+	int i = 0;
+	u32 temp;
+
+	rk[0] = GETU32(key     );
+	rk[1] = GETU32(key +  4);
+	rk[2] = GETU32(key +  8);
+	rk[3] = GETU32(key + 12);
+	if (nkey == 16) {
+		for (;;) {
+			temp  = rk[3];
+			rk[4] = rk[0] ^
+				(Te4[(temp >> 16) & 0xff] << 24) ^
+				(Te4[(temp >>  8) & 0xff] << 16) ^
+				(Te4[(temp      ) & 0xff] <<  8) ^
+				(Te4[(temp >> 24)       ]      ) ^
+				rcon[i];
+			rk[5] = rk[1] ^ rk[4];
+			rk[6] = rk[2] ^ rk[5];
+			rk[7] = rk[3] ^ rk[6];
+			if (++i == 10) {
+				return 10;
+			}
+			rk += 4;
+		}
+	}
+	rk[4] = GETU32(key + 16);
+	rk[5] = GETU32(key + 20);
+	if (nkey == 24) {
+		for (;;) {
+			temp = rk[ 5];
+			rk[ 6] = rk[ 0] ^
+				(Te4[(temp >> 16) & 0xff] << 24) ^
+				(Te4[(temp >>  8) & 0xff] << 16) ^
+				(Te4[(temp      ) & 0xff] <<  8) ^
+				(Te4[(temp >> 24)       ]      ) ^
+				rcon[i];
+			rk[ 7] = rk[ 1] ^ rk[ 6];
+			rk[ 8] = rk[ 2] ^ rk[ 7];
+			rk[ 9] = rk[ 3] ^ rk[ 8];
+			if (++i == 8) {
+				return 12;
+			}
+			rk[10] = rk[ 4] ^ rk[ 9];
+			rk[11] = rk[ 5] ^ rk[10];
+			rk += 6;
+		}
+	}
+	rk[6] = GETU32(key + 24);
+	rk[7] = GETU32(key + 28);
+	if (nkey == 32) {
+		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];
+			rk += 8;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Expand the cipher key into the encryption and decryption key schedules.
+ *
+ * @return	the number of rounds for the given cipher key size.
+ */
+static int
+AESsetup(ulong erk[/* 4*(Nr + 1) */], ulong drk[/* 4*(Nr + 1) */], uchar key[], int nkey)
+{
+	int Nr, i;
+
+	/* expand the cipher key: */
+	Nr = setupEnc(erk, key, nkey);
+
+	/*
+	 * 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[1       ] = erk[4*Nr + 1];
+	drk[2       ] = erk[4*Nr + 2];
+	drk[3       ] = erk[4*Nr + 3];
+	drk[4*Nr    ] = erk[0       ];
+	drk[4*Nr + 1] = erk[1       ];
+	drk[4*Nr + 2] = erk[2       ];
+	drk[4*Nr + 3] = erk[3       ];
+	erk += 4 * Nr;
+	for (i = 1; i < Nr; i++) {
+		drk += 4;
+		erk -= 4;
+		drk[0] =
+		 	Td0[Te4[(erk[0] >> 24)       ]] ^
+		 	Td1[Te4[(erk[0] >> 16) & 0xff]] ^
+		 	Td2[Te4[(erk[0] >>  8) & 0xff]] ^
+		 	Td3[Te4[(erk[0]      ) & 0xff]];
+		drk[1] =
+		 	Td0[Te4[(erk[1] >> 24)       ]] ^
+		 	Td1[Te4[(erk[1] >> 16) & 0xff]] ^
+		 	Td2[Te4[(erk[1] >>  8) & 0xff]] ^
+		 	Td3[Te4[(erk[1]      ) & 0xff]];
+		drk[2] =
+		 	Td0[Te4[(erk[2] >> 24)       ]] ^
+		 	Td1[Te4[(erk[2] >> 16) & 0xff]] ^
+		 	Td2[Te4[(erk[2] >>  8) & 0xff]] ^
+		 	Td3[Te4[(erk[2]      ) & 0xff]];
+		drk[3] =
+		 	Td0[Te4[(erk[3] >> 24)       ]] ^
+		 	Td1[Te4[(erk[3] >> 16) & 0xff]] ^
+		 	Td2[Te4[(erk[3] >>  8) & 0xff]] ^
+		 	Td3[Te4[(erk[3]      ) & 0xff]];
+	}
+	return Nr;
+}
+
+/* using round keys in rk, perform Nr rounds of encrypting pt into ct */
+static void
+AESencrypt(ulong rk[/* 4*(Nr + 1) */], int Nr, uchar pt[16], uchar ct[16])
+{
+	ulong s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(pt     ) ^ rk[0];
+	s1 = GETU32(pt +  4) ^ rk[1];
+	s2 = GETU32(pt +  8) ^ rk[2];
+	s3 = GETU32(pt + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+	/* 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];
+	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+	/* round 2: */
+	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+	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: */
+	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];
+	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+	/* round 4: */
+	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+	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: */
+	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];
+	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+	/* round 6: */
+	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+	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: */
+	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];
+	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+	/* round 8: */
+	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+	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: */
+	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 */
+	/*
+	 * 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];
+
+		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 */
+	/*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 =
+		(Te4[(t0 >> 24)       ] << 24) ^
+		(Te4[(t1 >> 16) & 0xff] << 16) ^
+		(Te4[(t2 >>  8) & 0xff] <<  8) ^
+		(Te4[(t3      ) & 0xff]      ) ^
+		rk[0];
+	PUTU32(ct     , s0);
+	s1 =
+		(Te4[(t1 >> 24)       ] << 24) ^
+		(Te4[(t2 >> 16) & 0xff] << 16) ^
+		(Te4[(t3 >>  8) & 0xff] <<  8) ^
+		(Te4[(t0      ) & 0xff]      ) ^
+		rk[1];
+	PUTU32(ct +  4, s1);
+	s2 =
+		(Te4[(t2 >> 24)       ] << 24) ^
+		(Te4[(t3 >> 16) & 0xff] << 16) ^
+		(Te4[(t0 >>  8) & 0xff] <<  8) ^
+		(Te4[(t1      ) & 0xff]      ) ^
+		rk[2];
+	PUTU32(ct +  8, s2);
+	s3 =
+		(Te4[(t3 >> 24)       ] << 24) ^
+		(Te4[(t0 >> 16) & 0xff] << 16) ^
+		(Te4[(t1 >>  8) & 0xff] <<  8) ^
+		(Te4[(t2      ) & 0xff]      ) ^
+		rk[3];
+	PUTU32(ct + 12, s3);
+}
+
+static void
+AESdecrypt(ulong rk[/* 4*(Nr + 1) */], int Nr, uchar ct[16], uchar pt[16])
+{
+	ulong s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif		/* ?FULL_UNROLL */
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(ct     ) ^ rk[0];
+	s1 = GETU32(ct +  4) ^ rk[1];
+	s2 = GETU32(ct +  8) ^ rk[2];
+	s3 = GETU32(ct + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+	/* round 1: */
+	t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+	t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+	t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+	t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+	/* round 2: */
+	s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+	s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+	s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+	s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+	/* round 3: */
+	t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+	t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+	t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+	t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+	/* round 4: */
+	s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+	s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+	s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+	s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+	/* round 5: */
+	t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+	t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+	t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+	t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+	/* round 6: */
+	s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+	s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+	s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+	s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+	/* round 7: */
+	t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+	t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+	t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+	t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+	/* round 8: */
+	s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+	s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+	s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+	s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+	/* round 9: */
+	t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+	t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+	t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+	t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+	if (Nr > 10) {
+		/* round 10: */
+		s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+		s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+		s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+		s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+		/* round 11: */
+		t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+		t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+		t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+		t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+		if (Nr > 12) {
+			/* round 12: */
+			s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+			s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+			s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+			s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+			/* round 13: */
+			t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+			t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+			t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+			t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+		}
+	}
+	rk += Nr << 2;
+#else					/* !FULL_UNROLL */
+	/*
+	 * Nr - 1 full rounds:
+	 */
+	r = Nr >> 1;
+	for (;;) {
+		t0 =
+			Td0[(s0 >> 24)       ] ^
+			Td1[(s3 >> 16) & 0xff] ^
+			Td2[(s2 >>  8) & 0xff] ^
+			Td3[(s1      ) & 0xff] ^
+			rk[4];
+		t1 =
+			Td0[(s1 >> 24)       ] ^
+			Td1[(s0 >> 16) & 0xff] ^
+			Td2[(s3 >>  8) & 0xff] ^
+			Td3[(s2      ) & 0xff] ^
+			rk[5];
+		t2 =
+			Td0[(s2 >> 24)       ] ^
+			Td1[(s1 >> 16) & 0xff] ^
+			Td2[(s0 >>  8) & 0xff] ^
+			Td3[(s3      ) & 0xff] ^
+			rk[6];
+		t3 =
+			Td0[(s3 >> 24)       ] ^
+			Td1[(s2 >> 16) & 0xff] ^
+			Td2[(s1 >>  8) & 0xff] ^
+			Td3[(s0      ) & 0xff] ^
+			rk[7];
+
+		rk += 8;
+		if (--r == 0)
+			break;
+
+		s0 =
+			Td0[(t0 >> 24)       ] ^
+			Td1[(t3 >> 16) & 0xff] ^
+			Td2[(t2 >>  8) & 0xff] ^
+			Td3[(t1      ) & 0xff] ^
+			rk[0];
+		s1 =
+			Td0[(t1 >> 24)       ] ^
+			Td1[(t0 >> 16) & 0xff] ^
+			Td2[(t3 >>  8) & 0xff] ^
+			Td3[(t2      ) & 0xff] ^
+			rk[1];
+		s2 =
+			Td0[(t2 >> 24)       ] ^
+			Td1[(t1 >> 16) & 0xff] ^
+			Td2[(t0 >>  8) & 0xff] ^
+			Td3[(t3      ) & 0xff] ^
+			rk[2];
+		s3 =
+			Td0[(t3 >> 24)       ] ^
+			Td1[(t2 >> 16) & 0xff] ^
+			Td2[(t1 >>  8) & 0xff] ^
+			Td3[(t0      ) & 0xff] ^
+			rk[3];
+	}
+#endif					/* ?FULL_UNROLL */
+	/*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 =
+		(Td4[(t0 >> 24)       ] << 24) ^
+		(Td4[(t3 >> 16) & 0xff] << 16) ^
+		(Td4[(t2 >>  8) & 0xff] <<  8) ^
+		(Td4[(t1      ) & 0xff]      ) ^
+		rk[0];
+	PUTU32(pt     , s0);
+	s1 =
+		(Td4[(t1 >> 24)       ] << 24) ^
+		(Td4[(t0 >> 16) & 0xff] << 16) ^
+		(Td4[(t3 >>  8) & 0xff] <<  8) ^
+		(Td4[(t2      ) & 0xff]      ) ^
+		rk[1];
+	PUTU32(pt +  4, s1);
+	s2 =
+		(Td4[(t2 >> 24)       ] << 24) ^
+		(Td4[(t1 >> 16) & 0xff] << 16) ^
+		(Td4[(t0 >>  8) & 0xff] <<  8) ^
+		(Td4[(t3      ) & 0xff]      ) ^
+		rk[2];
+	PUTU32(pt +  8, s2);
+	s3 =
+		(Td4[(t3 >> 24)       ] << 24) ^
+		(Td4[(t2 >> 16) & 0xff] << 16) ^
+		(Td4[(t1 >>  8) & 0xff] <<  8) ^
+		(Td4[(t0      ) & 0xff]      ) ^
+		rk[3];
+	PUTU32(pt + 12, s3);
+}
+
+void (*aes_encrypt)(ulong rk[], int Nr, uchar pt[16], uchar ct[16]) = AESencrypt;
+void (*aes_decrypt)(ulong rk[], int Nr, uchar ct[16], uchar pt[16]) = AESdecrypt;
+
+void
+setupAESstate(AESstate *s, uchar key[], int nkey, uchar *ivec)
+{
+	static int (*aes_setup)(ulong erk[/* 4*(Nr + 1) */], ulong drk[/* 4*(Nr + 1) */], uchar key[], int nkey);
+
+	if(aes_setup == nil){
+		extern void *aesni_init(void);
+		if((aes_setup = aesni_init()) == nil)
+			aes_setup = AESsetup;
+	}
+	memset(s, 0, sizeof(*s));
+	if(nkey > AESmaxkey)
+		nkey = AESmaxkey;
+	memmove(s->key, key, nkey);
+	s->keybytes = nkey;
+	s->ekey = s->storage+16 - (s->storage - (uchar*)0 & 15);
+	s->dkey = (uchar*)s->ekey + 16*(AESmaxrounds+1);
+	s->rounds = (*aes_setup)(s->ekey, s->dkey, s->key, nkey);
+	if(ivec != nil)
+		memmove(s->ivec, ivec, AESbsize);
+	if(s->rounds != 0)
+		s->setup = 0xcafebabe;
+}
--- /dev/null
+++ b/libsec/aesCBC.c
@@ -1,0 +1,60 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * 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;
+	uchar q[AESbsize];
+
+	for(; len >= AESbsize; len -= AESbsize){
+		p2 = p;
+		ip = s->ivec;
+		for(eip = ip+AESbsize; ip < eip; )
+			*p2++ ^= *ip++;
+		aes_encrypt(s->ekey, s->rounds, p, q);
+		memmove(s->ivec, q, AESbsize);
+		memmove(p, q, AESbsize);
+		p += AESbsize;
+	}
+
+	if(len > 0){
+		ip = s->ivec;
+		aes_encrypt(s->ekey, s->rounds, ip, q);
+		memmove(s->ivec, q, AESbsize);
+		for(eip = ip+len; ip < eip; )
+			*p++ ^= *ip++;
+	}
+}
+
+void
+aesCBCdecrypt(uchar *p, int len, AESstate *s)
+{
+	uchar *ip, *eip, *tp;
+	uchar tmp[AESbsize], q[AESbsize];
+
+	for(; len >= AESbsize; len -= AESbsize){
+		memmove(tmp, p, AESbsize);
+		aes_decrypt(s->dkey, s->rounds, p, q);
+		memmove(p, q, AESbsize);
+		tp = tmp;
+		ip = s->ivec;
+		for(eip = ip+AESbsize; ip < eip; ){
+			*p++ ^= *ip;
+			*ip++ = *tp++;
+		}
+	}
+
+	if(len > 0){
+		ip = s->ivec;
+		aes_encrypt(s->ekey, s->rounds, ip, q);
+		memmove(s->ivec, q, AESbsize);
+		for(eip = ip+len; ip < eip; )
+			*p++ ^= *ip++;
+	}
+}
--- /dev/null
+++ b/libsec/aesCFB.c
@@ -1,0 +1,50 @@
+#include "os.h"
+#include <libsec.h>
+
+typedef ulong u32;
+
+void
+aesCFBencrypt(uchar *p, int len, AESstate *s)
+{
+	u32 a, o = s->offset;
+
+	while(len > 0){
+		if(o % 16){
+		Odd:
+			a = (s->ivec[o++ % 16] ^= *p), *p++ = a, len--;
+			continue;
+		}
+		aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec);
+		if(len < 16 || ((p-(uchar*)0) & 3) != 0)
+			goto Odd;
+		((u32*)p)[0] = (((u32*)s->ivec)[0] ^= ((u32*)p)[0]);
+		((u32*)p)[1] = (((u32*)s->ivec)[1] ^= ((u32*)p)[1]);
+		((u32*)p)[2] = (((u32*)s->ivec)[2] ^= ((u32*)p)[2]);
+		((u32*)p)[3] = (((u32*)s->ivec)[3] ^= ((u32*)p)[3]);
+		o += 16, p += 16, len -= 16;
+	}
+	s->offset = o;
+}
+
+void
+aesCFBdecrypt(uchar *p, int len, AESstate *s)
+{
+	u32 a, o = s->offset;
+
+	while(len > 0){
+		if(o % 16){
+		Odd:
+			a = *p, *p++ ^= s->ivec[o % 16], s->ivec[o++ % 16] = a, len--;
+			continue;
+		}
+		aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec);
+		if(len < 16 || ((p-(uchar*)0) & 3) != 0)
+			goto Odd;
+		a = ((u32*)p)[0], ((u32*)p)[0] ^= ((u32*)s->ivec)[0], ((u32*)s->ivec)[0] = a;
+		a = ((u32*)p)[1], ((u32*)p)[1] ^= ((u32*)s->ivec)[1], ((u32*)s->ivec)[1] = a;
+		a = ((u32*)p)[2], ((u32*)p)[2] ^= ((u32*)s->ivec)[2], ((u32*)s->ivec)[2] = a;
+		a = ((u32*)p)[3], ((u32*)p)[3] ^= ((u32*)s->ivec)[3], ((u32*)s->ivec)[3] = a;
+		o += 16, p += 16, len -= 16;
+	}
+	s->offset = o;
+}
--- /dev/null
+++ b/libsec/aesOFB.c
@@ -1,0 +1,28 @@
+#include "os.h"
+#include <libsec.h>
+
+typedef ulong u32;
+
+void
+aesOFBencrypt(uchar *p, int len, AESstate *s)
+{
+	u32 o = s->offset;
+
+	while(len > 0){
+		if(o % 16){
+		Odd:
+			*p++ ^= s->ivec[o++ % 16], len--;
+			continue;
+		}
+		aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec);
+		if(len < 16 || ((p-(uchar*)0) & 3) != 0)
+			goto Odd;
+		((u32*)p)[0] ^= ((u32*)s->ivec)[0];
+		((u32*)p)[1] ^= ((u32*)s->ivec)[1];
+		((u32*)p)[2] ^= ((u32*)s->ivec)[2];
+		((u32*)p)[3] ^= ((u32*)s->ivec)[3];
+		o += 16, p += 16, len -= 16;
+	}
+	s->offset = o;
+}
+
--- /dev/null
+++ b/libsec/aes_gcm.c
@@ -1,0 +1,199 @@
+#include "os.h"
+#include <libsec.h>
+
+static void
+load128(uchar b[16], ulong W[4])
+{
+	W[0] = (ulong)b[15] | (ulong)b[14]<<8 | (ulong)b[13]<<16 | (ulong)b[12]<<24;
+	W[1] = (ulong)b[11] | (ulong)b[10]<<8 | (ulong)b[ 9]<<16 | (ulong)b[ 8]<<24;
+	W[2] = (ulong)b[ 7] | (ulong)b[ 6]<<8 | (ulong)b[ 5]<<16 | (ulong)b[ 4]<<24;
+	W[3] = (ulong)b[ 3] | (ulong)b[ 2]<<8 | (ulong)b[ 1]<<16 | (ulong)b[ 0]<<24;
+}
+
+static void
+store128(ulong W[4], uchar b[16])
+{
+	b[15] = W[0], b[14] = W[0]>>8, b[13] = W[0]>>16, b[12] = W[0]>>24;
+	b[11] = W[1], b[10] = W[1]>>8, b[ 9] = W[1]>>16, b[ 8] = W[1]>>24;
+	b[ 7] = W[2], b[ 6] = W[2]>>8, b[ 5] = W[2]>>16, b[ 4] = W[2]>>24;
+	b[ 3] = W[3], b[ 2] = W[3]>>8, b[ 1] = W[3]>>16, b[ 0] = W[3]>>24;
+}
+
+static void
+gfmul(ulong X[4], ulong Y[4], ulong Z[4])
+{
+	long m, i;
+
+	Z[0] = Z[1] = Z[2] = Z[3] = 0;
+	for(i=127; i>=0; i--){
+		m = ((long)Y[i>>5] << (31-(i&31))) >> 31;
+		Z[0] ^= X[0] & m;
+		Z[1] ^= X[1] & m;
+		Z[2] ^= X[2] & m;
+		Z[3] ^= X[3] & m;
+		m = ((long)X[0]<<31) >> 31;
+		X[0] = X[0]>>1 | X[1]<<31;
+		X[1] = X[1]>>1 | X[2]<<31;
+		X[2] = X[2]>>1 | X[3]<<31;
+		X[3] = X[3]>>1 ^ (0xE1000000 & m);
+	}
+}
+
+static void
+prepareM(ulong H[4], ulong M[16][256][4])
+{
+	ulong X[4], i, j;
+
+	for(i=0; i<16; i++){
+		for(j=0; j<256; j++){
+			X[0] = X[1] = X[2] = X[3] = 0;
+			X[i>>2] = j<<((i&3)<<3);
+			gfmul(X, H, M[i][j]);
+		}
+	}
+}
+
+static void
+ghash1(AESGCMstate *s, ulong X[4], ulong Y[4])
+{
+	ulong *Xi, i;
+
+	X[0] ^= Y[0], X[1] ^= Y[1], X[2] ^= Y[2], X[3] ^= Y[3];
+	if(0){
+		gfmul(X, s->H, Y);
+		return;
+	}
+
+	Y[0] = Y[1] = Y[2] = Y[3] = 0;
+	for(i=0; i<16; i++){
+		Xi = s->M[i][(X[i>>2]>>((i&3)<<3))&0xFF];
+		Y[0] ^= Xi[0];
+		Y[1] ^= Xi[1];
+		Y[2] ^= Xi[2];
+		Y[3] ^= Xi[3];
+	}
+}
+
+static void
+ghashn(AESGCMstate *s, uchar *dat, ulong len, ulong Y[4])
+{
+	uchar tmp[16];
+	ulong X[4];
+
+	while(len >= 16){
+		load128(dat, X);
+		ghash1(s, X, Y);
+		dat += 16, len -= 16;
+	}
+	if(len > 0){
+		memmove(tmp, dat, len);
+		memset(tmp+len, 0, 16-len);
+		load128(tmp, X);
+		ghash1(s, X, Y);
+	}
+}
+
+static ulong
+aesxctr1(AESstate *s, uchar ctr[AESbsize], uchar *dat, ulong len)
+{
+	uchar tmp[AESbsize];
+	ulong i;
+
+	aes_encrypt(s->ekey, s->rounds, ctr, tmp);
+	if(len > AESbsize)
+		len = AESbsize;
+	for(i=0; i<len; i++)
+		dat[i] ^= tmp[i];
+	return len;
+}
+
+static void
+aesxctrn(AESstate *s, uchar *dat, ulong len)
+{
+	uchar ctr[AESbsize];
+	ulong i;
+
+	memmove(ctr, s->ivec, AESbsize);
+	while(len > 0){
+		for(i=AESbsize-1; i>=AESbsize-4; i--)
+			if(++ctr[i] != 0)
+				break;
+
+		if(aesxctr1(s, ctr, dat, len) < AESbsize)
+			break;
+		dat += AESbsize;
+		len -= AESbsize;
+	}
+}
+
+void
+aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen)
+{
+	if(ivlen == 96/8){
+		memmove(s->a.ivec, iv, ivlen);
+		memset(s->a.ivec+ivlen, 0, AESbsize-ivlen);
+		s->a.ivec[AESbsize-1] = 1;
+	} else {
+		ulong L[4], Y[4] = {0};
+
+		ghashn(s, iv, ivlen, Y);
+		L[0] = ivlen << 3;
+		L[1] = ivlen >> 29;
+		L[2] = L[3] = 0;
+		ghash1(s, L, Y);
+		store128(Y, s->a.ivec);
+	}
+}
+
+void
+setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen)
+{
+	setupAESstate(&s->a, key, keylen, nil);
+
+	memset(s->a.ivec, 0, AESbsize);
+	aes_encrypt(s->a.ekey, s->a.rounds, s->a.ivec, s->a.ivec);
+	load128(s->a.ivec, s->H);
+	memset(s->a.ivec, 0, AESbsize);
+	prepareM(s->H, s->M);
+
+	if(iv != nil && ivlen > 0)
+		aesgcm_setiv(s, iv, ivlen);
+}
+
+void
+aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+{
+	ulong L[4], Y[4] = {0};
+
+	ghashn(s, aad, naad, Y);
+	aesxctrn(&s->a, dat, ndat);
+	ghashn(s, dat, ndat, Y);
+	L[0] = ndat << 3;
+	L[1] = ndat >> 29;
+	L[2] = naad << 3;
+	L[3] = naad >> 29;
+	ghash1(s, L, Y);
+	store128(Y, tag);
+	aesxctr1(&s->a, s->a.ivec, tag, 16);
+}
+
+int
+aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+{
+	ulong L[4], Y[4] = {0};
+	uchar tmp[16];
+
+	ghashn(s, aad, naad, Y);
+	ghashn(s, dat, ndat, Y);
+	L[0] = ndat << 3;
+	L[1] = ndat >> 29;
+	L[2] = naad << 3;
+	L[3] = naad >> 29;
+	ghash1(s, L, Y);
+	store128(Y, tmp);
+	aesxctr1(&s->a, s->a.ivec, tmp, 16);
+	if(tsmemcmp(tag, tmp, 16) != 0)
+		return -1;
+	aesxctrn(&s->a, dat, ndat);
+	return 0;
+}
--- /dev/null
+++ b/libsec/aes_xts.c
@@ -1,0 +1,83 @@
+#include "os.h"
+#include <libsec.h>
+
+/* little-endian data order */
+#define	GET4(p)		((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define	PUT4(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+
+static void
+gf_mulx(uchar *x)
+{
+	ulong t0, t1, t2, t3, t4;
+
+	t0 = GET4(x);
+	t1 = GET4(x+4);
+	t2 = GET4(x+8);
+	t3 = GET4(x+12);
+
+	t4 =             (t3 >> 31);
+	t3 = (t3 << 1) | (t2 >> 31);
+	t2 = (t2 << 1) | (t1 >> 31);
+	t1 = (t1 << 1) | (t0 >> 31);
+	t0 = (t0 << 1) ^ (t4*135);
+
+	PUT4(x, t0);
+	PUT4(x+4, t1);
+	PUT4(x+8, t2);
+	PUT4(x+12, t3);
+}
+
+static void
+xor128(uchar *o, uchar *i1, uchar *i2)
+{
+	int i;
+
+	for(i=0; i<16; i++)
+		o[i] = i1[i] ^ i2[i];
+}
+
+static void
+setupT(AESstate *tweak, uvlong sectorNumber, uchar T[AESbsize])
+{
+	PUT4(T+0, (ulong)sectorNumber), sectorNumber >>= 32;
+	PUT4(T+4, (ulong)sectorNumber);
+	PUT4(T+8, 0);
+	PUT4(T+12, 0);
+	aes_encrypt(tweak->ekey, tweak->rounds, T, T);
+}
+
+void
+aes_xts_encrypt(AESstate *tweak, AESstate *ecb,
+	uvlong sectorNumber, uchar *input, uchar *output, ulong len)
+{
+	uchar T[AESbsize], x[AESbsize];
+	
+	if(len % AESbsize)
+		abort();
+
+	setupT(tweak, sectorNumber, T);
+	for (; len > 0; len -= AESbsize, input += AESbsize, output += AESbsize) {
+		xor128(x, input, T);
+		aes_encrypt(ecb->ekey, ecb->rounds, x, x);
+		xor128(output, x, T);
+		gf_mulx(T);
+	}
+}
+
+void
+aes_xts_decrypt(AESstate *tweak, AESstate *ecb,
+	uvlong sectorNumber, uchar *input, uchar *output, ulong len)
+{
+	uchar T[AESbsize], x[AESbsize];
+	
+	if(len % AESbsize)
+		abort();
+
+	setupT(tweak, sectorNumber, T);
+	for (; len > 0; len -= AESbsize, input += AESbsize, output += AESbsize) {
+		xor128(x, input, T);
+		aes_decrypt(ecb->dkey, ecb->rounds, x, x);
+		xor128(output, x, T);
+		gf_mulx(T);
+	}
+}
--- /dev/null
+++ b/libsec/aesgcmtest.c
@@ -1,0 +1,314 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+typedef struct Test Test;
+struct Test
+{
+	char *K;
+	char *P;
+	char *A;
+	char *IV;
+	char *T;
+};
+
+Test tests[] = {
+	{	/* Test Case 1 */
+		"00000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+
+		"58E2FCCEFA7E3061367F1D57A4E7455A"
+	},
+	{	/* Test Case 2 */
+		"00000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+
+		"AB6E47D42CEC13BDF53A67B21257BDDF",
+	},
+	{	/* Test Case 3 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+
+		"4D5C2AF327CD64A62CF35ABD2BA6FAB4"
+	},
+	{	/* Test Case 4 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbaddecaf888",
+
+		"5BC94FBC3221A5DB94FAE95AE7121A47"
+	},
+	{	/* Test Case 5 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbad",
+
+		"3612D2E79E3B0785561BE14AACA2FCCB"
+	},
+	{	/* Test Case 6 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"9313225df88406e555909c5aff5269aa"
+		"6a7a9538534f7da1e4c303d2a318a728"
+		"c3c0c95156809539fcf0e2429a6b5254"
+		"16aedbf5a0de6a57a637b39b",
+
+		"619CC5AEFFFE0BFA462AF43C1699D050"
+	},
+	{	/* Test Case 7 */
+		"00000000000000000000000000000000"
+		"0000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+
+		"CD33B28AC773F74BA00ED1F312572435"
+	},
+	{	/* Test Case 8 */
+		"00000000000000000000000000000000"
+		"0000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+
+		"2FF58D80033927AB8EF4D4587514F0FB"
+	},
+	{	/* Test Case 9 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+
+		"9924A7C8587336BFB118024DB8674A14"
+	},
+	{	/* Test Case 10 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbaddecaf888",
+
+		"2519498E80F1478F37BA55BD6D27618C"
+	},
+	{	/* Test Case 11 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbad",
+
+		"65DCC57FCF623A24094FCCA40D3533F8"
+	},
+	{	/* Test Case 12 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"9313225df88406e555909c5aff5269aa"
+		"6a7a9538534f7da1e4c303d2a318a728"
+		"c3c0c95156809539fcf0e2429a6b5254"
+		"16aedbf5a0de6a57a637b39b",
+
+		"DCF566FF291C25BBB8568FC3D376A6D9"
+	},
+	{	/* Test Case 13 */
+		"00000000000000000000000000000000"
+		"00000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+
+		"530F8AFBC74536B9A963B4F1C4CB738B"
+	},
+	{	/* Test Case 14 */
+		"00000000000000000000000000000000"
+		"00000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+
+		"D0D1C8A799996BF0265B98B5D48AB919"
+	},
+	{	/* Test Case 15 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+
+		"B094DAC5D93471BDEC1A502270E3CC6C"
+	},
+	{	/* Test Case 16 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbaddecaf888",
+
+		"76FC6ECE0F4E1768CDDF8853BB2D551B"
+	},
+	{	/* Test Case 17 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbad",
+
+		"3A337DBF46A792C45E454913FE2EA8F2"
+	},
+	{	/* Test Case 18 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"9313225df88406e555909c5aff5269aa"
+		"6a7a9538534f7da1e4c303d2a318a728"
+		"c3c0c95156809539fcf0e2429a6b5254"
+		"16aedbf5a0de6a57a637b39b",
+
+		"A44A8266EE1C8EB0C8B5D4CF5AE9F19A"
+	},
+};
+
+int
+parsehex(char *s, uchar *h, char *l)
+{
+	char *e;
+	mpint *m;
+	int n;
+
+	n = strlen(s);
+	if(n == 0)
+		return 0;
+	assert((n & 1) == 0);
+	n >>= 1;
+	e = nil;
+	m = strtomp(s, &e, 16, nil);
+	if(m == nil || *e != '\0')
+		abort();
+	mptober(m, h, n);
+	if(l != nil)
+		print("%s = %.*H\n", l, n, h);
+	return n;
+}
+
+void
+runtest(Test *t)
+{
+	AESGCMstate s;
+	uchar key[1024], plain[1024], aad[1024], iv[1024], tag[16], tmp[16];
+	int nkey, nplain, naad, niv;
+
+	nkey = parsehex(t->K, key, "K");
+	nplain = parsehex(t->P, plain, "P");
+	naad = parsehex(t->A, aad, "A");
+	niv = parsehex(t->IV, iv, "IV");
+
+	setupAESGCMstate(&s, key, nkey, iv, niv);
+	aesgcm_encrypt(plain, nplain, aad, naad, tag, &s);
+	print("C = %.*H\n", nplain, plain);
+	print("T = %.*H\n", 16, tag);
+
+	parsehex(t->T, tmp, nil);
+	assert(memcmp(tmp, tag, 16) == 0);
+}
+
+void
+perftest(void)
+{
+	AESGCMstate s;
+	static uchar zeros[16];
+	uchar buf[1024*1024], tag[16];
+	vlong now;
+	int i, delta;
+
+	now = nsec();
+	for(i=0; i<100; i++){
+		memset(buf, 0, sizeof(buf));
+		if(1){
+			setupAESGCMstate(&s, zeros, 16, zeros, 12);
+			aesgcm_encrypt(buf, sizeof(buf), nil, 0, tag, &s);
+		} else {
+			setupAESstate(&s, zeros, 16, zeros);
+			aesCBCencrypt(buf, sizeof(buf), &s);
+		}
+	}
+	delta = (nsec() - now) / 1000000000LL;
+	fprint(2, "%ds = %d/s\n", delta, i*sizeof(buf) / delta);
+}
+
+void
+main(int argc, char **argv)
+{
+	int i;
+
+	fmtinstall('H', encodefmt);
+
+	ARGBEGIN {
+	case 'p':
+		perftest();
+		exits(nil);
+	} ARGEND;
+
+	for(i=0; i<nelem(tests); i++){
+		print("Test Case %d\n", i+1);
+		runtest(&tests[i]);
+		print("\n");
+	}
+}
--- /dev/null
+++ b/libsec/aesni.c
@@ -1,0 +1,5 @@
+void*
+aesni_init(void)
+{
+	return 0;
+}
--- /dev/null
+++ b/libsec/bftest.c
@@ -1,0 +1,279 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+enum{
+	Bsz = 8,
+};
+
+typedef struct Testvector Testvector;
+
+struct Testvector{
+	uchar key[Bsz];
+	uchar plain[Bsz];
+	uchar cipher[Bsz];
+};
+
+/*
+ * Blowfish test vectors from https://www.schneier.com/code/vectors.txt
+ */
+Testvector vector [] = {
+	{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
+
+	{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	 {0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}},
+
+	{{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+	 {0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}},
+
+	{{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+	 {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+	 {0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}},
+
+	{{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+	 {0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}},
+
+	{{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+	 {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}},
+
+	{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
+
+	{{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
+	 {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}},
+
+	{{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57},
+	 {0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42},
+	 {0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}},
+
+	{{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E},
+	 {0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA},
+	 {0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}},
+
+	{{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86},
+	 {0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72},
+	 {0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}},
+
+	{{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E},
+	 {0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A},
+	 {0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}},
+
+	{{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6},
+	 {0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2},
+	 {0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}},
+
+	{{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE},
+	 {0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A},
+	 {0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}},
+
+	{{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6},
+	 {0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2},
+	 {0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}},
+
+	{{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE},
+	 {0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A},
+	 {0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}},
+
+	{{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16},
+	 {0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
+	 {0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}},
+
+	{{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F},
+	 {0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A},
+	 {0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}},
+
+	{{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46},
+	 {0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32},
+	 {0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}},
+
+	{{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E},
+	 {0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA},
+	 {0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}},
+
+	{{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76},
+	 {0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62},
+	 {0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}},
+
+	{{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07},
+	 {0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2},
+	 {0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}},
+
+	{{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F},
+	 {0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA},
+	 {0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}},
+
+	{{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7},
+	 {0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92},
+	 {0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}},
+
+	{{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF},
+	 {0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A},
+	 {0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}},
+
+	{{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6},
+	 {0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2},
+	 {0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}},
+
+	{{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF},
+	 {0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A},
+	 {0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}},
+
+	{{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+	 {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}},
+
+	{{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
+	 {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}},
+
+	{{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
+	 {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}},
+
+	{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	 {0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}},
+
+	{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}},
+
+	{{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	 {0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}},
+
+	{{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
+	 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	 {0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}}
+};
+
+uchar CBCkey[16] = { 
+	 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 
+	 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87
+};
+
+uchar CBCiv[8] = {
+	 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10
+};
+
+uchar CBCdata[29] = {
+	 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x20, 
+	 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74, 
+	 0x68, 0x65, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 
+	 0x66, 0x6F, 0x72, 0x20, 0x00
+};
+
+uchar CBCcipher[32] = {
+	 0x6B, 0x77, 0xB4, 0xD6, 0x30, 0x06, 0xDE, 0xE6,
+	 0x05, 0xB1, 0x56, 0xE2, 0x74, 0x03, 0x97, 0x93,
+	 0x58, 0xDE, 0xB9, 0xE7, 0x15, 0x46, 0x16, 0xD9,
+	 0x59, 0xF1, 0x65, 0x2B, 0xD5, 0xFF, 0x92, 0xCC
+};
+
+int
+testECB(Testvector *t)
+{
+	BFstate s;
+	int i;
+	uchar aux[Bsz];
+
+	memcpy(aux, t->plain, Bsz);
+
+	memset(&s, 0, sizeof(BFstate));
+	setupBFstate(&s, t->key, Bsz, nil);
+	bfECBencrypt(aux, Bsz, &s);
+
+	if(memcmp(aux, t->cipher, Bsz) != 0){
+		fprint(2, "ECB encrypt failed, ciphertext is:\n");
+		for(i = 0; i < Bsz; i++)
+			fprint(2, "%02X", aux[i]);
+		fprint(2, "\nand should be:\n");
+		for(i = 0; i < Bsz; i++)
+			fprint(2, "%02X", t->cipher[i]);
+		fprint(2, "\n");
+		return -1;
+	}
+
+	memset(&s, 0, sizeof(BFstate));
+	setupBFstate(&s, t->key, Bsz, nil);
+	bfECBdecrypt(aux, Bsz, &s);
+
+	if(memcmp(aux, t->plain, Bsz) != 0){
+		fprint(2, "ECB decrypt failed, plaintext is:\n");
+		for(i = 0; i < Bsz; i++)
+			fprint(2, "%02X", aux[i]);
+		fprint(2, "\nand should be:\n");
+		for(i = 0; i < Bsz; i++)
+			fprint(2, "%02X", t->plain[i]);
+		fprint(2, "\n");
+		return -1;
+	}
+	return 0;
+}
+
+int
+testCBC(void)
+{
+	BFstate s;
+	uchar aux[32];
+	int i;
+
+	memset(aux, 0 , sizeof(aux));
+	memcpy(aux, CBCdata, sizeof(CBCdata));
+	memset(&s, 0, sizeof(BFstate));
+	setupBFstate(&s, CBCkey, sizeof(CBCkey), CBCiv);
+	bfCBCencrypt(aux, 32, &s);
+	
+	if(memcmp(aux, CBCcipher, sizeof(CBCcipher)) != 0){
+		fprint(2, "CBC encrypt failed, ciphertext is:\n");
+		for(i = 0; i < sizeof(aux); i++)
+			fprint(2, "%02X", aux[i]);
+		fprint(2, "\nand should be:\n");
+		for(i = 0; i < sizeof(CBCcipher); i++)
+			fprint(2, "%02X", CBCcipher[i]);
+		fprint(2, "\n");
+		return -1;
+	}
+
+	memset(&s, 0, sizeof(BFstate));
+	setupBFstate(&s, CBCkey, sizeof(CBCkey), CBCiv);
+	bfCBCdecrypt(aux, 32, &s);
+
+	if(memcmp(aux, CBCdata, sizeof(CBCdata)) != 0){
+		fprint(2, "CBC decrypt failed, plaintext is:\n");
+		for(i = 0; i < sizeof(aux); i++)
+			fprint(2, "%02X", aux[i]);
+		fprint(2, "\nand should be:\n");
+		for(i = 0; i < sizeof(CBCdata); i++)
+			fprint(2, "%02X", CBCdata[i]);
+		fprint(2, "\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void
+main(int argc, char **argv)
+{
+	int i;
+
+	if(argc != 1)
+		sysfatal("usage: %s", argv[0]);
+
+	for(i=0; i < nelem(vector); i++)
+		if(testECB(&vector[i]) < 0)
+			sysfatal("TestECB %d failed", i);
+
+	if(testCBC() < 0)
+		sysfatal("TestCBC failed");
+	exits(nil);
+}
--- /dev/null
+++ b/libsec/blowfish.c
@@ -1,0 +1,524 @@
+#include "os.h"
+#include <libsec.h>
+
+// Blowfish block cipher.  See:
+// 	Lecture Notes in Computer Science 809
+// 	Fast Software Encryption
+// 	Cambridge Security Workshop, Cambridge, England (1993)
+
+static u32int sbox[1024];
+static u32int pbox[BFrounds+2];
+
+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)
+{
+	int i, j;
+	u32int n, buf[2];
+
+	memset(s, 0, sizeof(*s));
+	memset(buf, 0, sizeof buf);
+
+	if (keybytes > sizeof(s->key))
+		keybytes = sizeof(s->key);
+
+	memmove(s->key, key, keybytes);
+
+	if (ivec != nil)
+		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));
+
+	if (keybytes > 4*(BFrounds + 2))
+		keybytes = 4*(BFrounds + 2);
+
+	for(i=j=0; i < BFrounds+2; i++) {
+		n = key[j];
+		j = (j+1) % keybytes;
+
+		n <<= 8;
+		n |= key[j];
+		j = (j+1) % keybytes;
+
+		n <<= 8;
+		n |= key[j];
+		j = (j+1) % keybytes;
+
+		n <<= 8;
+		n |= key[j];
+		j = (j+1) % keybytes;
+
+		s->pbox[i] ^= n;
+	}
+
+	for(i=0; i < BFrounds+2; i += 2) {
+		bfencrypt(buf, s);
+		s->pbox[i] = buf[0];
+		s->pbox[i+1] = buf[1];
+	}
+
+	for(i=0; i < 1024; i += 2) {
+		bfencrypt(buf, s);
+		s->sbox[i] = buf[0];
+		s->sbox[i+1] = buf[1];
+	}
+
+	s->setup = 0xcafebabe;
+}
+
+void
+bfCBCencrypt(uchar *buf, int n, BFstate *s)
+{
+	int i;
+	u32int bo[2], bi[2];
+
+	assert((n & 7) == 0);
+
+	buf2ints(s->ivec, bo);
+	for(i=0; i < n; i += 8, buf += 8) {
+		buf2ints(buf, bi);
+
+		bi[0] ^= bo[0];
+		bi[1] ^= bo[1];
+
+		bfencrypt(bi, s);
+
+		bo[0] = bi[0];
+		bo[1] = bi[1];
+
+		ints2buf(bi, buf);
+	}
+	ints2buf(bo, s->ivec);
+	return;
+}
+
+void
+bfCBCdecrypt(uchar *buf, int n, BFstate *s)
+{
+	int i;
+	u32int  bo[2], bi[2], xr[2];
+
+	assert((n & 7) == 0);
+
+	buf2ints(s->ivec, bo);
+	for(i=0; i < n; i += 8, buf += 8) {
+		buf2ints(buf, bi);
+
+		xr[0] = bi[0];
+		xr[1] = bi[1];
+
+		bfdecrypt(bi, s);
+
+		bo[0] ^= bi[0];
+		bo[1] ^= bi[1];
+
+		ints2buf(bo, buf);
+
+		bo[0] = xr[0];
+		bo[1] = xr[1];
+	}
+	ints2buf(bo, s->ivec);
+	return;
+}
+
+void
+bfECBencrypt(uchar *buf, int n, BFstate *s)
+{
+	int i;
+	u32int b[2];
+
+	for(i=0; i < n; i += 8, buf += 8) {
+		buf2ints(buf, b);
+		bfencrypt(b, s);
+		ints2buf(b, buf);
+	}
+
+	return;
+}
+
+void
+bfECBdecrypt(uchar *buf, int n, BFstate *s)
+{
+	int i;
+	u32int b[2];
+
+	for(i=0; i < n; i += 8, buf += 8) {
+		buf2ints(buf, b);
+		bfdecrypt(b, s);
+		ints2buf(b, buf);
+	}
+
+	return;		
+}
+
+static void
+bfencrypt(u32int *b, BFstate *s)
+{
+	int i;
+	u32int l, r;
+	u32int *pb, *sb;
+
+	l = b[0];
+	r = b[1];
+
+	pb = s->pbox;
+	sb = s->sbox;
+
+	l ^= pb[0];
+
+	for(i=1; i<16; i += 2) {
+		r ^= pb[i];
+		r ^= ( (sb[ (uchar) (l>>24)] + sb[256 + ((uchar) (l>>16))]) ^  
+			sb[512 + ((uchar) (l>>8))]) + sb[768 +((uchar) l)];
+
+		l ^= pb[i+1];
+		l ^= ( (sb[ (uchar) (r>>24)] + sb[256 + ((uchar) (r>>16))]) ^  
+			sb[512 + ((uchar) (r>>8))]) + sb[768 +((uchar) r)];
+	}
+
+	r ^= pb[BFrounds+1];
+
+	/* sic */
+	b[0] = r;
+	b[1] = l;
+
+	return;
+}
+
+static void
+bfdecrypt(u32int *b, BFstate *s)
+{
+	int i;
+	u32int l, r;
+	u32int *pb, *sb;
+
+	l = b[0];
+	r = b[1];
+
+	pb = s->pbox;
+	sb = s->sbox;
+
+	l ^= pb[BFrounds+1];
+
+	for(i=16; i > 0; i -= 2) {
+		r ^= pb[i];
+		r ^= ( (sb[ (uchar) (l>>24)] + sb[256 + ((uchar) (l>>16))]) ^  
+			sb[512 + ((uchar) (l>>8))]) + sb[768 +((uchar) l)];
+
+		l ^= pb[i-1];
+		l ^= ( (sb[ (uchar) (r>>24)] + sb[256 + ((uchar) (r>>16))]) ^  
+			sb[512 + ((uchar) (r>>8))]) + sb[768 +((uchar) r)];
+	}
+
+	r ^= pb[0];
+
+	/* sic */
+	b[0] = r;
+	b[1] = l;
+
+	return;
+}
+
+static u32int pbox[BFrounds+2] = {
+	0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 
+	0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 
+	0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 
+	0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 
+	0x9216d5d9, 0x8979fb1b
+};
+
+static u32int sbox[1024] = {
+	0xd1310ba6L, 0x98dfb5acL, 0x2ffd72dbL, 0xd01adfb7L, 
+	0xb8e1afedL, 0x6a267e96L, 0xba7c9045L, 0xf12c7f99L, 
+	0x24a19947L, 0xb3916cf7L, 0x0801f2e2L, 0x858efc16L, 
+	0x636920d8L, 0x71574e69L, 0xa458fea3L, 0xf4933d7eL, 
+	0x0d95748fL, 0x728eb658L, 0x718bcd58L, 0x82154aeeL, 
+	0x7b54a41dL, 0xc25a59b5L, 0x9c30d539L, 0x2af26013L, 
+	0xc5d1b023L, 0x286085f0L, 0xca417918L, 0xb8db38efL, 
+	0x8e79dcb0L, 0x603a180eL, 0x6c9e0e8bL, 0xb01e8a3eL, 
+	0xd71577c1L, 0xbd314b27L, 0x78af2fdaL, 0x55605c60L, 
+	0xe65525f3L, 0xaa55ab94L, 0x57489862L, 0x63e81440L, 
+	0x55ca396aL, 0x2aab10b6L, 0xb4cc5c34L, 0x1141e8ceL, 
+	0xa15486afL, 0x7c72e993L, 0xb3ee1411L, 0x636fbc2aL, 
+	0x2ba9c55dL, 0x741831f6L, 0xce5c3e16L, 0x9b87931eL, 
+	0xafd6ba33L, 0x6c24cf5cL, 0x7a325381L, 0x28958677L, 
+	0x3b8f4898L, 0x6b4bb9afL, 0xc4bfe81bL, 0x66282193L, 
+	0x61d809ccL, 0xfb21a991L, 0x487cac60L, 0x5dec8032L, 
+	0xef845d5dL, 0xe98575b1L, 0xdc262302L, 0xeb651b88L, 
+	0x23893e81L, 0xd396acc5L, 0x0f6d6ff3L, 0x83f44239L, 
+	0x2e0b4482L, 0xa4842004L, 0x69c8f04aL, 0x9e1f9b5eL, 
+	0x21c66842L, 0xf6e96c9aL, 0x670c9c61L, 0xabd388f0L, 
+	0x6a51a0d2L, 0xd8542f68L, 0x960fa728L, 0xab5133a3L, 
+	0x6eef0b6cL, 0x137a3be4L, 0xba3bf050L, 0x7efb2a98L, 
+	0xa1f1651dL, 0x39af0176L, 0x66ca593eL, 0x82430e88L, 
+	0x8cee8619L, 0x456f9fb4L, 0x7d84a5c3L, 0x3b8b5ebeL, 
+	0xe06f75d8L, 0x85c12073L, 0x401a449fL, 0x56c16aa6L, 
+	0x4ed3aa62L, 0x363f7706L, 0x1bfedf72L, 0x429b023dL, 
+	0x37d0d724L, 0xd00a1248L, 0xdb0fead3L, 0x49f1c09bL, 
+	0x075372c9L, 0x80991b7bL, 0x25d479d8L, 0xf6e8def7L, 
+	0xe3fe501aL, 0xb6794c3bL, 0x976ce0bdL, 0x04c006baL, 
+	0xc1a94fb6L, 0x409f60c4L, 0x5e5c9ec2L, 0x196a2463L, 
+	0x68fb6fafL, 0x3e6c53b5L, 0x1339b2ebL, 0x3b52ec6fL, 
+	0x6dfc511fL, 0x9b30952cL, 0xcc814544L, 0xaf5ebd09L, 
+	0xbee3d004L, 0xde334afdL, 0x660f2807L, 0x192e4bb3L, 
+	0xc0cba857L, 0x45c8740fL, 0xd20b5f39L, 0xb9d3fbdbL, 
+	0x5579c0bdL, 0x1a60320aL, 0xd6a100c6L, 0x402c7279L, 
+	0x679f25feL, 0xfb1fa3ccL, 0x8ea5e9f8L, 0xdb3222f8L, 
+	0x3c7516dfL, 0xfd616b15L, 0x2f501ec8L, 0xad0552abL, 
+	0x323db5faL, 0xfd238760L, 0x53317b48L, 0x3e00df82L, 
+	0x9e5c57bbL, 0xca6f8ca0L, 0x1a87562eL, 0xdf1769dbL, 
+	0xd542a8f6L, 0x287effc3L, 0xac6732c6L, 0x8c4f5573L, 
+	0x695b27b0L, 0xbbca58c8L, 0xe1ffa35dL, 0xb8f011a0L, 
+	0x10fa3d98L, 0xfd2183b8L, 0x4afcb56cL, 0x2dd1d35bL, 
+	0x9a53e479L, 0xb6f84565L, 0xd28e49bcL, 0x4bfb9790L, 
+	0xe1ddf2daL, 0xa4cb7e33L, 0x62fb1341L, 0xcee4c6e8L, 
+	0xef20cadaL, 0x36774c01L, 0xd07e9efeL, 0x2bf11fb4L, 
+	0x95dbda4dL, 0xae909198L, 0xeaad8e71L, 0x6b93d5a0L, 
+	0xd08ed1d0L, 0xafc725e0L, 0x8e3c5b2fL, 0x8e7594b7L, 
+	0x8ff6e2fbL, 0xf2122b64L, 0x8888b812L, 0x900df01cL, 
+	0x4fad5ea0L, 0x688fc31cL, 0xd1cff191L, 0xb3a8c1adL, 
+	0x2f2f2218L, 0xbe0e1777L, 0xea752dfeL, 0x8b021fa1L, 
+	0xe5a0cc0fL, 0xb56f74e8L, 0x18acf3d6L, 0xce89e299L, 
+	0xb4a84fe0L, 0xfd13e0b7L, 0x7cc43b81L, 0xd2ada8d9L, 
+	0x165fa266L, 0x80957705L, 0x93cc7314L, 0x211a1477L, 
+	0xe6ad2065L, 0x77b5fa86L, 0xc75442f5L, 0xfb9d35cfL, 
+	0xebcdaf0cL, 0x7b3e89a0L, 0xd6411bd3L, 0xae1e7e49L, 
+	0x00250e2dL, 0x2071b35eL, 0x226800bbL, 0x57b8e0afL, 
+	0x2464369bL, 0xf009b91eL, 0x5563911dL, 0x59dfa6aaL, 
+	0x78c14389L, 0xd95a537fL, 0x207d5ba2L, 0x02e5b9c5L, 
+	0x83260376L, 0x6295cfa9L, 0x11c81968L, 0x4e734a41L, 
+	0xb3472dcaL, 0x7b14a94aL, 0x1b510052L, 0x9a532915L, 
+	0xd60f573fL, 0xbc9bc6e4L, 0x2b60a476L, 0x81e67400L, 
+	0x08ba6fb5L, 0x571be91fL, 0xf296ec6bL, 0x2a0dd915L, 
+	0xb6636521L, 0xe7b9f9b6L, 0xff34052eL, 0xc5855664L, 
+	0x53b02d5dL, 0xa99f8fa1L, 0x08ba4799L, 0x6e85076aL, 
+	0x4b7a70e9L, 0xb5b32944L, 0xdb75092eL, 0xc4192623L, 
+	0xad6ea6b0L, 0x49a7df7dL, 0x9cee60b8L, 0x8fedb266L, 
+	0xecaa8c71L, 0x699a17ffL, 0x5664526cL, 0xc2b19ee1L, 
+	0x193602a5L, 0x75094c29L, 0xa0591340L, 0xe4183a3eL, 
+	0x3f54989aL, 0x5b429d65L, 0x6b8fe4d6L, 0x99f73fd6L, 
+	0xa1d29c07L, 0xefe830f5L, 0x4d2d38e6L, 0xf0255dc1L, 
+	0x4cdd2086L, 0x8470eb26L, 0x6382e9c6L, 0x021ecc5eL, 
+	0x09686b3fL, 0x3ebaefc9L, 0x3c971814L, 0x6b6a70a1L, 
+	0x687f3584L, 0x52a0e286L, 0xb79c5305L, 0xaa500737L, 
+	0x3e07841cL, 0x7fdeae5cL, 0x8e7d44ecL, 0x5716f2b8L, 
+	0xb03ada37L, 0xf0500c0dL, 0xf01c1f04L, 0x0200b3ffL, 
+	0xae0cf51aL, 0x3cb574b2L, 0x25837a58L, 0xdc0921bdL, 
+	0xd19113f9L, 0x7ca92ff6L, 0x94324773L, 0x22f54701L, 
+	0x3ae5e581L, 0x37c2dadcL, 0xc8b57634L, 0x9af3dda7L, 
+	0xa9446146L, 0x0fd0030eL, 0xecc8c73eL, 0xa4751e41L, 
+	0xe238cd99L, 0x3bea0e2fL, 0x3280bba1L, 0x183eb331L, 
+	0x4e548b38L, 0x4f6db908L, 0x6f420d03L, 0xf60a04bfL, 
+	0x2cb81290L, 0x24977c79L, 0x5679b072L, 0xbcaf89afL, 
+	0xde9a771fL, 0xd9930810L, 0xb38bae12L, 0xdccf3f2eL, 
+	0x5512721fL, 0x2e6b7124L, 0x501adde6L, 0x9f84cd87L, 
+	0x7a584718L, 0x7408da17L, 0xbc9f9abcL, 0xe94b7d8cL, 
+	0xec7aec3aL, 0xdb851dfaL, 0x63094366L, 0xc464c3d2L, 
+	0xef1c1847L, 0x3215d908L, 0xdd433b37L, 0x24c2ba16L, 
+	0x12a14d43L, 0x2a65c451L, 0x50940002L, 0x133ae4ddL, 
+	0x71dff89eL, 0x10314e55L, 0x81ac77d6L, 0x5f11199bL, 
+	0x043556f1L, 0xd7a3c76bL, 0x3c11183bL, 0x5924a509L, 
+	0xf28fe6edL, 0x97f1fbfaL, 0x9ebabf2cL, 0x1e153c6eL, 
+	0x86e34570L, 0xeae96fb1L, 0x860e5e0aL, 0x5a3e2ab3L, 
+	0x771fe71cL, 0x4e3d06faL, 0x2965dcb9L, 0x99e71d0fL, 
+	0x803e89d6L, 0x5266c825L, 0x2e4cc978L, 0x9c10b36aL, 
+	0xc6150ebaL, 0x94e2ea78L, 0xa5fc3c53L, 0x1e0a2df4L, 
+	0xf2f74ea7L, 0x361d2b3dL, 0x1939260fL, 0x19c27960L, 
+	0x5223a708L, 0xf71312b6L, 0xebadfe6eL, 0xeac31f66L, 
+	0xe3bc4595L, 0xa67bc883L, 0xb17f37d1L, 0x018cff28L, 
+	0xc332ddefL, 0xbe6c5aa5L, 0x65582185L, 0x68ab9802L, 
+	0xeecea50fL, 0xdb2f953bL, 0x2aef7dadL, 0x5b6e2f84L, 
+	0x1521b628L, 0x29076170L, 0xecdd4775L, 0x619f1510L, 
+	0x13cca830L, 0xeb61bd96L, 0x0334fe1eL, 0xaa0363cfL, 
+	0xb5735c90L, 0x4c70a239L, 0xd59e9e0bL, 0xcbaade14L, 
+	0xeecc86bcL, 0x60622ca7L, 0x9cab5cabL, 0xb2f3846eL, 
+	0x648b1eafL, 0x19bdf0caL, 0xa02369b9L, 0x655abb50L, 
+	0x40685a32L, 0x3c2ab4b3L, 0x319ee9d5L, 0xc021b8f7L, 
+	0x9b540b19L, 0x875fa099L, 0x95f7997eL, 0x623d7da8L, 
+	0xf837889aL, 0x97e32d77L, 0x11ed935fL, 0x16681281L, 
+	0x0e358829L, 0xc7e61fd6L, 0x96dedfa1L, 0x7858ba99L, 
+	0x57f584a5L, 0x1b227263L, 0x9b83c3ffL, 0x1ac24696L, 
+	0xcdb30aebL, 0x532e3054L, 0x8fd948e4L, 0x6dbc3128L, 
+	0x58ebf2efL, 0x34c6ffeaL, 0xfe28ed61L, 0xee7c3c73L, 
+	0x5d4a14d9L, 0xe864b7e3L, 0x42105d14L, 0x203e13e0L, 
+	0x45eee2b6L, 0xa3aaabeaL, 0xdb6c4f15L, 0xfacb4fd0L, 
+	0xc742f442L, 0xef6abbb5L, 0x654f3b1dL, 0x41cd2105L, 
+	0xd81e799eL, 0x86854dc7L, 0xe44b476aL, 0x3d816250L, 
+	0xcf62a1f2L, 0x5b8d2646L, 0xfc8883a0L, 0xc1c7b6a3L, 
+	0x7f1524c3L, 0x69cb7492L, 0x47848a0bL, 0x5692b285L, 
+	0x095bbf00L, 0xad19489dL, 0x1462b174L, 0x23820e00L, 
+	0x58428d2aL, 0x0c55f5eaL, 0x1dadf43eL, 0x233f7061L, 
+	0x3372f092L, 0x8d937e41L, 0xd65fecf1L, 0x6c223bdbL, 
+	0x7cde3759L, 0xcbee7460L, 0x4085f2a7L, 0xce77326eL, 
+	0xa6078084L, 0x19f8509eL, 0xe8efd855L, 0x61d99735L, 
+	0xa969a7aaL, 0xc50c06c2L, 0x5a04abfcL, 0x800bcadcL, 
+	0x9e447a2eL, 0xc3453484L, 0xfdd56705L, 0x0e1e9ec9L, 
+	0xdb73dbd3L, 0x105588cdL, 0x675fda79L, 0xe3674340L, 
+	0xc5c43465L, 0x713e38d8L, 0x3d28f89eL, 0xf16dff20L, 
+	0x153e21e7L, 0x8fb03d4aL, 0xe6e39f2bL, 0xdb83adf7L, 
+	0xe93d5a68L, 0x948140f7L, 0xf64c261cL, 0x94692934L, 
+	0x411520f7L, 0x7602d4f7L, 0xbcf46b2eL, 0xd4a20068L, 
+	0xd4082471L, 0x3320f46aL, 0x43b7d4b7L, 0x500061afL, 
+	0x1e39f62eL, 0x97244546L, 0x14214f74L, 0xbf8b8840L, 
+	0x4d95fc1dL, 0x96b591afL, 0x70f4ddd3L, 0x66a02f45L, 
+	0xbfbc09ecL, 0x03bd9785L, 0x7fac6dd0L, 0x31cb8504L, 
+	0x96eb27b3L, 0x55fd3941L, 0xda2547e6L, 0xabca0a9aL, 
+	0x28507825L, 0x530429f4L, 0x0a2c86daL, 0xe9b66dfbL, 
+	0x68dc1462L, 0xd7486900L, 0x680ec0a4L, 0x27a18deeL, 
+	0x4f3ffea2L, 0xe887ad8cL, 0xb58ce006L, 0x7af4d6b6L, 
+	0xaace1e7cL, 0xd3375fecL, 0xce78a399L, 0x406b2a42L, 
+	0x20fe9e35L, 0xd9f385b9L, 0xee39d7abL, 0x3b124e8bL, 
+	0x1dc9faf7L, 0x4b6d1856L, 0x26a36631L, 0xeae397b2L, 
+	0x3a6efa74L, 0xdd5b4332L, 0x6841e7f7L, 0xca7820fbL, 
+	0xfb0af54eL, 0xd8feb397L, 0x454056acL, 0xba489527L, 
+	0x55533a3aL, 0x20838d87L, 0xfe6ba9b7L, 0xd096954bL, 
+	0x55a867bcL, 0xa1159a58L, 0xcca92963L, 0x99e1db33L, 
+	0xa62a4a56L, 0x3f3125f9L, 0x5ef47e1cL, 0x9029317cL, 
+	0xfdf8e802L, 0x04272f70L, 0x80bb155cL, 0x05282ce3L, 
+	0x95c11548L, 0xe4c66d22L, 0x48c1133fL, 0xc70f86dcL, 
+	0x07f9c9eeL, 0x41041f0fL, 0x404779a4L, 0x5d886e17L, 
+	0x325f51ebL, 0xd59bc0d1L, 0xf2bcc18fL, 0x41113564L, 
+	0x257b7834L, 0x602a9c60L, 0xdff8e8a3L, 0x1f636c1bL, 
+	0x0e12b4c2L, 0x02e1329eL, 0xaf664fd1L, 0xcad18115L, 
+	0x6b2395e0L, 0x333e92e1L, 0x3b240b62L, 0xeebeb922L, 
+	0x85b2a20eL, 0xe6ba0d99L, 0xde720c8cL, 0x2da2f728L, 
+	0xd0127845L, 0x95b794fdL, 0x647d0862L, 0xe7ccf5f0L, 
+	0x5449a36fL, 0x877d48faL, 0xc39dfd27L, 0xf33e8d1eL, 
+	0x0a476341L, 0x992eff74L, 0x3a6f6eabL, 0xf4f8fd37L, 
+	0xa812dc60L, 0xa1ebddf8L, 0x991be14cL, 0xdb6e6b0dL, 
+	0xc67b5510L, 0x6d672c37L, 0x2765d43bL, 0xdcd0e804L, 
+	0xf1290dc7L, 0xcc00ffa3L, 0xb5390f92L, 0x690fed0bL, 
+	0x667b9ffbL, 0xcedb7d9cL, 0xa091cf0bL, 0xd9155ea3L, 
+	0xbb132f88L, 0x515bad24L, 0x7b9479bfL, 0x763bd6ebL, 
+	0x37392eb3L, 0xcc115979L, 0x8026e297L, 0xf42e312dL, 
+	0x6842ada7L, 0xc66a2b3bL, 0x12754cccL, 0x782ef11cL, 
+	0x6a124237L, 0xb79251e7L, 0x06a1bbe6L, 0x4bfb6350L, 
+	0x1a6b1018L, 0x11caedfaL, 0x3d25bdd8L, 0xe2e1c3c9L, 
+	0x44421659L, 0x0a121386L, 0xd90cec6eL, 0xd5abea2aL, 
+	0x64af674eL, 0xda86a85fL, 0xbebfe988L, 0x64e4c3feL, 
+	0x9dbc8057L, 0xf0f7c086L, 0x60787bf8L, 0x6003604dL, 
+	0xd1fd8346L, 0xf6381fb0L, 0x7745ae04L, 0xd736fcccL, 
+	0x83426b33L, 0xf01eab71L, 0xb0804187L, 0x3c005e5fL, 
+	0x77a057beL, 0xbde8ae24L, 0x55464299L, 0xbf582e61L, 
+	0x4e58f48fL, 0xf2ddfda2L, 0xf474ef38L, 0x8789bdc2L, 
+	0x5366f9c3L, 0xc8b38e74L, 0xb475f255L, 0x46fcd9b9L, 
+	0x7aeb2661L, 0x8b1ddf84L, 0x846a0e79L, 0x915f95e2L, 
+	0x466e598eL, 0x20b45770L, 0x8cd55591L, 0xc902de4cL, 
+	0xb90bace1L, 0xbb8205d0L, 0x11a86248L, 0x7574a99eL, 
+	0xb77f19b6L, 0xe0a9dc09L, 0x662d09a1L, 0xc4324633L, 
+	0xe85a1f02L, 0x09f0be8cL, 0x4a99a025L, 0x1d6efe10L, 
+	0x1ab93d1dL, 0x0ba5a4dfL, 0xa186f20fL, 0x2868f169L, 
+	0xdcb7da83L, 0x573906feL, 0xa1e2ce9bL, 0x4fcd7f52L, 
+	0x50115e01L, 0xa70683faL, 0xa002b5c4L, 0x0de6d027L, 
+	0x9af88c27L, 0x773f8641L, 0xc3604c06L, 0x61a806b5L, 
+	0xf0177a28L, 0xc0f586e0L, 0x006058aaL, 0x30dc7d62L, 
+	0x11e69ed7L, 0x2338ea63L, 0x53c2dd94L, 0xc2c21634L, 
+	0xbbcbee56L, 0x90bcb6deL, 0xebfc7da1L, 0xce591d76L, 
+	0x6f05e409L, 0x4b7c0188L, 0x39720a3dL, 0x7c927c24L, 
+	0x86e3725fL, 0x724d9db9L, 0x1ac15bb4L, 0xd39eb8fcL, 
+	0xed545578L, 0x08fca5b5L, 0xd83d7cd3L, 0x4dad0fc4L, 
+	0x1e50ef5eL, 0xb161e6f8L, 0xa28514d9L, 0x6c51133cL, 
+	0x6fd5c7e7L, 0x56e14ec4L, 0x362abfceL, 0xddc6c837L, 
+	0xd79a3234L, 0x92638212L, 0x670efa8eL, 0x406000e0L, 
+	0x3a39ce37L, 0xd3faf5cfL, 0xabc27737L, 0x5ac52d1bL, 
+	0x5cb0679eL, 0x4fa33742L, 0xd3822740L, 0x99bc9bbeL, 
+	0xd5118e9dL, 0xbf0f7315L, 0xd62d1c7eL, 0xc700c47bL, 
+	0xb78c1b6bL, 0x21a19045L, 0xb26eb1beL, 0x6a366eb4L, 
+	0x5748ab2fL, 0xbc946e79L, 0xc6a376d2L, 0x6549c2c8L, 
+	0x530ff8eeL, 0x468dde7dL, 0xd5730a1dL, 0x4cd04dc6L, 
+	0x2939bbdbL, 0xa9ba4650L, 0xac9526e8L, 0xbe5ee304L, 
+	0xa1fad5f0L, 0x6a2d519aL, 0x63ef8ce2L, 0x9a86ee22L, 
+	0xc089c2b8L, 0x43242ef6L, 0xa51e03aaL, 0x9cf2d0a4L, 
+	0x83c061baL, 0x9be96a4dL, 0x8fe51550L, 0xba645bd6L, 
+	0x2826a2f9L, 0xa73a3ae1L, 0x4ba99586L, 0xef5562e9L, 
+	0xc72fefd3L, 0xf752f7daL, 0x3f046f69L, 0x77fa0a59L, 
+	0x80e4a915L, 0x87b08601L, 0x9b09e6adL, 0x3b3ee593L, 
+	0xe990fd5aL, 0x9e34d797L, 0x2cf0b7d9L, 0x022b8b51L, 
+	0x96d5ac3aL, 0x017da67dL, 0xd1cf3ed6L, 0x7c7d2d28L, 
+	0x1f9f25cfL, 0xadf2b89bL, 0x5ad6b472L, 0x5a88f54cL, 
+	0xe029ac71L, 0xe019a5e6L, 0x47b0acfdL, 0xed93fa9bL, 
+	0xe8d3c48dL, 0x283b57ccL, 0xf8d56629L, 0x79132e28L, 
+	0x785f0191L, 0xed756055L, 0xf7960e44L, 0xe3d35e8cL, 
+	0x15056dd4L, 0x88f46dbaL, 0x03a16125L, 0x0564f0bdL, 
+	0xc3eb9e15L, 0x3c9057a2L, 0x97271aecL, 0xa93a072aL, 
+	0x1b3f6d9bL, 0x1e6321f5L, 0xf59c66fbL, 0x26dcf319L, 
+	0x7533d928L, 0xb155fdf5L, 0x03563482L, 0x8aba3cbbL, 
+	0x28517711L, 0xc20ad9f8L, 0xabcc5167L, 0xccad925fL, 
+	0x4de81751L, 0x3830dc8eL, 0x379d5862L, 0x9320f991L, 
+	0xea7a90c2L, 0xfb3e7bceL, 0x5121ce64L, 0x774fbe32L, 
+	0xa8b6e37eL, 0xc3293d46L, 0x48de5369L, 0x6413e680L, 
+	0xa2ae0810L, 0xdd6db224L, 0x69852dfdL, 0x09072166L, 
+	0xb39a460aL, 0x6445c0ddL, 0x586cdecfL, 0x1c20c8aeL, 
+	0x5bbef7ddL, 0x1b588d40L, 0xccd2017fL, 0x6bb4e3bbL, 
+	0xdda26a7eL, 0x3a59ff45L, 0x3e350a44L, 0xbcb4cdd5L, 
+	0x72eacea8L, 0xfa6484bbL, 0x8d6612aeL, 0xbf3c6f47L, 
+	0xd29be463L, 0x542f5d9eL, 0xaec2771bL, 0xf64e6370L, 
+	0x740e0d8dL, 0xe75b1357L, 0xf8721671L, 0xaf537d5dL, 
+	0x4040cb08L, 0x4eb4e2ccL, 0x34d2466aL, 0x0115af84L, 
+	0xe1b00428L, 0x95983a1dL, 0x06b89fb4L, 0xce6ea048L, 
+	0x6f3f3b82L, 0x3520ab82L, 0x011a1d4bL, 0x277227f8L, 
+	0x611560b1L, 0xe7933fdcL, 0xbb3a792bL, 0x344525bdL, 
+	0xa08839e1L, 0x51ce794bL, 0x2f32c9b7L, 0xa01fbac9L, 
+	0xe01cc87eL, 0xbcc7d1f6L, 0xcf0111c3L, 0xa1e8aac7L, 
+	0x1a908749L, 0xd44fbd9aL, 0xd0dadecbL, 0xd50ada38L, 
+	0x0339c32aL, 0xc6913667L, 0x8df9317cL, 0xe0b12b4fL, 
+	0xf79e59b7L, 0x43f5bb3aL, 0xf2d519ffL, 0x27d9459cL, 
+	0xbf97222cL, 0x15e6fc2aL, 0x0f91fc71L, 0x9b941525L, 
+	0xfae59361L, 0xceb69cebL, 0xc2a86459L, 0x12baa8d1L, 
+	0xb6c1075eL, 0xe3056a0cL, 0x10d25065L, 0xcb03a442L, 
+	0xe0ec6e0eL, 0x1698db3bL, 0x4c98a0beL, 0x3278e964L, 
+	0x9f1f9532L, 0xe0d392dfL, 0xd3a0342bL, 0x8971f21eL, 
+	0x1b0a7441L, 0x4ba3348cL, 0xc5be7120L, 0xc37632d8L, 
+	0xdf359f8dL, 0x9b992f2eL, 0xe60b6f47L, 0x0fe3f11dL, 
+	0xe54cda54L, 0x1edad891L, 0xce6279cfL, 0xcd3e7e6fL, 
+	0x1618b166L, 0xfd2c1d05L, 0x848fd2c5L, 0xf6fb2299L, 
+	0xf523f357L, 0xa6327623L, 0x93a83531L, 0x56cccd02L, 
+	0xacf08162L, 0x5a75ebb5L, 0x6e163697L, 0x88d273ccL, 
+	0xde966292L, 0x81b949d0L, 0x4c50901bL, 0x71c65614L, 
+	0xe6c6c7bdL, 0x327a140aL, 0x45e1d006L, 0xc3f27b9aL, 
+	0xc9aa53fdL, 0x62a80f00L, 0xbb25bfe2L, 0x35bdd2f6L, 
+	0x71126905L, 0xb2040222L, 0xb6cbcf7cL, 0xcd769c2bL, 
+	0x53113ec0L, 0x1640e3d3L, 0x38abbd60L, 0x2547adf0L, 
+	0xba38209cL, 0xf746ce76L, 0x77afa1c5L, 0x20756060L, 
+	0x85cbfe4eL, 0x8ae88dd8L, 0x7aaaf9b0L, 0x4cf9aa7eL, 
+	0x1948c25cL, 0x02fb8a8cL, 0x01c36ae4L, 0xd6ebe1f9L, 
+	0x90d4f869L, 0xa65cdea0L, 0x3f09252dL, 0xc208e69fL, 
+	0xb74e6132L, 0xce77e25bL, 0x578fdfe3L, 0x3ac372e6L, 
+};
+
--- /dev/null
+++ b/libsec/ccpoly.c
@@ -1,0 +1,90 @@
+#include "os.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,222 @@
+/*
+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 "os.h"
+#include <libsec.h>
+
+/* from chachablock.$O */
+extern void _chachablock(u32int x[16], int rounds);
+
+/* little-endian data order */
+#define	GET4(p)		((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define	PUT4(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+
+#define ENCRYPT(s, x, y, d) {\
+	u32int v; \
+	v = GET4(s); \
+	v ^= (x)+(y); \
+	PUT4(d, 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 != 64/8 && ivlen != 96/8
+	&& ivlen != 128/8 && ivlen != 192/8)	/* hchacha, xchacha */
+		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->xkey[0] = s->input[4];
+	s->xkey[1] = s->input[5];
+	s->xkey[2] = s->input[6];
+	s->xkey[3] = s->input[7];
+	s->xkey[4] = s->input[8];
+	s->xkey[5] = s->input[9];
+	s->xkey[6] = s->input[10];
+	s->xkey[7] = s->input[11];
+
+	s->ivwords = ivlen/4;
+	s->input[12] = 0;
+	s->input[13] = 0;
+	if(iv == nil){
+		s->input[14] = 0;
+		s->input[15] = 0;
+	}else
+		chacha_setiv(s, iv);
+}
+
+static void
+hchachablock(uchar h[32], Chachastate *s)
+{
+	u32int x[16];
+
+	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];
+
+	_chachablock(x, s->rounds);
+
+	PUT4(h+0*4, x[0]);
+	PUT4(h+1*4, x[1]);
+	PUT4(h+2*4, x[2]);
+	PUT4(h+3*4, x[3]);
+	PUT4(h+4*4, x[12]);
+	PUT4(h+5*4, x[13]);
+	PUT4(h+6*4, x[14]);
+	PUT4(h+7*4, x[15]);
+}
+
+void
+chacha_setiv(Chachastate *s, uchar *iv)
+{
+	if(s->ivwords == 192/32){
+		/* xchacha with 192-bit iv */
+		u32int counter[2];
+		uchar h[32];
+
+		s->input[4] = s->xkey[0];
+		s->input[5] = s->xkey[1];
+		s->input[6] = s->xkey[2];
+		s->input[7] = s->xkey[3];
+		s->input[8] = s->xkey[4];
+		s->input[9] = s->xkey[5];
+		s->input[10] = s->xkey[6];
+		s->input[11] = s->xkey[7];
+
+		counter[0] = s->input[12];
+		counter[1] = s->input[13];
+
+		load(&s->input[12], iv, 4);
+
+		hchachablock(h, s);
+		load(&s->input[4], h, 8);
+		memset(h, 0, 32);
+
+		s->input[12] = counter[0];
+		s->input[13] = counter[1];
+
+		load(&s->input[14], iv+16, 2);
+		return;
+	}
+	load(&s->input[16 - s->ivwords], iv, s->ivwords);
+}
+
+void
+chacha_setblock(Chachastate *s, u64int blockno)
+{
+	s->input[12] = blockno;
+	if(s->ivwords != 3)
+		s->input[13] = blockno>>32;
+}
+
+static void
+encryptblock(Chachastate *s, uchar *src, uchar *dst)
+{
+	u32int x[16];
+	int i;
+
+	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];
+	_chachablock(x, s->rounds);
+
+	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;
+	}
+
+	if(++s->input[12] == 0 && s->ivwords != 3)
+		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);
+}
+
+void
+hchacha(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds)
+{
+	Chachastate s[1];
+
+	setupChachastate(s, key, keylen, nonce, 16, rounds);
+	hchachablock(h, s);
+	memset(s, 0, sizeof(s));
+}
--- /dev/null
+++ b/libsec/chachablock.c
@@ -1,0 +1,29 @@
+#include "os.h"
+
+#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; \
+}
+
+void
+_chachablock(u32int x[16], int rounds)
+{
+	for(; rounds > 0; rounds -= 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)
+	}
+}
--- /dev/null
+++ b/libsec/chachatest.c
@@ -1,0 +1,176 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+static void
+printblock(uchar *b, usize n)
+{
+	int i;
+
+	for(i=0; i+8<=n; i+=8){
+		print("%#.2ux %#.2ux %#.2ux %#.2ux %#.2ux %#.2ux %#.2ux %#.2ux\n",
+			b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+		b += 8;
+	}
+	if(i < n){
+		print("%#.2ux", *b++);
+		while(++i < n)
+			print(" %#.2ux", *b++);
+		print("\n");
+	}
+}
+
+/* test vector from RFC7539 */
+uchar	rfckey[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
+uchar	rfcnonce[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00};
+u32int	rfccount = 1;
+char	rfctext[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, "
+	"sunscreen would be it.";
+uchar	rfcout[3*ChachaBsize];
+uchar	rfcref[] = {
+	0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
+	0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
+	0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
+	0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
+	0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
+	0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
+	0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
+	0x87, 0x4d
+};
+
+uchar	ccpaad[] = {
+	0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+};
+uchar	ccpkey[] = {
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+};
+uchar	ccpiv[] = {
+	0x07, 0x00, 0x00, 0x00,  
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+};
+uchar	ccptag[] = {
+	0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91,
+};
+
+uchar	ccp64aad[] = {
+	0x87, 0xe2, 0x29, 0xd4, 0x50, 0x08, 0x45, 0xa0, 0x79, 0xc0,
+};
+uchar	ccp64key[] = {
+	0x42, 0x90, 0xbc, 0xb1, 0x54, 0x17, 0x35, 0x31, 0xf3, 0x14, 0xaf, 0x57, 0xf3, 0xbe, 0x3b, 0x50,
+	0x06, 0xda, 0x37, 0x1e, 0xce, 0x27, 0x2a, 0xfa, 0x1b, 0x5d, 0xbd, 0xd1, 0x10, 0x0a, 0x10, 0x07,
+};
+uchar	ccp64iv[] = {
+	0xcd, 0x7c, 0xf6, 0x7b, 0xe3, 0x9c, 0x79, 0x4a,
+};
+uchar	ccp64inp[] = {
+	0x86, 0xd0, 0x99, 0x74, 0x84, 0x0b, 0xde, 0xd2, 0xa5, 0xca, 
+};
+uchar	ccp64out[] = {
+	0xe3, 0xe4, 0x46, 0xf7, 0xed, 0xe9, 0xa1, 0x9b, 0x62, 0xa4,
+};
+uchar	ccp64tag[] = {
+	0x67, 0x7d, 0xab, 0xf4, 0xe3, 0xd2, 0x4b, 0x87, 0x6b, 0xb2, 0x84, 0x75, 0x38, 0x96, 0xe1, 0xd6,
+};
+
+void
+main(int argc, char **argv)
+{
+	Chachastate s;
+	uchar tag[16];
+	int n;
+
+	ARGBEGIN{
+	}ARGEND
+	print("rfc7539:\n");
+	print("key:\n");
+	printblock(rfckey, sizeof(rfckey));
+	n = strlen(rfctext);
+	setupChachastate(&s, rfckey, sizeof(rfckey), rfcnonce, sizeof(rfcnonce), 0);
+	chacha_setblock(&s, rfccount);
+	print("rfc in:\n");
+	printblock((uchar*)rfctext, n);
+	chacha_encrypt2((uchar*)rfctext, rfcout, n, &s);
+	print("rfc out:\n");
+	printblock(rfcout, n);
+	if(memcmp(rfcout, rfcref, sizeof(rfcref)) != 0){
+		print("failure of vision\n");
+		exits("wrong");
+	}
+	print("\n");
+
+	print("ccpoly key:\n");
+	printblock(ccpkey, sizeof(ccpkey));
+
+	print("ccpoly iv:\n");
+	printblock(ccpiv, sizeof(ccpiv));
+
+	setupChachastate(&s, ccpkey, sizeof(ccpkey), ccpiv, sizeof(ccpiv), 20);
+
+	memmove(rfcout, rfctext, sizeof(rfctext)-1);
+	ccpoly_encrypt(rfcout, sizeof(rfctext)-1, ccpaad, sizeof(ccpaad), tag, &s);
+
+	print("ccpoly cipher:\n");
+	printblock(rfcout, sizeof(rfctext)-1);
+
+	print("ccpoly tag:\n");
+	printblock(tag, sizeof(tag));
+
+	if(memcmp(tag, ccptag, sizeof(tag)) != 0){
+		print("bad ccpoly tag\n");
+		exits("wrong");
+	}
+
+	if(ccpoly_decrypt(rfcout, sizeof(rfctext)-1, ccpaad, sizeof(ccpaad), tag, &s) != 0){
+		print("ccpoly decryption failed\n");
+		exits("wrong");
+	}
+
+	if(memcmp(rfcout, rfctext, sizeof(rfctext)-1) != 0){
+		print("ccpoly bad decryption\n");
+		exits("wrong");
+	}
+	print("\n");
+
+	print("ccpoly64 key:\n");
+	printblock(ccp64key, sizeof(ccp64key));
+
+	print("ccpoly64 iv:\n");
+	printblock(ccp64iv, sizeof(ccp64iv));
+
+	setupChachastate(&s, ccp64key, sizeof(ccp64key), ccp64iv, sizeof(ccp64iv), 20);
+
+	memmove(rfcout, ccp64inp, sizeof(ccp64inp));
+	ccpoly_encrypt(rfcout, sizeof(ccp64inp), ccp64aad, sizeof(ccp64aad), tag, &s);
+
+	print("ccpoly64 cipher:\n");
+	printblock(rfcout, sizeof(ccp64inp));
+
+	print("ccpoly64 tag:\n");
+	printblock(tag, sizeof(tag));
+
+	if(memcmp(rfcout, ccp64out, sizeof(ccp64out)) != 0){
+		print("ccpoly64 bad ciphertext\n");
+		exits("wrong");
+	}
+	if(memcmp(tag, ccp64tag, sizeof(ccp64tag)) != 0){
+		print("ccpoly64 bad encryption tag\n");
+		exits("wrong");
+	}
+
+	if(ccpoly_decrypt(rfcout, sizeof(ccp64inp), ccp64aad, sizeof(ccp64aad), tag, &s) != 0){
+		print("ccpoly64 decryption failed\n");
+		exits("wrong");
+	}
+	if(memcmp(rfcout, ccp64inp, sizeof(ccp64inp)) != 0){
+		print("ccpoly64 bad decryption\n");
+		exits("wrong");
+	}
+
+	print("passed\n");
+	exits(nil);
+}
--- /dev/null
+++ b/libsec/curve25519.c
@@ -1,0 +1,570 @@
+/* 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 <agl@imperialviolet.org>
+ *
+ * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+ *
+ * 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 "os.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);
+}
--- /dev/null
+++ b/libsec/decodepem.c
@@ -1,0 +1,89 @@
+#include "os.h"
+#include <libsec.h>
+
+#define STRLEN(s)	(sizeof(s)-1)
+
+uchar*
+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
+	 */
+	n = strlen(type);
+	e = strchr(s, '\0');
+	for(t = s; t != nil && t < e; ){
+		tt = t;
+		t = strchr(tt, '\n');
+		if(t != nil)
+			t++;
+		if(strncmp(tt, "-----BEGIN ", STRLEN("-----BEGIN ")) == 0
+		&& strncmp(&tt[STRLEN("-----BEGIN ")], type, n) == 0
+		&& strncmp(&tt[STRLEN("-----BEGIN ")+n], "-----", STRLEN("-----")) == 0
+		&& strchr("\r\n", tt[STRLEN("-----BEGIN ")+n+STRLEN("-----")]) != nil)
+			break;
+	}
+	for(tt = t; tt != nil && tt < e; tt++){
+		if(strncmp(tt, "-----END ", STRLEN("-----END ")) == 0
+		&& strncmp(&tt[STRLEN("-----END ")], type, n) == 0
+		&& strncmp(&tt[STRLEN("-----END ")+n], "-----", STRLEN("-----")) == 0
+		&& strchr("\r\n", tt[STRLEN("-----END ")+n+STRLEN("-----")]) != nil)
+			break;
+		tt = strchr(tt, '\n');
+		if(tt == nil)
+			break;
+	}
+	if(tt == nil || tt == e){
+		werrstr("incorrect .pem file format: bad header or trailer");
+		return nil;
+	}
+
+	if(new_s)
+		*new_s = tt+1;
+	n = ((tt - t) * 6 + 7) / 8;
+	d = malloc(n);
+	if(d == nil){
+		werrstr("out of memory");
+		return nil;
+	}
+	n = dec64(d, n, t, tt - t);
+	if(n < 0){
+		free(d);
+		werrstr("incorrect .pem file format: bad base64 encoded data");
+		return nil;
+	}
+	*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/des.c
@@ -1,0 +1,480 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * integrated sbox & p perm
+ */
+static u32int spbox[] = {
+
+0x00808200,0x00000000,0x00008000,0x00808202,0x00808002,0x00008202,0x00000002,0x00008000,
+0x00000200,0x00808200,0x00808202,0x00000200,0x00800202,0x00808002,0x00800000,0x00000002,
+0x00000202,0x00800200,0x00800200,0x00008200,0x00008200,0x00808000,0x00808000,0x00800202,
+0x00008002,0x00800002,0x00800002,0x00008002,0x00000000,0x00000202,0x00008202,0x00800000,
+0x00008000,0x00808202,0x00000002,0x00808000,0x00808200,0x00800000,0x00800000,0x00000200,
+0x00808002,0x00008000,0x00008200,0x00800002,0x00000200,0x00000002,0x00800202,0x00008202,
+0x00808202,0x00008002,0x00808000,0x00800202,0x00800002,0x00000202,0x00008202,0x00808200,
+0x00000202,0x00800200,0x00800200,0x00000000,0x00008002,0x00008200,0x00000000,0x00808002,
+
+0x40084010,0x40004000,0x00004000,0x00084010,0x00080000,0x00000010,0x40080010,0x40004010,
+0x40000010,0x40084010,0x40084000,0x40000000,0x40004000,0x00080000,0x00000010,0x40080010,
+0x00084000,0x00080010,0x40004010,0x00000000,0x40000000,0x00004000,0x00084010,0x40080000,
+0x00080010,0x40000010,0x00000000,0x00084000,0x00004010,0x40084000,0x40080000,0x00004010,
+0x00000000,0x00084010,0x40080010,0x00080000,0x40004010,0x40080000,0x40084000,0x00004000,
+0x40080000,0x40004000,0x00000010,0x40084010,0x00084010,0x00000010,0x00004000,0x40000000,
+0x00004010,0x40084000,0x00080000,0x40000010,0x00080010,0x40004010,0x40000010,0x00080010,
+0x00084000,0x00000000,0x40004000,0x00004010,0x40000000,0x40080010,0x40084010,0x00084000,
+
+0x00000104,0x04010100,0x00000000,0x04010004,0x04000100,0x00000000,0x00010104,0x04000100,
+0x00010004,0x04000004,0x04000004,0x00010000,0x04010104,0x00010004,0x04010000,0x00000104,
+0x04000000,0x00000004,0x04010100,0x00000100,0x00010100,0x04010000,0x04010004,0x00010104,
+0x04000104,0x00010100,0x00010000,0x04000104,0x00000004,0x04010104,0x00000100,0x04000000,
+0x04010100,0x04000000,0x00010004,0x00000104,0x00010000,0x04010100,0x04000100,0x00000000,
+0x00000100,0x00010004,0x04010104,0x04000100,0x04000004,0x00000100,0x00000000,0x04010004,
+0x04000104,0x00010000,0x04000000,0x04010104,0x00000004,0x00010104,0x00010100,0x04000004,
+0x04010000,0x04000104,0x00000104,0x04010000,0x00010104,0x00000004,0x04010004,0x00010100,
+
+0x80401000,0x80001040,0x80001040,0x00000040,0x00401040,0x80400040,0x80400000,0x80001000,
+0x00000000,0x00401000,0x00401000,0x80401040,0x80000040,0x00000000,0x00400040,0x80400000,
+0x80000000,0x00001000,0x00400000,0x80401000,0x00000040,0x00400000,0x80001000,0x00001040,
+0x80400040,0x80000000,0x00001040,0x00400040,0x00001000,0x00401040,0x80401040,0x80000040,
+0x00400040,0x80400000,0x00401000,0x80401040,0x80000040,0x00000000,0x00000000,0x00401000,
+0x00001040,0x00400040,0x80400040,0x80000000,0x80401000,0x80001040,0x80001040,0x00000040,
+0x80401040,0x80000040,0x80000000,0x00001000,0x80400000,0x80001000,0x00401040,0x80400040,
+0x80001000,0x00001040,0x00400000,0x80401000,0x00000040,0x00400000,0x00001000,0x00401040,
+
+0x00000080,0x01040080,0x01040000,0x21000080,0x00040000,0x00000080,0x20000000,0x01040000,
+0x20040080,0x00040000,0x01000080,0x20040080,0x21000080,0x21040000,0x00040080,0x20000000,
+0x01000000,0x20040000,0x20040000,0x00000000,0x20000080,0x21040080,0x21040080,0x01000080,
+0x21040000,0x20000080,0x00000000,0x21000000,0x01040080,0x01000000,0x21000000,0x00040080,
+0x00040000,0x21000080,0x00000080,0x01000000,0x20000000,0x01040000,0x21000080,0x20040080,
+0x01000080,0x20000000,0x21040000,0x01040080,0x20040080,0x00000080,0x01000000,0x21040000,
+0x21040080,0x00040080,0x21000000,0x21040080,0x01040000,0x00000000,0x20040000,0x21000000,
+0x00040080,0x01000080,0x20000080,0x00040000,0x00000000,0x20040000,0x01040080,0x20000080,
+
+0x10000008,0x10200000,0x00002000,0x10202008,0x10200000,0x00000008,0x10202008,0x00200000,
+0x10002000,0x00202008,0x00200000,0x10000008,0x00200008,0x10002000,0x10000000,0x00002008,
+0x00000000,0x00200008,0x10002008,0x00002000,0x00202000,0x10002008,0x00000008,0x10200008,
+0x10200008,0x00000000,0x00202008,0x10202000,0x00002008,0x00202000,0x10202000,0x10000000,
+0x10002000,0x00000008,0x10200008,0x00202000,0x10202008,0x00200000,0x00002008,0x10000008,
+0x00200000,0x10002000,0x10000000,0x00002008,0x10000008,0x10202008,0x00202000,0x10200000,
+0x00202008,0x10202000,0x00000000,0x10200008,0x00000008,0x00002000,0x10200000,0x00202008,
+0x00002000,0x00200008,0x10002008,0x00000000,0x10202000,0x10000000,0x00200008,0x10002008,
+
+0x00100000,0x02100001,0x02000401,0x00000000,0x00000400,0x02000401,0x00100401,0x02100400,
+0x02100401,0x00100000,0x00000000,0x02000001,0x00000001,0x02000000,0x02100001,0x00000401,
+0x02000400,0x00100401,0x00100001,0x02000400,0x02000001,0x02100000,0x02100400,0x00100001,
+0x02100000,0x00000400,0x00000401,0x02100401,0x00100400,0x00000001,0x02000000,0x00100400,
+0x02000000,0x00100400,0x00100000,0x02000401,0x02000401,0x02100001,0x02100001,0x00000001,
+0x00100001,0x02000000,0x02000400,0x00100000,0x02100400,0x00000401,0x00100401,0x02100400,
+0x00000401,0x02000001,0x02100401,0x02100000,0x00100400,0x00000000,0x00000001,0x02100401,
+0x00000000,0x00100401,0x02100000,0x00000400,0x02000001,0x02000400,0x00000400,0x00100001,
+
+0x08000820,0x00000800,0x00020000,0x08020820,0x08000000,0x08000820,0x00000020,0x08000000,
+0x00020020,0x08020000,0x08020820,0x00020800,0x08020800,0x00020820,0x00000800,0x00000020,
+0x08020000,0x08000020,0x08000800,0x00000820,0x00020800,0x00020020,0x08020020,0x08020800,
+0x00000820,0x00000000,0x00000000,0x08020020,0x08000020,0x08000800,0x00020820,0x00020000,
+0x00020820,0x00020000,0x08020800,0x00000800,0x00000020,0x08020020,0x00000800,0x00020820,
+0x08000800,0x00000020,0x08000020,0x08020000,0x08020020,0x08000000,0x00020000,0x08000820,
+0x00000000,0x08020820,0x00020020,0x08000020,0x08020000,0x08000800,0x08000820,0x00000000,
+0x08020820,0x00020800,0x00020800,0x00000820,0x00000820,0x00020020,0x08000000,0x08020800,
+};
+
+/*
+ * for manual index calculation
+ * #define fetch(box, i, sh) (*((u32int*)((uchar*)spbox + (box << 8) + ((i >> (sh)) & 0xfc))))
+ */
+#define fetch(box, i, sh) ((spbox+(box << 6))[((i >> (sh + 2)) & 0x3f)])
+
+/*
+ * DES electronic codebook encryption of one block
+ */
+void
+block_cipher(ulong key[32], uchar text[8], int decrypting)
+{
+	u32int right, left, v0, v1;
+	int i, keystep;
+
+	/*
+	 * initial permutation
+	 */
+	v0 = text[0] | ((u32int)text[2]<<8) | ((u32int)text[4]<<16) | ((u32int)text[6]<<24);
+	left = text[1] | ((u32int)text[3]<<8) | ((u32int)text[5]<<16) | ((u32int)text[7]<<24);
+	right = (left & 0xaaaaaaaa) | ((v0 >> 1) & 0x55555555);
+	left = ((left << 1) & 0xaaaaaaaa) | (v0 & 0x55555555);
+	left = ((left << 6) & 0x33003300)
+		| (left & 0xcc33cc33)
+		| ((left >> 6) & 0x00cc00cc);
+	left = ((left << 12) & 0x0f0f0000)
+		| (left & 0xf0f00f0f)
+		| ((left >> 12) & 0x0000f0f0);
+	right = ((right << 6) & 0x33003300)
+		| (right & 0xcc33cc33)
+		| ((right >> 6) & 0x00cc00cc);
+	right = ((right << 12) & 0x0f0f0000)
+		| (right & 0xf0f00f0f)
+		| ((right >> 12) & 0x0000f0f0);
+
+	if (decrypting) {
+		keystep = -2;
+		key = key + 32 - 2;
+	} else
+		keystep = 2;
+	for (i = 0; i < 8; i++) {
+		v0 = key[0];
+		v0 ^= (right >> 1) | (right << 31);
+		left ^= fetch(0, v0, 24)
+			^ fetch(2, v0, 16)
+			^ fetch(4, v0, 8)
+			^ fetch(6, v0, 0);
+		v1 = key[1];
+		v1 ^= (right << 3) | (right >> 29);
+		left ^= fetch(1, v1, 24)
+			^ fetch(3, v1, 16)
+			^ fetch(5, v1, 8)
+			^ fetch(7, v1, 0);
+		key += keystep;
+		
+		v0 = key[0];
+		v0 ^= (left >> 1) | (left << 31);
+		right ^= fetch(0, v0, 24)
+			^ fetch(2, v0, 16)
+			^ fetch(4, v0, 8)
+			^ fetch(6, v0, 0);
+		v1 = key[1];
+		v1 ^= (left << 3) | (left >> 29);
+		right ^= fetch(1, v1, 24)
+			^ fetch(3, v1, 16)
+			^ fetch(5, v1, 8)
+			^ fetch(7, v1, 0);
+		key += keystep;
+	}
+
+	/*
+	 * final permutation, inverse initial permutation
+	 */
+	v0 = ((left << 1) & 0xaaaaaaaa) | (right & 0x55555555);
+	v1 = (left & 0xaaaaaaaa) | ((right >> 1) & 0x55555555);
+	v1 = ((v1 << 6) & 0x33003300)
+		| (v1 & 0xcc33cc33)
+		| ((v1 >> 6) & 0x00cc00cc);
+	v1 = ((v1 << 12) & 0x0f0f0000)
+		| (v1 & 0xf0f00f0f)
+		| ((v1 >> 12) & 0x0000f0f0);
+	v0 = ((v0 << 6) & 0x33003300)
+		| (v0 & 0xcc33cc33)
+		| ((v0 >> 6) & 0x00cc00cc);
+	v0 = ((v0 << 12) & 0x0f0f0000)
+		| (v0 & 0xf0f00f0f)
+		| ((v0 >> 12) & 0x0000f0f0);
+	text[0] = v0;
+	text[2] = v0 >> 8;
+	text[4] = v0 >> 16;
+	text[6] = v0 >> 24;
+	text[1] = v1;
+	text[3] = v1 >> 8;
+	text[5] = v1 >> 16;
+	text[7] = v1 >> 24;
+}
+
+/*
+ * triple DES electronic codebook encryption of one block
+ */
+void
+triple_block_cipher(ulong expanded_key[3][32], uchar text[8], int ende)
+{
+	ulong *key;
+	u32int right, left, v0, v1;
+	int i, j, keystep;
+
+	/*
+	 * initial permutation
+	 */
+	v0 = text[0] | ((u32int)text[2]<<8) | ((u32int)text[4]<<16) | ((u32int)text[6]<<24);
+	left = text[1] | ((u32int)text[3]<<8) | ((u32int)text[5]<<16) | ((u32int)text[7]<<24);
+	right = (left & 0xaaaaaaaa) | ((v0 >> 1) & 0x55555555);
+	left = ((left << 1) & 0xaaaaaaaa) | (v0 & 0x55555555);
+	left = ((left << 6) & 0x33003300)
+		| (left & 0xcc33cc33)
+		| ((left >> 6) & 0x00cc00cc);
+	left = ((left << 12) & 0x0f0f0000)
+		| (left & 0xf0f00f0f)
+		| ((left >> 12) & 0x0000f0f0);
+	right = ((right << 6) & 0x33003300)
+		| (right & 0xcc33cc33)
+		| ((right >> 6) & 0x00cc00cc);
+	right = ((right << 12) & 0x0f0f0000)
+		| (right & 0xf0f00f0f)
+		| ((right >> 12) & 0x0000f0f0);
+
+	for(j = 0; j < 3; j++){
+		if((ende & 1) == DES3D) {
+			key = &expanded_key[2-j][32-2];
+			keystep = -2;
+		} else {
+			key = &expanded_key[j][0];
+			keystep = 2;
+		}
+		ende >>= 1;
+		for (i = 0; i < 8; i++) {
+			v0 = key[0];
+			v0 ^= (right >> 1) | (right << 31);
+			left ^= fetch(0, v0, 24)
+				^ fetch(2, v0, 16)
+				^ fetch(4, v0, 8)
+				^ fetch(6, v0, 0);
+			v1 = key[1];
+			v1 ^= (right << 3) | (right >> 29);
+			left ^= fetch(1, v1, 24)
+				^ fetch(3, v1, 16)
+				^ fetch(5, v1, 8)
+				^ fetch(7, v1, 0);
+			key += keystep;
+			
+			v0 = key[0];
+			v0 ^= (left >> 1) | (left << 31);
+			right ^= fetch(0, v0, 24)
+				^ fetch(2, v0, 16)
+				^ fetch(4, v0, 8)
+				^ fetch(6, v0, 0);
+			v1 = key[1];
+			v1 ^= (left << 3) | (left >> 29);
+			right ^= fetch(1, v1, 24)
+				^ fetch(3, v1, 16)
+				^ fetch(5, v1, 8)
+				^ fetch(7, v1, 0);
+			key += keystep;
+		}
+
+		v0 = left;
+		left = right;
+		right = v0;
+	}
+
+	/*
+	 * final permutation, inverse initial permutation
+	 * left and right are swapped here
+	 */
+	v0 = ((right << 1) & 0xaaaaaaaa) | (left & 0x55555555);
+	v1 = (right & 0xaaaaaaaa) | ((left >> 1) & 0x55555555);
+	v1 = ((v1 << 6) & 0x33003300)
+		| (v1 & 0xcc33cc33)
+		| ((v1 >> 6) & 0x00cc00cc);
+	v1 = ((v1 << 12) & 0x0f0f0000)
+		| (v1 & 0xf0f00f0f)
+		| ((v1 >> 12) & 0x0000f0f0);
+	v0 = ((v0 << 6) & 0x33003300)
+		| (v0 & 0xcc33cc33)
+		| ((v0 >> 6) & 0x00cc00cc);
+	v0 = ((v0 << 12) & 0x0f0f0000)
+		| (v0 & 0xf0f00f0f)
+		| ((v0 >> 12) & 0x0000f0f0);
+	text[0] = v0;
+	text[2] = v0 >> 8;
+	text[4] = v0 >> 16;
+	text[6] = v0 >> 24;
+	text[1] = v1;
+	text[3] = v1 >> 8;
+	text[5] = v1 >> 16;
+	text[7] = v1 >> 24;
+}
+
+/*
+ * key compression permutation, 4 bits at a time
+ */
+static u32int comptab[] = {
+
+0x000000,0x010000,0x000008,0x010008,0x000080,0x010080,0x000088,0x010088,
+0x000000,0x010000,0x000008,0x010008,0x000080,0x010080,0x000088,0x010088,
+
+0x000000,0x100000,0x000800,0x100800,0x000000,0x100000,0x000800,0x100800,
+0x002000,0x102000,0x002800,0x102800,0x002000,0x102000,0x002800,0x102800,
+
+0x000000,0x000004,0x000400,0x000404,0x000000,0x000004,0x000400,0x000404,
+0x400000,0x400004,0x400400,0x400404,0x400000,0x400004,0x400400,0x400404,
+
+0x000000,0x000020,0x008000,0x008020,0x800000,0x800020,0x808000,0x808020,
+0x000002,0x000022,0x008002,0x008022,0x800002,0x800022,0x808002,0x808022,
+
+0x000000,0x000200,0x200000,0x200200,0x001000,0x001200,0x201000,0x201200,
+0x000000,0x000200,0x200000,0x200200,0x001000,0x001200,0x201000,0x201200,
+
+0x000000,0x000040,0x000010,0x000050,0x004000,0x004040,0x004010,0x004050,
+0x040000,0x040040,0x040010,0x040050,0x044000,0x044040,0x044010,0x044050,
+
+0x000000,0x000100,0x020000,0x020100,0x000001,0x000101,0x020001,0x020101,
+0x080000,0x080100,0x0a0000,0x0a0100,0x080001,0x080101,0x0a0001,0x0a0101,
+
+0x000000,0x000100,0x040000,0x040100,0x000000,0x000100,0x040000,0x040100,
+0x000040,0x000140,0x040040,0x040140,0x000040,0x000140,0x040040,0x040140,
+
+0x000000,0x400000,0x008000,0x408000,0x000008,0x400008,0x008008,0x408008,
+0x000400,0x400400,0x008400,0x408400,0x000408,0x400408,0x008408,0x408408,
+
+0x000000,0x001000,0x080000,0x081000,0x000020,0x001020,0x080020,0x081020,
+0x004000,0x005000,0x084000,0x085000,0x004020,0x005020,0x084020,0x085020,
+
+0x000000,0x000800,0x000000,0x000800,0x000010,0x000810,0x000010,0x000810,
+0x800000,0x800800,0x800000,0x800800,0x800010,0x800810,0x800010,0x800810,
+
+0x000000,0x010000,0x000200,0x010200,0x000000,0x010000,0x000200,0x010200,
+0x100000,0x110000,0x100200,0x110200,0x100000,0x110000,0x100200,0x110200,
+
+0x000000,0x000004,0x000000,0x000004,0x000080,0x000084,0x000080,0x000084,
+0x002000,0x002004,0x002000,0x002004,0x002080,0x002084,0x002080,0x002084,
+
+0x000000,0x000001,0x200000,0x200001,0x020000,0x020001,0x220000,0x220001,
+0x000002,0x000003,0x200002,0x200003,0x020002,0x020003,0x220002,0x220003,
+};
+
+static int keysh[] =
+{
+	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
+};
+
+static void
+keycompperm(u32int left, u32int right, ulong *ek)
+{
+	u32int v0, v1;
+	int i;
+
+	for(i = 0; i < 16; i++){
+		left = (left << keysh[i]) | (left >> (28 - keysh[i]));
+		left &= 0xfffffff0;
+		right = (right << keysh[i]) | (right >> (28 - keysh[i]));
+		right &= 0xfffffff0;
+		v0 = comptab[6 * (1 << 4) + ((left >> (32-4)) & 0xf)]
+			| comptab[5 * (1 << 4) + ((left >> (32-8)) & 0xf)]
+			| comptab[4 * (1 << 4) + ((left >> (32-12)) & 0xf)]
+			| comptab[3 * (1 << 4) + ((left >> (32-16)) & 0xf)]
+			| comptab[2 * (1 << 4) + ((left >> (32-20)) & 0xf)]
+			| comptab[1 * (1 << 4) + ((left >> (32-24)) & 0xf)]
+			| comptab[0 * (1 << 4) + ((left >> (32-28)) & 0xf)];
+		v1 = comptab[13 * (1 << 4) + ((right >> (32-4)) & 0xf)]
+			| comptab[12 * (1 << 4) + ((right >> (32-8)) & 0xf)]
+			| comptab[11 * (1 << 4) + ((right >> (32-12)) & 0xf)]
+			| comptab[10 * (1 << 4) + ((right >> (32-16)) & 0xf)]
+			| comptab[9 * (1 << 4) + ((right >> (32-20)) & 0xf)]
+			| comptab[8 * (1 << 4) + ((right >> (32-24)) & 0xf)]
+			| comptab[7 * (1 << 4) + ((right >> (32-28)) & 0xf)];
+		ek[0] = (((v0 >> (24-6)) & 0x3f) << 26)
+			| (((v0 >> (24-18)) & 0x3f) << 18)
+			| (((v1 >> (24-6)) & 0x3f) << 10)
+			| (((v1 >> (24-18)) & 0x3f) << 2);
+		ek[1] = (((v0 >> (24-12)) & 0x3f) << 26)
+			| (((v0 >> (24-24)) & 0x3f) << 18)
+			| (((v1 >> (24-12)) & 0x3f) << 10)
+			| (((v1 >> (24-24)) & 0x3f) << 2);
+		ek += 2;
+	}
+}
+
+void
+des_key_setup(uchar key[8], ulong *ek)
+{
+	u32int left, right, v0, v1;
+
+	v0 = key[0] | ((u32int)key[2] << 8) | ((u32int)key[4] << 16) | ((u32int)key[6] << 24);
+	v1 = key[1] | ((u32int)key[3] << 8) | ((u32int)key[5] << 16) | ((u32int)key[7] << 24);
+	left = ((v0 >> 1) & 0x40404040)
+		| ((v0 >> 2) & 0x10101010)
+		| ((v0 >> 3) & 0x04040404)
+		| ((v0 >> 4) & 0x01010101)
+		| ((v1 >> 0) & 0x80808080)
+		| ((v1 >> 1) & 0x20202020)
+		| ((v1 >> 2) & 0x08080808)
+		| ((v1 >> 3) & 0x02020202);
+	right = ((v0 >> 1) & 0x04040404)
+		| ((v0 << 2) & 0x10101010)
+		| ((v0 << 5) & 0x40404040)
+		| ((v1 << 0) & 0x08080808)
+		| ((v1 << 3) & 0x20202020)
+		| ((v1 << 6) & 0x80808080);
+	left = ((left << 6) & 0x33003300)
+		| (left & 0xcc33cc33)
+		| ((left >> 6) & 0x00cc00cc);
+	v0 = ((left << 12) & 0x0f0f0000)
+		| (left & 0xf0f00f0f)
+		| ((left >> 12) & 0x0000f0f0);
+	right = ((right << 6) & 0x33003300)
+		| (right & 0xcc33cc33)
+		| ((right >> 6) & 0x00cc00cc);
+	v1 = ((right << 12) & 0x0f0f0000)
+		| (right & 0xf0f00f0f)
+		| ((right >> 12) & 0x0000f0f0);
+	left = v0 & 0xfffffff0;
+	right = (v1 & 0xffffff00) | ((v0 << 4) & 0xf0);
+
+	keycompperm(left, right, ek);
+}
+
+static uchar parity[128] =
+{
+	0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e, 
+	0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f, 
+	0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f, 
+	0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e, 
+	0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f, 
+	0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e, 
+	0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e, 
+	0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f, 
+	0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f, 
+	0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e, 
+	0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae, 
+	0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf, 
+	0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce, 
+	0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf, 
+	0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef, 
+	0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe,
+};
+
+/*
+ *  convert a 7 byte key to an 8 byte one
+ */
+void
+des56to64(uchar *k56, uchar *k64)
+{
+	u32int hi, lo;
+
+	hi = ((u32int)k56[0]<<24)|((u32int)k56[1]<<16)|((u32int)k56[2]<<8)|k56[3];
+	lo = ((u32int)k56[4]<<24)|((u32int)k56[5]<<16)|((u32int)k56[6]<<8);
+
+	k64[0] = parity[(hi>>25)&0x7f];
+	k64[1] = parity[(hi>>18)&0x7f];
+	k64[2] = parity[(hi>>11)&0x7f];
+	k64[3] = parity[(hi>>4)&0x7f];
+	k64[4] = parity[((hi<<3)|(lo>>29))&0x7f];
+	k64[5] = parity[(lo>>22)&0x7f];
+	k64[6] = parity[(lo>>15)&0x7f];
+	k64[7] = parity[(lo>>8)&0x7f];
+}
+
+/*
+ *  convert an 8 byte key to a 7 byte one
+ */
+void
+des64to56(uchar *k64, uchar *k56)
+{
+	u32int hi, lo;
+
+	hi = (((u32int)k64[0]&0xfe)<<24)|(((u32int)k64[1]&0xfe)<<17)|(((u32int)k64[2]&0xfe)<<10)
+		|((k64[3]&0xfe)<<3)|(k64[4]>>4);
+	lo = (((u32int)k64[4]&0xfe)<<28)|(((u32int)k64[5]&0xfe)<<21)|(((u32int)k64[6]&0xfe)<<14)
+		|(((u32int)k64[7]&0xfe)<<7);
+
+	k56[0] = hi>>24;
+	k56[1] = hi>>16;
+	k56[2] = hi>>8;
+	k56[3] = hi>>0;
+	k56[4] = lo>>24;
+	k56[5] = lo>>16;
+	k56[6] = lo>>8;
+}
+
+void
+key_setup(uchar key[7], ulong *ek)
+{
+	uchar k64[8];
+
+	des56to64(key, k64);
+	des_key_setup(k64, ek);	
+}
--- /dev/null
+++ b/libsec/des3CBC.c
@@ -1,0 +1,58 @@
+#include "os.h"
+#include <libsec.h>
+
+// Because of the way that non multiple of 8
+// buffers are handled, the decryptor must
+// be fed buffers of the same size as the
+// encryptor
+
+
+// If the length is not a multiple of 8, I encrypt
+// the overflow to be compatible with lacy's cryptlib
+void
+des3CBCencrypt(uchar *p, int len, DES3state *s)
+{
+	uchar *p2, *ip, *eip;
+
+	for(; len >= 8; len -= 8){
+		p2 = p;
+		ip = s->ivec;
+		for(eip = ip+8; ip < eip; )
+			*p2++ ^= *ip++;
+		triple_block_cipher(s->expanded, p, DES3EDE);
+		memmove(s->ivec, p, 8);
+		p += 8;
+	}
+
+	if(len > 0){
+		ip = s->ivec;
+		triple_block_cipher(s->expanded, ip, DES3EDE);
+		for(eip = ip+len; ip < eip; )
+			*p++ ^= *ip++;
+	}
+}
+
+void
+des3CBCdecrypt(uchar *p, int len, DES3state *s)
+{
+	uchar *ip, *eip, *tp;
+	uchar tmp[8];
+
+	for(; len >= 8; len -= 8){
+		memmove(tmp, p, 8);
+		triple_block_cipher(s->expanded, p, DES3DED);
+		tp = tmp;
+		ip = s->ivec;
+		for(eip = ip+8; ip < eip; ){
+			*p++ ^= *ip;
+			*ip++ = *tp++;
+		}
+	}
+
+	if(len > 0){
+		ip = s->ivec;
+		triple_block_cipher(s->expanded, ip, DES3EDE);
+		for(eip = ip+len; ip < eip; )
+			*p++ ^= *ip++;
+	}
+}
--- /dev/null
+++ b/libsec/des3ECB.c
@@ -1,0 +1,47 @@
+#include "os.h"
+#include <libsec.h>
+
+// I wasn't sure what to do when the buffer was not
+// a multiple of 8.  I did what lacy's cryptolib did
+// to be compatible, but it looks dangerous to me
+// since its encrypting plain text with the key. -- presotto
+
+void
+des3ECBencrypt(uchar *p, int len, DES3state *s)
+{
+	int i;
+	uchar tmp[8];
+
+	for(; len >= 8; len -= 8){
+		triple_block_cipher(s->expanded, p, DES3EDE);
+		p += 8;
+	}
+	
+	if(len > 0){
+		for (i=0; i<8; i++)
+			tmp[i] = i;
+		triple_block_cipher(s->expanded, tmp, DES3EDE);
+		for (i = 0; i < len; i++)
+			p[i] ^= tmp[i];
+	}
+}
+
+void
+des3ECBdecrypt(uchar *p, int len, DES3state *s)
+{
+	int i;
+	uchar tmp[8];
+
+	for(; len >= 8; len -= 8){
+		triple_block_cipher(s->expanded, p, DES3DED);
+		p += 8;
+	}
+	
+	if(len > 0){
+		for (i=0; i<8; i++)
+			tmp[i] = i;
+		triple_block_cipher(s->expanded, tmp, DES3EDE);
+		for (i = 0; i < len; i++)
+			p[i] ^= tmp[i];
+	}
+}
--- /dev/null
+++ b/libsec/desCBC.c
@@ -1,0 +1,58 @@
+#include "os.h"
+#include <libsec.h>
+
+// Because of the way that non multiple of 8
+// buffers are handled, the decryptor must
+// be fed buffers of the same size as the
+// encryptor
+
+
+// If the length is not a multiple of 8, I encrypt
+// the overflow to be compatible with lacy's cryptlib
+void
+desCBCencrypt(uchar *p, int len, DESstate *s)
+{
+	uchar *p2, *ip, *eip;
+
+	for(; len >= 8; len -= 8){
+		p2 = p;
+		ip = s->ivec;
+		for(eip = ip+8; ip < eip; )
+			*p2++ ^= *ip++;
+		block_cipher(s->expanded, p, 0);
+		memmove(s->ivec, p, 8);
+		p += 8;
+	}
+
+	if(len > 0){
+		ip = s->ivec;
+		block_cipher(s->expanded, ip, 0);
+		for(eip = ip+len; ip < eip; )
+			*p++ ^= *ip++;
+	}
+}
+
+void
+desCBCdecrypt(uchar *p, int len, DESstate *s)
+{
+	uchar *ip, *eip, *tp;
+	uchar tmp[8];
+
+	for(; len >= 8; len -= 8){
+		memmove(tmp, p, 8);
+		block_cipher(s->expanded, p, 1);
+		tp = tmp;
+		ip = s->ivec;
+		for(eip = ip+8; ip < eip; ){
+			*p++ ^= *ip;
+			*ip++ = *tp++;
+		}
+	}
+
+	if(len > 0){
+		ip = s->ivec;
+		block_cipher(s->expanded, ip, 0);
+		for(eip = ip+len; ip < eip; )
+			*p++ ^= *ip++;
+	}
+}
--- /dev/null
+++ b/libsec/desECB.c
@@ -1,0 +1,47 @@
+#include "os.h"
+#include <libsec.h>
+
+// I wasn't sure what to do when the buffer was not
+// a multiple of 8.  I did what lacy's cryptolib did
+// to be compatible, but it looks dangerous to me
+// since its encrypting plain text with the key. -- presotto
+
+void
+desECBencrypt(uchar *p, int len, DESstate *s)
+{
+	int i;
+	uchar tmp[8];
+
+	for(; len >= 8; len -= 8){
+		block_cipher(s->expanded, p, 0);
+		p += 8;
+	}
+	
+	if(len > 0){
+		for (i=0; i<8; i++)
+			tmp[i] = i;
+		block_cipher(s->expanded, tmp, 0);
+		for (i = 0; i < len; i++)
+			p[i] ^= tmp[i];
+	}
+}
+
+void
+desECBdecrypt(uchar *p, int len, DESstate *s)
+{
+	int i;
+	uchar tmp[8];
+
+	for(; len >= 8; len -= 8){
+		block_cipher(s->expanded, p, 1);
+		p += 8;
+	}
+	
+	if(len > 0){
+		for (i=0; i<8; i++)
+			tmp[i] = i;
+		block_cipher(s->expanded, tmp, 0);
+		for (i = 0; i < len; i++)
+			p[i] ^= tmp[i];
+	}
+}
--- /dev/null
+++ b/libsec/desmodes.c
@@ -1,0 +1,31 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ *  these routines use the 64bit format for
+ *  DES keys.
+ */
+
+void
+setupDESstate(DESstate *s, uchar key[8], uchar *ivec)
+{
+	memset(s, 0, sizeof(*s));
+	memmove(s->key, key, sizeof(s->key));
+	des_key_setup(key, s->expanded);
+	if(ivec)
+		memmove(s->ivec, ivec, 8);
+	s->setup = 0xdeadbeef;
+}
+
+void
+setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec)
+{
+	memset(s, 0, sizeof(*s));
+	memmove(s->key, key, sizeof(s->key));
+	des_key_setup(key[0], s->expanded[0]);
+	des_key_setup(key[1], s->expanded[1]);
+	des_key_setup(key[2], s->expanded[2]);
+	if(ivec)
+		memmove(s->ivec, ivec, 8);
+	s->setup = 0xdeadbeef;
+}
--- /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;
+}
--- /dev/null
+++ b/libsec/dsaalloc.c
@@ -1,0 +1,72 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+DSApub*
+dsapuballoc(void)
+{
+	DSApub *dsa;
+
+	dsa = mallocz(sizeof(*dsa), 1);
+	if(dsa == nil)
+		sysfatal("dsapuballoc");
+	return dsa;
+}
+
+void
+dsapubfree(DSApub *dsa)
+{
+	if(dsa == nil)
+		return;
+	mpfree(dsa->p);
+	mpfree(dsa->q);
+	mpfree(dsa->alpha);
+	mpfree(dsa->key);
+	free(dsa);
+}
+
+
+DSApriv*
+dsaprivalloc(void)
+{
+	DSApriv *dsa;
+
+	dsa = mallocz(sizeof(*dsa), 1);
+	if(dsa == nil)
+		sysfatal("dsaprivalloc");
+	return dsa;
+}
+
+void
+dsaprivfree(DSApriv *dsa)
+{
+	if(dsa == nil)
+		return;
+	mpfree(dsa->pub.p);
+	mpfree(dsa->pub.q);
+	mpfree(dsa->pub.alpha);
+	mpfree(dsa->pub.key);
+	mpfree(dsa->secret);
+	free(dsa);
+}
+
+DSAsig*
+dsasigalloc(void)
+{
+	DSAsig *dsa;
+
+	dsa = mallocz(sizeof(*dsa), 1);
+	if(dsa == nil)
+		sysfatal("dsasigalloc");
+	return dsa;
+}
+
+void
+dsasigfree(DSAsig *dsa)
+{
+	if(dsa == nil)
+		return;
+	mpfree(dsa->r);
+	mpfree(dsa->s);
+	free(dsa);
+}
--- /dev/null
+++ b/libsec/dsagen.c
@@ -1,0 +1,58 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+DSApriv*
+dsagen(DSApub *opub)
+{
+	DSApub *pub;
+	DSApriv *priv;
+	mpint *exp;
+	mpint *g;
+	mpint *r;
+	int bits;
+
+	priv = dsaprivalloc();
+	pub = &priv->pub;
+
+	if(opub != nil){
+		pub->p = mpcopy(opub->p);
+		pub->q = mpcopy(opub->q);
+	} else {
+		pub->p = mpnew(0);
+		pub->q = mpnew(0);
+		DSAprimes(pub->q, pub->p, nil);
+	}
+	bits = Dbits*pub->p->top;
+
+	pub->alpha = mpnew(0);
+	pub->key = mpnew(0);
+	priv->secret = mpnew(0);
+
+	// 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.
+	exp = mpnew(0);
+	g = mpnew(0);
+	r = mpnew(0);
+	mpsub(pub->p, mpone, exp);
+	mpdiv(exp, pub->q, exp, r);
+	if(mpcmp(r, mpzero) != 0)
+		sysfatal("dsagen foul up");
+	while(1){
+		mprand(bits, genrandom, g);
+		mpmod(g, pub->p, g);
+		mpexp(g, exp, pub->p, pub->alpha);
+		if(mpcmp(pub->alpha, mpone) != 0)
+			break;
+	}
+	mpfree(g);
+	mpfree(exp);
+
+	// create the secret key
+	mprand(bits, genrandom, priv->secret);
+	mpmod(priv->secret, pub->p, priv->secret);
+	mpexp(pub->alpha, priv->secret, pub->p, pub->key);
+
+	return priv;
+}
--- /dev/null
+++ b/libsec/dsaprimes.c
@@ -1,0 +1,97 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+// NIST algorithm for generating DSA primes
+// Menezes et al (1997) Handbook of Applied Cryptography, p.151
+// q is a 160-bit prime;  p is a 1024-bit prime;  q divides p-1
+
+// arithmetic on unsigned ints mod 2**160, represented
+//    as 20-byte, little-endian uchar array
+
+static void
+Hrand(uchar *s)
+{
+	u32int *u = (u32int*)s;
+	*u++ = fastrand();
+	*u++ = fastrand();
+	*u++ = fastrand();
+	*u++ = fastrand();
+	*u = fastrand();
+}
+
+static void
+Hincr(uchar *s)
+{
+	int i;
+	for(i=0; i<20; i++)
+		if(++s[i]!=0)
+			break;
+}
+
+// this can run for quite a while;  be patient
+void
+DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen])
+{
+	int i, j, k, n = 6, b = 63;
+	uchar s[SHA1dlen], Hs[SHA1dlen], Hs1[SHA1dlen], sj[SHA1dlen], sjk[SHA1dlen];
+	mpint *two1023, *mb, *Vk, *W, *X, *q2;
+
+	two1023 = mpnew(1024);
+	mpleft(mpone, 1023, two1023);
+	mb = mpnew(0);
+	mpleft(mpone, b, mb);
+	W = mpnew(1024);
+	Vk = mpnew(1024);
+	X = mpnew(0);
+	q2 = mpnew(0);
+forever:
+	do{
+		Hrand(s);
+		memcpy(sj, s, 20);
+		sha1(s, 20, Hs, 0);
+		Hincr(sj);
+		sha1(sj, 20, Hs1, 0);
+		for(i=0; i<20; i++)
+			Hs[i] ^= Hs1[i];
+		Hs[0] |= 1;
+		Hs[19] |= 0x80;
+		letomp(Hs, 20, q);
+	}while(!probably_prime(q, 18));
+	if(seed != nil)	// allow skeptics to confirm computation
+		memmove(seed, s, SHA1dlen);
+	i = 0;
+	j = 2;
+	Hincr(sj);
+	mpleft(q, 1, q2);
+	while(i<4096){
+		memcpy(sjk, sj, 20);
+		for(k=0; k <= n; k++){
+			sha1(sjk, 20, Hs, 0);
+			letomp(Hs, 20, Vk);
+			if(k == n)
+				mpmod(Vk, mb, Vk);
+			mpleft(Vk, 160*k, Vk);
+			mpadd(W, Vk, W);
+			Hincr(sjk);
+		}
+		mpadd(W, two1023, X);
+		mpmod(X, q2, W);
+		mpsub(W, mpone, W);
+		mpsub(X, W, p);
+		if(mpcmp(p, two1023)>=0 && probably_prime(p, 5))
+			goto done;
+		i += 1;
+		j += n+1;
+		for(k=0; k<n+1; k++)
+			Hincr(sj);
+	}
+	goto forever;
+done:
+	mpfree(q2);
+	mpfree(X);
+	mpfree(Vk);
+	mpfree(W);
+	mpfree(mb);
+	mpfree(two1023);
+}
--- /dev/null
+++ b/libsec/dsaprivtopub.c
@@ -1,0 +1,16 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+DSApub*
+dsaprivtopub(DSApriv *priv)
+{
+	DSApub *pub;
+
+	pub = dsapuballoc();
+	pub->p = mpcopy(priv->pub.p);
+	pub->q = mpcopy(priv->pub.q);
+	pub->alpha = mpcopy(priv->pub.alpha);
+	pub->key = mpcopy(priv->pub.key);
+	return pub;
+}
--- /dev/null
+++ b/libsec/dsasign.c
@@ -1,0 +1,52 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+DSAsig*
+dsasign(DSApriv *priv, mpint *m)
+{
+	DSApub *pub = &priv->pub;
+	DSAsig *sig;
+	mpint *qm1, *k, *kinv, *r, *s;
+	mpint *q = pub->q, *p = pub->p, *alpha = pub->alpha;
+	int qlen = mpsignif(q);
+
+	qm1 = mpnew(0);
+	kinv = mpnew(0);
+	r = mpnew(0);
+	s = mpnew(0);
+	k = mpnew(0);
+	mpsub(pub->q, mpone, qm1);
+
+	// find a k that has an inverse mod q
+	while(1){
+		mprand(qlen, genrandom, k);
+		if((mpcmp(mpone, k) > 0) || (mpcmp(k, pub->q) >= 0))
+			continue;
+		mpextendedgcd(k, q, r, kinv, s);
+		if(mpcmp(r, mpone) != 0)
+			sysfatal("dsasign: pub->q not prime");
+		break;
+	}
+
+  	// make kinv positive
+	mpmod(kinv, pub->q, kinv);
+
+	// r = ((alpha**k) mod p) mod q
+	mpexp(alpha, k, p, r);
+	mpmod(r, q, r);
+
+	// s = (kinv*(m + ar)) mod q
+	mpmul(r, priv->secret, s);
+	mpadd(s, m, s);
+	mpmul(s, kinv, s);
+	mpmod(s, q, s);
+
+	sig = dsasigalloc();
+	sig->r = r;
+	sig->s = s;
+	mpfree(qm1);
+	mpfree(k);
+	mpfree(kinv);
+	return sig;
+}
--- /dev/null
+++ b/libsec/dsaverify.c
@@ -1,0 +1,46 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+int
+dsaverify(DSApub *pub, DSAsig *sig, mpint *m)
+{
+	int rv = -1;
+	mpint *u1, *u2, *v, *sinv;
+
+	if(mpcmp(sig->r, mpone) < 0 || mpcmp(sig->r, pub->q) >= 0)
+		return rv;
+	if(mpcmp(sig->s, mpone) < 0 || mpcmp(sig->s, pub->q) >= 0)
+		return rv;
+	u1 = mpnew(0);
+	u2 = mpnew(0);
+	v = mpnew(0);
+	sinv = mpnew(0);
+
+	// find (s**-1) mod q, make sure it exists
+	mpextendedgcd(sig->s, pub->q, u1, sinv, v);
+	if(mpcmp(u1, mpone) != 0)
+		goto out;
+
+	// u1 = (sinv * m) mod q, u2 = (r * sinv) mod q
+	mpmul(sinv, m, u1);
+	mpmod(u1, pub->q, u1);
+	mpmul(sig->r, sinv, u2);
+	mpmod(u2, pub->q, u2);
+
+	// v = (((alpha**u1)*(key**u2)) mod p) mod q
+	mpexp(pub->alpha, u1, pub->p, sinv);
+	mpexp(pub->key, u2, pub->p, v);
+	mpmul(sinv, v, v);
+	mpmod(v, pub->p, v);
+	mpmod(v, pub->q, v);
+
+	if(mpcmp(v, sig->r) == 0)
+		rv = 0;
+out:
+	mpfree(v);
+	mpfree(u1);
+	mpfree(u2);
+	mpfree(sinv);
+	return rv;
+}
--- /dev/null
+++ b/libsec/ecc.c
@@ -1,0 +1,612 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+#include <ctype.h>
+
+extern void jacobian_affine(mpint *p,
+	mpint *X, mpint *Y, mpint *Z);
+extern void jacobian_dbl(mpint *p, mpint *a,
+	mpint *X1, mpint *Y1, mpint *Z1,
+	mpint *X3, mpint *Y3, mpint *Z3);
+extern void jacobian_add(mpint *p, mpint *a,
+	mpint *X1, mpint *Y1, mpint *Z1,
+	mpint *X2, mpint *Y2, mpint *Z2,
+	mpint *X3, mpint *Y3, mpint *Z3);
+
+void
+ecassign(ECdomain *dom, ECpoint *a, ECpoint *b)
+{
+	if((b->inf = a->inf) != 0)
+		return;
+	mpassign(a->x, b->x);
+	mpassign(a->y, b->y);
+	if(b->z != nil){
+		mpassign(a->z != nil ? a->z : mpone, b->z);
+		return;
+	}
+	if(a->z != nil){
+		b->z = mpcopy(a->z);
+		jacobian_affine(dom->p, b->x, b->y, b->z);
+		mpfree(b->z);
+		b->z = nil;
+	}
+}
+
+void
+ecadd(ECdomain *dom, ECpoint *a, ECpoint *b, ECpoint *s)
+{
+	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(s->z == nil){
+		s->z = mpcopy(mpone);
+		ecadd(dom, a, b, s);
+		if(!s->inf)
+			jacobian_affine(dom->p, s->x, s->y, s->z);
+		mpfree(s->z);
+		s->z = nil;
+		return;
+	}
+
+	if(a == b)
+		jacobian_dbl(dom->p, dom->a,
+			a->x, a->y, a->z != nil ? a->z : mpone,
+			s->x, s->y, s->z);
+	else
+		jacobian_add(dom->p, dom->a,
+			a->x, a->y, a->z != nil ? a->z : mpone,
+			b->x, b->y, b->z != nil ? b->z : mpone,
+			s->x, s->y, s->z);
+	s->inf = mpcmp(s->z, mpzero) == 0;
+}
+
+void
+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);
+	ns.z = mpnew(0);
+	na.x = mpnew(0);
+	na.y = mpnew(0);
+	na.z = mpnew(0);
+	ecassign(dom, a, &na);
+	l = mpcopy(k);
+	l->sign = 1;
+	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.inf){
+		ns.y->sign = -1;
+		mpmod(ns.y, dom->p, ns.y);
+	}
+	ecassign(dom, &ns, s);
+	mpfree(ns.x);
+	mpfree(ns.y);
+	mpfree(ns.z);
+	mpfree(na.x);
+	mpfree(na.y);
+	mpfree(na.z);
+	mpfree(l);
+}
+
+int
+ecverify(ECdomain *dom, ECpoint *a)
+{
+	mpint *p, *q;
+	int r;
+
+	if(a->inf)
+		return 1;
+
+	assert(a->z == nil);	/* need affine coordinates */
+	p = mpnew(0);
+	q = mpnew(0);
+	mpmodmul(a->y, a->y, dom->p, p);
+	mpmodmul(a->x, a->x, dom->p, q);
+	mpmodadd(q, dom->a, dom->p, q);
+	mpmodmul(q, a->x, dom->p, q);
+	mpmodadd(q, dom->b, dom->p, q);
+	r = mpcmp(p, q);
+	mpfree(p);
+	mpfree(q);
+	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);
+	p.z = mpnew(0);
+	ecmul(dom, a, dom->n, &p);
+	r = p.inf;
+	mpfree(p.x);
+	mpfree(p.y);
+	mpfree(p.z);
+	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(;;){
+			mpnrand(p, genrandom, a);
+			if(mpcmp(a, mpzero) > 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);
+	}
+	ret->inf = 0;
+	o = 0;
+	switch(octet(&s)){
+	case 0:
+		ret->inf = 1;
+		break;
+	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;
+		break;
+	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;
+		break;
+	}
+	if(ret->z != nil && !ret->inf)
+		mpassign(mpone, ret->z);
+	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->a.x = mpnew(0);
+		p->a.y = mpnew(0);
+		p->d = mpnew(0);
+	}
+	for(;;){
+		mpnrand(dom->n, genrandom, p->d);
+		if(mpcmp(p->d, mpzero) > 0)
+			break;
+	}
+	ecmul(dom, &dom->G, p->d, &p->a);
+	return p;
+}
+
+void
+ecdsasign(ECdomain *dom, ECpriv *priv, uchar *dig, int len, mpint *r, mpint *s)
+{
+	ECpriv tmp;
+	mpint *E, *t;
+
+	tmp.a.x = mpnew(0);
+	tmp.a.y = mpnew(0);
+	tmp.a.z = nil;
+	tmp.d = mpnew(0);
+	E = betomp(dig, len, nil);
+	t = mpnew(0);
+	if(mpsignif(dom->n) < 8*len)
+		mpright(E, 8*len - mpsignif(dom->n), E);
+	for(;;){
+		ecgen(dom, &tmp);
+		mpmod(tmp.a.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);
+		mpmodmul(s, t, dom->n, s);
+		if(mpcmp(s, mpzero) != 0)
+			break;
+	}
+	mpfree(t);
+	mpfree(E);
+	mpfree(tmp.a.x);
+	mpfree(tmp.a.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);
+	R.z = mpnew(0);
+	S.x = mpnew(0);
+	S.y = mpnew(0);
+	S.z = mpnew(0);
+	mpinvert(s, dom->n, t);
+	mpmodmul(E, t, dom->n, u1);
+	mpmodmul(r, t, dom->n, u2);
+	ecmul(dom, &dom->G, u1, &R);
+	ecmul(dom, pub, u2, &S);
+	ecadd(dom, &R, &S, &R);
+	ret = 0;
+	if(!R.inf){
+		jacobian_affine(dom->p, R.x, R.y, R.z);
+		mpmod(R.x, dom->n, t);
+		ret = mpcmp(r, t) == 0;
+	}
+	mpfree(E);
+	mpfree(t);
+	mpfree(u1);
+	mpfree(u2);
+	mpfree(R.x);
+	mpfree(R.y);
+	mpfree(R.z);
+	mpfree(S.x);
+	mpfree(S.y);
+	mpfree(S.z);
+	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);
+}
--- /dev/null
+++ b/libsec/egalloc.c
@@ -1,0 +1,70 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+EGpub*
+egpuballoc(void)
+{
+	EGpub *eg;
+
+	eg = mallocz(sizeof(*eg), 1);
+	if(eg == nil)
+		sysfatal("egpuballoc");
+	return eg;
+}
+
+void
+egpubfree(EGpub *eg)
+{
+	if(eg == nil)
+		return;
+	mpfree(eg->p);
+	mpfree(eg->alpha);
+	mpfree(eg->key);
+	free(eg);
+}
+
+
+EGpriv*
+egprivalloc(void)
+{
+	EGpriv *eg;
+
+	eg = mallocz(sizeof(*eg), 1);
+	if(eg == nil)
+		sysfatal("egprivalloc");
+	return eg;
+}
+
+void
+egprivfree(EGpriv *eg)
+{
+	if(eg == nil)
+		return;
+	mpfree(eg->pub.p);
+	mpfree(eg->pub.alpha);
+	mpfree(eg->pub.key);
+	mpfree(eg->secret);
+	free(eg);
+}
+
+EGsig*
+egsigalloc(void)
+{
+	EGsig *eg;
+
+	eg = mallocz(sizeof(*eg), 1);
+	if(eg == nil)
+		sysfatal("egsigalloc");
+	return eg;
+}
+
+void
+egsigfree(EGsig *eg)
+{
+	if(eg == nil)
+		return;
+	mpfree(eg->r);
+	mpfree(eg->s);
+	free(eg);
+}
--- /dev/null
+++ b/libsec/egdecrypt.c
@@ -1,0 +1,28 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+mpint*
+egdecrypt(EGpriv *priv, mpint *in, mpint *out)
+{
+	EGpub *pub = &priv->pub;
+	mpint *gamma, *delta;
+	mpint *p = pub->p;
+	int plen = mpsignif(p)+1;
+	int shift = ((plen+Dbits-1)/Dbits)*Dbits;
+
+	if(out == nil)
+		out = mpnew(0);
+	gamma = mpnew(0);
+	delta = mpnew(0);
+	mpright(in, shift, gamma);
+	mpleft(gamma, shift, delta);
+	mpsub(in, delta, delta);	
+	mpexp(gamma, priv->secret, p, out);
+	mpinvert(out, p, gamma);
+	mpmul(gamma, delta, out);
+	mpmod(out, p, out);
+	mpfree(gamma);
+	mpfree(delta);
+	return out;
+}
--- /dev/null
+++ b/libsec/egencrypt.c
@@ -1,0 +1,38 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+mpint*
+egencrypt(EGpub *pub, mpint *in, mpint *out)
+{
+	mpint *m, *k, *gamma, *delta, *pm1;
+	mpint *p = pub->p, *alpha = pub->alpha;
+	int plen = mpsignif(p);
+	int shift = ((plen+Dbits)/Dbits)*Dbits;
+	// in libcrypt version, (int)(LENGTH(pub->p)*sizeof(NumType)*CHARBITS);
+
+	if(out == nil)
+		out = mpnew(0);
+	pm1 = mpnew(0);
+	m = mpnew(0);
+	gamma = mpnew(0);
+	delta = mpnew(0);
+	mpmod(in, p, m);
+	while(1){
+		k = mprand(plen, genrandom, nil);
+		if((mpcmp(mpone, k) <= 0) && (mpcmp(k, pm1) < 0))
+			break;
+	}
+	mpexp(alpha, k, p, gamma);
+	mpexp(pub->key, k, p, delta);
+	mpmul(m, delta, delta);
+	mpmod(delta, p, delta);
+	mpleft(gamma, shift, out);
+	mpadd(delta, out, out);
+	mpfree(pm1);
+	mpfree(m);
+	mpfree(k);
+	mpfree(gamma);
+	mpfree(delta);
+	return out;
+}
--- /dev/null
+++ b/libsec/eggen.c
@@ -1,0 +1,21 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+EGpriv*
+eggen(int nlen, int rounds)
+{
+	EGpub *pub;
+	EGpriv *priv;
+
+	priv = egprivalloc();
+	pub = &priv->pub;
+	pub->p = mpnew(0);
+	pub->alpha = mpnew(0);
+	pub->key = mpnew(0);
+	priv->secret = mpnew(0);
+	gensafeprime(pub->p, pub->alpha, nlen, rounds);
+	mprand(nlen-1, genrandom, priv->secret);
+	mpexp(pub->alpha, priv->secret, pub->p, pub->key);
+	return priv;
+}
--- /dev/null
+++ b/libsec/egprivtopub.c
@@ -1,0 +1,17 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+EGpub*
+egprivtopub(EGpriv *priv)
+{
+	EGpub *pub;
+
+	pub = egpuballoc();
+	if(pub == nil)
+		return nil;
+	pub->p = mpcopy(priv->pub.p);
+	pub->alpha = mpcopy(priv->pub.alpha);
+	pub->key = mpcopy(priv->pub.key);
+	return pub;
+}
--- /dev/null
+++ b/libsec/egsign.c
@@ -1,0 +1,43 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+EGsig*
+egsign(EGpriv *priv, mpint *m)
+{
+	EGpub *pub = &priv->pub;
+	EGsig *sig;
+	mpint *pm1, *k, *kinv, *r, *s;
+	mpint *p = pub->p, *alpha = pub->alpha;
+	int plen = mpsignif(p);
+
+	pm1 = mpnew(0);
+	kinv = mpnew(0);
+	r = mpnew(0);
+	s = mpnew(0);
+	k = mpnew(0);
+	mpsub(p, mpone, pm1);
+	while(1){
+		mprand(plen, genrandom, k);
+		if((mpcmp(mpone, k) > 0) || (mpcmp(k, pm1) >= 0))
+			continue;
+		mpextendedgcd(k, pm1, r, kinv, s);
+		if(mpcmp(r, mpone) != 0)
+			continue;
+		break;
+	}
+	mpmod(kinv, pm1, kinv);  // make kinv positive
+	mpexp(alpha, k, p, r);
+	mpmul(priv->secret, r, s);
+	mpmod(s, pm1, s);
+	mpsub(m, s, s);
+	mpmul(kinv, s, s);
+	mpmod(s, pm1, s);
+	sig = egsigalloc();
+	sig->r = r;
+	sig->s = s;
+	mpfree(pm1);
+	mpfree(k);
+	mpfree(kinv);
+	return sig;
+}
--- /dev/null
+++ b/libsec/egtest.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+void
+main(void)
+{
+	EGpriv *sk;
+	mpint *m, *gamma, *delta, *in, *out;
+	int plen, shift;
+
+	fmtinstall('B', mpfmt);
+
+	sk = egprivalloc();
+	sk->pub.p = uitomp(2357, nil);
+	sk->pub.alpha = uitomp(2, nil);
+	sk->pub.key = uitomp(1185, nil);
+	sk->secret = uitomp(1751, nil);
+
+	m = uitomp(2035, nil);
+
+	plen = mpsignif(sk->pub.p)+1;
+	shift = ((plen+Dbits-1)/Dbits)*Dbits;
+	gamma = uitomp(1430, nil);
+	delta = uitomp(697, nil);
+	out = mpnew(0);
+	in = mpnew(0);
+	mpleft(gamma, shift, in);
+	mpadd(delta, in, in);
+	egdecrypt(sk, in, out);
+
+	if(mpcmp(m, out) != 0)
+		print("decrypt failed to recover message\n");
+}
--- /dev/null
+++ b/libsec/egverify.c
@@ -1,0 +1,29 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+int
+egverify(EGpub *pub, EGsig *sig, mpint *m)
+{
+	mpint *p = pub->p, *alpha = pub->alpha;
+	mpint *r = sig->r, *s = sig->s;
+	mpint *v1, *v2, *rs;
+	int rv = -1;
+
+	if(mpcmp(r, mpone) < 0 || mpcmp(r, p) >= 0)
+		return rv;
+	v1 = mpnew(0);
+	rs = mpnew(0);
+	v2 = mpnew(0);
+	mpexp(pub->key, r, p, v1);
+	mpexp(r, s, p, rs);
+	mpmul(v1, rs, v1);
+	mpmod(v1, p, v1);
+	mpexp(alpha, m, p, v2);
+	if(mpcmp(v1, v2) == 0)
+		rv = 0;
+	mpfree(v1);
+	mpfree(rs);
+	mpfree(v2);
+	return rv;
+}
--- /dev/null
+++ b/libsec/fastrand.c
@@ -1,0 +1,15 @@
+#include "os.h"
+#include <libsec.h>
+
+/* 
+ *  use the X917 random number generator to create random
+ *  numbers (faster than truerand() but not as random).
+ */
+ulong
+fastrand(void)
+{
+	ulong x;
+	
+	genrandom((uchar*)&x, sizeof x);
+	return x;
+}
--- /dev/null
+++ b/libsec/genprime.c
@@ -1,0 +1,28 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+//  generate a probable prime.  accuracy is the miller-rabin interations
+void
+genprime(mpint *p, int n, int accuracy)
+{
+	mpdigit x;
+
+	// generate n random bits with high and low bits 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;
+	p->p[0] |= 1;
+	mpnorm(p);
+
+	// keep icrementing till it looks prime
+	for(;;){
+		if(probably_prime(p, accuracy))
+			break;
+		mpadd(p, mptwo, p);
+	}
+}
--- /dev/null
+++ b/libsec/genrandom.c
@@ -1,0 +1,44 @@
+#include "os.h"
+#include <libsec.h>
+
+static void
+init(Chachastate *cs)
+{
+	ulong seed[11];
+	int i;
+
+	for(i=0; i<nelem(seed); i++)
+		seed[i] = truerand();
+
+	setupChachastate(cs, (uchar*)&seed[0], 32, (uchar*)&seed[8], 12, 20);
+	memset(seed, 0, sizeof(seed));
+}
+
+static void
+fill(Chachastate *cs, uchar *p, int n)
+{
+	Chachastate c;
+
+	c = *cs;
+	chacha_encrypt((uchar*)&cs->input[4], 32, &c);
+	if(++cs->input[13] == 0)
+		if(++cs->input[14] == 0)
+			++cs->input[15];
+
+	chacha_encrypt(p, n, &c);
+	memset(&c, 0, sizeof(c));
+}
+
+void
+genrandom(uchar *p, int n)
+{
+	static QLock lk;
+	static Chachastate cs;
+
+	qlock(&lk);
+	if(cs.rounds == 0)
+		init(&cs);
+	cs.input[4] ^= getpid();	/* fork protection */
+	fill(&cs, p, n);
+	qunlock(&lk);
+}
--- /dev/null
+++ b/libsec/gensafeprime.c
@@ -1,0 +1,36 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+// find a prime p of length n and a generator alpha of Z^*_p
+// Alg 4.86 Menezes et al () Handbook, p.164
+void
+gensafeprime(mpint *p, mpint *alpha, int n, int accuracy)
+{
+	mpint *q, *b;
+
+	q = mpnew(n-1);
+	while(1){
+		genprime(q, n-1, accuracy);
+		mpleft(q, 1, p);
+		mpadd(p, mpone, p); // p = 2*q+1
+		if(probably_prime(p, accuracy))
+			break;
+	}
+	// now find a generator alpha of the multiplicative
+	// group Z*_p of order p-1=2q
+	b = mpnew(0);
+	while(1){
+		mprand(n, genrandom, alpha);
+		mpmod(alpha, p, alpha);
+		mpmul(alpha, alpha, b);
+		mpmod(b, p, b);
+		if(mpcmp(b, mpone) == 0)
+			continue;
+		mpexp(alpha, q, p, b);
+		if(mpcmp(b, mpone) != 0)
+			break;
+	}
+	mpfree(b);
+	mpfree(q);
+}
--- /dev/null
+++ b/libsec/genstrongprime.c
@@ -1,0 +1,57 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+// Gordon's algorithm for generating a strong prime
+//	Menezes et al () Handbook, p.150
+void
+genstrongprime(mpint *p, int n, int accuracy)
+{
+	mpint *s, *t, *r, *i;
+
+	if(n < 64)
+		n = 64;
+
+	s = mpnew(n/2);
+	genprime(s, (n/2)-16, accuracy);
+	t = mpnew(n/2);
+	genprime(t, n-mpsignif(s)-32, accuracy);
+
+	// first r = 2it + 1 that's prime
+	i = mpnew(16);
+	r = mpnew(0);
+	itomp(0x8000, i);
+	mpleft(t, 1, t);	// 2t
+	mpmul(i, t, r);		// 2it
+	mpadd(r, mpone, r);	// 2it + 1
+	for(;;){
+		if(probably_prime(r, 18))
+			break;
+		mpadd(r, t, r);	// r += 2t
+	}
+
+	// p0 = 2(s**(r-2) mod r)s - 1
+	itomp(2, p);
+	mpsub(r, p, p);
+	mpexp(s, p, r, p);
+	mpmul(s, p, p);
+	mpleft(p, 1, p);
+	mpsub(p, mpone, p);
+
+	// first p = p0 + 2irs that's prime
+	itomp(0x8000, i);
+	mpleft(r, 1, r);	// 2r
+	mpmul(r, s, r);		// 2rs
+	mpmul(r, i, i);		// 2irs
+	mpadd(p, i, p);		// p0 + 2irs
+	for(;;){
+		if(probably_prime(p, accuracy))
+			break;
+		mpadd(p, r, p); // p += 2rs
+	}
+
+	mpfree(i);
+	mpfree(s);
+	mpfree(r);
+	mpfree(t);
+}
--- /dev/null
+++ b/libsec/hkdf.c
@@ -1,0 +1,38 @@
+#include "os.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);
+	}
+}
--- /dev/null
+++ b/libsec/hmac.c
@@ -1,0 +1,46 @@
+#include "os.h"
+#include <libsec.h>
+
+/* rfc2104 */
+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[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;
+	}
+
+	/* first time through */
+	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, Hmacblksz, nil, s);
+		if(s == nil)
+			return nil;
+	}
+
+	s = (*x)(p, len, nil, s);
+	if(digest == nil)
+		return s;
+
+	/* last time through */
+	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, Hmacblksz, nil, nil);
+	(*x)(innerdigest, xlen, digest, s);
+	return nil;
+}
--- /dev/null
+++ b/libsec/hmactest.c
@@ -1,0 +1,19 @@
+#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");
+}
--- /dev/null
+++ b/libsec/jacobian.c
@@ -1,0 +1,166 @@
+#include "os.h"
+#include <mp.h>
+void jacobian_new(mpint *x, mpint *y, mpint *z, mpint *X, mpint *Y, mpint *Z){
+	mpassign(x, X);
+	mpassign(y, Y);
+	mpassign(z, Z);
+	}
+void jacobian_inf(mpint *X, mpint *Y, mpint *Z){
+	jacobian_new(mpzero, mpone, mpzero, X, Y, Z);
+	}
+void jacobian_affine(mpint *p, mpint *X, mpint *Y, mpint *Z){
+	mpint *ZZZ = mpnew(0);
+	mpint *ZZ = mpnew(0);
+	if(mpcmp(Z, mpzero) != 0){
+		mpmodmul(Z, Z, p, ZZ);
+		mpmodmul(ZZ, Z, p, ZZZ);
+		mpint *tmp1 = mpnew(0);
+		mpinvert(ZZ, p, tmp1);
+		mpmodmul(X, tmp1, p, X);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		mpinvert(ZZZ, p, tmp1);
+		mpmodmul(Y, tmp1, p, Y);
+		mpfree(tmp1);
+		mpassign(mpone, Z);
+		}
+	mpfree(ZZZ);
+	mpfree(ZZ);
+	}
+void jacobian_dbl(mpint *p, mpint *a, mpint *X1, mpint *Y1, mpint *Z1, mpint *X3, mpint *Y3, mpint *Z3){
+	mpint *M = mpnew(0);
+	mpint *S = mpnew(0);
+	mpint *ZZ = mpnew(0);
+	mpint *YYYY = mpnew(0);
+	mpint *YY = mpnew(0);
+	mpint *XX = mpnew(0);
+	if(mpcmp(Y1, mpzero) == 0){
+		jacobian_inf(X3, Y3, Z3);
+		}else{
+		mpmodmul(X1, X1, p, XX);
+		mpmodmul(Y1, Y1, p, YY);
+		mpmodmul(YY, YY, p, YYYY);
+		mpmodmul(Z1, Z1, p, ZZ);
+		mpint *tmp1 = mpnew(0);
+		mpmodadd(X1, YY, p, tmp1);
+		mpmodmul(tmp1, tmp1, p, tmp1);
+		mpmodsub(tmp1, XX, p, tmp1);
+		mpmodsub(tmp1, YYYY, p, tmp1);
+		mpmodadd(tmp1, tmp1, p, S); // 2*tmp1
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		uitomp(3UL, tmp1);
+		mpmodmul(tmp1, XX, p, M);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		mpint *tmp2 = mpnew(0);
+		mpmodmul(ZZ, ZZ, p, tmp2);
+		mpmodmul(a, tmp2, p, tmp1);
+		mpfree(tmp2);
+		mpmodadd(M, tmp1, p, M);
+		mpfree(tmp1);
+		mpmodadd(Y1, Z1, p, Z3);
+		mpmodmul(Z3, Z3, p, Z3);
+		mpmodsub(Z3, YY, p, Z3);
+		mpmodsub(Z3, ZZ, p, Z3);
+		mpmodmul(M, M, p, X3);
+		tmp1 = mpnew(0);
+		mpmodadd(S, S, p, tmp1); // 2*S
+		mpmodsub(X3, tmp1, p, X3);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		mpmodsub(S, X3, p, tmp1);
+		mpmodmul(M, tmp1, p, Y3);
+		mpfree(tmp1);
+		tmp1 = mpnew(0);
+		tmp2 = mpnew(0);
+		uitomp(8UL, tmp2);
+		mpmodmul(tmp2, YYYY, p, tmp1);
+		mpfree(tmp2);
+		mpmodsub(Y3, tmp1, p, Y3);
+		mpfree(tmp1);
+		}
+	mpfree(M);
+	mpfree(S);
+	mpfree(ZZ);
+	mpfree(YYYY);
+	mpfree(YY);
+	mpfree(XX);
+	}
+void jacobian_add(mpint *p, mpint *a, mpint *X1, mpint *Y1, mpint *Z1, mpint *X2, mpint *Y2, mpint *Z2, mpint *X3, mpint *Y3, mpint *Z3){
+	mpint *V = mpnew(0);
+	mpint *r = mpnew(0);
+	mpint *J = mpnew(0);
+	mpint *I = mpnew(0);
+	mpint *H = mpnew(0);
+	mpint *S2 = mpnew(0);
+	mpint *S1 = mpnew(0);
+	mpint *U2 = mpnew(0);
+	mpint *U1 = mpnew(0);
+	mpint *Z2Z2 = mpnew(0);
+	mpint *Z1Z1 = mpnew(0);
+	mpmodmul(Z1, Z1, p, Z1Z1);
+	mpmodmul(Z2, Z2, p, Z2Z2);
+	mpmodmul(X1, Z2Z2, p, U1);
+	mpmodmul(X2, Z1Z1, p, U2);
+	mpint *tmp1 = mpnew(0);
+	mpmodmul(Y1, Z2, p, tmp1);
+	mpmodmul(tmp1, Z2Z2, p, S1);
+	mpfree(tmp1);
+	tmp1 = mpnew(0);
+	mpmodmul(Y2, Z1, p, tmp1);
+	mpmodmul(tmp1, Z1Z1, p, S2);
+	mpfree(tmp1);
+	if(mpcmp(U1, U2) == 0){
+		if(mpcmp(S1, S2) != 0){
+			jacobian_inf(X3, Y3, Z3);
+			}else{
+			jacobian_dbl(p, a, X1, Y1, Z1, X3, Y3, Z3);
+			}
+		}else{
+		mpmodsub(U2, U1, p, H);
+		mpmodadd(H, H, p, I); // 2*H
+		mpmodmul(I, I, p, I);
+		mpmodmul(H, I, p, J);
+		mpint *tmp2 = mpnew(0);
+		mpmodsub(S2, S1, p, tmp2);
+		mpmodadd(tmp2, tmp2, p, r); // 2*tmp2
+		mpfree(tmp2);
+		mpmodmul(U1, I, p, V);
+		mpmodmul(r, r, p, X3);
+		mpmodsub(X3, J, p, X3);
+		tmp2 = mpnew(0);
+		mpmodadd(V, V, p, tmp2); // 2*V
+		mpmodsub(X3, tmp2, p, X3);
+		mpfree(tmp2);
+		tmp2 = mpnew(0);
+		mpmodsub(V, X3, p, tmp2);
+		mpmodmul(r, tmp2, p, Y3);
+		mpfree(tmp2);
+		tmp2 = mpnew(0);
+		mpint *tmp3 = mpnew(0);
+		mpmodadd(S1, S1, p, tmp3); // 2*S1
+		mpmodmul(tmp3, J, p, tmp2);
+		mpfree(tmp3);
+		mpmodsub(Y3, tmp2, p, Y3);
+		mpfree(tmp2);
+		tmp2 = mpnew(0);
+		mpmodadd(Z1, Z2, p, tmp2);
+		mpmodmul(tmp2, tmp2, p, tmp2);
+		mpmodsub(tmp2, Z1Z1, p, tmp2);
+		mpmodsub(tmp2, Z2Z2, p, tmp2);
+		mpmodmul(tmp2, H, p, Z3);
+		mpfree(tmp2);
+		}
+	mpfree(V);
+	mpfree(r);
+	mpfree(J);
+	mpfree(I);
+	mpfree(H);
+	mpfree(S2);
+	mpfree(S1);
+	mpfree(U2);
+	mpfree(U1);
+	mpfree(Z2Z2);
+	mpfree(Z1Z1);
+	}
--- /dev/null
+++ b/libsec/md4.c
@@ -1,0 +1,271 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ *  This MD4 is implemented from the description in Stinson's Cryptography,
+ *  theory and practice. -- presotto
+ */
+
+/*
+ *	Rotate ammounts used in the algorithm
+ */
+enum
+{
+	S11=	3,
+	S12=	7,
+	S13=	11,
+	S14=	19,
+
+	S21=	3,
+	S22=	5,
+	S23=	9,
+	S24=	13,
+
+	S31=	3,
+	S32=	9,
+	S33=	11,
+	S34=	15,
+};
+
+typedef struct MD4Table MD4Table;
+struct MD4Table
+{
+	uchar	x;	/* index into data block */
+	uchar	rot;	/* amount to rotate left by */
+};
+
+static MD4Table tab[] =
+{
+	/* round 1 */
+/*[0]*/	{ 0,	S11},	
+	{ 1,	S12},	
+	{ 2,	S13},	
+	{ 3,	S14},	
+	{ 4,	S11},	
+	{ 5,	S12},	
+	{ 6,	S13},	
+	{ 7,	S14},	
+	{ 8,	S11},	
+	{ 9,	S12},	
+	{ 10,	S13},	
+	{ 11,	S14},	
+	{ 12,	S11},	
+	{ 13,	S12},	
+	{ 14,	S13},	
+	{ 15,	S14},
+
+	/* round 2 */
+/*[16]*/{ 0,	S21},	
+	{ 4,	S22},	
+	{ 8,	S23},	
+	{ 12,	S24},	
+	{ 1,	S21},	
+	{ 5,	S22},	
+	{ 9,	S23},	
+	{ 13,	S24},	
+	{ 2,	S21},	
+	{ 6,	S22},	
+	{ 10,	S23},	
+	{ 14,	S24},	
+	{ 3,	S21},	
+	{ 7,	S22},	
+	{ 11,	S23},	
+	{ 15,	S24},
+
+	/* round 3 */
+/*[32]*/{ 0,	S31},	
+	{ 8,	S32},	
+	{ 4,	S33},	
+	{ 12,	S34},	
+	{ 2,	S31},	
+	{ 10,	S32},	
+	{ 6,	S33},	
+	{ 14,	S34},	
+	{ 1,	S31},	
+	{ 9,	S32},	
+	{ 5,	S33},	
+	{ 13,	S34},	
+	{ 3,	S31},	
+	{ 11,	S32},	
+	{ 7,	S33},	
+	{ 15,	S34},	
+};
+
+static void encode(uchar*, u32int*, ulong);
+static void decode(u32int*, uchar*, ulong);
+
+static void
+md4block(uchar *p, ulong len, MD4state *s)
+{
+	int i;
+	u32int a, b, c, d, tmp;
+	MD4Table *t;
+	uchar *end;
+	u32int x[16];
+
+	for(end = p+len; p < end; p += 64){
+		a = s->state[0];
+		b = s->state[1];
+		c = s->state[2];
+		d = s->state[3];
+
+		decode(x, p, 64);
+	
+		for(i = 0; i < 48; i++){
+			t = tab + i;
+			switch(i>>4){
+			case 0:
+				a += (b & c) | (~b & d);
+				break;
+			case 1:
+				a += ((b & c) | (b & d) | (c & d)) + 0x5A827999;
+				break;
+			case 2:
+				a += (b ^ c ^ d) + 0x6ED9EBA1;
+				break;
+			}
+			a += x[t->x];
+			a = (a << t->rot) | (a >> (32 - t->rot));
+	
+			/* rotate variables */
+			tmp = d;
+			d = c;
+			c = b;
+			b = a;
+			a = tmp;
+		}
+
+		s->state[0] += a;
+		s->state[1] += b;
+		s->state[2] += c;
+		s->state[3] += d;
+
+		s->len += 64;
+	}
+}
+
+MD4state*
+md4(uchar *p, ulong len, uchar *digest, MD4state *s)
+{
+	u32int x[16];
+	uchar buf[128];
+	int i;
+	uchar *e;
+
+	if(s == nil){
+		s = malloc(sizeof(*s));
+		if(s == nil)
+			return nil;
+		memset(s, 0, sizeof(*s));
+		s->malloced = 1;
+	}
+
+	if(s->seeded == 0){
+		/* seed the state, these constants would look nicer big-endian */
+		s->state[0] = 0x67452301;
+		s->state[1] = 0xefcdab89;
+		s->state[2] = 0x98badcfe;
+		s->state[3] = 0x10325476;
+		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){
+			md4block(s->buf, s->blen, s);
+			s->blen = 0;
+		}
+	}
+
+	/* do 64 byte blocks */
+	i = len & ~0x3f;
+	if(i){
+		md4block(p, i, s);
+		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<<3;
+	x[1] = s->len>>29;
+	encode(p+len, x, 8);
+
+	/* digest the last part */
+	md4block(p, len+8, s);
+
+	/* return result and free state */
+	encode(digest, s->state, MD4dlen);
+	if(s->malloced == 1)
+		free(s);
+	return nil;
+}
+
+/*
+ *	encodes input (u32int) into output (uchar). Assumes len is
+ *	a multiple of 4.
+ */
+static void
+encode(uchar *output, u32int *input, ulong len)
+{
+	u32int x;
+	uchar *e;
+
+	for(e = output + len; output < e;) {
+		x = *input++;
+		*output++ = x;
+		*output++ = x >> 8;
+		*output++ = x >> 16;
+		*output++ = x >> 24;
+	}
+}
+
+/*
+ *	decodes input (uchar) into output (u32int). Assumes len is
+ *	a multiple of 4.
+ */
+static void
+decode(u32int *output, uchar *input, ulong len)
+{
+	uchar *e;
+
+	for(e = input+len; input < e; input += 4)
+		*output++ = input[0] | (input[1] << 8) |
+			(input[2] << 16) | (input[3] << 24);
+}
--- /dev/null
+++ b/libsec/md4test.c
@@ -1,0 +1,31 @@
+#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");
+	}
+}
--- /dev/null
+++ b/libsec/md5.c
@@ -1,0 +1,154 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ *  rfc1321 requires that I include this.  The code is new.  The constants
+ *  all come from the rfc (hence the copyright).  We trade a table for the
+ *  macros in rfc.  The total size is a lot less. -- presotto
+ *
+ *	Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ *	rights reserved.
+ *
+ *	License to copy and use this software is granted provided that it
+ *	is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ *	Algorithm" in all material mentioning or referencing this software
+ *	or this function.
+ *
+ *	License is also granted to make and use derivative works provided
+ *	that such works are identified as "derived from the RSA Data
+ *	Security, Inc. MD5 Message-Digest Algorithm" in all material
+ *	mentioning or referencing the derived work.
+ *
+ *	RSA Data Security, Inc. makes no representations concerning either
+ *	the merchantability of this software or the suitability of this
+ *	software forany particular purpose. It is provided "as is"
+ *	without express or implied warranty of any kind.
+ *	These notices must be retained in any copies of any part of this
+ *	documentation and/or software.
+ */
+
+static void encode(uchar*, u32int*, ulong);
+
+extern void _md5block(uchar*, ulong, u32int*);
+
+MD5state*
+md5(uchar *p, ulong len, uchar *digest, MD5state *s)
+{
+	u32int x[16];
+	uchar buf[128];
+	int i;
+	uchar *e;
+
+	if(s == nil){
+		s = malloc(sizeof(*s));
+		if(s == nil)
+			return nil;
+		memset(s, 0, sizeof(*s));
+		s->malloced = 1;
+	}
+
+	if(s->seeded == 0){
+		/* seed the state, these constants would look nicer big-endian */
+		s->state[0] = 0x67452301;
+		s->state[1] = 0xefcdab89;
+		s->state[2] = 0x98badcfe;
+		s->state[3] = 0x10325476;
+		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){
+			_md5block(s->buf, s->blen, s->state);
+			s->len += s->blen;
+			s->blen = 0;
+		}
+	}
+
+	/* do 64 byte blocks */
+	i = len & ~0x3f;
+	if(i){
+		_md5block(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<<3;
+	x[1] = s->len>>29;
+	encode(p+len, x, 8);
+
+	/* digest the last part */
+	_md5block(p, len+8, s->state);
+	s->len += len;
+
+	/* return result and free state */
+	encode(digest, s->state, MD5dlen);
+	if(s->malloced == 1)
+		free(s);
+	return nil;
+}
+
+/*
+ *	encodes input (u32int) into output (uchar). Assumes len is
+ *	a multiple of 4.
+ */
+static void
+encode(uchar *output, u32int *input, ulong len)
+{
+	u32int x;
+	uchar *e;
+
+	for(e = output + len; output < e;) {
+		x = *input++;
+		*output++ = x;
+		*output++ = x >> 8;
+		*output++ = x >> 16;
+		*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);
+}
--- /dev/null
+++ b/libsec/md5block.c
@@ -1,0 +1,267 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ *  rfc1321 requires that I include this.  The code is new.  The constants
+ *  all come from the rfc (hence the copyright).  We trade a table for the
+ *  macros in rfc.  The total size is a lot less. -- presotto
+ *
+ *	Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ *	rights reserved.
+ *
+ *	License to copy and use this software is granted provided that it
+ *	is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ *	Algorithm" in all material mentioning or referencing this software
+ *	or this function.
+ *
+ *	License is also granted to make and use derivative works provided
+ *	that such works are identified as "derived from the RSA Data
+ *	Security, Inc. MD5 Message-Digest Algorithm" in all material
+ *	mentioning or referencing the derived work.
+ *
+ *	RSA Data Security, Inc. makes no representations concerning either
+ *	the merchantability of this software or the suitability of this
+ *	software forany particular purpose. It is provided "as is"
+ *	without express or implied warranty of any kind.
+ *	These notices must be retained in any copies of any part of this
+ *	documentation and/or software.
+ */
+
+/*
+ *	Rotate ammounts used in the algorithm
+ */
+enum
+{
+	S11=	7,
+	S12=	12,
+	S13=	17,
+	S14=	22,
+
+	S21=	5,
+	S22=	9,
+	S23=	14,
+	S24=	20,
+
+	S31=	4,
+	S32=	11,
+	S33=	16,
+	S34=	23,
+
+	S41=	6,
+	S42=	10,
+	S43=	15,
+	S44=	21,
+};
+
+static u32int md5tab[] =
+{
+	/* round 1 */
+/*[0]*/	0xd76aa478,	
+	0xe8c7b756,	
+	0x242070db,	
+	0xc1bdceee,	
+	0xf57c0faf,	
+	0x4787c62a,	
+	0xa8304613,	
+	0xfd469501,	
+	0x698098d8,	
+	0x8b44f7af,	
+	0xffff5bb1,	
+	0x895cd7be,	
+	0x6b901122,	
+	0xfd987193,	
+	0xa679438e,	
+	0x49b40821,
+
+	/* round 2 */
+/*[16]*/0xf61e2562,	
+	0xc040b340,	
+	0x265e5a51,	
+	0xe9b6c7aa,	
+	0xd62f105d,	
+	 0x2441453,	
+	0xd8a1e681,	
+	0xe7d3fbc8,	
+	0x21e1cde6,	
+	0xc33707d6,	
+	0xf4d50d87,	
+	0x455a14ed,	
+	0xa9e3e905,	
+	0xfcefa3f8,	
+	0x676f02d9,	
+	0x8d2a4c8a,
+
+	/* round 3 */
+/*[32]*/0xfffa3942,	
+	0x8771f681,	
+	0x6d9d6122,	
+	0xfde5380c,	
+	0xa4beea44,	
+	0x4bdecfa9,	
+	0xf6bb4b60,	
+	0xbebfbc70,	
+	0x289b7ec6,	
+	0xeaa127fa,	
+	0xd4ef3085,	
+	 0x4881d05,	
+	0xd9d4d039,	
+	0xe6db99e5,	
+	0x1fa27cf8,	
+	0xc4ac5665,	
+
+	/* round 4 */
+/*[48]*/0xf4292244,	
+	0x432aff97,	
+	0xab9423a7,	
+	0xfc93a039,	
+	0x655b59c3,	
+	0x8f0ccc92,	
+	0xffeff47d,	
+	0x85845dd1,	
+	0x6fa87e4f,	
+	0xfe2ce6e0,	
+	0xa3014314,	
+	0x4e0811a1,	
+	0xf7537e82,	
+	0xbd3af235,	
+	0x2ad7d2bb,	
+	0xeb86d391,	
+};
+
+static void decode(u32int*, uchar*, ulong);
+extern void _md5block(uchar *p, ulong len, u32int *s);
+
+void
+_md5block(uchar *p, ulong len, u32int *s)
+{
+	u32int a, b, c, d, sh;
+	u32int *t;
+	uchar *end;
+	u32int x[16];
+
+	for(end = p+len; p < end; p += 64){
+		a = s[0];
+		b = s[1];
+		c = s[2];
+		d = s[3];
+
+		decode(x, p, 64);
+	
+		t = md5tab;
+		sh = 0;
+		for(; sh != 16; t += 4){
+			a += ((c ^ d) & b) ^ d;
+			a += x[sh] + t[0];
+			a = (a << S11) | (a >> (32 - S11));
+			a += b;
+
+			d += ((b ^ c) & a) ^ c;
+			d += x[sh + 1] + t[1];
+			d = (d << S12) | (d >> (32 - S12));
+			d += a;
+
+			c += ((a ^ b) & d) ^ b;
+			c += x[sh + 2] + t[2];
+			c = (c << S13) | (c >> (32 - S13));
+			c += d;
+
+			b += ((d ^ a) & c) ^ a;
+			b += x[sh + 3] + t[3];
+			b = (b << S14) | (b >> (32 - S14));
+			b += c;
+
+			sh += 4;
+		}
+		sh = 1;
+		for(; sh != 1+20*4; t += 4){
+			a += ((b ^ c) & d) ^ c;
+			a += x[sh & 0xf] + t[0];
+			a = (a << S21) | (a >> (32 - S21));
+			a += b;
+
+			d += ((a ^ b) & c) ^ b;
+			d += x[(sh + 5) & 0xf] + t[1];
+			d = (d << S22) | (d >> (32 - S22));
+			d += a;
+
+			c += ((d ^ a) & b) ^ a;
+			c += x[(sh + 10) & 0xf] + t[2];
+			c = (c << S23) | (c >> (32 - S23));
+			c += d;
+
+			b += ((c ^ d) & a) ^ d;
+			b += x[(sh + 15) & 0xf] + t[3];
+			b = (b << S24) | (b >> (32 - S24));
+			b += c;
+
+			sh += 20;
+		}
+		sh = 5;
+		for(; sh != 5+12*4; t += 4){
+			a += b ^ c ^ d;
+			a += x[sh & 0xf] + t[0];
+			a = (a << S31) | (a >> (32 - S31));
+			a += b;
+
+			d += a ^ b ^ c;
+			d += x[(sh + 3) & 0xf] + t[1];
+			d = (d << S32) | (d >> (32 - S32));
+			d += a;
+
+			c += d ^ a ^ b;
+			c += x[(sh + 6) & 0xf] + t[2];
+			c = (c << S33) | (c >> (32 - S33));
+			c += d;
+
+			b += c ^ d ^ a;
+			b += x[(sh + 9) & 0xf] + t[3];
+			b = (b << S34) | (b >> (32 - S34));
+			b += c;
+
+			sh += 12;
+		}
+		sh = 0;
+		for(; sh != 28*4; t += 4){
+			a += c ^ (b | ~d);
+			a += x[sh & 0xf] + t[0];
+			a = (a << S41) | (a >> (32 - S41));
+			a += b;
+
+			d += b ^ (a | ~c);
+			d += x[(sh + 7) & 0xf] + t[1];
+			d = (d << S42) | (d >> (32 - S42));
+			d += a;
+
+			c += a ^ (d | ~b);
+			c += x[(sh + 14) & 0xf] + t[2];
+			c = (c << S43) | (c >> (32 - S43));
+			c += d;
+
+			b += d ^ (c | ~a);
+			b += x[(sh + 21) & 0xf] + t[3];
+			b = (b << S44) | (b >> (32 - S44));
+			b += c;
+
+			sh += 28;
+		}
+
+		s[0] += a;
+		s[1] += b;
+		s[2] += c;
+		s[3] += d;
+	}
+}
+
+/*
+ *	decodes input (uchar) into output (u32int). Assumes len is
+ *	a multiple of 4.
+ */
+static void
+decode(u32int *output, uchar *input, ulong len)
+{
+	uchar *e;
+
+	for(e = input+len; input < e; input += 4)
+		*output++ = input[0] | (input[1] << 8) |
+			(input[2] << 16) | (input[3] << 24);
+}
--- /dev/null
+++ b/libsec/md5pickle.c
@@ -1,0 +1,39 @@
+#include "os.h"
+#include <libsec.h>
+
+char*
+md5pickle(MD5state *s)
+{
+	char *p;
+	int m, n;
+
+	m = 17+4*9+4*((s->blen+3)/3 + 1);
+	p = malloc(m);
+	if(p == nil)
+		return p;
+	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);
+	return p;
+}
+
+MD5state*
+md5unpickle(char *p)
+{
+	MD5state *s;
+
+	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);
+	s->state[3] = strtoul(p, &p, 16);
+	s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p));
+	s->malloced = 1;
+	s->seeded = 1;
+	return s;
+}
--- /dev/null
+++ b/libsec/nfastrand.c
@@ -1,0 +1,22 @@
+#include "os.h"
+#include <libsec.h>
+
+#define Maxrand	((1UL<<31)-1)
+
+ulong
+nfastrand(ulong n)
+{
+	ulong m, r;
+	
+	/*
+	 * set m to the maximum multiple of n <= 2^31-1
+	 * so we want a random number < m.
+	 */
+	if(n > Maxrand)
+		sysfatal("nfastrand: n too large");
+
+	m = Maxrand - Maxrand % n;
+	while((r = fastrand()) >= m)
+		;
+	return r%n;
+}
--- /dev/null
+++ b/libsec/os.h
@@ -1,0 +1,2 @@
+#include <u.h>
+#include <libc.h>
--- /dev/null
+++ b/libsec/p_sha256.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+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);
+	}
+}
\ No newline at end of file
--- /dev/null
+++ b/libsec/pbkdf2.c
@@ -1,0 +1,34 @@
+#include "os.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,195 @@
+#include "os.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;
+}
--- /dev/null
+++ b/libsec/primetest.c
@@ -1,0 +1,41 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+void
+main(void)
+{
+	mpint *z = mpnew(0);
+	mpint *p = mpnew(0);
+	mpint *q = mpnew(0);
+	mpint *nine = mpnew(0);
+
+	fmtinstall('B', mpfmt);
+	strtomp("2492491", nil, 16, z);	// 38347921 = x*y = (2**28-9)/7, 
+				//    an example of 3**(n-1)=1 mod n
+	strtomp("15662C00E811", nil, 16, p);// 23528569104401, a prime
+	uitomp(9, nine);
+
+	if(probably_prime(z, 5) == 1)
+		fprint(2, "tricked primality test\n");
+	if(probably_prime(nine, 5) == 1)
+		fprint(2, "9 passed primality test!\n");
+	if(probably_prime(p, 25) == 1)
+		fprint(2, "ok\n");
+
+	DSAprimes(q, p, nil);
+	print("q=%B\np=%B\n", q, p);
+
+	exits(0);
+}
+
+// example output, checked with Maple:
+// seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E
+// q=E0F0EF284E10796C5A2A511E94748BA03C795C13
+//  = 1284186945063585093695748280224501481698995297299
+// p=C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBDB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A863A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D93D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D
+//  = 137715385439333164327584575331308277462546592976152006175830654712456008630139443747529133857837818585400418619916530061955288983751958831927807888408309879880101870216437711393638413509484569804814373511469405934988856674935304074081350525593807908358867354528898618574659752879015380013845760006721861915693
+// r=DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D21C4656848614D888A4
+//  = 107239359478548771267308764204625458348785444483302647285245969203446101233421655396874997253111222983406676955642093641709149748793954493558324738441197139556917622937892491175016280660608595599724194374948056515856812347094848443460715881455884639869144172708
+// g=2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D2327173444ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1
+//  = 33081848392740465806285326014906437543653045153885419334085917570615301913274531387168723847139029827598735376746057461417880810924280288611116213062512408829164220104555543445909528701551198146080221790002337033997295756585193926863581671466708482411159477816144226847280417522524922667065714073338662508017
--- /dev/null
+++ b/libsec/prng.c
@@ -1,0 +1,14 @@
+#include "os.h"
+#include <libsec.h>
+
+//
+//  just use the libc prng to fill a buffer
+//
+void
+prng(uchar *p, int n)
+{
+	uchar *e;
+
+	for(e = p+n; p < e; p++)
+		*p = rand();
+}
--- /dev/null
+++ b/libsec/probably_prime.c
@@ -1,0 +1,91 @@
+#include "os.h"
+#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
+ */
+int
+probably_prime(mpint *n, int nrep)
+{
+	int j, k, rep, nbits, isprime;
+	mpint *nm1, *q, *x, *y, *r;
+
+	if(n->sign < 0)
+		sysfatal("negative prime candidate");
+
+	if(nrep <= 0)
+		nrep = 18;
+
+	k = mptoi(n);
+	if(k < 2)		/* 1 is not prime */
+		return 0;
+	if(k == 2 || k == 3)	/* 2, 3 is prime */
+		return 1;
+	if((n->p[0] & 1) == 0)	/* even is not prime */
+		return 0;
+
+	/* test against small prime numbers */
+	if(smallprimetest(n) < 0)
+		return 0;
+
+	/* fermat test, 2^n mod n == 2 if p is prime */
+	x = uitomp(2, nil);
+	y = mpnew(0);
+	mpexp(x, n, n, y);
+	k = mptoi(y);
+	if(k != 2){
+		mpfree(x);
+		mpfree(y);
+		return 0;
+	}
+
+	nbits = mpsignif(n);
+	nm1 = mpnew(nbits);
+	mpsub(n, mpone, nm1);	/* nm1 = n - 1 */
+	k = mplowbits0(nm1);
+	q = mpnew(0);
+	mpright(nm1, k, q);	/* q = (n-1)/2**k */
+
+	for(rep = 0; rep < nrep; rep++){
+		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 */
+		mpexp(x, q, n, y);
+
+		if(mpcmp(y, mpone) == 0 || mpcmp(y, nm1) == 0)
+		 	continue;
+
+		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 = 1;
+done:
+	mpfree(y);
+	mpfree(x);
+	mpfree(q);
+	mpfree(nm1);
+	return isprime;
+}
--- /dev/null
+++ b/libsec/rc4.c
@@ -1,0 +1,104 @@
+#include "os.h"
+#include <libsec.h>
+
+void
+setupRC4state(RC4state *key, uchar *start, int n)
+{
+	int t;
+	int index2;
+	uchar *state;
+	uchar *p, *e, *sp, *se;
+
+	state = key->state;
+	se = &state[256];
+	for(sp = state; sp < se; sp++)
+		*sp = sp - state;
+
+	key->x = 0;
+	key->y = 0;
+	index2 = 0;
+	e = start + n;
+	p = start;
+	for(sp = state; sp < se; sp++)
+	{
+		t = *sp;
+		index2 = (*p + t + index2) & 255;
+		*sp = state[index2];
+		state[index2] = t;
+		if(++p >= e)
+			p = start;
+	}
+}
+
+void
+rc4(RC4state *key, uchar *p, int len)
+{
+	int tx, ty;
+	int x, y;
+	uchar *state;
+	uchar *e;
+
+	x = key->x;
+	y = key->y;
+	state = &key->state[0];
+	for(e = p + len; p < e; p++)
+	{
+		x = (x+1)&255;
+		tx = state[x];
+		y = (y+tx)&255;
+		ty = state[y];
+		state[x] = ty;
+		state[y] = tx;
+		*p ^= state[(tx+ty)&255];
+	}
+	key->x = x;
+	key->y = y;
+}
+
+void
+rc4skip(RC4state *key, int len)
+{
+	int tx, ty;
+	int x, y;
+	uchar *state;
+	int i;
+
+	x = key->x;
+	y = key->y;
+	state = &key->state[0];
+	for(i=0; i<len; i++)
+	{
+		x = (x+1)&255;
+		tx = state[x];
+		y = (y+tx)&255;
+		ty = state[y];
+		state[x] = ty;
+		state[y] = tx;
+	}
+	key->x = x;
+	key->y = y;
+}
+
+void
+rc4back(RC4state *key, int len)
+{
+	int tx, ty;
+	int x, y;
+	uchar *state;
+	int i;
+
+	x = key->x;
+	y = key->y;
+	state = &key->state[0];
+	for(i=0; i<len; i++)
+	{
+		ty = state[x];
+		tx = state[y];
+		state[y] = ty;
+		state[x] = tx;
+		y = (y-tx)&255;
+		x = (x-1)&255;
+	}
+	key->x = x;
+	key->y = y;
+}
--- /dev/null
+++ b/libsec/readcert.c
@@ -1,0 +1,63 @@
+#include "os.h"
+#include <libsec.h>
+
+static char*
+readfile(char *name)
+{
+	int fd;
+	char *s;
+	Dir *d;
+
+	fd = open(name, OREAD);
+	if(fd < 0)
+		return 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);
+		free(d);
+		close(fd);
+		return nil;
+	}
+	close(fd);
+	s[d->length] = '\0';
+	free(d);
+	return s;
+}
+
+uchar*
+readcert(char *filename, int *pcertlen)
+{
+	char *pem;
+	uchar *binary;
+
+	pem = readfile(filename);
+	if(pem == nil){
+		werrstr("can't read %s: %r", filename);
+		return nil;
+	}
+	binary = decodePEM(pem, "CERTIFICATE", pcertlen, nil);
+	free(pem);
+	if(binary == nil){
+		werrstr("can't parse %s", filename);
+		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;
+
+}
--- /dev/null
+++ b/libsec/rsaalloc.c
@@ -1,0 +1,52 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+RSApub*
+rsapuballoc(void)
+{
+	RSApub *rsa;
+
+	rsa = mallocz(sizeof(*rsa), 1);
+	if(rsa == nil)
+		sysfatal("rsapuballoc");
+	return rsa;
+}
+
+void
+rsapubfree(RSApub *rsa)
+{
+	if(rsa == nil)
+		return;
+	mpfree(rsa->ek);
+	mpfree(rsa->n);
+	free(rsa);
+}
+
+
+RSApriv*
+rsaprivalloc(void)
+{
+	RSApriv *rsa;
+
+	rsa = mallocz(sizeof(*rsa), 1);
+	if(rsa == nil)
+		sysfatal("rsaprivalloc");
+	return rsa;
+}
+
+void
+rsaprivfree(RSApriv *rsa)
+{
+	if(rsa == nil)
+		return;
+	mpfree(rsa->pub.ek);
+	mpfree(rsa->pub.n);
+	mpfree(rsa->dk);
+	mpfree(rsa->p);
+	mpfree(rsa->q);
+	mpfree(rsa->kp);
+	mpfree(rsa->kq);
+	mpfree(rsa->c2);
+	free(rsa);
+}
--- /dev/null
+++ b/libsec/rsadecrypt.c
@@ -1,0 +1,37 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+// decrypt rsa using garner's algorithm for the chinese remainder theorem
+//	seminumerical algorithms, knuth, pp 253-254
+//	applied cryptography, menezes et al, pg 612
+mpint*
+rsadecrypt(RSApriv *rsa, mpint *in, mpint *out)
+{
+	mpint *v1, *v2;
+
+	if(out == nil)
+		out = mpnew(0);
+
+	// convert in to modular representation
+	v1 = mpnew(0);
+	mpmod(in, rsa->p, v1);
+	v2 = mpnew(0);
+	mpmod(in, rsa->q, v2);
+
+	// exponentiate the modular rep
+	mpexp(v1, rsa->kp, rsa->p, v1);
+	mpexp(v2, rsa->kq, rsa->q, v2);
+	
+	// out = v1 + p*((v2-v1)*c2 mod q)
+	mpsub(v2, v1, v2);
+	mpmul(v2, rsa->c2, v2);
+	mpmod(v2, rsa->q, v2);
+	mpmul(v2, rsa->p, out);
+	mpadd(v1, out, out);
+
+	mpfree(v1);
+	mpfree(v2);
+
+	return out;
+}
--- /dev/null
+++ b/libsec/rsaencrypt.c
@@ -1,0 +1,12 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+mpint*
+rsaencrypt(RSApub *rsa, mpint *in, mpint *out)
+{
+	if(out == nil)
+		out = mpnew(0);
+	mpexp(in, rsa->ek, rsa->n, out);
+	return out;
+}
--- /dev/null
+++ b/libsec/rsafill.c
@@ -1,0 +1,61 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+RSApriv*
+rsafill(mpint *n, mpint *e, mpint *d, mpint *p, mpint *q)
+{
+	mpint *c2, *kq, *kp, *x;
+	RSApriv *rsa;
+
+	// make sure we're not being hoodwinked
+	if(!probably_prime(p, 10) || !probably_prime(q, 10)){
+		werrstr("rsafill: p or q not prime");
+		return nil;
+	}
+	x = mpnew(0);
+	mpmul(p, q, x);
+	if(mpcmp(n, x) != 0){
+		werrstr("rsafill: n != p*q");
+		mpfree(x);
+		return nil;
+	}
+	c2 = mpnew(0);
+	mpsub(p, mpone, c2);
+	mpsub(q, mpone, x);
+	mpmul(c2, x, x);
+	mpmul(e, d, c2);
+	mpmod(c2, x, x);
+	if(mpcmp(x, mpone) != 0){
+		werrstr("rsafill: e*d != 1 mod (p-1)*(q-1)");
+		mpfree(x);
+		mpfree(c2);
+		return nil;
+	}
+
+	// compute chinese remainder coefficient
+	mpinvert(p, q, c2);
+
+	// for crt a**k mod p == (a**(k mod p-1)) mod p
+	kq = mpnew(0);
+	kp = mpnew(0);
+	mpsub(p, mpone, x);
+	mpmod(d, x, kp);
+	mpsub(q, mpone, x);
+	mpmod(d, x, kq);
+
+	rsa = rsaprivalloc();
+	rsa->pub.ek = mpcopy(e);
+	rsa->pub.n = mpcopy(n);
+	rsa->dk = mpcopy(d);
+	rsa->kp = kp;
+	rsa->kq = kq;
+	rsa->p = mpcopy(p);
+	rsa->q = mpcopy(q);
+	rsa->c2 = c2;
+
+	mpfree(x);
+
+	return rsa;
+}
+
--- /dev/null
+++ b/libsec/rsagen.c
@@ -1,0 +1,76 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+RSApriv*
+rsagen(int nlen, int elen, int rounds)
+{
+	mpint *p, *q, *e, *d, *phi, *n, *t1, *t2, *kp, *kq, *c2;
+	RSApriv *rsa;
+
+	p = mpnew(nlen/2);
+	q = mpnew(nlen/2);
+	n = mpnew(nlen);
+	e = mpnew(elen);
+	d = mpnew(0);
+	phi = mpnew(nlen);
+
+	// create the prime factors and euclid's function
+	genprime(p, nlen/2, rounds);
+	genprime(q, nlen - mpsignif(p) + 1, rounds);
+	mpmul(p, q, n);
+	mpsub(p, mpone, e);
+	mpsub(q, mpone, d);
+	mpmul(e, d, phi);
+
+	// find an e relatively prime to phi
+	t1 = mpnew(0);
+	t2 = mpnew(0);
+	if(elen == 0)
+		itomp(65537, e);
+	else {
+		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, t1, d, t2);
+		if(mpcmp(t1, mpone) == 0)
+			break;
+		mpadd(mpone, e, e);
+	}
+	if(d->sign < 0)
+		mpadd(phi, d, d);
+	mpfree(t1);
+	mpfree(t2);
+
+	// compute chinese remainder coefficient
+	c2 = mpnew(0);
+	mpinvert(p, q, c2);
+
+	// for crt a**k mod p == (a**(k mod p-1)) mod p
+	kq = mpnew(0);
+	kp = mpnew(0);
+	mpsub(p, mpone, phi);
+	mpmod(d, phi, kp);
+	mpsub(q, mpone, phi);
+	mpmod(d, phi, kq);
+
+	rsa = rsaprivalloc();
+	rsa->pub.ek = e;
+	rsa->pub.n = n;
+	rsa->dk = d;
+	rsa->kp = kp;
+	rsa->kq = kq;
+	rsa->p = p;
+	rsa->q = q;
+	rsa->c2 = c2;
+
+	mpfree(phi);
+
+	return rsa;
+}
--- /dev/null
+++ b/libsec/rsaprivtopub.c
@@ -1,0 +1,16 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+RSApub*
+rsaprivtopub(RSApriv *priv)
+{
+	RSApub *pub;
+
+	pub = rsapuballoc();
+	if(pub == nil)
+		return nil;
+	pub->n = mpcopy(priv->pub.n);
+	pub->ek = mpcopy(priv->pub.ek);
+	return pub;
+}
--- /dev/null
+++ b/libsec/rsatest.c
@@ -1,0 +1,56 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+#include <bio.h>
+
+void
+main(void)
+{
+	int n;
+	vlong start;
+	char *p;
+	uchar buf[4096];
+	Biobuf b;
+	RSApriv *rsa;
+	mpint *clr, *enc, *clr2;
+
+	fmtinstall('B', mpfmt);
+
+	rsa = rsagen(1024, 16, 0);
+	if(rsa == nil)
+		sysfatal("rsagen");
+	Binit(&b, 0, OREAD);
+	clr = mpnew(0);
+	clr2 = mpnew(0);
+	enc = mpnew(0);
+
+	strtomp("123456789abcdef123456789abcdef123456789abcdef123456789abcdef", nil, 16, clr);
+	rsaencrypt(&rsa->pub, clr, enc);
+	
+	start = nsec();
+	for(n = 0; n < 10; n++)
+		rsadecrypt(rsa, enc, clr);
+	print("%lld\n", nsec()-start);
+
+	start = nsec();
+	for(n = 0; n < 10; n++)
+		mpexp(enc, rsa->dk, rsa->pub.n, clr2);
+	print("%lld\n", nsec()-start);
+
+	if(mpcmp(clr, clr2) != 0)
+		print("%B != %B\n", clr, clr2);
+	
+	print("> ");
+	while(p = Brdline(&b, '\n')){
+		n = Blinelen(&b);
+		letomp((uchar*)p, n, clr);
+		print("clr %B\n", clr);
+		rsaencrypt(&rsa->pub, clr, enc);
+		print("enc %B\n", enc);
+		rsadecrypt(rsa, enc, clr);
+		print("clr %B\n", clr);
+		n = mptole(clr, buf, sizeof(buf), nil);
+		write(1, buf, n);
+		print("> ");
+	}
+}
--- /dev/null
+++ b/libsec/salsa.c
@@ -1,0 +1,308 @@
+#include "os.h"
+#include <libsec.h>
+
+/* little-endian data order */
+#define	GET4(p)		((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define	PUT4(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+
+#define ROTATE(v,c) (t = v, (u32int)(t << (c)) | (t >> (32 - (c))))
+
+#define ENCRYPT(s, x, y, d) {\
+	u32int v; \
+	v = GET4(s); \
+	v ^= (x)+(y); \
+	PUT4(d, 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)	/* hsalsa, xsalsa */
+		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->xkey[0] = s->input[1];
+	s->xkey[1] = s->input[2];
+	s->xkey[2] = s->input[3];
+	s->xkey[3] = s->input[4];
+	s->xkey[4] = s->input[11];
+	s->xkey[5] = s->input[12];
+	s->xkey[6] = s->input[13];
+	s->xkey[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
+dorounds(u32int x[16], int rounds)
+{
+	u32int t;
+
+	for(; rounds > 0; rounds -= 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);
+	}
+}
+
+static void
+hsalsablock(uchar h[32], Salsastate *s)
+{
+	u32int x[16];
+
+	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];
+
+	dorounds(x, s->rounds);
+
+	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 with 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->xkey[0];
+		s->input[2] = s->xkey[1];
+		s->input[3] = s->xkey[2];
+		s->input[4] = s->xkey[3];
+		s->input[11] = s->xkey[4];
+		s->input[12] = s->xkey[5];
+		s->input[13] = s->xkey[6];
+		s->input[14] = s->xkey[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[16];
+	int i;
+
+	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];
+
+	dorounds(x, s->rounds);
+
+	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;
+	}
+
+	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
+salsa_core(u32int in[16], u32int out[16], int rounds)
+{
+	u32int x[16];
+
+	x[0] = in[0];
+	x[1] = in[1];
+	x[2] = in[2];
+	x[3] = in[3];
+	x[4] = in[4];
+	x[5] = in[5];
+	x[6] = in[6];
+	x[7] = in[7];
+	x[8] = in[8];
+	x[9] = in[9];
+	x[10] = in[10];
+	x[11] = in[11];
+	x[12] = in[12];
+	x[13] = in[13];
+	x[14] = in[14];
+	x[15] = in[15];
+
+	dorounds(x, rounds);
+
+	out[0] = x[0] + in[0];
+	out[1] = x[1] + in[1];
+	out[2] = x[2] + in[2];
+	out[3] = x[3] + in[3];
+	out[4] = x[4] + in[4];
+	out[5] = x[5] + in[5];
+	out[6] = x[6] + in[6];
+	out[7] = x[7] + in[7];
+	out[8] = x[8] + in[8];
+	out[9] = x[9] + in[9];
+	out[10] = x[10] + in[10];
+	out[11] = x[11] + in[11];
+	out[12] = x[12] + in[12];
+	out[13] = x[13] + in[13];
+	out[14] = x[14] + in[14];
+	out[15] = x[15] + in[15];
+}
+
+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/scrypt.c
@@ -1,0 +1,119 @@
+#include "os.h"
+#include <libsec.h>
+
+#define movw(w, S, D)	memmove(D, S, (w)*4)
+
+static void
+xorw(ulong w, u32int *S, u32int *D)
+{
+	for(w /= 8; w; w--, D += 8, S += 8){
+		D[0] ^= S[0];
+		D[1] ^= S[1];
+		D[2] ^= S[2];
+		D[3] ^= S[3];
+		D[4] ^= S[4];
+		D[5] ^= S[5];
+		D[6] ^= S[6];
+		D[7] ^= S[7];
+	}
+}
+
+static void
+scryptBlockMix(ulong R, u32int *B, u32int *Y)
+{
+	u32int X[16];
+	ulong i;
+
+	R *= 2;
+	movw(16, &B[(R-1)*16], X);
+	for(i = 0; i < R; i += 2){
+		xorw(16, &B[i*16], X);
+		salsa_core(X, X, 8);
+		movw(16, X, &Y[i*8]);
+
+		xorw(16, &B[(i+1)*16], X);
+		salsa_core(X, X, 8);
+		movw(16, X, &Y[i*8 + R*8]);
+	}
+}
+
+static void
+scryptROMix(ulong R, ulong N, u32int *V, u32int *X, uchar *B)
+{
+	ulong w, i, d;
+	u32int *Y;
+
+	w = R*32;
+	for(i=0; i<w; i++, B+=4)
+		X[i] = B[0] | (B[1]<<8) | (B[2]<<16) | (B[3]<<24);
+
+	Y = &X[w];
+	for(i=0; i<N; i += 2){
+		movw(w, X, &V[i*w]);
+		scryptBlockMix(R, X, Y);
+
+		movw(w, Y, &V[(i+1)*w]);
+		scryptBlockMix(R, Y, X);
+	}
+	for(i=0; i<N; i += 2){
+		xorw(w, &V[(X[w-16] & (N-1))*w], X);
+		scryptBlockMix(R, X, Y);
+
+		xorw(w, &V[(Y[w-16] & (N-1))*w], Y);
+		scryptBlockMix(R, Y, X);
+	}
+
+	B -= w*4;
+	for(i=0; i<w; i++, B+=4)
+		d = X[i], B[0]=d, B[1]=d>>8, B[2]=d>>16, B[3]=d>>24;
+}
+
+char*
+scrypt(p, plen, s, slen, N, R, P, d, dlen)
+	ulong plen, slen, dlen, N, R, P;
+	uchar *p, *s, *d;
+{
+	static char oom[] = "out of memory";
+
+	ulong rb, i;
+	u32int *V, *X;
+	uchar *B;
+
+	if(P < 1)
+		return "invalid parallelization parameter P";
+	if(R < 1 || R >= (1UL<<(31-7))/P)
+		return "invalid block size parameter R";
+	if(N < 2 || (N & (N-1)) != 0 || N >= (1UL<<(31-7))/R)
+		return "invalid cpu/memory cost parameter N";
+
+	rb = R<<7;
+	if((B = malloc(P*rb)) == nil)
+		return oom;
+	if((V = malloc(N*rb)) == nil){
+		free(B);
+		return oom;
+	}
+	if((X = malloc(2*rb)) == nil){
+		free(V);
+		free(B);
+		return oom;
+	}
+
+	pbkdf2_x(p, plen, s, slen, 1, B, P*rb, hmac_sha2_256, SHA2_256dlen);
+
+	for(i=0; i<P; i++)
+		scryptROMix(R, N, V, X, &B[i*rb]);
+
+	memset(X, 0, 2*rb);
+	free(X);
+
+	memset(V, 0, N*rb);
+	free(V);
+
+	pbkdf2_x(p, plen, B, P*rb, 1, d, dlen, hmac_sha2_256, SHA2_256dlen);
+
+	memset(B, 0, P*rb);
+	free(B);
+
+	return nil;
+}
--- /dev/null
+++ b/libsec/secp256k1.c
@@ -1,0 +1,11 @@
+#include "os.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,12 @@
+#include "os.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);
+	}
--- /dev/null
+++ b/libsec/secp384r1.c
@@ -1,0 +1,12 @@
+#include "os.h"
+#include <mp.h>
+void secp384r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h){
+	strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", nil, 16, p);
+	uitomp(3UL, a);
+	mpsub(p, a, a);
+	strtomp("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", nil, 16, b);
+	strtomp("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", nil, 16, x);
+	strtomp("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", nil, 16, y);
+	strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", nil, 16, n);
+	mpassign(mpone, h);
+	}
--- /dev/null
+++ b/libsec/sha1.c
@@ -1,0 +1,134 @@
+#include "os.h"
+#include <libsec.h>
+
+static void encode(uchar*, u32int*, ulong);
+
+extern void _sha1block(uchar*, ulong, u32int*);
+
+/*
+ *  we require len to be a multiple of 64 for all but
+ *  the last call.  There must be room in the input buffer
+ *  to pad.
+ */
+SHA1state*
+sha1(uchar *p, ulong len, uchar *digest, SHA1state *s)
+{
+	uchar buf[128];
+	u32int x[16];
+	int i;
+	uchar *e;
+
+	if(s == nil){
+		s = malloc(sizeof(*s));
+		if(s == nil)
+			return nil;
+		memset(s, 0, sizeof(*s));
+		s->malloced = 1;
+	}
+
+	if(s->seeded == 0){
+		/* seed the state, these constants would look nicer big-endian */
+		s->state[0] = 0x67452301;
+		s->state[1] = 0xefcdab89;
+		s->state[2] = 0x98badcfe;
+		s->state[3] = 0x10325476;
+		s->state[4] = 0xc3d2e1f0;
+		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){
+			_sha1block(s->buf, s->blen, s->state);
+			s->len += s->blen;
+			s->blen = 0;
+		}
+	}
+
+	/* do 64 byte blocks */
+	i = len & ~0x3f;
+	if(i){
+		_sha1block(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;
+	encode(p+len, x, 8);
+
+	/* digest the last part */
+	_sha1block(p, len+8, s->state);
+	s->len += len+8;
+
+	/* return result and free state */
+	encode(digest, s->state, SHA1dlen);
+	if(s->malloced == 1)
+		free(s);
+	return nil;
+}
+
+/*
+ *	encodes input (ulong) into output (uchar). Assumes len is
+ *	a multiple of 4.
+ */
+static void
+encode(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_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/sha1block.c
@@ -1,0 +1,137 @@
+#include "os.h"
+
+#define ROTL(x,n)	(((x)<<n)|((x)>>(32-n)))
+
+#define F0(x,y,z)	(0x5a827999 + ((z) ^ ((x) & ((y) ^ (z)))))
+#define F1(x,y,z)	(0x6ed9eba1 + ((x) ^ (y) ^ (z)))
+#define F2(x,y,z)	(0x8f1bbcdc + (((x) & (y)) | (((x) | (y)) & (z))))
+#define F3(x,y,z)	(0xca62c1d6 + ((x) ^ (y) ^ (z)))
+
+void
+_sha1block(uchar *p, ulong len, u32int *s)
+{
+	u32int w[16], a, b, c, d, e;
+	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];
+
+#define STEP(a,b,c,d,e,f,i) \
+	if(i < 16) {\
+		w[i] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3]; \
+		p += 4; \
+	} else { \
+		u32int x = w[(i-3)&15] ^ w[(i-8)&15] ^ w[(i-14)&15] ^ w[(i-16)&15]; \
+		w[i&15] = ROTL(x, 1); \
+	} \
+	e += ROTL(a, 5) + w[i&15] + f(b,c,d); \
+	b = ROTL(b, 30);
+
+		STEP(a,b,c,d,e,F0,0);
+		STEP(e,a,b,c,d,F0,1);
+		STEP(d,e,a,b,c,F0,2);
+		STEP(c,d,e,a,b,F0,3);
+		STEP(b,c,d,e,a,F0,4);
+	
+		STEP(a,b,c,d,e,F0,5);
+		STEP(e,a,b,c,d,F0,6);
+		STEP(d,e,a,b,c,F0,7);
+		STEP(c,d,e,a,b,F0,8);
+		STEP(b,c,d,e,a,F0,9);
+	
+		STEP(a,b,c,d,e,F0,10);
+		STEP(e,a,b,c,d,F0,11);
+		STEP(d,e,a,b,c,F0,12);
+		STEP(c,d,e,a,b,F0,13);
+		STEP(b,c,d,e,a,F0,14);
+	
+		STEP(a,b,c,d,e,F0,15);
+		STEP(e,a,b,c,d,F0,16);
+		STEP(d,e,a,b,c,F0,17);
+		STEP(c,d,e,a,b,F0,18);
+		STEP(b,c,d,e,a,F0,19);
+	
+		STEP(a,b,c,d,e,F1,20);
+		STEP(e,a,b,c,d,F1,21);
+		STEP(d,e,a,b,c,F1,22);
+		STEP(c,d,e,a,b,F1,23);
+		STEP(b,c,d,e,a,F1,24);
+	
+		STEP(a,b,c,d,e,F1,25);
+		STEP(e,a,b,c,d,F1,26);
+		STEP(d,e,a,b,c,F1,27);
+		STEP(c,d,e,a,b,F1,28);
+		STEP(b,c,d,e,a,F1,29);
+	
+		STEP(a,b,c,d,e,F1,30);
+		STEP(e,a,b,c,d,F1,31);
+		STEP(d,e,a,b,c,F1,32);
+		STEP(c,d,e,a,b,F1,33);
+		STEP(b,c,d,e,a,F1,34);
+	
+		STEP(a,b,c,d,e,F1,35);
+		STEP(e,a,b,c,d,F1,36);
+		STEP(d,e,a,b,c,F1,37);
+		STEP(c,d,e,a,b,F1,38);
+		STEP(b,c,d,e,a,F1,39);
+	
+		STEP(a,b,c,d,e,F2,40);
+		STEP(e,a,b,c,d,F2,41);
+		STEP(d,e,a,b,c,F2,42);
+		STEP(c,d,e,a,b,F2,43);
+		STEP(b,c,d,e,a,F2,44);
+	
+		STEP(a,b,c,d,e,F2,45);
+		STEP(e,a,b,c,d,F2,46);
+		STEP(d,e,a,b,c,F2,47);
+		STEP(c,d,e,a,b,F2,48);
+		STEP(b,c,d,e,a,F2,49);
+	
+		STEP(a,b,c,d,e,F2,50);
+		STEP(e,a,b,c,d,F2,51);
+		STEP(d,e,a,b,c,F2,52);
+		STEP(c,d,e,a,b,F2,53);
+		STEP(b,c,d,e,a,F2,54);
+	
+		STEP(a,b,c,d,e,F2,55);
+		STEP(e,a,b,c,d,F2,56);
+		STEP(d,e,a,b,c,F2,57);
+		STEP(c,d,e,a,b,F2,58);
+		STEP(b,c,d,e,a,F2,59);
+	
+		STEP(a,b,c,d,e,F3,60);
+		STEP(e,a,b,c,d,F3,61);
+		STEP(d,e,a,b,c,F3,62);
+		STEP(c,d,e,a,b,F3,63);
+		STEP(b,c,d,e,a,F3,64);
+	
+		STEP(a,b,c,d,e,F3,65);
+		STEP(e,a,b,c,d,F3,66);
+		STEP(d,e,a,b,c,F3,67);
+		STEP(c,d,e,a,b,F3,68);
+		STEP(b,c,d,e,a,F3,69);
+	
+		STEP(a,b,c,d,e,F3,70);
+		STEP(e,a,b,c,d,F3,71);
+		STEP(d,e,a,b,c,F3,72);
+		STEP(c,d,e,a,b,F3,73);
+		STEP(b,c,d,e,a,F3,74);
+	
+		STEP(a,b,c,d,e,F3,75);
+		STEP(e,a,b,c,d,F3,76);
+		STEP(d,e,a,b,c,F3,77);
+		STEP(c,d,e,a,b,F3,78);
+		STEP(b,c,d,e,a,F3,79);
+
+		s[0] += a;
+		s[1] += b;
+		s[2] += c;
+		s[3] += d;
+		s[4] += e;
+	}
+}
--- /dev/null
+++ b/libsec/sha1pickle.c
@@ -1,0 +1,38 @@
+#include "os.h"
+#include <libsec.h>
+
+char*
+sha1pickle(SHA1state *s)
+{
+	char *p;
+	int m, n;
+
+	m = 5*9+4*((s->blen+3)/3);
+	p = malloc(m);
+	if(p == nil)
+		return p;
+	n = sprint(p, "%8.8ux %8.8ux %8.8ux %8.8ux %8.8ux ",
+		s->state[0], s->state[1], s->state[2],
+		s->state[3], s->state[4]);
+	enc64(p+n, m-n, s->buf, s->blen);
+	return p;
+}
+
+SHA1state*
+sha1unpickle(char *p)
+{
+	SHA1state *s;
+
+	s = malloc(sizeof(*s));
+	if(s == nil)
+		return nil;
+	s->state[0] = strtoul(p, &p, 16);
+	s->state[1] = strtoul(p, &p, 16);
+	s->state[2] = strtoul(p, &p, 16);
+	s->state[3] = strtoul(p, &p, 16);
+	s->state[4] = strtoul(p, &p, 16);
+	s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p));
+	s->malloced = 1;
+	s->seeded = 1;
+	return s;
+}
--- /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,176 @@
+/*
+ * sha2_512 block cipher - unrolled version
+ *
+ *   note: the following upper and lower case macro names are distinct
+ *	   and reflect the functions defined in FIPS pub. 180-2.
+ */
+
+#include "os.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)	((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x,y,z)	(((x) | (y)) & ((z) | ((x) & (y))))
+
+/*
+ * 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 w[16], a, b, c, d, e, f, g, h;
+	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];
+
+#define STEP(a,b,c,d,e,f,g,h,i) \
+	if(i < 16) { \
+		w[i] = 	(u64int)(p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3])<<32 | \
+			(p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7]); \
+		p += 8; \
+	} else { \
+		u64int s0, s1; \
+		s1 = sigma1(w[(i-2)&15]); \
+		s0 = sigma0(w[(i-15)&15]); \
+		w[i&15] += s1 + w[(i-7)&15] + s0; \
+	} \
+	h += SIGMA1(e) + Ch(e,f,g) + K512[i] + w[i&15]; \
+	d += h; \
+	h += SIGMA0(a) + Maj(a,b,c);
+
+		STEP(a,b,c,d,e,f,g,h,0);
+		STEP(h,a,b,c,d,e,f,g,1);
+		STEP(g,h,a,b,c,d,e,f,2);
+		STEP(f,g,h,a,b,c,d,e,3);
+		STEP(e,f,g,h,a,b,c,d,4);
+		STEP(d,e,f,g,h,a,b,c,5);
+		STEP(c,d,e,f,g,h,a,b,6);
+		STEP(b,c,d,e,f,g,h,a,7);
+
+		STEP(a,b,c,d,e,f,g,h,8);
+		STEP(h,a,b,c,d,e,f,g,9);
+		STEP(g,h,a,b,c,d,e,f,10);
+		STEP(f,g,h,a,b,c,d,e,11);
+		STEP(e,f,g,h,a,b,c,d,12);
+		STEP(d,e,f,g,h,a,b,c,13);
+		STEP(c,d,e,f,g,h,a,b,14);
+		STEP(b,c,d,e,f,g,h,a,15);
+
+		STEP(a,b,c,d,e,f,g,h,16);
+		STEP(h,a,b,c,d,e,f,g,17);
+		STEP(g,h,a,b,c,d,e,f,18);
+		STEP(f,g,h,a,b,c,d,e,19);
+		STEP(e,f,g,h,a,b,c,d,20);
+		STEP(d,e,f,g,h,a,b,c,21);
+		STEP(c,d,e,f,g,h,a,b,22);
+		STEP(b,c,d,e,f,g,h,a,23);
+
+		STEP(a,b,c,d,e,f,g,h,24);
+		STEP(h,a,b,c,d,e,f,g,25);
+		STEP(g,h,a,b,c,d,e,f,26);
+		STEP(f,g,h,a,b,c,d,e,27);
+		STEP(e,f,g,h,a,b,c,d,28);
+		STEP(d,e,f,g,h,a,b,c,29);
+		STEP(c,d,e,f,g,h,a,b,30);
+		STEP(b,c,d,e,f,g,h,a,31);
+
+		STEP(a,b,c,d,e,f,g,h,32);
+		STEP(h,a,b,c,d,e,f,g,33);
+		STEP(g,h,a,b,c,d,e,f,34);
+		STEP(f,g,h,a,b,c,d,e,35);
+		STEP(e,f,g,h,a,b,c,d,36);
+		STEP(d,e,f,g,h,a,b,c,37);
+		STEP(c,d,e,f,g,h,a,b,38);
+		STEP(b,c,d,e,f,g,h,a,39);
+
+		STEP(a,b,c,d,e,f,g,h,40);
+		STEP(h,a,b,c,d,e,f,g,41);
+		STEP(g,h,a,b,c,d,e,f,42);
+		STEP(f,g,h,a,b,c,d,e,43);
+		STEP(e,f,g,h,a,b,c,d,44);
+		STEP(d,e,f,g,h,a,b,c,45);
+		STEP(c,d,e,f,g,h,a,b,46);
+		STEP(b,c,d,e,f,g,h,a,47);
+
+		STEP(a,b,c,d,e,f,g,h,48);
+		STEP(h,a,b,c,d,e,f,g,49);
+		STEP(g,h,a,b,c,d,e,f,50);
+		STEP(f,g,h,a,b,c,d,e,51);
+		STEP(e,f,g,h,a,b,c,d,52);
+		STEP(d,e,f,g,h,a,b,c,53);
+		STEP(c,d,e,f,g,h,a,b,54);
+		STEP(b,c,d,e,f,g,h,a,55);
+
+		STEP(a,b,c,d,e,f,g,h,56);
+		STEP(h,a,b,c,d,e,f,g,57);
+		STEP(g,h,a,b,c,d,e,f,58);
+		STEP(f,g,h,a,b,c,d,e,59);
+		STEP(e,f,g,h,a,b,c,d,60);
+		STEP(d,e,f,g,h,a,b,c,61);
+		STEP(c,d,e,f,g,h,a,b,62);
+		STEP(b,c,d,e,f,g,h,a,63);
+
+		STEP(a,b,c,d,e,f,g,h,64);
+		STEP(h,a,b,c,d,e,f,g,65);
+		STEP(g,h,a,b,c,d,e,f,66);
+		STEP(f,g,h,a,b,c,d,e,67);
+		STEP(e,f,g,h,a,b,c,d,68);
+		STEP(d,e,f,g,h,a,b,c,69);
+		STEP(c,d,e,f,g,h,a,b,70);
+		STEP(b,c,d,e,f,g,h,a,71);
+
+		STEP(a,b,c,d,e,f,g,h,72);
+		STEP(h,a,b,c,d,e,f,g,73);
+		STEP(g,h,a,b,c,d,e,f,74);
+		STEP(f,g,h,a,b,c,d,e,75);
+		STEP(e,f,g,h,a,b,c,d,76);
+		STEP(d,e,f,g,h,a,b,c,77);
+		STEP(c,d,e,f,g,h,a,b,78);
+		STEP(b,c,d,e,f,g,h,a,79);
+
+		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,150 @@
+/*
+ * sha2_256 block cipher - unrolled version
+ *
+ *   note: the following upper and lower case macro names are distinct
+ *	   and reflect the functions defined in FIPS pub. 180-2.
+ */
+
+#include "os.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)	((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x,y,z)	(((x) | (y)) & ((z) | ((x) & (y))))
+
+/*
+ * 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 w[16], a, b, c, d, e, f, g, h;
+	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];
+
+#define STEP(a,b,c,d,e,f,g,h,i) \
+	if(i < 16) {\
+		w[i] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3]; \
+		p += 4; \
+	} else { \
+		w[i&15] += sigma1(w[(i-2)&15]) + w[(i-7)&15] + sigma0(w[(i-15)&15]); \
+	} \
+	h += SIGMA1(e) + Ch(e,f,g) + K256[i] + w[i&15]; \
+	d += h; \
+	h += SIGMA0(a) + Maj(a,b,c);
+
+		STEP(a,b,c,d,e,f,g,h,0);
+		STEP(h,a,b,c,d,e,f,g,1);
+		STEP(g,h,a,b,c,d,e,f,2);
+		STEP(f,g,h,a,b,c,d,e,3);
+		STEP(e,f,g,h,a,b,c,d,4);
+		STEP(d,e,f,g,h,a,b,c,5);
+		STEP(c,d,e,f,g,h,a,b,6);
+		STEP(b,c,d,e,f,g,h,a,7);
+
+		STEP(a,b,c,d,e,f,g,h,8);
+		STEP(h,a,b,c,d,e,f,g,9);
+		STEP(g,h,a,b,c,d,e,f,10);
+		STEP(f,g,h,a,b,c,d,e,11);
+		STEP(e,f,g,h,a,b,c,d,12);
+		STEP(d,e,f,g,h,a,b,c,13);
+		STEP(c,d,e,f,g,h,a,b,14);
+		STEP(b,c,d,e,f,g,h,a,15);
+
+		STEP(a,b,c,d,e,f,g,h,16);
+		STEP(h,a,b,c,d,e,f,g,17);
+		STEP(g,h,a,b,c,d,e,f,18);
+		STEP(f,g,h,a,b,c,d,e,19);
+		STEP(e,f,g,h,a,b,c,d,20);
+		STEP(d,e,f,g,h,a,b,c,21);
+		STEP(c,d,e,f,g,h,a,b,22);
+		STEP(b,c,d,e,f,g,h,a,23);
+
+		STEP(a,b,c,d,e,f,g,h,24);
+		STEP(h,a,b,c,d,e,f,g,25);
+		STEP(g,h,a,b,c,d,e,f,26);
+		STEP(f,g,h,a,b,c,d,e,27);
+		STEP(e,f,g,h,a,b,c,d,28);
+		STEP(d,e,f,g,h,a,b,c,29);
+		STEP(c,d,e,f,g,h,a,b,30);
+		STEP(b,c,d,e,f,g,h,a,31);
+
+		STEP(a,b,c,d,e,f,g,h,32);
+		STEP(h,a,b,c,d,e,f,g,33);
+		STEP(g,h,a,b,c,d,e,f,34);
+		STEP(f,g,h,a,b,c,d,e,35);
+		STEP(e,f,g,h,a,b,c,d,36);
+		STEP(d,e,f,g,h,a,b,c,37);
+		STEP(c,d,e,f,g,h,a,b,38);
+		STEP(b,c,d,e,f,g,h,a,39);
+
+		STEP(a,b,c,d,e,f,g,h,40);
+		STEP(h,a,b,c,d,e,f,g,41);
+		STEP(g,h,a,b,c,d,e,f,42);
+		STEP(f,g,h,a,b,c,d,e,43);
+		STEP(e,f,g,h,a,b,c,d,44);
+		STEP(d,e,f,g,h,a,b,c,45);
+		STEP(c,d,e,f,g,h,a,b,46);
+		STEP(b,c,d,e,f,g,h,a,47);
+
+		STEP(a,b,c,d,e,f,g,h,48);
+		STEP(h,a,b,c,d,e,f,g,49);
+		STEP(g,h,a,b,c,d,e,f,50);
+		STEP(f,g,h,a,b,c,d,e,51);
+		STEP(e,f,g,h,a,b,c,d,52);
+		STEP(d,e,f,g,h,a,b,c,53);
+		STEP(c,d,e,f,g,h,a,b,54);
+		STEP(b,c,d,e,f,g,h,a,55);
+
+		STEP(a,b,c,d,e,f,g,h,56);
+		STEP(h,a,b,c,d,e,f,g,57);
+		STEP(g,h,a,b,c,d,e,f,58);
+		STEP(f,g,h,a,b,c,d,e,59);
+		STEP(e,f,g,h,a,b,c,d,60);
+		STEP(d,e,f,g,h,a,b,c,61);
+		STEP(c,d,e,f,g,h,a,b,62);
+		STEP(b,c,d,e,f,g,h,a,63);
+
+		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/sha2test.c
@@ -1,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+#include "libsec.h"
+
+char *tests[] = {
+	"",
+	"a",
+	"abc",
+	"message digest",
+	"abcdefghijklmnopqrstuvwxyz",
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+	"123456789012345678901234567890123456789012345678901234567890"
+		"12345678901234567890",
+	"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+	"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhi"
+		"jklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+	0
+};
+
+void
+main(void)
+{
+	int i;
+	char **pp;
+	uchar *p;
+	uchar digest[SHA2_512dlen];
+
+	print("SHA2_224 tests:\n");
+	for(pp = tests; *pp; pp++){
+		p = (uchar*)*pp;
+		sha2_224(p, strlen(*pp), digest, 0);
+		for(i = 0; i < SHA2_224dlen; i++)
+			print("%2.2ux", digest[i]);
+		print("\n");
+	}
+
+	print("\nSHA256 tests:\n");
+	for(pp = tests; *pp; pp++){
+		p = (uchar*)*pp;
+		sha2_256(p, strlen(*pp), digest, 0);
+		for(i = 0; i < SHA2_256dlen; i++)
+			print("%2.2ux", digest[i]);
+		print("\n");
+	}
+
+	print("\nSHA384 tests:\n");
+	for(pp = tests; *pp; pp++){
+		p = (uchar*)*pp;
+		sha2_384(p, strlen(*pp), digest, 0);
+		for(i = 0; i < SHA2_384dlen; i++)
+			print("%2.2ux", digest[i]);
+		print("\n");
+	}
+
+	print("\nSHA512 tests:\n");
+	for(pp = tests; *pp; pp++){
+		p = (uchar*)*pp;
+		sha2_512(p, strlen(*pp), digest, 0);
+		for(i = 0; i < SHA2_512dlen; i++)
+			print("%2.2ux", digest[i]);
+		print("\n");
+	}
+}
--- /dev/null
+++ b/libsec/smallprimes.c
@@ -1,0 +1,1004 @@
+#include "os.h"
+
+ulong smallprimes[1000] = {
+	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,
+};
--- /dev/null
+++ b/libsec/smallprimetest.c
@@ -1,0 +1,1039 @@
+#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;
+}
--- /dev/null
+++ b/libsec/thumb.c
@@ -1,0 +1,170 @@
+#include "os.h"
+#include <bio.h>
+#include <libsec.h>
+
+enum{ ThumbTab = 1<<10 };
+
+static Thumbprint*
+tablehead(uchar *hash, Thumbprint *table)
+{
+	return &table[((hash[0]<<8) + hash[1]) & (ThumbTab-1)];
+}
+
+void
+freeThumbprints(Thumbprint *table)
+{
+	Thumbprint *hd, *p, *q;
+
+	if(table == nil)
+		return;
+	for(hd = table; hd < table+ThumbTab; hd++){
+		for(p = hd->next; p && p != hd; p = q){
+			q = p->next;
+			free(p);
+		}
+	}
+	free(table);
+}
+
+int
+okThumbprint(uchar *hash, int len, Thumbprint *table)
+{
+	Thumbprint *hd, *p;
+
+	if(table == nil)
+		return 0;
+	hd = tablehead(hash, table);
+	for(p = hd->next; p; p = p->next){
+		if(p->len == len && memcmp(hash, p->hash, len) == 0)
+			return 1;
+		if(p == hd)
+			break;
+	}
+	return 0;
+}
+
+int
+okCertificate(uchar *cert, int len, Thumbprint *table)
+{
+	uchar hash[SHA2_256dlen];
+	char thumb[2*SHA2_256dlen+1];
+
+	if(table == nil){
+		werrstr("no thumbprints provided");
+		return 0;
+	}
+	if(cert == nil || len <= 0){
+		werrstr("no certificate provided");
+		return 0;
+	}
+
+	sha1(cert, len, hash, nil);
+	if(okThumbprint(hash, SHA1dlen, table))
+		return 1;
+
+	sha2_256(cert, len, hash, nil);
+	if(okThumbprint(hash, SHA2_256dlen, table))
+		return 1;
+
+	if(X509digestSPKI(cert, len, sha2_256, hash) < 0)
+		return 0;
+	if(okThumbprint(hash, SHA2_256dlen, table))
+		return 1;
+
+	len = enc64(thumb, sizeof(thumb), hash, SHA2_256dlen);
+	while(len > 0 && thumb[len-1] == '=')
+		len--;
+	thumb[len] = '\0';
+	werrstr("sha256=%s", thumb);
+
+	return 0;
+}
+
+static int
+loadThumbprints(char *file, char *tag, Thumbprint *table, Thumbprint *crltab, int depth)
+{
+	Thumbprint *hd, *entry;
+	char *line, *field[50];
+	uchar hash[SHA2_256dlen];
+	Biobuf *bin;
+	int len, n;
+
+	if(depth > 8){
+		werrstr("too many includes, last file %s", file);
+		return -1;
+	}
+	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){
+			if(loadThumbprints(field[1], tag, table, crltab, depth+1) < 0)
+				goto err;
+			continue;
+		}
+		if(strcmp(field[0], tag) != 0)
+			continue;
+		if(strncmp(field[1], "sha1=", 5) == 0){
+			field[1] += 5;
+			len = SHA1dlen;
+		} else if(strncmp(field[1], "sha256=", 7) == 0){
+			field[1] += 7;
+			len = SHA2_256dlen;
+		} else {
+			continue;
+		}
+		n = strlen(field[1]);
+		if((n != len*2 || dec16(hash, len, field[1], n) != len)
+		&& dec64(hash, len, field[1], n) != len){
+			werrstr("malformed %s entry in %s: %s", tag, file, field[1]);
+			goto err;
+		}
+		if(crltab && okThumbprint(hash, len, crltab))
+			continue;
+		hd = tablehead(hash, table);
+		if(hd->next == nil)
+			entry = hd;
+		else {
+			if((entry = malloc(sizeof(*entry))) == nil)
+				goto err;
+			entry->next = hd->next;
+		}
+		hd->next = entry;
+		entry->len = len;
+		memcpy(entry->hash, hash, len);
+	}
+	Bterm(bin);
+	return 0;
+err:
+	free(line);
+	Bterm(bin);
+	return -1;
+}
+
+Thumbprint *
+initThumbprints(char *ok, char *crl, char *tag)
+{
+	Thumbprint *table, *crltab;
+
+	table = crltab = nil;
+	if(crl){
+		if((crltab = malloc(ThumbTab * sizeof(*crltab))) == nil)
+			goto err;
+		memset(crltab, 0, ThumbTab * sizeof(*crltab));
+		if(loadThumbprints(crl, tag, crltab, nil, 0) < 0)
+			goto err;
+	}
+	if((table = malloc(ThumbTab * sizeof(*table))) == nil)
+		goto err;
+	memset(table, 0, ThumbTab * sizeof(*table));
+	if(loadThumbprints(ok, tag, table, crltab, 0) < 0){
+		freeThumbprints(table);
+		table = nil;
+	}
+err:
+	freeThumbprints(crltab);
+	return table;
+}
--- /dev/null
+++ b/libsec/tsmemcmp.c
@@ -1,0 +1,25 @@
+#include "os.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;
+}
--- /dev/null
+++ b/libsec/x509.c
@@ -1,0 +1,3033 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+/*=============================================================*/
+/*  general ASN1 declarations and parsing
+ *
+ *  For now, this is used only for extracting the key from an
+ *  X509 certificate, so the entire collection is hidden.  But
+ *  someday we should probably make the functions visible and
+ *  give them their own man page.
+ */
+typedef struct Elem Elem;
+typedef struct Tag Tag;
+typedef struct Value Value;
+typedef struct Bytes Bytes;
+typedef struct Ints Ints;
+typedef struct Bits Bits;
+typedef struct Elist Elist;
+
+/* tag classes */
+#define Universal 0
+#define Context 0x80
+
+/* universal tags */
+#define BOOLEAN 1
+#define INTEGER 2
+#define BIT_STRING 3
+#define OCTET_STRING 4
+#define NULLTAG 5
+#define OBJECT_ID 6
+#define ObjectDescriptor 7
+#define EXTERNAL 8
+#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
+#define PrintableString 19
+#define TeletexString 20
+#define VideotexString 21
+#define IA5String 22
+#define UTCTime 23
+#define GeneralizedTime 24
+#define GraphicString 25
+#define VisibleString 26
+#define GeneralString 27
+#define UniversalString 28
+#define BMPString 30
+
+struct Bytes {
+	int	len;
+	uchar	data[];
+};
+
+struct Ints {
+	int	len;
+	int	data[];
+};
+
+struct Bits {
+	int	len;		/* number of bytes */
+	int	unusedbits;	/* unused bits in last byte */
+	uchar	data[];		/* most-significant bit first */
+};
+
+struct Tag {
+	int	class;
+	int	num;
+};
+
+enum { VBool, VInt, VOctets, VBigInt, VReal, VOther,
+	VBitString, VNull, VEOC, VObjId, VString, VSeq, VSet };
+struct Value {
+	int	tag;		/* VBool, etc. */
+	union {
+		int	boolval;
+		int	intval;
+		Bytes*	octetsval;
+		Bytes*	bigintval;
+		Bytes*	realval;	/* undecoded; hardly ever used */
+		Bytes*	otherval;
+		Bits*	bitstringval;
+		Ints*	objidval;
+		char*	stringval;
+		Elist*	seqval;
+		Elist*	setval;
+	} u;  /* (Don't use anonymous unions, for ease of porting) */
+};
+
+struct Elem {
+	Tag	tag;
+	Value	val;
+};
+
+struct Elist {
+	Elist*	tl;
+	Elem	hd;
+};
+
+/* decoding errors */
+enum { ASN_OK, ASN_ESHORT, ASN_ETOOBIG, ASN_EVALLEN,
+		ASN_ECONSTR, ASN_EPRIM, ASN_EINVAL, ASN_EUNIMPL };
+
+
+/* here are the functions to consider making extern someday */
+static Bytes*	newbytes(int len);
+static Bytes*	makebytes(uchar* buf, int len);
+static void	freebytes(Bytes* b);
+static Bytes*	catbytes(Bytes* b1, Bytes* b2);
+static Ints*	newints(int len);
+static Ints*	makeints(int* buf, int len);
+static void	freeints(Ints* b);
+static Bits*	newbits(int len);
+static Bits*	makebits(uchar* buf, int len, int unusedbits);
+static void	freebits(Bits* b);
+static Elist*	mkel(Elem e, Elist* tail);
+static void	freeelist(Elist* el);
+static int	elistlen(Elist* el);
+static int	is_seq(Elem* pe, Elist** pseq);
+static int	is_set(Elem* pe, Elist** pset);
+static int	is_int(Elem* pe, int* pint);
+static int	is_bigint(Elem* pe, Bytes** pbigint);
+static int	is_bitstring(Elem* pe, Bits** pbits);
+static int	is_octetstring(Elem* pe, Bytes** poctets);
+static int	is_oid(Elem* pe, Ints** poid);
+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	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
+#define MAXOBJIDLEN 20
+
+static int ber_decode(uchar** pp, uchar* pend, Elem* pelem);
+static int tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr);
+static int length_decode(uchar** pp, uchar* pend, int* plength);
+static int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval);
+static int int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint);
+static int uint7_decode(uchar** pp, uchar* pend, int* pint);
+static int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes);
+static int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist);
+static int enc(uchar** pp, Elem e, int lenonly);
+static int val_enc(uchar** pp, Elem e, int *pconstr, int lenonly);
+static void uint7_enc(uchar** pp, int num, int lenonly);
+static void int_enc(uchar** pp, int num, int unsgned, int lenonly);
+
+static void *
+emalloc(int n)
+{
+	void *p;
+	if(n==0)
+		n=1;
+	p = malloc(n);
+	if(p == nil)
+		sysfatal("out of memory");
+	memset(p, 0, n);
+	setmalloctag(p, getcallerpc(&n));
+	return p;
+}
+
+static char*
+estrdup(char *s)
+{
+	char *d;
+	int n;
+
+	n = strlen(s)+1;
+	d = emalloc(n);
+	memmove(d, s, n);
+	return d;
+}
+
+
+/*
+ * Decode a[0..len] as a BER encoding of an ASN1 type.
+ * The return value is one of ASN_OK, etc.
+ * Depending on the error, the returned elem may or may not
+ * be nil.
+ */
+static int
+decode(uchar* a, int alen, Elem* pelem)
+{
+	uchar* p = a;
+	int err;
+
+	err = ber_decode(&p, &a[alen], pelem);
+	if(err == ASN_OK && p != &a[alen])
+		err = ASN_EVALLEN;
+	return err;
+}
+
+/*
+ * All of the following decoding routines take arguments:
+ *	uchar **pp;
+ *	uchar *pend;
+ * Where parsing is supposed to start at **pp, and when parsing
+ * is done, *pp is updated to point at next char to be parsed.
+ * The pend pointer is just past end of string; an error should
+ * be returned parsing hasn't finished by then.
+ *
+ * The returned int is ASN_OK if all went fine, else ASN_ESHORT, etc.
+ * The remaining argument(s) are pointers to where parsed entity goes.
+ */
+
+/* Decode an ASN1 'Elem' (tag, length, value) */
+static int
+ber_decode(uchar** pp, uchar* pend, Elem* pelem)
+{
+	int err;
+	int isconstr;
+	int length;
+	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);
+		if(err == ASN_OK) {
+			if(tag.class == Universal)
+				err = value_decode(pp, pend, length, tag.num, isconstr, &val);
+			else
+				err = value_decode(pp, pend, length, OCTET_STRING, 0, &val);
+			if(err == ASN_OK) {
+				pelem->tag = tag;
+				pelem->val = val;
+			}
+		}
+	}
+	return err;
+}
+
+/* Decode a tag field */
+static int
+tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr)
+{
+	int err;
+	int v;
+	uchar* p;
+
+	err = ASN_OK;
+	p = *pp;
+	if(pend-p >= 2) {
+		v = *p++;
+		ptag->class = v&CLASS_MASK;
+		if(v&CONSTR_MASK)
+			*pisconstr = 1;
+		else
+			*pisconstr = 0;
+		v &= TAG_MASK;
+		if(v == TAG_MASK)
+			err = uint7_decode(&p, pend, &v);
+		ptag->num = v;
+	}
+	else
+		err = ASN_ESHORT;
+	*pp = p;
+	return err;
+}
+
+/* Decode a length field */
+static int
+length_decode(uchar** pp, uchar* pend, int* plength)
+{
+	int err;
+	int num;
+	int v;
+	uchar* p;
+
+	err = ASN_OK;
+	num = 0;
+	p = *pp;
+	if(p < pend) {
+		v = *p++;
+		if(v&0x80)
+			err = int_decode(&p, pend, v&0x7F, 1, &num);
+		else
+			num = v;
+	}
+	else
+		err = ASN_ESHORT;
+	*pp = p;
+	*plength = num;
+	return err;
+}
+
+/* Decode a value field  */
+static int
+value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval)
+{
+	int err;
+	Bytes* va;
+	int num;
+	int bitsunused;
+	int subids[MAXOBJIDLEN];
+	int isubid;
+	Elist*	vl;
+	uchar* p;
+	uchar* pe;
+
+	err = ASN_OK;
+	p = *pp;
+	if(length == -1) {	/* "indefinite" length spec */
+		if(!isconstr)
+			err = ASN_EINVAL;
+	}
+	else if(p + length > pend)
+		err = ASN_EVALLEN;
+	if(err != ASN_OK)
+		return err;
+
+	switch(kind) {
+	case 0:
+		/* marker for end of indefinite constructions */
+		if(length == 0)
+			pval->tag = VNull;
+		else
+			err = ASN_EINVAL;
+		break;
+
+	case BOOLEAN:
+		if(isconstr)
+			err = ASN_ECONSTR;
+		else if(length != 1)
+			err = ASN_EVALLEN;
+		else {
+			pval->tag = VBool;
+			pval->u.boolval = (*p++ != 0);
+		}
+		break;
+
+	case INTEGER:
+	case ENUMERATED:
+		if(isconstr)
+			err = ASN_ECONSTR;
+		else if(length <= 4) {
+			err = int_decode(&p, pend, length, 0, &num);
+			if(err == ASN_OK) {
+				pval->tag = VInt;
+				pval->u.intval = num;
+			}
+		}
+		else {
+			pval->tag = VBigInt;
+			pval->u.bigintval = makebytes(p, length);
+			p += length;
+		}
+		break;
+
+	case BIT_STRING:
+		pval->tag = VBitString;
+		if(isconstr) {
+			if(length == -1 && p + 2 <= pend && *p == 0 && *(p+1) ==0) {
+				pval->u.bitstringval = makebits(0, 0, 0);
+				p += 2;
+			}
+			else	/* TODO: recurse and concat results */
+				err = ASN_EUNIMPL;
+		}
+		else {
+			if(length < 2) {
+				if(length == 1 && *p == 0) {
+					pval->u.bitstringval = makebits(0, 0, 0);
+					p++;
+				}
+				else
+					err = ASN_EINVAL;
+			}
+			else {
+				bitsunused = *p;
+				if(bitsunused > 7)
+					err = ASN_EINVAL;
+				else if(length > 0x0FFFFFFF)
+					err = ASN_ETOOBIG;
+				else {
+					pval->u.bitstringval = makebits(p+1, length-1, bitsunused);
+					p += length;
+				}
+			}
+		}
+		break;
+
+	case OCTET_STRING:
+	case ObjectDescriptor:
+		err = octet_decode(&p, pend, length, isconstr, &va);
+		if(err == ASN_OK) {
+			pval->tag = VOctets;
+			pval->u.octetsval = va;
+		}
+		break;
+
+	case NULLTAG:
+		if(isconstr)
+			err = ASN_ECONSTR;
+		else if(length != 0)
+			err = ASN_EVALLEN;
+		else
+			pval->tag = VNull;
+		break;
+
+	case OBJECT_ID:
+		if(isconstr)
+			err = ASN_ECONSTR;
+		else if(length == 0)
+			err = ASN_EVALLEN;
+		else {
+			isubid = 0;
+			pe = p+length;
+			while(p < pe && isubid < MAXOBJIDLEN) {
+				err = uint7_decode(&p, pend, &num);
+				if(err != ASN_OK)
+					break;
+				if(isubid == 0) {
+					subids[isubid++] = num / 40;
+					subids[isubid++] = num % 40;
+				}
+				else
+					subids[isubid++] = num;
+			}
+			if(err == ASN_OK) {
+				if(p != pe)
+					err = ASN_EVALLEN;
+				else {
+					pval->tag = VObjId;
+					pval->u.objidval = makeints(subids, isubid);
+				}
+			}
+		}
+		break;
+
+	case EXTERNAL:
+	case EMBEDDED_PDV:
+		/* TODO: parse this internally */
+		if(p+length > pend)
+			err = ASN_EVALLEN;
+		else {
+			pval->tag = VOther;
+			pval->u.otherval = makebytes(p, length);
+			p += length;
+		}
+		break;
+
+	case REAL:
+		/* Let the application decode */
+		if(isconstr)
+			err = ASN_ECONSTR;
+		else if(p+length > pend)
+			err = ASN_EVALLEN;
+		else {
+			pval->tag = VReal;
+			pval->u.realval = makebytes(p, length);
+			p += length;
+		}
+		break;
+
+	case SEQUENCE:
+		err = seq_decode(&p, pend, length, isconstr, &vl);
+		if(err == ASN_OK) {
+			pval->tag = VSeq ;
+			pval->u.seqval = vl;
+		}
+		break;
+
+	case SETOF:
+		err = seq_decode(&p, pend, length, isconstr, &vl);
+		if(err == ASN_OK) {
+			pval->tag = VSet;
+			pval->u.setval = vl;
+		}
+		break;
+
+	case UTF8String:
+	case NumericString:
+	case PrintableString:
+	case TeletexString:
+	case VideotexString:
+	case IA5String:
+	case UTCTime:
+	case GeneralizedTime:
+	case GraphicString:
+	case VisibleString:
+	case GeneralString:
+	case UniversalString:
+	case BMPString:
+		err = octet_decode(&p, pend, length, isconstr, &va);
+		if(err == ASN_OK) {
+			uchar *s;
+			char *d;
+			Rune r;
+			int n;
+
+			switch(kind){
+			case UniversalString:
+				n = va->len / 4;
+				d = emalloc(n*UTFmax+1);
+				pval->u.stringval = d;
+				s = va->data;
+				while(n > 0){
+					r = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
+					if(r == 0)
+						break;
+					n--;
+					s += 4;
+					d += runetochar(d, &r);
+				}
+				*d = 0;
+				break;
+			case BMPString:
+				n = va->len / 2;
+				d = emalloc(n*UTFmax+1);
+				pval->u.stringval = d;
+				s = va->data;
+				while(n > 0){
+					r = s[0]<<8 | s[1];
+					if(r == 0)
+						break;
+					n--;
+					s += 2;
+					d += runetochar(d, &r);
+				}
+				*d = 0;
+				break;
+			default:
+				n = va->len;
+				d = emalloc(n+1);
+				pval->u.stringval = d;
+				s = va->data;
+				while(n > 0){
+					if((*d = *s) == 0)
+						break;
+					n--;
+					s++;
+					d++;
+				}
+				*d = 0;
+				break;
+			}
+			if(n != 0){
+				err = ASN_EINVAL;
+				free(pval->u.stringval);
+			} else 
+				pval->tag = VString;
+			free(va);
+		}
+		break;
+
+	default:
+		if(p+length > pend)
+			err = ASN_EVALLEN;
+		else {
+			pval->tag = VOther;
+			pval->u.otherval = makebytes(p, length);
+			p += length;
+		}
+		break;
+	}
+	*pp = p;
+	return err;
+}
+
+/*
+ * Decode an int in format where count bytes are
+ * concatenated to form value.
+ * Although ASN1 allows any size integer, we return
+ * an error if the result doesn't fit in a 32-bit int.
+ * If unsgned is not set, make sure to propagate sign bit.
+ */
+static int
+int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint)
+{
+	int err;
+	int num;
+	uchar* p;
+
+	p = *pp;
+	err = ASN_OK;
+	num = 0;
+	if(p+count <= pend) {
+		if((count > 4) || (unsgned && count == 4 && (*p&0x80)))
+			err = ASN_ETOOBIG;
+		else {
+			if(!unsgned && count > 0 && count < 4 && (*p&0x80))
+				num = -1;	/* set all bits, initially */
+			while(count--)
+				num = (num << 8)|(*p++);
+		}
+	}
+	else
+		err = ASN_ESHORT;
+	*pint = num;
+	*pp = p;
+	return err;
+}
+
+/*
+ * Decode an unsigned int in format where each
+ * byte except last has high bit set, and remaining
+ * seven bits of each byte are concatenated to form value.
+ * Although ASN1 allows any size integer, we return
+ * an error if the result doesn't fit in a 32 bit int.
+ */
+static int
+uint7_decode(uchar** pp, uchar* pend, int* pint)
+{
+	int err;
+	int num;
+	int more;
+	int v;
+	uchar* p;
+
+	p = *pp;
+	err = ASN_OK;
+	num = 0;
+	more = 1;
+	while(more && p < pend) {
+		v = *p++;
+		if(num&0x7F000000) {
+			err = ASN_ETOOBIG;
+			break;
+		}
+		num <<= 7;
+		more = v&0x80;
+		num |= (v&0x7F);
+	}
+	if(p == pend)
+		err = ASN_ESHORT;
+	*pint = num;
+	*pp = p;
+	return err;
+}
+
+/*
+ * Decode an octet string, recursively if isconstr.
+ * We've already checked that length==-1 implies isconstr==1,
+ * and otherwise that specified length fits within (*pp..pend)
+ */
+static int
+octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes)
+{
+	int err;
+	uchar* p;
+	Bytes* ans;
+	Bytes* newans;
+	uchar* pstart;
+	uchar* pold;
+	Elem	elem;
+
+	err = ASN_OK;
+	p = *pp;
+	ans = nil;
+	if(length >= 0 && !isconstr) {
+		ans = makebytes(p, length);
+		p += length;
+	}
+	else {
+		/* constructed, either definite or indefinite length */
+		pstart = p;
+		for(;;) {
+			if(length >= 0 && p >= pstart + length) {
+				if(p != pstart + length)
+					err = ASN_EVALLEN;
+				break;
+			}
+			pold = p;
+			err = ber_decode(&p, pend, &elem);
+			if(err != ASN_OK)
+				break;
+			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)
+					goto cloop_done;
+				/* no break */
+			default:
+				freevalfields(&elem.val);
+				p = pold;
+				err = ASN_EINVAL;
+				goto cloop_done;
+			}
+		}
+cloop_done:
+		if(err != ASN_OK){
+			freebytes(ans);
+			ans = nil;
+		}
+	}
+	*pp = p;
+	*pbytes = ans;
+	return err;
+}
+
+/*
+ * Decode a sequence or set.
+ * We've already checked that length==-1 implies isconstr==1,
+ * and otherwise that specified length fits within (*p..pend)
+ */
+static int
+seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist)
+{
+	int err;
+	uchar* p;
+	uchar* pstart;
+	uchar* pold;
+	Elist* ans;
+	Elem elem;
+	Elist* lve;
+	Elist* lveold;
+
+	err = ASN_OK;
+	ans = nil;
+	p = *pp;
+	if(!isconstr)
+		err = ASN_EPRIM;
+	else {
+		/* constructed, either definite or indefinite length */
+		lve = nil;
+		pstart = p;
+		for(;;) {
+			if(length >= 0 && p >= pstart + length) {
+				if(p != pstart + length)
+					err = ASN_EVALLEN;
+				break;
+			}
+			pold = p;
+			err = ber_decode(&p, pend, &elem);
+			if(err != ASN_OK)
+				break;
+			if(elem.val.tag == VEOC) {
+				if(length != -1) {
+					p = pold;
+					err = ASN_EINVAL;
+				}
+				break;
+			}
+			else
+				lve = mkel(elem, lve);
+		}
+		if(err != ASN_OK)
+			freeelist(lve);
+		else {
+			/* reverse back to original order */
+			while(lve != nil) {
+				lveold = lve;
+				lve = lve->tl;
+				lveold->tl = ans;
+				ans = lveold;
+			}
+		}
+	}
+	*pp = p;
+	*pelist = ans;
+	return err;
+}
+
+/*
+ * Encode e by BER rules, putting answer in *pbytes.
+ * This is done by first calling enc with lenonly==1
+ * to get the length of the needed buffer,
+ * then allocating the buffer and using enc again to fill it up.
+ */
+static int
+encode(Elem e, Bytes** pbytes)
+{
+	uchar* p;
+	Bytes* ans;
+	int err;
+	uchar uc;
+
+	p = &uc;
+	err = enc(&p, e, 1);
+	if(err == ASN_OK) {
+		ans = newbytes(p-&uc);
+		p = ans->data;
+		err = enc(&p, e, 0);
+		*pbytes = ans;
+	}
+	return err;
+}
+
+/*
+ * The various enc functions take a pointer to a pointer
+ * into a buffer, and encode their entity starting there,
+ * updating the pointer afterwards.
+ * If lenonly is 1, only the pointer update is done,
+ * allowing enc to be called first to calculate the needed
+ * buffer length.
+ * If lenonly is 0, it is assumed that the answer will fit.
+ */
+
+static int
+enc(uchar** pp, Elem e, int lenonly)
+{
+	int err;
+	int vlen;
+	int constr;
+	Tag tag;
+	int v;
+	int ilen;
+	uchar* p;
+	uchar* psave;
+
+	p = *pp;
+	err = val_enc(&p, e, &constr, 1);
+	if(err != ASN_OK)
+		return err;
+	vlen = p - *pp;
+	p = *pp;
+	tag = e.tag;
+	v = tag.class|constr;
+	if(tag.num < 31) {
+		if(!lenonly)
+			*p = (v|tag.num);
+		p++;
+	}
+	else {
+		if(!lenonly)
+			*p = (v|31);
+		p++;
+		if(tag.num < 0)
+			return ASN_EINVAL;
+		uint7_enc(&p, tag.num, lenonly);
+	}
+	if(vlen < 0x80) {
+		if(!lenonly)
+			*p = vlen;
+		p++;
+	}
+	else {
+		psave = p;
+		int_enc(&p, vlen, 1, 1);
+		ilen = p-psave;
+		p = psave;
+		if(!lenonly) {
+			*p++ = (0x80 | ilen);
+			int_enc(&p, vlen, 1, 0);
+		}
+		else
+			p += 1 + ilen;
+	}
+	if(!lenonly)
+		val_enc(&p, e, &constr, 0);
+	else
+		p += vlen;
+	*pp = p;
+	return err;
+}
+
+static int
+val_enc(uchar** pp, Elem e, int *pconstr, int lenonly)
+{
+	int err;
+	uchar* p;
+	int kind;
+	int cl;
+	int v;
+	Bytes* bb = nil;
+	Bits* bits;
+	Ints* oid;
+	int k;
+	Elist* el;
+	char* s;
+
+	p = *pp;
+	err = ASN_OK;
+	kind = e.tag.num;
+	cl = e.tag.class;
+	*pconstr = 0;
+	if(cl != Universal) {
+		switch(e.val.tag) {
+		case VBool:
+			kind = BOOLEAN;
+			break;
+		case VInt:
+			kind = INTEGER;
+			break;
+		case VBigInt:
+			kind = INTEGER;
+			break;
+		case VOctets:
+			kind = OCTET_STRING;
+			break;
+		case VReal:
+			kind = REAL;
+			break;
+		case VOther:
+			kind = OCTET_STRING;
+			break;
+		case VBitString:
+			kind = BIT_STRING;
+			break;
+		case VNull:
+			kind = NULLTAG;
+			break;
+		case VObjId:
+			kind = OBJECT_ID;
+			break;
+		case VString:
+			kind = UniversalString;
+			break;
+		case VSeq:
+			kind = SEQUENCE;
+			break;
+		case VSet:
+			kind = SETOF;
+			break;
+		}
+	}
+	switch(kind) {
+	case BOOLEAN:
+		if(is_int(&e, &v)) {
+			if(v != 0)
+				v = 255;
+			 int_enc(&p, v, 1, lenonly);
+		}
+		else
+			err = ASN_EINVAL;
+		break;
+
+	case INTEGER:
+	case ENUMERATED:
+		if(is_int(&e, &v))
+			int_enc(&p, v, 0, lenonly);
+		else {
+			if(is_bigint(&e, &bb)) {
+				if(!lenonly)
+					memmove(p, bb->data, bb->len);
+				p += bb->len;
+			}
+			else
+				err = ASN_EINVAL;
+		}
+		break;
+
+	case BIT_STRING:
+		if(is_bitstring(&e, &bits)) {
+			if(bits->len == 0) {
+				if(!lenonly)
+					*p = 0;
+				p++;
+			}
+			else {
+				v = bits->unusedbits;
+				if(v < 0 || v > 7)
+					err = ASN_EINVAL;
+				else {
+					if(!lenonly) {
+						*p = v;
+						memmove(p+1, bits->data, bits->len);
+					}
+					p += 1 + bits->len;
+				}
+			}
+		}
+		else
+			err = ASN_EINVAL;
+		break;
+
+	case OCTET_STRING:
+	case ObjectDescriptor:
+	case EXTERNAL:
+	case REAL:
+	case EMBEDDED_PDV:
+		bb = nil;
+		switch(e.val.tag) {
+		case VOctets:
+			bb = e.val.u.octetsval;
+			break;
+		case VReal:
+			bb = e.val.u.realval;
+			break;
+		case VOther:
+			bb = e.val.u.otherval;
+			break;
+		}
+		if(bb != nil) {
+			if(!lenonly)
+				memmove(p, bb->data, bb->len);
+			p += bb->len;
+		}
+		else
+			err = ASN_EINVAL;
+		break;
+
+	case NULLTAG:
+		break;
+
+	case OBJECT_ID:
+		if(is_oid(&e, &oid)) {
+			for(k = 0; k < oid->len; k++) {
+				v = oid->data[k];
+				if(k == 0) {
+					v *= 40;
+					if(oid->len > 1)
+						v += oid->data[++k];
+				}
+				uint7_enc(&p, v, lenonly);
+			}
+		}
+		else
+			err = ASN_EINVAL;
+		break;
+
+	case SEQUENCE:
+	case SETOF:
+		el = nil;
+		if(e.val.tag == VSeq)
+			el = e.val.u.seqval;
+		else if(e.val.tag == VSet)
+			el = e.val.u.setval;
+		else
+			err = ASN_EINVAL;
+		if(el != nil) {
+			*pconstr = CONSTR_MASK;
+			for(; el != nil; el = el->tl) {
+				err = enc(&p, el->hd, lenonly);
+				if(err != ASN_OK)
+					break;
+			}
+		}
+		break;
+
+	case UTF8String:
+	case NumericString:
+	case PrintableString:
+	case TeletexString:
+	case VideotexString:
+	case IA5String:
+	case UTCTime:
+	case GeneralizedTime:
+	case GraphicString:
+	case VisibleString:
+	case GeneralString:
+	case UniversalString:
+	case BMPString:
+		if(e.val.tag == VString) {
+			s = e.val.u.stringval;
+			if(s != nil) {
+				v = strlen(s);
+				if(!lenonly)
+					memmove(p, s, v);
+				p += v;
+			}
+		}
+		else
+			err = ASN_EINVAL;
+		break;
+
+	default:
+		err = ASN_EINVAL;
+	}
+	*pp = p;
+	return err;
+}
+
+/*
+ * Encode num as unsigned 7 bit values with top bit 1 on all bytes
+ * except last, only putting in bytes if !lenonly.
+ */
+static void
+uint7_enc(uchar** pp, int num, int lenonly)
+{
+	int n;
+	int v;
+	int k;
+	uchar* p;
+
+	p = *pp;
+	n = 1;
+	v = num >> 7;
+	while(v > 0) {
+		v >>= 7;
+		n++;
+	}
+	if(lenonly)
+		p += n;
+	else {
+		for(k = (n - 1)*7; k > 0; k -= 7)
+			*p++= ((num >> k)|0x80);
+		*p++ = (num&0x7F);
+	}
+	*pp = p;
+}
+
+/*
+ * Encode num as unsigned or signed integer,
+ * only putting in bytes if !lenonly.
+ * Encoding is length followed by bytes to concatenate.
+ */
+static void
+int_enc(uchar** pp, int num, int unsgned, int lenonly)
+{
+	int v;
+	int n;
+	int prevv;
+	int k;
+	uchar* p;
+
+	p = *pp;
+	v = num;
+	if(v < 0)
+		v = -(v + 1);
+	n = 1;
+	prevv = v;
+	v >>= 8;
+	while(v > 0) {
+		prevv = v;
+		v >>= 8;
+		n++;
+	}
+	if(!unsgned && (prevv&0x80))
+		n++;
+	if(lenonly)
+		p += n;
+	else {
+		for(k = (n - 1)*8; k >= 0; k -= 8)
+			*p++ = (num >> k);
+	}
+	*pp = p;
+}
+
+static int
+ints_eq(Ints* a, Ints* b)
+{
+	int	alen;
+	int	i;
+
+	alen = a->len;
+	if(alen != b->len)
+		return 0;
+	for(i = 0; i < alen; i++)
+		if(a->data[i] != b->data[i])
+			return 0;
+	return 1;
+}
+
+/*
+ * Look up o in tab (which must have nil entry to terminate).
+ * Return index of matching entry, or -1 if none.
+ */
+static int
+oid_lookup(Ints* o, Ints** tab)
+{
+	int i;
+
+	for(i = 0; tab[i] != nil; i++)
+		if(ints_eq(o, tab[i]))
+			return  i;
+	return -1;
+}
+
+/*
+ * Return true if *pe is a SEQUENCE, and set *pseq to
+ * the value of the sequence if so.
+ */
+static int
+is_seq(Elem* pe, Elist** pseq)
+{
+	if(pe->tag.class == Universal && pe->tag.num == SEQUENCE && pe->val.tag == VSeq) {
+		*pseq = pe->val.u.seqval;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+is_set(Elem* pe, Elist** pset)
+{
+	if(pe->tag.class == Universal && pe->tag.num == SETOF && pe->val.tag == VSet) {
+		*pset = pe->val.u.setval;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+is_int(Elem* pe, int* pint)
+{
+	if(pe->tag.class == Universal) {
+		if(pe->tag.num == INTEGER && pe->val.tag == VInt) {
+			*pint = pe->val.u.intval;
+			return 1;
+		}
+		else if(pe->tag.num == BOOLEAN && pe->val.tag == VBool) {
+			*pint = pe->val.u.boolval;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * for convience, all VInt's are readable via this routine,
+ * as well as all VBigInt's
+ */
+static int
+is_bigint(Elem* pe, Bytes** pbigint)
+{
+	if(pe->tag.class == Universal && pe->tag.num == INTEGER && pe->val.tag == VBigInt) {
+		*pbigint = pe->val.u.bigintval;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+is_bitstring(Elem* pe, Bits** pbits)
+{
+	if(pe->tag.class == Universal && pe->tag.num == BIT_STRING && pe->val.tag == VBitString) {
+		*pbits = pe->val.u.bitstringval;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+is_octetstring(Elem* pe, Bytes** poctets)
+{
+	if(pe->tag.class == Universal && pe->tag.num == OCTET_STRING && pe->val.tag == VOctets) {
+		*poctets = pe->val.u.octetsval;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+is_oid(Elem* pe, Ints** poid)
+{
+	if(pe->tag.class == Universal && pe->tag.num == OBJECT_ID && pe->val.tag == VObjId) {
+		*poid = pe->val.u.objidval;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+is_string(Elem* pe, char** pstring)
+{
+	if(pe->tag.class == Universal) {
+		switch(pe->tag.num) {
+		case UTF8String:
+		case NumericString:
+		case PrintableString:
+		case TeletexString:
+		case VideotexString:
+		case IA5String:
+		case GraphicString:
+		case VisibleString:
+		case GeneralString:
+		case UniversalString:
+		case BMPString:
+			if(pe->val.tag == VString) {
+				*pstring = pe->val.u.stringval;
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+is_time(Elem* pe, char** ptime)
+{
+	if(pe->tag.class == Universal
+	   && (pe->tag.num == UTCTime || pe->tag.num == GeneralizedTime)
+	   && pe->val.tag == VString) {
+		*ptime = pe->val.u.stringval;
+		return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * malloc and return a new Bytes structure capable of
+ * holding len bytes. (len >= 0)
+ */
+static Bytes*
+newbytes(int len)
+{
+	Bytes* ans;
+
+	if(len < 0)
+		abort();
+	ans = emalloc(sizeof(Bytes) + len);
+	ans->len = len;
+	return ans;
+}
+
+/*
+ * newbytes(len), with data initialized from buf
+ */
+static Bytes*
+makebytes(uchar* buf, int len)
+{
+	Bytes* ans;
+
+	ans = newbytes(len);
+	memmove(ans->data, buf, len);
+	return ans;
+}
+
+static void
+freebytes(Bytes* b)
+{
+	free(b);
+}
+
+/*
+ * Make a new Bytes, containing bytes of b1 followed by those of b2.
+ * Either b1 or b2 or both can be nil.
+ */
+static Bytes*
+catbytes(Bytes* b1, Bytes* b2)
+{
+	Bytes* ans;
+	int n;
+
+	if(b1 == nil) {
+		if(b2 == nil)
+			ans = newbytes(0);
+		else
+			ans = makebytes(b2->data, b2->len);
+	}
+	else if(b2 == nil) {
+		ans = makebytes(b1->data, b1->len);
+	}
+	else {
+		n = b1->len + b2->len;
+		ans = newbytes(n);
+		ans->len = n;
+		memmove(ans->data, b1->data, b1->len);
+		memmove(ans->data+b1->len, b2->data, b2->len);
+	}
+	return ans;
+}
+
+/* len is number of ints */
+static Ints*
+newints(int len)
+{
+	Ints* ans;
+
+	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);
+	memmove(ans->data, buf, len*sizeof(int));
+	return ans;
+}
+
+static void
+freeints(Ints* b)
+{
+	free(b);
+}
+
+/* len is number of bytes */
+static Bits*
+newbits(int len)
+{
+	Bits* ans;
+
+	if(len < 0)
+		abort();
+	ans = emalloc(sizeof(Bits) + len);
+	ans->len = len;
+	ans->unusedbits = 0;
+	return ans;
+}
+
+static Bits*
+makebits(uchar* buf, int len, int unusedbits)
+{
+	Bits* ans;
+
+	ans = newbits(len);
+	memmove(ans->data, buf, len);
+	ans->unusedbits = unusedbits;
+	return ans;
+}
+
+static void
+freebits(Bits* b)
+{
+	free(b);
+}
+
+static Elist*
+mkel(Elem e, Elist* tail)
+{
+	Elist* el;
+
+	el = (Elist*)emalloc(sizeof(Elist));
+	setmalloctag(el, getcallerpc(&e));
+	el->hd = e;
+	el->tl = tail;
+	return el;
+}
+
+static int
+elistlen(Elist* el)
+{
+	int ans = 0;
+	while(el != nil) {
+		ans++;
+		el = el->tl;
+	}
+	return ans;
+}
+
+/* Frees elist, but not fields inside values of constituent elems */
+static void
+freeelist(Elist* el)
+{
+	Elist* next;
+
+	while(el != nil) {
+		next = el->tl;
+		free(el);
+		el = next;
+	}
+}
+
+/* free any allocated structures inside v (recursively freeing Elists) */
+static void
+freevalfields(Value* v)
+{
+	Elist* el;
+	Elist* l;
+	if(v == nil)
+		return;
+	switch(v->tag) {
+ 	case VOctets:
+		freebytes(v->u.octetsval);
+		break;
+	case VBigInt:
+		freebytes(v->u.bigintval);
+		break;
+	case VReal:
+		freebytes(v->u.realval);
+		break;
+	case VOther:
+		freebytes(v->u.otherval);
+		break;
+	case VBitString:
+		freebits(v->u.bitstringval);
+		break;
+	case VObjId:
+		freeints(v->u.objidval);
+		break;
+	case VString:
+		free(v->u.stringval);
+		break;
+	case VSeq:
+		el = v->u.seqval;
+		for(l = el; l != nil; l = l->tl)
+			freevalfields(&l->hd.val);
+		freeelist(el);
+		break;
+	case VSet:
+		el = v->u.setval;
+		for(l = el; l != nil; l = l->tl)
+			freevalfields(&l->hd.val);
+		freeelist(el);
+		break;
+	}
+	memset(v, 0, sizeof(*v));
+}
+
+static mpint*
+asn1mpint(Elem *e)
+{
+	Bytes *b;
+	int v;
+
+	if(is_int(e, &v))
+		return itomp(v, nil);
+	if(is_bigint(e, &b))
+		return betomp(b->data, b->len, nil);
+	return nil;
+}
+
+/* end of general ASN1 functions */
+
+
+
+
+
+/*=============================================================*/
+/*
+ * Decode and parse an X.509 Certificate, defined by this ASN1:
+ *	Certificate ::= SEQUENCE {
+ *		certificateInfo CertificateInfo,
+ *		signatureAlgorithm AlgorithmIdentifier,
+ *		signature BIT STRING }
+ *
+ *	CertificateInfo ::= SEQUENCE {
+ *		version [0] INTEGER DEFAULT v1 (0),
+ *		serialNumber INTEGER,
+ *		signature AlgorithmIdentifier,
+ *		issuer Name,
+ *		validity Validity,
+ *		subject Name,
+ *		subjectPublicKeyInfo SubjectPublicKeyInfo }
+ *	(version v2 has two more fields, optional unique identifiers for
+ *  issuer and subject; since we ignore these anyway, we won't parse them)
+ *
+ *	Validity ::= SEQUENCE {
+ *		notBefore UTCTime,
+ *		notAfter UTCTime }
+ *
+ *	SubjectPublicKeyInfo ::= SEQUENCE {
+ *		algorithm AlgorithmIdentifier,
+ *		subjectPublicKey BIT STRING }
+ *
+ *	AlgorithmIdentifier ::= SEQUENCE {
+ *		algorithm OBJECT IDENTIFER,
+ *		parameters ANY DEFINED BY ALGORITHM OPTIONAL }
+ *
+ *	Name ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ *	RelativeDistinguishedName ::= SETOF SIZE(1..MAX) OF AttributeTypeAndValue
+ *
+ *	AttributeTypeAndValue ::= SEQUENCE {
+ *		type OBJECT IDENTIFER,
+ *		value DirectoryString }
+ *	(selected attributes have these Object Ids:
+ *		commonName {2 5 4 3}
+ *		countryName {2 5 4 6}
+ *		localityName {2 5 4 7}
+ *		stateOrProvinceName {2 5 4 8}
+ *		organizationName {2 5 4 10}
+ *		organizationalUnitName {2 5 4 11}
+ *	)
+ *
+ *	DirectoryString ::= CHOICE {
+ *		teletexString TeletexString,
+ *		printableString PrintableString,
+ *		universalString UniversalString }
+ *
+ *  See rfc1423, rfc2437 for AlgorithmIdentifier, subjectPublicKeyInfo, signature.
+ *
+ *  Not yet implemented:
+ *   CertificateRevocationList ::= SIGNED SEQUENCE{
+ *           signature       AlgorithmIdentifier,
+ *           issuer          Name,
+ *           lastUpdate      UTCTime,
+ *           nextUpdate      UTCTime,
+ *           revokedCertificates
+ *                           SEQUENCE OF CRLEntry OPTIONAL}
+ *   CRLEntry ::= SEQUENCE{
+ *           userCertificate SerialNumber,
+ *           revocationDate UTCTime}
+ */
+
+typedef struct CertX509 {
+	int	serial;
+	char*	issuer;
+	char*	validity_start;
+	char*	validity_end;
+	char*	subject;
+	int	publickey_alg;
+	Bits*	publickey;
+	int	signature_alg;
+	Bits*	signature;
+	int	curve;
+} CertX509;
+
+/* Algorithm object-ids */
+enum {
+	ALG_rsaEncryption,
+	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 Ints15 {
+	int		len;
+	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 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 Bytes* encode_digest(DigestAlg *da, uchar *digest);
+
+static Ints15 oid_secp256r1 = {7, 1, 2, 840, 10045, 3, 1, 7};
+static Ints15 oid_secp384r1 = {5, 1, 3, 132, 0, 34};
+
+static Ints *namedcurves_oid_tab[] = {
+	(Ints*)&oid_secp256r1,
+	(Ints*)&oid_secp384r1,
+	nil,
+};
+static void (*namedcurves[])(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h) = {
+	secp256r1,
+	secp384r1,
+	nil,
+};
+
+static void
+freecert(CertX509* c)
+{
+	if(c == nil)
+		return;
+	free(c->issuer);
+	free(c->validity_start);
+	free(c->validity_end);
+	free(c->subject);
+	freebits(c->publickey);
+	freebits(c->signature);
+	free(c);
+}
+
+/*
+ * Parse the Name ASN1 type.
+ * The sequence of RelativeDistinguishedName's gives a sort of pathname,
+ * from most general to most specific.  Each element of the path can be
+ * one or more (but usually just one) attribute-value pair, such as
+ * countryName="US".
+ * We'll just form a "postal-style" address string by concatenating the elements
+ * from most specific to least specific, separated by commas.
+ * Return name-as-string (which must be freed by caller).
+ */
+static char*
+parse_name(Elem* e)
+{
+	Elist* el;
+	Elem* es;
+	Elist* esetl;
+	Elem* eat;
+	Elist* eatl;
+	char* s;
+	enum { MAXPARTS = 100 };
+	char* parts[MAXPARTS];
+	int i;
+	int plen;
+	char* ans = nil;
+
+	if(!is_seq(e, &el))
+		goto errret;
+	i = 0;
+	plen = 0;
+	while(el != nil) {
+		es = &el->hd;
+		if(!is_set(es, &esetl))
+			goto errret;
+		while(esetl != nil) {
+			eat = &esetl->hd;
+			if(!is_seq(eat, &eatl) || elistlen(eatl) != 2)
+				goto errret;
+			if(!is_string(&eatl->tl->hd, &s) || i>=MAXPARTS)
+				goto errret;
+			parts[i++] = s;
+			plen += strlen(s) + 2;		/* room for ", " after */
+			esetl = esetl->tl;
+		}
+		el = el->tl;
+	}
+	if(i > 0) {
+		ans = (char*)emalloc(plen);
+		*ans = '\0';
+		while(--i >= 0) {
+			s = parts[i];
+			strcat(ans, s);
+			if(i > 0)
+				strcat(ans, ", ");
+		}
+	}
+
+errret:
+	return ans;
+}
+
+/*
+ * Parse an AlgorithmIdentifer ASN1 type.
+ * Look up the oid in oid_tab and return one of OID_rsaEncryption, etc..,
+ * or -1 if not found.
+ * For now, ignore parameters, since none of our algorithms need them.
+ */
+static int
+parse_alg(Elem* e)
+{
+	Elist* el;
+	Ints* oid;
+
+	if(!is_seq(e, &el) || el == nil || !is_oid(&el->hd, &oid))
+		return -1;
+	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(uchar *buf, int len)
+{
+	int ok = 0;
+	int n;
+	Elem  ecert;
+	Elem* ecertinfo;
+	Elem* esigalg;
+	Elem* esig;
+	Elem* eserial;
+	Elem* eissuer;
+	Elem* evalidity;
+	Elem* esubj;
+	Elem* epubkey;
+	Elist* el;
+	Elist* elcert = nil;
+	Elist* elcertinfo = nil;
+	Elist* elvalidity = nil;
+	Elist* elpubkey = nil;
+	Bits* bits = nil;
+	Bytes* b;
+	Elem* e;
+	CertX509* c = nil;
+
+	if(decode(buf, len, &ecert) != ASN_OK)
+		goto errret;
+
+	c = (CertX509*)emalloc(sizeof(CertX509));
+	c->serial = -1;
+	c->issuer = nil;
+	c->validity_start = nil;
+	c->validity_end = nil;
+	c->subject = nil;
+	c->publickey_alg = -1;
+	c->publickey = nil;
+	c->signature_alg = -1;
+	c->signature = nil;
+
+	/* Certificate */
+ 	if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3)
+		goto errret;
+ 	ecertinfo = &elcert->hd;
+ 	el = elcert->tl;
+ 	esigalg = &el->hd;
+	c->signature_alg = parse_alg(esigalg);
+ 	el = el->tl;
+ 	esig = &el->hd;
+
+	/* Certificate Info */
+	if(!is_seq(ecertinfo, &elcertinfo))
+		goto errret;
+	n = elistlen(elcertinfo);
+  	if(n < 6)
+		goto errret;
+	eserial =&elcertinfo->hd;
+ 	el = elcertinfo->tl;
+ 	/* check for optional version, marked by explicit context tag 0 */
+	if(eserial->tag.class == Context && eserial->tag.num == 0) {
+ 		eserial = &el->hd;
+ 		if(n < 7)
+ 			goto errret;
+ 		el = el->tl;
+ 	}
+
+	if(parse_alg(&el->hd) != c->signature_alg)
+		goto errret;
+ 	el = el->tl;
+ 	eissuer = &el->hd;
+ 	el = el->tl;
+ 	evalidity = &el->hd;
+ 	el = el->tl;
+ 	esubj = &el->hd;
+ 	el = el->tl;
+ 	epubkey = &el->hd;
+	if(!is_int(eserial, &c->serial)) {
+		if(!is_bigint(eserial, &b))
+			goto errret;
+		c->serial = -1;	/* else we have to change cert struct */
+  	}
+	c->issuer = parse_name(eissuer);
+	if(c->issuer == nil)
+		goto errret;
+	/* Validity */
+  	if(!is_seq(evalidity, &elvalidity))
+		goto errret;
+	if(elistlen(elvalidity) != 2)
+		goto errret;
+	e = &elvalidity->hd;
+	if(!is_time(e, &c->validity_start))
+		goto errret;
+	e->val.u.stringval = nil;	/* string ownership transfer */
+	e = &elvalidity->tl->hd;
+ 	if(!is_time(e, &c->validity_end))
+		goto errret;
+	e->val.u.stringval = nil;	/* string ownership transfer */
+
+	/* resume CertificateInfo */
+ 	c->subject = parse_name(esubj);
+	if(c->subject == nil)
+		goto errret;
+
+	/* SubjectPublicKeyInfo */
+	if(!is_seq(epubkey, &elpubkey))
+		goto errret;
+	if(elistlen(elpubkey) != 2)
+		goto errret;
+
+	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;
+	}
+	elpubkey = elpubkey->tl;
+	if(!is_bitstring(&elpubkey->hd, &bits))
+		goto errret;
+	elpubkey->hd.val.u.bitstringval = nil;	/* transfer ownership */
+	c->publickey = bits;
+
+	/*resume Certificate */
+	if(c->signature_alg < 0)
+		goto errret;
+	if(!is_bitstring(esig, &bits))
+		goto errret;
+	esig->val.u.bitstringval = nil;	/* transfer ownership */
+	c->signature = bits;
+	ok = 1;
+
+errret:
+	freevalfields(&ecert.val);	/* recurses through lists, too */
+	if(!ok){
+		freecert(c);
+		c = nil;
+	}
+	return c;
+}
+
+/*
+ *	RSAPublickKey ::= SEQUENCE {
+ *		modulus INTEGER,
+ *		publicExponent INTEGER
+ *	}
+ */
+RSApub*
+asn1toRSApub(uchar *buf, int len)
+{
+	Elem e;
+	Elist *el;
+	RSApub* key;
+
+	key = nil;
+	if(decode(buf, len, &e) != ASN_OK)
+		goto errret;
+	if(!is_seq(&e, &el) || elistlen(el) != 2)
+		goto errret;
+
+	key = rsapuballoc();
+	if((key->n = asn1mpint(&el->hd)) == nil)
+		goto errret;
+	el = el->tl;
+	if((key->ek = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	freevalfields(&e.val);
+	return key;
+errret:
+	freevalfields(&e.val);
+	rsapubfree(key);
+	return nil;
+
+}
+
+/*
+ *	RSAPrivateKey ::= SEQUENCE {
+ *		version Version,
+ *		modulus INTEGER, -- n
+ *		publicExponent INTEGER, -- e
+ *		privateExponent INTEGER, -- d
+ *		prime1 INTEGER, -- p
+ *		prime2 INTEGER, -- q
+ *		exponent1 INTEGER, -- d mod (p-1)
+ *		exponent2 INTEGER, -- d mod (q-1)
+ *		coefficient INTEGER -- (inverse of q) mod p }
+ */
+RSApriv*
+asn1toRSApriv(uchar *buf, int len)
+{
+	int version;
+	Elem e;
+	Elist *el;
+	Bytes *b;
+	RSApriv* key = nil;
+
+	if(decode(buf, len, &e) != ASN_OK)
+		goto errret;
+	if(!is_seq(&e, &el))
+		goto errret;
+
+	if(!is_int(&el->hd, &version) || version != 0)
+		goto errret;
+
+	if(elistlen(el) != 9){
+		if(elistlen(el) == 3
+		&& parse_alg(&el->tl->hd) == ALG_rsaEncryption
+		&& is_octetstring(&el->tl->tl->hd, &b)){
+			key = asn1toRSApriv(b->data, b->len);
+			if(key != nil)
+				goto done;
+		}
+		goto errret;
+	}
+
+	key = rsaprivalloc();
+	el = el->tl;
+	if((key->pub.n = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->pub.ek = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->dk = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->q = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->p = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->kq = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->kp = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+	el = el->tl;
+	if((key->c2 = asn1mpint(&el->hd)) == nil)
+		goto errret;
+
+done:
+	freevalfields(&e.val);
+	return key;
+errret:
+	freevalfields(&e.val);
+	rsaprivfree(key);
+	return nil;
+}
+
+/*
+ * 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 int
+digest_certinfo(uchar *cert, int ncert, DigestAlg *da, uchar *digest)
+{
+	uchar *info, *p, *pend;
+	int isconstr, length;
+	Tag tag;
+	Elem elem;
+
+	p = cert;
+	pend = cert + ncert;
+	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 ||
+	   p+length < p)
+		return -1;
+	info = p;
+	if(ber_decode(&p, pend, &elem) != ASN_OK)
+		return -1;
+	freevalfields(&elem.val);
+	if(elem.tag.num != SEQUENCE)
+		return -1;
+	(*da->fun)(info, p - info, digest, nil);
+	return da->len;
+}
+
+mpint*
+pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype)
+{
+	int i, n = (mpsignif(modulus)-1)/8;
+	int pad = n - 2 - len;
+	uchar *p;
+	mpint *mp;
+
+	if(pad < 8){
+		werrstr("rsa modulus too small");
+		return nil;
+	}
+	if((p = malloc(n)) == nil)
+		return nil;
+	p[0] = blocktype;
+	switch(blocktype){
+	default:
+	case 1:
+		memset(p+1, 0xFF, pad);
+		break;
+	case 2:
+		for(i=1; i <= pad; i++)
+			p[i] = 1 + nfastrand(255);
+		break;
+	}
+	p[1+pad] = 0;
+	memmove(p+2+pad, buf, len);
+	mp = betomp(p, n, nil);
+	free(p);
+	return mp;
+}
+
+int
+pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype)
+{
+	uchar *p = buf + 1, *e = buf + len;
+
+	if(len < 1 || len != (mpsignif(modulus)-1)/8 || buf[0] != blocktype)
+		return -1;
+	switch(blocktype){
+	default:
+	case 1:
+		while(p < e && *p == 0xFF)
+			p++;
+		break;
+	case 2:
+		while(p < e && *p != 0x00)
+			p++;
+		break;
+	}
+	if(p - buf <= 8 || p >= e || *p++ != 0x00)
+		return -1;
+	memmove(buf, p, len = e - p);
+	return len;
+}
+
+static char Ebadsig[] = "bad signature";
+
+char*
+X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk)
+{
+	mpint *x, *y;
+	DigestAlg **dp;
+	Bytes *digest;
+	uchar *buf;
+	int len;
+	char *err;
+
+	x = betomp(sig, siglen, nil);
+	y = rsaencrypt(pk, x, nil);
+	mpfree(x);
+	len = mptobe(y, nil, 0, &buf);
+	mpfree(y);	
+
+	err = Ebadsig;
+	len = pkcs1unpadbuf(buf, len, pk->n, 1);
+	if(len == edigestlen && tsmemcmp(buf, edigest, edigestlen) == 0)
+		err = nil;
+	for(dp = digestalg; err != nil && *dp != nil; dp++){
+		if((*dp)->len != edigestlen)
+			continue;
+		digest = encode_digest(*dp, edigest);
+		if(digest->len == len && tsmemcmp(digest->data, buf, len) == 0)
+			err = nil;
+		freebytes(digest);
+	}
+	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 = Ebadsig;
+	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;
+}
+
+static void
+copysubject(char *name, int nname, char *subject)
+{
+	char *e;
+
+	if(name == nil)
+		return;
+	memset(name, 0, nname);
+	if(subject == nil)
+		return;
+	strncpy(name, subject, nname-1);
+	e = strchr(name, ',');
+	if(e != nil)
+		*e = 0;	/* take just CN part of Distinguished Name */
+}
+
+ECpub*
+X509toECpub(uchar *cert, int ncert, char *name, int nname, ECdomain *dom)
+{
+	CertX509 *c;
+	ECpub *pub;
+
+	c = decode_cert(cert, ncert);
+	if(c == nil)
+		return nil;
+	copysubject(name, nname, c->subject);
+	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;
+	CertX509 *c;
+	int digestlen;
+	uchar digest[MAXdlen];
+
+	c = decode_cert(cert, ncert);
+	if(c == nil)
+		return "cannot decode cert";
+	digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
+	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)
+{
+	CertX509 *c;
+	RSApub *pub;
+
+	c = decode_cert(cert, ncert);
+	if(c == nil)
+		return nil;
+	copysubject(name, nname, c->subject);
+	pub = nil;
+	if(c->publickey_alg == ALG_rsaEncryption)
+		pub = asn1toRSApub(c->publickey->data, c->publickey->len);
+	freecert(c);
+	return pub;
+}
+
+char*
+X509rsaverify(uchar *cert, int ncert, RSApub *pk)
+{
+	char *e;
+	CertX509 *c;
+	int digestlen;
+	uchar digest[MAXdlen];
+
+	c = decode_cert(cert, ncert);
+	if(c == nil)
+		return "cannot decode cert";
+	digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
+	if(digestlen <= 0){
+		freecert(c);
+		return "cannot decode certinfo";
+	}
+	e = X509rsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
+	freecert(c);
+	return e;
+}
+
+/* ------- Elem constructors ---------- */
+static Elem
+Null(void)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = NULLTAG;
+	e.val.tag = VNull;
+	return e;
+}
+
+static Elem
+mkint(int j)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = INTEGER;
+	e.val.tag = VInt;
+	e.val.u.intval = j;
+	return e;
+}
+
+static Elem
+mkbigint(mpint *p)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = INTEGER;
+	e.val.tag = VBigInt;
+	e.val.u.bigintval = newbytes((mpsignif(p)+8)/8);
+	if(p->sign < 0){
+		mpint *s = mpnew(e.val.u.bigintval->len*8+1);
+		mpleft(mpone, e.val.u.bigintval->len*8, s);
+		mpadd(p, s, s);
+		mptober(s, e.val.u.bigintval->data, e.val.u.bigintval->len);
+		mpfree(s);
+	} else {
+		mptober(p, e.val.u.bigintval->data, e.val.u.bigintval->len);
+	}
+	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, int t)
+{
+	Elem e;
+
+	if(t == DirectoryString)
+		t = printable(s) ? PrintableString : UTF8String;
+	e.tag.class = Universal;
+	e.tag.num = t;
+	e.val.tag = VString;
+	e.val.u.stringval = estrdup(s);
+	return e;
+}
+
+static Elem
+mkoctet(uchar *buf, int buflen)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = OCTET_STRING;
+	e.val.tag = VOctets;
+	e.val.u.octetsval = makebytes(buf, buflen);
+	return e;
+}
+
+static Elem
+mkbits(uchar *buf, int buflen)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = BIT_STRING;
+	e.val.tag = VBitString;
+	e.val.u.bitstringval = makebits(buf, buflen, 0);
+	return e;
+}
+
+static Elem
+mkutc(long t)
+{
+	Elem e;
+	char utc[50];
+	Tm *tm = gmtime(t);
+
+	e.tag.class = Universal;
+	e.tag.num = UTCTime;
+	e.val.tag = VString;
+	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;
+}
+
+static Elem
+mkoid(Ints *oid)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = OBJECT_ID;
+	e.val.tag = VObjId;
+	e.val.u.objidval = makeints(oid->data, oid->len);
+	return e;
+}
+
+static Elem
+mkseq(Elist *el)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = SEQUENCE;
+	e.val.tag = VSeq;
+	e.val.u.seqval = el;
+	return e;
+}
+
+static Elem
+mkset(Elist *el)
+{
+	Elem e;
+
+	e.tag.class = Universal;
+	e.tag.num = SETOF;
+	e.val.tag = VSet;
+	e.val.u.setval = el;
+	return e;
+}
+
+static Elem
+mkalg(int alg)
+{
+	return mkseq(mkel(mkoid(alg_oid_tab[alg]), mkel(Null(), nil)));
+}
+
+typedef struct Ints7pref {
+	int	len;
+	int	data[7];
+	char	prefix[4];
+	int	stype;
+} Ints7pref;
+Ints7pref DN_oid[] = {
+	{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, oid->stype), nil))), nil));
+}
+
+static Elem
+mkDN(char *dn)
+{
+	int i, j, nf;
+	char *f[20], *prefix, *d2 = estrdup(dn);
+	Elist* el = nil;
+
+	nf = tokenize(d2, f, nelem(f));
+	for(i=nf-1; i>=0; i--){
+		for(j=0; j<nelem(DN_oid); j++){
+			prefix = DN_oid[j].prefix;
+			if(strncmp(f[i],prefix,strlen(prefix))==0){
+				el = mkel(mkname(&DN_oid[j],f[i]+strlen(prefix)), el);
+				break;
+			}
+		}
+	}
+	free(d2);
+	return mkseq(el);
+}
+
+/*
+ * DigestInfo ::= SEQUENCE {
+ *	digestAlgorithm AlgorithmIdentifier,
+ *	digest OCTET STRING }
+ */
+static Bytes*
+encode_digest(DigestAlg *da, uchar *digest)
+{
+	Bytes *b = nil;
+	Elem e = mkseq(
+		mkel(mkalg(da->alg),
+		mkel(mkoctet(digest, da->len),
+		nil)));
+	encode(e, &b);
+	freevalfields(&e.val);
+	return b;
+}
+
+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;
+}
+
+static Elem
+mkcont(Elem e, int num)
+{
+	e = mkseq(mkel(e, nil));
+	e.tag.class = Context;
+	e.tag.num = num;
+	return e;
+}
+
+static Elem
+mkaltname(char *s)
+{
+	Elem e;
+	int i;
+
+	for(i=0; i<nelem(DN_oid); i++){
+		if(strstr(s, DN_oid[i].prefix) != nil)
+			return mkcont(mkDN(s), 4); /* DN */
+	}
+	e = mkstring(s, IA5String);
+	e.tag.class = Context;
+	e.tag.num = strchr(s, '@') != nil ? 1 : 2; /* email : DNS */
+	return e;
+}
+
+static Elist*
+mkaltnames(char *alts)
+{
+	Elist *el;
+	char *s, *p;
+
+	if(alts == nil)
+		return nil;
+
+	el = nil;
+	alts = estrdup(alts);
+	for(s = alts; s != nil; s = p){
+		while(*s == ' ')
+			s++;
+		if(*s == '\0')
+			break;
+		if((p = strchr(s, ',')) != nil)
+			*p++ = 0;
+		el = mkel(mkaltname(s), el);
+	}
+	free(alts);
+	return el;
+}
+
+static Elist*
+mkextel(Elem e, Ints *oid, Elist *el)
+{
+	Bytes *b = nil;
+
+	if(encode(e, &b) == ASN_OK){
+		el = mkel(mkseq(
+			mkel(mkoid(oid),
+			mkel(mkoctet(b->data, b->len),
+			nil))), el);
+		freebytes(b);
+	}
+	freevalfields(&e.val);
+	return el;
+}
+
+static Ints15 oid_subjectAltName = {4, 2, 5, 29, 17 };
+static Ints15 oid_extensionRequest = { 7, 1, 2, 840, 113549, 1, 9, 14};
+
+static Elist*
+mkextensions(char *alts, int req)
+{
+	Elist *sl, *xl;
+
+	xl = nil;
+	if((sl = mkaltnames(alts)) != nil)
+		xl = mkextel(mkseq(sl), (Ints*)&oid_subjectAltName, xl);
+	if(xl != nil){
+		if(req) return mkel(mkcont(mkseq(
+			mkel(mkoid((Ints*)&oid_extensionRequest),
+			mkel(mkset(mkel(mkseq(xl), nil)), nil))), 0), nil);
+		return mkel(mkcont(mkseq(xl), 3), nil);
+	}
+	return nil;
+}
+
+static char*
+splitalts(char *s)
+{
+	int q;
+
+	for(q = 0; *s != '\0'; s++){
+		if(*s == '\'')
+			q ^= 1;
+		else if(q == 0 && *s == ','){
+			*s++ = 0;
+			return s;
+		}
+	}
+	return nil;
+}
+
+static Bytes*
+encode_rsapubkey(RSApub *pk)
+{
+	Bytes *b = nil;
+	Elem e = mkseq(
+		mkel(mkbigint(pk->n),
+		mkel(mpsignif(pk->ek)<32 ? mkint(mptoi(pk->ek)) : mkbigint(pk->ek),
+		nil)));
+	encode(e, &b);
+	freevalfields(&e.val);
+	return b;
+}
+
+int
+asn1encodeRSApub(RSApub *pk, uchar *buf, int len)
+{
+	Bytes *b = encode_rsapubkey(pk);
+	if(b == nil)
+		return -1;
+	if(b->len > len){
+		freebytes(b);
+		werrstr("buffer too small");
+		return -1;
+	}
+	memmove(buf, b->data, len = b->len);
+	freebytes(b);
+	return len;
+}
+
+uchar*
+X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen)
+{
+	int serial = 0, sigalg = ALG_sha256WithRSAEncryption;
+	uchar *cert = nil;
+	Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
+	Elem e, certinfo;
+	DigestAlg *da;
+	uchar digest[MAXdlen], *buf;
+	int buflen;
+	mpint *pkcs1;
+	char *alts;
+
+	if((pkbytes = encode_rsapubkey(&priv->pub)) == nil)
+		return nil;
+
+	subj = estrdup(subj);
+	alts = splitalts(subj);
+
+	e = mkseq(
+		mkel(mkcont(mkint(2), 0),
+		mkel(mkint(serial),
+		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))),
+		mkextensions(alts, 0)))))))));
+	freebytes(pkbytes);
+	if(encode(e, &certinfobytes) != ASN_OK)
+		goto errret;
+
+	da = digestalg[sigalg];
+	(*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
+	freebytes(certinfobytes);
+	certinfo = e;
+
+	sigbytes = encode_digest(da, digest);
+	if(sigbytes == nil)
+		goto errret;
+	pkcs1 = pkcs1padbuf(sigbytes->data, sigbytes->len, priv->pub.n, 1);
+	freebytes(sigbytes);
+	if(pkcs1 == nil)
+		goto errret;
+
+	rsadecrypt(priv, pkcs1, pkcs1);
+	buflen = mptobe(pkcs1, nil, 0, &buf);
+	mpfree(pkcs1);
+	e = mkseq(
+		mkel(certinfo,
+		mkel(mkalg(sigalg),
+		mkel(mkbits(buf, buflen),
+		nil))));
+	free(buf);
+	if(encode(e, &certbytes) != ASN_OK)
+		goto errret;
+	if(certlen != nil)
+		*certlen = certbytes->len;
+	cert = (uchar*)certbytes;
+	memmove(cert, certbytes->data, certbytes->len);
+errret:
+	freevalfields(&e.val);
+	free(subj);
+	return cert;
+}
+
+uchar*
+X509rsareq(RSApriv *priv, char *subj, int *certlen)
+{
+	/* RFC 2314, PKCS #10 Certification Request Syntax */
+	int version = 0, sigalg = ALG_sha256WithRSAEncryption;
+	uchar *cert = nil;
+	Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
+	Elem e, certinfo;
+	DigestAlg *da;
+	uchar digest[MAXdlen], *buf;
+	int buflen;
+	mpint *pkcs1;
+	char *alts;
+
+	if((pkbytes = encode_rsapubkey(&priv->pub)) == nil)
+		return nil;
+
+	subj = estrdup(subj);
+	alts = splitalts(subj);
+
+	e = mkseq(
+		mkel(mkint(version),
+		mkel(mkDN(subj),
+		mkel(mkseq(
+			mkel(mkalg(ALG_rsaEncryption),
+			mkel(mkbits(pkbytes->data, pkbytes->len),
+			nil))),
+		mkextensions(alts, 1)))));
+	freebytes(pkbytes);
+	if(encode(e, &certinfobytes) != ASN_OK)
+		goto errret;
+	da = digestalg[sigalg];
+	(*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
+	freebytes(certinfobytes);
+	certinfo = e;
+
+	sigbytes = encode_digest(da, digest);
+	if(sigbytes == nil)
+		goto errret;
+	pkcs1 = pkcs1padbuf(sigbytes->data, sigbytes->len, priv->pub.n, 1);
+	freebytes(sigbytes);
+	if(pkcs1 == nil)
+		goto errret;
+
+	rsadecrypt(priv, pkcs1, pkcs1);
+	buflen = mptobe(pkcs1, nil, 0, &buf);
+	mpfree(pkcs1);
+	e = mkseq(
+		mkel(certinfo,
+		mkel(mkalg(sigalg),
+		mkel(mkbits(buf, buflen),
+		nil))));
+	free(buf);
+	if(encode(e, &certbytes) != ASN_OK)
+		goto errret;
+	if(certlen != nil)
+		*certlen = certbytes->len;
+	cert = (uchar*)certbytes;
+	memmove(cert, certbytes->data, certbytes->len);
+errret:
+	freevalfields(&e.val);
+	free(subj);
+	return cert;
+}
+
+static void
+digestSPKI(int alg, uchar *pubkey, int npubkey, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
+{
+	Bytes *b = nil;
+	Elem e = mkseq(mkel(mkalg(alg), mkel(mkbits(pubkey, npubkey), nil)));
+	encode(e, &b);
+	freevalfields(&e.val);
+	(*fun)(b->data, b->len, digest, nil);
+	freebytes(b);
+}
+
+int
+X509digestSPKI(uchar *cert, int ncert, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
+{
+	CertX509 *c;
+
+	c = decode_cert(cert, ncert);
+	if(c == nil){
+		werrstr("cannot decode cert");
+		return -1;
+	}
+	digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, fun, digest);
+	freecert(c);
+	return 0;
+}
+
+static char*
+tagdump(Tag tag)
+{
+	static char buf[32];
+
+	if(tag.class != Universal){
+		snprint(buf, sizeof(buf), "class%d,num%d", tag.class, tag.num);
+		return buf;
+	}
+	switch(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:
+		snprint(buf, sizeof(buf), "Universal,num%d", tag.num);
+		return buf;
+	}
+}
+
+static void
+edump(Elem e)
+{
+	Value v;
+	Elist *el;
+	int i;
+
+	print("%s{", tagdump(e.tag));
+	v = e.val;
+	switch(v.tag){
+	case VBool: print("Bool %d",v.u.boolval); break;
+	case VInt: print("Int %d",v.u.intval); break;
+	case VOctets: print("Octets[%d] %.2x%.2x...",v.u.octetsval->len,v.u.octetsval->data[0],v.u.octetsval->data[1]); break;
+	case VBigInt: print("BigInt[%d] %.2x%.2x...",v.u.bigintval->len,v.u.bigintval->data[0],v.u.bigintval->data[1]); break;
+	case VReal: print("Real..."); break;
+	case VOther: print("Other..."); break;
+	case VBitString: print("BitString[%d]...", v.u.bitstringval->len*8 - v.u.bitstringval->unusedbits); break;
+	case VNull: print("Null"); break;
+	case VEOC: print("EOC..."); break;
+	case VObjId: print("ObjId");
+		for(i = 0; i<v.u.objidval->len; i++)
+			print(" %d", v.u.objidval->data[i]);
+		break;
+	case VString: print("String \"%s\"",v.u.stringval); break;
+	case VSeq: print("Seq\n");
+		for(el = v.u.seqval; el!=nil; el = el->tl)
+			edump(el->hd);
+		break;
+	case VSet: print("Set\n");
+		for(el = v.u.setval; el!=nil; el = el->tl)
+			edump(el->hd);
+		break;
+	}
+	print("}\n");
+}
+
+void
+asn1dump(uchar *der, int len)
+{
+	Elem e;
+
+	if(decode(der, len, &e) != ASN_OK){
+		print("didn't parse\n");
+		exits("didn't parse");
+	}
+	edump(e);
+}
+
+void
+X509dump(uchar *cert, int ncert)
+{
+	char *e;
+	CertX509 *c;
+	RSApub *rsapub;
+	ECpub *ecpub;
+	ECdomain ecdom;
+	int digestlen;
+	uchar digest[MAXdlen];
+
+	print("begin X509dump\n");
+	c = decode_cert(cert, ncert);
+	if(c == nil){
+		print("cannot decode cert\n");
+		return;
+	}
+
+	digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
+	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);
+	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);
+
+	switch(c->publickey_alg){
+	case ALG_rsaEncryption:
+		rsapub = asn1toRSApub(c->publickey->data, c->publickey->len);
+		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;
+	}
+
+	digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, sha2_256, digest);
+	print("publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);
+
+	sha2_256(cert, ncert, digest, nil);
+	print("cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);
+
+	sha1(cert, ncert, digest, nil);
+	print("cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);
+
+	freecert(c);
+	print("end X509dump\n");
+}