diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/secstore.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/secstore.b')
| -rw-r--r-- | appl/lib/secstore.b | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/appl/lib/secstore.b b/appl/lib/secstore.b new file mode 100644 index 00000000..c5ff24bc --- /dev/null +++ b/appl/lib/secstore.b @@ -0,0 +1,474 @@ +implement Secstore; + +# +# interact with the Plan 9 secstore +# + +include "sys.m"; + sys: Sys; + +include "keyring.m"; + kr: Keyring; + DigestState, IPint: import kr; + AESbsize, AESstate: import kr; + +include "security.m"; + ssl: SSL; + +include "encoding.m"; + base64: Encoding; + +include "secstore.m"; + + +init() +{ + sys = load Sys Sys->PATH; + kr = load Keyring Keyring->PATH; + ssl = load SSL SSL->PATH; + base64 = load Encoding Encoding->BASE64PATH; + initPAKparams(); +} + +privacy(): int +{ + fd := sys->open("#p/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "private") < 0) + return 0; + return 1; +} + +connect(addr: string, user: string, pwhash: array of byte): (ref Sys->Connection, string, string) +{ + conn := dial(addr); + if(conn == nil){ + sys->werrstr(sys->sprint("can't dial %s: %r", addr)); + return (nil, nil, sys->sprint("%r")); + } + (sname, diag) := auth(conn, user, pwhash); + if(sname == nil){ + sys->werrstr(sys->sprint("can't authenticate: %s", diag)); + return (nil, nil, sys->sprint("%r")); + } + return (conn, sname, diag); +} + +dial(netaddr: string): ref Sys->Connection +{ + if(netaddr == nil) + netaddr = "net!$auth!secstore"; + (ok, conn) := sys->dial(netaddr, nil); + if(ok < 0) + return nil; + (err, sslconn) := ssl->connect(conn.dfd); + if(err != nil) + sys->werrstr(err); + return sslconn; +} + +auth(conn: ref Sys->Connection, user: string, pwhash: array of byte): (string, string) +{ + sname := PAKclient(conn, user, pwhash); + if(sname == nil) + return (nil, sys->sprint("%r")); + s := readstr(conn.dfd); + if(s == "STA") + return (sname, "need pin"); + if(s != "OK"){ + if(s != nil) + sys->werrstr(s); + return (nil, sys->sprint("%r")); + } + return (sname, nil); +} + +cansecstore(netaddr: string, user: string): int +{ + conn := dial(netaddr); + if(conn == nil) + return 0; + if(sys->fprint(conn.dfd, "secstore\tPAK\nC=%s\nm=0\n", user) < 0) + return 0; + buf := array[128] of byte; + n := sys->read(conn.dfd, buf, len buf); + if(n <= 0) + return 0; + return string buf[0:n] == "!account exists"; +} + +sendpin(conn: ref Sys->Connection, pin: string): int +{ + if(sys->fprint(conn.dfd, "STA%s", pin) < 0) + return -1; + s := readstr(conn.dfd); + if(s != "OK"){ + if(s != nil) + sys->werrstr(s); + return -1; + } + return 0; +} + +files(conn: ref Sys->Connection): list of (string, int, string, string, array of byte) +{ + file := getfile(conn, ".", 0); + if(file == nil) + return nil; + rl: list of (string, int, string, string, array of byte); + for(linelist := lines(file); linelist != nil; linelist = tl linelist){ + s := string hd linelist; + # factotum\t2552 Dec 9 13:04:49 GMT 2005 n9wSk45SPDxgljOIflGQoXjOkjs= + for(i := 0; i < len s && s[i] != '\t' && s[i] != ' '; i++){} # can be trailing spaces + name := s[0:i]; + for(; i < len s && (s[i] == ' ' || s[i] == '\t'); i++){} + for(j := i; j < len s && s[j] != ' '; j++){} + size := int s[i+1:j]; + for(i = j; i < len s && s[i] == ' '; i++){} + date := s[i:i+24]; + i += 24+1; + for(j = i; j < len s && s[j] != '\n'; j++){} + sha1 := s[i:j]; + rl = (name, int size, date, sha1, base64->dec(sha1)) :: rl; + } + l: list of (string, int, string, string, array of byte); + for(; rl != nil; rl = tl rl) + l = hd rl :: l; + return l; +} + +getfile(conn: ref Sys->Connection, name: string, maxsize: int): array of byte +{ + fd := conn.dfd; + if(maxsize <= 0) + maxsize = Maxfilesize; + if(sys->fprint(fd, "GET %s\n", name) < 0 || + (s := readstr(fd)) == nil){ + sys->werrstr(sys->sprint("can't get %q: %r", name)); + return nil; + } + nb := int s; + if(nb == -1){ + sys->werrstr(sys->sprint("remote file %q does not exist", name)); + return nil; + } + if(nb < 0 || nb > maxsize){ + sys->werrstr(sys->sprint("implausible file size %d for %q", nb, name)); + return nil; + } + file := array[nb] of byte; + for(nr := 0; nr < nb;){ + n := sys->read(fd, file[nr:], nb-nr); + if(n < 0){ + sys->werrstr(sys->sprint("error reading %q: %r", name)); + return nil; + } + if(n == 0){ + sys->werrstr(sys->sprint("empty file chunk reading %q at offset %d", name, nr)); + return nil; + } + nr += n; + } + return file; +} + +remove(conn: ref Sys->Connection, name: string): int +{ + if(sys->fprint(conn.dfd, "RM %s\n", name) < 0) + return -1; + + return 0; +} + +bye(conn: ref Sys->Connection) +{ + if(conn != nil){ + if(conn.dfd != nil) + sys->fprint(conn.dfd, "BYE"); + conn.dfd = nil; + conn.cfd = nil; + } +} + +mkseckey(s: string): array of byte +{ + key := array of byte s; + skey := array[Keyring->SHA1dlen] of byte; + kr->sha1(key, len key, skey, nil); + erasekey(key); + return skey; +} + +Checkpat: con "XXXXXXXXXXXXXXXX"; # it's what Plan 9's aescbc uses +Checklen: con len Checkpat; + +mkfilekey(s: string): array of byte +{ + key := array of byte s; + skey := array[Keyring->SHA1dlen] of byte; + sha := kr->sha1(array of byte "aescbc file", 11, nil, nil); + kr->sha1(key, len key, skey, sha); + erasekey(key); + erasekey(skey[AESbsize:]); + return skey[0:AESbsize]; +} + +decrypt(file: array of byte, key: array of byte): array of byte +{ + length := len file; + if(length == 0) + return file; + if(length < AESbsize+Checklen) + return nil; + state := kr->aessetup(key, file[0:AESbsize]); + if(state == nil){ + sys->werrstr("can't set AES state"); + return nil; + } + kr->aescbc(state, file[AESbsize:], length-AESbsize, Keyring->Decrypt); + if(string file[length-Checklen:] != Checkpat){ + sys->werrstr("file did not decrypt correctly"); + return nil; + } + return file[AESbsize: length-Checklen]; +} + +lines(file: array of byte): list of array of byte +{ + rl: list of array of byte; + for(i := 0; i < len file;){ + for(j := i; j < len file; j++) + if(file[j] == byte '\n'){ + j++; + break; + } + rl = file[i:j] :: rl; + i = j; + } + l: list of array of byte; + for(; rl != nil; rl = tl rl) + l = (hd rl) :: l; + return l; +} + +readstr(fd: ref Sys->FD): string +{ + buf := array[500] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return nil; + s := string buf[0:n]; + if(s[0] == '!'){ + sys->werrstr(s[1:]); + return nil; + } + return s; +} + +writerr(fd: ref Sys->FD, s: string) +{ + sys->fprint(fd, "!%s", s); + sys->werrstr(s); +} + +setsecret(conn: ref Sys->Connection, sigma: array of byte, direction: int): string +{ + secretin := array[Keyring->SHA1dlen] of byte; + secretout := array[Keyring->SHA1dlen] of byte; + if(direction != 0){ + kr->hmac_sha1(sigma, len sigma, array of byte "one", secretout, nil); + kr->hmac_sha1(sigma, len sigma, array of byte "two", secretin, nil); + }else{ + kr->hmac_sha1(sigma, len sigma, array of byte "two", secretout, nil); + kr->hmac_sha1(sigma, len sigma, array of byte "one", secretin, nil); + } + return ssl->secret(conn, secretin, secretout); +} + +erasekey(a: array of byte) +{ + for(i := 0; i < len a; i++) + a[i] = byte 0; +} + +# +# the following must only be used to talk to a Plan 9 secstore +# + +VERSION: con "secstore"; + +PAKparams: adt { + q: ref IPint; + p: ref IPint; + r: ref IPint; + g: ref IPint; +}; + +pak: ref PAKparams; + +# from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E + +initPAKparams() +{ + if(pak != nil) + return; + lpak := ref PAKparams; + lpak.q = IPint.strtoip("E0F0EF284E10796C5A2A511E94748BA03C795C13", 16); + lpak.p = IPint.strtoip("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB"+ + "DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"+ + "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"+ + "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", 16); + lpak.r = IPint.strtoip("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241"+ + "CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E"+ + "887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D"+ + "21C4656848614D888A4", 16); + lpak.g = IPint.strtoip("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734"+ + "44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD"+ + "410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734"+ + "E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", 16); + pak = lpak; # atomic store +} + +# H = (sha(ver,C,sha(passphrase)))^r mod p, +# a hash function expensive to attack by brute force. + +longhash(ver: string, C: string, passwd: array of byte): ref IPint +{ + aver := array of byte ver; + aC := array of byte C; + Cp := array[len aver + len aC + len passwd] of byte; + Cp[0:] = aver; + Cp[len aver:] = aC; + Cp[len aver+len aC:] = passwd; + buf := array[7*Keyring->SHA1dlen] of byte; + for(i := 0; i < 7; i++){ + key := array[] of { byte('A'+i) }; + kr->hmac_sha1(Cp, len Cp, key, buf[i*Keyring->SHA1dlen:], nil); + } + erasekey(Cp); + return mod(IPint.bebytestoip(buf), pak.p).expmod(pak.r, pak.p); # H +} + +mod(a, b: ref IPint): ref IPint +{ + return a.div(b).t1; +} + +shaz(s: string, digest: array of byte, state: ref DigestState): ref DigestState +{ + a := array of byte s; + state = kr->sha1(a, len a, digest, state); + erasekey(a); + return state; +} + +# Hi = H^-1 mod p +PAK_Hi(C: string, passhash: array of byte): (string, ref IPint, ref IPint) +{ + H := longhash(VERSION, C, passhash); + Hi := H.invert(pak.p); + return (Hi.iptostr(64), H, Hi); +} + +# another, faster, hash function for each party to +# confirm that the other has the right secrets. + +shorthash(mess: string, C: string, S: string, m: string, mu: string, sigma: string, Hi: string): array of byte +{ + state := shaz(mess, nil, nil); + state = shaz(C, nil, state); + state = shaz(S, nil, state); + state = shaz(m, nil, state); + state = shaz(mu, nil, state); + state = shaz(sigma, nil, state); + state = shaz(Hi, nil, state); + state = shaz(mess, nil, state); + state = shaz(C, nil, state); + state = shaz(S, nil, state); + state = shaz(m, nil, state); + state = shaz(mu, nil, state); + state = shaz(sigma, nil, state); + digest := array[Keyring->SHA1dlen] of byte; + shaz(Hi, digest, state); + return digest; +} + +# +# On input, conn provides an open channel to the server; +# C is the name this client calls itself; +# pass is the user's passphrase +# On output, session secret has been set in conn +# (unless return code is negative, which means failure). +# +PAKclient(conn: ref Sys->Connection, C: string, pwhash: array of byte): string +{ + dfd := conn.dfd; + + (hexHi, H, nil) := PAK_Hi(C, pwhash); + + # random 1<=x<=q-1; send C, m=g**x H + x := mod(IPint.random(240, 240), pak.q); + if(x.eq(IPint.inttoip(0))) + x = IPint.inttoip(1); + m := mod(pak.g.expmod(x, pak.p).mul(H), pak.p); + hexm := m.iptostr(64); + + if(sys->fprint(dfd, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm) < 0) + return nil; + + # recv g**y, S, check hash1(g**xy) + s := readstr(dfd); + if(s == nil){ + e := sys->sprint("%r"); + writerr(dfd, "couldn't read g**y"); + sys->werrstr(e); + return nil; + } + # should be: "mu=%s\nk=%s\nS=%s\n" + (nf, flds) := sys->tokenize(s, "\n"); + if(nf != 3){ + writerr(dfd, "verifier syntax error"); + return nil; + } + hexmu := ex("mu=", hd flds); flds = tl flds; + ks := ex("k=", hd flds); flds = tl flds; + S := ex("S=", hd flds); + if(hexmu == nil || ks == nil || S == nil){ + writerr(dfd, "verifier syntax error"); + return nil; + } + mu := IPint.strtoip(hexmu, 64); + sigma := mu.expmod(x, pak.p); + hexsigma := sigma.iptostr(64); + digest := shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi); + kc := base64->enc(digest); + if(ks != kc){ + writerr(dfd, "verifier didn't match"); + return nil; + } + + # send hash2(g**xy) + digest = shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi); + kc = base64->enc(digest); + if(sys->fprint(dfd, "k'=%s\n", kc) < 0) + return nil; + + # set session key + digest = shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi); + for(i := 0; i < len hexsigma; i++) + hexsigma[i] = 0; + + err := setsecret(conn, digest, 0); + if(err != nil) + return nil; + erasekey(digest); + if(sys->fprint(conn.cfd, "alg sha1 rc4_128") < 0) + return nil; + return S; +} + +ex(tag: string, s: string): string +{ + if(len s < len tag || s[0:len tag] != tag) + return nil; + return s[len tag:]; +} |
