1implement Secstore; 2 3# 4# interact with the Plan 9 secstore 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "dial.m"; 11 dialler: Dial; 12 13include "keyring.m"; 14 kr: Keyring; 15 DigestState, IPint: import kr; 16 AESbsize, AESstate: import kr; 17 18include "security.m"; 19 ssl: SSL; 20 random: Random; 21 22include "encoding.m"; 23 base64: Encoding; 24 25include "secstore.m"; 26 27 28init() 29{ 30 sys = load Sys Sys->PATH; 31 kr = load Keyring Keyring->PATH; 32 ssl = load SSL SSL->PATH; 33 random = load Random Random->PATH; 34 base64 = load Encoding Encoding->BASE64PATH; 35 dialler = load Dial Dial->PATH; 36 initPAKparams(); 37} 38 39privacy(): int 40{ 41 fd := sys->open("#p/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE); 42 if(fd == nil || sys->fprint(fd, "private") < 0) 43 return 0; 44 return 1; 45} 46 47connect(addr: string, user: string, pwhash: array of byte): (ref Dial->Connection, string, string) 48{ 49 conn := dial(addr); 50 if(conn == nil){ 51 sys->werrstr(sys->sprint("can't dial %s: %r", addr)); 52 return (nil, nil, sys->sprint("%r")); 53 } 54 (sname, diag) := auth(conn, user, pwhash); 55 if(sname == nil){ 56 sys->werrstr(sys->sprint("can't authenticate: %s", diag)); 57 return (nil, nil, sys->sprint("%r")); 58 } 59 return (conn, sname, diag); 60} 61 62dial(netaddr: string): ref Dial->Connection 63{ 64 if(netaddr == nil) 65 netaddr = "net!$auth!secstore"; 66 conn := dialler->dial(netaddr, nil); 67 if(conn == nil) 68 return nil; 69 (err, sslconn) := ssl->connect(conn.dfd); 70 if(err != nil) 71 sys->werrstr(err); 72 return sslconn; 73} 74 75auth(conn: ref Dial->Connection, user: string, pwhash: array of byte): (string, string) 76{ 77 sname := PAKclient(conn, user, pwhash); 78 if(sname == nil) 79 return (nil, sys->sprint("%r")); 80 s := readstr(conn.dfd); 81 if(s == "STA") 82 return (sname, "need pin"); 83 if(s != "OK"){ 84 if(s != nil) 85 sys->werrstr(s); 86 return (nil, sys->sprint("%r")); 87 } 88 return (sname, nil); 89} 90 91cansecstore(netaddr: string, user: string): int 92{ 93 conn := dial(netaddr); 94 if(conn == nil) 95 return 0; 96 if(sys->fprint(conn.dfd, "secstore\tPAK\nC=%s\nm=0\n", user) < 0) 97 return 0; 98 buf := array[128] of byte; 99 n := sys->read(conn.dfd, buf, len buf); 100 if(n <= 0) 101 return 0; 102 return string buf[0:n] == "!account exists"; 103} 104 105sendpin(conn: ref Dial->Connection, pin: string): int 106{ 107 if(sys->fprint(conn.dfd, "STA%s", pin) < 0) 108 return -1; 109 s := readstr(conn.dfd); 110 if(s != "OK"){ 111 if(s != nil) 112 sys->werrstr(s); 113 return -1; 114 } 115 return 0; 116} 117 118files(conn: ref Dial->Connection): list of (string, int, string, string, array of byte) 119{ 120 file := getfile(conn, ".", 0); 121 if(file == nil) 122 return nil; 123 rl: list of (string, int, string, string, array of byte); 124 for(linelist := lines(file); linelist != nil; linelist = tl linelist){ 125 s := string hd linelist; 126 # factotum\t2552 Dec 9 13:04:49 GMT 2005 n9wSk45SPDxgljOIflGQoXjOkjs= 127 for(i := 0; i < len s && s[i] != '\t' && s[i] != ' '; i++){} # can be trailing spaces 128 name := s[0:i]; 129 for(; i < len s && (s[i] == ' ' || s[i] == '\t'); i++){} 130 for(j := i; j < len s && s[j] != ' '; j++){} 131 size := int s[i+1:j]; 132 for(i = j; i < len s && s[i] == ' '; i++){} 133 date := s[i:i+24]; 134 i += 24+1; 135 for(j = i; j < len s && s[j] != '\n'; j++){} 136 sha1 := s[i:j]; 137 rl = (name, int size, date, sha1, base64->dec(sha1)) :: rl; 138 } 139 l: list of (string, int, string, string, array of byte); 140 for(; rl != nil; rl = tl rl) 141 l = hd rl :: l; 142 return l; 143} 144 145getfile(conn: ref Dial->Connection, name: string, maxsize: int): array of byte 146{ 147 fd := conn.dfd; 148 if(maxsize <= 0) 149 maxsize = Maxfilesize; 150 if(sys->fprint(fd, "GET %s\n", name) < 0 || 151 (s := readstr(fd)) == nil){ 152 sys->werrstr(sys->sprint("can't get %q: %r", name)); 153 return nil; 154 } 155 nb := int s; 156 if(nb == -1){ 157 sys->werrstr(sys->sprint("remote file %q does not exist", name)); 158 return nil; 159 } 160 if(nb < 0 || nb > maxsize){ 161 sys->werrstr(sys->sprint("implausible file size %d for %q", nb, name)); 162 return nil; 163 } 164 file := array[nb] of byte; 165 for(nr := 0; nr < nb;){ 166 n := sys->read(fd, file[nr:], nb-nr); 167 if(n < 0){ 168 sys->werrstr(sys->sprint("error reading %q: %r", name)); 169 return nil; 170 } 171 if(n == 0){ 172 sys->werrstr(sys->sprint("empty file chunk reading %q at offset %d", name, nr)); 173 return nil; 174 } 175 nr += n; 176 } 177 return file; 178} 179 180remove(conn: ref Dial->Connection, name: string): int 181{ 182 if(sys->fprint(conn.dfd, "RM %s\n", name) < 0) 183 return -1; 184 185 return 0; 186} 187 188putfile(conn: ref Dial->Connection, name: string, data: array of byte): int 189{ 190 if(len data > Maxfilesize){ 191 sys->werrstr("file too long"); 192 return -1; 193 } 194 fd := conn.dfd; 195 if(sys->fprint(fd, "PUT %s\n", name) < 0) 196 return -1; 197 if(sys->fprint(fd, "%d", len data) < 0) 198 return -1; 199 for(o := 0; o < len data;){ 200 n := len data-o; 201 if(n > Maxmsg) 202 n = Maxmsg; 203 if(sys->write(fd, data[o:o+n], n) != n) 204 return -1; 205 o += n; 206 } 207 return 0; 208} 209 210bye(conn: ref Dial->Connection) 211{ 212 if(conn != nil){ 213 if(conn.dfd != nil) 214 sys->fprint(conn.dfd, "BYE"); 215 conn.dfd = nil; 216 conn.cfd = nil; 217 } 218} 219 220mkseckey(s: string): array of byte 221{ 222 key := array of byte s; 223 skey := array[Keyring->SHA1dlen] of byte; 224 kr->sha1(key, len key, skey, nil); 225 erasekey(key); 226 return skey; 227} 228 229Checkpat: con "XXXXXXXXXXXXXXXX"; # it's what Plan 9's aescbc uses 230Checklen: con len Checkpat; 231 232mkfilekey(s: string): array of byte 233{ 234 key := array of byte s; 235 skey := array[Keyring->SHA1dlen] of byte; 236 sha := kr->sha1(array of byte "aescbc file", 11, nil, nil); 237 kr->sha1(key, len key, skey, sha); 238 erasekey(key); 239 erasekey(skey[AESbsize:]); 240 return skey[0:AESbsize]; 241} 242 243decrypt(file: array of byte, key: array of byte): array of byte 244{ 245 length := len file; 246 if(length == 0) 247 return file; 248 if(length < AESbsize+Checklen) 249 return nil; 250 state := kr->aessetup(key, file[0:AESbsize]); 251 if(state == nil){ 252 sys->werrstr("can't set AES state"); 253 return nil; 254 } 255 kr->aescbc(state, file[AESbsize:], length-AESbsize, Keyring->Decrypt); 256 if(string file[length-Checklen:] != Checkpat){ 257 sys->werrstr("file did not decrypt correctly"); 258 return nil; 259 } 260 return file[AESbsize: length-Checklen]; 261} 262 263encrypt(file: array of byte, key: array of byte): array of byte 264{ 265 dat := array[AESbsize+len file+Checklen] of byte; 266 iv := random->randombuf(random->NotQuiteRandom, AESbsize); 267 if(len iv != AESbsize) 268 return nil; 269 dat[:] = iv; 270 dat[len iv:] = file; 271 dat[len iv+len file:] = array of byte Checkpat; 272 state := kr->aessetup(key, iv); 273 if(state == nil){ 274 sys->werrstr("can't set AES state"); 275 return nil; 276 } 277 kr->aescbc(state, dat[AESbsize:], len dat-AESbsize, Keyring->Encrypt); 278 return dat; 279} 280 281lines(file: array of byte): list of array of byte 282{ 283 rl: list of array of byte; 284 for(i := 0; i < len file;){ 285 for(j := i; j < len file; j++) 286 if(file[j] == byte '\n'){ 287 j++; 288 break; 289 } 290 rl = file[i:j] :: rl; 291 i = j; 292 } 293 l: list of array of byte; 294 for(; rl != nil; rl = tl rl) 295 l = (hd rl) :: l; 296 return l; 297} 298 299readstr(fd: ref Sys->FD): string 300{ 301 buf := array[500] of byte; 302 n := sys->read(fd, buf, len buf); 303 if(n < 0) 304 return nil; 305 s := string buf[0:n]; 306 if(s[0] == '!'){ 307 sys->werrstr(s[1:]); 308 return nil; 309 } 310 return s; 311} 312 313writerr(fd: ref Sys->FD, s: string) 314{ 315 sys->fprint(fd, "!%s", s); 316 sys->werrstr(s); 317} 318 319setsecret(conn: ref Dial->Connection, sigma: array of byte, direction: int): string 320{ 321 secretin := array[Keyring->SHA1dlen] of byte; 322 secretout := array[Keyring->SHA1dlen] of byte; 323 if(direction != 0){ 324 kr->hmac_sha1(sigma, len sigma, array of byte "one", secretout, nil); 325 kr->hmac_sha1(sigma, len sigma, array of byte "two", secretin, nil); 326 }else{ 327 kr->hmac_sha1(sigma, len sigma, array of byte "two", secretout, nil); 328 kr->hmac_sha1(sigma, len sigma, array of byte "one", secretin, nil); 329 } 330 return ssl->secret(conn, secretin, secretout); 331} 332 333erasekey(a: array of byte) 334{ 335 for(i := 0; i < len a; i++) 336 a[i] = byte 0; 337} 338 339# 340# the following must only be used to talk to a Plan 9 secstore 341# 342 343VERSION: con "secstore"; 344 345PAKparams: adt { 346 q: ref IPint; 347 p: ref IPint; 348 r: ref IPint; 349 g: ref IPint; 350}; 351 352pak: ref PAKparams; 353 354# from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E 355 356initPAKparams() 357{ 358 if(pak != nil) 359 return; 360 lpak := ref PAKparams; 361 lpak.q = IPint.strtoip("E0F0EF284E10796C5A2A511E94748BA03C795C13", 16); 362 lpak.p = IPint.strtoip("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB"+ 363 "DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"+ 364 "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"+ 365 "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", 16); 366 lpak.r = IPint.strtoip("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241"+ 367 "CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E"+ 368 "887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D"+ 369 "21C4656848614D888A4", 16); 370 lpak.g = IPint.strtoip("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734"+ 371 "44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD"+ 372 "410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734"+ 373 "E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", 16); 374 pak = lpak; # atomic store 375} 376 377# H = (sha(ver,C,sha(passphrase)))^r mod p, 378# a hash function expensive to attack by brute force. 379 380longhash(ver: string, C: string, passwd: array of byte): ref IPint 381{ 382 aver := array of byte ver; 383 aC := array of byte C; 384 Cp := array[len aver + len aC + len passwd] of byte; 385 Cp[0:] = aver; 386 Cp[len aver:] = aC; 387 Cp[len aver+len aC:] = passwd; 388 buf := array[7*Keyring->SHA1dlen] of byte; 389 for(i := 0; i < 7; i++){ 390 key := array[] of { byte('A'+i) }; 391 kr->hmac_sha1(Cp, len Cp, key, buf[i*Keyring->SHA1dlen:], nil); 392 } 393 erasekey(Cp); 394 return mod(IPint.bebytestoip(buf), pak.p).expmod(pak.r, pak.p); # H 395} 396 397mod(a, b: ref IPint): ref IPint 398{ 399 return a.div(b).t1; 400} 401 402shaz(s: string, digest: array of byte, state: ref DigestState): ref DigestState 403{ 404 a := array of byte s; 405 state = kr->sha1(a, len a, digest, state); 406 erasekey(a); 407 return state; 408} 409 410# Hi = H^-1 mod p 411PAK_Hi(C: string, passhash: array of byte): (string, ref IPint, ref IPint) 412{ 413 H := longhash(VERSION, C, passhash); 414 Hi := H.invert(pak.p); 415 return (Hi.iptostr(64), H, Hi); 416} 417 418# another, faster, hash function for each party to 419# confirm that the other has the right secrets. 420 421shorthash(mess: string, C: string, S: string, m: string, mu: string, sigma: string, Hi: string): array of byte 422{ 423 state := shaz(mess, nil, nil); 424 state = shaz(C, nil, state); 425 state = shaz(S, nil, state); 426 state = shaz(m, nil, state); 427 state = shaz(mu, nil, state); 428 state = shaz(sigma, nil, state); 429 state = shaz(Hi, nil, state); 430 state = shaz(mess, nil, state); 431 state = shaz(C, nil, state); 432 state = shaz(S, nil, state); 433 state = shaz(m, nil, state); 434 state = shaz(mu, nil, state); 435 state = shaz(sigma, nil, state); 436 digest := array[Keyring->SHA1dlen] of byte; 437 shaz(Hi, digest, state); 438 return digest; 439} 440 441# 442# On input, conn provides an open channel to the server; 443# C is the name this client calls itself; 444# pass is the user's passphrase 445# On output, session secret has been set in conn 446# (unless return code is negative, which means failure). 447# 448PAKclient(conn: ref Dial->Connection, C: string, pwhash: array of byte): string 449{ 450 dfd := conn.dfd; 451 452 (hexHi, H, nil) := PAK_Hi(C, pwhash); 453 454 # random 1<=x<=q-1; send C, m=g**x H 455 x := mod(IPint.random(240, 240), pak.q); 456 if(x.eq(IPint.inttoip(0))) 457 x = IPint.inttoip(1); 458 m := mod(pak.g.expmod(x, pak.p).mul(H), pak.p); 459 hexm := m.iptostr(64); 460 461 if(sys->fprint(dfd, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm) < 0) 462 return nil; 463 464 # recv g**y, S, check hash1(g**xy) 465 s := readstr(dfd); 466 if(s == nil){ 467 e := sys->sprint("%r"); 468 writerr(dfd, "couldn't read g**y"); 469 sys->werrstr(e); 470 return nil; 471 } 472 # should be: "mu=%s\nk=%s\nS=%s\n" 473 (nf, flds) := sys->tokenize(s, "\n"); 474 if(nf != 3){ 475 writerr(dfd, "verifier syntax error"); 476 return nil; 477 } 478 hexmu := ex("mu=", hd flds); flds = tl flds; 479 ks := ex("k=", hd flds); flds = tl flds; 480 S := ex("S=", hd flds); 481 if(hexmu == nil || ks == nil || S == nil){ 482 writerr(dfd, "verifier syntax error"); 483 return nil; 484 } 485 mu := IPint.strtoip(hexmu, 64); 486 sigma := mu.expmod(x, pak.p); 487 hexsigma := sigma.iptostr(64); 488 digest := shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi); 489 kc := base64->enc(digest); 490 if(ks != kc){ 491 writerr(dfd, "verifier didn't match"); 492 return nil; 493 } 494 495 # send hash2(g**xy) 496 digest = shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi); 497 kc = base64->enc(digest); 498 if(sys->fprint(dfd, "k'=%s\n", kc) < 0) 499 return nil; 500 501 # set session key 502 digest = shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi); 503 for(i := 0; i < len hexsigma; i++) 504 hexsigma[i] = 0; 505 506 err := setsecret(conn, digest, 0); 507 if(err != nil) 508 return nil; 509 erasekey(digest); 510 if(sys->fprint(conn.cfd, "alg sha1 rc4_128") < 0) 511 return nil; 512 return S; 513} 514 515ex(tag: string, s: string): string 516{ 517 if(len s < len tag || s[0:len tag] != tag) 518 return nil; 519 return s[len tag:]; 520} 521