shithub: pki

ref: 065d312a1728a00c7a12f8cc64f05b8b210d8426
dir: /libsec.diff/

View raw version
diff 051804186780ef7f3b2f4cb548be1dd0685de5fe uncommitted
--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -1,7 +1,6 @@
 #pragma	lib	"libsec.a"
 #pragma	src	"/sys/src/libsec"
 
-
 #ifndef _MPINT
 typedef struct mpint mpint;
 #endif
@@ -318,6 +317,7 @@
 typedef struct RSApub RSApub;
 typedef struct RSApriv RSApriv;
 typedef struct PEMChain PEMChain;
+typedef struct CertX509 CertX509;
 
 /* public/encryption key */
 struct RSApub
@@ -347,6 +347,24 @@
 	int	pemlen;
 };
 
+struct CertX509 {
+	int	serial;
+	char*	issuer;
+	vlong	validity_start;
+	vlong	validity_end;
+	char*	subject;
+	char**	altsubject;
+	int	naltsubject;
+	int	publickey_alg;
+	void*	publickey;
+	int	signature_alg;
+	void*	signature;
+	int	curve;
+	void*	ext;
+	uchar	digest[SHA2_512dlen];
+	int	digestlen;
+};
+
 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 +374,9 @@
 RSApriv*	rsaprivalloc(void);
 void		rsaprivfree(RSApriv*);
 RSApub*		rsaprivtopub(RSApriv*);
+char*		X509verify(CertX509*, CertX509*);
+CertX509*	X509decode(uchar*, int);
+void		X509free(CertX509*);
 RSApub*		X509toRSApub(uchar*, int, char*, int);
 RSApub*		X509reqtoRSApub(uchar*, int, char*, int);
 RSApub*		asn1toRSApub(uchar*, int);
@@ -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 @@
 static int	is_octetstring(Elem* pe, Bytes** poctets);
 static int	is_oid(Elem* pe, Ints** poid);
 static int	is_string(Elem* pe, char** pstring);
-static int	is_time(Elem* pe, char** ptime);
+static int	parse_time(Elem* pe, vlong* ptime);
 static int	decode(uchar* a, int alen, Elem* pelem);
 static int	encode(Elem e, Bytes** pbytes);
 static int	oid_lookup(Ints* o, Ints** tab);
@@ -1277,15 +1277,24 @@
 }
 
 static int
-is_time(Elem* pe, char** ptime)
+parse_time(Elem* pe, vlong* ptime)
 {
-	if(pe->tag.class == Universal
-	   && (pe->tag.num == UTCTime || pe->tag.num == GeneralizedTime)
-	   && pe->val.tag == VString) {
-		*ptime = pe->val.u.stringval;
-		return 1;
+	char *e, *fmt;
+	Tm t;
+
+	if(pe->tag.class != Universal)
+		return 0;
+	if(pe->val.tag != VString)
+		return 0;
+	switch(pe->tag.num){
+	case UTCTime:		fmt = "YYMMDDhhmmss?Z";	break;
+	case GeneralizedTime:	fmt = "YYYYMMDDhhmmss?Z";	break;
+	default:		return 0;		break;
 	}
-	return 0;
+	if(tmparse(&t, fmt, pe->val.u.stringval, nil, &e) == nil)
+		return 0;
+	*ptime = tmnorm(&t);
+	return 1;
 }
 
 
@@ -1581,20 +1590,6 @@
  *           revocationDate UTCTime}
  */
 
-typedef struct CertX509 {
-	int	serial;
-	char*	issuer;
-	char*	validity_start;
-	char*	validity_end;
-	char*	subject;
-	int	publickey_alg;
-	Bits*	publickey;
-	int	signature_alg;
-	Bits*	signature;
-	int	curve;
-	Bytes*	ext;
-} CertX509;
-
 /* Algorithm object-ids */
 enum {
 	ALG_rsaEncryption,
@@ -1730,14 +1725,12 @@
 
 static void appendaltnames(char *name, int nname, Bytes *ext, int req);
 
-static void
-freecert(CertX509* c)
+void
+X509free(CertX509* c)
 {
 	if(c == nil)
 		return;
 	free(c->issuer);
-	free(c->validity_start);
-	free(c->validity_end);
 	free(c->subject);
 	freebits(c->publickey);
 	freebits(c->signature);
@@ -1791,7 +1784,7 @@
 		el = el->tl;
 	}
 	if(i > 0) {
-		ans = (char*)emalloc(plen);
+		ans = emalloc(plen);
 		*ans = '\0';
 		while(--i >= 0) {
 			s = parts[i];
@@ -1833,154 +1826,6 @@
 	return oid_lookup(oid, namedcurves_oid_tab);
 }
 
-static CertX509*
-decode_cert(uchar *buf, int len)
-{
-	int ok = 0;
-	int n;
-	Elem  ecert;
-	Elem* ecertinfo;
-	Elem* esigalg;
-	Elem* esig;
-	Elem* eserial;
-	Elem* eissuer;
-	Elem* evalidity;
-	Elem* esubj;
-	Elem* epubkey;
-	Elist* el;
-	Elist* elcert = nil;
-	Elist* elcertinfo = nil;
-	Elist* elvalidity = nil;
-	Elist* elpubkey = nil;
-	Bits* bits = nil;
-	Bytes* b;
-	Elem* e;
-	CertX509* c = nil;
-
-	if(decode(buf, len, &ecert) != ASN_OK)
-		goto errret;
-
-	c = (CertX509*)emalloc(sizeof(CertX509));
-	c->serial = -1;
-	c->issuer = nil;
-	c->validity_start = nil;
-	c->validity_end = nil;
-	c->subject = nil;
-	c->publickey_alg = -1;
-	c->publickey = nil;
-	c->signature_alg = -1;
-	c->signature = nil;
-	c->ext = nil;
-
-	/* Certificate */
- 	if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3)
-		goto errret;
- 	ecertinfo = &elcert->hd;
- 	el = elcert->tl;
- 	esigalg = &el->hd;
-	c->signature_alg = parse_alg(esigalg);
- 	el = el->tl;
- 	esig = &el->hd;
-
-	/* Certificate Info */
-	if(!is_seq(ecertinfo, &elcertinfo))
-		goto errret;
-	n = elistlen(elcertinfo);
-  	if(n < 6)
-		goto errret;
-	eserial =&elcertinfo->hd;
- 	el = elcertinfo->tl;
- 	/* check for optional version, marked by explicit context tag 0 */
-	if(eserial->tag.class == Context && eserial->tag.num == 0) {
- 		eserial = &el->hd;
- 		if(n < 7)
- 			goto errret;
- 		el = el->tl;
- 	}
-
-	if(parse_alg(&el->hd) != c->signature_alg)
-		goto errret;
- 	el = el->tl;
- 	eissuer = &el->hd;
- 	el = el->tl;
- 	evalidity = &el->hd;
- 	el = el->tl;
- 	esubj = &el->hd;
- 	el = el->tl;
- 	epubkey = &el->hd;
-	if(el->tl != nil
-	&& el->tl->hd.tag.class == Context
-	&& el->tl->hd.tag.num == 3
-	&& el->tl->hd.val.tag == VOctets){
-		c->ext = el->tl->hd.val.u.octetsval;
-		el->tl->hd.val.u.octetsval = nil;	/* transfer ownership */
-	}
-	if(!is_int(eserial, &c->serial)) {
-		if(!is_bigint(eserial, &b))
-			goto errret;
-		c->serial = -1;	/* else we have to change cert struct */
-  	}
-	c->issuer = parse_name(eissuer);
-	if(c->issuer == nil)
-		goto errret;
-	/* Validity */
-  	if(!is_seq(evalidity, &elvalidity))
-		goto errret;
-	if(elistlen(elvalidity) != 2)
-		goto errret;
-	e = &elvalidity->hd;
-	if(!is_time(e, &c->validity_start))
-		goto errret;
-	e->val.u.stringval = nil;	/* string ownership transfer */
-	e = &elvalidity->tl->hd;
- 	if(!is_time(e, &c->validity_end))
-		goto errret;
-	e->val.u.stringval = nil;	/* string ownership transfer */
-
-	/* resume CertificateInfo */
- 	c->subject = parse_name(esubj);
-	if(c->subject == nil)
-		goto errret;
-
-	/* SubjectPublicKeyInfo */
-	if(!is_seq(epubkey, &elpubkey))
-		goto errret;
-	if(elistlen(elpubkey) != 2)
-		goto errret;
-
-	c->publickey_alg = parse_alg(&elpubkey->hd);
-	if(c->publickey_alg < 0)
-		goto errret;
-	c->curve = -1;
-	if(c->publickey_alg == ALG_ecPublicKey){
-		c->curve = parse_curve(&elpubkey->hd);
-		if(c->curve < 0)
-			goto errret;
-	}
-	elpubkey = elpubkey->tl;
-	if(!is_bitstring(&elpubkey->hd, &bits))
-		goto errret;
-	elpubkey->hd.val.u.bitstringval = nil;	/* transfer ownership */
-	c->publickey = bits;
-
-	/*resume Certificate */
-	if(c->signature_alg < 0)
-		goto errret;
-	if(!is_bitstring(esig, &bits))
-		goto errret;
-	esig->val.u.bitstringval = nil;	/* transfer ownership */
-	c->signature = bits;
-	ok = 1;
-
-errret:
-	freevalfields(&ecert.val);	/* recurses through lists, too */
-	if(!ok){
-		freecert(c);
-		c = nil;
-	}
-	return c;
-}
-
 /*
  *	RSAPublickKey ::= SEQUENCE {
  *		modulus INTEGER,
@@ -2187,6 +2032,156 @@
 
 static char Ebadsig[] = "bad signature";
 
+CertX509*
+X509decode(uchar *buf, int len)
+{
+	int ok = 0;
+	int n;
+	Elem  ecert;
+	Elem* ecertinfo;
+	Elem* esigalg;
+	Elem* esig;
+	Elem* eserial;
+	Elem* eissuer;
+	Elem* evalidity;
+	Elem* esubj;
+	Elem* epubkey;
+	Elist* el;
+	Elist* elcert = nil;
+	Elist* elcertinfo = nil;
+	Elist* elvalidity = nil;
+	Elist* elpubkey = nil;
+	Bits* bits = nil;
+	Bytes* b;
+	Elem* e;
+	CertX509* c = nil;
+
+	if(decode(buf, len, &ecert) != ASN_OK)
+		goto errret;
+
+	c = (CertX509*)emalloc(sizeof(CertX509));
+	c->serial = -1;
+	c->issuer = nil;
+	c->validity_start = -1;
+	c->validity_end = -1;
+	c->subject = nil;
+	c->altsubject = nil;
+	c->publickey_alg = -1;
+	c->publickey = nil;
+	c->signature_alg = -1;
+	c->signature = nil;
+	c->ext = nil;
+
+	/* Certificate */
+ 	if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3)
+		goto errret;
+ 	ecertinfo = &elcert->hd;
+ 	el = elcert->tl;
+ 	esigalg = &el->hd;
+	c->signature_alg = parse_alg(esigalg);
+ 	el = el->tl;
+ 	esig = &el->hd;
+
+	/* Certificate Info */
+	if(!is_seq(ecertinfo, &elcertinfo))
+		goto errret;
+	n = elistlen(elcertinfo);
+  	if(n < 6)
+		goto errret;
+	eserial =&elcertinfo->hd;
+ 	el = elcertinfo->tl;
+ 	/* check for optional version, marked by explicit context tag 0 */
+	if(eserial->tag.class == Context && eserial->tag.num == 0) {
+ 		eserial = &el->hd;
+ 		if(n < 7)
+ 			goto errret;
+ 		el = el->tl;
+ 	}
+
+	if(parse_alg(&el->hd) != c->signature_alg)
+		goto errret;
+ 	el = el->tl;
+ 	eissuer = &el->hd;
+ 	el = el->tl;
+ 	evalidity = &el->hd;
+ 	el = el->tl;
+ 	esubj = &el->hd;
+ 	el = el->tl;
+ 	epubkey = &el->hd;
+	if(el->tl != nil
+	&& el->tl->hd.tag.class == Context
+	&& el->tl->hd.tag.num == 3
+	&& el->tl->hd.val.tag == VOctets){
+		c->ext = el->tl->hd.val.u.octetsval;
+		el->tl->hd.val.u.octetsval = nil;	/* transfer ownership */
+	}
+	if(!is_int(eserial, &c->serial)) {
+		if(!is_bigint(eserial, &b))
+			goto errret;
+		c->serial = -1;	/* else we have to change cert struct */
+  	}
+	c->issuer = parse_name(eissuer);
+	if(c->issuer == nil)
+		goto errret;
+	/* Validity */
+  	if(!is_seq(evalidity, &elvalidity))
+		goto errret;
+	if(elistlen(elvalidity) != 2)
+		goto errret;
+	e = &elvalidity->hd;
+	if(!parse_time(e, &c->validity_start))
+		goto errret;
+	e = &elvalidity->tl->hd;
+ 	if(!parse_time(e, &c->validity_end))
+		goto errret;
+
+	/* resume CertificateInfo */
+ 	c->subject = parse_name(esubj);
+	if(c->subject == nil)
+		goto errret;
+
+	/* SubjectPublicKeyInfo */
+	if(!is_seq(epubkey, &elpubkey))
+		goto errret;
+	if(elistlen(elpubkey) != 2)
+		goto errret;
+
+	c->publickey_alg = parse_alg(&elpubkey->hd);
+	if(c->publickey_alg < 0)
+		goto errret;
+	c->curve = -1;
+	if(c->publickey_alg == ALG_ecPublicKey){
+		c->curve = parse_curve(&elpubkey->hd);
+		if(c->curve < 0)
+			goto errret;
+	}
+	elpubkey = elpubkey->tl;
+	if(!is_bitstring(&elpubkey->hd, &bits))
+		goto errret;
+	elpubkey->hd.val.u.bitstringval = nil;	/* transfer ownership */
+	c->publickey = bits;
+
+	/*resume Certificate */
+	if(c->signature_alg < 0)
+		goto errret;
+	if(!is_bitstring(esig, &bits))
+		goto errret;
+	esig->val.u.bitstringval = nil;	/* transfer ownership */
+	c->signature = bits;
+	c->digestlen = digest_certinfo(buf, len, digestalg[c->signature_alg], c->digest);
+	if(c->digestlen < 0)
+		goto errret;
+	ok = 1;
+
+errret:
+	freevalfields(&ecert.val);	/* recurses through lists, too */
+	if(!ok){
+		X509free(c);
+		c = nil;
+	}
+	return c;
+}
+
 char*
 X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk)
 {
@@ -2288,8 +2283,9 @@
 {
 	CertX509 *c;
 	ECpub *pub;
+	Bits *pk;
 
-	c = decode_cert(cert, ncert);
+	c = X509decode(cert, ncert);
 	if(c == nil)
 		return nil;
 	copysubject(name, nname, c->subject);
@@ -2297,11 +2293,12 @@
 	pub = nil;
 	if(c->publickey_alg == ALG_ecPublicKey){
 		ecdominit(dom, namedcurves[c->curve]);
-		pub = ecdecodepub(dom, c->publickey->data, c->publickey->len);
+		pk = c->publickey;
+		pub = ecdecodepub(dom, pk->data, pk->len);
 		if(pub == nil)
 			ecdomfree(dom);
 	}
-	freecert(c);
+	X509free(c);
 	return pub;
 }
 
@@ -2309,20 +2306,22 @@
 X509ecdsaverify(uchar *cert, int ncert, ECdomain *dom, ECpub *pk)
 {
 	char *e;
+	Bits *sig;
 	CertX509 *c;
 	int digestlen;
 	uchar digest[MAXdlen];
 
-	c = decode_cert(cert, ncert);
+	c = X509decode(cert, ncert);
 	if(c == nil)
 		return "cannot decode cert";
 	digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
 	if(digestlen <= 0){
-		freecert(c);
+		X509free(c);
 		return "cannot decode certinfo";
 	}
-	e = X509ecdsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, dom, pk);
-	freecert(c);
+	sig = c->signature;
+	e = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, dom, pk);
+	X509free(c);
 	return e;
 }
 
@@ -2331,40 +2330,71 @@
 {
 	CertX509 *c;
 	RSApub *pub;
+	Bits *pk;
 
-	c = decode_cert(cert, ncert);
+	c = X509decode(cert, ncert);
 	if(c == nil)
 		return nil;
 	copysubject(name, nname, c->subject);
 	appendaltnames(name, nname, c->ext, 0);
 	pub = nil;
+	pk = c->publickey;
 	if(c->publickey_alg == ALG_rsaEncryption)
-		pub = asn1toRSApub(c->publickey->data, c->publickey->len);
-	freecert(c);
+		pub = asn1toRSApub(pk->data, pk->len);
+	X509free(c);
 	return pub;
 }
 
-char*
+char*	
 X509rsaverify(uchar *cert, int ncert, RSApub *pk)
 {
 	char *e;
+	Bits *sig;
 	CertX509 *c;
 	int digestlen;
 	uchar digest[MAXdlen];
 
-	c = decode_cert(cert, ncert);
+	c = X509decode(cert, ncert);
 	if(c == nil)
 		return "cannot decode cert";
 	digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
 	if(digestlen <= 0){
-		freecert(c);
+		X509free(c);
 		return "cannot decode certinfo";
 	}
-	e = X509rsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
-	freecert(c);
+	sig = c->signature;
+	e = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, pk);
+	X509free(c);
 	return e;
 }
 
+char*
+X509verify(CertX509 *crt, CertX509 *vrf)
+{
+	RSApub *rsapub;
+	ECpub *ecpub;
+	ECdomain ecdom;
+	Bits *pk, *sig;
+	char *e;
+
+	e = "unknown algorithm";
+	pk = vrf->publickey;
+	sig = crt->signature;
+	switch(vrf->publickey_alg){
+	case ALG_rsaEncryption:
+		rsapub = asn1toRSApub(pk->data, pk->len);
+		e = X509rsaverifydigest(sig->data, sig->len, crt->digest, crt->digestlen, rsapub);
+		break;
+	case ALG_ecPublicKey:
+		ecdominit(&ecdom, namedcurves[vrf->curve]);
+		ecpub = ecdecodepub(&ecdom, pk->data, pk->len);
+		e = X509ecdsaverifydigest(sig->data, sig->len, crt->digest, crt->digestlen, &ecdom, ecpub);
+		ecdomfree(&ecdom);
+		break;
+	}
+	return e;
+}
+
 /* ------- Elem constructors ---------- */
 static Elem
 Null(void)
@@ -3125,14 +3155,16 @@
 X509digestSPKI(uchar *cert, int ncert, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
 {
 	CertX509 *c;
+	Bits *pk;
 
-	c = decode_cert(cert, ncert);
+	c = X509decode(cert, ncert);
 	if(c == nil){
 		werrstr("cannot decode cert");
 		return -1;
 	}
-	digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, fun, digest);
-	freecert(c);
+	pk = c->publickey;
+	digestSPKI(c->publickey_alg, pk->data, pk->len, fun, digest);
+	X509free(c);
 	return 0;
 }
 
@@ -3227,60 +3259,55 @@
 }
 
 void
-X509dump(uchar *cert, int ncert)
+X509dump(int fd, uchar *cert, int ncert)
 {
 	char *e;
+	Bits *pk, *sig;
 	CertX509 *c;
 	RSApub *rsapub;
 	ECpub *ecpub;
 	ECdomain ecdom;
-	int digestlen;
 	uchar digest[MAXdlen];
 
-	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");
+		fprint(fd, "cannot decode cert\n");
 		return;
 	}
 
-	digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
-	if(digestlen <= 0){
-		freecert(c);
-		print("cannot decode certinfo\n");
-		return;
-	}
+	pk = c->publickey;
+	sig = c->signature;
+	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("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);
-
 	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);
-			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);
+			fprint(fd, "self-signed X509rsaverifydigest returns: %s\n", e);
 			rsapubfree(rsapub);
 		}
 		break;
 	case ALG_ecPublicKey:
 		ecdominit(&ecdom, namedcurves[c->curve]);
-		ecpub = ecdecodepub(&ecdom, c->publickey->data, c->publickey->len);
+		ecpub = ecdecodepub(&ecdom, pk->data, pk->len);
 		if(ecpub != nil){
-			e = X509ecdsaverifydigest(c->signature->data, c->signature->len,
-				digest, digestlen, &ecdom, ecpub);
+			e = X509ecdsaverifydigest(sig->data, sig->len,
+				c->digest, c->digestlen, &ecdom, ecpub);
 			if(e==nil)
 				e = "nil (meaning ok)";
-			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;
 	}
 
-	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);
+	fprint(fd, "publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, c->digest);
 
 	sha2_256(cert, ncert, digest, nil);
-	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);
+	fprint(fd, "cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);
 
-	freecert(c);
-	print("end X509dump\n");
+	X509free(c);
+	fprint(fd, "end X509dump\n");
 }