summaryrefslogtreecommitdiff
path: root/appl/cmd/auth/factotum/proto
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/auth/factotum/proto')
-rw-r--r--appl/cmd/auth/factotum/proto/infauth.b362
-rw-r--r--appl/cmd/auth/factotum/proto/keyreps.b173
-rw-r--r--appl/cmd/auth/factotum/proto/keyreps.m23
-rw-r--r--appl/cmd/auth/factotum/proto/mkfile22
-rw-r--r--appl/cmd/auth/factotum/proto/p9any.b232
-rw-r--r--appl/cmd/auth/factotum/proto/pass.b29
6 files changed, 841 insertions, 0 deletions
diff --git a/appl/cmd/auth/factotum/proto/infauth.b b/appl/cmd/auth/factotum/proto/infauth.b
new file mode 100644
index 00000000..244979bc
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/infauth.b
@@ -0,0 +1,362 @@
+implement Authproto;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "keyring.m";
+ keyring: Keyring;
+ IPint: import keyring;
+ SK, PK, Certificate, DigestState: import Keyring;
+include "security.m";
+include "bufio.m";
+include "sexprs.m";
+ sexprs: Sexprs;
+ Sexp: import sexprs;
+include "spki.m";
+ spki: SPKI;
+include "daytime.m";
+ daytime: Daytime;
+include "keyreps.m";
+ keyreps: Keyreps;
+ Keyrep: import keyreps;
+include "../authio.m";
+ authio: Authio;
+ Aattr, Aval, Aquery: import Authio;
+ Attr, IO, Key, Authinfo: import authio;
+
+# at end of authentication, sign a hash of the authenticated username and
+# a secret known only to factotum. that certificate can act as
+# a later proof that this factotum has authenticated that user,
+# and hence factotum will disclose certificates that allow disclosure
+# only to that username.
+
+Debug: con 0;
+
+Maxmsg: con 4000;
+
+Error0, Error1: exception(string);
+
+init(f: Authio): string
+{
+ authio = f;
+ sys = load Sys Sys->PATH;
+ spki = load SPKI SPKI->PATH;
+ spki->init();
+ sexprs = load Sexprs Sexprs->PATH;
+ sexprs->init();
+ keyring = load Keyring Keyring->PATH;
+ daytime = load Daytime Daytime->PATH;
+ keyreps = load Keyreps Keyreps->PATH;
+ keyreps->init();
+ return nil;
+}
+
+interaction(attrs: list of ref Attr, io: ref IO): string
+{
+ ai: ref Authinfo;
+ (key, err) := io.findkey(attrs, "proto=infauth");
+ if(key == nil)
+ return err;
+ info: ref Keyring->Authinfo;
+ (info, err) = keytoauthinfo(key);
+ if(info == nil)
+ return err;
+ anysigner := int authio->lookattrval(key.attrs, "anysigner");
+ rattrs: list of ref Sexp;
+ {
+ # send auth protocol version number
+ sendmsg(io, array of byte "1");
+
+ # get auth protocol version number
+ if(int string getmsg(io) != 1)
+ raise Error0("incompatible authentication protocol");
+
+ # generate alpha**r0
+ p := info.p;
+ low := p.shr(p.bits()/4);
+ r0 := rand(low, p, Random->NotQuiteRandom);
+ αr0 := info.alpha.expmod(r0, p);
+ # trim(αr0); the IPint library should do this for us, i think.
+
+ # send alpha**r0 mod p, mycert, and mypk
+ sendmsg(io, array of byte αr0.iptob64());
+ sendmsg(io, array of byte keyring->certtostr(info.cert));
+ sendmsg(io, array of byte keyring->pktostr(info.mypk));
+
+ # get alpha**r1 mod p, hiscert, hispk
+ αr1 := IPint.b64toip(string getmsg(io));
+
+ # trying a fast one
+ if(p.cmp(αr1) <= 0)
+ raise Error0("implausible parameter value");
+
+ # if alpha**r1 == alpha**r0, someone may be trying a replay
+ if(αr0.eq(αr1))
+ raise Error0("possible replay attack");
+
+ hiscert := keyring->strtocert(string getmsg(io));
+ if(hiscert == nil && !anysigner)
+ raise Error0(sys->sprint("bad certificate: %r"));
+
+ buf := getmsg(io);
+ hispk := keyring->strtopk(string buf);
+ if(!anysigner){
+ # verify their public key
+ if(verify(info.spk, hiscert, buf) == 0)
+ raise Error0("pk doesn't match certificate"); # likely the signers don't match.
+
+ # check expiration date - in seconds of epoch
+ if(hiscert.exp != 0 && hiscert.exp <= now())
+ raise Error0("certificate expired");
+ }
+ buf = nil;
+
+ # sign alpha**r0 and alpha**r1 and send
+ αcert := sign(info.mysk, "sha", 0, array of byte (αr0.iptob64() + αr1.iptob64()));
+ sendmsg(io, array of byte keyring->certtostr(αcert));
+
+ # get signature of alpha**r1 and alpha**r0 and verify
+ αcert = keyring->strtocert(string getmsg(io));
+ if(αcert == nil)
+ raise Error0("alpha**r1 doesn't match certificate");
+
+ if(verify(hispk, αcert, array of byte (αr1.iptob64() + αr0.iptob64())) == 0)
+ raise Error0(sys->sprint("bad certificate: %r"));
+
+ ai = ref Authinfo;
+ # we are now authenticated and have a common secret, alpha**(r0*r1)
+ if(!anysigner)
+ rattrs = sl(ss("signer") :: principal(info.spk) :: nil) :: rattrs;
+ rattrs = sl(ss("remote-pk") :: principal(hispk) :: nil) :: rattrs;
+ rattrs = sl(ss("local-pk") :: principal(info.mypk) :: nil) :: rattrs;
+ rattrs = sl(ss("secret") :: sb(αr1.expmod(r0, p).iptobytes()) :: nil) :: rattrs;
+ ai.suid = hispk.owner;
+ ai.cuid = info.mypk.owner;
+ sendmsg(io, array of byte "OK");
+ }exception e{
+ Error0 =>
+ err = e;
+ senderr(io, e);
+ break;
+ Error1 =>
+ senderr(io, "missing your authentication data");
+ x: string = e;
+ return "remote: "+x;
+ }
+
+ {
+ while(string getmsg(io) != "OK")
+ ;
+ }exception e{
+ Error0 =>
+ return e;
+ Error1 =>
+ x: string = e;
+ return "remote: "+x;
+ }
+ if(err != nil)
+ return err;
+
+ return negotiatecrypto(io, key, ai, rattrs);
+}
+
+negotiatecrypto(io: ref IO, key: ref Key, ai: ref Authinfo, attrs: list of ref Sexp): string
+{
+ role := authio->lookattrval(key.attrs, "role");
+ alg: string;
+ {
+ if(role == "client"){
+ alg = authio->lookattrval(key.attrs, "alg");
+ if(alg == nil)
+ alg = "md5/rc4_256";
+ sendmsg(io, array of byte alg);
+ }else if(role == "server"){
+ alg = string getmsg(io);
+ if(!algcompatible(alg, sys->tokenize(authio->lookattrval(key.attrs, "algs"), " ").t1))
+ raise Error0("unsupported client algorithm");
+ }
+ }exception e{
+ Error0 or
+ Error1 =>
+ return e;
+ }
+
+ if(alg != nil)
+ attrs = sl(ss("alg") :: ss(alg) :: nil) :: attrs;
+ ai.secret = sl(attrs).pack();
+
+ io.done(ai);
+ return nil;
+}
+
+algcompatible(nil: string, nil: list of string): int
+{
+ return 1; # XXX
+}
+
+principal(pk: ref Keyring->PK): ref Sexp
+{
+ return spki->(Keyrep.pk(pk).mkkey()).sexp();
+}
+
+ipint(i: int): ref IPint
+{
+ return IPint.inttoip(i);
+}
+
+rand(p, q: ref IPint, nil: int): ref IPint
+{
+ if(p.cmp(q) > 0)
+ (p, q) = (q, p);
+ diff := q.sub(p);
+ q = nil;
+ if(diff.cmp(ipint(2)) < 0){
+ sys->print("rand range must be at least 2");
+ return IPint.inttoip(0);
+ }
+ l := diff.bits();
+ T := ipint(1).shl(l);
+ l = ((l + 7) / 8) * 8;
+ slop := T.div(diff).t1;
+ r: ref IPint;
+ do{
+ r = IPint.random(0, l);
+ }while(r.cmp(slop) < 0);
+ r = r.div(diff).t1.add(p);
+ return r;
+}
+
+now(): int
+{
+ return daytime->now();
+}
+
+Hashfn: type ref fn(a: array of byte, alen: int, digest: array of byte, state: ref DigestState): ref DigestState;
+
+hashalg(ha: string): Hashfn
+{
+ case ha {
+ "sha" or
+ "sha1" =>
+ return keyring->sha1;
+ "md4" =>
+ return keyring->md4;
+ "md5" =>
+ return keyring->md5;
+ }
+ return nil;
+}
+
+sign(sk: ref SK, ha: string, exp: int, buf: array of byte): ref Certificate
+{
+ state := hashalg(ha)(buf, len buf, nil, nil);
+ return keyring->sign(sk, exp, state, ha);
+}
+
+verify(pk: ref PK, cert: ref Certificate, buf: array of byte): int
+{
+ state := hashalg(cert.ha)(buf, len buf, nil, nil);
+ return keyring->verify(pk, cert, state);
+}
+
+getmsg(io: ref IO): array of byte raises (Error0, Error1)
+{
+ while((buf := io.read()) == nil || (n := len buf) < 5)
+ io.toosmall(5);
+ if(len buf != 5)
+ raise Error0("io error: (impossible?) msg length " + string n);
+ h := string buf;
+ if(h[0] == '!')
+ m := int h[1:];
+ else
+ m = int h;
+ while((buf = io.read()) == nil || (n = len buf) < m)
+ io.toosmall(m);
+ if(len buf != m)
+ raise Error0("io error: (impossible?) msg length " + string m);
+ if(h[0] == '!'){
+sys->print("got remote error: %s, len %d\n", string buf, len string buf);
+ raise Error1(string buf);
+ }
+ return buf;
+}
+
+sendmsg(io: ref IO, buf: array of byte)
+{
+ h := sys->aprint("%4.4d\n", len buf);
+ io.write(h, len h);
+ io.write(buf, len buf);
+}
+
+senderr(io: ref IO, e: string)
+{
+ buf := array of byte e;
+ h := sys->aprint("!%3.3d\n", len buf);
+ io.write(h, len h);
+ io.write(buf, len buf);
+}
+
+keytoauthinfo(key:ref Key): (ref Keyring->Authinfo, string)
+{
+ if((s := authio->lookattrval(key.secrets, "!authinfo")) == nil){
+ # XXX could look up authinfo by hash at this point
+ return (nil, "no authinfo attribute");
+ }
+
+ return strtoauthinfo(s);
+}
+
+strtoauthinfo(s: string): (ref Keyring->Authinfo, string)
+{
+ (se, err, nil) := Sexp.parse(s);
+ if(se == nil)
+ return (nil, err);
+ els := se.els();
+ if(len els != 5)
+ return (nil, "bad authinfo contents");
+ ai := ref Keyring->Authinfo;
+ if((ai.spk = keyring->strtopk((hd els).astext())) == nil)
+ return (nil, "bad signer public key");
+ els = tl els;
+ if((ai.cert = keyring->strtocert((hd els).astext())) == nil)
+ return (nil, "bad certificate");
+ els = tl els;
+ if((ai.mysk = keyring->strtosk((hd els).astext())) == nil)
+ return (nil, "bad secret/public key");
+ if((ai.mypk = keyring->sktopk(ai.mysk)) == nil)
+ return (nil, "cannot make pk from sk");
+ els = tl els;
+ if((ai.alpha = IPint.bytestoip((hd els).asdata())) == nil)
+ return (nil, "bad value for alpha");
+ els = tl els;
+ if((ai.p = IPint.bytestoip((hd els).asdata())) == nil)
+ return (nil, "bad value for p");
+ return (ai, nil);
+}
+
+authinfotostr(ai: ref Keyring->Authinfo): string
+{
+ return (ref Sexp.List(
+ ss(keyring->pktostr(ai.spk)) ::
+ ss(keyring->certtostr(ai.cert)) ::
+ ss(keyring->sktostr(ai.mysk)) ::
+ sb(ai.alpha.iptobytes()) ::
+ sb(ai.p.iptobytes()) ::
+ nil
+ )).b64text();
+}
+
+ss(s: string): ref Sexp.String
+{
+ return ref Sexp.String(s, nil);
+}
+
+sb(d: array of byte): ref Sexp.Binary
+{
+ return ref Sexp.Binary(d, nil);
+}
+
+sl(l: list of ref Sexp): ref Sexp
+{
+ return ref Sexp.List(l);
+}
diff --git a/appl/cmd/auth/factotum/proto/keyreps.b b/appl/cmd/auth/factotum/proto/keyreps.b
new file mode 100644
index 00000000..5fdac2c0
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/keyreps.b
@@ -0,0 +1,173 @@
+implement Keyreps;
+include "sys.m";
+ sys: Sys;
+include "keyring.m";
+ kr: Keyring;
+ IPint: import kr;
+include "sexprs.m";
+include "spki.m";
+include "encoding.m";
+ base64: Encoding;
+include "keyreps.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ base64 = load Encoding Encoding->BASE64PATH;
+}
+
+keyextract(flds: list of string, names: list of (string, int)): list of (string, ref IPint)
+{
+ a := array[len flds] of ref IPint;
+ for(i := 0; i < len a; i++){
+ a[i] = IPint.b64toip(hd flds);
+ flds = tl flds;
+ }
+ rl: list of (string, ref IPint);
+ for(; names != nil; names = tl names){
+ (n, p) := hd names;
+ if(p < len a)
+ rl = (n, a[p]) :: rl;
+ }
+ return revt(rl);
+}
+
+Keyrep.pk(pk: ref Keyring->PK): ref Keyrep.PK
+{
+ s := kr->pktostr(pk);
+ (nf, flds) := sys->tokenize(s, "\n");
+ if((nf -= 2) < 0)
+ return nil;
+ case hd flds {
+ "rsa" =>
+ return ref Keyrep.PK(hd flds, hd tl flds,
+ keyextract(tl tl flds, list of {("e",1), ("n",0)}));
+ "elgamal" or "dsa" =>
+ return ref Keyrep.PK(hd flds, hd tl flds,
+ keyextract(tl tl flds, list of {("p",0), ("alpha",1), ("key",2)}));
+ * =>
+ return nil;
+ }
+}
+
+Keyrep.sk(pk: ref Keyring->SK): ref Keyrep.SK
+{
+ s := kr->pktostr(pk);
+ (nf, flds) := sys->tokenize(s, "\n");
+ if((nf -= 2) < 0)
+ return nil;
+ case hd flds {
+ "rsa" =>
+ return ref Keyrep.SK(hd flds, hd tl flds,
+ keyextract(tl tl flds,list of {("e",1), ("n",0), ("!dk",2), ("!p",3), ("!q",4), ("!kp",5), ("!kq",6), ("!c2",7)}));
+ "elgamal" or "dsa" =>
+ return ref Keyrep.SK(hd flds, hd tl flds,
+ keyextract(tl tl flds, list of {("p",0), ("alpha",1), ("key",2), ("!secret",3)}));
+ * =>
+ return nil;
+ }
+}
+
+Keyrep.get(k: self ref Keyrep, n: string): ref IPint
+{
+ for(el := k.els; el != nil; el = tl el)
+ if((hd el).t0 == n)
+ return (hd el).t1;
+ return nil;
+}
+
+Keyrep.getb(k: self ref Keyrep, n: string): array of byte
+{
+ v := k.get(n);
+ if(v == nil)
+ return nil;
+ return pre0(v.iptobebytes());
+}
+
+pre0(a: array of byte): array of byte
+{
+ for(i:=0; i<len a-1; i++)
+ if(a[i] != a[i+1] && (a[i] != byte 0 || (int a[i+1] & 16r80) != 0))
+ break;
+ if(i > 0)
+ a = a[i:];
+ if(len a < 1 || (int a[0] & 16r80) == 0)
+ return a;
+ b := array[len a + 1] of byte;
+ b[0] = byte 0;
+ b[1:] = a;
+ return b;
+}
+
+Keyrep.mkpk(k: self ref Keyrep): (ref Keyring->PK, int)
+{
+ case k.alg {
+ "rsa" =>
+ e := k.get("e");
+ n := k.get("n");
+ return (kr->strtopk(sys->sprint("rsa\n%s\n%s\n%s\n", k.owner, n.iptob64(), e.iptob64())), n.bits());
+ * =>
+ raise "Keyrep: unknown algorithm" + k.alg;
+ }
+}
+
+Keyrep.mksk(k: self ref Keyrep): ref Keyring->SK
+{
+ case k.alg {
+ "rsa" =>
+ e := k.get("e");
+ n := k.get("n");
+ dk := k.get("!dk");
+ p := k.get("!p");
+ q := k.get("!q");
+ kp := k.get("!kp");
+ kq := k.get("!kq");
+ c12 := k.get("!c2");
+ return kr->strtosk(sys->sprint("rsa\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ k.owner, n.iptob64(), e.iptob64(), dk.iptob64(), p.iptob64(), q.iptob64(),
+ kp.iptob64(), kq.iptob64(), c12.iptob64()));
+ * =>
+ raise "Keyrep: unknown algorithm";
+ }
+}
+
+Keyrep.eq(k1: self ref Keyrep, k2: ref Keyrep): int
+{
+ # n⁲ but n is small
+ for(l1 := k1.els; l1 != nil; l1 = tl l1){
+ (n, v1) := hd l1;
+ v2 := k2.get(n);
+ if(v2 == nil || !v1.eq(v2))
+ return 0;
+ }
+ for(l2 := k2.els; l2 != nil; l2 = tl l2)
+ if(k1.get((hd l2).t0) == nil)
+ return 0;
+ return 1;
+}
+
+Keyrep.mkkey(kr: self ref Keyrep): ref SPKI->Key
+{
+ k := ref SPKI->Key;
+ (k.pk, k.nbits) = kr.mkpk();
+ k.sk = kr.mksk();
+ return k;
+}
+
+sig2icert(sig: ref SPKI->Signature, signer: string, exp: int): ref Keyring->Certificate
+{
+ if(sig.sig == nil)
+ return nil;
+ s := sys->sprint("%s\n%s\n%s\n%d\n%s\n", "rsa", sig.hash.alg, signer, exp, base64->enc((hd sig.sig).t1));
+#sys->print("alg %s *** %s\n", sig.sa, base64->enc((hd sig.sig).t1));
+ return kr->strtocert(s);
+}
+
+revt[S,T](l: list of (S,T)): list of (S,T)
+{
+ rl: list of (S,T);
+ for(; l != nil; l = tl l)
+ rl = hd l :: rl;
+ return rl;
+}
diff --git a/appl/cmd/auth/factotum/proto/keyreps.m b/appl/cmd/auth/factotum/proto/keyreps.m
new file mode 100644
index 00000000..ddfd7f0d
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/keyreps.m
@@ -0,0 +1,23 @@
+Keyreps: module
+{
+ PATH: con "/dis/lib/spki/keyreps.dis";
+ init: fn();
+ Keyrep: adt {
+ alg: string;
+ owner: string;
+ els: list of (string, ref Keyring->IPint);
+ pick{ # keeps a type distance between public and private keys
+ PK =>
+ SK =>
+ }
+
+ pk: fn(pk: ref Keyring->PK): ref Keyrep.PK;
+ sk: fn(sk: ref Keyring->SK): ref Keyrep.SK;
+ mkpk: fn(k: self ref Keyrep): (ref Keyring->PK, int);
+ mksk: fn(k: self ref Keyrep): ref Keyring->SK;
+ get: fn(k: self ref Keyrep, n: string): ref Keyring->IPint;
+ getb: fn(k: self ref Keyrep, n: string): array of byte;
+ eq: fn(k1: self ref Keyrep, k2: ref Keyrep): int;
+ mkkey: fn(k: self ref Keyrep): ref SPKI->Key;
+ };
+};
diff --git a/appl/cmd/auth/factotum/proto/mkfile b/appl/cmd/auth/factotum/proto/mkfile
new file mode 100644
index 00000000..efdd73da
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/mkfile
@@ -0,0 +1,22 @@
+<../../../../../mkconfig
+
+TARG=\
+ p9any.dis\
+ pass.dis\
+
+SYSMODULES=\
+ factotum.m\
+ keyring.m\
+ security.m\
+ rand.m\
+ sys.m\
+ draw.m\
+ bufio.m\
+ string.m\
+
+MODULES=\
+ ../authio.m\
+
+DISBIN=$ROOT/dis/auth/proto
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/cmd/auth/factotum/proto/p9any.b b/appl/cmd/auth/factotum/proto/p9any.b
new file mode 100644
index 00000000..1668a701
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/p9any.b
@@ -0,0 +1,232 @@
+implement Authproto;
+
+# currently includes p9sk1
+
+include "sys.m";
+ sys: Sys;
+ Rread, Rwrite: import Sys;
+
+include "draw.m";
+
+include "keyring.m";
+ kr: Keyring;
+
+include "auth9.m";
+ auth9: Auth9;
+ ANAMELEN, AERRLEN, DOMLEN, DESKEYLEN, CHALLEN, SECRETLEN: import Auth9;
+ TICKREQLEN, TICKETLEN, AUTHENTLEN: import Auth9;
+ Ticketreq, Ticket, Authenticator: import auth9;
+
+include "../authio.m";
+ authio: Authio;
+ Aattr, Aval, Aquery: import Authio;
+ Attr, IO, Key, Authinfo: import authio;
+ netmkaddr, eqbytes, memrandom: import authio;
+
+include "encoding.m";
+ base16: Encoding;
+
+Debug: con 0;
+
+# init, addkey, closekey, write, read, close, keyprompt
+
+init(f: Authio): string
+{
+ authio = f;
+ sys = load Sys Sys->PATH;
+ kr = load Keyring Keyring->PATH;
+ auth9 = load Auth9 Auth9->PATH;
+ auth9->init();
+ base16 = load Encoding Encoding->BASE16PATH;
+ return nil;
+}
+
+version := 1;
+
+interaction(attrs: list of ref Attr, io: ref IO): string
+{
+ return p9any(io);
+}
+
+p9any(io: ref IO): string
+{
+ while((buf := io.read()) == nil || (n := len buf) == 0 || buf[n-1] != byte 0)
+ io.toosmall(2048);
+ s := string buf[0:n-1];
+ if(Debug)
+ sys->print("s: %q\n", s);
+ (nil, flds) := sys->tokenize(s, " \t");
+ if(flds != nil && len hd flds >= 2 && (hd flds)[0:2] == "v."){
+ if(hd flds == "v.2"){
+ version = 2;
+ flds = tl flds;
+ if(Debug)
+ sys->print("version 2\n");
+ }else
+ return "p9any: unknown version";
+ }
+ doms: list of string;
+ for(; flds != nil; flds = tl flds){
+ (nf, subf) := sys->tokenize(hd flds, "@");
+ if(nf == 2 && hd subf == "p9sk1")
+ doms = hd tl subf :: doms;
+ }
+ if(doms == nil)
+ return "p9any: unsupported protocol";
+ if(Debug){
+ for(l := doms; l != nil; l = tl l)
+ sys->print("dom: %q\n", hd l);
+ }
+ r := array of byte ("p9sk1 "+hd doms);
+ buf[0:] = r;
+ buf[len r] = byte 0;
+ io.write(buf, len r + 1);
+ if(version == 2){
+ b := io.readn(3);
+ if(b == nil || b[0] != byte 'O' || b[1] != byte 'K' || b[2] != byte 0)
+ return "p9any: AS protocol botch: not OK";
+ if(Debug)
+ sys->print("OK\n");
+ }
+ return p9sk1client(io, hd doms);
+}
+
+#p9sk1:
+# C->S: nonce-C
+# S->C: nonce-S, uid-S, domain-S
+# C->A: nonce-S, uid-S, domain-S, uid-C, factotum-C
+# A->C: Kc{nonce-S, uid-C, uid-S, Kn}, Ks{nonce-S, uid-C, uid-S, K-n}
+# C->S: Ks{nonce-S, uid-C, uid-S, K-n}, Kn{nonce-S, counter}
+# S->C: Kn{nonce-C, counter}
+
+#asserts that uid-S and uid-C share new secret Kn
+#increment the counter to reuse the ticket.
+
+p9sk1client(io: ref IO, udom: string): string
+{
+
+ # C->S: nonce-C
+ cchal := array[CHALLEN] of byte;
+ memrandom(cchal, CHALLEN);
+ if(io.write(cchal, len cchal) != len cchal)
+ return sys->sprint("p9sk1: can't write cchal: %r");
+
+ # S->C: nonce-S, uid-S, domain-S
+ trbuf := io.readn(TICKREQLEN);
+ if(trbuf == nil)
+ return sys->sprint("p9sk1: can't read ticketreq: %r");
+
+ (nil, tr) := Ticketreq.unpack(trbuf);
+ if(tr == nil)
+ return "p9sk1: can't unpack ticket request";
+ if(Debug)
+ sys->print("ticketreq: type=%d authid=%q authdom=%q chal= hostid=%q uid=%q\n",
+ tr.rtype, tr.authid, tr.authdom, tr.hostid, tr.uid);
+
+ (mykey, diag) := io.findkey(nil, sys->sprint("dom=%q proto=p9sk1 user? !password?", udom));
+ if(mykey == nil)
+ return "can't find key: "+diag;
+ ukey: array of byte;
+ if((a := authio->lookattrval(mykey.secrets, "!hex")) != nil){
+ ukey = base16->dec(a);
+ if(len ukey != DESKEYLEN)
+ return "p9sk1: invalid !hex key";
+ }else if((a = authio->lookattrval(mykey.secrets, "!password")) != nil)
+ ukey = auth9->passtokey(a);
+ else
+ return "no !password (or !hex) in key";
+
+ # A->C: Kc{nonce-S, uid-C, uid-S, Kn}, Ks{nonce-S, uid-C, uid-S, K-n}
+ user := authio->lookattrval(mykey.attrs, "user");
+ if(user == nil)
+ user = authio->user(); # shouldn't happen
+ tr.rtype = Auth9->AuthTreq;
+ tr.hostid = user;
+ tr.uid = tr.hostid; # not speaking for anyone else
+ (tick, serverbits) := getastickets(tr, ukey);
+ if(tick == nil)
+ return sys->sprint("p9sk1: getasticket failed: %r");
+ if(tick.num != Auth9->AuthTc)
+ return "p9sk1: getasticket: failed: wrong key?";
+ if(Debug)
+ sys->print("ticket: num=%d chal= cuid=%q suid=%q key=\n", tick.num, tick.cuid, tick.suid);
+
+ # C->S: Ks{nonce-S, uid-C, uid-S, K-n}, Kn{nonce-S, counter}
+ ar := ref Authenticator;
+ ar.num = Auth9->AuthAc;
+ ar.chal = tick.chal;
+ ar.id = 0;
+ obuf := array[TICKETLEN+AUTHENTLEN] of byte;
+ obuf[0:] = serverbits;
+ obuf[TICKETLEN:] = ar.pack(tick.key);
+ if(io.write(obuf, len obuf) != len obuf)
+ return "p9sk1: error writing authenticator: %r";
+
+ # S->C: Kn{nonce-C, counter}
+ sbuf := io.readn(AUTHENTLEN);
+ if(sbuf == nil)
+ return sys->sprint("p9sk1: can't read server's authenticator: %r");
+ (nil, ar) = Authenticator.unpack(sbuf, tick.key);
+ if(ar.num != Auth9->AuthAs || !eqbytes(ar.chal, cchal) || ar.id != 0)
+ return "invalid authenticator from server";
+
+ ai := ref Authinfo(tick.cuid, tick.suid, nil, auth9->des56to64(tick.key));
+ io.done(ai);
+
+ return nil;
+}
+
+getastickets(tr: ref Ticketreq, key: array of byte): (ref Ticket, array of byte)
+{
+ afd := authdial(nil, tr.authdom);
+ if(afd == nil)
+ return (nil, nil);
+ return auth9->_asgetticket(afd, tr, key);
+}
+
+#
+# where to put the following functions?
+#
+
+csgetvalue(netroot: string, keytag: string, keyval: string, needtag: string): string
+{
+ cs := "/net/cs";
+ if(netroot != nil)
+ cs = netroot+"/cs";
+ fd := sys->open(cs, Sys->ORDWR); # TO DO: choice of root
+ if(fd == nil)
+ return nil;
+ if(sys->fprint(fd, "!%s=%s %s=*", keytag, keyval, needtag) < 0)
+ return nil;
+ sys->seek(fd, big 0, 0);
+ buf := array[1024] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0){
+ al := authio->parseline(string buf[0:n]); # assume the conventions match factotum's
+ for(; al != nil; al = tl al)
+ if((hd al).name == needtag)
+ return (hd al).val;
+ }
+ return nil;
+}
+
+authdial(netroot: string, dom: string): ref Sys->FD
+{
+ p: string;
+ if(dom != nil){
+ # look up an auth server in an authentication domain
+ p = csgetvalue(netroot, "authdom", dom, "auth");
+
+ # if that didn't work, just try the IP domain
+ if(p == nil)
+ p = csgetvalue(netroot, "dom", dom, "auth");
+ if(p == nil)
+ p = "$auth"; # temporary ...
+ if(p == nil){
+ sys->werrstr("no auth server found for "+dom);
+ return nil;
+ }
+ }else
+ p = "$auth"; # look for one relative to my machine
+ (nil, conn) := sys->dial(netmkaddr(p, netroot, "ticket"), nil);
+ return conn.dfd;
+}
diff --git a/appl/cmd/auth/factotum/proto/pass.b b/appl/cmd/auth/factotum/proto/pass.b
new file mode 100644
index 00000000..9c4462b3
--- /dev/null
+++ b/appl/cmd/auth/factotum/proto/pass.b
@@ -0,0 +1,29 @@
+implement Authproto;
+
+include "sys.m";
+ sys: Sys;
+
+include "../authio.m";
+ authio: Authio;
+ Attr, IO: import authio;
+
+init(f: Authio): string
+{
+ sys = load Sys Sys->PATH;
+ authio = f;
+ return nil;
+}
+
+interaction(attrs: list of ref Attr, io: ref Authio->IO): string
+{
+ (key, err) := io.findkey(attrs, "user? !password?");
+ if(key == nil)
+ return err;
+ user := authio->lookattrval(key.attrs, "user");
+ if(user == nil)
+ return "unknown user";
+ pass := authio->lookattrval(key.secrets, "!password");
+ a := sys->aprint("%q %q", user, pass);
+ io.write(a, len a);
+ return nil;
+}