Charles.Forsyth20060303a828770d19 years agoHistory
474 lines · 11.5 KB · Brainfuck
RawDownload
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 usesChecklen: 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 pPAK_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:];}