Small tweaks
Lots of new code imported.
diff --git a/src/cmd/factotum/sshrsa.c b/src/cmd/factotum/sshrsa.c
new file mode 100644
index 0000000..7227c85
--- /dev/null
+++ b/src/cmd/factotum/sshrsa.c
@@ -0,0 +1,172 @@
+/*
+ * SSH RSA authentication.
+ * 
+ * Client protocol:
+ *	read public key
+ *		if you don't like it, read another, repeat
+ *	write challenge
+ *	read response
+ * all numbers are hexadecimal biginits parsable with strtomp.
+ */
+
+#include "dat.h"
+
+enum {
+	CHavePub,
+	CHaveResp,
+
+	Maxphase,
+};
+
+static char *phasenames[] = {
+[CHavePub]	"CHavePub",
+[CHaveResp]	"CHaveResp",
+};
+
+struct State
+{
+	RSApriv *priv;
+	mpint *resp;
+	int off;
+	Key *key;
+};
+
+static RSApriv*
+readrsapriv(Key *k)
+{
+	char *a;
+	RSApriv *priv;
+
+	priv = rsaprivalloc();
+
+	if((a=strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	if((a=strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil)
+		goto Error;
+	return priv;
+
+Error:
+	rsaprivfree(priv);
+	return nil;
+}
+
+static int
+sshrsainit(Proto*, Fsstate *fss)
+{
+	int iscli;
+	State *s;
+
+	if((iscli = isclient(strfindattr(fss->attr, "role"))) < 0)
+		return failure(fss, nil);
+	if(iscli==0)
+		return failure(fss, "sshrsa server unimplemented");
+
+	s = emalloc(sizeof *s);
+	fss->phasename = phasenames;
+	fss->maxphase = Maxphase;
+	fss->phase = CHavePub;
+	fss->ps = s;
+	return RpcOk;
+}
+
+static int
+sshrsaread(Fsstate *fss, void *va, uint *n)
+{
+	RSApriv *priv;
+	State *s;
+
+	s = fss->ps;
+	switch(fss->phase){
+	default:
+		return phaseerror(fss, "read");
+	case CHavePub:
+		if(s->key){
+			closekey(s->key);
+			s->key = nil;
+		}
+		if((s->key = findkey(fss, Kuser, nil, s->off, fss->attr, nil)) == nil)
+			return failure(fss, nil);
+		s->off++;
+		priv = s->key->priv;
+		*n = snprint(va, *n, "%B", priv->pub.n);
+		return RpcOk;
+	case CHaveResp:
+		*n = snprint(va, *n, "%B", s->resp);
+		fss->phase = Established;
+		return RpcOk;
+	}
+}
+
+static int
+sshrsawrite(Fsstate *fss, void *va, uint)
+{
+	mpint *m;
+	State *s;
+
+	s = fss->ps;
+	switch(fss->phase){
+	default:
+		return phaseerror(fss, "write");
+	case CHavePub:
+		if(s->key == nil)
+			return failure(fss, "no current key");
+		m = strtomp(va, nil, 16, nil);
+		m = rsadecrypt(s->key->priv, m, m);
+		s->resp = m;
+		fss->phase = CHaveResp;
+		return RpcOk;
+	}
+}
+
+static void
+sshrsaclose(Fsstate *fss)
+{
+	State *s;
+
+	s = fss->ps;
+	if(s->key)
+		closekey(s->key);
+	if(s->resp)
+		mpfree(s->resp);
+	free(s);
+}
+
+static int
+sshrsaaddkey(Key *k)
+{
+	fmtinstall('B', mpconv);
+
+	if((k->priv = readrsapriv(k)) == nil){
+		werrstr("malformed key data");
+		return -1;
+	}
+	return replacekey(k);
+}
+
+static void
+sshrsaclosekey(Key *k)
+{
+	rsaprivfree(k->priv);
+}
+
+Proto sshrsa = {
+.name=	"sshrsa",
+.init=		sshrsainit,
+.write=	sshrsawrite,
+.read=	sshrsaread,
+.close=	sshrsaclose,
+.addkey=	sshrsaaddkey,
+.closekey=	sshrsaclosekey,
+};