new
diff --git a/bin/.cvsignore b/bin/.cvsignore
index 5a9f6fd..fc6a4a4 100644
--- a/bin/.cvsignore
+++ b/bin/.cvsignore
@@ -16,6 +16,8 @@
 aescbc
 ajuke
 ascii
+asn12dsa
+asn12rsa
 astro
 auxclog
 auxstats
@@ -41,6 +43,8 @@
 dial
 dict
 diff
+dsa2ssh
+dsagen
 dump9660
 echo
 ed
@@ -92,6 +96,7 @@
 mtime
 namespace
 ndbipquery
+ndbmkdb
 ndbmkhash
 ndbmkhosts
 ndbquery
@@ -99,7 +104,10 @@
 news
 notes
 p
+passwd
 pbd
+pemdecode
+pemencode
 pic
 plot
 plumb
@@ -113,8 +121,15 @@
 ramfs
 rc
 read
+readcons
 rio
 rm
+rsa2csr
+rsa2pub
+rsa2ssh
+rsa2x509
+rsafill
+rsagen
 sam
 samterm
 scat
@@ -128,10 +143,12 @@
 sort
 split
 sprog
+ssh-agent
 stats
 strings
 sum
 tail
+tar
 tbl
 tcolors
 tcs
diff --git a/src/cmd/auth/factotum/apop.c b/src/cmd/auth/factotum/apop.c
index 8e340d4..5703571 100644
--- a/src/cmd/auth/factotum/apop.c
+++ b/src/cmd/auth/factotum/apop.c
@@ -6,7 +6,7 @@
  * Protocol:
  *
  *	S -> C:	random@domain
- *	C -> S:	hex-response
+ *	C -> S:	user hex-response
  *	S -> C:	ok
  *
  * Note that this is the protocol between factotum and the local
@@ -15,7 +15,7 @@
  * programs.
  *
  * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad.
- * The protocol goes back to "C -> S: user".
+ * The protocol goes back to "C -> S: user hex-response".
  */
 
 #include "std.h"
@@ -240,7 +240,7 @@
 	keyclose(s.k);
 	free(user);
 	free(resp);
-//	xioclose(s.asfd);
+	xioclose(s.asfd);
 	return ret;
 }
 
@@ -336,15 +336,18 @@
 };
 
 Proto apop = {
-.name=		"apop",
-.roles=		apoproles,
-.checkkey=	apopcheck,
-.keyprompt=	"user? !password?",
+	"apop",
+	apoproles,
+	"user? !password?",
+	apopcheck,
+	nil
 };
 
 Proto cram = {
-.name=		"cram",
-.roles=		apoproles,
-.checkkey=	apopcheck,
-.keyprompt=	"user? !password?",
+	"cram",
+	apoproles,
+	"user? !password?",
+	apopcheck,
+	nil
 };
+
diff --git a/src/cmd/auth/factotum/httpdigest.c b/src/cmd/auth/factotum/httpdigest.c
new file mode 100644
index 0000000..70815a5
--- /dev/null
+++ b/src/cmd/auth/factotum/httpdigest.c
@@ -0,0 +1,119 @@
+/*
+ * HTTPDIGEST - MD5 challenge/response authentication (RFC 2617)
+ *
+ * Client protocol:
+ *	write challenge: nonce method uri 
+ *	read response: 2*MD5dlen hex digits
+ *
+ * Server protocol:
+ *	unimplemented
+ */
+#include "std.h"
+#include "dat.h"
+
+static void
+digest(char *user, char *realm, char *passwd,
+	char *nonce, char *method, char *uri,
+	char *dig);
+
+static int
+hdclient(Conv *c)
+{
+	char *realm, *passwd, *user, *f[4], *s, resp[MD5dlen*2+1];
+	int ret;
+	Key *k;
+	
+	ret = -1;
+	s = nil;
+	
+	c->state = "keylookup";
+	k = keyfetch(c, "%A", c->attr);
+	if(k == nil)
+		goto out;
+
+	user = strfindattr(k->attr, "user");
+	realm = strfindattr(k->attr, "realm");
+	passwd = strfindattr(k->attr, "!password");
+
+	if(convreadm(c, &s) < 0)
+		goto out;
+	if(tokenize(s, f, 4) != 3){
+		werrstr("bad challenge -- want nonce method uri");
+		goto out;
+	}
+
+	digest(user, realm, passwd, f[0], f[1], f[2], resp);
+	convwrite(c, resp, strlen(resp));
+	ret = 0;
+	
+out:
+	free(s);
+	keyclose(k);
+	return ret;
+}
+
+static void
+strtolower(char *s)
+{
+	while(*s){
+		*s = tolower(*s);
+		s++;
+	}
+}
+
+static void
+digest(char *user, char *realm, char *passwd,
+	char *nonce, char *method, char *uri,
+	char *dig)
+{
+	uchar b[MD5dlen];
+	char ha1[MD5dlen*2+1];
+	char ha2[MD5dlen*2+1];
+	DigestState *s;
+
+	/*
+	 *  H(A1) = MD5(uid + ":" + realm ":" + passwd)
+	 */
+	s = md5((uchar*)user, strlen(user), nil, nil);
+	md5((uchar*)":", 1, nil, s);
+	md5((uchar*)realm, strlen(realm), nil, s);
+	md5((uchar*)":", 1, nil, s);
+	md5((uchar*)passwd, strlen(passwd), b, s);
+	enc16(ha1, sizeof(ha1), b, MD5dlen);
+	strtolower(ha1);
+
+	/*
+	 *  H(A2) = MD5(method + ":" + uri)
+	 */
+	s = md5((uchar*)method, strlen(method), nil, nil);
+	md5((uchar*)":", 1, nil, s);
+	md5((uchar*)uri, strlen(uri), b, s);
+	enc16(ha2, sizeof(ha2), b, MD5dlen);
+	strtolower(ha2);
+
+	/*
+	 *  digest = MD5(H(A1) + ":" + nonce + ":" + H(A2))
+	 */
+	s = md5((uchar*)ha1, MD5dlen*2, nil, nil);
+	md5((uchar*)":", 1, nil, s);
+	md5((uchar*)nonce, strlen(nonce), nil, s);
+	md5((uchar*)":", 1, nil, s);
+	md5((uchar*)ha2, MD5dlen*2, b, s);
+	enc16(dig, MD5dlen*2+1, b, MD5dlen);
+	strtolower(dig);
+}
+
+static Role hdroles[] = 
+{
+	"client",	hdclient,
+	0
+};
+
+Proto httpdigest =
+{
+	"httpdigest",
+	hdroles,
+	"user? realm? !password?",
+	nil,
+	nil
+};
diff --git a/src/cmd/auth/factotum/main.c b/src/cmd/auth/factotum/main.c
index 1a8c4ff..f1e6477 100644
--- a/src/cmd/auth/factotum/main.c
+++ b/src/cmd/auth/factotum/main.c
@@ -48,6 +48,9 @@
 	case 'a':
 		authaddr = EARGF(usage());
 		break;
+	case 'd':
+		debug = 1;
+		break;
 	case 'g':
 		usage();
 	case 'm':
diff --git a/src/cmd/auth/factotum/mkfile b/src/cmd/auth/factotum/mkfile
index d6d58af..e5475f8 100644
--- a/src/cmd/auth/factotum/mkfile
+++ b/src/cmd/auth/factotum/mkfile
@@ -5,10 +5,12 @@
 	apop.$O\
 	chap.$O\
 	dsa.$O\
+	httpdigest.$O\
 	p9any.$O\
 	p9sk1.$O\
 	pass.$O\
 	rsa.$O\
+	wep.$O\
 
 OFILES=\
 	$PROTO\
@@ -24,9 +26,9 @@
 	pkcs1.$O\
 	proto.$O\
 	rpc.$O\
+	secstore.$O\
 	util.$O\
 	xio.$O\
-	secstore.$O\
 
 HFILES=dat.h
 
diff --git a/src/cmd/auth/factotum/p9cr.c b/src/cmd/auth/factotum/p9cr.c
index 7f53e44..abbad4c 100644
--- a/src/cmd/auth/factotum/p9cr.c
+++ b/src/cmd/auth/factotum/p9cr.c
@@ -18,16 +18,6 @@
 #include "dat.h"
 
 static int
-p9crcheck(Key *k)
-{
-	if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
-		werrstr("need user and !password attributes");
-		return -1;
-	}
-	return 0;
-}
-
-static int
 p9crclient(Conv *c)
 {
 	char *chal, *pw, *res, *user;
@@ -113,10 +103,11 @@
 static int
 p9crserver(Conv *c)
 {
-	char chal[APOPCHALLEN], *user, *resp;
-	ServerState s;
-	int astype, ret;
+	char chal[MAXCHAL], *user, *resp;
+	int astype, challen, asfd, fd, ret;
 	Attr *a;
+	Key *k;
+	char *hostid, *dom;
 
 	ret = -1;
 	user = nil;
@@ -124,28 +115,32 @@
 	memset(&s, 0, sizeof s);
 	s.asfd = -1;
 
-	if(c->proto == &apop)
-		astype = AuthApop;
-	else if(c->proto == &cram)
-		astype = AuthCram;
-	else{
+	if(c->proto == &p9cr){
+		astype = AuthChal;
+		challen = NETCHLEN;
+	}else if(c->proto == &vnc){
+		astype = AuthVnc;
+		challen = MAXCHAL;
+	}else{
 		werrstr("bad proto");
 		goto out;
 	}
 
 	c->state = "find key";
-	if((s.k = plan9authkey(c->attr)) == nil)
+	if((k = plan9authkey(c->attr)) == nil)
 		goto out;
 
-	a = copyattr(s.k->attr);
+/*
+	a = copyattr(k->attr);
 	a = delattr(a, "proto");
 	c->attr = addattrs(c->attr, a);
 	freeattr(a);
+*/
 
 	c->state = "authdial";
-	s.hostid = strfindattr(s.k->attr, "user");
-	s.dom = strfindattr(s.k->attr, "dom");
-	if((s.asfd = xioauthdial(nil, s.dom)) < 0){
+	hostid = strfindattr(s.k->attr, "user");
+	dom = strfindattr(s.k->attr, "dom");
+	if((asfd = xioauthdial(nil, s.dom)) < 0){
 		werrstr("authdial %s: %r", s.dom);
 		goto out;
 	}
@@ -196,7 +191,7 @@
 	keyclose(s.k);
 	free(user);
 	free(resp);
-//	xioclose(s.asfd);
+	xioclose(s.asfd);
 	return ret;
 }
 
diff --git a/src/cmd/auth/factotum/proto.c b/src/cmd/auth/factotum/proto.c
index 5a65e5c..97da15e 100644
--- a/src/cmd/auth/factotum/proto.c
+++ b/src/cmd/auth/factotum/proto.c
@@ -5,6 +5,7 @@
 extern Proto	chap;		/* chap.c */
 extern Proto	cram;		/* apop.c */
 extern Proto	dsa;			/* dsa.c */
+extern Proto	httpdigest;	/* httpdigest.c */
 extern Proto	mschap;		/* chap.c */
 extern Proto	p9any;		/* p9any.c */
 extern Proto	p9sk1;		/* p9sk1.c */
@@ -16,6 +17,7 @@
 	&apop,
 	&cram,
 	&dsa,
+	&httpdigest,
 	&p9any,
 	&p9sk1,
 	&p9sk2,
diff --git a/src/cmd/auth/factotum/secstore.c b/src/cmd/auth/factotum/secstore.c
index d82d786..0156e17 100644
--- a/src/cmd/auth/factotum/secstore.c
+++ b/src/cmd/auth/factotum/secstore.c
@@ -45,8 +45,11 @@
 	hnputs(buf, 0x8000+n-2);
 
 	fd = secdial();
-	if(fd < 0)
+	if(fd < 0){
+		if(debug)
+			fprint(2, "secdial: %r\n");
 		return 0;
+	}
 	if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2){
 		close(fd);
 		return 0;
diff --git a/src/cmd/auth/factotum/wep.c b/src/cmd/auth/factotum/wep.c
new file mode 100644
index 0000000..1d516db
--- /dev/null
+++ b/src/cmd/auth/factotum/wep.c
@@ -0,0 +1,83 @@
+/*
+ * Copy WEP key to ethernet device.
+ */
+
+#include "std.h"
+#include "dat.h"
+
+static int
+wepclient(Conv *c)
+{
+	char *dev, buf[128], *p, *kp;
+	Key *k;
+	int ret, fd, cfd;
+	
+	fd = cfd = -1;
+	ret = -1;
+	dev = nil;
+	
+	if((k = keylookup("%A !key1?", c->attr)) == nil
+	&& (k = keylookup("%A !key2?", c->attr)) == nil
+	&& (k = keylookup("%A !key3?", c->attr)) == nil){
+		werrstr("cannot find wep keys");
+		goto out;
+	}
+	if(convreadm(c, &dev) < 0)
+		return -1;
+	if(dev[0] != '#' || dev[1] != 'l'){
+		werrstr("not an ethernet device: %s", dev);
+		goto out;
+	}
+	snprint(buf, sizeof buf, "%s!0", dev);
+	if((fd = dial(buf, 0, 0, &cfd)) < 0)
+		goto out;
+	if(!(p = strfindattr(k->privattr, kp="!key1"))
+	&& !(p = strfindattr(k->privattr, kp="key2"))
+	&& !(p = strfindattr(k->privattr, kp="key3"))){
+		werrstr("lost key");
+		goto out;
+	}
+	if(fprint(cfd, "%s %q", kp+1, p) < 0)
+		goto out;
+	if((p = strfindattr(k->attr, "essid")) != nil
+	&& fprint(cfd, "essid %q", p) < 0)
+		goto out;
+	if(fprint(cfd, "crypt on") < 0)
+		goto out;
+	ret = 0;
+
+out:
+	free(dev);
+	if(cfd >= 0)
+		close(cfd);
+	if(fd >= 0)
+		close(fd);
+	keyclose(k);
+	return ret;
+}
+
+static int
+wepcheck(Key *k)
+{
+	if(strfindattr(k->privattr, "!key1") == nil
+	&& strfindattr(k->privattr, "!key2") == nil
+	&& strfindattr(k->privattr, "!key3") == nil){
+		werrstr("need !key1, !key2, or !key3 attribute");
+		return -1;
+	}
+	return 0;
+}
+
+static Role weproles[] = {
+	"client",	wepclient,
+	0
+};
+
+Proto wep = 
+{
+	"wep",
+	weproles,
+	nil,
+	wepcheck,
+	nil
+};