xref: /plan9-contrib/sys/src/9/ip/esp.c (revision 681a769e2eddd1ad53c76aa4a6c64957ff432c36)
16c0968e3SDavid du Colombier /*
26c0968e3SDavid du Colombier  * Encapsulating Security Payload for IPsec for IPv4, rfc1827.
3da0b1336SDavid du Colombier  * extended to IPv6.
4410ea80bSDavid du Colombier  * rfc2104 defines hmac computation.
5c168f9f3SDavid du Colombier  *	currently only implements tunnel mode.
6410ea80bSDavid du Colombier  * TODO: verify aes algorithms;
7410ea80bSDavid du Colombier  *	transport mode (host-to-host)
86c0968e3SDavid du Colombier  */
97dd7cddfSDavid du Colombier #include	"u.h"
107dd7cddfSDavid du Colombier #include	"../port/lib.h"
117dd7cddfSDavid du Colombier #include	"mem.h"
127dd7cddfSDavid du Colombier #include	"dat.h"
137dd7cddfSDavid du Colombier #include	"fns.h"
147dd7cddfSDavid du Colombier #include	"../port/error.h"
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier #include	"ip.h"
17c168f9f3SDavid du Colombier #include	"ipv6.h"
187dd7cddfSDavid du Colombier #include	"libsec.h"
197dd7cddfSDavid du Colombier 
20410ea80bSDavid du Colombier #define BITS2BYTES(bi) (((bi) + BI2BY - 1) / BI2BY)
21410ea80bSDavid du Colombier #define BYTES2BITS(by)  ((by) * BI2BY)
22410ea80bSDavid du Colombier 
23410ea80bSDavid du Colombier typedef struct Algorithm Algorithm;
24c168f9f3SDavid du Colombier typedef struct Esp4hdr Esp4hdr;
25c168f9f3SDavid du Colombier typedef struct Esp6hdr Esp6hdr;
26410ea80bSDavid du Colombier typedef struct Espcb Espcb;
27410ea80bSDavid du Colombier typedef struct Esphdr Esphdr;
28410ea80bSDavid du Colombier typedef struct Esppriv Esppriv;
297dd7cddfSDavid du Colombier typedef struct Esptail Esptail;
307dd7cddfSDavid du Colombier typedef struct Userhdr Userhdr;
317dd7cddfSDavid du Colombier 
32410ea80bSDavid du Colombier enum {
33410ea80bSDavid du Colombier 	Encrypt,
34410ea80bSDavid du Colombier 	Decrypt,
35410ea80bSDavid du Colombier 
360774058cSDavid du Colombier 	IP_ESPPROTO	= 50,	/* IP v4 and v6 protocol number */
37c168f9f3SDavid du Colombier 	Esp4hdrlen	= IP4HDR + 8,
38c168f9f3SDavid du Colombier 	Esp6hdrlen	= IP6HDR + 8,
390774058cSDavid du Colombier 
40c168f9f3SDavid du Colombier 	Esptaillen	= 2,	/* does not include pad or auth data */
41c168f9f3SDavid du Colombier 	Userhdrlen	= 4,	/* user-visible header size - if enabled */
42410ea80bSDavid du Colombier 
43410ea80bSDavid du Colombier 	Desblk	 = BITS2BYTES(64),
44410ea80bSDavid du Colombier 	Des3keysz = BITS2BYTES(192),
45410ea80bSDavid du Colombier 
46410ea80bSDavid du Colombier 	Aesblk	 = BITS2BYTES(128),
47410ea80bSDavid du Colombier 	Aeskeysz = BITS2BYTES(128),
487dd7cddfSDavid du Colombier };
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier struct Esphdr
517dd7cddfSDavid du Colombier {
52c168f9f3SDavid du Colombier 	uchar	espspi[4];	/* Security parameter index */
53c168f9f3SDavid du Colombier 	uchar	espseq[4];	/* Sequence number */
54410ea80bSDavid du Colombier 	uchar	payload[];
55c168f9f3SDavid du Colombier };
56c168f9f3SDavid du Colombier 
57c168f9f3SDavid du Colombier /*
58410ea80bSDavid du Colombier  * tunnel-mode (network-to-network, etc.) layout is:
59410ea80bSDavid du Colombier  * new IP hdrs | ESP hdr |
60410ea80bSDavid du Colombier  *	 enc { orig IP hdrs | TCP/UDP hdr | user data | ESP trailer } | ESP ICV
61410ea80bSDavid du Colombier  *
62410ea80bSDavid du Colombier  * transport-mode (host-to-host) layout would be:
63410ea80bSDavid du Colombier  *	orig IP hdrs | ESP hdr |
64410ea80bSDavid du Colombier  *			enc { TCP/UDP hdr | user data | ESP trailer } | ESP ICV
65c168f9f3SDavid du Colombier  */
66c168f9f3SDavid du Colombier struct Esp4hdr
67c168f9f3SDavid du Colombier {
686c0968e3SDavid du Colombier 	/* ipv4 header */
697dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
707dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
717dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
727dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
737dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
747dd7cddfSDavid du Colombier 	uchar	Unused;
757dd7cddfSDavid du Colombier 	uchar	espproto;	/* Protocol */
767dd7cddfSDavid du Colombier 	uchar	espplen[2];	/* Header plus data length */
777dd7cddfSDavid du Colombier 	uchar	espsrc[4];	/* Ip source */
787dd7cddfSDavid du Colombier 	uchar	espdst[4];	/* Ip destination */
797dd7cddfSDavid du Colombier 
80c168f9f3SDavid du Colombier 	Esphdr;
81c168f9f3SDavid du Colombier };
82c168f9f3SDavid du Colombier 
83c168f9f3SDavid du Colombier /* tunnel-mode layout */
84c168f9f3SDavid du Colombier struct Esp6hdr
85c168f9f3SDavid du Colombier {
86410ea80bSDavid du Colombier 	IPV6HDR;
87c168f9f3SDavid du Colombier 	Esphdr;
887dd7cddfSDavid du Colombier };
897dd7cddfSDavid du Colombier 
907dd7cddfSDavid du Colombier struct Esptail
917dd7cddfSDavid du Colombier {
927dd7cddfSDavid du Colombier 	uchar	pad;
937dd7cddfSDavid du Colombier 	uchar	nexthdr;
947dd7cddfSDavid du Colombier };
957dd7cddfSDavid du Colombier 
96410ea80bSDavid du Colombier /* IP-version-dependent data */
97410ea80bSDavid du Colombier typedef struct Versdep Versdep;
98410ea80bSDavid du Colombier struct Versdep
99410ea80bSDavid du Colombier {
100410ea80bSDavid du Colombier 	ulong	version;
101410ea80bSDavid du Colombier 	ulong	iphdrlen;
102410ea80bSDavid du Colombier 	ulong	hdrlen;		/* iphdrlen + esp hdr len */
103410ea80bSDavid du Colombier 	ulong	spi;
104410ea80bSDavid du Colombier 	uchar	laddr[IPaddrlen];
105410ea80bSDavid du Colombier 	uchar	raddr[IPaddrlen];
106410ea80bSDavid du Colombier };
107410ea80bSDavid du Colombier 
1087dd7cddfSDavid du Colombier /* header as seen by the user */
1097dd7cddfSDavid du Colombier struct Userhdr
1107dd7cddfSDavid du Colombier {
1116c0968e3SDavid du Colombier 	uchar	nexthdr;	/* next protocol */
1127dd7cddfSDavid du Colombier 	uchar	unused[3];
1137dd7cddfSDavid du Colombier };
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier struct Esppriv
1167dd7cddfSDavid du Colombier {
117*3468a491SDavid du Colombier 	uvlong	in;
1187dd7cddfSDavid du Colombier 	ulong	inerrors;
1197dd7cddfSDavid du Colombier };
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier /*
1227dd7cddfSDavid du Colombier  *  protocol specific part of Conv
1237dd7cddfSDavid du Colombier  */
1247dd7cddfSDavid du Colombier struct Espcb
1257dd7cddfSDavid du Colombier {
1267dd7cddfSDavid du Colombier 	int	incoming;
127410ea80bSDavid du Colombier 	int	header;		/* user-level header */
1287dd7cddfSDavid du Colombier 	ulong	spi;
1296c0968e3SDavid du Colombier 	ulong	seq;		/* last seq sent */
1306c0968e3SDavid du Colombier 	ulong	window;		/* for replay attacks */
131410ea80bSDavid du Colombier 
1327dd7cddfSDavid du Colombier 	char	*espalg;
1336c0968e3SDavid du Colombier 	void	*espstate;	/* other state for esp */
1346c0968e3SDavid du Colombier 	int	espivlen;	/* in bytes */
1357dd7cddfSDavid du Colombier 	int	espblklen;
1367dd7cddfSDavid du Colombier 	int	(*cipher)(Espcb*, uchar *buf, int len);
137410ea80bSDavid du Colombier 
1387dd7cddfSDavid du Colombier 	char	*ahalg;
1396c0968e3SDavid du Colombier 	void	*ahstate;	/* other state for esp */
1406c0968e3SDavid du Colombier 	int	ahlen;		/* auth data length in bytes */
1417dd7cddfSDavid du Colombier 	int	ahblklen;
1427dd7cddfSDavid du Colombier 	int	(*auth)(Espcb*, uchar *buf, int len, uchar *hash);
143410ea80bSDavid du Colombier 	DigestState *ds;
1447dd7cddfSDavid du Colombier };
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier struct Algorithm
1477dd7cddfSDavid du Colombier {
1487dd7cddfSDavid du Colombier 	char	*name;
1496c0968e3SDavid du Colombier 	int	keylen;		/* in bits */
150410ea80bSDavid du Colombier 	void	(*init)(Espcb*, char* name, uchar *key, unsigned keylen);
1517dd7cddfSDavid du Colombier };
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier static	Conv* convlookup(Proto *esp, ulong spi);
1547dd7cddfSDavid du Colombier static	char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg);
1550774058cSDavid du Colombier static	void espkick(void *x);
1560774058cSDavid du Colombier 
157410ea80bSDavid du Colombier static	void nullespinit(Espcb*, char*, uchar *key, unsigned keylen);
158410ea80bSDavid du Colombier static	void des3espinit(Espcb*, char*, uchar *key, unsigned keylen);
159410ea80bSDavid du Colombier static	void aescbcespinit(Espcb*, char*, uchar *key, unsigned keylen);
160410ea80bSDavid du Colombier static	void aesctrespinit(Espcb*, char*, uchar *key, unsigned keylen);
161410ea80bSDavid du Colombier static	void desespinit(Espcb *ecb, char *name, uchar *k, unsigned n);
1620774058cSDavid du Colombier 
163410ea80bSDavid du Colombier static	void nullahinit(Espcb*, char*, uchar *key, unsigned keylen);
164410ea80bSDavid du Colombier static	void shaahinit(Espcb*, char*, uchar *key, unsigned keylen);
165410ea80bSDavid du Colombier static	void aesahinit(Espcb*, char*, uchar *key, unsigned keylen);
166410ea80bSDavid du Colombier static	void md5ahinit(Espcb*, char*, uchar *key, unsigned keylen);
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier static Algorithm espalg[] =
1697dd7cddfSDavid du Colombier {
1707dd7cddfSDavid du Colombier 	"null",		0,	nullespinit,
171410ea80bSDavid du Colombier 	"des3_cbc",	192,	des3espinit,	/* new rfc2451, des-ede3 */
172410ea80bSDavid du Colombier 	"aes_128_cbc",	128,	aescbcespinit,	/* new rfc3602 */
173410ea80bSDavid du Colombier 	"aes_ctr",	128,	aesctrespinit,	/* new rfc3686 */
1740774058cSDavid du Colombier 	"des_56_cbc",	64,	desespinit,	/* rfc2405, deprecated */
175410ea80bSDavid du Colombier 	/* rc4 was never required, was used in original bandt */
176410ea80bSDavid du Colombier //	"rc4_128",	128,	rc4espinit,
1777dd7cddfSDavid du Colombier 	nil,		0,	nil,
1787dd7cddfSDavid du Colombier };
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier static Algorithm ahalg[] =
1817dd7cddfSDavid du Colombier {
1827dd7cddfSDavid du Colombier 	"null",		0,	nullahinit,
1830774058cSDavid du Colombier 	"hmac_sha1_96",	128,	shaahinit,	/* rfc2404 */
184410ea80bSDavid du Colombier 	"aes_xcbc_mac_96", 128,	aesahinit,	/* new rfc3566 */
1850774058cSDavid du Colombier 	"hmac_md5_96",	128,	md5ahinit,	/* rfc2403 */
1867dd7cddfSDavid du Colombier 	nil,		0,	nil,
1877dd7cddfSDavid du Colombier };
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier static char*
espconnect(Conv * c,char ** argv,int argc)1907dd7cddfSDavid du Colombier espconnect(Conv *c, char **argv, int argc)
1917dd7cddfSDavid du Colombier {
192410ea80bSDavid du Colombier 	char *p, *pp, *e = nil;
1937dd7cddfSDavid du Colombier 	ulong spi;
1947dd7cddfSDavid du Colombier 	Espcb *ecb = (Espcb*)c->ptcl;
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	switch(argc) {
1977dd7cddfSDavid du Colombier 	default:
1987dd7cddfSDavid du Colombier 		e = "bad args to connect";
1997dd7cddfSDavid du Colombier 		break;
2007dd7cddfSDavid du Colombier 	case 2:
2017dd7cddfSDavid du Colombier 		p = strchr(argv[1], '!');
2027dd7cddfSDavid du Colombier 		if(p == nil){
2037dd7cddfSDavid du Colombier 			e = "malformed address";
2047dd7cddfSDavid du Colombier 			break;
2057dd7cddfSDavid du Colombier 		}
2067dd7cddfSDavid du Colombier 		*p++ = 0;
207410ea80bSDavid du Colombier 		if (parseip(c->raddr, argv[1]) == -1) {
208410ea80bSDavid du Colombier 			e = Ebadip;
209410ea80bSDavid du Colombier 			break;
210410ea80bSDavid du Colombier 		}
2117dd7cddfSDavid du Colombier 		findlocalip(c->p->f, c->laddr, c->raddr);
2127dd7cddfSDavid du Colombier 		ecb->incoming = 0;
2137dd7cddfSDavid du Colombier 		ecb->seq = 0;
2147dd7cddfSDavid du Colombier 		if(strcmp(p, "*") == 0) {
2157dd7cddfSDavid du Colombier 			qlock(c->p);
2167dd7cddfSDavid du Colombier 			for(;;) {
2177dd7cddfSDavid du Colombier 				spi = nrand(1<<16) + 256;
2187dd7cddfSDavid du Colombier 				if(convlookup(c->p, spi) == nil)
2197dd7cddfSDavid du Colombier 					break;
2207dd7cddfSDavid du Colombier 			}
2217dd7cddfSDavid du Colombier 			qunlock(c->p);
2227dd7cddfSDavid du Colombier 			ecb->spi = spi;
2237dd7cddfSDavid du Colombier 			ecb->incoming = 1;
2247dd7cddfSDavid du Colombier 			qhangup(c->wq, nil);
2257dd7cddfSDavid du Colombier 		} else {
2267dd7cddfSDavid du Colombier 			spi = strtoul(p, &pp, 10);
2277dd7cddfSDavid du Colombier 			if(pp == p) {
2287dd7cddfSDavid du Colombier 				e = "malformed address";
2297dd7cddfSDavid du Colombier 				break;
2307dd7cddfSDavid du Colombier 			}
2317dd7cddfSDavid du Colombier 			ecb->spi = spi;
2327dd7cddfSDavid du Colombier 			qhangup(c->rq, nil);
2337dd7cddfSDavid du Colombier 		}
2347dd7cddfSDavid du Colombier 		nullespinit(ecb, "null", nil, 0);
2357dd7cddfSDavid du Colombier 		nullahinit(ecb, "null", nil, 0);
2367dd7cddfSDavid du Colombier 	}
2377dd7cddfSDavid du Colombier 	Fsconnected(c, e);
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier 	return e;
2407dd7cddfSDavid du Colombier }
2417dd7cddfSDavid du Colombier 
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier static int
espstate(Conv * c,char * state,int n)2447dd7cddfSDavid du Colombier espstate(Conv *c, char *state, int n)
2457dd7cddfSDavid du Colombier {
2467dd7cddfSDavid du Colombier 	return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n");
2477dd7cddfSDavid du Colombier }
2487dd7cddfSDavid du Colombier 
2497dd7cddfSDavid du Colombier static void
espcreate(Conv * c)2507dd7cddfSDavid du Colombier espcreate(Conv *c)
2517dd7cddfSDavid du Colombier {
2523ff48bf5SDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, 0);
2533ff48bf5SDavid du Colombier 	c->wq = qopen(64*1024, Qkick, espkick, c);
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier static void
espclose(Conv * c)2577dd7cddfSDavid du Colombier espclose(Conv *c)
2587dd7cddfSDavid du Colombier {
2597dd7cddfSDavid du Colombier 	Espcb *ecb;
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier 	qclose(c->rq);
2627dd7cddfSDavid du Colombier 	qclose(c->wq);
2637dd7cddfSDavid du Colombier 	qclose(c->eq);
2647dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
2657dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 	ecb = (Espcb*)c->ptcl;
2687dd7cddfSDavid du Colombier 	free(ecb->espstate);
2697dd7cddfSDavid du Colombier 	free(ecb->ahstate);
2707dd7cddfSDavid du Colombier 	memset(ecb, 0, sizeof(Espcb));
2717dd7cddfSDavid du Colombier }
2727dd7cddfSDavid du Colombier 
273c168f9f3SDavid du Colombier static int
convipvers(Conv * c)274410ea80bSDavid du Colombier convipvers(Conv *c)
275c168f9f3SDavid du Colombier {
276c168f9f3SDavid du Colombier 	if((memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
277c168f9f3SDavid du Colombier 	    memcmp(c->laddr, v4prefix, IPv4off) == 0) ||
278c168f9f3SDavid du Colombier 	    ipcmp(c->raddr, IPnoaddr) == 0)
279c168f9f3SDavid du Colombier 		return V4;
280c168f9f3SDavid du Colombier 	else
281c168f9f3SDavid du Colombier 		return V6;
282c168f9f3SDavid du Colombier }
283c168f9f3SDavid du Colombier 
284410ea80bSDavid du Colombier static int
pktipvers(Fs * f,Block ** bpp)285410ea80bSDavid du Colombier pktipvers(Fs *f, Block **bpp)
286410ea80bSDavid du Colombier {
287410ea80bSDavid du Colombier 	if (*bpp == nil || BLEN(*bpp) == 0) {
288410ea80bSDavid du Colombier 		/* get enough to identify the IP version */
289410ea80bSDavid du Colombier 		*bpp = pullupblock(*bpp, IP4HDR);
290410ea80bSDavid du Colombier 		if(*bpp == nil) {
291410ea80bSDavid du Colombier 			netlog(f, Logesp, "esp: short packet\n");
292410ea80bSDavid du Colombier 			return 0;
293410ea80bSDavid du Colombier 		}
294410ea80bSDavid du Colombier 	}
295410ea80bSDavid du Colombier 	return (((Esp4hdr*)(*bpp)->rp)->vihl & 0xf0) == IP_VER4? V4: V6;
296410ea80bSDavid du Colombier }
297410ea80bSDavid du Colombier 
298410ea80bSDavid du Colombier static void
getverslens(int version,Versdep * vp)299410ea80bSDavid du Colombier getverslens(int version, Versdep *vp)
300410ea80bSDavid du Colombier {
301410ea80bSDavid du Colombier 	vp->version = version;
302410ea80bSDavid du Colombier 	switch(vp->version) {
303410ea80bSDavid du Colombier 	case V4:
304410ea80bSDavid du Colombier 		vp->iphdrlen = IP4HDR;
305410ea80bSDavid du Colombier 		vp->hdrlen   = Esp4hdrlen;
306410ea80bSDavid du Colombier 		break;
307410ea80bSDavid du Colombier 	case V6:
308410ea80bSDavid du Colombier 		vp->iphdrlen = IP6HDR;
309410ea80bSDavid du Colombier 		vp->hdrlen   = Esp6hdrlen;
310410ea80bSDavid du Colombier 		break;
311410ea80bSDavid du Colombier 	default:
312410ea80bSDavid du Colombier 		panic("esp: getverslens version %d wrong", version);
313410ea80bSDavid du Colombier 	}
314410ea80bSDavid du Colombier }
315410ea80bSDavid du Colombier 
316410ea80bSDavid du Colombier static void
getpktspiaddrs(uchar * pkt,Versdep * vp)317410ea80bSDavid du Colombier getpktspiaddrs(uchar *pkt, Versdep *vp)
318410ea80bSDavid du Colombier {
319410ea80bSDavid du Colombier 	Esp4hdr *eh4;
320410ea80bSDavid du Colombier 	Esp6hdr *eh6;
321410ea80bSDavid du Colombier 
322410ea80bSDavid du Colombier 	switch(vp->version) {
323410ea80bSDavid du Colombier 	case V4:
324410ea80bSDavid du Colombier 		eh4 = (Esp4hdr*)pkt;
325410ea80bSDavid du Colombier 		v4tov6(vp->raddr, eh4->espsrc);
326410ea80bSDavid du Colombier 		v4tov6(vp->laddr, eh4->espdst);
327410ea80bSDavid du Colombier 		vp->spi = nhgetl(eh4->espspi);
328410ea80bSDavid du Colombier 		break;
329410ea80bSDavid du Colombier 	case V6:
330410ea80bSDavid du Colombier 		eh6 = (Esp6hdr*)pkt;
331410ea80bSDavid du Colombier 		ipmove(vp->raddr, eh6->src);
332410ea80bSDavid du Colombier 		ipmove(vp->laddr, eh6->dst);
333410ea80bSDavid du Colombier 		vp->spi = nhgetl(eh6->espspi);
334410ea80bSDavid du Colombier 		break;
335410ea80bSDavid du Colombier 	default:
336410ea80bSDavid du Colombier 		panic("esp: getpktspiaddrs vp->version %ld wrong", vp->version);
337410ea80bSDavid du Colombier 	}
338410ea80bSDavid du Colombier }
339410ea80bSDavid du Colombier 
340410ea80bSDavid du Colombier /*
341410ea80bSDavid du Colombier  * encapsulate next IP packet on x's write queue in IP/ESP packet
342410ea80bSDavid du Colombier  * and initiate output of the result.
343410ea80bSDavid du Colombier  */
3443ff48bf5SDavid du Colombier static void
espkick(void * x)3453ff48bf5SDavid du Colombier espkick(void *x)
3467dd7cddfSDavid du Colombier {
347410ea80bSDavid du Colombier 	int nexthdr, payload, pad, align;
348410ea80bSDavid du Colombier 	uchar *auth;
349410ea80bSDavid du Colombier 	Block *bp;
3503ff48bf5SDavid du Colombier 	Conv *c = x;
351c168f9f3SDavid du Colombier 	Esp4hdr *eh4;
352c168f9f3SDavid du Colombier 	Esp6hdr *eh6;
353410ea80bSDavid du Colombier 	Espcb *ecb;
3547dd7cddfSDavid du Colombier 	Esptail *et;
3557dd7cddfSDavid du Colombier 	Userhdr *uh;
356410ea80bSDavid du Colombier 	Versdep vers;
3577dd7cddfSDavid du Colombier 
358410ea80bSDavid du Colombier 	getverslens(convipvers(c), &vers);
3597dd7cddfSDavid du Colombier 	bp = qget(c->wq);
3607dd7cddfSDavid du Colombier 	if(bp == nil)
3617dd7cddfSDavid du Colombier 		return;
3627dd7cddfSDavid du Colombier 
3637dd7cddfSDavid du Colombier 	qlock(c);
3647dd7cddfSDavid du Colombier 	ecb = c->ptcl;
3657dd7cddfSDavid du Colombier 
3667dd7cddfSDavid du Colombier 	if(ecb->header) {
3677dd7cddfSDavid du Colombier 		/* make sure the message has a User header */
368c168f9f3SDavid du Colombier 		bp = pullupblock(bp, Userhdrlen);
3697dd7cddfSDavid du Colombier 		if(bp == nil) {
3707dd7cddfSDavid du Colombier 			qunlock(c);
3717dd7cddfSDavid du Colombier 			return;
3727dd7cddfSDavid du Colombier 		}
3737dd7cddfSDavid du Colombier 		uh = (Userhdr*)bp->rp;
3747dd7cddfSDavid du Colombier 		nexthdr = uh->nexthdr;
375c168f9f3SDavid du Colombier 		bp->rp += Userhdrlen;
3767dd7cddfSDavid du Colombier 	} else {
3776c0968e3SDavid du Colombier 		nexthdr = 0;	/* what should this be? */
3787dd7cddfSDavid du Colombier 	}
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	payload = BLEN(bp) + ecb->espivlen;
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 	/* Make space to fit ip header */
383410ea80bSDavid du Colombier 	bp = padblock(bp, vers.hdrlen + ecb->espivlen);
384410ea80bSDavid du Colombier 	getpktspiaddrs(bp->rp, &vers);
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 	align = 4;
3877dd7cddfSDavid du Colombier 	if(ecb->espblklen > align)
3887dd7cddfSDavid du Colombier 		align = ecb->espblklen;
3897dd7cddfSDavid du Colombier 	if(align % ecb->ahblklen != 0)
3907dd7cddfSDavid du Colombier 		panic("espkick: ahblklen is important after all");
391c168f9f3SDavid du Colombier 	pad = (align-1) - (payload + Esptaillen-1)%align;
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier 	/*
3947dd7cddfSDavid du Colombier 	 * Make space for tail
3957dd7cddfSDavid du Colombier 	 * this is done by calling padblock with a negative size
3967dd7cddfSDavid du Colombier 	 * Padblock does not change bp->wp!
3977dd7cddfSDavid du Colombier 	 */
398c168f9f3SDavid du Colombier 	bp = padblock(bp, -(pad+Esptaillen+ecb->ahlen));
399c168f9f3SDavid du Colombier 	bp->wp += pad+Esptaillen+ecb->ahlen;
4007dd7cddfSDavid du Colombier 
401410ea80bSDavid du Colombier 	et = (Esptail*)(bp->rp + vers.hdrlen + payload + pad);
4027dd7cddfSDavid du Colombier 
4036c0968e3SDavid du Colombier 	/* fill in tail */
4047dd7cddfSDavid du Colombier 	et->pad = pad;
4057dd7cddfSDavid du Colombier 	et->nexthdr = nexthdr;
4067dd7cddfSDavid du Colombier 
407410ea80bSDavid du Colombier 	/* encrypt the payload */
408410ea80bSDavid du Colombier 	ecb->cipher(ecb, bp->rp + vers.hdrlen, payload + pad + Esptaillen);
409410ea80bSDavid du Colombier 	auth = bp->rp + vers.hdrlen + payload + pad + Esptaillen;
4107dd7cddfSDavid du Colombier 
411410ea80bSDavid du Colombier 	/* fill in head; construct a new IP header and an ESP header */
412410ea80bSDavid du Colombier 	if (vers.version == V4) {
413410ea80bSDavid du Colombier 		eh4 = (Esp4hdr *)bp->rp;
414c168f9f3SDavid du Colombier 		eh4->vihl = IP_VER4;
415c168f9f3SDavid du Colombier 		v6tov4(eh4->espsrc, c->laddr);
416c168f9f3SDavid du Colombier 		v6tov4(eh4->espdst, c->raddr);
417c168f9f3SDavid du Colombier 		eh4->espproto = IP_ESPPROTO;
418c168f9f3SDavid du Colombier 		eh4->frag[0] = 0;
419c168f9f3SDavid du Colombier 		eh4->frag[1] = 0;
420410ea80bSDavid du Colombier 
421410ea80bSDavid du Colombier 		hnputl(eh4->espspi, ecb->spi);
422410ea80bSDavid du Colombier 		hnputl(eh4->espseq, ++ecb->seq);
423c168f9f3SDavid du Colombier 	} else {
424410ea80bSDavid du Colombier 		eh6 = (Esp6hdr *)bp->rp;
425c168f9f3SDavid du Colombier 		eh6->vcf[0] = IP_VER6;
426c168f9f3SDavid du Colombier 		ipmove(eh6->src, c->laddr);
427c168f9f3SDavid du Colombier 		ipmove(eh6->dst, c->raddr);
428c168f9f3SDavid du Colombier 		eh6->proto = IP_ESPPROTO;
429410ea80bSDavid du Colombier 
430410ea80bSDavid du Colombier 		hnputl(eh6->espspi, ecb->spi);
431410ea80bSDavid du Colombier 		hnputl(eh6->espseq, ++ecb->seq);
432c168f9f3SDavid du Colombier 	}
4337dd7cddfSDavid du Colombier 
434410ea80bSDavid du Colombier 	/* compute secure hash */
435410ea80bSDavid du Colombier 	ecb->auth(ecb, bp->rp + vers.iphdrlen, (vers.hdrlen - vers.iphdrlen) +
436c168f9f3SDavid du Colombier 		payload + pad + Esptaillen, auth);
4377dd7cddfSDavid du Colombier 
4387dd7cddfSDavid du Colombier 	qunlock(c);
4396c0968e3SDavid du Colombier 	/* print("esp: pass down: %uld\n", BLEN(bp)); */
440410ea80bSDavid du Colombier 	if (vers.version == V4)
441a6a9e072SDavid du Colombier 		ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c);
442c168f9f3SDavid du Colombier 	else
443c168f9f3SDavid du Colombier 		ipoput6(c->p->f, bp, 0, c->ttl, c->tos, c);
4447dd7cddfSDavid du Colombier }
4457dd7cddfSDavid du Colombier 
446410ea80bSDavid du Colombier /*
447410ea80bSDavid du Colombier  * decapsulate IP packet from IP/ESP packet in bp and
448410ea80bSDavid du Colombier  * pass the result up the spi's Conv's read queue.
449410ea80bSDavid du Colombier  */
4507dd7cddfSDavid du Colombier void
espiput(Proto * esp,Ipifc *,Block * bp)4519a747e4fSDavid du Colombier espiput(Proto *esp, Ipifc*, Block *bp)
4527dd7cddfSDavid du Colombier {
453410ea80bSDavid du Colombier 	int payload, nexthdr;
454410ea80bSDavid du Colombier 	uchar *auth, *espspi;
4557dd7cddfSDavid du Colombier 	Conv *c;
4567dd7cddfSDavid du Colombier 	Espcb *ecb;
457410ea80bSDavid du Colombier 	Esptail *et;
4587dd7cddfSDavid du Colombier 	Fs *f;
459410ea80bSDavid du Colombier 	Userhdr *uh;
460410ea80bSDavid du Colombier 	Versdep vers;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	f = esp->f;
463410ea80bSDavid du Colombier 
464410ea80bSDavid du Colombier 	getverslens(pktipvers(f, &bp), &vers);
465410ea80bSDavid du Colombier 
466410ea80bSDavid du Colombier 	bp = pullupblock(bp, vers.hdrlen + Esptaillen);
467c168f9f3SDavid du Colombier 	if(bp == nil) {
468c168f9f3SDavid du Colombier 		netlog(f, Logesp, "esp: short packet\n");
469c168f9f3SDavid du Colombier 		return;
470c168f9f3SDavid du Colombier 	}
471410ea80bSDavid du Colombier 	getpktspiaddrs(bp->rp, &vers);
4727dd7cddfSDavid du Colombier 
4737dd7cddfSDavid du Colombier 	qlock(esp);
4747dd7cddfSDavid du Colombier 	/* Look for a conversation structure for this port */
475410ea80bSDavid du Colombier 	c = convlookup(esp, vers.spi);
4767dd7cddfSDavid du Colombier 	if(c == nil) {
4777dd7cddfSDavid du Colombier 		qunlock(esp);
4787133e0eeSDavid du Colombier 		netlog(f, Logesp, "esp: no conv %I -> %I!%lud\n", vers.raddr,
479410ea80bSDavid du Colombier 			vers.laddr, vers.spi);
4807dd7cddfSDavid du Colombier 		icmpnoconv(f, bp);
4817dd7cddfSDavid du Colombier 		freeblist(bp);
4827dd7cddfSDavid du Colombier 		return;
4837dd7cddfSDavid du Colombier 	}
4847dd7cddfSDavid du Colombier 
4857dd7cddfSDavid du Colombier 	qlock(c);
4867dd7cddfSDavid du Colombier 	qunlock(esp);
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	ecb = c->ptcl;
4896c0968e3SDavid du Colombier 	/* too hard to do decryption/authentication on block lists */
4907dd7cddfSDavid du Colombier 	if(bp->next)
4917dd7cddfSDavid du Colombier 		bp = concatblock(bp);
4927dd7cddfSDavid du Colombier 
493410ea80bSDavid du Colombier 	if(BLEN(bp) < vers.hdrlen + ecb->espivlen + Esptaillen + ecb->ahlen) {
4947dd7cddfSDavid du Colombier 		qunlock(c);
4957133e0eeSDavid du Colombier 		netlog(f, Logesp, "esp: short block %I -> %I!%lud\n", vers.raddr,
496410ea80bSDavid du Colombier 			vers.laddr, vers.spi);
4977dd7cddfSDavid du Colombier 		freeb(bp);
4987dd7cddfSDavid du Colombier 		return;
4997dd7cddfSDavid du Colombier 	}
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 	auth = bp->wp - ecb->ahlen;
502410ea80bSDavid du Colombier 	espspi = vers.version == V4?	((Esp4hdr*)bp->rp)->espspi:
503c168f9f3SDavid du Colombier 					((Esp6hdr*)bp->rp)->espspi;
504410ea80bSDavid du Colombier 
505410ea80bSDavid du Colombier 	/* compute secure hash and authenticate */
506c168f9f3SDavid du Colombier 	if(!ecb->auth(ecb, espspi, auth - espspi, auth)) {
5077dd7cddfSDavid du Colombier 		qunlock(c);
508410ea80bSDavid du Colombier print("esp: bad auth %I -> %I!%ld\n", vers.raddr, vers.laddr, vers.spi);
5097133e0eeSDavid du Colombier 		netlog(f, Logesp, "esp: bad auth %I -> %I!%lud\n", vers.raddr,
510410ea80bSDavid du Colombier 			vers.laddr, vers.spi);
5117dd7cddfSDavid du Colombier 		freeb(bp);
5127dd7cddfSDavid du Colombier 		return;
5137dd7cddfSDavid du Colombier 	}
5147dd7cddfSDavid du Colombier 
515410ea80bSDavid du Colombier 	payload = BLEN(bp) - vers.hdrlen - ecb->ahlen;
5167dd7cddfSDavid du Colombier 	if(payload <= 0 || payload % 4 != 0 || payload % ecb->espblklen != 0) {
5177dd7cddfSDavid du Colombier 		qunlock(c);
5187133e0eeSDavid du Colombier 		netlog(f, Logesp, "esp: bad length %I -> %I!%lud payload=%d BLEN=%lud\n",
519410ea80bSDavid du Colombier 			vers.raddr, vers.laddr, vers.spi, payload, BLEN(bp));
5207dd7cddfSDavid du Colombier 		freeb(bp);
5217dd7cddfSDavid du Colombier 		return;
5227dd7cddfSDavid du Colombier 	}
523410ea80bSDavid du Colombier 
524410ea80bSDavid du Colombier 	/* decrypt payload */
525410ea80bSDavid du Colombier 	if(!ecb->cipher(ecb, bp->rp + vers.hdrlen, payload)) {
5267dd7cddfSDavid du Colombier 		qunlock(c);
527410ea80bSDavid du Colombier print("esp: cipher failed %I -> %I!%ld: %s\n", vers.raddr, vers.laddr, vers.spi, up->errstr);
5287133e0eeSDavid du Colombier 		netlog(f, Logesp, "esp: cipher failed %I -> %I!%lud: %s\n",
529410ea80bSDavid du Colombier 			vers.raddr, vers.laddr, vers.spi, up->errstr);
5307dd7cddfSDavid du Colombier 		freeb(bp);
5317dd7cddfSDavid du Colombier 		return;
5327dd7cddfSDavid du Colombier 	}
5337dd7cddfSDavid du Colombier 
534c168f9f3SDavid du Colombier 	payload -= Esptaillen;
535410ea80bSDavid du Colombier 	et = (Esptail*)(bp->rp + vers.hdrlen + payload);
5367dd7cddfSDavid du Colombier 	payload -= et->pad + ecb->espivlen;
5377dd7cddfSDavid du Colombier 	nexthdr = et->nexthdr;
5387dd7cddfSDavid du Colombier 	if(payload <= 0) {
5397dd7cddfSDavid du Colombier 		qunlock(c);
5407133e0eeSDavid du Colombier 		netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%lud\n",
541410ea80bSDavid du Colombier 			vers.raddr, vers.laddr, vers.spi);
5427dd7cddfSDavid du Colombier 		freeb(bp);
5437dd7cddfSDavid du Colombier 		return;
5447dd7cddfSDavid du Colombier 	}
5457dd7cddfSDavid du Colombier 
5466c0968e3SDavid du Colombier 	/* trim packet */
547410ea80bSDavid du Colombier 	bp->rp += vers.hdrlen + ecb->espivlen; /* toss original IP & ESP hdrs */
5487dd7cddfSDavid du Colombier 	bp->wp = bp->rp + payload;
5497dd7cddfSDavid du Colombier 	if(ecb->header) {
550c168f9f3SDavid du Colombier 		/* assume Userhdrlen < Esp4hdrlen < Esp6hdrlen */
551c168f9f3SDavid du Colombier 		bp->rp -= Userhdrlen;
5527dd7cddfSDavid du Colombier 		uh = (Userhdr*)bp->rp;
553c168f9f3SDavid du Colombier 		memset(uh, 0, Userhdrlen);
5547dd7cddfSDavid du Colombier 		uh->nexthdr = nexthdr;
5557dd7cddfSDavid du Colombier 	}
5567dd7cddfSDavid du Colombier 
557410ea80bSDavid du Colombier 	/* ingress filtering here? */
558410ea80bSDavid du Colombier 
5597dd7cddfSDavid du Colombier 	if(qfull(c->rq)){
560410ea80bSDavid du Colombier 		netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", vers.raddr,
561410ea80bSDavid du Colombier 			vers.laddr, vers.spi);
5627dd7cddfSDavid du Colombier 		freeblist(bp);
5637dd7cddfSDavid du Colombier 	}else {
5647dd7cddfSDavid du Colombier //		print("esp: pass up: %uld\n", BLEN(bp));
565410ea80bSDavid du Colombier 		qpass(c->rq, bp);	/* pass packet up the read queue */
5667dd7cddfSDavid du Colombier 	}
5677dd7cddfSDavid du Colombier 
5687dd7cddfSDavid du Colombier 	qunlock(c);
5697dd7cddfSDavid du Colombier }
5707dd7cddfSDavid du Colombier 
5717dd7cddfSDavid du Colombier char*
espctl(Conv * c,char ** f,int n)5727dd7cddfSDavid du Colombier espctl(Conv *c, char **f, int n)
5737dd7cddfSDavid du Colombier {
5747dd7cddfSDavid du Colombier 	Espcb *ecb = c->ptcl;
5757dd7cddfSDavid du Colombier 	char *e = nil;
5767dd7cddfSDavid du Colombier 
5777dd7cddfSDavid du Colombier 	if(strcmp(f[0], "esp") == 0)
5787dd7cddfSDavid du Colombier 		e = setalg(ecb, f, n, espalg);
5797dd7cddfSDavid du Colombier 	else if(strcmp(f[0], "ah") == 0)
5807dd7cddfSDavid du Colombier 		e = setalg(ecb, f, n, ahalg);
5817dd7cddfSDavid du Colombier 	else if(strcmp(f[0], "header") == 0)
5827dd7cddfSDavid du Colombier 		ecb->header = 1;
5837dd7cddfSDavid du Colombier 	else if(strcmp(f[0], "noheader") == 0)
5847dd7cddfSDavid du Colombier 		ecb->header = 0;
5857dd7cddfSDavid du Colombier 	else
5867dd7cddfSDavid du Colombier 		e = "unknown control request";
5877dd7cddfSDavid du Colombier 	return e;
5887dd7cddfSDavid du Colombier }
5897dd7cddfSDavid du Colombier 
590410ea80bSDavid du Colombier /* called from icmp(v6) for unreachable hosts, time exceeded, etc. */
5917dd7cddfSDavid du Colombier void
espadvise(Proto * esp,Block * bp,char * msg)5927dd7cddfSDavid du Colombier espadvise(Proto *esp, Block *bp, char *msg)
5937dd7cddfSDavid du Colombier {
5947dd7cddfSDavid du Colombier 	Conv *c;
595410ea80bSDavid du Colombier 	Versdep vers;
5967dd7cddfSDavid du Colombier 
597410ea80bSDavid du Colombier 	getverslens(pktipvers(esp->f, &bp), &vers);
598410ea80bSDavid du Colombier 	getpktspiaddrs(bp->rp, &vers);
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier 	qlock(esp);
601410ea80bSDavid du Colombier 	c = convlookup(esp, vers.spi);
6027dd7cddfSDavid du Colombier 	if(c != nil) {
6037dd7cddfSDavid du Colombier 		qhangup(c->rq, msg);
6047dd7cddfSDavid du Colombier 		qhangup(c->wq, msg);
6057dd7cddfSDavid du Colombier 	}
6067dd7cddfSDavid du Colombier 	qunlock(esp);
6077dd7cddfSDavid du Colombier 	freeblist(bp);
6087dd7cddfSDavid du Colombier }
6097dd7cddfSDavid du Colombier 
6107dd7cddfSDavid du Colombier int
espstats(Proto * esp,char * buf,int len)6117dd7cddfSDavid du Colombier espstats(Proto *esp, char *buf, int len)
6127dd7cddfSDavid du Colombier {
6137dd7cddfSDavid du Colombier 	Esppriv *upriv;
6147dd7cddfSDavid du Colombier 
6157dd7cddfSDavid du Colombier 	upriv = esp->priv;
616*3468a491SDavid du Colombier 	return snprint(buf, len, "%llud %lud\n",
6177dd7cddfSDavid du Colombier 		upriv->in,
6187dd7cddfSDavid du Colombier 		upriv->inerrors);
6197dd7cddfSDavid du Colombier }
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier static int
esplocal(Conv * c,char * buf,int len)6227dd7cddfSDavid du Colombier esplocal(Conv *c, char *buf, int len)
6237dd7cddfSDavid du Colombier {
6247dd7cddfSDavid du Colombier 	Espcb *ecb = c->ptcl;
6257dd7cddfSDavid du Colombier 	int n;
6267dd7cddfSDavid du Colombier 
6277dd7cddfSDavid du Colombier 	qlock(c);
6287dd7cddfSDavid du Colombier 	if(ecb->incoming)
6297dd7cddfSDavid du Colombier 		n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi);
6307dd7cddfSDavid du Colombier 	else
6317dd7cddfSDavid du Colombier 		n = snprint(buf, len, "%I\n", c->laddr);
6327dd7cddfSDavid du Colombier 	qunlock(c);
6337dd7cddfSDavid du Colombier 	return n;
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier static int
espremote(Conv * c,char * buf,int len)6377dd7cddfSDavid du Colombier espremote(Conv *c, char *buf, int len)
6387dd7cddfSDavid du Colombier {
6397dd7cddfSDavid du Colombier 	Espcb *ecb = c->ptcl;
6407dd7cddfSDavid du Colombier 	int n;
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier 	qlock(c);
6437dd7cddfSDavid du Colombier 	if(ecb->incoming)
6447dd7cddfSDavid du Colombier 		n = snprint(buf, len, "%I\n", c->raddr);
6457dd7cddfSDavid du Colombier 	else
6467dd7cddfSDavid du Colombier 		n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi);
6477dd7cddfSDavid du Colombier 	qunlock(c);
6487dd7cddfSDavid du Colombier 	return n;
6497dd7cddfSDavid du Colombier }
6507dd7cddfSDavid du Colombier 
6517dd7cddfSDavid du Colombier static	Conv*
convlookup(Proto * esp,ulong spi)6527dd7cddfSDavid du Colombier convlookup(Proto *esp, ulong spi)
6537dd7cddfSDavid du Colombier {
6547dd7cddfSDavid du Colombier 	Conv *c, **p;
6557dd7cddfSDavid du Colombier 	Espcb *ecb;
6567dd7cddfSDavid du Colombier 
6577dd7cddfSDavid du Colombier 	for(p=esp->conv; *p; p++){
6587dd7cddfSDavid du Colombier 		c = *p;
6597dd7cddfSDavid du Colombier 		ecb = c->ptcl;
6607dd7cddfSDavid du Colombier 		if(ecb->incoming && ecb->spi == spi)
6617dd7cddfSDavid du Colombier 			return c;
6627dd7cddfSDavid du Colombier 	}
6637dd7cddfSDavid du Colombier 	return nil;
6647dd7cddfSDavid du Colombier }
6657dd7cddfSDavid du Colombier 
6667dd7cddfSDavid du Colombier static char *
setalg(Espcb * ecb,char ** f,int n,Algorithm * alg)6677dd7cddfSDavid du Colombier setalg(Espcb *ecb, char **f, int n, Algorithm *alg)
6687dd7cddfSDavid du Colombier {
6697dd7cddfSDavid du Colombier 	uchar *key;
670*3468a491SDavid du Colombier 	int c, nbyte, nchar;
671*3468a491SDavid du Colombier 	uint i;
6727dd7cddfSDavid du Colombier 
673*3468a491SDavid du Colombier 	if(n < 2 || n > 3)
6747dd7cddfSDavid du Colombier 		return "bad format";
6757dd7cddfSDavid du Colombier 	for(; alg->name; alg++)
6767dd7cddfSDavid du Colombier 		if(strcmp(f[1], alg->name) == 0)
6777dd7cddfSDavid du Colombier 			break;
6787dd7cddfSDavid du Colombier 	if(alg->name == nil)
6797dd7cddfSDavid du Colombier 		return "unknown algorithm";
6807dd7cddfSDavid du Colombier 
6817dd7cddfSDavid du Colombier 	nbyte = (alg->keylen + 7) >> 3;
682*3468a491SDavid du Colombier 	if (n == 2)
683*3468a491SDavid du Colombier 		nchar = 0;
684*3468a491SDavid du Colombier 	else
6857dd7cddfSDavid du Colombier 		nchar = strlen(f[2]);
686*3468a491SDavid du Colombier 	if(nchar != 2 * nbyte)			/* TODO: maybe < is ok */
687*3468a491SDavid du Colombier 		return "key not required length";
688*3468a491SDavid du Colombier 	/* convert hex digits from ascii, in place */
6897dd7cddfSDavid du Colombier 	for(i=0; i<nchar; i++) {
6907dd7cddfSDavid du Colombier 		c = f[2][i];
6917dd7cddfSDavid du Colombier 		if(c >= '0' && c <= '9')
6927dd7cddfSDavid du Colombier 			f[2][i] -= '0';
6937dd7cddfSDavid du Colombier 		else if(c >= 'a' && c <= 'f')
6947dd7cddfSDavid du Colombier 			f[2][i] -= 'a'-10;
6957dd7cddfSDavid du Colombier 		else if(c >= 'A' && c <= 'F')
6967dd7cddfSDavid du Colombier 			f[2][i] -= 'A'-10;
6977dd7cddfSDavid du Colombier 		else
698*3468a491SDavid du Colombier 			return "non-hex character in key";
6997dd7cddfSDavid du Colombier 	}
700*3468a491SDavid du Colombier 	/* collapse hex digits into complete bytes in reverse order in key */
7017dd7cddfSDavid du Colombier 	key = smalloc(nbyte);
702*3468a491SDavid du Colombier 	for(i = 0; i < nchar && i/2 < nbyte; i++) {
7037dd7cddfSDavid du Colombier 		c = f[2][nchar-i-1];
7047dd7cddfSDavid du Colombier 		if(i&1)
7057dd7cddfSDavid du Colombier 			c <<= 4;
706*3468a491SDavid du Colombier 		key[i/2] |= c;
7077dd7cddfSDavid du Colombier 	}
7087dd7cddfSDavid du Colombier 
7097dd7cddfSDavid du Colombier 	alg->init(ecb, alg->name, key, alg->keylen);
7107dd7cddfSDavid du Colombier 	free(key);
7117dd7cddfSDavid du Colombier 	return nil;
7127dd7cddfSDavid du Colombier }
7137dd7cddfSDavid du Colombier 
714410ea80bSDavid du Colombier 
715410ea80bSDavid du Colombier /*
716410ea80bSDavid du Colombier  * null encryption
717410ea80bSDavid du Colombier  */
718410ea80bSDavid du Colombier 
7197dd7cddfSDavid du Colombier static int
nullcipher(Espcb *,uchar *,int)7207dd7cddfSDavid du Colombier nullcipher(Espcb*, uchar*, int)
7217dd7cddfSDavid du Colombier {
7227dd7cddfSDavid du Colombier 	return 1;
7237dd7cddfSDavid du Colombier }
7247dd7cddfSDavid du Colombier 
7257dd7cddfSDavid du Colombier static void
nullespinit(Espcb * ecb,char * name,uchar *,unsigned)726410ea80bSDavid du Colombier nullespinit(Espcb *ecb, char *name, uchar*, unsigned)
7277dd7cddfSDavid du Colombier {
7287dd7cddfSDavid du Colombier 	ecb->espalg = name;
7297dd7cddfSDavid du Colombier 	ecb->espblklen = 1;
7307dd7cddfSDavid du Colombier 	ecb->espivlen = 0;
7317dd7cddfSDavid du Colombier 	ecb->cipher = nullcipher;
7327dd7cddfSDavid du Colombier }
7337dd7cddfSDavid du Colombier 
7347dd7cddfSDavid du Colombier static int
nullauth(Espcb *,uchar *,int,uchar *)7357dd7cddfSDavid du Colombier nullauth(Espcb*, uchar*, int, uchar*)
7367dd7cddfSDavid du Colombier {
7377dd7cddfSDavid du Colombier 	return 1;
7387dd7cddfSDavid du Colombier }
7397dd7cddfSDavid du Colombier 
7407dd7cddfSDavid du Colombier static void
nullahinit(Espcb * ecb,char * name,uchar *,unsigned)741410ea80bSDavid du Colombier nullahinit(Espcb *ecb, char *name, uchar*, unsigned)
7427dd7cddfSDavid du Colombier {
7437dd7cddfSDavid du Colombier 	ecb->ahalg = name;
7447dd7cddfSDavid du Colombier 	ecb->ahblklen = 1;
7457dd7cddfSDavid du Colombier 	ecb->ahlen = 0;
7467dd7cddfSDavid du Colombier 	ecb->auth = nullauth;
7477dd7cddfSDavid du Colombier }
7487dd7cddfSDavid du Colombier 
749410ea80bSDavid du Colombier 
750410ea80bSDavid du Colombier /*
751410ea80bSDavid du Colombier  * sha1
752410ea80bSDavid du Colombier  */
753410ea80bSDavid du Colombier 
754410ea80bSDavid du Colombier static void
seanq_hmac_sha1(uchar hash[SHA1dlen],uchar * t,long tlen,uchar * key,long klen)7557dd7cddfSDavid du Colombier seanq_hmac_sha1(uchar hash[SHA1dlen], uchar *t, long tlen, uchar *key, long klen)
7567dd7cddfSDavid du Colombier {
7577dd7cddfSDavid du Colombier 	int i;
758410ea80bSDavid du Colombier 	uchar ipad[Hmacblksz+1], opad[Hmacblksz+1], innerhash[SHA1dlen];
7597dd7cddfSDavid du Colombier 	DigestState *digest;
7607dd7cddfSDavid du Colombier 
761410ea80bSDavid du Colombier 	memset(ipad, 0x36, Hmacblksz);
762410ea80bSDavid du Colombier 	memset(opad, 0x5c, Hmacblksz);
763410ea80bSDavid du Colombier 	ipad[Hmacblksz] = opad[Hmacblksz] = 0;
7647dd7cddfSDavid du Colombier 	for(i = 0; i < klen; i++){
7657dd7cddfSDavid du Colombier 		ipad[i] ^= key[i];
7667dd7cddfSDavid du Colombier 		opad[i] ^= key[i];
7677dd7cddfSDavid du Colombier 	}
768410ea80bSDavid du Colombier 	digest = sha1(ipad, Hmacblksz, nil, nil);
7697dd7cddfSDavid du Colombier 	sha1(t, tlen, innerhash, digest);
770410ea80bSDavid du Colombier 	digest = sha1(opad, Hmacblksz, nil, nil);
7717dd7cddfSDavid du Colombier 	sha1(innerhash, SHA1dlen, hash, digest);
7727dd7cddfSDavid du Colombier }
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier static int
shaauth(Espcb * ecb,uchar * t,int tlen,uchar * auth)7757dd7cddfSDavid du Colombier shaauth(Espcb *ecb, uchar *t, int tlen, uchar *auth)
7767dd7cddfSDavid du Colombier {
7777dd7cddfSDavid du Colombier 	int r;
778410ea80bSDavid du Colombier 	uchar hash[SHA1dlen];
7797dd7cddfSDavid du Colombier 
7807dd7cddfSDavid du Colombier 	memset(hash, 0, SHA1dlen);
781410ea80bSDavid du Colombier 	seanq_hmac_sha1(hash, t, tlen, (uchar*)ecb->ahstate, BITS2BYTES(128));
7827dd7cddfSDavid du Colombier 	r = memcmp(auth, hash, ecb->ahlen) == 0;
7837dd7cddfSDavid du Colombier 	memmove(auth, hash, ecb->ahlen);
7847dd7cddfSDavid du Colombier 	return r;
7857dd7cddfSDavid du Colombier }
7867dd7cddfSDavid du Colombier 
7877dd7cddfSDavid du Colombier static void
shaahinit(Espcb * ecb,char * name,uchar * key,unsigned klen)788410ea80bSDavid du Colombier shaahinit(Espcb *ecb, char *name, uchar *key, unsigned klen)
7897dd7cddfSDavid du Colombier {
7907dd7cddfSDavid du Colombier 	if(klen != 128)
7917dd7cddfSDavid du Colombier 		panic("shaahinit: bad keylen");
792410ea80bSDavid du Colombier 	klen /= BI2BY;
7937dd7cddfSDavid du Colombier 
7947dd7cddfSDavid du Colombier 	ecb->ahalg = name;
7957dd7cddfSDavid du Colombier 	ecb->ahblklen = 1;
796410ea80bSDavid du Colombier 	ecb->ahlen = BITS2BYTES(96);
7977dd7cddfSDavid du Colombier 	ecb->auth = shaauth;
7987dd7cddfSDavid du Colombier 	ecb->ahstate = smalloc(klen);
7997dd7cddfSDavid du Colombier 	memmove(ecb->ahstate, key, klen);
8007dd7cddfSDavid du Colombier }
8017dd7cddfSDavid du Colombier 
802410ea80bSDavid du Colombier 
803410ea80bSDavid du Colombier /*
804410ea80bSDavid du Colombier  * aes
805410ea80bSDavid du Colombier  */
806410ea80bSDavid du Colombier 
807410ea80bSDavid du Colombier /* ah_aes_xcbc_mac_96, rfc3566 */
808410ea80bSDavid du Colombier static int
aesahauth(Espcb * ecb,uchar * t,int tlen,uchar * auth)809410ea80bSDavid du Colombier aesahauth(Espcb *ecb, uchar *t, int tlen, uchar *auth)
810410ea80bSDavid du Colombier {
811410ea80bSDavid du Colombier 	int r;
812410ea80bSDavid du Colombier 	uchar hash[AESdlen];
813410ea80bSDavid du Colombier 
814410ea80bSDavid du Colombier 	memset(hash, 0, AESdlen);
815*3468a491SDavid du Colombier 	ecb->ds = hmac_aes(t, tlen, (uchar*)ecb->ahstate, BITS2BYTES(96), hash,
816410ea80bSDavid du Colombier 		ecb->ds);
817410ea80bSDavid du Colombier 	r = memcmp(auth, hash, ecb->ahlen) == 0;
818410ea80bSDavid du Colombier 	memmove(auth, hash, ecb->ahlen);
819410ea80bSDavid du Colombier 	return r;
820410ea80bSDavid du Colombier }
821410ea80bSDavid du Colombier 
822410ea80bSDavid du Colombier static void
aesahinit(Espcb * ecb,char * name,uchar * key,unsigned klen)823410ea80bSDavid du Colombier aesahinit(Espcb *ecb, char *name, uchar *key, unsigned klen)
824410ea80bSDavid du Colombier {
825410ea80bSDavid du Colombier 	if(klen != 128)
826410ea80bSDavid du Colombier 		panic("aesahinit: keylen not 128");
827410ea80bSDavid du Colombier 	klen /= BI2BY;
828410ea80bSDavid du Colombier 
829410ea80bSDavid du Colombier 	ecb->ahalg = name;
830410ea80bSDavid du Colombier 	ecb->ahblklen = 1;
831410ea80bSDavid du Colombier 	ecb->ahlen = BITS2BYTES(96);
832410ea80bSDavid du Colombier 	ecb->auth = aesahauth;
833410ea80bSDavid du Colombier 	ecb->ahstate = smalloc(klen);
834410ea80bSDavid du Colombier 	memmove(ecb->ahstate, key, klen);
835410ea80bSDavid du Colombier }
836410ea80bSDavid du Colombier 
837410ea80bSDavid du Colombier static int
aescbccipher(Espcb * ecb,uchar * p,int n)838410ea80bSDavid du Colombier aescbccipher(Espcb *ecb, uchar *p, int n)	/* 128-bit blocks */
839410ea80bSDavid du Colombier {
840410ea80bSDavid du Colombier 	uchar tmp[AESbsize], q[AESbsize];
841410ea80bSDavid du Colombier 	uchar *pp, *tp, *ip, *eip, *ep;
842410ea80bSDavid du Colombier 	AESstate *ds = ecb->espstate;
843410ea80bSDavid du Colombier 
844410ea80bSDavid du Colombier 	ep = p + n;
845410ea80bSDavid du Colombier 	if(ecb->incoming) {
846410ea80bSDavid du Colombier 		memmove(ds->ivec, p, AESbsize);
847410ea80bSDavid du Colombier 		p += AESbsize;
848410ea80bSDavid du Colombier 		while(p < ep){
849410ea80bSDavid du Colombier 			memmove(tmp, p, AESbsize);
850410ea80bSDavid du Colombier 			aes_decrypt(ds->dkey, ds->rounds, p, q);
851410ea80bSDavid du Colombier 			memmove(p, q, AESbsize);
852410ea80bSDavid du Colombier 			tp = tmp;
853410ea80bSDavid du Colombier 			ip = ds->ivec;
854410ea80bSDavid du Colombier 			for(eip = ip + AESbsize; ip < eip; ){
855410ea80bSDavid du Colombier 				*p++ ^= *ip;
856410ea80bSDavid du Colombier 				*ip++ = *tp++;
857410ea80bSDavid du Colombier 			}
858410ea80bSDavid du Colombier 		}
859410ea80bSDavid du Colombier 	} else {
860410ea80bSDavid du Colombier 		memmove(p, ds->ivec, AESbsize);
861410ea80bSDavid du Colombier 		for(p += AESbsize; p < ep; p += AESbsize){
862410ea80bSDavid du Colombier 			pp = p;
863410ea80bSDavid du Colombier 			ip = ds->ivec;
864410ea80bSDavid du Colombier 			for(eip = ip + AESbsize; ip < eip; )
865410ea80bSDavid du Colombier 				*pp++ ^= *ip++;
866410ea80bSDavid du Colombier 			aes_encrypt(ds->ekey, ds->rounds, p, q);
867410ea80bSDavid du Colombier 			memmove(ds->ivec, q, AESbsize);
868410ea80bSDavid du Colombier 			memmove(p, q, AESbsize);
869410ea80bSDavid du Colombier 		}
870410ea80bSDavid du Colombier 	}
871410ea80bSDavid du Colombier 	return 1;
872410ea80bSDavid du Colombier }
873410ea80bSDavid du Colombier 
874410ea80bSDavid du Colombier static void
aescbcespinit(Espcb * ecb,char * name,uchar * k,unsigned n)875410ea80bSDavid du Colombier aescbcespinit(Espcb *ecb, char *name, uchar *k, unsigned n)
876410ea80bSDavid du Colombier {
877410ea80bSDavid du Colombier 	uchar key[Aeskeysz], ivec[Aeskeysz];
878410ea80bSDavid du Colombier 	int i;
879410ea80bSDavid du Colombier 
880410ea80bSDavid du Colombier 	n = BITS2BYTES(n);
881410ea80bSDavid du Colombier 	if(n > Aeskeysz)
882410ea80bSDavid du Colombier 		n = Aeskeysz;
883410ea80bSDavid du Colombier 	memset(key, 0, sizeof(key));
884410ea80bSDavid du Colombier 	memmove(key, k, n);
885410ea80bSDavid du Colombier 	for(i = 0; i < Aeskeysz; i++)
886410ea80bSDavid du Colombier 		ivec[i] = nrand(256);
887410ea80bSDavid du Colombier 	ecb->espalg = name;
888410ea80bSDavid du Colombier 	ecb->espblklen = Aesblk;
889410ea80bSDavid du Colombier 	ecb->espivlen = Aesblk;
890410ea80bSDavid du Colombier 	ecb->cipher = aescbccipher;
891410ea80bSDavid du Colombier 	ecb->espstate = smalloc(sizeof(AESstate));
892410ea80bSDavid du Colombier 	setupAESstate(ecb->espstate, key, n /* keybytes */, ivec);
893410ea80bSDavid du Colombier }
894410ea80bSDavid du Colombier 
895410ea80bSDavid du Colombier static int
aesctrcipher(Espcb * ecb,uchar * p,int n)896410ea80bSDavid du Colombier aesctrcipher(Espcb *ecb, uchar *p, int n)	/* 128-bit blocks */
897410ea80bSDavid du Colombier {
898410ea80bSDavid du Colombier 	uchar tmp[AESbsize], q[AESbsize];
899410ea80bSDavid du Colombier 	uchar *pp, *tp, *ip, *eip, *ep;
900410ea80bSDavid du Colombier 	AESstate *ds = ecb->espstate;
901410ea80bSDavid du Colombier 
902410ea80bSDavid du Colombier 	ep = p + n;
903410ea80bSDavid du Colombier 	if(ecb->incoming) {
904410ea80bSDavid du Colombier 		memmove(ds->ivec, p, AESbsize);
905410ea80bSDavid du Colombier 		p += AESbsize;
906410ea80bSDavid du Colombier 		while(p < ep){
907410ea80bSDavid du Colombier 			memmove(tmp, p, AESbsize);
908410ea80bSDavid du Colombier 			aes_decrypt(ds->dkey, ds->rounds, p, q);
909410ea80bSDavid du Colombier 			memmove(p, q, AESbsize);
910410ea80bSDavid du Colombier 			tp = tmp;
911410ea80bSDavid du Colombier 			ip = ds->ivec;
912410ea80bSDavid du Colombier 			for(eip = ip + AESbsize; ip < eip; ){
913410ea80bSDavid du Colombier 				*p++ ^= *ip;
914410ea80bSDavid du Colombier 				*ip++ = *tp++;
915410ea80bSDavid du Colombier 			}
916410ea80bSDavid du Colombier 		}
917410ea80bSDavid du Colombier 	} else {
918410ea80bSDavid du Colombier 		memmove(p, ds->ivec, AESbsize);
919410ea80bSDavid du Colombier 		for(p += AESbsize; p < ep; p += AESbsize){
920410ea80bSDavid du Colombier 			pp = p;
921410ea80bSDavid du Colombier 			ip = ds->ivec;
922410ea80bSDavid du Colombier 			for(eip = ip + AESbsize; ip < eip; )
923410ea80bSDavid du Colombier 				*pp++ ^= *ip++;
924410ea80bSDavid du Colombier 			aes_encrypt(ds->ekey, ds->rounds, p, q);
925410ea80bSDavid du Colombier 			memmove(ds->ivec, q, AESbsize);
926410ea80bSDavid du Colombier 			memmove(p, q, AESbsize);
927410ea80bSDavid du Colombier 		}
928410ea80bSDavid du Colombier 	}
929410ea80bSDavid du Colombier 	return 1;
930410ea80bSDavid du Colombier }
931410ea80bSDavid du Colombier 
932410ea80bSDavid du Colombier static void
aesctrespinit(Espcb * ecb,char * name,uchar * k,unsigned n)933410ea80bSDavid du Colombier aesctrespinit(Espcb *ecb, char *name, uchar *k, unsigned n)
934410ea80bSDavid du Colombier {
935410ea80bSDavid du Colombier 	uchar key[Aesblk], ivec[Aesblk];
936410ea80bSDavid du Colombier 	int i;
937410ea80bSDavid du Colombier 
938410ea80bSDavid du Colombier 	n = BITS2BYTES(n);
939410ea80bSDavid du Colombier 	if(n > Aeskeysz)
940410ea80bSDavid du Colombier 		n = Aeskeysz;
941410ea80bSDavid du Colombier 	memset(key, 0, sizeof(key));
942410ea80bSDavid du Colombier 	memmove(key, k, n);
943410ea80bSDavid du Colombier 	for(i = 0; i < Aesblk; i++)
944410ea80bSDavid du Colombier 		ivec[i] = nrand(256);
945410ea80bSDavid du Colombier 	ecb->espalg = name;
946410ea80bSDavid du Colombier 	ecb->espblklen = Aesblk;
947410ea80bSDavid du Colombier 	ecb->espivlen = Aesblk;
948410ea80bSDavid du Colombier 	ecb->cipher = aesctrcipher;
949410ea80bSDavid du Colombier 	ecb->espstate = smalloc(sizeof(AESstate));
950410ea80bSDavid du Colombier 	setupAESstate(ecb->espstate, key, n /* keybytes */, ivec);
951410ea80bSDavid du Colombier }
952410ea80bSDavid du Colombier 
953410ea80bSDavid du Colombier 
954410ea80bSDavid du Colombier /*
955410ea80bSDavid du Colombier  * md5
956410ea80bSDavid du Colombier  */
957410ea80bSDavid du Colombier 
958410ea80bSDavid du Colombier static void
seanq_hmac_md5(uchar hash[MD5dlen],uchar * t,long tlen,uchar * key,long klen)9597dd7cddfSDavid du Colombier seanq_hmac_md5(uchar hash[MD5dlen], uchar *t, long tlen, uchar *key, long klen)
9607dd7cddfSDavid du Colombier {
9617dd7cddfSDavid du Colombier 	int i;
962410ea80bSDavid du Colombier 	uchar ipad[Hmacblksz+1], opad[Hmacblksz+1], innerhash[MD5dlen];
9637dd7cddfSDavid du Colombier 	DigestState *digest;
9647dd7cddfSDavid du Colombier 
965410ea80bSDavid du Colombier 	memset(ipad, 0x36, Hmacblksz);
966410ea80bSDavid du Colombier 	memset(opad, 0x5c, Hmacblksz);
967410ea80bSDavid du Colombier 	ipad[Hmacblksz] = opad[Hmacblksz] = 0;
9687dd7cddfSDavid du Colombier 	for(i = 0; i < klen; i++){
9697dd7cddfSDavid du Colombier 		ipad[i] ^= key[i];
9707dd7cddfSDavid du Colombier 		opad[i] ^= key[i];
9717dd7cddfSDavid du Colombier 	}
972410ea80bSDavid du Colombier 	digest = md5(ipad, Hmacblksz, nil, nil);
9737dd7cddfSDavid du Colombier 	md5(t, tlen, innerhash, digest);
974410ea80bSDavid du Colombier 	digest = md5(opad, Hmacblksz, nil, nil);
9757dd7cddfSDavid du Colombier 	md5(innerhash, MD5dlen, hash, digest);
9767dd7cddfSDavid du Colombier }
9777dd7cddfSDavid du Colombier 
9787dd7cddfSDavid du Colombier static int
md5auth(Espcb * ecb,uchar * t,int tlen,uchar * auth)9797dd7cddfSDavid du Colombier md5auth(Espcb *ecb, uchar *t, int tlen, uchar *auth)
9807dd7cddfSDavid du Colombier {
9817dd7cddfSDavid du Colombier 	uchar hash[MD5dlen];
9827dd7cddfSDavid du Colombier 	int r;
9837dd7cddfSDavid du Colombier 
9847dd7cddfSDavid du Colombier 	memset(hash, 0, MD5dlen);
985410ea80bSDavid du Colombier 	seanq_hmac_md5(hash, t, tlen, (uchar*)ecb->ahstate, BITS2BYTES(128));
9867dd7cddfSDavid du Colombier 	r = memcmp(auth, hash, ecb->ahlen) == 0;
9877dd7cddfSDavid du Colombier 	memmove(auth, hash, ecb->ahlen);
9887dd7cddfSDavid du Colombier 	return r;
9897dd7cddfSDavid du Colombier }
9907dd7cddfSDavid du Colombier 
9917dd7cddfSDavid du Colombier static void
md5ahinit(Espcb * ecb,char * name,uchar * key,unsigned klen)992410ea80bSDavid du Colombier md5ahinit(Espcb *ecb, char *name, uchar *key, unsigned klen)
9937dd7cddfSDavid du Colombier {
9947dd7cddfSDavid du Colombier 	if(klen != 128)
9957dd7cddfSDavid du Colombier 		panic("md5ahinit: bad keylen");
996410ea80bSDavid du Colombier 	klen = BITS2BYTES(klen);
9977dd7cddfSDavid du Colombier 	ecb->ahalg = name;
9987dd7cddfSDavid du Colombier 	ecb->ahblklen = 1;
999410ea80bSDavid du Colombier 	ecb->ahlen = BITS2BYTES(96);
10007dd7cddfSDavid du Colombier 	ecb->auth = md5auth;
10017dd7cddfSDavid du Colombier 	ecb->ahstate = smalloc(klen);
10027dd7cddfSDavid du Colombier 	memmove(ecb->ahstate, key, klen);
10037dd7cddfSDavid du Colombier }
10047dd7cddfSDavid du Colombier 
1005410ea80bSDavid du Colombier 
1006410ea80bSDavid du Colombier /*
1007410ea80bSDavid du Colombier  * des, single and triple
1008410ea80bSDavid du Colombier  */
1009410ea80bSDavid du Colombier 
10107dd7cddfSDavid du Colombier static int
descipher(Espcb * ecb,uchar * p,int n)10117dd7cddfSDavid du Colombier descipher(Espcb *ecb, uchar *p, int n)
10127dd7cddfSDavid du Colombier {
10137dd7cddfSDavid du Colombier 	DESstate *ds = ecb->espstate;
10147dd7cddfSDavid du Colombier 
10157dd7cddfSDavid du Colombier 	if(ecb->incoming) {
1016410ea80bSDavid du Colombier 		memmove(ds->ivec, p, Desblk);
1017410ea80bSDavid du Colombier 		desCBCdecrypt(p + Desblk, n - Desblk, ds);
10187dd7cddfSDavid du Colombier 	} else {
1019410ea80bSDavid du Colombier 		memmove(p, ds->ivec, Desblk);
1020410ea80bSDavid du Colombier 		desCBCencrypt(p + Desblk, n - Desblk, ds);
10217dd7cddfSDavid du Colombier 	}
1022410ea80bSDavid du Colombier 	return 1;
1023410ea80bSDavid du Colombier }
1024410ea80bSDavid du Colombier 
1025410ea80bSDavid du Colombier static int
des3cipher(Espcb * ecb,uchar * p,int n)1026410ea80bSDavid du Colombier des3cipher(Espcb *ecb, uchar *p, int n)
1027410ea80bSDavid du Colombier {
1028410ea80bSDavid du Colombier 	DES3state *ds = ecb->espstate;
1029410ea80bSDavid du Colombier 
1030410ea80bSDavid du Colombier 	if(ecb->incoming) {
1031410ea80bSDavid du Colombier 		memmove(ds->ivec, p, Desblk);
1032410ea80bSDavid du Colombier 		des3CBCdecrypt(p + Desblk, n - Desblk, ds);
1033410ea80bSDavid du Colombier 	} else {
1034410ea80bSDavid du Colombier 		memmove(p, ds->ivec, Desblk);
1035410ea80bSDavid du Colombier 		des3CBCencrypt(p + Desblk, n - Desblk, ds);
10367dd7cddfSDavid du Colombier 	}
10377dd7cddfSDavid du Colombier 	return 1;
10387dd7cddfSDavid du Colombier }
10397dd7cddfSDavid du Colombier 
10407dd7cddfSDavid du Colombier static void
desespinit(Espcb * ecb,char * name,uchar * k,unsigned n)1041410ea80bSDavid du Colombier desespinit(Espcb *ecb, char *name, uchar *k, unsigned n)
10427dd7cddfSDavid du Colombier {
1043410ea80bSDavid du Colombier 	uchar key[Desblk], ivec[Desblk];
10447dd7cddfSDavid du Colombier 	int i;
10457dd7cddfSDavid du Colombier 
1046410ea80bSDavid du Colombier 	n = BITS2BYTES(n);
1047410ea80bSDavid du Colombier 	if(n > Desblk)
1048410ea80bSDavid du Colombier 		n = Desblk;
10497dd7cddfSDavid du Colombier 	memset(key, 0, sizeof(key));
10507dd7cddfSDavid du Colombier 	memmove(key, k, n);
1051410ea80bSDavid du Colombier 	for(i = 0; i < Desblk; i++)
10527dd7cddfSDavid du Colombier 		ivec[i] = nrand(256);
10537dd7cddfSDavid du Colombier 	ecb->espalg = name;
1054410ea80bSDavid du Colombier 	ecb->espblklen = Desblk;
1055410ea80bSDavid du Colombier 	ecb->espivlen = Desblk;
1056410ea80bSDavid du Colombier 
10577dd7cddfSDavid du Colombier 	ecb->cipher = descipher;
10587dd7cddfSDavid du Colombier 	ecb->espstate = smalloc(sizeof(DESstate));
10597dd7cddfSDavid du Colombier 	setupDESstate(ecb->espstate, key, ivec);
10607dd7cddfSDavid du Colombier }
10617dd7cddfSDavid du Colombier 
1062410ea80bSDavid du Colombier static void
des3espinit(Espcb * ecb,char * name,uchar * k,unsigned n)1063410ea80bSDavid du Colombier des3espinit(Espcb *ecb, char *name, uchar *k, unsigned n)
1064410ea80bSDavid du Colombier {
1065410ea80bSDavid du Colombier 	uchar key[3][Desblk], ivec[Desblk];
1066410ea80bSDavid du Colombier 	int i;
1067410ea80bSDavid du Colombier 
1068410ea80bSDavid du Colombier 	n = BITS2BYTES(n);
1069410ea80bSDavid du Colombier 	if(n > Des3keysz)
1070410ea80bSDavid du Colombier 		n = Des3keysz;
1071410ea80bSDavid du Colombier 	memset(key, 0, sizeof(key));
1072410ea80bSDavid du Colombier 	memmove(key, k, n);
1073410ea80bSDavid du Colombier 	for(i = 0; i < Desblk; i++)
1074410ea80bSDavid du Colombier 		ivec[i] = nrand(256);
1075410ea80bSDavid du Colombier 	ecb->espalg = name;
1076410ea80bSDavid du Colombier 	ecb->espblklen = Desblk;
1077410ea80bSDavid du Colombier 	ecb->espivlen = Desblk;
1078410ea80bSDavid du Colombier 
1079410ea80bSDavid du Colombier 	ecb->cipher = des3cipher;
1080410ea80bSDavid du Colombier 	ecb->espstate = smalloc(sizeof(DES3state));
1081410ea80bSDavid du Colombier 	setupDES3state(ecb->espstate, key, ivec);
1082410ea80bSDavid du Colombier }
1083410ea80bSDavid du Colombier 
1084410ea80bSDavid du Colombier 
1085410ea80bSDavid du Colombier /*
1086410ea80bSDavid du Colombier  * interfacing to devip
1087410ea80bSDavid du Colombier  */
1088c168f9f3SDavid du Colombier void
espinit(Fs * fs)1089c168f9f3SDavid du Colombier espinit(Fs *fs)
1090c168f9f3SDavid du Colombier {
1091c168f9f3SDavid du Colombier 	Proto *esp;
1092c168f9f3SDavid du Colombier 
1093c168f9f3SDavid du Colombier 	esp = smalloc(sizeof(Proto));
1094c168f9f3SDavid du Colombier 	esp->priv = smalloc(sizeof(Esppriv));
1095c168f9f3SDavid du Colombier 	esp->name = "esp";
1096c168f9f3SDavid du Colombier 	esp->connect = espconnect;
1097c168f9f3SDavid du Colombier 	esp->announce = nil;
1098c168f9f3SDavid du Colombier 	esp->ctl = espctl;
1099c168f9f3SDavid du Colombier 	esp->state = espstate;
1100c168f9f3SDavid du Colombier 	esp->create = espcreate;
1101c168f9f3SDavid du Colombier 	esp->close = espclose;
1102c168f9f3SDavid du Colombier 	esp->rcv = espiput;
1103c168f9f3SDavid du Colombier 	esp->advise = espadvise;
1104c168f9f3SDavid du Colombier 	esp->stats = espstats;
1105c168f9f3SDavid du Colombier 	esp->local = esplocal;
1106c168f9f3SDavid du Colombier 	esp->remote = espremote;
1107c168f9f3SDavid du Colombier 	esp->ipproto = IP_ESPPROTO;
1108c168f9f3SDavid du Colombier 	esp->nc = Nchans;
1109c168f9f3SDavid du Colombier 	esp->ptclsize = sizeof(Espcb);
1110c168f9f3SDavid du Colombier 
1111c168f9f3SDavid du Colombier 	Fsproto(fs, esp);
1112c168f9f3SDavid du Colombier }
1113