shithub: pki

Download patch

ref: 065d312a1728a00c7a12f8cc64f05b8b210d8426
parent: 22a5a98241a10902152b66dde3cb3d9682c83b21
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Dec 2 20:24:22 EST 2024

libsec.diff: update libsec to verify cert chains, make matching pki changes

--- a/fs.c
+++ b/fs.c
@@ -89,6 +89,8 @@
 int
 checkchain(CertTab *tab, CertX509 *c, char *name)
 {
+	if(c == nil)
+		return Sreject;
 	if(strcmp(c->subject, name) != 0)
 		return Sreject;
 	if(vfcert(tab, c) == 0)
@@ -243,7 +245,7 @@
 		return Enomem;
 	v->phase = xphase(v, Scdata);
 	e = certfill(v, r->ifcall.data + n, r->ifcall.count - n);
-	if(e != nil)
+	if(e == nil)
 		r->ofcall.count = r->ifcall.count;
 	return e;
 
@@ -259,7 +261,7 @@
 	if(v == nil || v->phase != Scdata)
 		return Ephase;
 	e = certfill(v, r->ifcall.data, r->ifcall.count);
-	if(e != nil)
+	if(e == nil)
 		r->ofcall.count = r->ifcall.count;
 	return e;
 }
@@ -516,11 +518,12 @@
 	roottab = mktab(nil, 512);
 
 	ARGBEGIN{
-	case 'r':	loadcerts(roottab, EARGF(usage()), 1);	break;
-	case 'n':	mntname = EARGF(usage());		break;
-	case 's':	srvname = EARGF(usage());		break;
-	case 'd':	debug++; chatty9p++;			break;
-	default:	usage();				break;
+	case 'n':	mntname = EARGF(usage());	break;
+	case 's':	srvname = EARGF(usage());	break;
+	case 'r':	loadroots(EARGF(usage()));	break;
+	case 't':	loadthumbs(EARGF(usage()));	break;
+	case 'd':	if(debug++) chatty9p++;		break;
+	default:	usage();			break;
 	}ARGEND;
 
 	postmountsrv(&pkisrv, srvname, mntname, MREPL);
--- a/libsec.diff
+++ b/libsec.diff
@@ -1,19 +1,15 @@
-diff 5a39d5532beb8f49ef0cbd0a260d7248787952f9 uncommitted
+diff 051804186780ef7f3b2f4cb548be1dd0685de5fe uncommitted
 --- a/sys/include/libsec.h
 +++ b/sys/include/libsec.h
-@@ -1,11 +1,6 @@
+@@ -1,7 +1,6 @@
  #pragma	lib	"libsec.a"
  #pragma	src	"/sys/src/libsec"
  
 -
--#ifndef _MPINT
--typedef struct mpint mpint;
--#endif
--
- /*
-  * AES definitions
-  */
-@@ -318,6 +313,7 @@
+ #ifndef _MPINT
+ typedef struct mpint mpint;
+ #endif
+@@ -318,6 +317,7 @@
  typedef struct RSApub RSApub;
  typedef struct RSApriv RSApriv;
  typedef struct PEMChain PEMChain;
@@ -21,7 +17,7 @@
  
  /* public/encryption key */
  struct RSApub
-@@ -347,6 +343,22 @@
+@@ -347,6 +347,24 @@
  	int	pemlen;
  };
  
@@ -31,6 +27,8 @@
 +	vlong	validity_start;
 +	vlong	validity_end;
 +	char*	subject;
++	char**	altsubject;
++	int	naltsubject;
 +	int	publickey_alg;
 +	void*	publickey;
 +	int	signature_alg;
@@ -44,7 +42,7 @@
  RSApriv*	rsagen(int nlen, int elen, int rounds);
  RSApriv*	rsafill(mpint *n, mpint *e, mpint *d, mpint *p, mpint *q);
  mpint*		rsaencrypt(RSApub *k, mpint *in, mpint *out);
-@@ -356,6 +368,9 @@
+@@ -356,6 +374,9 @@
  RSApriv*	rsaprivalloc(void);
  void		rsaprivfree(RSApriv*);
  RSApub*		rsaprivtopub(RSApriv*);
@@ -54,15 +52,465 @@
  RSApub*		X509toRSApub(uchar*, int, char*, int);
  RSApub*		X509reqtoRSApub(uchar*, int, char*, int);
  RSApub*		asn1toRSApub(uchar*, int);
-@@ -476,6 +491,8 @@
- 	uchar	*sessionID;
- 	uchar	*psk;
- 	int	certlen;
-+	uchar	**certchain;
-+	int	*certchainlen;
- 	int	sessionIDlen;
- 	int	psklen;
- 	int	(*trace)(char*fmt, ...);
+@@ -368,7 +389,7 @@
+ char*		X509rsaverify(uchar *cert, int ncert, RSApub *pk);
+ char*		X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk);
+ 
+-void		X509dump(uchar *cert, int ncert);
++void		X509dump(int fd, uchar *cert, int ncert);
+ 
+ mpint*		pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+ int		pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+@@ -488,7 +509,51 @@
+ 	char	*pskID;
+ } TLSconn;
+ 
++typedef struct TLSClientConf {
++	uchar	*cert;
++	int	ncert;
++	int	noverify;
++	char	*sessiontype;
++	int	(*trace)(char*fmt, ...);
++	struct {
++		uchar	*key;
++		int	nkey;
++		char	*id;
++	} psk;
++	struct {
++		int	nkey;
++		char	*constant;
++	} ttls;
++} TLSClientConf;
++
++typedef struct TLSServerConf {
++	PEMChain	*certs;
++	char		*sessiontype;
++	int		(*trace)(char*fmt, ...);
++	struct {
++		uchar	*key;
++		int	nkey;
++		char	*id;
++	} psk;
++	struct {
++		int	nkey;
++		char	*constant;
++	} ttls;
++} TLSServerConf;
++
++typedef struct TLSParams {
++	char		dir[40];	/* connection directory */
++	PEMChain	*certs;
++	struct {
++		uchar	*key;
++		int	nkey;
++	} ttls;
++} TLSParams;
++
+ /* tlshand.c */
++int tlsclient(int fd, char *srv, TLSClientConf *cfg, TLSParams **pparam);
++int tlsserver(int fd, TLSServerConf *cfg, TLSParams **pparam);
++void tlsparamfree(TLSParams *p);
+ int tlsClient(int fd, TLSconn *c);
+ int tlsServer(int fd, TLSconn *c);
+ 
+--- a/sys/src/libsec/port/tlshand.c
++++ b/sys/src/libsec/port/tlshand.c
+@@ -91,7 +91,8 @@
+ 	int erred;		// set when tlsError called
+ 	int (*trace)(char*fmt, ...); // for debugging
+ 	int version;	// protocol we are speaking
+-	Bytes *cert;	// server certificate; only last - no chain
++	Bytes **certs;	// server certificate; only last - no chain
++	int ncerts;
+ 
+ 	int cipher;
+ 	int nsecret;	// amount of secret data to init keys
+@@ -406,6 +407,8 @@
+ static Bytes*	pkcs1_decrypt(TlsSec *sec, Bytes *data);
+ static Bytes*	pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg);
+ 
++static int	validCert(char *rsrc, PEMChain *pc);
++
+ static void* emalloc(int);
+ static void* erealloc(void*, int);
+ static void put32(uchar *p, u32int);
+@@ -424,6 +427,78 @@
+ 
+ //================= client/server ========================
+ 
++int
++tlsserver(int fd, TLSServerConf *cfg, TLSParams **pparam)
++{
++	char buf[8];
++	char dname[32];
++	uchar seed[2*RandomSize];
++	int n, data, ctl, hand;
++	TlsConnection *tls;
++	TLSParams *param;
++
++	param = emalloc(sizeof(TLSParams));
++	ctl = open("/net/tls/clone", ORDWR|OCEXEC);
++	if(ctl < 0)
++		return -1;
++	n = read(ctl, buf, sizeof(buf)-1);
++	if(n < 0){
++		close(ctl);
++		return -1;
++	}
++	buf[n] = 0;
++	snprint(param->dir, sizeof(param->dir), "/net/tls/%s", buf);
++	snprint(dname, sizeof(dname), "/net/tls/%s/hand", buf);
++	hand = open(dname, ORDWR|OCEXEC);
++	if(hand < 0){
++		close(ctl);
++		return -1;
++	}
++	data = -1;
++	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
++	tls = tlsServer2(ctl, hand,
++		cfg->certs->pem, cfg->certs->pemlen,
++		cfg->psk.id, cfg->psk.key, cfg->psk.nkey,
++		cfg->trace, cfg->certs->next);
++	if(tls != nil){
++		snprint(dname, sizeof(dname), "/net/tls/%s/data", buf);
++		data = open(dname, ORDWR);
++	}
++	close(hand);
++	close(ctl);
++	if(data < 0){
++		tlsparamfree(param);
++		tlsConnectionFree(tls);
++		return -1;
++	}
++	param->certs = nil;  // client certificates are not yet implemented
++	if(cfg->sessiontype != nil){
++		if(strcmp(cfg->sessiontype, "ttls") != 0 || cfg->ttls.nkey == 0){
++			werrstr("invalid tls session: %s", cfg->sessiontype);
++			close(data);
++			tlsparamfree(param);
++			tlsConnectionFree(tls);
++			return -1;
++		}
++		param->ttls.key = emalloc(param->ttls.nkey);
++		memmove(seed, tls->sec->crandom, RandomSize);
++		memmove(seed+RandomSize, tls->sec->srandom, RandomSize);
++		tls->sec->prf(
++			param->ttls.key, param->ttls.nkey,
++			tls->sec->sec, MasterSecretSize,
++			cfg->ttls.constant, 
++			seed, sizeof(seed));
++	}
++	tlsConnectionFree(tls);
++	close(fd);
++	if(pparam == nil)
++		tlsparamfree(param);
++	else
++		*pparam = param;
++	return data;
++
++}
++
+ //	push TLS onto fd, returning new (application) file descriptor
+ //		or -1 if error.
+ int
+@@ -491,7 +566,7 @@
+ }
+ 
+ static uchar*
+-tlsClientExtensions(TLSconn *conn, int *plen)
++tlsClientExtensions(char *srvname, int *plen)
+ {
+ 	uchar *b, *p;
+ 	int i, n, m;
+@@ -499,7 +574,7 @@
+ 	p = b = nil;
+ 
+ 	// RFC6066 - Server Name Identification
+-	if(conn->serverName != nil && (n = strlen(conn->serverName)) > 0){
++	if(srvname != nil && (n = strlen(srvname)) > 0){
+ 		m = p - b;
+ 		b = erealloc(b, m + 2+2+2+1+2+n);
+ 		p = b + m;
+@@ -509,7 +584,7 @@
+ 		put16(p, 1+2+n), p += 2;	/* Server Name list length */
+ 		*p++ = 0;			/* Server Name Type: host_name */
+ 		put16(p, n), p += 2;		/* Server Name length */
+-		memmove(p, conn->serverName, n);
++		memmove(p, srvname, n);
+ 		p += n;
+ 	}
+ 
+@@ -557,6 +632,118 @@
+ 	return b;
+ }
+ 
++int
++tlsclient(int fd, char *srvname, TLSClientConf *cfg, TLSParams **pparam)
++{
++	char buf[8];
++	char dname[32];
++	uchar seed[2*RandomSize];
++	int i, n, data, ctl, hand;
++	TlsConnection *tls;
++	TLSParams *param;
++	PEMChain *pc, **ppc;
++	uchar *ext;
++
++	param = emalloc(sizeof(TLSParams));
++	ctl = open("/net/tls/clone", ORDWR|OCEXEC);
++	if(ctl < 0)
++		return -1;
++	n = read(ctl, buf, sizeof(buf)-1);
++	if(n < 0){
++		close(ctl);
++		return -1;
++	}
++	buf[n] = 0;
++	snprint(param->dir, sizeof(param->dir), "/net/tls/%s", buf);
++	snprint(dname, sizeof(dname), "/net/tls/%s/hand", buf);
++	hand = open(dname, ORDWR|OCEXEC);
++	if(hand < 0){
++		close(ctl);
++		return -1;
++	}
++	snprint(dname, sizeof(dname), "/net/tls/%s/data", buf);
++	data = open(dname, ORDWR);
++	if(data < 0){
++		close(hand);
++		close(ctl);
++		return -1;
++	}
++	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
++	ext = tlsClientExtensions(srvname, &n);
++	tls = tlsClient2(ctl, hand,
++		cfg->cert, cfg->ncert, 
++		cfg->psk.id, cfg->psk.key, cfg->psk.nkey,
++		ext, n, cfg->trace);
++	free(ext);
++	close(hand);
++	close(ctl);
++	if(tls == nil){
++		tlsparamfree(param);
++		close(data);
++		return -1;
++	}
++fprint(2, "certs: %#p, ncerts: %d\n", tls->certs, tls->ncerts);
++	if(tls->certs != nil){
++		ppc = &param->certs;
++		for(i = 0; i < tls->ncerts; i++){
++fprint(2, "add cert [%d]\n", i);
++			pc = emalloc(sizeof(PEMChain) + tls->certs[i]->len);
++			pc->next = nil;
++			pc->pem = (uchar*)(pc+1);
++			pc->pemlen = tls->certs[i]->len;
++			memcpy(pc->pem, tls->certs[i]->data, pc->pemlen);
++			*ppc = pc;
++			ppc = &pc->next;
++		}
++	} else {
++		param->certs = nil;
++	}
++	param->ttls.nkey = cfg->ttls.nkey;
++	if(cfg->sessiontype != nil){
++		if(strcmp(cfg->sessiontype, "ttls") != 0 || cfg->ttls.nkey == 0){
++			werrstr("invalid tls session: %s", cfg->sessiontype);
++			tlsparamfree(param);
++			close(data);
++			return -1;
++		}
++		param->ttls.key = emalloc(param->ttls.nkey);
++		memmove(seed, tls->sec->crandom, RandomSize);
++		memmove(seed+RandomSize, tls->sec->srandom, RandomSize);
++		tls->sec->prf(
++			param->ttls.key, param->ttls.nkey,
++			tls->sec->sec, MasterSecretSize,
++			cfg->ttls.constant, 
++			seed, sizeof(seed));
++	}
++	tlsConnectionFree(tls);
++	close(fd);
++	if(cfg->noverify == 0 
++	&& cfg->psk.key == nil
++	&& validCert("orib.dev", param->certs) == 0){
++		tlsparamfree(param);
++		close(data);
++		return -1;
++	}
++	if(pparam == nil)
++		tlsparamfree(param);
++	else
++		*pparam = param;
++	return data;
++}
++
++void
++tlsparamfree(TLSParams *param)
++{
++	PEMChain *pc, *pn;
++
++	for(pc = param->certs; pc != nil; pc = pn){
++		pn = pc->next;
++		free(pc);
++	}
++	free(param->ttls.key);
++	free(param);
++}
++
+ //	push TLS onto fd, returning new (application) file descriptor
+ //		or -1 if error.
+ int
+@@ -595,7 +782,7 @@
+ 		return -1;
+ 	}
+ 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
+-	ext = tlsClientExtensions(conn, &n);
++	ext = tlsClientExtensions(conn->serverName, &n);
+ 	tls = tlsClient2(ctl, hand,
+ 		conn->cert, conn->certlen, 
+ 		conn->pskID, conn->psk, conn->psklen,
+@@ -608,10 +795,10 @@
+ 		return -1;
+ 	}
+ 	free(conn->cert);
+-	if(tls->cert != nil){
+-		conn->certlen = tls->cert->len;
++	if(tls->certs != nil){
++		conn->certlen = tls->certs[0]->len;
+ 		conn->cert = emalloc(conn->certlen);
+-		memcpy(conn->cert, tls->cert->data, conn->certlen);
++		memcpy(conn->cert, tls->certs[0]->data, conn->certlen);
+ 	} else {
+ 		conn->certlen = 0;
+ 		conn->cert = nil;
+@@ -1042,7 +1229,7 @@
+ 	uchar *ext, int extlen,
+ 	int (*trace)(char*fmt, ...))
+ {
+-	int creq, dhx, cipher;
++	int i, creq, dhx, cipher;
+ 	TlsConnection *c;
+ 	Bytes *epm;
+ 	Msg m;
+@@ -1057,7 +1244,7 @@
+ 	c->ctl = ctl;
+ 	c->hand = hand;
+ 	c->trace = trace;
+-	c->cert = nil;
++	c->certs = nil;
+ 	c->sendp = c->buf;
+ 
+ 	c->version = ProtocolVersion;
+@@ -1118,11 +1305,15 @@
+ 	if(!msgRecv(c, &m))
+ 		goto Err;
+ 	if(m.tag == HCertificate){
++fprint(2, "here! %d\n", m.u.certificate.ncert);
+ 		if(m.u.certificate.ncert < 1) {
+ 			tlsError(c, EIllegalParameter, "runt certificate");
+ 			goto Err;
+ 		}
+-		c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len);
++		c->ncerts = m.u.certificate.ncert;
++		c->certs = emalloc(c->ncerts * sizeof(Bytes*));
++		for(i = 0; i < c->ncerts; i++)
++			c->certs[i] = makebytes(m.u.certificate.certs[i]->data, m.u.certificate.certs[i]->len);
+ 		if(!msgRecv(c, &m))
+ 			goto Err;
+ 	} else if(psklen == 0) {
+@@ -1133,7 +1324,7 @@
+ 		if(dhx){
+ 			char *err = verifyDHparams(c->sec,
+ 				m.u.serverKeyExchange.dh_parameters,
+-				c->cert,
++				c->certs[0],
+ 				m.u.serverKeyExchange.dh_signature,
+ 				c->version<TLS12Version ? 0x01 : m.u.serverKeyExchange.sigalg);
+ 			if(err != nil){
+@@ -1179,8 +1370,8 @@
+ 	msgClear(&m);
+ 
+ 	if(!dhx){
+-		if(c->cert != nil){
+-			epm = tlsSecRSAc(c->sec, c->cert->data, c->cert->len);
++		if(c->certs != nil){
++			epm = tlsSecRSAc(c->sec, c->certs[0]->data, c->certs[0]->len);
+ 			if(epm == nil){
+ 				tlsError(c, EBadCertificate, "bad certificate: %r");
+ 				goto Err;
+@@ -2100,11 +2291,12 @@
+ static void
+ tlsConnectionFree(TlsConnection *c)
+ {
++	int i;
++
+ 	if(c == nil)
+ 		return;
+ 
+ 	dh_finish(&c->sec->dh, nil);
+-
+ 	mpfree(c->sec->ec.Q.x);
+ 	mpfree(c->sec->ec.Q.y);
+ 	mpfree(c->sec->ec.Q.d);
+@@ -2112,12 +2304,58 @@
+ 
+ 	factotum_rsa_close(c->sec->rpc);
+ 	rsapubfree(c->sec->rsapub);
+-	freebytes(c->cert);
++	for(i = 0; i < c->ncerts; i++)
++		freebytes(c->certs[i]);
++	free(c->certs);
+ 
+ 	memset(c, 0, sizeof(*c));
+ 	free(c);
+ }
+ 
++static int
++validCert(char *host, PEMChain *pc)
++{
++	char *q, *t, buf[64];
++	PEMChain *e;
++	int n, fd, r;
++
++print("host: %s\n", host);
++	if(host == nil)
++		return 0;
++	if((fd = open("/mnt/pki/new", ORDWR)) == -1)
++		return 0;
++	q = quotestrdup(host);
++	fprint(fd, "verify host %s", q);
++	free(q);
++
++	t = "cert";
++	for(e = pc; e != nil; e = e->next){
++fprint(2, "%s der %d\n", t, e->pemlen);
++		if((r = fprint(fd, "%s der %d\n", t, e->pemlen)) == -1){
++fprint(2, "nope0[%d]: %r", r);
++			close(fd);
++			return 0;
++		}
++		if((write(fd, e->pem, e->pemlen)) != e->pemlen){
++fprint(2, "nope1: %r");
++			close(fd);
++			return 0;
++		}
++		t = "icert";
++	}
++	fprint(fd, "done\n");
++	if((n = pread(fd, buf, sizeof(buf)-1, 0)) == -1){
++		close(fd);
++		return 0;
++	}
++	buf[n] = 0;
++	close(fd);
++	if(strcmp(buf, "accept") != 0){
++		werrstr("cert validation failed: %s", buf);
++		return 0;
++	}
++	return 1;
++}
+ 
+ //================= cipher choices ========================
+ 
 --- a/sys/src/libsec/port/x509.c
 +++ b/sys/src/libsec/port/x509.c
 @@ -128,7 +128,7 @@
@@ -618,8 +1066,12 @@
  	return 0;
  }
  
-@@ -3230,42 +3262,37 @@
- X509dump(uchar *cert, int ncert)
+@@ -3227,60 +3259,55 @@
+ }
+ 
+ void
+-X509dump(uchar *cert, int ncert)
++X509dump(int fd, uchar *cert, int ncert)
  {
  	char *e;
 +	Bits *pk, *sig;
@@ -630,11 +1082,13 @@
 -	int digestlen;
  	uchar digest[MAXdlen];
  
- 	print("begin X509dump\n");
+-	print("begin X509dump\n");
 -	c = decode_cert(cert, ncert);
++	fprint(fd, "begin X509dump\n");
 +	c = X509decode(cert, ncert);
  	if(c == nil){
- 		print("cannot decode cert\n");
+-		print("cannot decode cert\n");
++		fprint(fd, "cannot decode cert\n");
  		return;
  	}
  
@@ -644,35 +1098,41 @@
 -		print("cannot decode certinfo\n");
 -		return;
 -	}
--
 +	pk = c->publickey;
 +	sig = c->signature;
- 	print("serial %d\n", c->serial);
- 	print("issuer %s\n", c->issuer);
++	fprint(fd, "serial %d\n", c->serial);
++	fprint(fd, "issuer %s\n", c->issuer);
++	fprint(fd, "validity %lld %lld\n", c->validity_start, c->validity_end);
++	fprint(fd, "subject %s\n", c->subject);
++	fprint(fd, "sigalg=%d digest=%.*H\n", c->signature_alg, c->digestlen, c->digest);
++	fprint(fd, "publickey_alg=%d pubkey[%d] %.*H\n", c->publickey_alg, pk->len,
++		pk->len, pk->data);
+ 
+-	print("serial %d\n", c->serial);
+-	print("issuer %s\n", c->issuer);
 -	print("validity %s %s\n", c->validity_start, c->validity_end);
-+	print("validity %lld %lld\n", c->validity_start, c->validity_end);
- 	print("subject %s\n", c->subject);
+-	print("subject %s\n", c->subject);
 -	print("sigalg=%d digest=%.*H\n", c->signature_alg, digestlen, digest);
 -	print("publickey_alg=%d pubkey[%d] %.*H\n", c->publickey_alg, c->publickey->len,
 -		c->publickey->len, c->publickey->data);
-+	print("sigalg=%d digest=%.*H\n", c->signature_alg, c->digestlen, c->digest);
-+	print("publickey_alg=%d pubkey[%d] %.*H\n", c->publickey_alg, pk->len,
-+		pk->len, pk->data);
- 
+-
  	switch(c->publickey_alg){
  	case ALG_rsaEncryption:
 -		rsapub = asn1toRSApub(c->publickey->data, c->publickey->len);
 +		rsapub = asn1toRSApub(pk->data, pk->len);
  		if(rsapub != nil){
- 			print("rsa pubkey e=%B n(%d)=%B\n", rsapub->ek, mpsignif(rsapub->n), rsapub->n);
+-			print("rsa pubkey e=%B n(%d)=%B\n", rsapub->ek, mpsignif(rsapub->n), rsapub->n);
 -			e = X509rsaverifydigest(c->signature->data, c->signature->len,
 -				digest, digestlen, rsapub);
++			fprint(fd, "rsa pubkey e=%B n(%d)=%B\n", rsapub->ek, mpsignif(rsapub->n), rsapub->n);
 +			e = X509rsaverifydigest(sig->data, sig->len,
 +				c->digest, c->digestlen, rsapub);
  			if(e==nil)
  				e = "nil (meaning ok)";
- 			print("self-signed X509rsaverifydigest returns: %s\n", e);
-@@ -3274,10 +3301,10 @@
+-			print("self-signed X509rsaverifydigest returns: %s\n", e);
++			fprint(fd, "self-signed X509rsaverifydigest returns: %s\n", e);
+ 			rsapubfree(rsapub);
+ 		}
  		break;
  	case ALG_ecPublicKey:
  		ecdominit(&ecdom, namedcurves[c->curve]);
@@ -685,8 +1145,12 @@
 +				c->digest, c->digestlen, &ecdom, ecpub);
  			if(e==nil)
  				e = "nil (meaning ok)";
- 			print("self-signed X509ecdsaverifydigest returns: %s\n", e);
-@@ -3287,8 +3314,8 @@
+-			print("self-signed X509ecdsaverifydigest returns: %s\n", e);
++			fprint(fd, "self-signed X509ecdsaverifydigest returns: %s\n", e);
+ 			ecpubfree(ecpub);
+ 		}
+ 		ecdomfree(&ecdom);
+@@ -3287,15 +3314,15 @@
  		break;
  	}
  
@@ -693,15 +1157,18 @@
 -	digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, sha2_256, digest);
 -	print("publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);
 +	digestSPKI(c->publickey_alg, pk->data, pk->len, sha2_256, c->digest);
-+	print("publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, c->digest);
++	fprint(fd, "publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, c->digest);
  
  	sha2_256(cert, ncert, digest, nil);
- 	print("cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);
-@@ -3296,6 +3323,6 @@
+-	print("cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);
++	fprint(fd, "cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);
+ 
  	sha1(cert, ncert, digest, nil);
- 	print("cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);
+-	print("cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);
++	fprint(fd, "cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);
  
 -	freecert(c);
+-	print("end X509dump\n");
 +	X509free(c);
- 	print("end X509dump\n");
++	fprint(fd, "end X509dump\n");
  }
--- a/pki.c
+++ b/pki.c
@@ -7,6 +7,7 @@
 #include "pki.h"
 
 CertTab *roottab;
+Thumbprint *thumbtab;
 
 static u32int
 strhash(char *s)
@@ -105,12 +106,12 @@
 vfsig(CertX509 *crt, CertX509 *vrf)
 {
 	char *r;
+
 	r = X509verify(crt, vrf);
-	if(r != nil){
-		werrstr("%s", r);
-		return -1;
-	}
-	return 0;
+	if(r == nil || okThumbprint(crt->digest, crt->digestlen, thumbtab))
+		return 0;
+	werrstr("%s", r);
+	return -1;
 }
 
 static int
@@ -155,7 +156,7 @@
 }
 
 void
-loadcerts(CertTab *tab, char *path, int isroot)
+loadroots(char *path)
 {
 	PEMChain *pc, *e;
 	CertX509 *c;
@@ -165,6 +166,13 @@
 	for(e = pc; e != nil; e = e->next){
 		if((c = X509decode(e->pem, e->pemlen)) == nil)
 			sysfatal("decode cert: %r");
-		addcert(tab, c, isroot);
+		addcert(roottab, c, 1);
 	}
+}
+
+void
+loadthumbs(char *path)
+{
+	if((thumbtab = initThumbprints(path, nil, "x509")) == nil)
+		sysfatal("load thumbprints: %r");
 }
--- a/pki.h
+++ b/pki.h
@@ -29,6 +29,7 @@
 	CertEnt		*next;
 	CertX509	*c;
 	int		isroot;
+	int		thumbok;
 };
 
 struct Issuer {
@@ -62,7 +63,8 @@
 };
 
 extern	CertTab	*roottab;
-void	loadcerts(CertTab*, char*, int);
+void	loadroots(char*);
+void	loadthumbs(char*);
 int	addcert(CertTab*, CertX509*, int);
 CertTab* mktab(CertTab*, int);
 int	vfcert(CertTab*, CertX509*);
--