summaryrefslogtreecommitdiff
path: root/appl/cmd/auth/factotum/proto/infauth.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/auth/factotum/proto/infauth.b')
-rw-r--r--appl/cmd/auth/factotum/proto/infauth.b362
1 files changed, 362 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);
+}