shithub: riscv

Download patch

ref: 464763202be7bdafa703c8c2ecfe1f4a5142f26f
parent: 21aee5d8cbd1ebae796cdc58404b8d8902738370
author: cinap_lenrek <[email protected]>
date: Mon Apr 11 16:23:34 EDT 2016

ape: add libauth, libbio, libmp and libsec as replacements for openssl

--- /dev/null
+++ b/sys/include/ape/auth.h
@@ -1,0 +1,155 @@
+#ifndef _PLAN9_SOURCE
+  This header file is an extension to ANSI/POSIX
+#endif
+
+#ifndef __AUTH_H_
+#define __AUTH_H_
+
+#pragma	src	"/sys/src/ape/lib/auth"
+#pragma	lib	"/$M/lib/ape/libauth.a"
+
+#include <u.h>
+#include <fmt.h>
+
+/*
+ * Interface for typical callers.
+ */
+
+typedef struct	AuthInfo	AuthInfo;
+typedef struct	Chalstate	Chalstate;
+typedef struct	Chapreply	Chapreply;
+typedef struct	MSchapreply	MSchapreply;
+typedef struct	UserPasswd	UserPasswd;
+typedef struct	AuthRpc		AuthRpc;
+
+enum
+{
+	MAXCHLEN=	256,		/* max challenge length	*/
+	MAXNAMELEN=	256,		/* maximum name length */
+	MD5LEN=		16,
+
+	ARok = 0,			/* rpc return values */
+	ARdone,
+	ARerror,
+	ARneedkey,
+	ARbadkey,
+	ARwritenext,
+	ARtoosmall,
+	ARtoobig,
+	ARrpcfailure,
+	ARphase,
+
+	AuthRpcMax = 4096,
+};
+
+struct AuthRpc
+{
+	int afd;
+	char ibuf[AuthRpcMax+1];	/* +1 for NUL in auth_rpc.c */
+	char obuf[AuthRpcMax];
+	char *arg;
+	uint narg;
+};
+
+struct AuthInfo
+{
+	char	*cuid;		/* caller id */
+	char	*suid;		/* server id */
+	char	*cap;		/* capability (only valid on server side) */
+	int	nsecret;	/* length of secret */
+	uchar	*secret;	/* secret */
+};
+
+struct Chalstate
+{
+	char	*user;
+	char	chal[MAXCHLEN];
+	int	nchal;
+	void	*resp;
+	int	nresp;
+
+/* for implementation only */
+	int	afd;			/* to factotum */
+	AuthRpc	*rpc;			/* to factotum */
+	char	userbuf[MAXNAMELEN];	/* temp space if needed */
+	int	userinchal;		/* user was sent to obtain challenge */
+};
+
+struct	Chapreply		/* for protocol "chap" */
+{
+	uchar	id;
+	char	resp[MD5LEN];
+};
+
+struct	MSchapreply	/* for protocol "mschap" */
+{
+	char	LMresp[24];		/* Lan Manager response */
+	char	NTresp[24];		/* NT response */
+};
+
+struct	UserPasswd
+{
+	char	*user;
+	char	*passwd;
+};
+
+extern	int	newns(char*, char*);
+extern	int	addns(char*, char*);
+
+extern	int	noworld(char*);
+extern	int	amount(int, char*, int, char*);
+
+/* these two may get generalized away -rsc */
+extern	int	login(char*, char*, char*);
+extern	int	httpauth(char*, char*);
+
+typedef struct Attr Attr;
+enum {
+	AttrNameval,		/* name=val -- when matching, must have name=val */
+	AttrQuery,		/* name? -- when matching, must be present */
+	AttrDefault,		/* name:=val -- when matching, if present must match INTERNAL */
+};
+struct Attr
+{
+	int type;
+	Attr *next;
+	char *name;
+	char *val;
+};
+
+typedef int AuthGetkey(char*);
+
+int	_attrfmt(Fmt*);
+Attr	*_copyattr(Attr*);
+Attr	*_delattr(Attr*, char*);
+Attr	*_findattr(Attr*, char*);
+void	_freeattr(Attr*);
+Attr	*_mkattr(int, char*, char*, Attr*);
+Attr	*_parseattr(char*);
+char	*_strfindattr(Attr*, char*);
+#pragma varargck type "A" Attr*
+
+extern AuthInfo*	fauth_proxy(int, AuthRpc *rpc, AuthGetkey *getkey, char *params);
+extern AuthInfo*	auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...);
+extern int		auth_getkey(char*);
+extern int		(*amount_getkey)(char*);
+extern void		auth_freeAI(AuthInfo *ai);
+extern int		auth_chuid(AuthInfo *ai, char *ns);
+extern Chalstate	*auth_challenge(char*, ...);
+extern AuthInfo*	auth_response(Chalstate*);
+extern int		auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...);
+extern void		auth_freechal(Chalstate*);
+extern AuthInfo*	auth_userpasswd(char *user, char *passwd);
+extern UserPasswd*	auth_getuserpasswd(AuthGetkey *getkey, char*, ...);
+extern AuthInfo*	auth_getinfo(AuthRpc *rpc);
+extern AuthRpc*		auth_allocrpc(int afd);
+extern Attr*		auth_attr(AuthRpc *rpc);
+extern void		auth_freerpc(AuthRpc *rpc);
+extern uint		auth_rpc(AuthRpc *rpc, char *verb, void *a, int n);
+extern int		auth_wep(char*, char*, ...);
+#pragma varargck argpos auth_proxy 3
+#pragma varargck argpos auth_challenge 1
+#pragma varargck argpos auth_respond 8
+#pragma varargck argpos auth_getuserpasswd 2
+
+#endif
--- /dev/null
+++ b/sys/include/ape/bio.h
@@ -1,0 +1,89 @@
+#ifndef _PLAN9_SOURCE
+  This header file is an extension to ANSI/POSIX
+#endif
+
+#ifndef __BIO_H_
+#define __BIO_H_
+#pragma	src	"/sys/src/libbio"
+#pragma	lib	"/$M/lib/ape/libbio.a"
+
+#include <u.h>
+
+typedef	struct	Biobuf	Biobuf;
+typedef	struct	Biobufhdr	Biobufhdr;
+
+enum
+{
+	Bsize		= 8*1024,
+	Bungetsize	= UTFmax+1,	/* space for ungetc */
+	Bmagic		= 0x314159,
+	Beof		= -1,
+	Bbad		= -2,
+
+	Binactive	= 0,		/* states */
+	Bractive,
+	Bwactive,
+	Bracteof,
+};
+
+struct	Biobufhdr
+{
+	int	icount;		/* neg num of bytes at eob */
+	int	ocount;		/* num of bytes at bob */
+	int	rdline;		/* num of bytes after rdline */
+	int	runesize;	/* num of bytes of last getrune */
+	int	state;		/* r/w/inactive */
+	int	fid;		/* open file */
+	int	flag;		/* magic if malloc'ed */
+	vlong	offset;		/* offset of buffer in file */
+	int	bsize;		/* size of buffer */
+	uchar*	bbuf;		/* pointer to beginning of buffer */
+	uchar*	ebuf;		/* pointer to end of buffer */
+	uchar*	gbuf;		/* pointer to good data in buf */
+	void	(*errorf)(char *);	/* called on error if not nil */
+};
+
+struct	Biobuf
+{
+	Biobufhdr;
+	uchar	b[Bungetsize+Bsize];
+};
+
+/* Dregs, redefined as functions for backwards compatibility */
+#define	BGETC(bp)	Bgetc(bp)
+#define	BPUTC(bp,c)	Bputc(bp,c)
+#define	BOFFSET(bp)	Boffset(bp)
+#define	BLINELEN(bp)	Blinelen(bp)
+#define	BFILDES(bp)	Bfildes(bp)
+
+int	Bbuffered(Biobufhdr*);
+int	Bfildes(Biobufhdr*);
+int	Bflush(Biobufhdr*);
+int	Bgetc(Biobufhdr*);
+int	Bgetd(Biobufhdr*, double*);
+long	Bgetrune(Biobufhdr*);
+int	Binit(Biobuf*, int, int);
+int	Binits(Biobufhdr*, int, int, uchar*, int);
+int	Blinelen(Biobufhdr*);
+vlong	Boffset(Biobufhdr*);
+Biobuf*	Bopen(char*, int);
+Biobuf*	Bfdopen(int, int);
+int	Bprint(Biobufhdr*, char*, ...);
+int	Bvprint(Biobufhdr*, char*, va_list);
+int	Bputc(Biobufhdr*, int);
+int	Bputrune(Biobufhdr*, long);
+void*	Brdline(Biobufhdr*, int);
+char*	Brdstr(Biobufhdr*, int, int);
+long	Bread(Biobufhdr*, void*, long);
+vlong	Bseek(Biobufhdr*, vlong, int);
+int	Bterm(Biobufhdr*);
+int	Bungetc(Biobufhdr*);
+int	Bungetrune(Biobufhdr*);
+long	Bwrite(Biobufhdr*, void*, long);
+void	Blethal(Biobufhdr*, void(*)(char*));
+void	Berror(Biobufhdr*, char*, ...);
+
+#pragma	varargck	argpos	Bprint	2
+#pragma	varargck	argpos	Berror	2
+
+#endif
--- /dev/null
+++ b/sys/include/ape/libsec.h
@@ -1,0 +1,584 @@
+#ifndef _PLAN9_SOURCE
+  This header file is an extension to ANSI/POSIX
+#endif
+
+#ifndef __LIBSEC_H_
+#define __LIBSEC_H_
+
+#pragma	src	"/sys/src/ape/lib/sec"
+#pragma	lib	"/$M/lib/ape/libsec.a"
+
+#include <u.h>
+
+#ifndef _MPINT
+typedef struct mpint mpint;
+#endif
+
+/*
+ * AES definitions
+ */
+
+enum
+{
+	AESbsize=	16,
+	AESmaxkey=	32,
+	AESmaxrounds=	14
+};
+
+typedef struct AESstate AESstate;
+struct AESstate
+{
+	ulong	setup;
+	int	rounds;
+	int	keybytes;
+	uchar	key[AESmaxkey];			/* unexpanded key */
+	ulong	ekey[4*(AESmaxrounds + 1)];	/* encryption key */
+	ulong	dkey[4*(AESmaxrounds + 1)];	/* decryption key */
+	uchar	ivec[AESbsize];			/* initialization vector */
+	uchar	mackey[3 * AESbsize];	/* 3 XCBC mac 96 keys */
+};
+
+/* block ciphers */
+void	aes_encrypt(ulong rk[], int Nr, uchar pt[16], uchar ct[16]);
+void	aes_decrypt(ulong rk[], int Nr, uchar ct[16], uchar pt[16]);
+
+void	setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec);
+void	aesCBCencrypt(uchar *p, int len, AESstate *s);
+void	aesCBCdecrypt(uchar *p, int len, AESstate *s);
+
+void	setupAESXCBCstate(AESstate *s);
+uchar*	aesXCBCmac(uchar *p, int len, AESstate *s);
+
+typedef struct AESGCMstate AESGCMstate;
+struct AESGCMstate
+{
+	AESstate;
+
+	ulong	H[4];
+	ulong	M[16][256][4];
+};
+
+void	setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen);
+void	aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen);
+void	aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s);
+int	aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s);
+
+/*
+ * Blowfish Definitions
+ */
+
+enum
+{
+	BFbsize	= 8,
+	BFrounds= 16
+};
+
+/* 16-round Blowfish */
+typedef struct BFstate BFstate;
+struct BFstate
+{
+	ulong	setup;
+
+	uchar	key[56];
+	uchar	ivec[8];
+
+	u32int 	pbox[BFrounds+2];
+	u32int	sbox[1024];
+};
+
+void	setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec);
+void	bfCBCencrypt(uchar*, int, BFstate*);
+void	bfCBCdecrypt(uchar*, int, BFstate*);
+void	bfECBencrypt(uchar*, int, BFstate*);
+void	bfECBdecrypt(uchar*, int, BFstate*);
+
+/*
+ * Chacha definitions
+ */
+
+enum
+{
+	ChachaBsize=	64,
+	ChachaKeylen=	256/8,
+	ChachaIVlen=	96/8,
+};
+
+typedef struct Chachastate Chachastate;
+struct Chachastate
+{
+	union{
+		u32int	input[16];
+		struct {
+			u32int	constant[4];
+			u32int	key[8];
+			u32int	counter;
+			u32int	iv[3];
+		};
+	};
+	int	rounds;
+	int	ivwords;
+};
+
+void	setupChachastate(Chachastate*, uchar*, ulong, uchar*, ulong, int);
+void	chacha_setiv(Chachastate *, uchar*);
+void	chacha_setblock(Chachastate*, u64int);
+void	chacha_encrypt(uchar*, ulong, Chachastate*);
+void	chacha_encrypt2(uchar*, uchar*, ulong, Chachastate*);
+
+void	ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
+int	ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
+
+/*
+ * Salsa definitions
+ */
+enum
+{
+	SalsaBsize=	64,
+	SalsaKeylen=	256/8,
+	SalsaIVlen=	64/8,
+	XSalsaIVlen=	192/8,
+};
+
+typedef struct Salsastate Salsastate;
+struct Salsastate
+{
+	u32int	input[16];
+	u32int	key[8];
+	int	rounds;
+	int	ivwords;
+};
+
+void	setupSalsastate(Salsastate*, uchar*, ulong, uchar*, ulong, int);
+void	salsa_setiv(Salsastate*, uchar*);
+void	salsa_setblock(Salsastate*, u64int);
+void	salsa_encrypt(uchar*, ulong, Salsastate*);
+void	salsa_encrypt2(uchar*, uchar*, ulong, Salsastate*);
+
+void	hsalsa(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds);
+
+/*
+ * DES definitions
+ */
+
+enum
+{
+	DESbsize=	8
+};
+
+/* single des */
+typedef struct DESstate DESstate;
+struct DESstate
+{
+	ulong	setup;
+	uchar	key[8];		/* unexpanded key */
+	ulong	expanded[32];	/* expanded key */
+	uchar	ivec[8];	/* initialization vector */
+};
+
+void	setupDESstate(DESstate *s, uchar key[8], uchar *ivec);
+void	des_key_setup(uchar[8], ulong[32]);
+void	block_cipher(ulong*, uchar*, int);
+void	desCBCencrypt(uchar*, int, DESstate*);
+void	desCBCdecrypt(uchar*, int, DESstate*);
+void	desECBencrypt(uchar*, int, DESstate*);
+void	desECBdecrypt(uchar*, int, DESstate*);
+
+/* for backward compatibility with 7-byte DES key format */
+void	des56to64(uchar *k56, uchar *k64);
+void	des64to56(uchar *k64, uchar *k56);
+void	key_setup(uchar[7], ulong[32]);
+
+/* triple des encrypt/decrypt orderings */
+enum {
+	DES3E=		0,
+	DES3D=		1,
+	DES3EEE=	0,
+	DES3EDE=	2,
+	DES3DED=	5,
+	DES3DDD=	7
+};
+
+typedef struct DES3state DES3state;
+struct DES3state
+{
+	ulong	setup;
+	uchar	key[3][8];		/* unexpanded key */
+	ulong	expanded[3][32];	/* expanded key */
+	uchar	ivec[8];		/* initialization vector */
+};
+
+void	setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec);
+void	triple_block_cipher(ulong keys[3][32], uchar*, int);
+void	des3CBCencrypt(uchar*, int, DES3state*);
+void	des3CBCdecrypt(uchar*, int, DES3state*);
+void	des3ECBencrypt(uchar*, int, DES3state*);
+void	des3ECBdecrypt(uchar*, int, DES3state*);
+
+/*
+ * digests
+ */
+
+enum
+{
+	SHA1dlen=	20,	/* SHA digest length */
+	SHA2_224dlen=	28,	/* SHA-224 digest length */
+	SHA2_256dlen=	32,	/* SHA-256 digest length */
+	SHA2_384dlen=	48,	/* SHA-384 digest length */
+	SHA2_512dlen=	64,	/* SHA-512 digest length */
+	MD4dlen=	16,	/* MD4 digest length */
+	MD5dlen=	16,	/* MD5 digest length */
+	Poly1305dlen=	16,	/* Poly1305 digest length */
+
+	Hmacblksz	= 64,	/* in bytes; from rfc2104 */
+};
+
+typedef struct DigestState DigestState;
+struct DigestState
+{
+	uvlong	len;
+	union {
+		u32int	state[16];
+		u64int	bstate[8];
+	};
+	uchar	buf[256];
+	int	blen;
+	char	malloced;
+	char	seeded;
+};
+typedef struct DigestState SHAstate;	/* obsolete name */
+typedef struct DigestState SHA1state;
+typedef struct DigestState SHA2_224state;
+typedef struct DigestState SHA2_256state;
+typedef struct DigestState SHA2_384state;
+typedef struct DigestState SHA2_512state;
+typedef struct DigestState MD5state;
+typedef struct DigestState MD4state;
+
+DigestState*	md4(uchar*, ulong, uchar*, DigestState*);
+DigestState*	md5(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha1(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_224(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_256(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_384(uchar*, ulong, uchar*, DigestState*);
+DigestState*	sha2_512(uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_x(uchar *p, ulong len, uchar *key, ulong klen,
+			uchar *digest, DigestState *s,
+			DigestState*(*x)(uchar*, ulong, uchar*, DigestState*),
+			int xlen);
+DigestState*	hmac_md5(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha1(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_224(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_256(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_384(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+DigestState*	hmac_sha2_512(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+char*		md5pickle(MD5state*);
+MD5state*	md5unpickle(char*);
+char*		sha1pickle(SHA1state*);
+SHA1state*	sha1unpickle(char*);
+
+DigestState*	poly1305(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+
+/*
+ * random number generation
+ */
+void	genrandom(uchar *buf, int nbytes);
+void	prng(uchar *buf, int nbytes);
+ulong	fastrand(void);
+ulong	nfastrand(ulong);
+
+/*
+ * primes
+ */
+void	genprime(mpint *p, int n, int accuracy); /* generate n-bit probable prime */
+void	gensafeprime(mpint *p, mpint *alpha, int n, int accuracy); /* prime & generator */
+void	genstrongprime(mpint *p, int n, int accuracy); /* generate n-bit strong prime */
+void	DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen]);
+int	probably_prime(mpint *n, int nrep);	/* miller-rabin test */
+int	smallprimetest(mpint *p);  /* returns -1 if not prime, 0 otherwise */
+
+/*
+ * rc4
+ */
+typedef struct RC4state RC4state;
+struct RC4state
+{
+	 uchar	state[256];
+	 uchar	x;
+	 uchar	y;
+};
+
+void	setupRC4state(RC4state*, uchar*, int);
+void	rc4(RC4state*, uchar*, int);
+void	rc4skip(RC4state*, int);
+void	rc4back(RC4state*, int);
+
+/*
+ * rsa
+ */
+typedef struct RSApub RSApub;
+typedef struct RSApriv RSApriv;
+typedef struct PEMChain PEMChain;
+
+/* public/encryption key */
+struct RSApub
+{
+	mpint	*n;	/* modulus */
+	mpint	*ek;	/* exp (encryption key) */
+};
+
+/* private/decryption key */
+struct RSApriv
+{
+	RSApub	pub;
+
+	mpint	*dk;	/* exp (decryption key) */
+
+	/* precomputed values to help with chinese remainder theorem calc */
+	mpint	*p;
+	mpint	*q;
+	mpint	*kp;	/* dk mod p-1 */
+	mpint	*kq;	/* dk mod q-1 */
+	mpint	*c2;	/* (inv p) mod q */
+};
+
+struct PEMChain{
+	PEMChain*next;
+	uchar	*pem;
+	int	pemlen;
+};
+
+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);
+mpint*		rsadecrypt(RSApriv *k, mpint *in, mpint *out);
+RSApub*		rsapuballoc(void);
+void		rsapubfree(RSApub*);
+RSApriv*	rsaprivalloc(void);
+void		rsaprivfree(RSApriv*);
+RSApub*		rsaprivtopub(RSApriv*);
+RSApub*		X509toRSApub(uchar*, int, char*, int);
+RSApriv*	asn1toRSApriv(uchar*, int);
+void		asn1dump(uchar *der, int len);
+uchar*		decodePEM(char *s, char *type, int *len, char **new_s);
+PEMChain*	decodepemchain(char *s, char *type);
+uchar*		X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen);
+uchar*		X509rsareq(RSApriv *priv, char *subj, int *certlen);
+char*		X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk);
+char*		X509rsaverify(uchar *cert, int ncert, RSApub *pk);
+
+void		X509dump(uchar *cert, int ncert);
+
+/*
+ * elgamal
+ */
+typedef struct EGpub EGpub;
+typedef struct EGpriv EGpriv;
+typedef struct EGsig EGsig;
+
+/* public/encryption key */
+struct EGpub
+{
+	mpint	*p;	/* modulus */
+	mpint	*alpha;	/* generator */
+	mpint	*key;	/* (encryption key) alpha**secret mod p */
+};
+
+/* private/decryption key */
+struct EGpriv
+{
+	EGpub	pub;
+	mpint	*secret;	/* (decryption key) */
+};
+
+/* signature */
+struct EGsig
+{
+	mpint	*r, *s;
+};
+
+EGpriv*		eggen(int nlen, int rounds);
+mpint*		egencrypt(EGpub *k, mpint *in, mpint *out);	/* deprecated */
+mpint*		egdecrypt(EGpriv *k, mpint *in, mpint *out);
+EGsig*		egsign(EGpriv *k, mpint *m);
+int		egverify(EGpub *k, EGsig *sig, mpint *m);
+EGpub*		egpuballoc(void);
+void		egpubfree(EGpub*);
+EGpriv*		egprivalloc(void);
+void		egprivfree(EGpriv*);
+EGsig*		egsigalloc(void);
+void		egsigfree(EGsig*);
+EGpub*		egprivtopub(EGpriv*);
+
+/*
+ * dsa
+ */
+typedef struct DSApub DSApub;
+typedef struct DSApriv DSApriv;
+typedef struct DSAsig DSAsig;
+
+/* public/encryption key */
+struct DSApub
+{
+	mpint	*p;	/* modulus */
+	mpint	*q;	/* group order, q divides p-1 */
+	mpint	*alpha;	/* group generator */
+	mpint	*key;	/* (encryption key) alpha**secret mod p */
+};
+
+/* private/decryption key */
+struct DSApriv
+{
+	DSApub	pub;
+	mpint	*secret;	/* (decryption key) */
+};
+
+/* signature */
+struct DSAsig
+{
+	mpint	*r, *s;
+};
+
+DSApriv*	dsagen(DSApub *opub);	/* opub not checked for consistency! */
+DSAsig*		dsasign(DSApriv *k, mpint *m);
+int		dsaverify(DSApub *k, DSAsig *sig, mpint *m);
+DSApub*		dsapuballoc(void);
+void		dsapubfree(DSApub*);
+DSApriv*	dsaprivalloc(void);
+void		dsaprivfree(DSApriv*);
+DSAsig*		dsasigalloc(void);
+void		dsasigfree(DSAsig*);
+DSApub*		dsaprivtopub(DSApriv*);
+DSApriv*	asn1toDSApriv(uchar*, int);
+
+/*
+ * TLS
+ */
+typedef struct Thumbprint{
+	struct Thumbprint *next;
+	uchar	sha1[SHA1dlen];
+} Thumbprint;
+
+typedef struct TLSconn{
+	char	dir[40];	/* connection directory */
+	uchar	*cert;	/* certificate (local on input, remote on output) */
+	uchar	*sessionID;
+	uchar	*psk;
+	int	certlen;
+	int	sessionIDlen;
+	int	psklen;
+	int	(*trace)(char*fmt, ...);
+	PEMChain*chain;	/* optional extra certificate evidence for servers to present */
+	char	*sessionType;
+	uchar	*sessionKey;
+	int	sessionKeylen;
+	char	*sessionConst;
+	char	*serverName;
+	char	*pskID;
+} TLSconn;
+
+/* tlshand.c */
+int tlsClient(int fd, TLSconn *c);
+int tlsServer(int fd, TLSconn *c);
+
+/* thumb.c */
+Thumbprint* initThumbprints(char *ok, char *crl);
+void	freeThumbprints(Thumbprint *ok);
+int	okThumbprint(uchar *sha1, Thumbprint *ok);
+
+/* readcert.c */
+uchar	*readcert(char *filename, int *pcertlen);
+PEMChain*readcertchain(char *filename);
+
+/* aes_xts.c */
+int aes_xts_encrypt(ulong tweak[], ulong ecb[],  vlong sectorNumber, uchar *input, uchar *output, ulong len) ;
+int aes_xts_decrypt(ulong tweak[], ulong ecb[], vlong sectorNumber, uchar *input, uchar *output, ulong len);
+
+typedef struct ECpoint{
+	int inf;
+	mpint *x;
+	mpint *y;
+} ECpoint;
+
+typedef ECpoint ECpub;
+typedef struct ECpriv{
+	ECpoint;
+	mpint *d;
+} ECpriv;
+
+typedef struct ECdomain{
+	mpint *p;
+	mpint *a;
+	mpint *b;
+	ECpoint G;
+	mpint *n;
+	mpint *h;
+} ECdomain;
+
+void	ecdominit(ECdomain *, void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h));
+void	ecdomfree(ECdomain *);
+
+void	ecassign(ECdomain *, ECpoint *old, ECpoint *new);
+void	ecadd(ECdomain *, ECpoint *a, ECpoint *b, ECpoint *s);
+void	ecmul(ECdomain *, ECpoint *a, mpint *k, ECpoint *s);
+ECpoint*	strtoec(ECdomain *, char *, char **, ECpoint *);
+ECpriv*	ecgen(ECdomain *, ECpriv*);
+int	ecverify(ECdomain *, ECpoint *);
+int	ecpubverify(ECdomain *, ECpub *);
+void	ecdsasign(ECdomain *, ECpriv *, uchar *, int, mpint *, mpint *);
+int	ecdsaverify(ECdomain *, ECpub *, uchar *, int, mpint *, mpint *);
+void	base58enc(uchar *, char *, int);
+int	base58dec(char *, uchar *, int);
+
+ECpub*	ecdecodepub(ECdomain *dom, uchar *, int);
+int	ecencodepub(ECdomain *dom, ECpub *, uchar *, int);
+void	ecpubfree(ECpub *);
+
+ECpub*	X509toECpub(uchar *cert, int ncert, ECdomain *dom);
+char*	X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub);
+char*	X509ecdsaverify(uchar *sig, int siglen, ECdomain *dom, ECpub *pub);
+
+/* curves */
+void	secp256r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+void	secp256k1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+
+DigestState*	ripemd160(uchar *, ulong, uchar *, DigestState *);
+
+/*
+ * Diffie-Hellman key exchange
+ */
+
+typedef struct DHstate DHstate;
+struct DHstate
+{
+	mpint	*g;	/* base g */
+	mpint	*p;	/* large prime */
+	mpint	*q;	/* subgroup prime */
+	mpint	*x;	/* random secret */
+	mpint	*y;	/* public key y = g**x % p */
+};
+
+/* generate new public key: y = g**x % p */
+mpint* dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g);
+
+/* calculate shared key: k = y**x % p */
+mpint* dh_finish(DHstate *dh, mpint *y);
+
+/* Curve25519 elliptic curve, public key function */
+void curve25519(uchar mypublic[32], uchar secret[32], uchar basepoint[32]);
+
+/* Curve25519 diffie hellman */
+void curve25519_dh_new(uchar x[32], uchar y[32]);
+void curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]);
+
+/* password-based key derivation function 2 (rfc2898) */
+void pbkdf2_x(uchar *p, ulong plen, uchar *s, ulong slen, ulong rounds, uchar *d, ulong dlen,
+	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen);
+
+/* hmac-based key derivation function (rfc5869) */
+void hkdf_x(uchar *salt, ulong nsalt, uchar *info, ulong ninfo, uchar *key, ulong nkey, uchar *d, ulong dlen,
+	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen);
+
+/* timing safe memcmp() */
+int tsmemcmp(void*, void*, ulong);
+
+#endif
--- /dev/null
+++ b/sys/include/ape/mp.h
@@ -1,0 +1,196 @@
+#ifndef _PLAN9_SOURCE
+  This header file is an extension to ANSI/POSIX
+#endif
+
+#ifndef __LIBMP_H_
+#define __LIBMP_H_
+
+#pragma	src	"/sys/src/ape/lib/mp"
+#pragma	lib	"/$M/lib/ape/libmp.a"
+
+#include <u.h>
+#include <fmt.h>
+
+typedef unsigned int	mpdigit;	/* from /$objtype/include/u.h */
+
+#define _MPINT 1
+
+/*
+ * the code assumes mpdigit to be at least an int
+ * mpdigit must be an atomic type.  mpdigit is defined
+ * in the architecture specific u.h
+ */
+typedef struct mpint mpint;
+
+struct mpint
+{
+	int	sign;	/* +1 or -1 */
+	int	size;	/* allocated digits */
+	int	top;	/* significant digits */
+	mpdigit	*p;
+	char	flags;
+};
+
+enum
+{
+	MPstatic=	0x01,	/* static constant */
+	MPnorm=		0x02,	/* normalization status */
+	MPtimesafe=	0x04,	/* request time invariant computation */
+	MPfield=	0x08,	/* this mpint is a field modulus */
+
+	Dbytes=		sizeof(mpdigit),	/* bytes per digit */
+	Dbits=		Dbytes*8		/* bits per digit */
+};
+
+/* allocation */
+void	mpsetminbits(int n);	/* newly created mpint's get at least n bits */
+mpint*	mpnew(int n);		/* create a new mpint with at least n bits */
+void	mpfree(mpint *b);
+void	mpbits(mpint *b, int n);	/* ensure that b has at least n bits */
+mpint*	mpnorm(mpint *b);		/* dump leading zeros */
+mpint*	mpcopy(mpint *b);
+void	mpassign(mpint *old, mpint *new);
+
+/* random bits */
+mpint*	mprand(int bits, void (*gen)(uchar*, int), mpint *b);
+/* return uniform random [0..n-1] */
+mpint*	mpnrand(mpint *n, void (*gen)(uchar*, int), mpint *b);
+
+/* conversion */
+mpint*	strtomp(char*, char**, int, mpint*);	/* ascii */
+int	mpfmt(Fmt*);
+char*	mptoa(mpint*, int, char*, int);
+mpint*	letomp(uchar*, uint, mpint*);	/* byte array, little-endian */
+int	mptole(mpint*, uchar*, uint, uchar**);
+void	mptolel(mpint *b, uchar *p, int n);
+mpint*	betomp(uchar*, uint, mpint*);	/* byte array, big-endian */
+int	mptobe(mpint*, uchar*, uint, uchar**);
+void	mptober(mpint *b, uchar *p, int n);
+uint	mptoui(mpint*);			/* unsigned int */
+mpint*	uitomp(uint, mpint*);
+int	mptoi(mpint*);			/* int */
+mpint*	itomp(int, mpint*);
+uvlong	mptouv(mpint*);			/* unsigned vlong */
+mpint*	uvtomp(uvlong, mpint*);
+vlong	mptov(mpint*);			/* vlong */
+mpint*	vtomp(vlong, mpint*);
+
+/* divide 2 digits by one */
+void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
+
+/* in the following, the result mpint may be */
+/* the same as one of the inputs. */
+void	mpadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpsub(mpint *b1, mpint *b2, mpint *diff);	/* diff = b1-b2 */
+void	mpleft(mpint *b, int shift, mpint *res);	/* res = b<<shift */
+void	mpright(mpint *b, int shift, mpint *res);	/* res = b>>shift */
+void	mpmul(mpint *b1, mpint *b2, mpint *prod);	/* prod = b1*b2 */
+void	mpexp(mpint *b, mpint *e, mpint *m, mpint *res);	/* res = b**e mod m */
+void	mpmod(mpint *b, mpint *m, mpint *remainder);	/* remainder = b mod m */
+
+/* logical operations */
+void	mpand(mpint *b1, mpint *b2, mpint *res);
+void	mpbic(mpint *b1, mpint *b2, mpint *res);
+void	mpor(mpint *b1, mpint *b2, mpint *res);
+void	mpnot(mpint *b, mpint *res);
+void	mpxor(mpint *b1, mpint *b2, mpint *res);
+void	mptrunc(mpint *b, int n, mpint *res);
+void	mpxtend(mpint *b, int n, mpint *res);
+
+/* modular arithmetic, time invariant when 0≤b1≤m-1 and 0≤b2≤m-1 */
+void	mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum);	/* sum = b1+b2 % m */
+void	mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff);	/* diff = b1-b2 % m */
+void	mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod);	/* prod = b1*b2 % m */
+
+/* quotient = dividend/divisor, remainder = dividend % divisor */
+void	mpdiv(mpint *dividend, mpint *divisor,  mpint *quotient, mpint *remainder);
+
+/* return neg, 0, pos as b1-b2 is neg, 0, pos */
+int	mpcmp(mpint *b1, mpint *b2);
+
+/* res = s != 0 ? b1 : b2 */
+void	mpsel(int s, mpint *b1, mpint *b2, mpint *res);
+
+/* extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d */
+void	mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y);
+
+/* res = b**-1 mod m */
+void	mpinvert(mpint *b, mpint *m, mpint *res);
+
+/* bit counting */
+int	mpsignif(mpint*);	/* number of sigificant bits in mantissa */
+int	mplowbits0(mpint*);	/* k, where n = 2**k * q for odd q */
+
+/* well known constants */
+extern mpint	*mpzero, *mpone, *mptwo;
+
+/* sum[0:alen] = a[0:alen-1] + b[0:blen-1] */
+/* prereq: alen >= blen, sum has room for alen+1 digits */
+void	mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum);
+
+/* diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] */
+/* prereq: alen >= blen, diff has room for alen digits */
+void	mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff);
+
+/* p[0:n] += m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
+void	mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p);
+
+/* p[0:n] -= m * b[0:n-1] */
+/* prereq: p has room for n+1 digits */
+int	mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p);
+
+/* p[0:alen+blen-1] = a[0:alen-1] * b[0:blen-1] */
+/* prereq: alen >= blen, p has room for m*n digits */
+void	mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+void	mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
+
+/* sign of a - b or zero if the same */
+int	mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen);
+int	mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen);
+
+/* divide the 2 digit dividend by the one digit divisor and stick in quotient */
+/* we assume that the result is one digit - overflow is all 1's */
+void	mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
+
+/* playing with magnitudes */
+int	mpmagcmp(mpint *b1, mpint *b2);
+void	mpmagadd(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+void	mpmagsub(mpint *b1, mpint *b2, mpint *sum);	/* sum = b1+b2 */
+
+/* chinese remainder theorem */
+typedef struct CRTpre	CRTpre;		/* precomputed values for converting */
+					/*  twixt residues and mpint */
+typedef struct CRTres	CRTres;		/* residue form of an mpint */
+
+#pragma incomplete CRTpre
+
+struct CRTres
+{
+	int	n;		/* number of residues */
+	mpint	*r[1];		/* residues */
+};
+
+CRTpre*	crtpre(int, mpint**);			/* precompute conversion values */
+CRTres*	crtin(CRTpre*, mpint*);			/* convert mpint to residues */
+void	crtout(CRTpre*, CRTres*, mpint*);	/* convert residues to mpint */
+void	crtprefree(CRTpre*);
+void	crtresfree(CRTres*);
+
+/* fast field arithmetic */
+typedef struct Mfield	Mfield;
+
+struct Mfield
+{
+	mpint;
+	int	(*reduce)(Mfield*, mpint*, mpint*);
+};
+
+mpint *mpfield(mpint*);
+
+Mfield *gmfield(mpint*);
+Mfield *cnfield(mpint*);
+
+#pragma	varargck	type	"B"	mpint*
+
+#endif
--- a/sys/include/ape/u.h
+++ b/sys/include/ape/u.h
@@ -16,4 +16,9 @@
 typedef 	union FPdbleword FPdbleword;
 typedef	char*	p9va_list;
 
+typedef uchar	u8int;
+typedef ushort	u16int;
+typedef ulong	u32int;
+typedef uvlong	u64int;
+
 #endif
--- /dev/null
+++ b/sys/src/ape/lib/9/ctime.c
@@ -1,0 +1,22 @@
+#include "libc.h"
+
+#undef gmtime
+
+Tm*
+_gmtime(time_t t)
+{
+	static Tm r;
+	struct tm *p;
+
+	p = gmtime(&t);
+	r.sec = p->tm_sec;
+	r.min = p->tm_min;
+	r.hour = p->tm_hour;
+	r.mday = p->tm_mday;
+	r.mon = p->tm_mon;
+	r.year = p->tm_year;
+	r.wday = p->tm_wday;
+	r.yday = p->tm_yday;
+	strcpy(r.zone, "GMT");
+	return &r;
+}
--- a/sys/src/ape/lib/9/libc.h
+++ b/sys/src/ape/lib/9/libc.h
@@ -1,6 +1,11 @@
 #define _LOCK_EXTENSION
 #define _QLOCK_EXTENSION
 #define _BSD_EXTENSION
+
+#ifdef _NET_EXTENSION
+#include <libnet.h>
+#endif
+
 #include <stdint.h>
 #include <sys/types.h>
 #include <lock.h>
@@ -15,6 +20,7 @@
 #include <utf.h>
 #include <fmt.h>
 #include <signal.h>
+#include <time.h>
 
 #define	nelem(x)	(sizeof(x)/sizeof((x)[0]))
 
@@ -54,6 +60,17 @@
 void _nulldir(Dir*);
 uint _sizeD2M(Dir*);
 
+#define convM2D	_convM2D
+#define convD2M	_convD2M
+#define dirstat _dirstat
+#define dirwstat _dirwstat
+#define dirfstat _dirfstat
+#define dirfwstat _dirfwstat
+#define dirread _dirread
+#define dirreadall _dirreadall
+#define nulldir _nulldir
+#define sizeD2M _sizeD2M
+
 typedef
 struct Waitmsg
 {
@@ -62,7 +79,6 @@
 	char	*msg;
 } Waitmsg;
 
-
 extern	int	_AWAIT(char*, int);
 extern	int	_ALARM(unsigned long);
 extern	int	_BIND(const char*, const char*, int);
@@ -106,13 +122,14 @@
 extern	int	_IOUNIT(int);
 extern	vlong	_NSEC(void);
 
-#define dirstat _dirstat
-#define dirfstat _dirfstat
-
 #define OREAD 0
 #define OWRITE 1
 #define ORDWR 2
-#define OCEXEC 32
+#define	OEXEC	3	/* execute, == read but check execute permission */
+#define	OTRUNC	16	/* or'ed in (except for exec), truncate file first */
+#define	OCEXEC	32	/* or'ed in, close on exec */
+#define	ORCLOSE	64	/* or'ed in, remove on close */
+#define	OEXCL	0x1000	/* or'ed in, exclusive use (create only) */
 
 #define AREAD 4
 #define AWRITE 2
@@ -125,6 +142,8 @@
 #define create(file, omode, perm) open(file, (omode) |O_CREAT | O_TRUNC, perm)
 #define seek(fd, off, dir) lseek(fd, off, dir)
 
+#define fauth _FAUTH
+#define wait _WAIT
 #define readn _READN
 #define pread _PREAD
 #define pwrite _PWRITE
@@ -132,11 +151,15 @@
 #define nsec	_NSEC
 #define iounit	_IOUNIT
 
+#define getwd(buf,len)	getcwd(buf,len)
 #define postnote(who,pid,note)	kill(pid,SIGTERM)
 #define atnotify(func,in)
 
 #define ERRMAX 128
 
+int errstr(char*, unsigned int);
+extern void sysfatal(char*, ...);
+
 extern	void		setmalloctag(void*, uintptr_t);
 extern	void		setrealloctag(void*, uintptr_t);
 extern	uintptr_t	getcallerpc(void*);
@@ -148,6 +171,29 @@
 extern int  dec64(uchar *, int, char *, int);
 extern int  enc64(char *, int, uchar *, int);
 
-extern	int tokenize(char*, char**, int);
-extern void sysfatal(char*, ...);
-extern	ulong truerand(void); /* uses /dev/random */
+extern int tokenize(char*, char**, int);
+extern int getfields(char*, char**, int, int, char*);
+extern int gettokens(char*, char**, int, char*);
+
+extern ulong truerand(void); /* uses /dev/random */
+
+extern int encrypt(void*, void*, int len);
+extern int decrypt(void*, void*, int len);
+
+typedef
+struct Tm
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	mday;
+	int	mon;
+	int	year;
+	int	wday;
+	int	yday;
+	char	zone[4];
+	int	tzoff;
+} Tm;
+
+Tm* _gmtime(time_t);
+#define gmtime _gmtime
--- a/sys/src/ape/lib/9/mkfile
+++ b/sys/src/ape/lib/9/mkfile
@@ -2,11 +2,15 @@
 <$APE/config
 
 LIB=/$objtype/lib/ape/lib9.a
-OFILES=argv0.$O\
-	errstr.$O\
+OFILES=\
+	argv0.$O\
 	bind.$O\
+	crypt.$O\
+	ctime.$O\
+	errstr.$O\
 	getcallerpc.$O\
 	getfcr.$O\
+	getfields.$O\
 	mount.$O\
 	rendezvous.$O\
 	rfork.$O\
@@ -40,8 +44,14 @@
 sysfatal.$O: ../../../libc/9sys/sysfatal.c
 	$CC $CFLAGS -I. ../../../libc/9sys/sysfatal.c
 
+getfields.$O: ../../../libc/port/getfields.c
+	$CC $CFLAGS -I. ../../../libc/port/getfields.c
+
 tokenize.$O: ../../../libc/port/tokenize.c
 	$CC $CFLAGS -I. ../../../libc/port/tokenize.c
+
+crypt.$O: ../../../libc/port/crypt.c
+	$CC $CFLAGS -I. ../../../libc/port/crypt.c
 
 truerand.$O: ../../../libc/9sys/truerand.c
 	$CC $CFLAGS -I. ../../../libc/9sys/truerand.c
--- /dev/null
+++ b/sys/src/ape/lib/auth/authsrv.h
@@ -1,0 +1,45 @@
+enum
+{
+	ANAMELEN=	28,	/* name max size in previous proto */
+	AERRLEN=	64,	/* errstr max size in previous proto */
+	DOMLEN=		48,	/* authentication domain name length */
+	DESKEYLEN=	7,	/* encrypt/decrypt des key length */
+	AESKEYLEN=	16,	/* encrypt/decrypt aes key length */
+
+	CHALLEN=	8,	/* plan9 sk1 challenge length */
+	NETCHLEN=	16,	/* max network challenge length (used in AS protocol) */
+	CONFIGLEN=	14,
+	SECRETLEN=	32,	/* secret max size */
+
+	NONCELEN=	32,
+
+	KEYDBOFF=	8,	/* bytes of random data at key file's start */
+	OKEYDBLEN=	ANAMELEN+DESKEYLEN+4+2,	/* old key file entry length */
+	KEYDBLEN=	OKEYDBLEN+SECRETLEN,	/* key file entry length */
+	OMD5LEN=	16,
+
+	/* AuthPAK constants */
+	PAKKEYLEN=	32,
+	PAKSLEN=	(448+7)/8,	/* ed448 scalar */
+	PAKPLEN=	4*PAKSLEN,	/* point in extended format X,Y,Z,T */
+	PAKHASHLEN=	2*PAKPLEN,	/* hashed points PM,PN */
+	PAKXLEN=	PAKSLEN,	/* random scalar secret key */ 
+	PAKYLEN=	PAKSLEN,	/* decaf encoded public key */
+};
+
+typedef struct	Authkey		Authkey;
+struct	Authkey
+{
+	char	des[DESKEYLEN];		/* DES key from password */
+	uchar	aes[AESKEYLEN];		/* AES key from password */
+	uchar	pakkey[PAKKEYLEN];	/* shared key from AuthPAK exchange (see authpak_finish()) */
+	uchar	pakhash[PAKHASHLEN];	/* secret hash from AES key and user name (see authpak_hash()) */
+};
+
+/*
+ *  convert ascii password to auth key
+ */
+extern	void	passtokey(Authkey*, char*);
+
+extern	void	passtodeskey(char key[DESKEYLEN], char *p);
+extern	void	passtoaeskey(uchar key[AESKEYLEN], char *p);
--- /dev/null
+++ b/sys/src/ape/lib/auth/fcall.h
@@ -1,0 +1,20 @@
+#define	VERSION9P	"9P2000"
+#define	MAXWELEM	16
+
+#define	GBIT8(p)	((p)[0])
+#define	GBIT16(p)	((p)[0]|((p)[1]<<8))
+#define	GBIT32(p)	((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define	GBIT64(p)	((u32int)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\
+				((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32))
+
+#define	PBIT8(p,v)	(p)[0]=(v)
+#define	PBIT16(p,v)	(p)[0]=(v);(p)[1]=(v)>>8
+#define	PBIT32(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define	PBIT64(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\
+			(p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56
+
+#define	BIT8SZ		1
+#define	BIT16SZ		2
+#define	BIT32SZ		4
+#define	BIT64SZ		8
+#define	QIDSZ	(BIT8SZ+BIT32SZ+BIT64SZ)
--- /dev/null
+++ b/sys/src/ape/lib/auth/mkfile
@@ -1,0 +1,43 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libauth.a
+OFILES=\
+	amount.$O\
+	amount_getkey.$O\
+	attr.$O\
+	auth_attr.$O\
+	auth_challenge.$O\
+	auth_chuid.$O\
+	auth_getkey.$O\
+	auth_getuserpasswd.$O\
+	auth_proxy.$O\
+	auth_respond.$O\
+	auth_rpc.$O\
+	auth_userpasswd.$O\
+	auth_wep.$O\
+	login.$O\
+	newns.$O\
+	noworld.$O\
+	passtokey.$O\
+
+HFILES=\
+	/sys/include/ape/auth.h\
+	/sys/src/libauth/authlocal.h\
+	../9/libc.h
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+	${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-TVwc -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_NET_EXTENSION -I. -I../9 -I/sys/src/libauth
+
+%.$O:	/sys/src/libauth/%.c
+	$CC $CFLAGS /sys/src/libauth/$stem.c
+
+passtokey.$O:	/sys/src/libauthsrv/passtokey.c
+	$CC $CFLAGS /sys/src/libauthsrv/passtokey.c
--- /dev/null
+++ b/sys/src/ape/lib/bio/mkfile
@@ -1,0 +1,38 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libbio.a
+OFILES=\
+	bbuffered.$O\
+	bfildes.$O\
+	bflush.$O\
+	bgetrune.$O\
+	bgetc.$O\
+#	bgetd.$O\
+	binit.$O\
+	blethal.$O\
+	boffset.$O\
+	bprint.$O\
+	bputrune.$O\
+	bputc.$O\
+	brdline.$O\
+	brdstr.$O\
+	bread.$O\
+	bseek.$O\
+	bwrite.$O\
+	bvprint.$O\
+
+HFILES=/sys/include/ape/bio.h
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+	${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-TVwc -D_PLAN9_SOURCE -D_POSIX_SOURCE -I. -I../9
+
+%.$O:	/sys/src/libbio/%.c
+	$CC $CFLAGS /sys/src/libbio/$stem.c
--- /dev/null
+++ b/sys/src/ape/lib/mp/386/mkfile
@@ -1,0 +1,26 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+SFILES=\
+	mpvecadd.s\
+	mpvecdigmuladd.s\
+	mpvecdigmulsub.s\
+	mpvecsub.s\
+	mpdigdiv.s\
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	../../../../libmp/386/%.s
+	$AS ../../../../libmp/386/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/mp/alpha/mkfile
@@ -1,0 +1,15 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=\
+
+UPDATE=mkfile\
+	$HFILES\
+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/sys/src/ape/lib/mp/amd64/mkfile
@@ -1,0 +1,26 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+SFILES=\
+	mpvecadd.s\
+	mpvecdigmuladd.s\
+	mpvecdigmulsub.s\
+	mpvecsub.s\
+	mpdigdiv.s\
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	../../../../libmp/amd64/%.s
+	$AS ../../../../libmp/amd64/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/mp/arm/mkfile
@@ -1,0 +1,21 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+SFILES=mpvecdigmuladd.s mpvecdigmulsub.s mpvecadd.s mpvecsub.s
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	../../../../libmp/arm/%.s
+	$AS ../../../../libmp/arm/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/mp/mips/mkfile
@@ -1,0 +1,26 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+SFILES=\
+	mpvecadd.s\
+	mpvecsub.s\
+	mpvecdigmuladd.s\
+	mpvecdigmulsub.s\
+#	mpdigdiv.s\
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	../../../../libmp/mips/%.s
+	$AS ../../../../libmp/mips/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/mp/mkfile
@@ -1,0 +1,54 @@
+APE=/sys/src/ape
+<$APE/config
+
+DIRS=port $CPUS
+
+default:V:	all
+
+install all:V:
+	for(i in port $objtype)@{
+		echo $i
+		cd $i
+		mk $MKFLAGS $target
+	}
+
+nuke:V: clean
+	rm -f /$objtype/lib/ape/libmp.a
+
+clean:V:
+	for(i in $DIRS)@{
+		echo $i
+		cd $i
+		mk $MKFLAGS $target
+	}
+
+installall:V:
+	for(objtype in $CPUS) mk $MKFLAGS install
+
+everything:V:
+	rm -f */*.[$OS]
+	for(objtype in 386)@{
+		echo $objtype
+		mk $MKFLAGS install
+	}
+	rm -f */*.[$OS]
+
+test.$O: ../../../libmp/test.c /sys/include/ape/mp.h ../../../libmp/port/dat.h
+	$CC -c -D_POSIX_SOURCE -D_PLAN9_SOURCE -I../9 -I../../../libmp/port ../../../libmp/test.c
+
+$O.test: test.$O /$objtype/lib/ape/libmp.a
+	$LD -o $O.test test.$O
+
+bigtest.$O: ../../../libmp/bigtest.c /sys/include/ape/mp.h ../../../libmp/port/dat.h
+	$CC -c -D_POSIX_SOURCE -D_PLAN9_SOURCE -I../9 -I../../../libmp/port ../../../libmp/bigtest.c
+
+$O.bigtest: bigtest.$O /$objtype/lib/ape/libmp.a
+	$LD -o $O.bigtest bigtest.$O
+
+allout:
+	objtype=386; mk; mk 8.test 8.bigtest
+	objtype=amd64; mk; mk 6.test 6.bigtest
+	objtype=arm; mk; mk 5.test 5.bigtest
+
+cleanout:
+	rm -f [568].* *.[568]
--- /dev/null
+++ b/sys/src/ape/lib/mp/port/mkfile
@@ -1,0 +1,72 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+FILES=\
+	mpaux\
+	mpfmt\
+	strtomp\
+	mptobe\
+	mptober\
+	mptole\
+	mptolel\
+	betomp\
+	letomp\
+	mpadd\
+	mpsub\
+	mpcmp\
+	mpsel\
+	mpfactorial\
+	mpmul\
+	mpleft\
+	mpright\
+	mpvecadd\
+	mpvecsub\
+	mpvecdigmuladd\
+	mpveccmp\
+	mpvectscmp\
+	mpdigdiv\
+	mpdiv\
+	mpexp\
+	mpmod\
+	mpmodop\
+	mpextendedgcd\
+	mpinvert\
+	mprand\
+	mpnrand\
+	crt\
+	mptoi\
+	mptoui\
+	mptov\
+	mptouv\
+	mpfield\
+	cnfield\
+	gmfield\
+	mplogic\
+
+ALLOFILES=${FILES:%=%.$O}
+
+# cull things in the per-machine directories from this list
+OFILES=	`{rfork en; \
+	if(~ $objtype spim) objtype=mips; \
+	bind -a ../../../../libmp/$objtype ../$objtype; \
+	rc ../../../../libmp/port/reduce $O $objtype $ALLOFILES}
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h\
+
+CFILES=${FILES:%=%.c}
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	$CFILES\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-TVwc -+ -D_POSIX_SOURCE -D_PLAN9_SOURCE -I. -I../../9
+
+%.$O:	../../../../libmp/port/%.c
+	$CC $CFLAGS ../../../../libmp/port/$stem.c
--- /dev/null
+++ b/sys/src/ape/lib/mp/power/mkfile
@@ -1,0 +1,25 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+SFILES=\
+	mpvecadd.s\
+	mpvecsub.s\
+	mpvecdigmuladd.s\
+	mpvecdigmulsub.s\
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	../../../../libmp/power/%.s
+	$AS ../../../../libmp/power/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/mp/spim/mkfile
@@ -1,0 +1,26 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libmp.a
+
+SFILES=\
+	mpvecadd.s\
+	mpvecsub.s\
+	mpvecdigmuladd.s\
+	mpvecdigmulsub.s\
+#	mpdigdiv.s\
+
+HFILES=\
+	/sys/include/ape/mp.h\
+	../../../../libmp/port/dat.h
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	../../../../libmp/mips/%.s
+	$AS ../../../../libmp/mips/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/386/mkfile
@@ -1,0 +1,23 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+FILES=\
+	md5block\
+	sha1block\
+
+HFILES=/sys/include/ape/libsec.h
+
+SFILES=${FILES:%=%.s}
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	/sys/src/libsec/$objtype/%.s
+	$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/alpha/mkfile
@@ -1,0 +1,15 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+OFILES=	\
+
+HFILES=/sys/include/ape/libsec.h
+
+UPDATE=mkfile
+
+</sys/src/cmd/mksyslib
+
+%.$O:	/sys/src/libsec/$objtype/%.s
+	$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/amd64/mkfile
@@ -1,0 +1,22 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+FILES=\
+	md5block\
+	sha1block\
+
+HFILES=/sys/include/ape/libsec.h
+
+SFILES=${FILES:%=%.s}
+
+OFILES=${FILES:%=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	/sys/src/libsec/$objtype/%.s
+	$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/arm/mkfile
@@ -1,0 +1,15 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+OFILES=	\
+
+HFILES=/sys/include/ape/libsec.h
+
+UPDATE=mkfile
+
+</sys/src/cmd/mksyslib
+
+%.$O:	/sys/src/libsec/$objtype/%.s
+	$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/mips/mkfile
@@ -1,0 +1,23 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+FILES=\
+	md5block\
+	sha1block\
+
+HFILES=/sys/include/ape/libsec.h
+
+SFILES=${FILES:%=%.s}
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+	$HFILES\
+	$SFILES\
+
+</sys/src/cmd/mksyslib
+
+%.$O:	/sys/src/libsec/$objtype/%.s
+	$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/mkfile
@@ -1,0 +1,46 @@
+</$objtype/mkfile
+
+DIRS=port $CPUS
+
+default:V:	all
+
+install all:V:
+	for(i in port $objtype)@{
+		echo $i
+		cd $i
+		mk $MKFLAGS $target
+	}
+
+clean:V:
+	for(i in $DIRS)@{
+		echo $i
+		cd $i
+		mk $MKFLAGS $target
+	}
+
+nuke:V: clean
+	rm -f /$objtype/lib/libsec.a
+
+update:V:
+	for(i in $DIRS)@{
+		echo $i
+		cd $i
+		mk $MKFLAGS update
+	}
+	update $UPDATEFLAGS /386/lib/libsec.a
+
+installall:V:
+	for(objtype in $CPUS) mk $MKFLAGS install
+
+everything:V:
+	rm -f */*.[$OS]
+	for(objtype in $CPUS)@{
+		echo $objtype
+		mk $MKFLAGS install
+	}
+	rm -f */*.[$OS]
+
+APE=/sys/src/ape
+<$APE/config
+$O.tlsclient: tlsclient.c
+	$CC -o $target $CFLAGS -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_NET_EXTENSION tlsclient.c
--- /dev/null
+++ b/sys/src/ape/lib/sec/port/mkfile
@@ -1,0 +1,72 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\
+	aes.c aes_gcm.c blowfish.c \
+	hmac.c md5.c md5block.c md4.c sha1.c sha1block.c\
+	sha2_64.c sha2_128.c sha2block64.c sha2block128.c\
+	sha1pickle.c md5pickle.c\
+	poly1305.c\
+	rc4.c\
+	chacha.c\
+	salsa.c\
+	genrandom.c prng.c fastrand.c nfastrand.c\
+	probably_prime.c smallprimetest.c genprime.c dsaprimes.c\
+	gensafeprime.c genstrongprime.c\
+	rsagen.c rsafill.c rsaencrypt.c rsadecrypt.c rsaalloc.c \
+	rsaprivtopub.c \
+	x509.c \
+	decodepem.c \
+	eggen.c egencrypt.c egdecrypt.c egalloc.c egprivtopub.c \
+	egsign.c egverify.c \
+	dsagen.c dsaalloc.c dsaprivtopub.c dsasign.c dsaverify.c \
+	tlshand.c \
+	thumb.c readcert.c \
+	aes_xts.c  \
+	ecc.c\
+	ripemd.c\
+	dh.c\
+	curve25519.c\
+	curve25519_dh.c\
+	pbkdf2.c\
+	hkdf.c\
+	ccpoly.c\
+	tsmemcmp.c\
+	secp256r1.c\
+	secp256k1.c\
+
+CLEANFILES=secp256r1.c secp256k1.c
+
+ALLOFILES=${CFILES:%.c=%.$O}
+
+# cull things in the per-machine directories from this list
+OFILES=	`{rfork n; \
+	bind -a ../../../../libsec/$objtype ../$objtype; \
+	rc ../../../../libsec/port/reduce $O $objtype $ALLOFILES}
+
+HFILES=/sys/include/ape/libsec.h
+
+UPDATE=mkfile\
+	$HFILES\
+	$CFILES\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-TVwc -+ -D_POSIX_SOURCE -D_PLAN9_SOURCE -I. -I../../9 -I../../../../libmp/port
+
+../../../../libsec/port/%.c:D:	../../../../libsec/port/%.mp
+	@{cd ../../../../libsec/port && mk $stem.c}
+
+%.$O:	../../../../libsec/port/%.c
+	$CC $CFLAGS ../../../../libsec/port/$stem.c
+	
+$O.rsatest: rsatest.$O
+	$LD -o $target $prereq
+
+$O.chachatest: chachatest.$O
+	$LD -o $target $prereq
+
+$O.aesgcmtest: aesgcmtest.$O
+	$LD -o $target $prereq
--- /dev/null
+++ b/sys/src/ape/lib/sec/power/mkfile
@@ -1,0 +1,15 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+OFILES=	\
+
+HFILES=/sys/include/ape/libsec.h
+
+UPDATE=mkfile
+
+</sys/src/cmd/mksyslib
+
+%.$O:	/sys/src/libsec/$objtype/%.s
+	$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
--- /dev/null
+++ b/sys/src/ape/lib/sec/spim/mkfile
@@ -1,0 +1,12 @@
+APE=/sys/src/ape
+<$APE/config
+
+LIB=/$objtype/lib/ape/libsec.a
+
+HFILES=/sys/include/ape/libsec.h
+
+OFILES=\
+
+UPDATE=mkfile $HFILES
+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/sys/src/ape/lib/sec/tlsclient.c
@@ -1,0 +1,177 @@
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <lib9.h>
+
+#include <libsec.h>
+#include <libnet.h>
+
+#include <auth.h>
+
+int debug, auth, dialfile;
+char *keyspec = "";
+char *servername, *file, *filex, *ccert;
+
+void
+sysfatal(char *fmt, ...)
+{
+	va_list a;
+
+	va_start(a, fmt);
+	vfprintf(stderr, fmt, a);
+	va_end(a);
+	fprintf(stderr, "\n");
+	exit(1);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: tlsclient [-D] [-a [-k keyspec] ] [-c lib/tls/clientcert] [-t /sys/lib/tls/xxx] [-x /sys/lib/tls/xxx.exclude] [-n servername] [-o] dialstring [cmd [args...]]\n");
+	exit(1);
+}
+
+void
+xfer(int from, int to)
+{
+	char buf[12*1024];
+	int n;
+
+	while((n = read(from, buf, sizeof buf)) > 0)
+		if(write(to, buf, n) < 0)
+			break;
+}
+
+static int
+reporter(char *fmt, ...)
+{
+	va_list ap;
+	
+	va_start(ap, fmt);
+	fprint(2, "%s:  tls reports ", argv0);
+	vfprint(2, fmt, ap);
+	fprint(2, "\n");
+
+	va_end(ap);
+	return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+	int fd, pid;
+	char *addr;
+	TLSconn *conn;
+	Thumbprint *thumb;
+	AuthInfo *ai = nil;
+
+//	fmtinstall('H', encodefmt);
+
+	ARGBEGIN{
+	case 'D':
+		debug++;
+		break;
+	case 'a':
+		auth++;
+		break;
+	case 'k':
+		keyspec = EARGF(usage());
+		break;
+	case 't':
+		file = EARGF(usage());
+		break;
+	case 'x':
+		filex = EARGF(usage());
+		break;
+	case 'c':
+		ccert = EARGF(usage());
+		break;
+	case 'n':
+		servername = EARGF(usage());
+		break;
+	case 'o':
+		dialfile = 1;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc < 1)
+		usage();
+
+	if(filex && !file)	
+		sysfatal("specifying -x without -t is useless");
+
+	if(file){
+		thumb = initThumbprints(file, filex);
+		if(thumb == nil)
+			sysfatal("initThumbprints: %r");
+	} else
+		thumb = nil;
+
+	addr = *argv++;
+	if((fd = dial(addr, 0, 0, 0)) < 0)
+		sysfatal("dial %s: %r", addr);
+
+	conn = (TLSconn*)malloc(sizeof *conn);
+	memset(conn, 0, sizeof(*conn));
+	conn->serverName = servername;
+	if(ccert){
+		conn->cert = readcert(ccert, &conn->certlen);
+		if(conn->cert == nil)
+			sysfatal("readcert: %r");
+	}
+
+	if(auth){
+		ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client %s", keyspec);
+		if(ai == nil)
+			sysfatal("auth_proxy: %r");
+
+		conn->pskID = "p9secret";
+		conn->psk = ai->secret;
+		conn->psklen = ai->nsecret;
+	}
+
+	if(debug)
+		conn->trace = reporter;
+
+	fd = tlsClient(fd, conn);
+	if(fd < 0)
+		sysfatal("tlsclient: %r");
+
+	if(thumb){
+		uchar digest[20];
+
+		if(conn->cert==nil || conn->certlen<=0)
+			sysfatal("server did not provide TLS certificate");
+		sha1(conn->cert, conn->certlen, digest, nil);
+		if(!okThumbprint(digest, thumb))
+			sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
+		freeThumbprints(thumb);
+	}
+
+	free(conn->cert);
+	free(conn->sessionID);
+	free(conn);
+	if(ai != nil)
+		auth_freeAI(ai);
+
+	pid = fork();
+	switch(pid){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		pid = getppid();
+		xfer(0, fd);
+		break;
+	default:
+		xfer(fd, 1);
+		break;
+	}
+	if(pid) kill(pid, SIGTERM);
+	return 0;
+}