shithub: purgatorio

ref: 3efb5bbb4061056e523858b134c555949591efe2
dir: /appl/lib/oldauth.b/

View raw version
implement Oldauth;

#
# TO DO
#	- more error checking?
#	- details of auth error handling
#

include "sys.m";
	sys: Sys;

include "ipints.m";
	ipints: IPints;
	IPint: import ipints;

include "crypt.m";
	crypt: Crypt;
	PK, SK, PKsig: import crypt;

include "msgio.m";
	msgio: Msgio;

include "oldauth.m";

init()
{
	sys = load Sys Sys->PATH;
	ipints = load IPints IPints->PATH;
	crypt = load Crypt Crypt->PATH;
	msgio = load Msgio Msgio->PATH;
	msgio->init();
}

efmt()
{
	sys->werrstr("input or format error");
}

readauthinfo(filename: string): ref Authinfo
{
	fd := sys->open(filename, Sys->OREAD);
	if(fd == nil)
		return nil;
	a := array[5] of string;
	for(i := 0; i < len a; i++){
		(s, err) := getstr(fd);
		if(err != nil){
			sys->werrstr(sys->sprint("%q: input or format error", filename));
			return nil;
		}
		a[i] = s;
	}
	info := ref Authinfo;
	(info.spk, nil) = strtopk(a[0]);
	info.cert = strtocert(a[1]);
	(info.mysk, info.owner) = strtosk(a[2]);
	if(info.spk == nil || info.cert == nil || info.mysk == nil){
		efmt();
		return nil;
	}
	info.mypk = crypt->sktopk(info.mysk);
	info.alpha = IPint.strtoip(a[3], 64);
	info.p = IPint.strtoip(a[4], 64);
	if(info.alpha == nil || info.p == nil){
		efmt();
		return nil;
	}
	return info;
}

writeauthinfo(filename: string, info: ref Authinfo): int
{
	if(info.alpha == nil || info.p == nil ||
	   info.spk == nil || info.mysk == nil || info.cert == nil){
		sys->werrstr("invalid authinfo");
		return -1;
	}
	a := array[5] of string;
	a[0] = pktostr(info.spk, info.cert.signer);	# signer's public key
	a[1] = certtostr(info.cert);	# certificate for my public key
	a[2] = sktostr(info.mysk, info.owner);	# my secret/public key
	a[3] = b64(info.alpha);	# diffie hellman base
	a[4] = b64(info.p);	# diffie hellman modulus
	fd := sys->open(filename, Sys->OWRITE|Sys->OTRUNC);
	if(fd == nil){
		fd = sys->create(filename, Sys->OWRITE, 8r600);
		if(fd == nil){
			fd = sys->open(filename, Sys->OWRITE);
			if(fd == nil)
				return -1;
		}
	}
	for(i := 0; i < len a; i++)
		if(sendstr(fd, a[i]) <= 0)
			return -1;
	return 0;
}

sendstr(fd: ref Sys->FD, s: string): int
{
	a := array of byte s;
	return msgio->sendmsg(fd, a, len a);
}

getstr(fd: ref Sys->FD): (string, string)
{
	b := msgio->getmsg(fd);
	if(b == nil)
		return (nil, sys->sprint("%r"));
	return (string b, nil);
}

certtostr(c: ref Certificate): string
{
	s := sys->sprint("%s\n%s\n%s\n%ud\n", c.sa, c.ha, c.signer, c.exp);
	pick r := c.sig {
	RSA =>
		s += b64(r.n)+"\n";
	Elgamal =>
		s += b64(r.r)+"\n"+b64(r.s)+"\n";
	DSA =>
		s += b64(r.r)+"\n"+b64(r.s)+"\n";
	* =>
		raise "unknown key type";
	}
	return s;
}

pktostr(pk: ref PK, owner: string): string
{
	pick k := pk {
	RSA =>
		s := sys->sprint("rsa\n%s\n", owner);
		s += b64(k.n)+"\n"+b64(k.ek)+"\n";
		return s;
	Elgamal =>
		s := sys->sprint("elgamal\n%s\n", owner);
		s += b64(k.p)+"\n"+b64(k.alpha)+"\n"+b64(k.key)+"\n";
		return s;
	DSA =>
		s := sys->sprint("dsa\n%s\n", owner);
		s += b64(k.p)+"\n"+b64(k.q)+"\n"+b64(k.alpha)+"\n"+b64(k.key)+"\n";
		return s;
	* =>
		raise "unknown key type";
	}
}

sktostr(sk: ref SK, owner: string): string
{
	pick k := sk {
	RSA =>
		s := sys->sprint("rsa\n%s\n", owner);
		s += b64(k.pk.n)+"\n"+b64(k.pk.ek)+"\n"+b64(k.dk)+"\n"+
			b64(k.p)+"\n"+b64(k.q)+"\n"+
			b64(k.kp)+"\n"+b64(k.kq)+"\n"+
			k.c2.iptob64()+"\n";
		return s;
	Elgamal =>
		pk := k.pk;
		s := sys->sprint("elgamal\n%s\n", owner);
		s += b64(pk.p)+"\n"+b64(pk.alpha)+"\n"+b64(pk.key)+"\n"+b64(k.secret)+"\n";
		return s;
	DSA =>
		pk := k.pk;
		s := sys->sprint("dsa\n%s\n", owner);
		s += b64(pk.p)+"\n"+b64(pk.q)+"\n"+b64(pk.alpha)+"\n"+b64(k.secret)+"\n";
		return s;
	* =>
		raise "unknown key type";
	}
}

fields(s: string): array of string
{
	(nf, flds) := sys->tokenize(s, "\n^");
	a := array[nf] of string;
	for(i := 0; i < len a; i++){
		a[i] = hd flds;
		flds = tl flds;
	}
	return a;
}

bigs(a: array of string): array of ref IPint
{
	b := array[len a] of ref IPint;
	for(i := 0; i < len b; i++){
		b[i] = IPint.strtoip(a[i], 64);
		if(b[i] == nil)
			return nil;
	}
	return b;
}

need[T](a: array of T, min: int): int
{
	if(len a < min){
		efmt();
		return 1;
	}
	return 0;
}

strtocert(s: string): ref Certificate
{
	f := fields(s);
	if(need(f, 4))
		return nil;
	sa := f[0];
	ha := f[1];
	signer := f[2];
	exp := int big f[3];	# unsigned
	b := bigs(f[4:]);
	case f[0] {
	"rsa" =>
		if(need(b, 1))
			return nil;
		return ref Certificate(sa, ha, signer, exp, ref PKsig.RSA(b[0]));
	"elgamal" =>
		if(need(b, 2))
			return nil;
		return ref Certificate(sa, ha, signer, exp, ref PKsig.Elgamal(b[0], b[1]));
	"dsa" =>
		if(need(b, 2))
			return nil;
		return ref Certificate(sa, ha, signer, exp, ref PKsig.DSA(b[0], b[1]));
	* =>
		sys->werrstr("unknown algorithm: "+f[0]);
		return nil;
	}
}

strtopk(s: string): (ref PK, string)
{
	f := fields(s);
	if(need(f, 3))
		return (nil, "format error");
	sa := f[0];
	owner := f[1];
	b := bigs(f[2:]);
	case sa {
	"rsa" =>
		if(need(b, 2))
			return (nil, "format error");
		return (ref PK.RSA(b[0], b[1]), owner);
	"elgamal" =>
		if(need(b, 3))
			return (nil, "format error");
		return (ref PK.Elgamal(b[0], b[1], b[2]), owner);
	"dsa" =>
		if(need(b, 4))
			return (nil, "format error");
		return (ref PK.DSA(b[0], b[1], b[2], b[3]), owner);
	* =>
		return (nil, "unknown algorithm: "+f[0]);
	}
}

strtosk(s: string): (ref SK, string)
{
	f := fields(s);
	if(need(f, 3))
		return (nil, "format error");
	sa := f[0];
	owner := f[1];
	b := bigs(f[2:]);
	case sa {
	"rsa" =>
		if(need(b, 8))
			return (nil, "format error");
		return (ref SK.RSA(ref PK.RSA(b[0], b[1]), b[2], b[3], b[4], b[5], b[6], b[7]), owner);
	"elgamal" =>
		if(need(b, 4))
			return (nil, "format error");
		return (ref SK.Elgamal(ref PK.Elgamal(b[0], b[1], b[2]), b[3]), owner);
	"dsa" =>
		if(need(b, 5))
			return (nil, "format error");
		return (ref SK.DSA(ref PK.DSA(b[0], b[1], b[2], b[3]), b[4]), owner);
	* =>
		return (nil, "unknown algorithm: "+f[0]);
	}
}

skalg(sk: ref SK): string
{
	if(sk == nil)
		return "nil";
	case tagof sk {
	tagof SK.RSA =>	return "rsa";
	tagof SK.Elgamal =>	return "elgamal";
	tagof SK.DSA =>	return "dsa";
	* =>	return "gok";
	}
}

sign(sk: ref SK, signer: string, exp: int, state: ref Crypt->DigestState, ha: string): ref Certificate
{
	# add signer name and expiration time to hash
	if(state == nil)
		return nil;
	a := sys->aprint("%s %d", signer, exp);
	digest := hash(ha, a, state);
	if(digest == nil)
		return nil;
	b := IPint.bebytestoip(digest);
	return ref Certificate(skalg(sk), ha, signer, exp, crypt->sign(sk, b));
}

verify(pk: ref PK, cert: ref Certificate, state: ref Crypt->DigestState): int
{
	if(state == nil)
		return 0;
	a := sys->aprint("%s %d", cert.signer, cert.exp);
	digest := hash(cert.ha, a, state);
	if(digest == nil)
		return 0;
	b := IPint.bebytestoip(digest);
	return crypt->verify(pk, cert.sig, b);
}

hash(ha: string, a: array of byte, state: ref Crypt->DigestState): array of byte
{
	digest: array of byte;
	case ha {
	"sha" or "sha1" =>
		digest = array[Crypt->SHA1dlen] of byte;
		crypt->sha1(a, len a, digest, state);
	"md5" =>
		digest = array[Crypt->MD5dlen] of byte;
		crypt->md5(a, len a, digest, state);
	* =>
		# don't bother with md4
		sys->werrstr("unimplemented algorithm: "+ha);
		return nil;
	}
	return digest;
}

b64(ip: ref IPint): string
{
	return ip.iptob64z();
}