shithub: riscv

Download patch

ref: 2dae1ed53a73d81bfb86778793a6bda265d5140d
parent: e064752dd476b7a2f76567f8cc15f9c2645e5d3d
author: cinap_lenrek <[email protected]>
date: Tue Jan 5 22:09:00 EST 2016

auth: release dp9ik implementation and reentrant factotum

--- a/sys/include/authsrv.h
+++ b/sys/include/authsrv.h
@@ -20,16 +20,27 @@
 	AERRLEN=	64,	/* errstr max size in previous proto */
 	DOMLEN=		48,	/* authentication domain name length */
 	DESKEYLEN=	7,	/* encrypt/decrypt des key length */
-	AESKEYLEN=	16,
+	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 */
 
+	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) */
@@ -48,8 +59,7 @@
 	AuthCram=12,	/* CRAM verification for IMAP (RFC2195 & rfc2104) */
 	AuthHttp=13,	/* http domain login */
 	AuthVNC=14,	/* VNC server login (deprecated) */
-
-
+	AuthPAK=19,	/* authenticated diffie hellman key agreement */
 	AuthTs=64,	/* ticket encrypted with server's key */
 	AuthTc,		/* ticket encrypted with client's key */
 	AuthAs,		/* server generated authenticator */
@@ -75,17 +85,19 @@
 	char	chal[CHALLEN];		/* server challenge */
 	char	cuid[ANAMELEN];		/* uid on client */
 	char	suid[ANAMELEN];		/* uid on server */
-	char	key[DESKEYLEN];		/* nonce DES key */
+	uchar	key[NONCELEN];		/* nonce key */
+
+	char	form;			/* (not transmitted) format (0 = des, 1 = ccpoly) */
 };
-#define	TICKETLEN	(CHALLEN+2*ANAMELEN+DESKEYLEN+1)
+#define	MAXTICKETLEN	(12+CHALLEN+2*ANAMELEN+NONCELEN+16)
 
 struct Authenticator
 {
 	char	num;			/* replay protection */
-	char	chal[CHALLEN];
-	ulong	id;			/* authenticator id, ++'d with each auth */
+	char	chal[CHALLEN];		/* server/client challenge */
+	uchar	rand[NONCELEN];		/* server/client nonce */
 };
-#define	AUTHENTLEN	(CHALLEN+4+1)
+#define	MAXAUTHENTLEN	(12+CHALLEN+NONCELEN+16)
 
 struct Passwordreq
 {
@@ -95,7 +107,7 @@
 	char	changesecret;
 	char	secret[SECRETLEN];	/* new secret */
 };
-#define	PASSREQLEN	(2*ANAMELEN+1+1+SECRETLEN)
+#define	MAXPASSREQLEN	(12+2*ANAMELEN+1+SECRETLEN+16)
 
 struct	OChapreply
 {
@@ -115,8 +127,10 @@
 
 struct	Authkey
 {
-	char	des[DESKEYLEN];
-	uchar	aes[AESKEYLEN];
+	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()) */
 };
 
 /*
@@ -132,10 +146,13 @@
 extern	int	convM2PR(char*, int, Passwordreq*, Ticket*);
 
 /*
- *  convert ascii password to DES key
+ *  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
  */
@@ -169,7 +186,7 @@
 };
 
 extern	uchar	nvcsum(void*, int);
-extern int	readnvram(Nvrsafe*, int);
+extern	int	readnvram(Nvrsafe*, int);
 
 /*
  *  call up auth server
@@ -179,7 +196,23 @@
 /*
  *  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]);
--- a/sys/man/6/authsrv
+++ b/sys/man/6/authsrv
@@ -1,6 +1,6 @@
 .TH AUTHSRV 6
 .SH NAME
-authsrv, p9any, p9sk1, p9sk2 \- authentication protocols
+authsrv, p9any, p9sk1, dp9ik \- authentication protocols
 .SH DESCRIPTION
 This manual page describes
 the protocols used to authorize connections, confirm the identities
@@ -14,8 +14,9 @@
 holds for each public machine, such as a CPU server or
 file server, the name of the authentication server that machine uses.
 .PP
-Each machine contains three values important to authentication; a 56-bit DES
-key, a 28-byte authentication ID, and a 48-byte authentication domain name.
+Each machine contains four values important to authentication; a 56-bit DES
+key, a 128-bit AES key, a 28-byte authentication ID, and a 48-byte authentication
+domain name.
 The ID is a user name and identifies who is currently responsible for the
 kernel running on that machine.
 The domain name identifies the machines across which the ID is valid.
@@ -29,7 +30,7 @@
 .I passtokey
 (see
 .IR authsrv (2))
-into a 56-bit DES key and saved in memory.
+into a 56-bit DES and 128-bit AES keys and saved in memory.
 The authentication domain is set to the null string.
 If possible,
 .I factotum
@@ -60,7 +61,7 @@
 a nonce key created for a ticket
 .RB ( key )
 .TP
-.IR K { m }
+.I K{m}
 message
 .I m
 encrypted with key
@@ -91,6 +92,24 @@
 client's desired ID on server
 .RB ( uid ,
 .BR suid )
+.TP
+.I YAc
+client \(-> AS DH public key
+.TP
+.I YBc
+AS \(-> client DH public key
+.TP
+.I YAs
+server \(-> AS DH public key
+.TP
+.I YBs
+AS \(-> server DH public key
+.TP
+.I RNc
+client's 32-byte random string
+.TP
+.I RNs
+server's 32-byte random string
 .PD
 .PP
 The parenthesized names are the ones used in the
@@ -112,8 +131,9 @@
 .IR AuthChap ,
 .IR AuthMSchap ,
 .IR AuthCram ,
+.IR AuthVNC ,
 and
-.IR AuthVNC
+.IR AuthPAK
 .RB ( type )
 are defined in
 .BR <authsrv.h> ,
@@ -142,7 +162,6 @@
 .IR CHs ,
 .IR IDc ,
 .IR IDr
-.sp -\n(PDu
 .TP
 .IR A \(-> C
 .IR AuthOK ,
@@ -265,16 +284,254 @@
 .I Kn
 and therefore 
 .I Ks .
-.PD
+.SS "Password authenticated key exchange"
+Initially, the server and client keys
+.I Ks
+and
+.I Kc
+where equivalent to the password derived 56-bit DES keys, which
+made the encrypted tickets subject to offline dictionary attacks
+and provides a too small key space against brute force attacks
+on current hardware.
 .PP
-.I P9sk2
-is an older variant of 
+The
+.I AuthPAK
+protocol is used to establish new 256-bit random keys with the
+AS for
+.I Ks
+and
+.I Kc
+before each ticket request on the connection.
+.PP
+The protocol is based on SPAKE2EE, where a hash of the user's secret
+is used to encypt the public keys of a Elliptic-Curve Diffie-Hellman
+key exchange. The user's
+.I ID
+and 128-bit AES key is hashed and mapped (using Elligator2)
+into two curve points
+.I PM
+and
+.IR PN ,
+called the
+.IR pakhash .
+Both sides generate a random number
+.IR xa / xb
+and make the public keys
+.IR YA / YB
+as:
+.IR YA = xa*G+PM ,
+.IR YB = xb*G+PN .
+After the public keys have been exchanged, each side calculates the
+shared secret as:
+.IR Z = xa*(YB-PN) = xb*(YA-PM) .
+The shared secret
+.I Z
+is then hashed with the transmitted public keys
+.IR YA | YB
+producing the 256-bit
+.IR pakkey .
+.PP
+The
+.I pakkey
+is then used in place of
+.I Ks
+and
+.I Kc
+to authenticate and encrypt tickets from the AS using
+Chacha20/Poly1305 AEAD for the next following
+request made on the connection.
+.PP
+The protocol (for
+.IR AuthTreq )
+to establish keys
+.I Ks
+and
+.I Kc
+with the AS for
+.I IDs
+and
+.I IDc
+is:
+.TP
+.IR C \(-> A
+.IR AuthPAK ,
+.IR IDs ,
+.IR DN ,
+.IR CHs ,
+.IR IDc ,
+.IR IDr ,
+.IR YAs ,
+.I YAc
+.TP
+.IR A \(-> C
+.IR AuthOK ,
+.IR YBs ,
+.I YBc
+.PP
+The protocol (for
+.IR AuthApop ,
+.IR AuthChap ...)
+to establish a single server key
+.I Ks
+for
+.IR IDs :
+.TP
+.IR C \(-> A
+.IR AuthPAK ,
+.IR \- ,
+.IR DN ,
+.IR CHs ,
+.IR IDs ,
+.IR IDc ,
+.I YAs
+.TP
+.IR A \(-> C
+.IR AuthOK ,
+.I YBs
+.PP
+The protocol (for
+.IR AuthPass )
+to establish a single client key
+.I Kc
+for
+.IR IDc :
+.TP
+.IR C \(-> A
+.IR AuthPAK ,
+.IR \- ,
+.IR \- ,
+.IR CHc ,
+.IR \- ,
+.IR IDc ,
+.I YAc
+.TP
+.IR A \(-> C
+.IR AuthOK ,
+.I YBc
+.SS "Dp9ik"
+The
+.I dp9ik
+protocol is an extended version of
 .I p9sk1
-used only when connecting to pre-9P2000 remote
-execution services.
-It omits the first message and last 
-messages and therefore does not
-authenticate the server to the client.
+that adds the random strings
+.I RNc
+and
+.I RNs
+in the
+.I authenticator
+messages for the session key derivation and uses the
+password authenticated key exchange as described above
+to derive the ticket encryption keys
+.I Ks
+and
+.IR Kc :
+.TP
+.IR C \(-> S
+.I CHc
+.br
+The client starts by sending a random challenge to the server.
+.TP
+.IR S \(-> C
+.IR AuthPAK ,
+.IR IDs ,
+.IR DN ,
+.IR CHs ,
+.IR \- ,
+.IR \- ,
+.IR YAs
+.br
+The server generates a new public key
+.I YAs
+and replies with a
+.I AuthPAK
+request giving its
+.I IDs
+and authentication domain
+.I DNs
+along with its own random challenge
+.I CHs
+and its public key
+.IR YAs .
+.TP
+.IR C \(-> S
+.IR YBs ,
+.IR Ks { AuthTs ,
+.IR CHs ,
+.IR IDc ,
+.IR IDr ,
+.IR Kn },
+.IR Kn { AuthAc ,
+.IR CHs ,
+.IR RNc }
+.br
+The client generates its own public key
+.I YAc
+and adds it along with 
+.I IDc
+and
+.I IDr
+to the
+.I AuthPAK
+request and obtains the public keys
+.I YBs
+and
+.I YBc
+from the AS response. At this point, client and AS
+have completed ther authenticated key exchange and
+derive
+.I Kc
+as described above.
+Then the client requests a ticket pair using the same
+message but with
+.I AuthPAK
+type changed to
+.IR AuthTreq .
+It decrypts his ticket with
+.I Kc
+extracting the shared secret
+.IR Kn .
+The client relays the server's
+.I YBs
+and ticket along with an
+.IR authenticator ,
+the
+.I AuthAc
+message.
+The server finishes his authenticated key exchange
+using
+.I YBs
+and derives
+.I Ks
+to decrypt his ticket to extract the shared secret
+.IR Kn .
+When the decryption of the clients authenticator using
+.I Kn
+is successfull then this proves to the server that the
+client knows
+.I Kn
+and is therefore allowed to authenticate as
+.IR IDr .
+The random string
+.I RNc
+is used in the derivation of the session secret.
+.TP
+.IR S \(-> C
+.IR Kn { AuthAs ,
+.IR CHc ,
+.IR RNs }
+.br
+The server replies with its own authenticator,
+proving to the client that it also knows
+.I Kn
+and contributes its random string
+.IR RNs
+for the session secret.
+.PP
+The 2048-bit session secret is derived with a PRF hashing the
+concatenated random strings
+.IR RNc | RNs
+with the the shared secret key
+.IR Kn .
 .SS "P9any
 .I P9any
 is the standard Plan 9 authentication protocol.
@@ -288,12 +545,10 @@
 .IB proto@authdom
 .IB proto@authdom
 .I ...
-.sp -\n(PDu
 .TP
 .IR C \(-> S
 .I proto
 .I dom
-.sp -\n(PDu
 .TP
 .IR S \(-> C
 .B OK
@@ -349,10 +604,10 @@
 .TP
 .IR C \(-> A
 .IR AuthPass ,
-.IR IDc ,
-.IR DN ,
+.IR \- ,
+.IR \- ,
 .IR CHc ,
-.IR IDc ,
+.IR \- ,
 .IR IDc
 .br
 The client sends a password change ticket request.
@@ -471,18 +726,15 @@
 .I AuthErr
 message may be substituted.
 .de Ok
-.sp -\n(PDu
 .TP
 .IR A \(-> S
 .IR AuthOK ,
-.IR Ks { \\$1 ,
-.IR IDs ,
-.IR DN ,
+.IR Ks { AuthTs ,
 .IR CHs ,
-.IR IDs ,
 .IR IDc ,
+.IR IDc ,
 .IR Kn },
-.IR Kn { AuthTs ,
+.IR Kn { AuthAc ,
 .IR CHs }
 ..
 .PP
@@ -489,21 +741,19 @@
 .TP
 .IR S \(-> A
 .IR AuthChal ,
-.IR IDs ,
+.IR \- ,
 .IR DN ,
 .IR CHs ,
 .IR IDs ,
 .IR IDc
-.sp -\n(PDu
 .TP
 .IR A \(-> S
 .IR AuthOK ,
 .IR challenge
-.sp -\n(PDu
 .TP
 .IR S \(-> A
 .IR response
-.Ok AuthChal
+.Ok
 .IP
 This protocol allows the use of 
 handheld authenticators such as SecureNet
@@ -562,28 +812,26 @@
 same id in both places.
 .TP
 .IR S \(-> A
-AuthApop ,
-.IR IDs ,
+.IR AuthApop ,
+.IR \- ,
 .IR DN ,
 .IR CHs ,
-.IR \- ,
+.IR IDs ,
 .IR \-
-.sp -\n(PDu
 .TP
 .IR A \(-> S
 .IR AuthOKvar ,
 .IR challenge
-.sp -\n(PDu
 .TP
 .IR S \(-> A 
-AuthApop ,
-.IR IDs ,
+.IR AuthApop ,
+.IR \- ,
 .IR DN ,
 .IR CHs ,
-.IR IDc ,
+.IR IDs ,
 .IR IDc ;
 hexadecimal MD5 checksum
-.Ok AuthApop
+.Ok
 .IP
 This protocol implements APOP authentication
 (see
@@ -616,22 +864,20 @@
 .TP
 .IR S \(-> A
 .IR AuthChap ,
-.IR IDs ,
+.IR \- ,
 .IR DN ,
 .IR CHs ,
-.IR \- ,
+.IR IDs ,
 .IR \-
-.sp -\n(PDu
 .TP
 .IR A \(-> S
 .I challenge
-.sp -\n(PDu
 .TP
 .IR S \(-> A
 .IR pktid ,
 .IR IDc ,
 .IR response
-.Ok AuthChap
+.Ok
 .IP
 This protocol implements CHAP authentication
 (see
@@ -648,22 +894,20 @@
 .TP
 .IR S \(-> A
 .IR AuthMSchap ,
-.IR IDs ,
+.IR \- ,
 .IR DN ,
 .IR CHs ,
-.IR \- ,
+.IR IDs ,
 .IR \-
-.sp -\n(PDu
 .TP
 .IR A \(-> S
 .I challenge
-.sp -\n(PDu
 .TP
 .IR S \(-> A
 .IR IDc ,
 .IR lm-response ,
 .IR nt-response
-.Ok AuthMschap
+.Ok
 .IP
 This protocol implements Microsoft's MS-CHAP
 authentication
@@ -682,17 +926,15 @@
 .TP
 .IR S \(-> A
 .IR AuthVNC ,
-.IR IDs ,
+.IR \- ,
 .IR DN ,
 .IR CHs ,
 .IR IDs ,
 .IR IDc
-.sp -\n(PDu
 .TP
 .IR A \(-> S
 .IR AuthOKvar ,
 .I challenge
-.sp -\n(PDu
 .TP
 .IR S \(-> A
 .I response
--- a/sys/src/cmd/auth/authcmdlib.h
+++ b/sys/src/cmd/auth/authcmdlib.h
@@ -53,6 +53,7 @@
 char*	netdecimal(char*);
 char*	netresp(char*, long, char*);
 char*	okpasswd(char*);
+void	private(void);
 int	querybio(char*, char*, Acctbio*);
 void	rdbio(char*, char*, Acctbio*);
 int	readarg(int, char*, int);
--- a/sys/src/cmd/auth/authsrv.c
+++ b/sys/src/cmd/auth/authsrv.c
@@ -8,11 +8,21 @@
 #include <authsrv.h>
 #include "authcmdlib.h"
 
-int debug;
 Ndb *db;
 char raddr[128];
 uchar zeros[16];
 
+typedef struct Keyslot Keyslot;
+struct Keyslot
+{
+	Authkey;
+	char	id[ANAMELEN];
+};
+Keyslot hkey, akey, ukey;
+uchar keyseed[SHA2_256dlen];
+
+char ticketform;
+
 /* Microsoft auth constants */
 enum {
 	MShashlen = 16,
@@ -20,18 +30,19 @@
 	MSresplen = 24,
 };
 
-int	ticketrequest(Ticketreq*);
+void	pak(Ticketreq*);
+void	ticketrequest(Ticketreq*);
 void	challengebox(Ticketreq*);
 void	changepasswd(Ticketreq*);
 void	apop(Ticketreq*, int);
 void	chap(Ticketreq*);
 void	mschap(Ticketreq*);
-void	http(Ticketreq*);
 void	vnc(Ticketreq*);
 int	speaksfor(char*, char*);
 void	replyerror(char*, ...);
 void	getraddr(char*);
-void	mkkey(Authkey*);
+void	initkeyseed(void);
+void	mkkey(Keyslot*);
 void	mkticket(Ticketreq*, Ticket*);
 void	nthash(uchar hash[MShashlen], char *passwd);
 void	lmhash(uchar hash[MShashlen], char *passwd);
@@ -38,7 +49,7 @@
 void	ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom);
 void	mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
 void	desencrypt(uchar data[8], uchar key[7]);
-int	tickauthreply(Ticketreq*, Authkey*);
+void	tickauthreply(Ticketreq*, Authkey*);
 void	safecpy(char*, char*, int);
 
 void
@@ -49,8 +60,9 @@
 	int n;
 
 	ARGBEGIN{
-	case 'd':
-		debug++;
+	case 'N':
+		ticketform = 1;
+		break;
 	}ARGEND
 
 	strcpy(raddr, "unknown");
@@ -59,6 +71,9 @@
 
 	alarm(10*60*1000);	/* kill a connection after 10 minutes */
 
+	private();
+	initkeyseed();
+
 	db = ndbopen("/lib/ndb/auth");
 	if(db == 0)
 		syslog(0, AUTHLOG, "no /lib/ndb/auth");
@@ -89,48 +104,114 @@
 		case AuthCram:
 			apop(&tr, AuthCram);
 			break;
-		case AuthHttp:
-			http(&tr);
-			break;
 		case AuthVNC:
 			vnc(&tr);
 			break;
+		case AuthPAK:
+			pak(&tr);
+			continue;
 		default:
 			syslog(0, AUTHLOG, "unknown ticket request type: %d", tr.type);
 			exits(0);
 		}
+		/* invalidate pak keys */
+		akey.id[0] = 0;
+		hkey.id[0] = 0;
+		ukey.id[0] = 0;
 	}
 	/* not reached */
 }
 
+void
+pak1(char *u, Keyslot *k)
+{
+	uchar y[PAKYLEN];
+	PAKpriv p;
+
+	safecpy(k->id, u, sizeof(k->id));
+	if(!findkey(KEYDB, k->id, k) || tsmemcmp(k->aes, zeros, AESKEYLEN) == 0) {
+		/* make one up so caller doesn't know it was wrong */
+		mkkey(k);
+		authpak_hash(k, k->id);
+	}
+	authpak_new(&p, k, y, 0);
+	if(write(1, y, PAKYLEN) != PAKYLEN)
+		exits(0);
+	if(readn(0, y, PAKYLEN) != PAKYLEN)
+		exits(0);
+	if(authpak_finish(&p, k, y))
+		exits(0);
+}
+
+void
+pak(Ticketreq *tr)
+{
+	static uchar ok[1] = {AuthOK};
+
+	if(write(1, ok, 1) != 1)
+		exits(0);
+
+	/* invalidate pak keys */
+	akey.id[0] = 0;
+	hkey.id[0] = 0;
+	ukey.id[0] = 0;
+
+	if(tr->hostid[0]) {
+		if(tr->authid[0])
+			pak1(tr->authid, &akey);
+		pak1(tr->hostid, &hkey);
+	} else if(tr->uid[0]) {
+		pak1(tr->uid, &ukey);
+	}
+
+	ticketform = 1;
+}
+
 int
+getkey(char *u, Keyslot *k)
+{
+	/* empty user id is an error */
+	if(*u == 0)
+		exits(0);
+
+	if(k == &hkey && strcmp(u, k->id) == 0)
+		return 1;
+	if(k == &akey && strcmp(u, k->id) == 0)
+		return 1;
+	if(k == &ukey && strcmp(u, k->id) == 0)
+		return 1;
+
+	if(ticketform != 0)
+		exits(0);
+
+	return findkey(KEYDB, u, k);
+}
+
+void
 ticketrequest(Ticketreq *tr)
 {
-	Authkey akey, hkey;
-	char tbuf[2*TICKETLEN+1];
+	char tbuf[2*MAXTICKETLEN+1];
 	Ticket t;
 	int n;
 
-	if(!findkey(KEYDB, tr->authid, &akey)){
+	if(tr->uid[0] == 0)
+		exits(0);
+	if(!getkey(tr->authid, &akey)){
 		/* make one up so caller doesn't know it was wrong */
 		mkkey(&akey);
-		if(debug)
-			syslog(0, AUTHLOG, "tr-fail authid %s", raddr);
+		syslog(0, AUTHLOG, "tr-fail authid %s", tr->authid);
 	}
-	if(!findkey(KEYDB, tr->hostid, &hkey)){
+	if(!getkey(tr->hostid, &hkey)){
 		/* make one up so caller doesn't know it was wrong */
 		mkkey(&hkey);
-		if(debug)
-			syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr);
+		syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr);
 	}
-
 	mkticket(tr, &t);
 	if(!speaksfor(tr->hostid, tr->uid)){
 		mkkey(&akey);
 		mkkey(&hkey);
-		if(debug)
-			syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for",
-				tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
+		syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for",
+			tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
 	}
 	n = 0;
 	tbuf[n++] = AuthOK;
@@ -138,29 +219,21 @@
 	n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &hkey);
 	t.num = AuthTs;
 	n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &akey);
-	if(write(1, tbuf, n) < 0){
-		if(debug)
-			syslog(0, AUTHLOG, "tr-fail %s@%s(%s): hangup",
-				tr->uid, tr->hostid, raddr);
+	if(write(1, tbuf, n) != n)
 		exits(0);
-	}
-	if(debug)
-		syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s",
-			tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
 
-	return 0;
+	syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s", tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
 }
 
 void
 challengebox(Ticketreq *tr)
 {
+	char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], buf[NETCHLEN+1];
+	char *key, *netkey, *err;
 	long chal;
-	char *key, *netkey;
-	Authkey hkey;
-	char kbuf[DESKEYLEN], nkbuf[DESKEYLEN];
-	char buf[NETCHLEN+1];
-	char *err;
 
+	if(tr->uid[0] == 0)
+		exits(0);
 	key = finddeskey(KEYDB, tr->uid, kbuf);
 	netkey = finddeskey(NETKEYDB, tr->uid, nkbuf);
 	if(key == nil && netkey == nil){
@@ -167,15 +240,13 @@
 		/* make one up so caller doesn't know it was wrong */
 		genrandom((uchar*)nkbuf, DESKEYLEN);
 		netkey = nkbuf;
-		if(debug)
-			syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr);
+		syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr);
 	}
-	if(!findkey(KEYDB, tr->hostid, &hkey)){
+
+	if(!getkey(tr->hostid, &hkey)){
 		/* make one up so caller doesn't know it was wrong */
 		mkkey(&hkey);
-		if(debug)
-			syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid,
-				tr->uid, raddr);
+		syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid, tr->uid, raddr);
 	}
 
 	/*
@@ -185,9 +256,9 @@
 	buf[0] = AuthOK;
 	chal = nfastrand(MAXNETCHAL);
 	sprint(buf+1, "%lud", chal);
-	if(write(1, buf, NETCHLEN+1) < 0)
+	if(write(1, buf, NETCHLEN+1) != NETCHLEN+1)
 		exits(0);
-	if(readn(0, buf, NETCHLEN) < 0)
+	if(readn(0, buf, NETCHLEN) != NETCHLEN)
 		exits(0);
 	if(!(key != nil && netcheck(key, chal, buf))
 	&& !(netkey != nil && netcheck(netkey, chal, buf))
@@ -194,9 +265,6 @@
 	&& (err = secureidcheck(tr->uid, buf)) != nil){
 		replyerror("cr-fail %s %s %s", err, tr->uid, raddr);
 		logfail(tr->uid);
-		if(debug)
-			syslog(0, AUTHLOG, "cr-fail %s@%s(%s): bad resp",
-				tr->uid, tr->hostid, raddr);
 		return;
 	}
 	succeed(tr->uid);
@@ -204,33 +272,24 @@
 	/*
 	 *  reply with ticket & authenticator
 	 */
-	if(tickauthreply(tr, &hkey) < 0){
-		if(debug)
-			syslog(0, AUTHLOG, "cr-fail %s@%s(%s): hangup",
-				tr->uid, tr->hostid, raddr);
-		exits(0);
-	}
+	tickauthreply(tr, &hkey);
 
-	if(debug)
-		syslog(0, AUTHLOG, "cr-ok %s@%s(%s)",
-			tr->uid, tr->hostid, raddr);
+	syslog(0, AUTHLOG, "cr-ok %s@%s(%s)", tr->uid, tr->hostid, raddr);
 }
 
 void
 changepasswd(Ticketreq *tr)
 {
-	Ticket t;
-	char tbuf[TICKETLEN+1];
-	char prbuf[PASSREQLEN];
+	char tbuf[MAXTICKETLEN+1], prbuf[MAXPASSREQLEN], *err;
 	Passwordreq pr;
-	Authkey okey, nkey;
-	char *err;
-	int n;
+	Authkey nkey;
+	Ticket t;
+	int n, m;
 
-	if(!findkey(KEYDB, tr->uid, &okey)){
+	if(!getkey(tr->uid, &ukey)){
 		/* make one up so caller doesn't know it was wrong */
-		mkkey(&okey);
-		syslog(0, AUTHLOG, "cp-fail uid %s", raddr);
+		mkkey(&ukey);
+		syslog(0, AUTHLOG, "cp-fail uid %s@%s", tr->uid, raddr);
 	}
 
 	/* send back a ticket with a new key */
@@ -238,25 +297,30 @@
 	t.num = AuthTp;
 	n = 0;
 	tbuf[n++] = AuthOK;
-	n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &okey);
+	n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &ukey);
 	if(write(1, tbuf, n) != n)
 		exits(0);
 
 	/* loop trying passwords out */
 	for(;;){
-		n = readn(0, prbuf, sizeof(prbuf));
-		if(n <= 0 || convM2PR(prbuf, n, &pr, &t) <= 0)
-			exits(0);
+		for(n=0; (m = convM2PR(prbuf, n, &pr, &t)) <= 0; n += m){
+			m = -m;
+			if(m <= n || m > sizeof(prbuf))
+				exits(0);
+			m -= n;
+			if(readn(0, prbuf+n, m) != m)
+				exits(0);
+		}
 		if(pr.num != AuthPass){
 			replyerror("protocol botch1: %s", raddr);
 			exits(0);
 		}
 		passtokey(&nkey, pr.old);
-		if(memcmp(nkey.des, okey.des, DESKEYLEN) != 0){
+		if(tsmemcmp(ukey.des, nkey.des, DESKEYLEN) != 0){
 			replyerror("protocol botch2: %s", raddr);
 			continue;
 		}
-		if(memcmp(okey.aes, zeros, AESKEYLEN) != 0 && memcmp(okey.aes, nkey.aes, AESKEYLEN) != 0){
+		if(tsmemcmp(ukey.aes, zeros, AESKEYLEN) != 0 && tsmemcmp(ukey.aes, nkey.aes, AESKEYLEN) != 0){
 			replyerror("protocol botch3: %s", raddr);
 			continue;
 		}
@@ -276,58 +340,17 @@
 			replyerror("can't write key %s", raddr);
 			continue;
 		}
+		memmove(ukey.des, nkey.des, DESKEYLEN);
+		memmove(ukey.aes, nkey.aes, AESKEYLEN);
 		break;
 	}
+	succeed(tr->uid);
 
 	prbuf[0] = AuthOK;
-	write(1, prbuf, 1);
-	succeed(tr->uid);
-	return;
+	if(write(1, prbuf, 1) != 1)
+		exits(0);
 }
 
-void
-http(Ticketreq *tr)
-{
-	Ticket t;
-	char tbuf[TICKETLEN+1];
-	Authkey key;
-	char *p;
-	Biobuf *b;
-	int n;
-
-	/* use plan9 key when there is any */
-	if(!findkey(KEYDB, tr->uid, &key))
-		mkkey(&key);
-
-	n = strlen(tr->uid);
-	b = Bopen("/sys/lib/httppasswords", OREAD);
-	if(b != nil){
-		for(;;){
-			p = Brdline(b, '\n');
-			if(p == nil)
-				break;
-			p[Blinelen(b)-1] = 0;
-			if(strncmp(p, tr->uid, n) == 0)
-			if(p[n] == ' ' || p[n] == '\t'){
-				p += n;
-				while(*p == ' ' || *p == '\t')
-					p++;
-				passtokey(&key, p);
-			}
-		}
-		Bterm(b);
-	}
-
-	/* send back a ticket encrypted with the key */
-	mkticket(tr, &t);
-	genrandom((uchar*)t.chal, CHALLEN);
-	t.num = AuthHr;
-	n = 0;
-	tbuf[n++] = AuthOK;
-	n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &key);
-	write(1, tbuf, n);
-}
-
 static char*
 domainname(void)
 {
@@ -371,7 +394,6 @@
 {
 	int challen, i, n, tries;
 	char *secret, *p;
-	Authkey hkey;
 	Ticketreq treq;
 	DigestState *s;
 	char sbuf[SECRETLEN];
@@ -402,7 +424,7 @@
 		if(n <= 0 || convM2TR(trbuf, n, &treq) <= 0)
 			exits(0);
 		tr = &treq;
-		if(tr->type != type)
+		if(tr->type != type || tr->uid[0] == 0)
 			exits(0);
 
 		/*
@@ -417,11 +439,11 @@
 		 * lookup
 		 */
 		secret = findsecret(KEYDB, tr->uid, sbuf);
-		if(!findkey(KEYDB, tr->hostid, &hkey) || secret == nil){
+		if(!getkey(tr->hostid, &hkey) || secret == nil){
 			replyerror("apop-fail bad response %s", raddr);
 			logfail(tr->uid);
 			if(tries > 5)
-				return;
+				exits(0);
 			continue;
 		}
 
@@ -436,11 +458,11 @@
 			s = md5((uchar*)chal, challen, 0, 0);
 			md5((uchar*)secret, strlen(secret), digest, s);
 		}
-		if(memcmp(digest, resp, MD5dlen) != 0){
+		if(tsmemcmp(digest, resp, MD5dlen) != 0){
 			replyerror("apop-fail bad response %s", raddr);
 			logfail(tr->uid);
 			if(tries > 5)
-				return;
+				exits(0);
 			continue;
 		}
 		break;
@@ -451,15 +473,12 @@
 	/*
 	 *  reply with ticket & authenticator
 	 */
-	if(tickauthreply(tr, &hkey) < 0)
-		exits(0);
+	tickauthreply(tr, &hkey);
 
-	if(debug){
-		if(type == AuthCram)
-			syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
-		else
-			syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
-	}
+	if(type == AuthCram)
+		syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
+	else
+		syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
 }
 
 enum {
@@ -489,14 +508,16 @@
 void
 vnc(Ticketreq *tr)
 {
-	char *secret;
-	Authkey hkey;
 	uchar chal[VNCchallen+6];
 	uchar reply[VNCchallen];
 	char sbuf[SECRETLEN];
+	char *secret;
 	DESstate s;
 	int i;
 
+	if(tr->uid[0] == 0)
+		exits(0);
+
 	/*
 	 *  Create a challenge and send it.
 	 */
@@ -504,7 +525,7 @@
 	chal[0] = AuthOKvar;
 	sprint((char*)chal+1, "%-5d", VNCchallen);
 	if(write(1, chal, sizeof(chal)) != sizeof(chal))
-		return;
+		exits(0);
 
 	/*
 	 *  lookup keys (and swizzle bits)
@@ -511,7 +532,8 @@
 	 */
 	memset(sbuf, 0, sizeof(sbuf));
 	secret = findsecret(KEYDB, tr->uid, sbuf);
-	if(secret == nil){
+	if(!getkey(tr->hostid, &hkey) || secret == nil){
+		mkkey(&hkey);
 		genrandom((uchar*)sbuf, sizeof(sbuf));
 		secret = sbuf;
 	}
@@ -518,14 +540,11 @@
 	for(i = 0; i < 8; i++)
 		secret[i] = swizzletab[(uchar)secret[i]];
 
-	if(!findkey(KEYDB, tr->hostid, &hkey))
-		mkkey(&hkey);
-
 	/*
 	 *  get response
 	 */
 	if(readn(0, reply, sizeof(reply)) != sizeof(reply))
-		return;
+		exits(0);
 
 	/*
 	 *  decrypt response and compare
@@ -532,7 +551,7 @@
 	 */
 	setupDESstate(&s, (uchar*)secret, nil);
 	desECBdecrypt(reply, sizeof(reply), &s);
-	if(memcmp(reply, chal+6, VNCchallen) != 0){
+	if(tsmemcmp(reply, chal+6, VNCchallen) != 0){
 		replyerror("vnc-fail bad response %s", raddr);
 		logfail(tr->uid);
 		return;
@@ -542,11 +561,9 @@
 	/*
 	 *  reply with ticket & authenticator
 	 */
-	if(tickauthreply(tr, &hkey) < 0)
-		exits(0);
+	tickauthreply(tr, &hkey);
 
-	if(debug)
-		syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
+	syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
 }
 
 void
@@ -553,7 +570,6 @@
 chap(Ticketreq *tr)
 {
 	char *secret;
-	Authkey hkey;
 	DigestState *s;
 	char sbuf[SECRETLEN];
 	uchar digest[MD5dlen];
@@ -564,7 +580,8 @@
 	 *  Create a challenge and send it.
 	 */
 	genrandom((uchar*)chal, sizeof(chal));
-	write(1, chal, sizeof(chal));
+	if(write(1, chal, sizeof(chal)) != sizeof(chal))
+		exits(0);
 
 	/*
 	 *  get chap reply
@@ -572,15 +589,17 @@
 	if(readn(0, &reply, sizeof(reply)) < 0)
 		exits(0);
 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
+	if(tr->uid[0] == 0)
+		exits(0);
 
 	/*
 	 * lookup
 	 */
 	secret = findsecret(KEYDB, tr->uid, sbuf);
-	if(!findkey(KEYDB, tr->hostid, &hkey) || secret == nil){
+	if(!getkey(tr->hostid, &hkey) || secret == nil){
 		replyerror("chap-fail bad response %s", raddr);
 		logfail(tr->uid);
-		exits(0);
+		return;
 	}
 
 	/*
@@ -590,10 +609,10 @@
 	md5((uchar*)secret, strlen(secret), 0, s);
 	md5((uchar*)chal, sizeof(chal), digest, s);
 
-	if(memcmp(digest, reply.resp, MD5dlen) != 0){
+	if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){
 		replyerror("chap-fail bad response %s", raddr);
 		logfail(tr->uid);
-		exits(0);
+		return;
 	}
 
 	succeed(tr->uid);
@@ -601,25 +620,11 @@
 	/*
 	 *  reply with ticket & authenticator
 	 */
-	if(tickauthreply(tr, &hkey) < 0)
-		exits(0);
+	tickauthreply(tr, &hkey);
 
-	if(debug)
-		syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
+	syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
 }
 
-void
-printresp(uchar resp[MSresplen])
-{
-	char buf[200], *p;
-	int i;
-
-	p = buf;
-	for(i=0; i<MSresplen; i++)
-		p += sprint(p, "%.2ux ", resp[i]);
-	syslog(0, AUTHLOG, "resp = %s", buf);
-}
-
 enum {
 	MsvAvEOL = 0,
 	MsvAvNbComputerName,
@@ -666,7 +671,6 @@
 mschap(Ticketreq *tr)
 {
 	char *secret;
-	Authkey hkey;
 	char sbuf[SECRETLEN], windom[128];
 	uchar chal[CHALLEN], ntblob[1024];
 	uchar hash[MShashlen];
@@ -681,7 +685,8 @@
 	 *  Create a challenge and send it.
 	 */
 	genrandom(chal, sizeof(chal));
-	write(1, chal, sizeof(chal));
+	if(write(1, chal, sizeof(chal)) != sizeof(chal))
+		exits(0);
 
 	/*
 	 *  get chap reply
@@ -734,15 +739,17 @@
 	}
 
 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
+	if(tr->uid[0] == 0)
+		exits(0);
+
 	/*
 	 * lookup
 	 */
 	secret = findsecret(KEYDB, tr->uid, sbuf);
-	if(!findkey(KEYDB, tr->hostid, &hkey) || secret == nil){
-		replyerror("mschap-fail bad response %s/%s(%s)",
-			tr->uid, tr->hostid, raddr);
+	if(!getkey(tr->hostid, &hkey) || secret == nil){
+		replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
 		logfail(tr->uid);
-		exits(0);
+		return;
 	}
 
 	if(ntbloblen > 0){
@@ -756,7 +763,7 @@
 			 */
 			s = hmac_md5(chal, 8, hash, MShashlen, nil, nil);
 			hmac_md5((uchar*)reply.LMresp+16, 8, hash, MShashlen, resp, s);
-			lmok = memcmp(resp, reply.LMresp, 16) == 0;
+			lmok = tsmemcmp(resp, reply.LMresp, 16) == 0;
 
 			/*
 			 * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob)
@@ -763,7 +770,7 @@
 			 */
 			s = hmac_md5(chal, 8, hash, MShashlen, nil, nil);
 			hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s);
-			ntok = memcmp(resp, reply.NTresp, 16) == 0;
+			ntok = tsmemcmp(resp, reply.NTresp, 16) == 0;
 
 			if(lmok || ntok || windom[0] == '\0')
 				break;
@@ -774,11 +781,11 @@
 	} else {
 		lmhash(hash, secret);
 		mschalresp(resp, hash, chal);
-		lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
+		lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0;
 		nthash(hash, secret);
 		mschalresp(resp, hash, chal);
-		ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
-		dupe = memcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
+		ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0;
+		dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
 	}
 
 	/*
@@ -795,10 +802,9 @@
 	 * windows clients don't seem to use the feature.
 	 */
 	if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
-		replyerror("mschap-fail bad response %s/%s(%s) %d,%d,%d",
-			tr->uid, tr->hostid, raddr, dupe, lmok, ntok);
+		replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr);
 		logfail(tr->uid);
-		exits(0);
+		return;
 	}
 
 	succeed(tr->uid);
@@ -806,11 +812,9 @@
 	/*
 	 *  reply with ticket & authenticator
 	 */
-	if(tickauthreply(tr, &hkey) < 0)
-		exits(0);
+	tickauthreply(tr, &hkey);
 
-	if(debug)
-		replyerror("mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr);
+	syslog(0, AUTHLOG, "mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr);
 
 	nthash(hash, secret);
 	md4(hash, 16, hash2, 0);
@@ -818,7 +822,7 @@
 	sha1(hash2, 16, 0, s);
 	sha1(chal, 8, digest, s);
 
-	if(write(1, digest, 16) < 0)
+	if(write(1, digest, 16) != 16)
 		exits(0);
 }
 
@@ -997,31 +1001,57 @@
 }
 
 void
-mkkey(Authkey *k)
+initkeyseed(void)
 {
-	genrandom((uchar*)k->des, DESKEYLEN);
-	genrandom((uchar*)k->aes, AESKEYLEN);
+	static char info[] = "PRF key for generation of dummy user keys";
+	char k[DESKEYLEN], *u;
+
+	u = getuser();
+	if(!finddeskey(KEYDB, u, k)){
+		syslog(0, AUTHLOG, "user %s not in keydb", u);
+		exits(0);
+	}
+	hmac_sha2_256((uchar*)info, sizeof(info)-1, (uchar*)k, sizeof(k), keyseed, nil);
+	memset(k, 0, sizeof(k));
 }
 
 void
+mkkey(Keyslot *k)
+{
+	uchar h[SHA2_256dlen];
+	Authkey *a = k;
+
+	genrandom((uchar*)a, sizeof(Authkey));
+
+	/*
+	 * the DES key has to be constant for a user in each response,
+	 * so we make one up pseudo randomly from a keyseed and user name.
+	 */
+	hmac_sha2_256((uchar*)k->id, strlen(k->id), keyseed, sizeof(keyseed), h, nil);
+	memmove(a->des, h, DESKEYLEN);
+	memset(h, 0, sizeof(h));
+}
+
+void
 mkticket(Ticketreq *tr, Ticket *t)
 {
 	memset(t, 0, sizeof(Ticket));
 	memmove(t->chal, tr->chal, CHALLEN);
-	safecpy(t->cuid, tr->uid, sizeof(t->cuid));
-	safecpy(t->suid, tr->uid, sizeof(t->suid));
-	genrandom((uchar*)t->key, DESKEYLEN);
+	safecpy(t->cuid, tr->uid, ANAMELEN);
+	safecpy(t->suid, tr->uid, ANAMELEN);
+	genrandom(t->key, NONCELEN);
+	t->form = ticketform;
 }
 
 /*
  *  reply with ticket and authenticator
  */
-int
-tickauthreply(Ticketreq *tr, Authkey *hkey)
+void
+tickauthreply(Ticketreq *tr, Authkey *key)
 {
 	Ticket t;
 	Authenticator a;
-	char buf[TICKETLEN+AUTHENTLEN+1];
+	char buf[MAXTICKETLEN+MAXAUTHENTLEN+1];
 	int n;
 
 	mkticket(tr, &t);
@@ -1028,15 +1058,14 @@
 	t.num = AuthTs;
 	n = 0;
 	buf[n++] = AuthOK;
-	n += convT2M(&t, buf+n, sizeof(buf)-n, hkey);
+	n += convT2M(&t, buf+n, sizeof(buf)-n, key);
 	memset(&a, 0, sizeof(a));
 	memmove(a.chal, t.chal, CHALLEN);
+	genrandom(a.rand, NONCELEN);
 	a.num = AuthAc;
-	a.id = 0;
 	n += convA2M(&a, buf+n, sizeof(buf)-n, &t);
 	if(write(1, buf, n) != n)
-		return -1;
-	return 0;
+		exits(0);
 }
 
 void
--- a/sys/src/cmd/auth/convkeys.c
+++ b/sys/src/cmd/auth/convkeys.c
@@ -43,6 +43,8 @@
 		usage();
 	file = argv[0];
 
+	private();
+
 	/* get original key */
 	if(usepass){
 		print("enter password file is encoded with\n");
--- a/sys/src/cmd/auth/debug.c
+++ b/sys/src/cmd/auth/debug.c
@@ -116,8 +116,8 @@
 	}
 }
 
-void authdialfutz(char*, char*);
-void authfutz(char*, char*);
+void authdialfutz(char*, char*, char*);
+void authfutz(char*, char*, char*);
 
 /* scan factotum for p9sk1 keys; check them */
 void
@@ -143,7 +143,7 @@
 		a = _parseattr(s+4);
 		free(s);
 		proto = _strfindattr(a, "proto");
-		if(proto==nil || strcmp(proto, "p9sk1")!=0)
+		if(proto==nil || (strcmp(proto, "p9sk1")!=0 && strcmp(proto, "dp9ik")!=0))
 			continue;
 		dom = _strfindattr(a, "dom");
 		if(dom == nil){
@@ -157,17 +157,17 @@
 			_freeattr(a);
 			continue;
 		}
-		print("p9sk1 key: %A\n", a);
+		print("key: %A\n", a);
 		found = 1;
-		authdialfutz(dom, user);
+		authdialfutz(dom, user, proto);
 		_freeattr(a);
 	}
 	if(!found)
-		print("no p9sk1 keys found in factotum\n");
+		print("no p9sk1/dp9ik keys found in factotum\n");
 }
 
 void
-authdialfutz(char *dom, char *user)
+authdialfutz(char *dom, char *user, char *proto)
 {
 	int fd;
 	char *server;
@@ -177,7 +177,7 @@
 	if(fd >= 0){
 		print("\tsuccessfully dialed auth server\n");
 		close(fd);
-		authfutz(dom, user);
+		authfutz(dom, user, proto);
 		return;
 	}
 	print("\tcannot dial auth server: %r\n");
@@ -205,11 +205,44 @@
 	print("\tdial %s failed: %r\n", addr);
 }
 
+int
+getpakkeys(int fd, Ticketreq *tr, Authkey *akey, Authkey *hkey)
+{
+	uchar y[PAKYLEN];
+	PAKpriv p;
+	int ret, type;
+
+	ret = -1;
+	type = tr->type;
+	tr->type = AuthPAK;
+	if(_asrequest(fd, tr) < 0 || _asrdresp(fd, (char*)y, 0) < 0)
+		goto out;
+
+	authpak_hash(akey, tr->authid);
+	authpak_new(&p, akey, y, 1);
+	if(write(fd, y, PAKYLEN) != PAKYLEN
+	|| readn(fd, y, PAKYLEN) != PAKYLEN
+	|| authpak_finish(&p, akey, y))
+		goto out;
+
+	authpak_hash(hkey, tr->hostid);
+	authpak_new(&p, hkey, y, 1);
+	if(write(fd, y, PAKYLEN) != PAKYLEN
+	|| readn(fd, y, PAKYLEN) != PAKYLEN
+	|| authpak_finish(&p, hkey, y))
+		goto out;
+
+	ret = 0;
+out:
+	tr->type = type;
+	return ret;
+}
+
 void
-authfutz(char *dom, char *user)
+authfutz(char *dom, char *user, char *proto)
 {
 	int fd, nobootes, n, m;
-	char pw[128], prompt[128], tbuf[2*TICKETLEN];
+	char pw[128], prompt[128], tbuf[2*MAXTICKETLEN];
 	Authkey key, booteskey;
 	Ticket t;
 	Ticketreq tr;
@@ -219,6 +252,7 @@
 	if(pw[0] == '\0')
 		return;
 	passtokey(&key, pw);
+	booteskey = key;
 
 	fd = authdial(nil, dom);
 	if(fd < 0){
@@ -234,6 +268,13 @@
 	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, user);
 	strecpy(tr.uid, tr.uid+sizeof tr.uid, user);
 	memset(tr.chal, 0xAA, sizeof tr.chal);
+
+	if(strcmp(proto, "dp9ik") == 0 && getpakkeys(fd, &tr, &booteskey, &key) < 0){
+		print("\tgetpakkeys failed: %r\n");
+		close(fd);
+		return;
+	}
+
 	if((n = _asgetticket(fd, &tr, tbuf, sizeof(tbuf))) < 0){
 		print("\t_asgetticket failed: %r\n");
 		close(fd);
@@ -252,7 +293,7 @@
 		return;
 	}
 
-	convM2T(tbuf+m, n-m, &t, &key);
+	convM2T(tbuf+m, n-m, &t, &booteskey);
 	if(t.num != AuthTs){
 		print("\tcannot decrypt ticket2 from auth server (bad t.num=0x%.2ux)\n", t.num);
 		print("\tauth server and you do not agree on key for %s@%s\n", user, dom);
@@ -269,9 +310,24 @@
 	/* try ticket request using bootes key */
 	snprint(prompt, sizeof prompt, "\tcpu server owner for domain %s ", dom);
 	readcons(prompt, "glenda", 0, tr.authid, sizeof tr.authid);
-	if((n = _asgetticket(fd, &tr, tbuf, sizeof(tbuf))) < 0){
+	snprint(prompt, sizeof prompt, "\tpassword for %s@%s [hit enter to skip test]", tr.authid, dom);
+	readcons(prompt, nil, 1, pw, sizeof pw);
+	if(pw[0] == '\0'){
+		nobootes=1;
+		goto Nobootes;
+	}
+	nobootes = 0;
+	passtokey(&booteskey, pw);
+
+	if(strcmp(proto, "dp9ik") == 0 && getpakkeys(fd, &tr, &booteskey, &key) < 0){
+		print("\tgetpakkeys failed: %r\n");
 		close(fd);
+		return;
+	}
+
+	if((n = _asgetticket(fd, &tr, tbuf, sizeof(tbuf))) < 0){
 		print("\t_asgetticket failed: %r\n");
+		close(fd);
 		return;
 	}
 	m = convM2T(tbuf, n, &t, &key);
@@ -286,16 +342,7 @@
 		print("\tauth server is rogue\n");
 		return;
 	}
-
-	snprint(prompt, sizeof prompt, "\tpassword for %s@%s [hit enter to skip test]", tr.authid, dom);
-	readcons(prompt, nil, 1, pw, sizeof pw);
-	if(pw[0] == '\0'){
-		nobootes=1;
-		goto Nobootes;
-	}
-	nobootes = 0;
-	passtokey(&booteskey, pw);
-
+	
 	convM2T(tbuf+m, n-m, &t, &booteskey);
 	if(t.num != AuthTs){
 		print("\tcannot decrypt ticket2 from auth server (bad t.num=0x%.2ux)\n", t.num);
@@ -321,7 +368,6 @@
 	 * auth server (assumes running cpu service)
 	 * to test that bootes key is right over there
 	 */
-
 }
 
 void
--- a/sys/src/cmd/auth/factotum/apop.c
+++ b/sys/src/cmd/auth/factotum/apop.c
@@ -20,6 +20,7 @@
 	int asfd;
 	int astype;
 	Key *key;
+	Authkey k;
 	Ticket	t;
 	Ticketreq	tr;
 	char chal[128];
@@ -214,28 +215,22 @@
 	s->asfd = -1;
 
 	/* send request to authentication server and get challenge */
-	/* send request to authentication server and get challenge */
 	if((dom = _strfindattr(s->key->attr, "dom")) == nil
 	|| (user = _strfindattr(s->key->attr, "user")) == nil){
 		werrstr("apop/dochal cannot happen");
 		goto err;
 	}
+	memmove(&s->k, s->key->priv, sizeof(Authkey));
 
-	s->asfd = _authdial(nil, dom);
-
-	/* could generate our own challenge on error here */
-	if(s->asfd < 0)
-		goto err;
-
 	memset(&s->tr, 0, sizeof(s->tr));
 	safecpy(s->tr.authdom, dom, sizeof s->tr.authdom);
 	safecpy(s->tr.hostid, user, sizeof(s->tr.hostid));
 	s->tr.type = s->astype;
-	alarm(30*1000);
-	if(_asrequest(s->asfd, &s->tr) < 0){
-		alarm(0);
+
+	s->asfd = _authreq(&s->tr, &s->k);
+	if(s->asfd < 0)
 		goto err;
-	}
+	alarm(30*1000);
 	n = _asrdresp(s->asfd, s->chal, sizeof s->chal);
 	alarm(0);
 	if(n <= 5)
@@ -272,7 +267,7 @@
 		alarm(0);
 		goto err;
 	}
-	n = _asgetresp(s->asfd, &s->t, &a, (Authkey*)s->key->priv);
+	n = _asgetresp(s->asfd, &s->t, &a, &s->k);
 	alarm(0);
 	if(n < 0){
 		/* leave connection open so we can try again */
@@ -282,7 +277,7 @@
 	s->asfd = -1;
 
 	if(s->t.num != AuthTs
-	|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
+	|| tsmemcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
 		if(s->key->successes == 0)
 			disablekey(s->key);
 		werrstr(Easproto);
@@ -290,8 +285,7 @@
 	}
 	s->key->successes++;
 	if(a.num != AuthAc
-	|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
-	|| a.id != 0){
+	|| tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){
 		werrstr(Easproto);
 		goto err;
 	}
--- a/sys/src/cmd/auth/factotum/chap.c
+++ b/sys/src/cmd/auth/factotum/chap.c
@@ -41,6 +41,7 @@
 	int astype;
 	int asfd;
 	Key *key;
+	Authkey k;
 	Ticket t;
 	Ticketreq tr;
 	char chal[ChapChallen];
@@ -309,19 +310,18 @@
 		werrstr("chap/dochal cannot happen");
 		goto err;
 	}
-	s->asfd = _authdial(nil, dom);
-	if(s->asfd < 0)
-		goto err;
-	
+	memmove(&s->k, s->key->priv, sizeof(Authkey));
+
 	memset(&s->tr, 0, sizeof(s->tr));
 	safecpy(s->tr.authdom, dom, sizeof(s->tr.authdom));
 	safecpy(s->tr.hostid, user, sizeof(s->tr.hostid));
 	s->tr.type = s->astype;
-	alarm(30*1000);
-	if(_asrequest(s->asfd, &s->tr) < 0){
-		alarm(0);
+
+	s->asfd = _authreq(&s->tr, &s->k);
+	if(s->asfd < 0)
 		goto err;
-	}
+	
+	alarm(30*1000);
 	n = readn(s->asfd, s->chal, sizeof s->chal);
 	alarm(0);
 	if(n != sizeof s->chal)
@@ -347,7 +347,7 @@
 		alarm(0);
 		goto err;
 	}
-	n = _asgetresp(s->asfd, &s->t, &a, (Authkey*)s->key->priv);
+	n = _asgetresp(s->asfd, &s->t, &a, &s->k);
 	if(n < 0){
 		alarm(0);
 		/* leave connection open so we can try again */
@@ -361,7 +361,7 @@
 	s->asfd = -1;
 
 	if(s->t.num != AuthTs
-	|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
+	|| tsmemcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
 		if(s->key->successes == 0)
 			disablekey(s->key);
 		werrstr(Easproto);
@@ -369,8 +369,7 @@
 	}
 	s->key->successes++;
 	if(a.num != AuthAc
-	|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
-	|| a.id != 0){
+	|| tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){
 		werrstr(Easproto);
 		return -1;
 	}
--- a/sys/src/cmd/auth/factotum/confirm.c
+++ b/sys/src/cmd/auth/factotum/confirm.c
@@ -16,6 +16,7 @@
 {
 	Req **l;
 
+	qlock(&confbuf);
 	for(l=&cusewait; *l; l=&(*l)->aux){
 		if(*l == r){
 			*l = r->aux;
@@ -22,10 +23,12 @@
 			if(r->aux == nil)
 				cuselast = l;
 			r->aux = nil;
+			qunlock(&confbuf);
 			respond(r, "interrupted");
 			return;
 		}
 	}
+	qunlock(&confbuf);
 	logbufflush(&confbuf, r);
 }
 
@@ -43,7 +46,7 @@
 }
 
 int
-confirmwrite(char *s)
+confirmwrite(Srv *srv, char *s)
 {
 	char *t, *ans;
 	int allow, tagoff;
@@ -54,15 +57,18 @@
 
 	a = _parseattr(s);
 	if(a == nil){
+		_freeattr(a);
 		werrstr("empty write");
 		return -1;
 	}
 	if((t = _strfindattr(a, "tag")) == nil){
+		_freeattr(a);
 		werrstr("no tag");
 		return -1;
 	}
 	tag = strtoul(t, 0, 0);
 	if((ans = _strfindattr(a, "answer")) == nil){
+		_freeattr(a);
 		werrstr("no answer");
 		return -1;
 	}
@@ -71,14 +77,18 @@
 	else if(strcmp(ans, "no") == 0)
 		allow = 0;
 	else{
+		_freeattr(a);
 		werrstr("bad answer");
 		return -1;
 	}
-	r = nil;
+	_freeattr(a);
+	srvrelease(srv);
+	qlock(&confbuf);
+	fss = nil;
 	tagoff = -1;
-	for(l=&cusewait; *l; l=&(*l)->aux){
-		r = *l;
-		if(hastag(r->fid->aux, tag, &tagoff)){
+	for(l=&cusewait; (r = *l) != nil; l=&(*l)->aux){
+		fss = r->fid->aux;
+		if(hastag(fss, tag, &tagoff)){
 			*l = r->aux;
 			if(r->aux == nil)
 				cuselast = l;
@@ -86,13 +96,17 @@
 			break;
 		}
 	}
+	qunlock(&confbuf);
 	if(r == nil || tagoff == -1){
+		srvacquire(srv);
 		werrstr("tag not found");
 		return -1;
 	}
-	fss = r->fid->aux;
+	qlock(fss);
 	fss->conf[tagoff].canuse = allow;
 	rpcread(r);
+	qunlock(fss);
+	srvacquire(srv);
 	return 0;
 }
 
@@ -118,9 +132,11 @@
 		respond(r, "no confirmations to wait for (bug)");
 		return;
 	}
-	*cuselast = r;
+	qlock(&confbuf);
 	r->aux = nil;
+	*cuselast = r;
 	cuselast = &r->aux;
+	qunlock(&confbuf);
 }
 
 /* Yes, I am unhappy that the code below is a copy of the code above. */
@@ -140,6 +156,7 @@
 {
 	Req **l;
 
+	qlock(&needkeybuf);
 	for(l=&needwait; *l; l=&(*l)->aux){
 		if(*l == r){
 			*l = r->aux;
@@ -146,32 +163,38 @@
 			if(r->aux == nil)
 				needlast = l;
 			r->aux = nil;
+			qunlock(&needkeybuf);
 			respond(r, "interrupted");
 			return;
 		}
 	}
+	qunlock(&needkeybuf);
 	logbufflush(&needkeybuf, r);
 }
 
 int
-needkeywrite(char *s)
+needkeywrite(Srv *srv, char *s)
 {
 	char *t;
 	ulong tag;
 	Attr *a;
 	Req *r, **l;
+	Fsstate *fss;
 
 	a = _parseattr(s);
 	if(a == nil){
+		_freeattr(a);
 		werrstr("empty write");
 		return -1;
 	}
 	if((t = _strfindattr(a, "tag")) == nil){
+		_freeattr(a);
 		werrstr("no tag");
 		return -1;
 	}
 	tag = strtoul(t, 0, 0);
 	r = nil;
+	qlock(&needkeybuf);
 	for(l=&needwait; *l; l=&(*l)->aux){
 		r = *l;
 		if(r->tag == tag){
@@ -182,15 +205,23 @@
 			break;
 		}
 	}
+	qunlock(&needkeybuf);
 	if(r == nil){
+		_freeattr(a);
 		werrstr("tag not found");
 		return -1;
 	}
+	fss = r->fid->aux;
+	srvrelease(srv);
+	qlock(fss);
 	if(s = _strfindattr(a, "error")){
 		werrstr("%s", s);
-		retrpc(r, RpcErrstr, (Fsstate*)r->fid->aux);
+		retrpc(r, RpcErrstr, fss);
 	}else
 		rpcread(r);
+	_freeattr(a);
+	qunlock(fss);
+	srvacquire(srv);
 	return 0;
 }
 
@@ -204,9 +235,13 @@
 
 	snprint(msg, sizeof msg, "needkey tag=%lud %s", r->tag, fss->keyinfo);
 	logbufappend(&needkeybuf, msg);
-	*needlast = r;
+
+	qlock(&needkeybuf);
 	r->aux = nil;
+	*needlast = r;
 	needlast = &r->aux;
+	qunlock(&needkeybuf);
+
 	return 0;
 }
 
--- a/sys/src/cmd/auth/factotum/dat.h
+++ b/sys/src/cmd/auth/factotum/dat.h
@@ -45,6 +45,7 @@
 
 struct Fsstate
 {
+	QLock;
 	char *sysuser;	/* user according to system */
 
 	/* keylist, protolist */
@@ -76,7 +77,8 @@
 
 struct Key
 {
-	int ref;
+	Ref;
+
 	Attr *attr;
 	Attr *privattr;	/* private attributes, like *data */
 	Proto *proto;
@@ -97,6 +99,8 @@
 
 struct Keyring
 {
+	QLock;
+
 	Key **key;
 	int nkey;
 };
@@ -103,6 +107,8 @@
 
 struct Logbuf
 {
+	QLock;
+
 	Req *wait;
 	Req **waitlast;
 	int rp;
@@ -133,11 +139,11 @@
 /* confirm.c */
 void confirmread(Req*);
 void confirmflush(Req*);
-int confirmwrite(char*);
+int confirmwrite(Srv*, char*);
 void confirmqueue(Req*, Fsstate*);
 void needkeyread(Req*);
 void needkeyflush(Req*);
-int needkeywrite(char*);
+int needkeywrite(Srv*, char*);
 int needkeyqueue(Req*, Fsstate*);
 
 /* fs.c */
@@ -162,12 +168,7 @@
 void logflush(Req*);
 void logbufflush(Logbuf*, Req*);
 void logbufread(Logbuf*, Req*);
-void logbufproc(Logbuf*);
 void logbufappend(Logbuf*, char*);
-void needkeyread(Req*);
-void needkeyflush(Req*);
-int needkeywrite(char*);
-int needkeyqueue(Req*, Fsstate*);
 
 /* rpc.c */
 int ctlwrite(char*, int);
@@ -185,7 +186,8 @@
 #pragma varargck argpos findkey 3
 #pragma varargck argpos setattr 2
 
-int		_authdial(char*, char*);
+int		_authdial(char*);
+int		_authreq(Ticketreq *, Authkey *);
 void		askuser(char*);
 int		attrnamefmt(Fmt *fmt);
 int		canusekey(Fsstate*, Key*);
@@ -199,7 +201,7 @@
 int		findkey(Key**, Keyinfo*, char*, ...);
 int		findp9authkey(Key**, Fsstate*);
 Proto	*findproto(char*);
-char		*getnvramkey(int);
+int		getnvramkey(int);
 void		initcap(void);
 int		isclient(char*);
 int		matchattr(Attr*, Attr*, Attr*);
@@ -221,7 +223,7 @@
 
 /* protocols */
 extern Proto apop, cram;		/* apop.c */
-extern Proto p9any, p9sk1, p9sk2;	/* p9sk.c */
+extern Proto p9any, p9sk1, dp9ik;	/* p9sk.c */
 extern Proto chap, mschap, mschapv2, mschap2;	/* chap.c */
 extern Proto p9cr, vnc;			/* p9cr.c */
 extern Proto pass;			/* pass.c */
@@ -231,4 +233,3 @@
 extern Proto httpdigest;		/* httpdigest.c */
 extern Proto ecdsa;			/* ecdsa.c */
 extern Proto wpapsk;			/* wpapsk.c */
-
--- a/sys/src/cmd/auth/factotum/fs.c
+++ b/sys/src/cmd/auth/factotum/fs.c
@@ -36,7 +36,7 @@
 	&p9any,
 	&p9cr,
 	&p9sk1,
-	&p9sk2,
+	&dp9ik,
 	&pass,
 /*	&srs, */
 	&rsa,
@@ -148,15 +148,8 @@
 	}
 
 	if(sflag){
-		s = getnvramkey(kflag ? NVwrite : NVwriteonerr);
-		if(s == nil)
+		if(getnvramkey(kflag ? NVwrite : NVwriteonerr) < 0)
 			fprint(2, "factotum warning: cannot read nvram: %r\n");
-		else if(ctlwrite(s, 0) < 0)
-			fprint(2, "factotum warning: cannot add nvram key: %r\n");
-		if (s != nil) {
-			memset(s, 0, strlen(s));
-			free(s);
-		}
 	} else if(uflag)
 		promptforhostowner();
 	owner = getuser();
@@ -418,9 +411,11 @@
 	fss = fid->aux;
 	if(fss == nil)
 		return;
+	qlock(fss);
 	if(fss->ps)
 		(*fss->proto->close)(fss);
 	_freeattr(fss->attr);
+	qunlock(fss);
 	free(fss);
 }
 
@@ -452,14 +447,14 @@
 	int wb;
 	Keyinfo ki;
 	Key *k;
-	static char zero[Nearend];
 
 	k = nil;
 	mkkeyinfo(&ki, fss, nil);
 	ki.attr = nil;
+	ki.noconf = 1;
 	ki.skip = i;
 	ki.usedisabled = 1;
-	if(findkey(&k, &ki, "") != RpcOk)
+	if(findkey(&k, &ki, nil) != RpcOk)
 		return 0;
 
 	memset(a + n - Nearend, 0, Nearend);
@@ -489,6 +484,24 @@
 }
 
 static void
+fsrpcio(Req *r)
+{
+	Fsstate *fss;
+	Srv *srv;
+
+	fss = r->fid->aux;
+	srv = r->srv;
+	srvrelease(srv);
+	qlock(fss);
+	if(r->ifcall.type == Tread)
+		rpcread(r);
+	else
+		rpcwrite(r);
+	qunlock(fss);
+	srvacquire(srv);
+}
+
+static void
 fsread(Req *r)
 {
 	Fsstate *s;
@@ -507,7 +520,7 @@
 		respond(r, nil);
 		break;
 	case Qrpc:
-		rpcread(r);
+		fsrpcio(r);
 		break;
 	case Qneedkey:
 		needkeyread(r);
@@ -540,7 +553,7 @@
 		respond(r, "bug in fswrite");
 		break;
 	case Qrpc:
-		rpcwrite(r);
+		fsrpcio(r);
 		break;
 	case Qneedkey:
 	case Qconfirm:
@@ -552,10 +565,10 @@
 		default:
 			abort();
 		case Qneedkey:
-			ret = needkeywrite(s);
+			ret = needkeywrite(r->srv, s);
 			break;
 		case Qconfirm:
-			ret = confirmwrite(s);
+			ret = confirmwrite(r->srv, s);
 			break;
 		case Qctl:
 			ret = ctlwrite(s, 0);
--- a/sys/src/cmd/auth/factotum/log.c
+++ b/sys/src/cmd/auth/factotum/log.c
@@ -1,6 +1,6 @@
 #include "dat.h"
 
-void
+static void
 logbufproc(Logbuf *lb)
 {
 	char *s;
@@ -43,6 +43,7 @@
 void
 logbufread(Logbuf *lb, Req *r)
 {
+	qlock(lb);
 	if(lb->waitlast == nil)
 		lb->waitlast = &lb->wait;
 	*(lb->waitlast) = r;
@@ -49,6 +50,7 @@
 	lb->waitlast = &r->aux;
 	r->aux = nil;
 	logbufproc(lb);
+	qunlock(lb);
 }
 
 void
@@ -56,6 +58,7 @@
 {
 	Req **l;
 
+	qlock(lb);
 	for(l=&lb->wait; *l; l=&(*l)->aux){
 		if(*l == r){
 			*l = r->aux;
@@ -66,6 +69,7 @@
 			break;
 		}
 	}
+	qunlock(lb);
 }
 
 void
@@ -74,6 +78,7 @@
 	if(debug)
 		fprint(2, "%s\n", buf);
 
+	qlock(lb);
 	if(lb->msg[lb->wp])
 		free(lb->msg[lb->wp]);
 	lb->msg[lb->wp] = estrdup9p(buf);
@@ -80,6 +85,7 @@
 	if(++lb->wp == nelem(lb->msg))
 		lb->wp = 0;
 	logbufproc(lb);
+	qunlock(lb);
 }
 
 Logbuf logbuf;
--- a/sys/src/cmd/auth/factotum/p9any.c
+++ b/sys/src/cmd/auth/factotum/p9any.c
@@ -13,7 +13,8 @@
 #include "dat.h"
 
 static Proto *negotiable[] = {
-	&p9sk1,
+	&p9sk1,			/* has to be first because of drawterm bug */
+	&dp9ik,
 };
 
 struct State
@@ -98,6 +99,7 @@
 	fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name);
 	fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom"));
 	s->subfss.attr = fss->attr;
+	s->subfss.proto = s->subproto;
 	s->subfss.phase = Notstarted;
 	s->subfss.sysuser = fss->sysuser;
 	s->subfss.seqnum = fss->seqnum;
@@ -229,8 +231,8 @@
 	return p+1;
 }
 
-static Proto*
-findneg(char *name)
+static int
+findprotox(char *name)
 {
 	int i, len;
 	char *p;
@@ -239,14 +241,28 @@
 		len = p-name;
 	else
 		len = strlen(name);
-
 	for(i=0; i<nelem(negotiable); i++)
 		if(strncmp(negotiable[i]->name, name, len) == 0 && negotiable[i]->name[len] == 0)
-			return negotiable[i];
+			return i;
+	return -1;
+}
+
+static Proto*
+findneg(char *name)
+{
+	int x = findprotox(name);
+	if(x >= 0)
+		return negotiable[x];
 	return nil;
 }
 
 static int
+protopref(void *a, void *b)
+{
+	return findprotox(*(char**)b) - findprotox(*(char**)a);
+}
+
+static int
 p9anywrite(Fsstate *fss, void *va, uint n)
 {
 	char *a, *dom, *user, *token[20];
@@ -274,7 +290,11 @@
 				free(a);
 				return failure(fss, "unknown version of p9any");
 			}
+			if(--m > 0)
+				memmove(token, token+1, m*sizeof(token[0]));
 		}
+		/* put prefered protocols first */
+		qsort(token, m, sizeof(token[0]), protopref);
 	
 		/*
 		 * look for a key
@@ -285,7 +305,7 @@
 		k = nil;
 		p = nil;
 		dom = nil;
-		for(i=(s->version==1?0:1); i<m; i++){
+		for(i=0; i<m; i++){
 			p = findneg(token[i]);
 			if(p == nil)
 				continue;
--- a/sys/src/cmd/auth/factotum/p9cr.c
+++ b/sys/src/cmd/auth/factotum/p9cr.c
@@ -24,6 +24,7 @@
 	Key	*key;
 	int	astype;
 	int	asfd;
+	Authkey	k;
 	Ticket	t;
 	Ticketreq tr;
 	char	chal[Maxchal];
@@ -165,18 +166,14 @@
 static int
 p9response(Fsstate *fss, State *s)
 {
-	Authkey key;
+	Authkey *akey;
 	uchar buf[8];
 	ulong chal;
-	char *pw;
 
-	pw = _strfindattr(s->key->privattr, "!password");
-	if(pw == nil)
-		return failure(fss, "vncresponse cannot happen");
-	passtokey(&key, pw);
 	memset(buf, 0, 8);
 	sprint((char*)buf, "%d", atoi(s->chal));
-	if(encrypt(key.des, buf, 8) < 0)
+	akey = (Authkey*)s->key->priv;
+	if(encrypt(akey->des, buf, 8) < 0)
 		return failure(fss, "can't encrypt response");
 	chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
 	s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
@@ -287,7 +284,7 @@
 			return failure(fss, Easproto);
 		}
 		/* get ticket plus authenticator from auth server */
-		ret = _asgetresp(s->asfd, &s->t, &a, (Authkey*)s->key->priv);
+		ret = _asgetresp(s->asfd, &s->t, &a, &s->k);
 		alarm(0);
 
 		if(ret < 0)
@@ -295,7 +292,7 @@
 
 		/* check ticket */
 		if(s->t.num != AuthTs
-		|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
+		|| tsmemcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
 			if (s->key->successes == 0)
 				disablekey(s->key);
 			return failure(fss, Easproto);
@@ -302,8 +299,7 @@
 		}
 		s->key->successes++;
 		if(a.num != AuthAc
-		|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
-		|| a.id != 0)
+		|| tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0)
 			return failure(fss, Easproto);
 
 		fss->haveai = 1;
@@ -321,19 +317,16 @@
 {
 	int n;
 
+	memmove(&s->k, s->key->priv, sizeof(Authkey));
+
 	safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof(s->tr.hostid));
 	safecpy(s->tr.authdom, _strfindattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
 	s->tr.type = s->astype;
 
-	/* get challenge from auth server */
-	s->asfd = _authdial(nil, _strfindattr(s->key->attr, "dom"));
+	s->asfd = _authreq(&s->tr, &s->k);
 	if(s->asfd < 0)
 		return failure(fss, Easproto);
 	alarm(30*1000);
-	if(_asrequest(s->asfd, &s->tr) < 0){
-		alarm(0);
-		return failure(fss, Easproto);
-	}
 	n = _asrdresp(s->asfd, s->chal, s->challen);
 	alarm(0);
 	if(n <= 0){
--- a/sys/src/cmd/auth/factotum/p9sk1.c
+++ b/sys/src/cmd/auth/factotum/p9sk1.c
@@ -1,18 +1,24 @@
 /*
- * p9sk1, p9sk2 - Plan 9 secret (private) key authentication.
- * p9sk2 is an incomplete flawed variant of p9sk1.
+ * p9sk1, dp9ik - Plan 9 secret (private) key authentication.
+ * dp9ik uses AuthPAK diffie hellman key exchange with the
+ * auth server to protect the password derived key from offline
+ * dictionary attacks.
  *
  * Client protocol:
- *	write challenge[challen]	(p9sk1 only)
- *	read tickreq[tickreqlen]
- *	write ticket[ticketlen]
- *	read authenticator[authentlen]
+ *	write challenge[challen]
+ *	 read pakreq[ticketreqlen + pakylen]	(dp9ik only)
+ *	  write paky[pakylen]
+ *	 read tickreq[tickreqlen]		(p9sk1 only)
+ *	write ticket + authenticator
+ *	read authenticator
  *
  * Server protocol:
- * 	read challenge[challen]	(p9sk1 only)
- *	write tickreq[tickreqlen]
- *	read ticket[ticketlen]
- *	write authenticator[authentlen]
+ * 	read challenge[challen]
+ *	 write pakreq[ticketreqlen + pakylen]	(dp9ik only)
+ *	  read paky[pakylen]
+ *	 write tickreq[tickreqlen]		(p9sk1 only)
+ *	read ticket + authenticator
+ *	write authenticator
  */
 
 #include "dat.h"
@@ -19,12 +25,14 @@
 
 struct State
 {
-	int vers;
 	Key *key;
 	Ticket t;
 	Ticketreq tr;
+	Authkey k;
+	PAKpriv p;
 	char cchal[CHALLEN];
-	char tbuf[TICKETLEN+AUTHENTLEN];
+	uchar y[PAKYLEN], rand[2*NONCELEN];
+	char tbuf[MAXTICKETLEN + MAXAUTHENTLEN];
 	int tbuflen;
 	uchar *secret;
 	int speakfor;
@@ -34,16 +42,20 @@
 {
 	/* client phases */
 	CHaveChal,
-	CNeedTreq,
+	 CNeedPAKreq,	/* dp9ik only */
+	 CHavePAKy,
+	 CNeedTreq,	/* p9sk1 only */
 	CHaveTicket,
 	CNeedAuth,
 
 	/* server phases */
 	SNeedChal,
-	SHaveTreq,
+	 SHavePAKreq,	/* dp9ik only */
+	 SNeedPAKy,
+	 SHaveTreq,	/* p9sk1 only */
 	SNeedTicket,
 	SHaveAuth,
-
+	
 	Maxphase,
 };
 
@@ -50,17 +62,21 @@
 static char *phasenames[Maxphase] =
 {
 [CHaveChal]		"CHaveChal",
+[CNeedPAKreq]		"CNeedPAKreq",
+[CHavePAKy]		"CHavePAKy",
 [CNeedTreq]		"CNeedTreq",
 [CHaveTicket]		"CHaveTicket",
 [CNeedAuth]		"CNeedAuth",
 
 [SNeedChal]		"SNeedChal",
+[SHavePAKreq]		"SHavePAKreq",
+[SNeedPAKy]		"SNeedPAKy",
 [SHaveTreq]		"SHaveTreq",
 [SNeedTicket]		"SNeedTicket",
 [SHaveAuth]		"SHaveAuth",
 };
 
-static int gettickets(State*, Ticketreq *, char*, int);
+static int gettickets(State*, Ticketreq*, uchar*, char*, int);
 
 static int
 p9skinit(Proto *p, Fsstate *fss)
@@ -78,25 +94,13 @@
 	fss = fss;
 	fss->phasename = phasenames;
 	fss->maxphase = Maxphase;
-	if(p == &p9sk1)
-		s->vers = 1;
-	else if(p == &p9sk2)
-		s->vers = 2;
-	else
-		abort();
+	fss->proto = p;
 	if(iscli){
-		switch(s->vers){
-		case 1:
-			fss->phase = CHaveChal;
-			genrandom((uchar*)s->cchal, CHALLEN);
-			break;
-		case 2:
-			fss->phase = CNeedTreq;
-			break;
-		}
+		fss->phase = CHaveChal;
+		genrandom((uchar*)s->cchal, CHALLEN);
 	}else{
 		s->tr.type = AuthTreq;
-		attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
+		attr = setattr(_copyattr(fss->attr), "proto=%q", p->name);
 		mkkeyinfo(&ki, fss, attr);
 		ki.user = nil;
 		ret = findkey(&k, &ki, "user? dom?");
@@ -108,16 +112,9 @@
 		safecpy(s->tr.authid, _strfindattr(k->attr, "user"), sizeof(s->tr.authid));
 		safecpy(s->tr.authdom, _strfindattr(k->attr, "dom"), sizeof(s->tr.authdom));
 		s->key = k;
+		memmove(&s->k, k->priv, sizeof(Authkey));
 		genrandom((uchar*)s->tr.chal, sizeof s->tr.chal);
-		switch(s->vers){
-		case 1:
-			fss->phase = SNeedChal;
-			break;
-		case 2:
-			fss->phase = SHaveTreq;
-			memmove(s->cchal, s->tr.chal, CHALLEN);
-			break;
-		}
+		fss->phase = SNeedChal;
 	}
 	s->tbuflen = 0;
 	s->secret = nil;
@@ -126,6 +123,39 @@
 }
 
 static int
+establish(Fsstate *fss)
+{
+	AuthInfo *ai;
+	State *s;
+
+	s = fss->ps;
+	ai = &fss->ai;
+
+	ai->cuid = s->t.cuid;
+	ai->suid = s->t.suid;
+	if(fss->proto == &dp9ik){
+		static char info[] = "Plan 9 session secret";
+
+		ai->nsecret = 256;
+		ai->secret = emalloc(ai->nsecret);
+		hkdf_x(	s->rand, 2*NONCELEN,
+			(uchar*)info, sizeof(info)-1,
+			s->t.key, NONCELEN,
+			ai->secret, ai->nsecret,
+			hmac_sha2_256, SHA2_256dlen);
+	} else {
+		ai->nsecret = 8;
+		ai->secret = emalloc(ai->nsecret);
+		des56to64((uchar*)s->t.key, ai->secret);
+	}
+	s->secret = ai->secret;
+
+	fss->haveai = 1;
+	fss->phase = Established;
+	return RpcOk;
+}
+
+static int
 p9skread(Fsstate *fss, void *a, uint *n)
 {
 	int m;
@@ -142,13 +172,37 @@
 			return toosmall(fss, m);
 		*n = m;
 		memmove(a, s->cchal, m);
-		fss->phase = CNeedTreq;
+		if(fss->proto == &dp9ik)
+			fss->phase = CNeedPAKreq;
+		else
+			fss->phase = CNeedTreq;
 		return RpcOk;
 
+	case SHavePAKreq:
+		m = TICKREQLEN + PAKYLEN;
+		if(*n < m)
+			return toosmall(fss, m);
+		s->tr.type = AuthPAK;
+		*n = convTR2M(&s->tr, a, *n);
+		authpak_new(&s->p, &s->k, (uchar*)a + *n, 1);
+		*n += PAKYLEN;
+		fss->phase = SNeedPAKy;
+		return RpcOk;
+
+	case CHavePAKy:
+		m = PAKYLEN;
+		if(*n < m)
+			return toosmall(fss, m);
+		*n = m;
+		memmove(a, s->y, m);
+		fss->phase = CHaveTicket;
+		return RpcOk;
+
 	case SHaveTreq:
 		m = TICKREQLEN;
 		if(*n < m)
 			return toosmall(fss, m);
+		s->tr.type = AuthTreq;
 		*n = convTR2M(&s->tr, a, *n);
 		fss->phase = SNeedTicket;
 		return RpcOk;
@@ -168,15 +222,7 @@
 			return toosmall(fss, m);
 		*n = m;
 		memmove(a, s->tbuf, m);
-		fss->ai.cuid = s->t.cuid;
-		fss->ai.suid = s->t.suid;
-		s->secret = emalloc(8);
-		des56to64((uchar*)s->t.key, s->secret);
-		fss->ai.secret = s->secret;
-		fss->ai.nsecret = 8;
-		fss->haveai = 1;
-		fss->phase = Established;
-		return RpcOk;
+		return establish(fss);
 	}
 }
 
@@ -184,12 +230,12 @@
 p9skwrite(Fsstate *fss, void *a, uint n)
 {
 	int m, ret, sret;
-	char tbuf[2*TICKETLEN], *user;
+	char tbuf[2*PAKYLEN+2*MAXTICKETLEN], *user;
 	Attr *attr;
-	Authenticator auth;
 	State *s;
 	Key *srvkey;
 	Keyinfo ki;
+	Authenticator auth;
 
 	s = fss->ps;
 	switch(fss->phase){
@@ -201,24 +247,32 @@
 		if(n < m)
 			return toosmall(fss, m);
 		memmove(s->cchal, a, m);
-		fss->phase = SHaveTreq;
+		if(fss->proto == &dp9ik)
+			fss->phase = SHavePAKreq;
+		else
+			fss->phase = SHaveTreq;
 		return RpcOk;
 
+	case CNeedPAKreq:
+		m = TICKREQLEN+PAKYLEN;
+		if(n < m)
+			return toosmall(fss, m);
 	case CNeedTreq:
+		m = TICKREQLEN;
+		if(n < m)
+			return toosmall(fss, m);
+
 		m = convM2TR(a, n, &s->tr);
-		if(m <= 0)
-			return toosmall(fss, -m);
+		if(m <= 0 || s->tr.type != (fss->phase==CNeedPAKreq ? AuthPAK : AuthTreq))
+			return failure(fss, Easproto);
 
-		/* remember server's chal */
-		if(s->vers == 2)
-			memmove(s->cchal, s->tr.chal, CHALLEN);
-
 		if(s->key != nil)
 			closekey(s->key);
 
 		attr = _delattr(_delattr(_copyattr(fss->attr), "role"), "user");
-		attr = setattr(attr, "proto=p9sk1");
+		attr = setattr(attr, "proto=%q", fss->proto->name);
 		user = _strfindattr(fss->attr, "user");
+
 		/*
 		 * If our client is the user who started factotum (client==owner), then
 		 * he can use whatever keys we have to speak as whoever he pleases.
@@ -241,7 +295,7 @@
 			attr = setattr(attr, "user=%q", user);
 		mkkeyinfo(&ki, fss, attr);
 		ret = findkey(&s->key, &ki,
-			"role=client dom=%q %s", s->tr.authdom, p9sk1.keyprompt);
+			"role=client dom=%q %s", s->tr.authdom, fss->proto->keyprompt);
 		if(ret == RpcOk)
 			closekey(srvkey);
 		else if(sret == RpcOk){
@@ -256,7 +310,6 @@
 		}
 
 		/* fill in the rest of the request */
-		s->tr.type = AuthTreq;
 		safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof s->tr.hostid);
 		if(s->speakfor)
 			safecpy(s->tr.uid, fss->sysuser, sizeof s->tr.uid);
@@ -263,20 +316,26 @@
 		else
 			safecpy(s->tr.uid, s->tr.hostid, sizeof s->tr.uid);
 
-		/* get tickets, from auth server or invent if we can */
-		ret = gettickets(s, &s->tr, tbuf, sizeof(tbuf));
-		if(ret < 0){
+		/* get tickets from authserver or invent if we can */
+		if(fss->phase == CNeedPAKreq)
+			ret = gettickets(s, &s->tr, (uchar*)a + m, tbuf, sizeof(tbuf));
+		else
+			ret = gettickets(s, &s->tr, nil, tbuf, sizeof(tbuf));
+		if(ret <= 0){
 			_freeattr(attr);
 			return failure(fss, nil);
 		}
-
-		m = convM2T(tbuf, ret, &s->t, (Authkey*)s->key->priv);
+		m = convM2T(tbuf, ret, &s->t, &s->k);
+		if(m <= 0 || (fss->proto == &dp9ik && s->t.form == 0)){
+			_freeattr(attr);
+			return failure(fss, Easproto);
+		}
 		if(s->t.num != AuthTc){
 			if(s->key->successes == 0 && !s->speakfor)
 				disablekey(s->key);
 			if(askforkeys && !s->speakfor){
 				snprint(fss->keyinfo, sizeof fss->keyinfo,
-					"%A %s", attr, p9sk1.keyprompt);
+					"%A %s", attr, fss->proto->keyprompt);
 				_freeattr(attr);
 				return RpcNeedkey;
 			}else{
@@ -288,32 +347,44 @@
 		_freeattr(attr);
 		ret -= m;
 		memmove(s->tbuf, tbuf+m, ret);
-
+		genrandom(s->rand, NONCELEN);
 		auth.num = AuthAc;
 		memmove(auth.chal, s->tr.chal, CHALLEN);
-		auth.id = 0;
+		memmove(auth.rand, s->rand, NONCELEN);
 		ret += convA2M(&auth, s->tbuf+ret, sizeof(s->tbuf)-ret, &s->t);
 		s->tbuflen = ret;
-		fss->phase = CHaveTicket;
+		if(fss->phase == CNeedPAKreq)
+			fss->phase = CHavePAKy;
+		else
+			fss->phase = CHaveTicket;
 		return RpcOk;
 
+	case SNeedPAKy:
+		m = PAKYLEN;
+		if(n < m)
+			return toosmall(fss, m);
+		if(authpak_finish(&s->p, &s->k, (uchar*)a))
+			return failure(fss, Easproto);
+		fss->phase = SNeedTicket;
+		return RpcOk;
+
 	case SNeedTicket:
-		m = convM2T(a, n, &s->t, (Authkey*)s->key->priv);
-		if(m <= 0)
-			return toosmall(fss, -m);
+		m = convM2T(a, n, &s->t, &s->k);
+		if(m <= 0 || convM2A((char*)a+m, n-m, &auth, &s->t) <= 0)
+			return toosmall(fss, MAXTICKETLEN + MAXAUTHENTLEN);
+		if(fss->proto == &dp9ik && s->t.form == 0)
+			return failure(fss, Easproto);
 		if(s->t.num != AuthTs
-		|| memcmp(s->t.chal, s->tr.chal, CHALLEN) != 0)
+		|| tsmemcmp(s->t.chal, s->tr.chal, CHALLEN) != 0)
 			return failure(fss, Easproto);
-		ret = convM2A((char*)a+m, n-m, &auth, &s->t);
-		if(ret <= 0)
-			return toosmall(fss, -ret + m);
 		if(auth.num != AuthAc
-		|| memcmp(auth.chal, s->tr.chal, CHALLEN) != 0
-		|| auth.id != 0)
+		|| tsmemcmp(auth.chal, s->tr.chal, CHALLEN) != 0)
 			return failure(fss, Easproto);
+		memmove(s->rand, auth.rand, NONCELEN);
+		genrandom(s->rand + NONCELEN, NONCELEN);
 		auth.num = AuthAs;
 		memmove(auth.chal, s->cchal, CHALLEN);
-		auth.id = 0;
+		memmove(auth.rand, s->rand + NONCELEN, NONCELEN);
 		s->tbuflen = convA2M(&auth, s->tbuf, sizeof(s->tbuf), &s->t);
 		fss->phase = SHaveAuth;
 		return RpcOk;
@@ -323,18 +394,10 @@
 		if(m <= 0)
 			return toosmall(fss, -m);
 		if(auth.num != AuthAs
-		|| memcmp(auth.chal, s->cchal, CHALLEN) != 0
-		|| auth.id != 0)
+		|| tsmemcmp(auth.chal, s->cchal, CHALLEN) != 0)
 			return failure(fss, Easproto);
-		fss->ai.cuid = s->t.cuid;
-		fss->ai.suid = s->t.suid;
-		s->secret = emalloc(8);
-		des56to64((uchar*)s->t.key, s->secret);
-		fss->ai.secret = s->secret;
-		fss->ai.nsecret = 8;
-		fss->haveai = 1;
-		fss->phase = Established;
-		return RpcOk;
+		memmove(s->rand+NONCELEN, auth.rand, NONCELEN);
+		return establish(fss);
 	}
 }
 
@@ -352,6 +415,7 @@
 		closekey(s->key);
 		s->key = nil;
 	}
+	memset(s, 0, sizeof(State));
 	free(s);
 }
 
@@ -386,14 +450,27 @@
 p9skaddkey(Key *k, int before)
 {
 	Authkey *akey;
-	char *s;
+	char *s, *u;
 
+	u = _strfindattr(k->attr, "user");
+	if(u == nil){
+		werrstr("no user attribute");
+		return -1;
+	}
 	akey = emalloc(sizeof(Authkey));
 	if(s = _strfindattr(k->privattr, "!hex")){
-		if(hexparse(s, (uchar*)akey->des, DESKEYLEN) < 0){
-			free(akey);
-			werrstr("malformed key data");
-			return -1;
+		if(k->proto == &dp9ik){
+			if(hexparse(s, akey->aes, AESKEYLEN) < 0){
+				free(akey);
+				werrstr("malformed key data");
+				return -1;
+			}
+		} else {
+			if(hexparse(s, (uchar*)akey->des, DESKEYLEN) < 0){
+				free(akey);
+				werrstr("malformed key data");
+				return -1;
+			}
 		}
 	}else if(s = _strfindattr(k->privattr, "!password")){
 		passtokey(akey, s);
@@ -402,6 +479,10 @@
 		free(akey);
 		return -1;
 	}
+	if(k->proto == &dp9ik)
+		authpak_hash(akey, u);
+	else
+		memset(akey->aes, 0, AESKEYLEN);	/* don't attempt AuthPAK for p9sk1 key */
 	k->priv = akey;
 	return replacekey(k, before);
 }
@@ -409,32 +490,13 @@
 static void
 p9skclosekey(Key *k)
 {
+	memset(k->priv, 0, sizeof(Authkey));
 	free(k->priv);
 }
 
 static int
-getastickets(State *s, Ticketreq *tr, char *tbuf, int tbuflen)
+mkservertickets(State *s, uchar *y, char *tbuf, int tbuflen)
 {
-	int asfd, rv;
-	char *dom;
-
-	if((dom = _strfindattr(s->key->attr, "dom")) == nil){
-		werrstr("auth key has no domain");
-		return -1;
-	}
-	asfd = _authdial(nil, dom);
-	if(asfd < 0)
-		return -1;
-	alarm(30*1000);
-	rv = _asgetticket(asfd, tr, tbuf, tbuflen);
-	alarm(0);
-	close(asfd);
-	return rv;
-}
-
-static int
-mkserverticket(State *s, char *tbuf, int tbuflen)
-{
 	Ticketreq *tr = &s->tr;
 	Ticket t;
 	int ret;
@@ -446,31 +508,86 @@
 		return -1;
 */
 	memset(&t, 0, sizeof(t));
+	ret = 0;
+	if(y != nil){
+		t.form = 1;
+		authpak_new(&s->p, &s->k, s->y, 0);
+		authpak_finish(&s->p, &s->k, y);
+	}
 	memmove(t.chal, tr->chal, CHALLEN);
 	strcpy(t.cuid, tr->uid);
 	strcpy(t.suid, tr->uid);
-	genrandom((uchar*)t.key, DESKEYLEN);
+	genrandom((uchar*)t.key, sizeof(t.key));
 	t.num = AuthTc;
-	ret = convT2M(&t, tbuf, tbuflen, (Authkey*)s->key->priv);
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, &s->k);
 	t.num = AuthTs;
-	ret += convT2M(&t, tbuf+ret, tbuflen-ret, (Authkey*)s->key->priv);
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, &s->k);
+	memset(&t, 0, sizeof(t));
+
 	return ret;
 }
 
 static int
-gettickets(State *s, Ticketreq *tr, char *tbuf, int tbuflen)
+getastickets(State *s, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
 {
+	int asfd, rv;
+	char *dom;
+
+	if((dom = _strfindattr(s->key->attr, "dom")) == nil){
+		werrstr("auth key has no domain");
+		return -1;
+	}
+	asfd = _authdial(dom);
+	if(asfd < 0)
+		return -1;
+	alarm(30*1000);
+	if(y != nil){
+		rv = -1;
+		s->tr.type = AuthPAK;
+		if(_asrequest(asfd, tr) != 0 || write(asfd, y, PAKYLEN) != PAKYLEN)
+			goto Out;
+
+		authpak_new(&s->p, &s->k, (uchar*)tbuf, 1);
+		if(write(asfd, tbuf, PAKYLEN) != PAKYLEN)
+			goto Out;
+
+		if(_asrdresp(asfd, tbuf, 2*PAKYLEN) != 2*PAKYLEN)
+			goto Out;
+	
+		memmove(s->y, tbuf, PAKYLEN);
+		if(authpak_finish(&s->p, &s->k, (uchar*)tbuf+PAKYLEN))
+			goto Out;
+	}
+	s->tr.type = AuthTreq;
+	rv = _asgetticket(asfd, tr, tbuf, tbuflen);
+Out:
+	alarm(0);
+	close(asfd);
+	return rv;
+}
+
+static int
+gettickets(State *s, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
 	int ret;
 
-	ret = getastickets(s, tr, tbuf, tbuflen);
-	if(ret >= 0)
+	if(tr->authdom[0] == 0
+	|| tr->authid[0] == 0
+	|| tr->hostid[0] == 0
+	|| tr->uid[0] == 0){
+		werrstr("bad ticket request");
+		return -1;
+	}
+	memmove(&s->k, s->key->priv, sizeof(Authkey));
+	ret = getastickets(s, tr, y, tbuf, tbuflen);
+	if(ret > 0)
 		return ret;
-	return mkserverticket(s, tbuf, tbuflen);
+	return mkservertickets(s, y, tbuf, tbuflen);
 }
 
 Proto p9sk1 = {
 .name=	"p9sk1",
-.init=		p9skinit,
+.init=	p9skinit,
 .write=	p9skwrite,
 .read=	p9skread,
 .close=	p9skclose,
@@ -479,11 +596,13 @@
 .keyprompt=	"user? !password?"
 };
 
-Proto p9sk2 = {
-.name=	"p9sk2",
-.init=		p9skinit,
+Proto dp9ik = {
+.name=	"dp9ik",
+.init=	p9skinit,
 .write=	p9skwrite,
 .read=	p9skread,
 .close=	p9skclose,
+.addkey=	p9skaddkey,
+.closekey=	p9skclosekey,
+.keyprompt=	"user? !password?"
 };
-
--- a/sys/src/cmd/auth/factotum/rpc.c
+++ b/sys/src/cmd/auth/factotum/rpc.c
@@ -429,15 +429,20 @@
 				return -1;
 			}
 		}
-		for(i=0; i<ring->nkey; ){
-			if(matchattr(attr, ring->key[i]->attr, ring->key[i]->privattr)){
+	Again:
+		qlock(ring);
+		for(i=0; i<ring->nkey; i++){
+			k = ring->key[i];
+			if(matchattr(attr, k->attr, k->privattr)){
 				nmatch++;
-				closekey(ring->key[i]);
 				ring->nkey--;
 				memmove(&ring->key[i], &ring->key[i+1], (ring->nkey-i)*sizeof(ring->key[0]));
-			}else
-				i++;
+				qunlock(ring);
+				closekey(k);
+				goto Again;
+			}
 		}
+		qunlock(ring);
 		_freeattr(attr);
 		if(nmatch == 0){
 			werrstr("found no keys to delete");
--- a/sys/src/cmd/auth/factotum/util.c
+++ b/sys/src/cmd/auth/factotum/util.c
@@ -1,6 +1,7 @@
 #include "dat.h"
 
 static char secstore[100];   /* server name */
+static uchar zeros[16];
 
 /* bind in the default network and cs */
 static int
@@ -72,14 +73,13 @@
 }
 
 int
-_authdial(char *net, char *authdom)
+_authdial(char *authdom)
 {
-	int i, fd, vanilla;
+	int i, fd;
 
 	alarm(30*1000);
-	vanilla = net==nil || strcmp(net, "/net")==0;
-	if(!vanilla || bindnetcs()>=0)
-		fd = authdial(net, authdom);
+	if(bindnetcs()>=0)
+		fd = authdial(nil, authdom);
 	else {
 		/*
 		 * If we failed to mount /srv/cs, assume that
@@ -106,6 +106,31 @@
 	return fd;
 }
 
+int
+_authreq(Ticketreq *tr, Authkey *k)
+{
+	int fd;
+
+	fd = _authdial(tr->authdom);
+	if(fd < 0)
+		return -1;
+	alarm(30*1000);
+	if(tsmemcmp(k->aes, zeros, AESKEYLEN) != 0){
+		if(_asgetpakkey(fd, tr, k) < 0){
+			alarm(0);
+			close(fd);
+			return -1;
+		}
+	}
+	if(_asrequest(fd, tr) < 0){
+		alarm(0);
+		close(fd);
+		return -1;
+	}
+	alarm(0);
+	return fd;
+}
+
 /*
  *  prompt user for a key.  don't care about memory leaks, runs standalone
  */
@@ -200,8 +225,8 @@
 				return fss->conf[i].canuse;
 		if(fss->nconf%16 == 0)
 			fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0])));
+		incref(k);
 		fss->conf[fss->nconf].key = k;
-		k->ref++;
 		fss->conf[fss->nconf].canuse = -1;
 		fss->conf[fss->nconf].tag = conftaggen++;
 		fss->nconf++;
@@ -216,7 +241,7 @@
 {
 	if(k == nil)
 		return;
-	if(--k->ref != 0)
+	if(decref(k))
 		return;
 	if(k->proto && k->proto->closekey)
 		(*k->proto->closekey)(k);
@@ -377,6 +402,7 @@
 		return failure(ki->fss, nil);
 	}
 
+	qlock(ring);
 	nmatch = 0; 
 	for(i=0; i<ring->nkey; i++){
 		k = ring->key[i];
@@ -391,6 +417,7 @@
 			if(!ki->noconf){
 				switch(canusekey(ki->fss, k)){
 				case -1:
+					qunlock(ring);
 					_freeattr(attr1);
 					return RpcConfirm;
 				case 0:
@@ -402,11 +429,13 @@
 			_freeattr(attr1);
 			_freeattr(attr2);
 			_freeattr(attr3);
-			k->ref++;
+			incref(k);
 			*ret = k;
+			qunlock(ring);
 			return RpcOk;
 		}
 	}
+	qunlock(ring);
 	flog("%d: no key matches %A %A %A %A", ki->fss->seqnum, attr0, attr1, attr2, attr3);
 	werrstr("no key matches %A %A", attr0, attr1);
 	_freeattr(attr2);
@@ -446,6 +475,7 @@
 {
 	char *dom;
 	Keyinfo ki;
+	int rv;
 
 	/*
 	 * We don't use fss->attr here because we don't
@@ -454,10 +484,18 @@
 	mkkeyinfo(&ki, fss, nil);
 	ki.attr = nil;
 	ki.user = nil;
-	if(dom = _strfindattr(fss->attr, "dom"))
-		return findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom);
+	dom = _strfindattr(fss->attr, "dom");
+	if(dom != nil)
+		rv = findkey(k, &ki, "proto=dp9ik dom=%q role=server user?", dom);
 	else
-		return findkey(k, &ki, "proto=p9sk1 role=server dom? user?");
+		rv = findkey(k, &ki, "proto=dp9ik role=server dom? user?");
+	if(rv != RpcOk){
+		if(dom != nil)
+			rv = findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom);
+		else
+			rv = findkey(k, &ki, "proto=p9sk1 role=server dom? user?");
+	}
+	return rv;
 }
 
 Proto*
@@ -471,12 +509,11 @@
 	return nil;
 }
 
-char*
+int
 getnvramkey(int flag)
 {
 	Nvrsafe safe;
 	char *s;
-	int i;
 
 	memset(&safe, 0, sizeof safe);
 	/*
@@ -484,24 +521,30 @@
 	 * but safe still holds good data.
 	 */
 	if(readnvram(&safe, flag)<0 && safe.authid[0]==0) 
-		return nil;
+		return -1;
 
-	/*
-	 *  only use nvram key if it is non-zero
-	 */
-	for(i = 0; i < DESKEYLEN; i++)
-		if(safe.machkey[i] != 0)
-			break;
-	if(i == DESKEYLEN)
-		return nil;
-
-	fmtinstall('H', encodefmt);
-	s = smprint("key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", 
-		safe.authid, safe.authdom, DESKEYLEN, safe.machkey);
+	if(tsmemcmp(safe.machkey, zeros, DESKEYLEN) != 0){
+		s = smprint("key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", 
+			safe.authid, safe.authdom, DESKEYLEN, safe.machkey);
+		if(s != nil){
+			ctlwrite(s, 0);
+			memset(s, 0, strlen(s));
+			free(s);
+		}
+	}
+	if(tsmemcmp(safe.aesmachkey, zeros, AESKEYLEN) != 0){
+		s = smprint("key proto=dp9ik user=%q dom=%q !hex=%.*H !password=______", 
+			safe.authid, safe.authdom, AESKEYLEN, safe.aesmachkey);
+		if(s != nil){
+			ctlwrite(s, 0);
+			memset(s, 0, strlen(s));
+			free(s);
+		}
+	}
 	writehostowner(safe.authid);
 	memset(&safe, 0, sizeof safe);
 
-	return s;
+	return 0;
 }
 
 int
@@ -781,18 +824,19 @@
 	int i;
 	Key *k;
 
+	qlock(ring);
+	incref(kn);
 	for(i=0; i<ring->nkey; i++){
 		k = ring->key[i];
 		if(matchattr(kn->attr, k->attr, nil) && matchattr(k->attr, kn->attr, nil)){
-			closekey(k);
-			kn->ref++;
 			ring->key[i] = kn;
+			qunlock(ring);
+			closekey(k);
 			return 0;
 		}
 	}
 	if(ring->nkey%16 == 0)
 		ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0]));
-	kn->ref++;
 	if(before){
 		memmove(ring->key+1, ring->key, ring->nkey*sizeof ring->key[0]);
 		ring->key[0] = kn;
@@ -799,6 +843,7 @@
 		ring->nkey++;
 	}else
 		ring->key[ring->nkey++] = kn;
+	qunlock(ring);
 	return 0;
 }
 
--- a/sys/src/cmd/auth/keyfs.c
+++ b/sys/src/cmd/auth/keyfs.c
@@ -25,6 +25,7 @@
 	Quser,
 	Qkey,
 	Qaeskey,
+	Qpakhash,
 	Qsecret,
 	Qlog,
 	Qstatus,
@@ -54,8 +55,7 @@
 
 struct User {
 	char	*name;
-	char	key[DESKEYLEN];
-	uchar	aeskey[AESKEYLEN];
+	Authkey	key;
 	char	secret[SECRETLEN];
 	ulong	expire;			/* 0 == never */
 	uchar	status;
@@ -73,6 +73,7 @@
 	[Quser]		".",
 	[Qkey]		"key",
 	[Qaeskey]	"aeskey",
+	[Qpakhash]	"pakhash",
 	[Qsecret]	"secret",
 	[Qlog]		"log",
 	[Qexpire]	"expire",
@@ -180,6 +181,7 @@
 	if(pipe(p) < 0)
 		error("can't make pipe: %r");
 
+	private();
 	if(usepass)
 		getpass(&authkey, nil, 0, 0);
 	else {
@@ -374,7 +376,7 @@
 	mode = rhdr.mode;
 	if(f->qtype == Quser && (mode & (OWRITE|OTRUNC)))
 		return "user already exists";
-	if(f->qtype == Qaeskey && !keydbaes)
+	if((f->qtype == Qaeskey || f->qtype == Qpakhash) && !keydbaes)
 		return "keyfile not in aes format";
 	thdr.qid = mkqid(f->user, f->qtype);
 	thdr.iounit = messagesize - IOHDRSZ;
@@ -458,6 +460,7 @@
 		return 0;
 	case Qkey:
 	case Qaeskey:
+	case Qpakhash:
 	case Qsecret:
 		if(f->user->status != Sok)
 			return "user disabled";
@@ -468,13 +471,17 @@
 		m = 0;
 		switch(f->qtype){
 		case Qkey:
-			data = f->user->key;
+			data = (char*)f->user->key.des;
 			m = DESKEYLEN;
 			break;
 		case Qaeskey:
-			data = (char*)f->user->aeskey;
+			data = (char*)f->user->key.aes;
 			m = AESKEYLEN;
 			break;
+		case Qpakhash:
+			data = (char*)f->user->key.pakhash;
+			m = PAKHASHLEN;
+			break;
 		case Qsecret:
 			data = f->user->secret;
 	Readstr:
@@ -531,14 +538,15 @@
 	case Qkey:
 		if(n != DESKEYLEN)
 			return "garbled write data";
-		memmove(f->user->key, data, DESKEYLEN);
-		thdr.count = DESKEYLEN;
+		memmove(f->user->key.des, data, n);
+		thdr.count = n;
 		break;
 	case Qaeskey:
 		if(n != AESKEYLEN)
 			return "garbled write data";
-		memmove(f->user->aeskey, data, AESKEYLEN);
-		thdr.count = AESKEYLEN;
+		memmove(f->user->key.aes, data, n);
+		authpak_hash(&f->user->key, f->user->name);
+		thdr.count = n;
 		break;
 	case Qsecret:
 		if(n >= SECRETLEN)
@@ -728,7 +736,7 @@
 		for(u = users[i]; u != nil; u = u->link){
 			strncpy((char*)p, u->name, Namelen);
 			p += Namelen;
-			memmove(p, u->key, DESKEYLEN);
+			memmove(p, u->key.des, DESKEYLEN);
 			p += DESKEYLEN;
 			*p++ = u->status;
 			*p++ = u->warnings;
@@ -740,7 +748,7 @@
 			memmove(p, u->secret, SECRETLEN);
 			p += SECRETLEN;
 			if(keydbaes){
-				memmove(p, u->aeskey, AESKEYLEN);
+				memmove(p, u->key.aes, AESKEYLEN);
 				p += AESKEYLEN;
 			}
 		}
@@ -773,6 +781,8 @@
 
 	free(buf);
 	close(fd);
+
+	newkeys();
 }
 
 int
@@ -897,7 +907,7 @@
 		u = finduser((char*)ep);
 		if(u == nil)
 			u = installuser((char*)ep);
-		memmove(u->key, ep + Namelen, DESKEYLEN);
+		memmove(u->key.des, ep + Namelen, DESKEYLEN);
 		p = ep + Namelen + DESKEYLEN;
 		u->status = *p++;
 		u->warnings = *p++;
@@ -908,8 +918,10 @@
 		memmove(u->secret, p, SECRETLEN);
 		u->secret[SECRETLEN-1] = 0;
 		p += SECRETLEN;
-		if(keydbaes)
-			memmove(u->aeskey, p, AESKEYLEN);
+		if(keydbaes){
+			memmove(u->key.aes, p, AESKEYLEN);
+			authpak_hash(&u->key, u->name);
+		}
 		nu++;
 	}
 	free(buf);
--- a/sys/src/cmd/auth/lib/mkfile
+++ b/sys/src/cmd/auth/lib/mkfile
@@ -6,6 +6,7 @@
 	keyfmt.$O\
 	netcheck.$O\
 	okpasswd.$O\
+	private.$O\
 	readwrite.$O\
 	readarg.$O\
 	readln.$O\
--- a/sys/src/cmd/auth/lib/readwrite.c
+++ b/sys/src/cmd/auth/lib/readwrite.c
@@ -66,8 +66,14 @@
 	int ret;
 
 	memset(key, 0, sizeof(Authkey));
-	ret = finddeskey(db, user, key->des) != nil;
-	ret |= findaeskey(db, user, key->aes) != nil;
+	ret = findaeskey(db, user, key->aes) != nil;
+	if(ret){
+		char filename[Maxpath];
+		snprint(filename, sizeof filename, "%s/%s/pakhash", db, user);
+		if(readfile(filename, (char*)key->pakhash, PAKHASHLEN) != PAKHASHLEN)
+			authpak_hash(key, user);
+	}
+	ret |= finddeskey(db, user, key->des) != nil;
 	return ret;
 }
 
--- a/sys/src/cmd/auth/netkey.c
+++ b/sys/src/cmd/auth/netkey.c
@@ -15,8 +15,7 @@
 void
 main(int argc, char *argv[])
 {
-	Authkey key;
-	char buf[32], pass[32];
+	char buf[32], pass[32], key[DESKEYLEN];
 	char *s;
 	int n;
 
@@ -34,7 +33,7 @@
 	}
 
 	readln("Password: ", pass, sizeof pass, 1);
-	passtokey(&key, pass);
+	passtodeskey(key, pass);
 
 	for(;;){
 		print("challenge: ");
@@ -44,7 +43,7 @@
 		buf[n] = '\0';
 		n = strtol(buf, 0, 10);
 		sprint(buf, "%d", n);
-		netcrypt(key.des, buf);
+		netcrypt(key, buf);
 		print("response: %s\n", buf);
 	}
 }
--- a/sys/src/cmd/auth/passwd.c
+++ b/sys/src/cmd/auth/passwd.c
@@ -7,7 +7,7 @@
 void
 main(int argc, char **argv)
 {
-	int fd, n;
+	int fd, n, try;
 	Ticketreq tr;
 	Ticket t;
 	Passwordreq pr;
@@ -20,6 +20,8 @@
 	ARGBEGIN{
 	}ARGEND
 
+	private();
+
 	s = nil;
 	if(argc > 0){
 		user = argv[0];
@@ -34,6 +36,10 @@
 	if(fd < 0)
 		error("authdial: %r");
 
+	memset(&tr, 0, sizeof(tr));
+	strncpy(tr.uid, user, sizeof(tr.uid)-1);
+	tr.type = AuthPass;
+
 	/*
 	 *  get a password from the user and try to decrypt the
 	 *  ticket.  If it doesn't work we've got a bad password,
@@ -43,18 +49,31 @@
 	readln("Plan 9 Password: ", pr.old, sizeof pr.old, 1);
 	passtokey(&key, pr.old);
 
-	memset(&tr, 0, sizeof(tr));
-	strcpy(tr.uid, user);
-	tr.type = AuthPass;
-
+	/*
+	 *  negotiate PAK key. we need to retry in case the AS does
+	 *  not support the AuthPAK request or when the user has
+	 *  not yet setup a new key and the AS made one up.
+	 */
+	try = 0;
+	authpak_hash(&key, tr.uid);
+	if(_asgetpakkey(fd, &tr, &key) < 0){
+Retry:
+		try++;
+		close(fd);
+		fd = authdial(nil, s);
+		if(fd < 0)
+			error("authdial: %r");
+	}
 	/* send ticket request to AS */
 	if(_asrequest(fd, &tr) < 0)
 		error("%r");
 	if(_asgetresp(fd, &t, nil, &key) < 0)
 		error("%r");
-
-	if(t.num != AuthTp || strcmp(t.cuid, tr.uid) != 0)
+	if(t.num != AuthTp || strcmp(t.cuid, tr.uid) != 0){
+		if(try == 0)
+			goto Retry;
 		error("bad password");
+	}
 
 	/* loop trying new passwords */
 	for(;;){
@@ -62,8 +81,7 @@
 		*pr.new = 0;
 		readln("change Plan 9 Password? (y/n) ", buf, sizeof buf, 0);
 		if(*buf == 'y' || *buf == 'Y'){
-			readln("Password(8 to 31 characters): ", pr.new,
-				sizeof pr.new, 1);
+			readln("Password: ", pr.new, sizeof pr.new, 1);
 			readln("Confirm: ", buf, sizeof buf, 1);
 			if(strcmp(pr.new, buf)){
 				print("!mismatch\n");
@@ -81,8 +99,7 @@
 				else
 					strcpy(pr.secret, pr.new);
 			} else {
-				readln("Secret(0 to 256 characters): ", pr.secret,
-					sizeof pr.secret, 1);
+				readln("Secret: ", pr.secret, sizeof pr.secret, 1);
 				readln("Confirm: ", buf, sizeof buf, 1);
 				if(strcmp(pr.secret, buf)){
 					print("!mismatch\n");
--- a/sys/src/libauth/auth_userpasswd.c
+++ b/sys/src/libauth/auth_userpasswd.c
@@ -11,13 +11,13 @@
  * this was copied from inet's guard.
  */
 static void
-netresp(Authkey *key, long chal, char *answer)
+netresp(char key[DESKEYLEN], long chal, char *answer)
 {
 	uchar buf[8];
 
 	memset(buf, 0, sizeof buf);
 	snprint((char *)buf, sizeof buf, "%lud", chal);
-	if(encrypt(key->des, buf, 8) < 0)
+	if(encrypt(key, buf, 8) < 0)
 		abort();
 	sprint(answer, "%.8ux", buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]);
 }
@@ -25,8 +25,7 @@
 AuthInfo*
 auth_userpasswd(char *user, char *passwd)
 {
-	char resp[16];
-	Authkey key;
+	char resp[16], key[DESKEYLEN];
 	AuthInfo *ai;
 	Chalstate *ch;
 
@@ -38,9 +37,9 @@
 	if((ch = auth_challenge("user=%q proto=p9cr role=server", user)) == nil)
 		return nil;
 
-	passtokey(&key, passwd);
-	netresp(&key, atol(ch->chal), resp);
-	memset(&key, 0, sizeof(Authkey));
+	passtodeskey(key, passwd);
+	netresp(key, atol(ch->chal), resp);
+	memset(key, 0, sizeof(key));
 
 	ch->resp = resp;
 	ch->nresp = strlen(resp);
--- /dev/null
+++ b/sys/src/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);
+}
--- a/sys/src/libauthsrv/_asgetresp.c
+++ b/sys/src/libauthsrv/_asgetresp.c
@@ -5,28 +5,40 @@
 int
 _asgetresp(int fd, Ticket *t, Authenticator *a, Authkey *k)
 {
-	char tbuf[TICKETLEN+AUTHENTLEN];
+	char buf[MAXTICKETLEN+MAXAUTHENTLEN], err[ERRMAX];
 	int n, m;
 
-	m = TICKETLEN;
 	memset(t, 0, sizeof(Ticket));
-	if(a != nil){
-		m += AUTHENTLEN;
+	if(a != nil)
 		memset(a, 0, sizeof(Authenticator));
-	}
 
-	n = _asrdresp(fd, tbuf, m);
-	if(n <= 0)
-		return -1;
+	strcpy(err, "AS protocol botch");
+	errstr(err, ERRMAX);
 
-	m = convM2T(tbuf, n, t, k);
-	if(m <= 0)
+	if(_asrdresp(fd, buf, 0) < 0)
 		return -1;
 
-	if(a != nil){
-		if(convM2A(tbuf+m, n-m, a, t) <= 0)
+	for(n = 0; (m = convM2T(buf, n, t, k)) <= 0; n += m){
+		m = -m;
+		if(m <= n || m > sizeof(buf))
 			return -1;
+		m -= n;
+		if(readn(fd, buf+n, m) != m)
+			return -1;
 	}
+
+	if(a != nil){
+		for(n = 0; (m = convM2A(buf, n, a, t)) <= 0; n += m){
+			m = -m;
+			if(m <= n || m > sizeof(buf))
+				return -1;
+			m -= n;
+			if(readn(fd, buf+n, m) != m)
+				return -1;
+		}
+	}
+
+	errstr(err, ERRMAX);
 
 	return 0;
 }
--- a/sys/src/libauthsrv/_asgetticket.c
+++ b/sys/src/libauthsrv/_asgetticket.c
@@ -2,16 +2,36 @@
 #include <libc.h>
 #include <authsrv.h>
 
-static char *pbmsg = "AS protocol botch";
-
 int
 _asgetticket(int fd, Ticketreq *tr, char *tbuf, int tbuflen)
 {
-	if(_asrequest(fd, tr) < 0){
-		werrstr(pbmsg);
+	char err[ERRMAX];
+	int i, n, m, r;
+
+	strcpy(err, "AS protocol botch");
+	errstr(err, ERRMAX);
+
+	if(_asrequest(fd, tr) < 0)
 		return -1;
+	if(_asrdresp(fd, tbuf, 0) < 0)
+		return -1;
+
+	r = 0;
+	for(i = 0; i<2; i++){
+		for(n=0; (m = convM2T(tbuf, n, nil, nil)) <= 0; n += m){
+			m = -m;
+			if(m <= n || m > tbuflen)
+				return -1;
+			m -= n;
+			if(readn(fd, tbuf+n, m) != m)
+				return -1;
+		}
+		r += n;
+		tbuf += n;
+		tbuflen -= n;
 	}
-	if(tbuflen > 2*TICKETLEN)
-		tbuflen = 2*TICKETLEN;
-	return _asrdresp(fd, tbuf, tbuflen);
+
+	errstr(err, ERRMAX);
+
+	return r;
 }
--- /dev/null
+++ b/sys/src/libauthsrv/authpak.c
@@ -1,0 +1,214 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+#include <authsrv.h>
+
+#include "msqrt.mpc"
+#include "decaf.mpc"
+#include "edwards.mpc"
+#include "elligator2.mpc"
+#include "spake2ee.mpc"
+#include "ed448.mpc"
+
+typedef struct PAKcurve PAKcurve;
+struct PAKcurve
+{
+	Lock;
+	mpint	*P;
+	mpint	*A;
+	mpint	*D;
+	mpint	*X;
+	mpint	*Y;
+};
+
+static PAKcurve*
+authpak_curve(void)
+{
+	static PAKcurve a;
+
+	lock(&a);
+	if(a.P == nil){
+		a.P = mpnew(0);
+		a.A = mpnew(0);
+		a.D = mpnew(0);
+		a.X = mpnew(0);
+		a.Y = mpnew(0);
+		ed448_curve(a.P, a.A, a.D, a.X, a.Y);
+		a.P = mpfield(a.P);
+	}
+	unlock(&a);
+	return &a;
+}
+
+void
+authpak_hash(Authkey *k, char *u)
+{
+	static char info[] = "Plan 9 AuthPAK hash";
+	uchar *bp, salt[SHA2_256dlen], h[2*PAKSLEN];
+	mpint *H, *PX,*PY,*PZ,*PT;
+	PAKcurve *c;
+
+	H = mpnew(0);
+	PX = mpnew(0);
+	PY = mpnew(0);
+	PZ = mpnew(0);
+	PT = mpnew(0);
+
+	sha2_256((uchar*)u, strlen(u), salt, nil);
+
+	hkdf_x(	salt, SHA2_256dlen,
+		(uchar*)info, sizeof(info)-1,
+		k->aes, AESKEYLEN,
+		h, sizeof(h),
+		hmac_sha2_256, SHA2_256dlen);
+
+	c = authpak_curve();
+
+	betomp(h + 0*PAKSLEN, PAKSLEN, H);		/* HM */
+	spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT);	/* PM */
+
+	bp = k->pakhash;
+	mptober(PX, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PY, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PZ, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PT, bp, PAKSLEN), bp += PAKSLEN;
+
+	betomp(h + 1*PAKSLEN, PAKSLEN, H);		/* HN */
+	spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT);	/* PN */
+
+	mptober(PX, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PY, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PZ, bp, PAKSLEN), bp += PAKSLEN;
+	mptober(PT, bp, PAKSLEN);
+
+	mpfree(PX);
+	mpfree(PY);
+	mpfree(PZ);
+	mpfree(PT);
+	mpfree(H);
+}
+
+void
+authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient)
+{
+	mpint *PX,*PY,*PZ,*PT, *X, *Y;
+	PAKcurve *c;
+	uchar *bp;
+
+	memset(p, 0, sizeof(PAKpriv));
+	p->isclient = isclient != 0;
+
+	X = mpnew(0);
+	Y = mpnew(0);
+
+	PX = mpnew(0);
+	PY = mpnew(0);
+	PZ = mpnew(0);
+	PT = mpnew(0);
+
+	PX->flags |= MPtimesafe;
+	PY->flags |= MPtimesafe;
+	PZ->flags |= MPtimesafe;
+	PT->flags |= MPtimesafe;
+
+	bp = k->pakhash + PAKPLEN*(p->isclient == 0);
+	betomp(bp, PAKSLEN, PX), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PY), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PZ), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PT);
+
+	c = authpak_curve();
+
+	X->flags |= MPtimesafe;
+	mpnrand(c->P, genrandom, X);
+
+	spake2ee_1(c->P,c->A,c->D, X, c->X,c->Y, PX,PY,PZ,PT, Y);
+
+	mptober(X, p->x, PAKXLEN);
+	mptober(Y, p->y, PAKYLEN);
+
+	memmove(y, p->y, PAKYLEN);
+
+	mpfree(PX);
+	mpfree(PY);
+	mpfree(PZ);
+	mpfree(PT);
+
+	mpfree(X);
+	mpfree(Y);
+}
+
+int
+authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN])
+{
+	static char info[] = "Plan 9 AuthPAK key";
+	uchar *bp, z[PAKSLEN], salt[SHA2_256dlen];
+	mpint *PX,*PY,*PZ,*PT, *X, *Y, *Z, *ok;
+	DigestState *s;
+	PAKcurve *c;
+	int ret;
+
+	X = mpnew(0);
+	Y = mpnew(0);
+	Z = mpnew(0);
+	ok = mpnew(0);
+
+	PX = mpnew(0);
+	PY = mpnew(0);
+	PZ = mpnew(0);
+	PT = mpnew(0);
+
+	PX->flags |= MPtimesafe;
+	PY->flags |= MPtimesafe;
+	PZ->flags |= MPtimesafe;
+	PT->flags |= MPtimesafe;
+
+	bp = k->pakhash + PAKPLEN*(p->isclient != 0);
+	betomp(bp, PAKSLEN, PX), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PY), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PZ), bp += PAKSLEN;
+	betomp(bp, PAKSLEN, PT);
+
+	Z->flags |= MPtimesafe;
+	X->flags |= MPtimesafe;
+	betomp(p->x, PAKXLEN, X);
+
+	betomp(y, PAKYLEN, Y);
+
+	c = authpak_curve();
+	spake2ee_2(c->P,c->A,c->D, PX,PY,PZ,PT, X, Y, ok, Z);
+
+	if(mpcmp(ok, mpzero) == 0){
+		ret = -1;
+		goto out;
+	}
+
+	mptober(Z, z, sizeof(z));
+
+	s = sha2_256(p->isclient ? p->y : y, PAKYLEN, nil, nil);
+	sha2_256(p->isclient ? y : p->y, PAKYLEN, salt, s);
+
+	hkdf_x(	salt, SHA2_256dlen,
+		(uchar*)info, sizeof(info)-1,
+		z, sizeof(z),
+		k->pakkey, PAKKEYLEN,
+		hmac_sha2_256, SHA2_256dlen);
+
+	ret = 0;
+out:
+	memset(z, 0, sizeof(z));
+	memset(p, 0, sizeof(PAKpriv));
+
+	mpfree(PX);
+	mpfree(PY);
+	mpfree(PZ);
+	mpfree(PT);
+
+	mpfree(X);
+	mpfree(Y);
+	mpfree(Z);
+	mpfree(ok);
+
+	return ret;
+}
--- a/sys/src/libauthsrv/convA2M.c
+++ b/sys/src/libauthsrv/convA2M.c
@@ -2,11 +2,7 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
+extern int form1B2M(char *ap, int n, uchar key[32]);
 
 int
 convA2M(Authenticator *f, char *ap, int n, Ticket *t)
@@ -13,15 +9,28 @@
 {
 	uchar *p;
 
-	if(n < AUTHENTLEN)
+	if(n < 1+CHALLEN)
 		return 0;
 
 	p = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	LONG(id);
-	n = p - (uchar*)ap;
-	if(t)
+	*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;
+		return n;
+	case 1:
+		if(n < 12+CHALLEN+NONCELEN+16)
+			return 0;
+
+		memmove(p, f->rand, NONCELEN), p += NONCELEN;
+		return form1B2M(ap, (char*)p - ap, t->key);
+	}
+
+	return 0;
 }
--- a/sys/src/libauthsrv/convM2A.c
+++ b/sys/src/libauthsrv/convM2A.c
@@ -2,30 +2,35 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
+extern int form1M2B(char *ap, int n, uchar key[32]);
 
 int
 convM2A(char *ap, int n, Authenticator *f, Ticket *t)
 {
-	uchar *p, buf[AUTHENTLEN];
+	uchar buf[MAXAUTHENTLEN], *p;
+	int m;
 
 	memset(f, 0, sizeof(Authenticator));
-	if(n < AUTHENTLEN)
-		return -AUTHENTLEN;
-
-	if(t) {
-		memmove(buf, ap, AUTHENTLEN);
-		ap = (char*)buf;
-		decrypt(t->key, ap, AUTHENTLEN);
+	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 = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	LONG(id);
-	n = p - (uchar*)ap;
-	return n;
+	p = buf;
+	f->num = *p++;
+	memmove(f->chal, p, CHALLEN);
+	p += CHALLEN;
+	if(t->form == 1)
+		memmove(f->rand, p, NONCELEN);
+
+	return m;
 }
--- a/sys/src/libauthsrv/convM2PR.c
+++ b/sys/src/libauthsrv/convM2PR.c
@@ -2,35 +2,38 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
+extern int form1M2B(char *ap, int n, uchar key[32]);
 
 int
 convM2PR(char *ap, int n, Passwordreq *f, Ticket *t)
 {
-	uchar *p, buf[PASSREQLEN];
+	uchar *p, buf[MAXPASSREQLEN];
+	int m;
 
 	memset(f, 0, sizeof(Passwordreq));
-	if(n < PASSREQLEN)
-		return -PASSREQLEN;
-
-	if(t){
-		memmove(buf, ap, PASSREQLEN);
-		ap = (char*)buf;
-		decrypt(t->key, ap, PASSREQLEN);
+	if(t->form == 0){
+		m = 1+2*ANAMELEN+1+SECRETLEN;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		decrypt(t->key, buf, m);
+	} else {
+		m = 12+2*ANAMELEN+1+SECRETLEN+16;
+		if(n < m)
+			return -m;
+		memmove(buf, ap, m);
+		if(form1M2B((char*)buf, m, t->key) < 0)
+			return m;
 	}
-	p = (uchar*)ap;
-	CHAR(num);
-	STRING(old, ANAMELEN);
+	p = buf;
+	f->num = *p++;
+	memmove(f->old, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->new, p, ANAMELEN), p += ANAMELEN;
+	f->changesecret = *p++;
+	memmove(f->secret, p, SECRETLEN);
 	f->old[ANAMELEN-1] = 0;
-	STRING(new, ANAMELEN);
 	f->new[ANAMELEN-1] = 0;
-	CHAR(changesecret);
-	STRING(secret, SECRETLEN);
 	f->secret[SECRETLEN-1] = 0;
-	n = p - (uchar*)ap;
-	return n;
+
+	return m;
 }
--- a/sys/src/libauthsrv/convM2T.c
+++ b/sys/src/libauthsrv/convM2T.c
@@ -2,34 +2,50 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
+extern int form1check(char *ap, int n);
+extern int form1M2B(char *ap, int n, uchar key[32]);
 
 int
-convM2T(char *ap, int n, Ticket *f, Authkey *key)
+convM2T(char *ap, int n, Ticket *f, Authkey *k)
 {
-	uchar *p, buf[TICKETLEN];
+	uchar buf[MAXTICKETLEN], *p;
+	int m;
 
-	memset(f, 0, sizeof(Ticket));
-	if(n < TICKETLEN)
-		return -TICKETLEN;
+	if(f != nil)
+		memset(f, 0, sizeof(Ticket));
 
-	if(key){
-		memmove(buf, ap, TICKETLEN);
-		ap = (char*)buf;
-		decrypt(key->des, ap, TICKETLEN);
+	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 = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	STRING(cuid, ANAMELEN);
+	p = buf;
+	f->num = *p++;
+	memmove(f->chal, p, CHALLEN), p += CHALLEN;
+	memmove(f->cuid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->suid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->key, p, f->form == 0 ? DESKEYLEN : NONCELEN);
+
 	f->cuid[ANAMELEN-1] = 0;
-	STRING(suid, ANAMELEN);
 	f->suid[ANAMELEN-1] = 0;
-	STRING(key, DESKEYLEN);
-	n = p - (uchar*)ap;
-	return n;
+
+	return m;
 }
--- a/sys/src/libauthsrv/convM2TR.c
+++ b/sys/src/libauthsrv/convM2TR.c
@@ -2,12 +2,6 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		f->x = *p++
-#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(f->x, p, n); p += n
-
 int
 convM2TR(char *ap, int n, Ticketreq *f)
 {
@@ -18,16 +12,18 @@
 		return -TICKREQLEN;
 
 	p = (uchar*)ap;
-	CHAR(type);
-	STRING(authid, ANAMELEN);
+	f->type = *p++;
+	memmove(f->authid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->authdom, p, DOMLEN), p += DOMLEN;
+	memmove(f->chal, p, CHALLEN), p += CHALLEN;
+	memmove(f->hostid, p, ANAMELEN), p += ANAMELEN;
+	memmove(f->uid, p, ANAMELEN), p += ANAMELEN;
+
 	f->authid[ANAMELEN-1] = 0;
-	STRING(authdom, DOMLEN);
 	f->authdom[DOMLEN-1] = 0;
-	STRING(chal, CHALLEN);
-	STRING(hostid, ANAMELEN);
 	f->hostid[ANAMELEN-1] = 0;
-	STRING(uid, ANAMELEN);
 	f->uid[ANAMELEN-1] = 0;
 	n = p - (uchar*)ap;
+
 	return n;
 }
--- a/sys/src/libauthsrv/convPR2M.c
+++ b/sys/src/libauthsrv/convPR2M.c
@@ -2,11 +2,7 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
+extern int form1B2M(char *ap, int n, uchar key[32]);
 
 int
 convPR2M(Passwordreq *f, char *ap, int n, Ticket *t)
@@ -13,18 +9,26 @@
 {
 	uchar *p;
 
-	if(n < PASSREQLEN)
+	if(n < 1+2*ANAMELEN+1+SECRETLEN)
 		return 0;
 
 	p = (uchar*)ap;
-	CHAR(num);
-	STRING(old, ANAMELEN);
-	STRING(new, ANAMELEN);
-	CHAR(changesecret);
-	STRING(secret, SECRETLEN);
-	n = p - (uchar*)ap;
-	if(t)
+	*p++ = f->num;
+	memmove(p, f->old, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->new, ANAMELEN), p += ANAMELEN;
+	*p++ = f->changesecret;
+	memmove(p, f->secret, SECRETLEN), p += SECRETLEN;
+	switch(t->form){
+	case 0:
+		n = p - (uchar*)ap;
 		encrypt(t->key, ap, n);
-	return n;
+		return n;
+	case 1:
+		if(n < 12+2*ANAMELEN+1+SECRETLEN+16)
+			return 0;
+		return form1B2M(ap, p - (uchar*)ap, t->key);
+	}
+
+	return 0;
 }
 
--- a/sys/src/libauthsrv/convT2M.c
+++ b/sys/src/libauthsrv/convT2M.c
@@ -1,12 +1,9 @@
 #include <u.h>
 #include <libc.h>
 #include <authsrv.h>
+#include <libsec.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
+extern int form1B2M(char *ap, int n, uchar key[32]);
 
 int
 convT2M(Ticket *f, char *ap, int n, Authkey *key)
@@ -13,17 +10,30 @@
 {
 	uchar *p;
 
-	if(n < TICKETLEN)
+	if(n < 1+CHALLEN+2*ANAMELEN)
 		return 0;
 
 	p = (uchar*)ap;
-	CHAR(num);
-	STRING(chal, CHALLEN);
-	STRING(cuid, ANAMELEN);
-	STRING(suid, ANAMELEN);
-	STRING(key, DESKEYLEN);
-	n = p - (uchar*)ap;
-	if(key)
+	*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;
+		return n;
+	case 1:
+		if(n < 12+CHALLEN+2*ANAMELEN+NONCELEN+16)
+			return 0;
+
+		memmove(p, f->key, NONCELEN), p += NONCELEN;
+		return form1B2M(ap, p - (uchar*)ap, key->pakkey);
+	}
+
+	return 0;
 }
--- a/sys/src/libauthsrv/convTR2M.c
+++ b/sys/src/libauthsrv/convTR2M.c
@@ -2,12 +2,6 @@
 #include <libc.h>
 #include <authsrv.h>
 
-#define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		VLONG(f->x)
-#define	STRING(x,n)	memmove(p, f->x, n); p += n
-
 int
 convTR2M(Ticketreq *f, char *ap, int n)
 {
@@ -17,12 +11,13 @@
 		return 0;
 
 	p = (uchar*)ap;
-	CHAR(type);
-	STRING(authid, 28);	/* BUG */
-	STRING(authdom, DOMLEN);
-	STRING(chal, CHALLEN);
-	STRING(hostid, 28);	/* BUG */
-	STRING(uid, 28);	/* BUG */
+	*p++ = f->type;
+	memmove(p, f->authid, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->authdom, DOMLEN), p += DOMLEN;
+	memmove(p, f->chal, CHALLEN), p += CHALLEN;
+	memmove(p, f->hostid, ANAMELEN), p += ANAMELEN;
+	memmove(p, f->uid, ANAMELEN), p += ANAMELEN;
 	n = p - (uchar*)ap;
+
 	return n;
 }
--- /dev/null
+++ b/sys/src/libauthsrv/decaf.mp
@@ -1,0 +1,49 @@
+# negate r when n > (p-1)/2
+decaf_neg(p, n, r) {
+	mod(p) m = -r;
+	r = n > (p-1)>>1 ? m : r;
+}
+
+#	field F_p
+#	curve a*x**2+y**2==1+d*x**2*y**2
+#	input X,Y,Z,T (extended coordinates)
+decaf_encode(p,a,d, X,Y,Z,T, s) mod(p) {
+	r = misqrt((a-d)*(Z+Y)*(Z-Y), p);
+	u = (a-d)*r;
+	decaf_neg(p, -2*u*Z, r);
+	s = u*(r*(a*Z*X-d*Y*T)+Y)/a;
+	decaf_neg(p, s, s);
+}
+
+#	field F_p
+#	curve a*x**2+y**2==1+d*x**2*y**2
+#	input s
+#	output in extended coordinates
+decaf_decode(p,a,d, s, ok,X,Y,Z,T) {
+	if(s > (p-1)>>1){
+		ok = 0;
+	} else mod(p) {
+		ss = s^2;
+		Z = 1+a*ss;
+		u = Z^2 - 4*d*ss;
+		v = u*ss;
+		if(v == 0)
+			ok = 1;
+		else {
+			ok = msqrt(v, p);
+			if(ok != 0){
+				v = 1/ok;
+				ok = 1;
+			}
+		}
+		if(ok != 0) {
+			decaf_neg(p, u*v, v);
+			w = v * s * (2-Z);
+			if(s == 0)
+				w = w + 1;
+			X = 2*s;
+			Y = w * Z;
+			T = w * X;
+		}
+	}
+}
--- /dev/null
+++ b/sys/src/libauthsrv/ed448.mp
@@ -1,0 +1,11 @@
+# Edwards Curve Ed448-Goldilocks
+
+# x^2+y^2 = 1-39081x^2y^2
+# modulo p = 2^448 - 1^224 - 1
+ed448_curve(p,a,d,x,y) {
+	p = (1<<448) - (1<<224) - 1;
+	a = 1;
+	d = -39081;
+	x = 117812161263436946737282484343310064665180535357016373416879082147939404277809514858788439644911793978499419995990477371552926308078495;
+	y = 19;
+}
--- /dev/null
+++ b/sys/src/libauthsrv/edwards.mp
@@ -1,0 +1,40 @@
+# Edwards curve arithmetic
+edwards_add(p,a,d, X1,Y1,Z1,T1, X2,Y2,Z2,T2, X3,Y3,Z3,T3) mod(p) {
+	A = X1*X2;
+	B = Y1*Y2;
+	C = d*T1*T2;
+	D = Z1*Z2;
+	E = (X1+Y1)*(X2+Y2);
+	E = E - A - B;
+	F = D - C;
+	G = D + C;
+	H = B - a*A;
+	X3 = E*F;
+	Y3 = G*H;
+	Z3 = F*G;
+	T3 = E*H;
+}
+edwards_sel(s, X1,Y1,Z1,T1, X2,Y2,Z2,T2, X3,Y3,Z3,T3){
+	X3 = s != 0 ? X1 : X2;
+	Y3 = s != 0 ? Y1 : Y2;
+	Z3 = s != 0 ? Z1 : Z2;
+	T3 = s != 0 ? T1 : T2;
+}
+edwards_new(x,y,z,t, X,Y,Z,T) {
+	X = x;
+	Y = y;
+	Z = z;
+	T = t;
+}
+edwards_scale(p,a,d, s, X1,Y1,Z1,T1, X3,Y3,Z3,T3) {
+	X2,Y2,Z2,T2 = edwards_new(X1,Y1,Z1,T1);
+	X4,Y4,Z4,T4 = edwards_new( 0, 1, 1, 0);
+	X3,Y3,Z3,T3 = edwards_sel(s % 2, X2,Y2,Z2,T2, X4,Y4,Z4,T4);
+	k = s >> 1; j = p >> 1;
+	while(j != 0){
+		X2,Y2,Z2,T2 = edwards_add(p,a,d, X2,Y2,Z2,T2, X2,Y2,Z2,T2);
+		X4,Y4,Z4,T4 = edwards_add(p,a,d, X2,Y2,Z2,T2, X3,Y3,Z3,T3);
+		X3,Y3,Z3,T3 = edwards_sel(k % 2, X4,Y4,Z4,T4, X3,Y3,Z3,T3);
+		k = k >> 1; j = j >> 1;
+	}
+}
--- /dev/null
+++ b/sys/src/libauthsrv/elligator2.mp
@@ -1,0 +1,30 @@
+#elligator2:
+#	curve a*x^2+y^2==1+d*x^2*y^2
+#	input r0
+#	n is any non-square
+#
+elligator2(p,a,d, n, r0, X,Y,Z,T) mod(p) {
+	r = n*r0*r0;
+	D = (d*r+a-d)*(d*r-a*r-d);
+	N = (r+1)*(a-2*d);
+	ND = N*D;
+	if(ND == 0) {
+		c = 1;
+		e = 0;
+	} else {
+		e = msqrt(ND, p);
+		if(e != 0) {
+			c = 1;
+			e = 1/e;
+		} else {
+			c = -1;
+			e = n*r0*misqrt(n*ND, p);
+		}
+	}
+	s = c*N*e;
+	t = -c*N*(r-1)*((a-2*d)*e)^2-1;
+	X = 2*s*t;
+	Y = (1-a*s*s)*(1+a*s*s);
+	Z = (1+a*s*s)*t;
+	T = (2*s)*(1-a*s*s);
+}
--- /dev/null
+++ b/sys/src/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;
+}
--- a/sys/src/libauthsrv/mkfile
+++ b/sys/src/libauthsrv/mkfile
@@ -2,10 +2,12 @@
 
 LIB=/$objtype/lib/libauthsrv.a
 OFILES=\
+	_asgetpakkey.$O\
 	_asgetticket.$O\
 	_asgetresp.$O\
 	_asrequest.$O\
 	_asrdresp.$O\
+	authpak.$O\
 	authdial.$O\
 	convA2M.$O\
 	convM2A.$O\
@@ -15,17 +17,35 @@
 	convPR2M.$O\
 	convT2M.$O\
 	convTR2M.$O\
+	form1.$O\
 	nvcsum.$O\
 	passtokey.$O\
 	readnvram.$O\
 
 HFILES=\
-	/sys/include/authsrv.h\
+	/sys/include/authsrv.h
 
+MPCFILES=\
+	msqrt.mpc\
+	decaf.mpc\
+	edwards.mpc\
+	elligator2.mpc\
+	spake2ee.mpc\
+	ed448.mpc\
+
 UPDATE=\
 	mkfile\
 	$HFILES\
 	${OFILES:%.$O=%.c}\
+	${MPCFILES:.mpc=%.mp}\
 	${LIB:/$objtype/%=/386/%}\
 
+
+CLEANFILES=$MPCFILES
+
 </sys/src/cmd/mksyslib
+
+authpak.$O: $MPCFILES
+
+%.mpc:	%.mp
+	mpc $stem.mp > $target
--- /dev/null
+++ b/sys/src/libauthsrv/msqrt.mp
@@ -1,0 +1,100 @@
+# derived from: http://eli.thegreenplace.net/2009/03/07/computing-square-roots-in-python
+
+# Compute the Legendre symbol a|p using Euler's criterion.
+# p is a prime, a is relatively prime to p (if p divides a,
+# then a|p = 0)
+legendresymbol(a, p, r) {
+	pm1 = p-1;
+	mod(p) r = a^(pm1>>1);
+	if(r == pm1)
+		r = -1;
+}
+
+# Find a quadratic residue (mod p) of 'a'. p must be an
+# odd prime.
+#
+# Solve the congruence of the form:
+#	x^2 = a (mod p)
+# And returns x. Node that p - x is also a root.
+#
+# 0 is returned if no square root exists for these
+# a and p.
+#
+# The Tonelli-Shanks algorithm is used (except
+# for some simple cases in which the solution is known 
+# from an identity).
+msqrt(a, p, r) {
+	if(legendresymbol(a, p) != 1)
+		r = 0;
+	else if(a == 0)
+		r = 0;
+	else if(p == 2)
+		r = a;
+	else if(p%4 == 3){
+		e = p+1 >> 2;
+		mod(p) r = a^e;
+	} else {
+		# Partition p-1 to s * 2^e for an odd s (i.e.
+		# reduce all the powers of 2 from p-1)
+		s = p-1;
+		e = 0;
+		while(s%2 == 0){
+			s = s >> 1;
+			e = e + 1;
+		}
+
+		# Find some 'n' with a legendre symbol n|p = -1.
+		# Shouldn't take long.
+		n = 2;
+		while(legendresymbol(n, p) != -1)
+			n = n + 1;
+
+		# x is a guess of the square root that gets better
+		# with each iteration.
+		# b is the "fudge factor" - by now much we're off
+		# with the guess. The invariant x^2 == a*b (mod p)
+		# is maintained throughout the loop.
+		# g is used for successive powers of n to update
+		# both a and b
+		# e is the exponent - decreases with each update
+		mod(p){
+			x = a^(s+1 >> 1);
+			b = a^s;
+			g = n^s;
+		}
+		while(1==1){
+			t = b;
+			m = 0;
+			while(m < e){
+				if(t == 1)
+					break;
+				t = t*t % p;
+				m = m + 1;
+			}
+			if(m == 0){
+				r = x;
+				break;
+			}
+			t = 2^(e-m-1);
+			mod(p){
+				gs = g^t;
+				g = gs*gs;
+				x = x*gs;
+				b = b*g;
+			}
+			e = m;
+		}
+	}
+}
+
+# modular inverse square-root
+misqrt(a, p, r) {
+	if((p % 4) == 3){
+		e = ((p-3)>>2);
+		mod(p) r = a^e;
+	} else {
+		r = msqrt(a, p);
+		if(r != 0)
+			mod(p) r = 1/r;
+	}
+}
--- a/sys/src/libauthsrv/passtokey.c
+++ b/sys/src/libauthsrv/passtokey.c
@@ -3,8 +3,8 @@
 #include <authsrv.h>
 #include <libsec.h>
 
-static void
-passtodeskey(char *key, char *p)
+void
+passtodeskey(char key[DESKEYLEN], char *p)
 {
 	uchar buf[ANAMELEN], *t;
 	int i, n;
@@ -32,8 +32,8 @@
 	}
 }
 
-static void
-passtoaeskey(uchar *key, char *p)
+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);
@@ -40,9 +40,9 @@
 }
 
 void
-passtokey(Authkey *key, char *p)
+passtokey(Authkey *key, char *pw)
 {
 	memset(key, 0, sizeof(Authkey));
-	passtodeskey(key->des, p);
-	passtoaeskey(key->aes, p);
+	passtodeskey(key->des, pw);
+	passtoaeskey(key->aes, pw);
 }
--- /dev/null
+++ b/sys/src/libauthsrv/spake2ee.mp
@@ -1,0 +1,35 @@
+#
+# this implements a variant of SPAKE2 Elligator edition described in:
+# https://www.mail-archive.com/[email protected]/msg00412.html
+#
+
+# derive points PM or PN from a (password) hash
+spake2ee_h2P(p,a,d, h, PX,PY,PZ,PT){
+	# find a small non-square for elligator
+	n = 2;
+	while(legendresymbol(n, p) != -1)
+		n = n + 1;
+	PX,PY,PZ,PT = elligator2(p,a,d, n, h%p);
+}
+
+# Ya = xa*G+PM, Yb = xb*G+PN
+spake2ee_1(p,a,d, x, GX,GY, PX,PY,PZ,PT, y){
+	mod(p) X,Y,Z,T = edwards_scale(p,a,d, x, GX,GY,1,GX*GY);
+	X,Y,Z,T = edwards_add(p,a,d, X,Y,Z,T, PX,PY,PZ,PT);
+	y = decaf_encode(p,a,d, X,Y,Z,T);
+}
+
+# Z = xa*(Yb-PN)
+#   = xa*(xb*G+PN-PN)
+#   = xa*xb*G
+#   = xb*xa*G
+#   = xb*(xa*G+PM-PM)
+#   = xb*(Ya-PM)
+spake2ee_2(p,a,d, PX,PY,PZ,PT, x, y, ok, z){
+	ok, X,Y,Z,T = decaf_decode(p,a,d, y);
+	if(ok != 0){
+		mod(p) X,Y,Z,T = edwards_add(p,a,d, X,Y,Z,T, -PX,PY,PZ,-PT);
+		X,Y,Z,T = edwards_scale(p,a,d, x, X,Y,Z,T);
+		z = decaf_encode(p,a,d, X,Y,Z,T);
+	}
+}