ref: 206fff9e92b75aa0e3151c2fd657e93bb458581c
parent: eff99e9be0f63c0cc54146e21af5bd423238c226
author: halfwit <michaelmisch1985@gmail.com>
date: Wed Jul 23 19:07:16 EDT 2025
Bring in the rest of our servers, start on /proc
--- a/Makefile
+++ b/Makefile
@@ -12,8 +12,12 @@
LIBS1=\
kern/libkern.a\
libip/libip.a\
+ libauth/libauth.a\
+ libauthsrv/libauthsrv.a\
libmemdraw/libmemdraw.a\
libmemlayer/libmemlayer.a\
+ libmp/libmp.a\
+ libsec/libsec.a\
libdraw/libdraw.a\
gui-$(GUI)/libgui.a\
libc/libc.a\
@@ -34,6 +38,12 @@
kern/libkern.a:
(cd kern; $(MAKE))
+libauth/libauth.a:
+ (cd libauth; $(MAKE))
+
+libauthsrv/libauthsrv.a:
+ (cd libauthsrv; $(MAKE))
+
libip/libip.a:
(cd libip; $(MAKE))
@@ -45,6 +55,12 @@
libdraw/libdraw.a:
(cd libdraw; $(MAKE))
+
+libsec/libsec.a:
+ (cd libsec; $(MAKE))
+
+libmp/libmp.a:
+ (cd libmp; $(MAKE))
libc/libc.a:
(cd libc; $(MAKE))
--- a/TODO
+++ b/TODO
@@ -27,11 +27,11 @@
- devip - #I: needed, networking
- devlfd - #L: needed, local fd
- devmnt - #M: needed, client shares
- - devpipe - #|: needed for pipes
+ - devpipe - #|: needed, for pipes
- devroot - #/: needed, base of stack
- - devssl - #D: !needed, ssl - eventually add
- - devtls - #a: !needed, tls - eventually add
- - devproc - #p: needed, /dev/proc, overlay any existing
+ - devssl - #D: needed, ssl
+ - devtls - #a: needed, tls
+ - devproc - #p: needed, /proc, overlay any existing
- devsrv - #s: !needed, eventually add
- devbin - #9: needed, just a path that can use PLAN9
- devtab - meta, needed - add/remove any devices that we add/remove, from here as well
--- /dev/null
+++ b/include/auth.h
@@ -1,0 +1,146 @@
+#ifdef PLAN9
+#pragma src "/sys/src/libauth"
+#pragma lib "libauth.a"
+#endif
+
+/*
+ * 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*);
+
+extern int login(char*, 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*);
+#ifdef VARARGCK
+#pragma varargck type "A" Attr*
+#endif
+
+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);
+#ifdef VARARGCK
+#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/include/authsrv.h
@@ -1,0 +1,217 @@
+/*
+ * Interface for talking to authentication server.
+ */
+typedef struct Ticket Ticket;
+typedef struct Ticketreq Ticketreq;
+typedef struct Authenticator Authenticator;
+typedef struct Nvrsafe Nvrsafe;
+typedef struct Passwordreq Passwordreq;
+typedef struct OChapreply OChapreply;
+typedef struct OMSchapreply OMSchapreply;
+
+typedef struct Authkey Authkey;
+
+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 */
+ PASSWDLEN= 28, /* password 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 */
+};
+
+/* encryption numberings (anti-replay) */
+enum
+{+ AuthTreq=1, /* ticket request */
+ AuthChal=2, /* challenge box request */
+ AuthPass=3, /* change password */
+ AuthOK=4, /* fixed length reply follows */
+ AuthErr=5, /* error follows */
+ AuthMod=6, /* modify user */
+ AuthApop=7, /* apop authentication for pop3 */
+ AuthOKvar=9, /* variable length reply follows */
+ AuthChap=10, /* chap authentication for ppp */
+ AuthMSchap=11, /* MS chap authentication for ppp */
+ AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */
+ AuthHttp=13, /* http domain login */
+ AuthVNC=14, /* VNC server login (deprecated) */
+ AuthPAK=19, /* authenticated diffie hellman key agreement */
+ AuthTs=64, /* ticket encrypted with server's key */
+ AuthTc, /* ticket encrypted with client's key */
+ AuthAs, /* server generated authenticator */
+ AuthAc, /* client generated authenticator */
+ AuthTp, /* ticket encrypted with client's key for password change */
+ AuthHr, /* http reply */
+};
+
+struct Ticketreq
+{+ char type;
+ char authid[ANAMELEN]; /* server's encryption id */
+ char authdom[DOMLEN]; /* server's authentication domain */
+ char chal[CHALLEN]; /* challenge from server */
+ char hostid[ANAMELEN]; /* host's encryption id */
+ char uid[ANAMELEN]; /* uid of requesting user on host */
+};
+#define TICKREQLEN (3*ANAMELEN+CHALLEN+DOMLEN+1)
+
+struct Ticket
+{+ char num; /* replay protection */
+ char chal[CHALLEN]; /* server challenge */
+ char cuid[ANAMELEN]; /* uid on client */
+ char suid[ANAMELEN]; /* uid on server */
+ uchar key[NONCELEN]; /* nonce key */
+
+ char form; /* (not transmitted) format (0 = des, 1 = ccpoly) */
+};
+#define MAXTICKETLEN (12+CHALLEN+2*ANAMELEN+NONCELEN+16)
+
+struct Authenticator
+{+ char num; /* replay protection */
+ char chal[CHALLEN]; /* server/client challenge */
+ uchar rand[NONCELEN]; /* server/client nonce */
+};
+#define MAXAUTHENTLEN (12+CHALLEN+NONCELEN+16)
+
+struct Passwordreq
+{+ char num;
+ char old[PASSWDLEN];
+ char new[PASSWDLEN];
+ char changesecret;
+ char secret[SECRETLEN]; /* new secret */
+};
+#define MAXPASSREQLEN (12+2*PASSWDLEN+1+SECRETLEN+16)
+
+struct OChapreply
+{+ uchar id;
+ char uid[ANAMELEN];
+ char resp[OMD5LEN];
+};
+#define OCHAPREPLYLEN (1+ANAMELEN+OMD5LEN)
+
+struct OMSchapreply
+{+ char uid[ANAMELEN];
+ char LMresp[24]; /* Lan Manager response */
+ char NTresp[24]; /* NT response */
+};
+#define OMSCHAPREPLYLEN (ANAMELEN+24+24)
+
+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 to/from wire format
+ */
+extern int convT2M(Ticket*, char*, int, Authkey*);
+extern int convM2T(char*, int, Ticket*, Authkey*);
+extern int convA2M(Authenticator*, char*, int, Ticket*);
+extern int convM2A(char*, int, Authenticator*, Ticket*);
+extern int convTR2M(Ticketreq*, char*, int);
+extern int convM2TR(char*, int, Ticketreq*);
+extern int convPR2M(Passwordreq*, char*, int, Ticket*);
+extern int convM2PR(char*, int, Passwordreq*, Ticket*);
+
+/*
+ * 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);
+
+/*
+ * Nvram interface
+ */
+enum {+ NVread = 0, /* just read */
+ NVwrite = 1<<0, /* always prompt and rewrite nvram */
+ NVwriteonerr = 1<<1, /* prompt and rewrite nvram when corrupt */
+ NVwritemem = 1<<2, /* don't prompt, write nvram from argument */
+};
+
+/* storage layout */
+struct Nvrsafe
+{+ char machkey[DESKEYLEN]; /* file server's authid's des key */
+ uchar machsum;
+ char authkey[DESKEYLEN]; /* authid's des key from password */
+ uchar authsum;
+ /*
+ * file server config string of device holding full configuration;
+ * secstore key on non-file-servers.
+ */
+ char config[CONFIGLEN];
+ uchar configsum;
+ char authid[ANAMELEN]; /* auth userid, e.g., bootes */
+ uchar authidsum;
+ char authdom[DOMLEN]; /* auth domain, e.g., cs.bell-labs.com */
+ uchar authdomsum;
+
+ uchar aesmachkey[AESKEYLEN];
+ uchar aesmachsum;
+};
+
+extern uchar nvcsum(void*, int);
+extern int readnvram(Nvrsafe*, int);
+extern char* readcons(char*, char*, int);
+
+/*
+ * call up auth server
+ */
+extern int authdial(char *netroot, char *authdom);
+
+/*
+ * exchange messages with auth server
+ */
+extern int _asgetpakkey(int, Ticketreq*, Authkey*);
+extern int _asgetticket(int, Ticketreq*, char*, int);
+extern int _asrequest(int, Ticketreq*);
+extern int _asgetresp(int, Ticket*, Authenticator*, Authkey *);
+extern int _asrdresp(int, char*, int);
+
+/*
+ * AuthPAK protocol
+ */
+typedef struct PAKpriv PAKpriv;
+struct PAKpriv
+{+ int isclient;
+ uchar x[PAKXLEN];
+ uchar y[PAKYLEN];
+};
+
+extern void authpak_hash(Authkey *k, char *u);
+extern void authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient);
+extern int authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN]);
--- a/include/lib.h
+++ b/include/lib.h
@@ -227,6 +227,7 @@
ulong flags;
};
+#pragma pack(push, 1)
typedef struct Tos Tos;
typedef struct Plink Plink;
@@ -247,8 +248,7 @@
u32int clock;
/* top of stack is here */
};
-
-extern Tos *_tos;
+#pragma pack(pop)
enum{FmtWidth = 1,
--- /dev/null
+++ b/include/libsec.h
@@ -1,0 +1,589 @@
+#ifndef _MPINT
+typedef struct mpint mpint;
+#endif
+
+/*
+ * AES definitions
+ */
+
+enum
+{+ AESbsize= 16,
+ AESmaxkey= 32,
+ AESmaxrounds= 14
+};
+
+typedef struct AESstate AESstate;
+struct AESstate
+{+ ulong setup;
+ ulong offset;
+ int rounds;
+ int keybytes;
+ void *ekey; /* expanded encryption round key */
+ void *dkey; /* expanded decryption round key */
+ uchar key[AESmaxkey]; /* unexpanded key */
+ uchar ivec[AESbsize]; /* initialization vector */
+ uchar storage[512]; /* storage for expanded keys */
+};
+
+/* block ciphers */
+extern void (*aes_encrypt)(ulong rk[], int Nr, uchar pt[16], uchar ct[16]);
+extern void (*aes_decrypt)(ulong rk[], int Nr, uchar ct[16], uchar pt[16]);
+
+void setupAESstate(AESstate *s, uchar key[], int nkey, uchar *ivec);
+
+void aesCBCencrypt(uchar *p, int len, AESstate *s);
+void aesCBCdecrypt(uchar *p, int len, AESstate *s);
+void aesCFBencrypt(uchar *p, int len, AESstate *s);
+void aesCFBdecrypt(uchar *p, int len, AESstate *s);
+void aesOFBencrypt(uchar *p, int len, AESstate *s);
+
+void aes_xts_encrypt(AESstate *tweak, AESstate *ecb, uvlong sectorNumber, uchar *input, uchar *output, ulong len);
+void aes_xts_decrypt(AESstate *tweak, AESstate *ecb, uvlong sectorNumber, uchar *input, uchar *output, ulong len);
+
+typedef struct AESGCMstate AESGCMstate;
+struct AESGCMstate
+{+ AESstate a;
+
+ 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,
+ XChachaIVlen= 192/8,
+};
+
+typedef struct Chachastate Chachastate;
+struct Chachastate
+{+ union{+ u32int input[16];
+ struct {+ u32int constant[4];
+ u32int key[8];
+ u32int counter;
+ u32int iv[3];
+ };
+ };
+ u32int xkey[8];
+ 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 hchacha(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds);
+
+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 xkey[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 salsa_core(u32int in[16], u32int out[16], int rounds);
+
+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[32], uchar[8], 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[3][32], uchar[8], 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 */
+ RIPEMD160dlen= 20, /* RIPEMD-160 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* ripemd160(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*);
+
+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);
+RSApub* asn1toRSApub(uchar*, 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* X509rsaverify(uchar *cert, int ncert, RSApub *pk);
+char* X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk);
+
+void X509dump(uchar *cert, int ncert);
+
+mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+int pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+int asn1encodeRSApub(RSApub *pk, uchar *buf, int len);
+int asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*),
+ uchar *digest, uchar *buf, int len);
+
+
+/*
+ * 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*);
+
+/*
+ * TLS
+ */
+typedef struct Thumbprint{+ struct Thumbprint *next;
+ uchar hash[SHA2_256dlen];
+ uchar len;
+} 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, char *tag);
+void freeThumbprints(Thumbprint *ok);
+int okThumbprint(uchar *hash, int len, Thumbprint *ok);
+int okCertificate(uchar *cert, int len, Thumbprint *ok);
+
+/* readcert.c */
+uchar *readcert(char *filename, int *pcertlen);
+PEMChain*readcertchain(char *filename);
+
+typedef struct ECpoint{+ int inf;
+ mpint *x;
+ mpint *y;
+ mpint *z; /* nil when using affine coordinates */
+} ECpoint;
+
+typedef ECpoint ECpub;
+typedef struct ECpriv{+ ECpoint a;
+ 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, char *name, int nname, ECdomain *dom);
+char* X509ecdsaverify(uchar *cert, int ncert, ECdomain *dom, ECpub *pub);
+char* X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, 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);
+void secp384r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+
+/*
+ * 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]);
+int 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);
+
+/* scrypt password-based key derivation function */
+char* scrypt(uchar *p, ulong plen, uchar *s, ulong slen,
+ ulong N, ulong R, ulong P,
+ uchar *d, ulong dlen);
+
+/* 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);
--- /dev/null
+++ b/include/mp.h
@@ -1,0 +1,176 @@
+#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);
+void mpasr(mpint *b, int shift, 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 */
+
+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 m;
+ int (*reduce)(Mfield*, mpint*, mpint*);
+};
+
+mpint *mpfield(mpint*);
+
+Mfield *gmfield(mpint*);
+Mfield *cnfield(mpint*);
--- a/include/u.h
+++ b/include/u.h
@@ -27,3 +27,4 @@
#undef unmount
#undef pipe
#undef iounit
+extern int debug;
\ No newline at end of file
--- a/include/user.h
+++ b/include/user.h
@@ -97,7 +97,7 @@
extern void setmalloctag(void*, uintptr);
extern void setrealloctag(void*, uintptr);
extern int errstr(char*, uint);
-extern char *estrdup(char*);
+//extern char *estrdup(char*);
extern int rerrstr(char*, uint);
extern int notify(void (*)(void*, char*));
extern int noted(int);
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -18,9 +18,12 @@
devip-$(OS).$O\
devip.$O\
devmnt.$O\
+ devproc.$O\
devpipe.$O\
devroot.$O\
+ devssl.$O\
devtab.$O\
+ devtls.$O\
dirread.$O\
error.$O\
fpa.$O\
--- a/kern/arm.c
+++ b/kern/arm.c
@@ -5,14 +5,15 @@
#include "proc.h"
enum {- fI = 1<<25,
- fP = 1<<24,
- fLi = 1<<24,
- fU = 1<<23,
- fB = 1<<22,
- fW = 1<<21,
- fL = 1<<20,
- fS = 1<<20,
+ fI = 1<<25, // Immediate bit
+ fP = 1<<24, // Pre/post indexing bit
+ fLi = 1<<24, // Link bit for branching
+ fU = 1<<23, // Up/down bit
+ fB = 1<<22, // Byte/word bit
+ fW = 1<<21, // Write back bit
+ fL = 1<<20, // Load/store bit
+ fS = 1<<20, // Set condition bit in multiply
+ fA = 1<<21, // Accumulate bit in multiply
fSg = 1<<6,
fH = 1<<5,
};
@@ -20,7 +21,7 @@
void
invalid(u32int instr)
{- panic("undefined instruction %.8ux @ %.8ux\n", instr, up->R[15] - 4);+ panic("undefined instruction %.8ux @ %.8ux", instr, up->R[15] - 4);}
u32int
@@ -33,10 +34,9 @@
}
static u32int
-doshift(u32int instr, uchar *carry)
+doshift(u32int instr, uint *carry)
{ulong amount, val;
-print("doshift\n");val = up->R[instr & 15];
if(instr & (1<<4)) {if(instr & (1<<7))
@@ -89,6 +89,7 @@
}
}
+/* Single Data Transfer instruction */
static void
single(u32int instr)
{@@ -97,10 +98,9 @@
u32int *Rn, *Rd;
void *targ;
Segment *seg;
-print("single\n"); if(instr & fI) {- uchar carry = 0;
+ uint carry = 0;
if(instr & (1<<4))
invalid(instr);
offset = doshift(instr, &carry);
@@ -110,7 +110,8 @@
offset = - offset;
Rn = up->R + ((instr >> 16) & 15);
Rd = up->R + ((instr >> 12) & 15);
-
+ if (debug)
+ print("single: Rn=%d Rd=%d offset=%d\n", ((instr >> 16) & 15), ((instr >> 12) & 15), offset);if((instr & (fW | fP)) == fW)
invalid(instr);
if(Rn == up->R + 15) {@@ -129,18 +130,18 @@
*(u32int*) targ = *Rd;
break;
case fB:
- *(uchar*) targ = *Rd;
+ *(uint*) targ = *Rd;
break;
case fL:
*Rd = *(u32int*) targ;
break;
case fB | fL:
- *Rd = *(uchar*) targ;
+ *Rd = *(uint*) targ;
break;
}
if(Rd == up->R + 15 && !(instr & fL)) {if(instr & fB)
- *(uchar*) targ += 8;
+ *(uint*) targ += 8;
else
*(u32int*) targ += 8;
}
@@ -156,10 +157,11 @@
{u32int *Rm, *Rn, *Rd, *targ, addr, old, new;
Segment *seg;
-print("swap\n");Rm = up->R + (instr & 15);
Rd = up->R + ((instr >> 12) & 15);
Rn = up->R + ((instr >> 16) & 15);
+ if(debug)
+ print("swap: Rm=%d Rd=%d Rn=%d\n", (instr & 15), ((instr >> 12) & 15), ((instr >> 16) & 15));if(Rm == up->R + 15 || Rd == up->R + 15 || Rn == up->R + 15)
invalid(instr);
addr = *Rn;
@@ -184,11 +186,10 @@
}
static u32int
-add(u32int a, u32int b, uchar type, uchar *carry, uchar *overflow)
+add(u32int a, u32int b, uint type, uint *carry, uint *overflow)
{u32int res1;
u64int res2;
-print("add\n"); if(type) {res2 = (u64int)a - b + *carry - 1;
res1 = res2;
@@ -211,10 +212,9 @@
alu(u32int instr)
{u32int Rn, *Rd, operand, shift, result, op;
- uchar carry, overflow;
+ uint carry, overflow;
Rn = up->R[(instr >> 16) & 15];
Rd = up->R + ((instr >> 12) & 15);
-print("alu\n"); if(((instr >> 16) & 15) == 15) {Rn += 4;
if(!(instr & fI) && (instr & (1<<4)))
@@ -238,7 +238,7 @@
op = (instr >> 21) & 15;
if(op >= 8 && op <= 11 && !(instr & fS))
- sysfatal("no PSR transfers plz");+ panic("no PSR transfers plz");if(op >= 5 && op < 8)
carry = (up->CPSR & flC) != 0;
switch(op) {@@ -253,6 +253,8 @@
case 15: result = ~operand; break;
default: result = 0; /* never happens */
}
+ if(debug)
+ print("alu: Rn=%d Rd=%d op=%d operand=%.8ux\n", ((instr >> 16) & 15), ((instr >> 12) & 15), ((instr >> 21) & 15), operand); if(instr & fS) {up->CPSR &= ~FLAGS;
if(result == 0)
@@ -272,7 +274,7 @@
branch(u32int instr)
{long offset;
-print("branch\n"); +
offset = instr & ((1<<24) - 1);
if(offset & (1<<23))
offset |= ~((1 << 24) - 1);
@@ -287,7 +289,7 @@
{u32int offset, target, *Rn, *Rd;
Segment *seg;
-print("halfword\n"); +
if(instr & (1<<22)) {offset = (instr & 15) | ((instr >> 4) & 0xF0);
} else {@@ -301,6 +303,8 @@
invalid(instr);
Rn = up->R + ((instr >> 16) & 15);
Rd = up->R + ((instr >> 12) & 15);
+ if(debug)
+ print("halfword: Rn=%d, Rd=%d\n", ((instr >> 16) & 15), ((instr >> 12) & 15)); target = *Rn;
if(instr & fP)
target += offset;
@@ -307,7 +311,7 @@
if(instr & fH)
target = evenaddr(target, 1);
switch(instr & (fSg | fH | fL)) {- case fSg: *(uchar*) vaddr(target, 1, &seg) = *Rd; break;
+ case fSg: *(uint*) vaddr(target, 1, &seg) = *Rd; break;
case fSg | fL: *Rd = (long) *(char*) vaddr(target, 1, &seg); break;
case fH: case fSg | fH: *(uint16_t*) vaddr(target, 2, &seg) = *Rd; break;
case fH | fL: *Rd = *(uint16_t*) vaddr(target, 2, &seg); break;
@@ -328,15 +332,15 @@
Segment *seg;
if(instr & (1<<22))
- invalid(instr);
-print("block\n"); + invalid(instr);
Rn = up->R +((instr >> 16) & 15);
+ if(debug)
+ print("block: Rn=%d\n", ((instr >> 16) & 15)); targ = evenaddr(*Rn, 3);
if(instr & fU) { for(i = 0; i < 16; i++) {if(!(instr & (1<<i)))
continue;
-print("%d\n", i);if(instr & fP)
targ += 4;
if(instr & fL)
@@ -370,11 +374,13 @@
multiply(u32int instr)
{u32int *Rd, *Rn, *Rs, *Rm, res;
-print("multiply\n");+
Rm = up->R + (instr & 15);
Rs = up->R + ((instr >> 8) & 15);
Rn = up->R + ((instr >> 12) & 15);
Rd = up->R + ((instr >> 16) & 15);
+ if(debug)
+ print("multiply: Rm=%d Rs=%d Rn=%d Rd=%d\n", (instr & 15), ((instr >> 8) & 15), ((instr >> 12) & 15), ((instr >> 16) & 15));if(Rd == Rm || Rm == up->R + 15 || Rs == up->R + 15 || Rn == up->R + 15 || Rd == up->R + 15)
invalid(instr);
res = *Rm * *Rs;
@@ -395,7 +401,8 @@
{u32int *RdH, *RdL, *Rs, *Rm;
u64int res;
-print("multiplylong\n");+ if(debug)
+ print("multiplylong\n");Rm = up->R + (instr & 15);
Rs = up->R + ((instr >> 8) & 15);
RdL = up->R + ((instr >> 12) & 15);
@@ -428,7 +435,8 @@
{u32int *Rn, *Rd, *Rm, *targ, addr;
Segment *seg;
-print("singleex\n");+ if(debug)
+ print("singleex: Rd=%d, Rn=%d\n", ((instr >> 12) & 15), ((instr >> 16) & 15));Rd = up->R + ((instr >> 12) & 15);
Rn = up->R + ((instr >> 16) & 15);
if(Rd == up->R + 15 || Rn == up->R + 15)
@@ -454,7 +462,8 @@
void
clrex(void)
{-print("clrex\n");+ if(debug)
+ print("clrex\n");up->lladdr = 0;
up->llval = 0;
}
@@ -462,7 +471,8 @@
static void
barrier(void)
{-print("barrier\n");+ if(debug)
+ print("barrier\n");static Lock l;
lock(&l);
unlock(&l);
@@ -474,22 +484,14 @@
u32int instr;
Segment *seg;
- if (!up->S[SEGTEXT] || up->R[15] < up->S[SEGTEXT]->start || up->R[15] >= up->S[SEGTEXT]->start + up->S[SEGTEXT]->size) {- panic("step: invalid PC=%.8ux outside text segment [%.8ux, %.8ux)\n",- up->R[15],
- up->S[SEGTEXT] ? up->S[SEGTEXT]->start : 0,
- up->S[SEGTEXT] ? up->S[SEGTEXT]->start + up->S[SEGTEXT]->size : 0);
- return;
- }
-
- void *targ = vaddr(up->R[15], 4, &seg);
- instr = *(u32int*)targ;
+ instr = *(u32int*) vaddr(up->R[15], 4, &seg);
segunlock(seg);
up->R[15] += 4;
+ // CPSR testing for conditional execution
switch(instr >> 28) {- case 0x0: if(!(up->CPSR & flZ)) return; break;
+ case 0x0: if(!(up->CPSR & flZ)) return; break;
case 0x1: if(up->CPSR & flZ) return; break;
case 0x2: if(!(up->CPSR & flC)) return; break;
case 0x3: if(up->CPSR & flC) return; break;
@@ -516,7 +518,7 @@
return;
}
default:
- sysfatal("condition code %x not implemented (instr %ux, ps %ux)", instr >> 28, instr, up->R[15]);+ panic("condition code %x not implemented (instr %ux, ps %ux)", instr >> 28, instr, up->R[15]);}
if ((instr & 0x0FB00FF0) == 0x01000090)
--- /dev/null
+++ b/kern/devproc.c
@@ -1,0 +1,416 @@
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+static char *luser;
+extern int pflag;
+
+enum
+{+ Qdir = 1,
+ Qtrace,
+ Qargs,
+ Qctl,
+ Qfd,
+ Qfpregs,
+ Qkregs,
+ Qmem,
+ Qnote,
+ Qnoteid,
+ Qnotepg,
+ Qns,
+ Qproc,
+ Qregs,
+ Qsegment,
+ Qstatus,
+ Qtext,
+ Qwait,
+ Qprofile,
+ Qsyscall,
+};
+
+#define TYPE(x) ((x).path & 0xf)
+typedef struct Aux Aux;
+
+struct Aux {+ Proc *p;
+ int fd;
+ Dirtab *d;
+};
+
+Dirtab procdir[] =
+{+ "args", {Qargs}, 0, 0660,+ "ctl", {Qctl}, 0, 0600,+ "fd", {Qfd}, 0, 0444,+ "fpregs", {Qfpregs}, 0, 0400,+ "kregs", {Qkregs}, 18 * 4, 0400,+ "mem", {Qmem}, 0, 0400,+ "note", {Qnote}, 0, 0000,+ "noteid", {Qnoteid}, 0, 0664,+ "notepg", {Qnotepg}, 0, 0000,+ "ns", {Qns}, 0, 0444,+ "proc", {Qproc}, 0, 0400,+ "regs", {Qregs}, 18 * 4, 0400,+ "segment", {Qsegment}, 0, 0444,+ "status", {Qstatus}, 176, 0444,+ "text", {Qtext}, 0, 0400,+ "wait", {Qwait}, 0, 0400,+ "profile", {Qprofile}, 0, 0400,+ "syscall", {Qsyscall}, 0, 0400, +};
+
+static int
+procgen(Chan *c, char *n, Dirtab *d, int nd, int s, Dir *dp)
+{+ Qid q;
+ Proc *p;
+ int t;
+
+ q.vers = 0;
+ q.type = QTFILE;
+
+}
+
+static void
+procinit(void)
+{+
+}
+
+static char *
+readin(int pid, char *file)
+{+ char *name, *buf;
+ int fd, rc;
+
+ name = smprint("/proc/%d/%s", pid, file);+ fd = open(name, OREAD);
+ if(fd < 0){+ free(name);
+ return nil;
+ }
+ buf = malloc(1024);
+ rc = read(fd, buf, 1023);
+ if(rc < 0){+ free(buf);
+ free(name);
+ close(fd);
+ return nil;
+ }
+ buf[rc] = 0;
+ free(name);
+ close(fd);
+ return buf;
+}
+
+static int
+calcmem(Proc *p)
+{+ int i, r;
+
+ r = 0;
+ for(i = 0; i < SEGNUM; i++) {+ if(i == SEGSTACK)
+ continue;
+ if(p->S[i] == nil)
+ continue;
+ r += p->S[i]->size;
+ }
+ r = (r + 1023) / 1024;
+ return r;
+}
+
+static int
+copymem(Proc *p, char *buf, u32int addr, int len)
+{+ int i, n, r;
+
+ r = len;
+ while(len > 0) {+ for(i = 0; i < SEGNUM; i++) {+ if(p->S[i] == nil)
+ continue;
+ if(p->S[i]->start <= addr && p->S[i]->start + p->S[i]->size > addr)
+ break;
+ }
+ if(i == SEGNUM) {+ up->errstr = "bad arg in syscall";
+ return -1;
+ }
+ n = p->S[i]->start + p->S[i]->size - addr;
+ if(n > len)
+ n = len;
+ memcpy(buf, (char*)p->S[i]->data + addr - p->S[i]->start, n);
+ len -= n;
+ buf += n;
+ }
+ return r;
+}
+
+static char *
+segments(Proc *p)
+{+ char *r, *s;
+ static char *names[] = {+ [SEGTEXT] "Text",
+ [SEGSTACK] "Stack",
+ [SEGDATA] "Data",
+ [SEGBSS] "Bss",
+ };
+ int i;
+
+ r = malloc(1024);
+ s = r;
+ for(i = 0; i < SEGNUM; i++) {+ if(p->S[i] == nil)
+ continue;
+ s += sprint(s, "%-7s%c %.8ux %.8ux %4ld\n", names[i], i == SEGTEXT ? 'R' : ' ', p->S[i]->start, p->S[i]->start + p->S[i]->size, p->S[i]->dref->ref);
+ }
+ return r;
+}
+
+static Chan*
+procattach(char *spec)
+{+ /*
+ req->fid->qid = (Qid) {0, 0, 0x80};+ req->fid->aux = emallocz(sizeof(Aux));
+ ((Aux *) req->fid->aux)->fd = -1;
+ req->ofcall.qid = req->fid->qid;
+ respond(req, nil);
+ */
+}
+
+static Walkqid*
+procwalk(Chan *c, Chan *nc, char **name, int nname)
+{+ return devwalk(c, nc, name, nname, nil, 0, procgen);
+}
+
+static Chan*
+procopen(Chan *c, int omode)
+{+ Aux *a;
+ int t;
+/*
+ t = TYPE(c->qid);
+ //a = req->fid->aux;
+ switch(t) {+ case Qtext:
+ a->fd = open((char*)(a->p->path + 1), OREAD);
+ break;
+ default:
+ respond(req, nil);
+ return;
+ }
+*/
+}
+
+/*
+static int
+procgen(int n, Dir *d, void *)
+{+ int i;
+ Proc *p;
+
+ p = &plist;
+ for(i = 0;; i++) {+ p = p->next;
+ if(p == &plist)
+ return -1;
+ if(i == n)
+ break;
+ }
+ d->uid = estrdup9p(luser);
+ d->gid = estrdup9p(luser);
+ d->muid = estrdup9p(luser);
+ d->name = smprint("%d", p->pid);+ d->mode = DMDIR | 0555;
+ d->qid = (Qid) {p->pid * NQid, 0, 0x80};+ return 0;
+}
+
+static int
+procsubgen(int n, Dir *d, void *)
+{+ Dirtab *di;
+
+ if(n >= nelem(procdir) - 1)
+ return -1;
+
+ di = procdir + n;
+ d->uid = estrdup9p(luser);
+ d->gid = estrdup9p(luser);
+ d->muid = estrdup9p(luser);
+ d->name = estrdup9p(di->name);
+ d->mode = di->perm;
+ d->length = di->length;
+ d->qid = di->qid;
+ return 0;
+}
+*/
+
+static Block*
+procbread(Chan *c, long n, ulong o)
+{+ //
+}
+
+static long
+procread(Chan *c, void *a, long n, vlong offset)
+{+ Proc *p;
+ char *buf;
+ int rc;
+
+ /*
+ a = req->fid->aux;
+ if(a == nil) {+ respond(req, "the front fell off");
+ return;
+ }
+ if(req->fid->qid.path == 0) {+ dirread9p(req, procgen, nil);
+ respond(req, nil);
+ return;
+ }
+ p = a->p;
+
+ switch((int)(req->fid->qid.path % NQid)) {+ case Qdir:
+ dirread9p(req, procsubgen, nil);
+ respond(req, nil);
+ break;
+ case Qstatus:
+ buf = readin(p->pid, "status");
+ if(buf == nil)
+ responderror(req);
+ else {+ memset(buf, ' ', 27);
+ memcpy(buf, p->name, strlen(p->name));
+ sprint(buf + 149, "%d", calcmem(p));
+ buf[strlen(buf)] = ' ';
+ readstr(req, buf);
+ free(buf);
+ respond(req, nil);
+ }
+ break;
+ case Qsegment:
+ buf = segments(p);
+ readstr(req, buf);
+ free(buf);
+ respond(req, nil);
+ break;
+ case Qtext:
+ rc = pread(a->fd, req->ofcall.data, req->ifcall.count, req->ifcall.offset);
+ if(rc >= 0) {+ req->ofcall.count = rc;
+ respond(req, nil);
+ } else
+ responderror(req);
+ break;
+ case Qmem:
+ rc = copymem(p, req->ofcall.data, req->ifcall.offset, req->ifcall.count);
+ if(rc >= 0) {+ req->ofcall.count = rc;
+ respond(req, nil);
+ } else
+ responderror(req);
+ break;
+ case Qregs:
+ buf = emallocz(18 * 4);
+ memcpy(buf, p->R, 15 * 4);
+ memcpy(buf + 16 * 4, &p->CPSR, 4);
+ memcpy(buf + 17 * 4, p->R + 15, 4);
+ readbuf(req, buf, 18 * 4);
+ free(buf);
+ respond(req, nil);
+ break;
+ default:
+ respond(req, "the front fell off");
+ }
+ */
+}
+
+/*
+static void
+writeto(Req *req, char *fmt, ...)
+{+ int fd, rc;
+ va_list va;
+ char *file;
+
+ va_start(va, fmt);
+ file = vsmprint(fmt, va);
+ va_end(va);
+ fd = open(file, OWRITE);
+ free(file);
+ if(fd < 0) {+ responderror(req);
+ return;
+ }
+ rc = write(fd, req->ifcall.data, req->ifcall.count);
+ req->ofcall.count = rc;
+ if(rc < req->ifcall.count)
+ responderror(req);
+ else
+ respond(req, nil);
+ close(fd);
+}
+*/
+
+static long
+procbwrite(Chan *c, Block *b, ulong o)
+{+ //
+}
+
+static long
+procwrite(Chan *c, void *va, long n, vlong offset)
+{+ /*
+ switch((int)(req->fid->qid.path % NQid)) {+ case Qnote:
+ writeto(req, "/proc/%lld/note", req->fid->qid.path / NQid);
+ break;
+ default:
+ respond(req, "the front fell off");
+ }
+ */
+}
+
+static void
+procclose(Chan *c)
+{+ // Any that we care about on close, basically
+}
+
+static int
+procstat(Chan *c, uchar *db, int n)
+{+ return devstat(c, db, n, nil, 0, procgen);
+}
+
+Dev procdevtab = {+ 'P',
+ "proc",
+
+ devreset,
+ procinit,
+ devshutdown,
+ procattach,
+ procwalk,
+ procstat,
+ procopen,
+ devcreate,
+ procclose,
+ procread,
+ procbread,
+ procwrite,
+ procbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/kern/devssl.c
@@ -1,0 +1,1450 @@
+/*
+ * devssl - secure sockets layer
+ */
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <libsec.h>
+
+typedef struct OneWay OneWay;
+struct OneWay
+{+ QLock q;
+ QLock ctlq;
+
+ void *state; /* encryption state */
+ int slen; /* hash data length */
+ uchar *secret; /* secret */
+ ulong mid; /* message id */
+};
+
+enum
+{+ /* connection states */
+ Sincomplete= 0,
+ Sclear= 1,
+ Sencrypting= 2,
+ Sdigesting= 4,
+ Sdigenc= Sencrypting|Sdigesting,
+
+ /* encryption algorithms */
+ Noencryption= 0,
+ DESCBC= 1,
+ DESECB= 2,
+ RC4= 3
+};
+
+typedef struct Dstate Dstate;
+struct Dstate
+{+ Chan *c; /* io channel */
+ uchar state; /* state of connection */
+ int ref; /* serialized by dslock for atomic destroy */
+
+ uchar encryptalg; /* encryption algorithm */
+ ushort blocklen; /* blocking length */
+
+ ushort diglen; /* length of digest */
+ DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */
+
+ /* for SSL format */
+ int max; /* maximum unpadded data per msg */
+ int maxpad; /* maximum padded data per msg */
+
+ /* input side */
+ OneWay in;
+ Block *processed;
+ Block *unprocessed;
+
+ /* output side */
+ OneWay out;
+
+ /* protections */
+ char *user;
+ int perm;
+};
+
+enum
+{+ Maxdmsg= 1<<16,
+ Maxdstate= 512, /* max. open ssl conn's; must be a power of 2 */
+};
+
+static Lock dslock;
+static int dshiwat;
+static char *dsname[Maxdstate];
+static Dstate *dstate[Maxdstate];
+static char *encalgs;
+static char *hashalgs;
+
+enum{+ Qtopdir = 1, /* top level directory */
+ Qprotodir,
+ Qclonus,
+ Qconvdir, /* directory for a conversation */
+ Qdata,
+ Qctl,
+ Qsecretin,
+ Qsecretout,
+ Qencalgs,
+ Qhashalgs,
+};
+
+#define TYPE(x) ((x).path & 0xf)
+#define CONV(x) (((x).path >> 5)&(Maxdstate-1))
+#define QID(c, y) (((c)<<5) | (y))
+
+static void ensure(Dstate*, Block**, int);
+static void consume(Block**, uchar*, int);
+static void setsecret(OneWay*, uchar*, int);
+static Block* encryptb(Dstate*, Block*, int);
+static Block* decryptb(Dstate*, Block*);
+static Block* digestb(Dstate*, Block*, int);
+static void checkdigestb(Dstate*, Block*);
+static Chan* buftochan(char*);
+static void sslhangup(Dstate*);
+static Dstate* dsclone(Chan *c);
+static void dsnew(Chan *c, Dstate **);
+static long sslput(Dstate *s, Block * volatile b);
+
+char *sslnames[] = {+[Qclonus] "clone",
+[Qdata] "data",
+[Qctl] "ctl",
+[Qsecretin] "secretin",
+[Qsecretout] "secretout",
+[Qencalgs] "encalgs",
+[Qhashalgs] "hashalgs",
+};
+
+static int
+sslgen(Chan *c, char *n, Dirtab *d, int nd, int s, Dir *dp)
+{+ Qid q;
+ Dstate *ds;
+ char name[16], *p, *nm;
+ int ft;
+
+ USED(n);
+ USED(nd);
+ USED(d);
+
+ q.type = QTFILE;
+ q.vers = 0;
+
+ ft = TYPE(c->qid);
+ switch(ft) {+ case Qtopdir:
+ if(s == DEVDOTDOT){+ q.path = QID(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, "#D", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s > 0)
+ return -1;
+ q.path = QID(0, Qprotodir);
+ q.type = QTDIR;
+ devdir(c, q, "ssl", 0, eve, 0555, dp);
+ return 1;
+ case Qprotodir:
+ if(s == DEVDOTDOT){+ q.path = QID(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, ".", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < dshiwat) {+ q.path = QID(s, Qconvdir);
+ q.type = QTDIR;
+ ds = dstate[s];
+ if(ds != 0)
+ nm = ds->user;
+ else
+ nm = eve;
+ if(dsname[s] == nil){+ sprint(name, "%d", s);
+ kstrdup(&dsname[s], name);
+ }
+ devdir(c, q, dsname[s], 0, nm, 0555, dp);
+ return 1;
+ }
+ if(s > dshiwat)
+ return -1;
+ q.path = QID(0, Qclonus);
+ devdir(c, q, "clone", 0, eve, 0555, dp);
+ return 1;
+ case Qconvdir:
+ if(s == DEVDOTDOT){+ q.path = QID(0, Qprotodir);
+ q.type = QTDIR;
+ devdir(c, q, "ssl", 0, eve, 0555, dp);
+ return 1;
+ }
+ ds = dstate[CONV(c->qid)];
+ if(ds != 0)
+ nm = ds->user;
+ else
+ nm = eve;
+ switch(s) {+ default:
+ return -1;
+ case 0:
+ q.path = QID(CONV(c->qid), Qctl);
+ p = "ctl";
+ break;
+ case 1:
+ q.path = QID(CONV(c->qid), Qdata);
+ p = "data";
+ break;
+ case 2:
+ q.path = QID(CONV(c->qid), Qsecretin);
+ p = "secretin";
+ break;
+ case 3:
+ q.path = QID(CONV(c->qid), Qsecretout);
+ p = "secretout";
+ break;
+ case 4:
+ q.path = QID(CONV(c->qid), Qencalgs);
+ p = "encalgs";
+ break;
+ case 5:
+ q.path = QID(CONV(c->qid), Qhashalgs);
+ p = "hashalgs";
+ break;
+ }
+ devdir(c, q, p, 0, nm, 0660, dp);
+ return 1;
+ case Qclonus:
+ devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, eve, 0555, dp);
+ return 1;
+ default:
+ ds = dstate[CONV(c->qid)];
+ if(ds != 0)
+ nm = ds->user;
+ else
+ nm = eve;
+ devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, nm, 0660, dp);
+ return 1;
+ }
+}
+
+static Chan*
+sslattach(char *spec)
+{+ Chan *c;
+
+ c = devattach('D', spec);+ c->qid.path = QID(0, Qtopdir);
+ c->qid.vers = 0;
+ c->qid.type = QTDIR;
+ return c;
+}
+
+static Walkqid*
+sslwalk(Chan *c, Chan *nc, char **name, int nname)
+{+ return devwalk(c, nc, name, nname, nil, 0, sslgen);
+}
+
+static int
+sslstat(Chan *c, uchar *db, int n)
+{+ return devstat(c, db, n, nil, 0, sslgen);
+}
+
+static Chan*
+sslopen(Chan *c, int omode)
+{+ Dstate *s, **pp;
+ int ft;
+
+ ft = TYPE(c->qid);
+ switch(ft) {+ default:
+ panic("sslopen");+ case Qtopdir:
+ case Qprotodir:
+ case Qconvdir:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ case Qclonus:
+ s = dsclone(c);
+ if(s == 0)
+ error(Enodev);
+ break;
+ case Qctl:
+ case Qdata:
+ case Qsecretin:
+ case Qsecretout:
+ if(waserror()) {+ unlock(&dslock);
+ nexterror();
+ }
+ lock(&dslock);
+ pp = &dstate[CONV(c->qid)];
+ s = *pp;
+ if(s == 0)
+ dsnew(c, pp);
+ else {+ devpermcheck(s->user, s->perm, omode);
+ s->ref++;
+ }
+ unlock(&dslock);
+ poperror();
+ break;
+ case Qencalgs:
+ case Qhashalgs:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ }
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static int
+sslwstat(Chan *c, uchar *db, int n)
+{+ Dir *dir;
+ Dstate *s;
+ int m;
+
+ s = dstate[CONV(c->qid)];
+ if(s == 0)
+ error(Ebadusefd);
+ if(strcmp(s->user, up->user) != 0)
+ error(Eperm);
+
+ dir = smalloc(sizeof(Dir)+n);
+ m = convM2D(db, n, &dir[0], (char*)&dir[1]);
+ if(m == 0){+ free(dir);
+ error(Eshortstat);
+ }
+
+ if(!emptystr(dir->uid))
+ kstrdup(&s->user, dir->uid);
+ if(dir->mode != -1)
+ s->perm = dir->mode;
+
+ free(dir);
+ return m;
+}
+
+static void
+sslclose(Chan *c)
+{+ Dstate *s;
+ int ft;
+
+ ft = TYPE(c->qid);
+ switch(ft) {+ case Qctl:
+ case Qdata:
+ case Qsecretin:
+ case Qsecretout:
+ if((c->flag & COPEN) == 0)
+ break;
+
+ s = dstate[CONV(c->qid)];
+ if(s == 0)
+ break;
+
+ lock(&dslock);
+ if(--s->ref > 0) {+ unlock(&dslock);
+ break;
+ }
+ dstate[CONV(c->qid)] = 0;
+ unlock(&dslock);
+
+ if(s->user != nil)
+ free(s->user);
+ sslhangup(s);
+ if(s->c)
+ cclose(s->c);
+ secfree(s->in.secret);
+ secfree(s->out.secret);
+ secfree(s->in.state);
+ secfree(s->out.state);
+ free(s);
+
+ }
+}
+
+/*
+ * make sure we have at least 'n' bytes in list 'l'
+ */
+static void
+ensure(Dstate *s, Block **l, int n)
+{+ int sofar, i;
+ Block *b, *bl;
+
+ sofar = 0;
+ for(b = *l; b; b = b->next){+ sofar += BLEN(b);
+ if(sofar >= n)
+ return;
+ l = &b->next;
+ }
+
+ while(sofar < n){+ bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
+ if(bl == 0)
+ nexterror();
+ *l = bl;
+ i = 0;
+ for(b = bl; b; b = b->next){+ i += BLEN(b);
+ l = &b->next;
+ }
+ if(i == 0)
+ error(Ehungup);
+ sofar += i;
+ }
+}
+
+/*
+ * copy 'n' bytes from 'l' into 'p' and free
+ * the bytes in 'l'
+ */
+static void
+consume(Block **l, uchar *p, int n)
+{+ Block *b;
+ int i;
+
+ for(; *l && n > 0; n -= i){+ b = *l;
+ i = BLEN(b);
+ if(i > n)
+ i = n;
+ memmove(p, b->rp, i);
+ b->rp += i;
+ p += i;
+ if(BLEN(b) < 0)
+ panic("consume");+ if(BLEN(b))
+ break;
+ *l = b->next;
+ freeb(b);
+ }
+}
+
+/*
+ * give back n bytes
+static void
+regurgitate(Dstate *s, uchar *p, int n)
+{+ Block *b;
+
+ if(n <= 0)
+ return;
+ b = s->unprocessed;
+ if(s->unprocessed == nil || b->rp - b->base < n) {+ b = allocb(n);
+ memmove(b->wp, p, n);
+ b->wp += n;
+ b->next = s->unprocessed;
+ s->unprocessed = b;
+ } else {+ b->rp -= n;
+ memmove(b->rp, p, n);
+ }
+}
+ */
+
+/*
+ * remove at most n bytes from the queue, if discard is set
+ * dump the remainder
+ */
+static Block*
+qtake(Block **l, int n, int discard)
+{+ Block *nb, *b, *first;
+ int i;
+
+ first = *l;
+ for(b = first; b; b = b->next){+ i = BLEN(b);
+ if(i == n){+ if(discard){+ freeblist(b->next);
+ *l = 0;
+ } else
+ *l = b->next;
+ b->next = 0;
+ return first;
+ } else if(i > n){+ i -= n;
+ if(discard){+ freeblist(b->next);
+ b->wp -= i;
+ *l = 0;
+ } else {+ nb = allocb(i);
+ memmove(nb->wp, b->rp+n, i);
+ nb->wp += i;
+ b->wp -= i;
+ nb->next = b->next;
+ *l = nb;
+ }
+ b->next = 0;
+ if(BLEN(b) < 0)
+ panic("qtake");+ return first;
+ } else
+ n -= i;
+ if(BLEN(b) < 0)
+ panic("qtake");+ }
+ *l = 0;
+ return first;
+}
+
+/*
+ * We can't let Eintr's lose data since the program
+ * doing the read may be able to handle it. The only
+ * places Eintr is possible is during the read's in consume.
+ * Therefore, we make sure we can always put back the bytes
+ * consumed before the last ensure.
+ */
+static Block*
+sslbread(Chan *c, long n, ulong o)
+{+ Dstate * volatile s;
+ Block *b;
+ uchar consumed[3], *p;
+ int toconsume;
+ int len, pad;
+
+ USED(o);
+ s = dstate[CONV(c->qid)];
+ if(s == 0)
+ panic("sslbread");+ if(s->state == Sincomplete)
+ error(Ebadusefd);
+
+ qlock(&s->in.q);
+ if(waserror()){+ qunlock(&s->in.q);
+ nexterror();
+ }
+
+ if(s->processed == 0){+ /*
+ * Read in the whole message. Until we've got it all,
+ * it stays on s->unprocessed, so that if we get Eintr,
+ * we'll pick up where we left off.
+ */
+ ensure(s, &s->unprocessed, 3);
+ s->unprocessed = pullupblock(s->unprocessed, 2);
+ p = s->unprocessed->rp;
+ if(p[0] & 0x80){+ len = ((p[0] & 0x7f)<<8) | p[1];
+ ensure(s, &s->unprocessed, len);
+ pad = 0;
+ toconsume = 2;
+ } else {+ s->unprocessed = pullupblock(s->unprocessed, 3);
+ len = ((p[0] & 0x3f)<<8) | p[1];
+ pad = p[2];
+ if(pad > len){+ print("pad %d buf len %d\n", pad, len);+ error("bad pad in ssl message");+ }
+ toconsume = 3;
+ }
+ ensure(s, &s->unprocessed, toconsume+len);
+
+ /* skip header */
+ consume(&s->unprocessed, consumed, toconsume);
+
+ /* grab the next message and decode/decrypt it */
+ b = qtake(&s->unprocessed, len, 0);
+
+ if(blocklen(b) != len)
+ print("devssl: sslbread got wrong count %d != %d", blocklen(b), len);+
+ if(waserror()){+ qunlock(&s->in.ctlq);
+ if(b != nil)
+ freeb(b);
+ nexterror();
+ }
+ qlock(&s->in.ctlq);
+ switch(s->state){+ case Sencrypting:
+ if(b == nil)
+ error("ssl message too short (encrypting)");+ b = decryptb(s, b);
+ break;
+ case Sdigesting:
+ b = pullupblock(b, s->diglen);
+ if(b == nil)
+ error("ssl message too short (digesting)");+ checkdigestb(s, b);
+ pullblock(&b, s->diglen);
+ len -= s->diglen;
+ break;
+ case Sdigenc:
+ b = decryptb(s, b);
+ b = pullupblock(b, s->diglen);
+ if(b == nil)
+ error("ssl message too short (dig+enc)");+ checkdigestb(s, b);
+ pullblock(&b, s->diglen);
+ len -= s->diglen;
+ break;
+ }
+
+ /* remove pad */
+ if(pad)
+ s->processed = qtake(&b, len - pad, 1);
+ else
+ s->processed = b;
+ b = nil;
+ s->in.mid++;
+ qunlock(&s->in.ctlq);
+ poperror();
+ }
+
+ /* return at most what was asked for */
+ b = qtake(&s->processed, n, 0);
+
+ qunlock(&s->in.q);
+ poperror();
+
+ return b;
+}
+
+static long
+sslread(Chan *c, void *a, long n, vlong off)
+{+ Block * volatile b;
+ Block *nb;
+ uchar *va;
+ int i;
+ char buf[128];
+ ulong offset = off;
+ int ft;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, sslgen);
+
+ ft = TYPE(c->qid);
+ switch(ft) {+ default:
+ error(Ebadusefd);
+ case Qctl:
+ ft = CONV(c->qid);
+ sprint(buf, "%d", ft);
+ return readstr(offset, a, n, buf);
+ case Qdata:
+ b = sslbread(c, n, offset);
+ break;
+ case Qencalgs:
+ return readstr(offset, a, n, encalgs);
+ break;
+ case Qhashalgs:
+ return readstr(offset, a, n, hashalgs);
+ break;
+ }
+
+ if(waserror()){+ freeblist(b);
+ nexterror();
+ }
+
+ n = 0;
+ va = a;
+ for(nb = b; nb; nb = nb->next){+ i = BLEN(nb);
+ memmove(va+n, nb->rp, i);
+ n += i;
+ }
+
+ freeblist(b);
+ poperror();
+
+ return n;
+}
+
+static long
+sslbwrite(Chan *c, Block *b, ulong o)
+{+ Dstate * volatile s;
+ long rv;
+
+ USED(o);
+ s = dstate[CONV(c->qid)];
+ if(s == nil)
+ panic("sslbwrite");+
+ if(s->state == Sincomplete){+ freeb(b);
+ error(Ebadusefd);
+ }
+
+ /* lock so split writes won't interleave */
+ if(waserror()){+ qunlock(&s->out.q);
+ nexterror();
+ }
+ qlock(&s->out.q);
+
+ rv = sslput(s, b);
+
+ poperror();
+ qunlock(&s->out.q);
+
+ return rv;
+}
+
+/*
+ * use SSL record format, add in count, digest and/or encrypt.
+ * the write is interruptable. if it is interrupted, we'll
+ * get out of sync with the far side. not much we can do about
+ * it since we don't know if any bytes have been written.
+ */
+static long
+sslput(Dstate *s, Block * volatile b)
+{+ Block *nb;
+ int h, n, m, pad, rv;
+ uchar *p;
+ int offset;
+
+ if(waserror()){+ if(b != nil)
+ freeb(b);
+ nexterror();
+ }
+
+ rv = 0;
+ while(b != nil){+ m = n = BLEN(b);
+ h = s->diglen + 2;
+
+ /* trim to maximum block size */
+ pad = 0;
+ if(m > s->max){+ m = s->max;
+ } else if(s->blocklen != 1){+ pad = (m + s->diglen)%s->blocklen;
+ if(pad){+ if(m > s->maxpad){+ pad = 0;
+ m = s->maxpad;
+ } else {+ pad = s->blocklen - pad;
+ h++;
+ }
+ }
+ }
+
+ rv += m;
+ if(m != n){+ nb = allocb(m + h + pad);
+ memmove(nb->wp + h, b->rp, m);
+ nb->wp += m + h;
+ b->rp += m;
+ } else {+ /* add header space */
+ nb = padblock(b, h);
+ b = 0;
+ }
+ m += s->diglen;
+
+ /* SSL style count */
+ if(pad){+ nb = padblock(nb, -pad);
+ prng(nb->wp, pad);
+ nb->wp += pad;
+ m += pad;
+
+ p = nb->rp;
+ p[0] = (m>>8);
+ p[1] = m;
+ p[2] = pad;
+ offset = 3;
+ } else {+ p = nb->rp;
+ p[0] = (m>>8) | 0x80;
+ p[1] = m;
+ offset = 2;
+ }
+
+ switch(s->state){+ case Sencrypting:
+ nb = encryptb(s, nb, offset);
+ break;
+ case Sdigesting:
+ nb = digestb(s, nb, offset);
+ break;
+ case Sdigenc:
+ nb = digestb(s, nb, offset);
+ nb = encryptb(s, nb, offset);
+ break;
+ }
+
+ s->out.mid++;
+
+ m = BLEN(nb);
+ devtab[s->c->type]->bwrite(s->c, nb, s->c->offset);
+ s->c->offset += m;
+ }
+
+ poperror();
+ return rv;
+}
+
+static void
+setsecret(OneWay *w, uchar *secret, int n)
+{+ secfree(w->secret);
+ w->secret = secalloc(n);
+ memmove(w->secret, secret, n);
+ w->slen = n;
+}
+
+static void
+initDESkey(OneWay *w)
+{+ secfree(w->state);
+ w->state = secalloc(sizeof(DESstate));
+ if(w->slen >= 16)
+ setupDESstate(w->state, w->secret, w->secret+8);
+ else if(w->slen >= 8)
+ setupDESstate(w->state, w->secret, 0);
+ else
+ error("secret too short");+}
+
+/*
+ * 40 bit DES is the same as 56 bit DES. However,
+ * 16 bits of the key are masked to zero.
+ */
+static void
+initDESkey_40(OneWay *w)
+{+ uchar key[8];
+
+ if(w->slen >= 8){+ memmove(key, w->secret, 8);
+ key[0] &= 0x0f;
+ key[2] &= 0x0f;
+ key[4] &= 0x0f;
+ key[6] &= 0x0f;
+ }
+ initDESkey(w);
+}
+
+static void
+initRC4key(OneWay *w)
+{+ secfree(w->state);
+ w->state = secalloc(sizeof(RC4state));
+ setupRC4state(w->state, w->secret, w->slen);
+}
+
+/*
+ * 40 bit RC4 is the same as n-bit RC4. However,
+ * we ignore all but the first 40 bits of the key.
+ */
+static void
+initRC4key_40(OneWay *w)
+{+ if(w->slen > 5)
+ w->slen = 5;
+ initRC4key(w);
+}
+
+/*
+ * 128 bit RC4 is the same as n-bit RC4. However,
+ * we ignore all but the first 128 bits of the key.
+ */
+static void
+initRC4key_128(OneWay *w)
+{+ if(w->slen > 16)
+ w->slen = 16;
+ initRC4key(w);
+}
+
+
+typedef struct Hashalg Hashalg;
+struct Hashalg
+{+ char *name;
+ int diglen;
+ DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
+};
+
+Hashalg hashtab[] =
+{+ { "md5", MD5dlen, md5, },+ { "sha1", SHA1dlen, sha1, },+ { "sha", SHA1dlen, sha1, },+ { 0 }+};
+
+static int
+parsehashalg(char *p, Dstate *s)
+{+ Hashalg *ha;
+
+ for(ha = hashtab; ha->name; ha++){+ if(strcmp(p, ha->name) == 0){+ s->hf = ha->hf;
+ s->diglen = ha->diglen;
+ s->state &= ~Sclear;
+ s->state |= Sdigesting;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+typedef struct Encalg Encalg;
+struct Encalg
+{+ char *name;
+ int blocklen;
+ int alg;
+ void (*keyinit)(OneWay*);
+};
+
+static
+Encalg encrypttab[] =
+{+ { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */+ { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */+ { "des_56_cbc", 8, DESCBC, initDESkey, },+ { "des_56_ecb", 8, DESECB, initDESkey, },+ { "des_40_cbc", 8, DESCBC, initDESkey_40, },+ { "des_40_ecb", 8, DESECB, initDESkey_40, },+ { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */+ { "rc4_256", 1, RC4, initRC4key, },+ { "rc4_128", 1, RC4, initRC4key_128, },+ { "rc4_40", 1, RC4, initRC4key_40, },+ { 0 }+};
+
+static int
+parseencryptalg(char *p, Dstate *s)
+{+ Encalg *ea;
+
+ for(ea = encrypttab; ea->name; ea++){+ if(strcmp(p, ea->name) == 0){+ s->encryptalg = ea->alg;
+ s->blocklen = ea->blocklen;
+ (*ea->keyinit)(&s->in);
+ (*ea->keyinit)(&s->out);
+ s->state &= ~Sclear;
+ s->state |= Sencrypting;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+enum {+ Cfd,
+ Calg,
+ Csin,
+ Csout,
+};
+
+static
+Cmdtab sslcmds[] = {+ {Cfd, "fd", 2 },+ {Calg, "alg", 0 },+ {Csin, "secretin", 2 },+ {Csout, "secretout", 2 },+};
+
+static long
+sslwrite(Chan *c, void *a, long n, vlong o)
+{+ Dstate * volatile s;
+ Block * volatile b;
+ int m, t, i;
+ char *p, *e;
+ uchar *x;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+
+ USED(o);
+ s = dstate[CONV(c->qid)];
+ if(s == 0)
+ panic("sslwrite");+
+ t = TYPE(c->qid);
+ if(t == Qdata){+ if(s->state == Sincomplete)
+ error(Ebadusefd);
+
+ /* lock should a write gets split over multiple records */
+ if(waserror()){+ qunlock(&s->out.q);
+ nexterror();
+ }
+ qlock(&s->out.q);
+
+ p = a;
+ e = p + n;
+ do {+ m = e - p;
+ if(m > s->max)
+ m = s->max;
+
+ b = allocb(m);
+ if(waserror()){+ freeb(b);
+ nexterror();
+ }
+ memmove(b->wp, p, m);
+ poperror();
+ b->wp += m;
+
+ sslput(s, b);
+
+ p += m;
+ } while(p < e);
+
+ poperror();
+ qunlock(&s->out.q);
+ return n;
+ }
+
+ /* mutex with operations using what we're about to change */
+ if(waserror()){+ qunlock(&s->in.ctlq);
+ qunlock(&s->out.q);
+ nexterror();
+ }
+ qlock(&s->in.ctlq);
+ qlock(&s->out.q);
+
+ switch(t){+ default:
+ panic("sslwrite");+ case Qsecretin:
+ setsecret(&s->in, a, n);
+ goto out;
+ case Qsecretout:
+ setsecret(&s->out, a, n);
+ goto out;
+ case Qctl:
+ break;
+ }
+
+ cb = parsecmd(a, n);
+ if(waserror()){+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, sslcmds, nelem(sslcmds));
+ switch(ct->index){+ case Cfd:
+ s->c = buftochan(cb->f[1]);
+
+ /* default is clear (msg delimiters only) */
+ s->state = Sclear;
+ s->blocklen = 1;
+ s->diglen = 0;
+ s->maxpad = s->max = (1<<15) - s->diglen - 1;
+ s->in.mid = 0;
+ s->out.mid = 0;
+ break;
+ case Calg:
+ if(cb->nf < 2)
+ cmderror(cb, "no algorithms");
+
+ s->blocklen = 1;
+ s->diglen = 0;
+
+ if(s->c == 0)
+ error("must set fd before algorithm");+
+ s->state = Sclear;
+ s->maxpad = s->max = (1<<15) - s->diglen - 1;
+ if(strcmp(cb->f[1], "clear") == 0)
+ break;
+
+ if(s->in.secret && s->out.secret == 0)
+ setsecret(&s->out, s->in.secret, s->in.slen);
+ if(s->out.secret && s->in.secret == 0)
+ setsecret(&s->in, s->out.secret, s->out.slen);
+ if(s->in.secret == 0 || s->out.secret == 0)
+ error("algorithm but no secret");+
+ s->hf = 0;
+ s->encryptalg = Noencryption;
+ s->blocklen = 1;
+
+ for(i=1; i<cb->nf; i++){+ p = cb->f[i];
+ if(parsehashalg(p, s) < 0)
+ if(parseencryptalg(p, s) < 0)
+ error("bad algorithm");+ }
+
+ if(s->hf == 0 && s->encryptalg == Noencryption)
+ error("bad algorithm");+
+ if(s->blocklen != 1){+ s->max = (1<<15) - s->diglen - 1;
+ s->max -= s->max % s->blocklen;
+ s->maxpad = (1<<14) - s->diglen - 1;
+ s->maxpad -= s->maxpad % s->blocklen;
+ } else
+ s->maxpad = s->max = (1<<15) - s->diglen - 1;
+ break;
+ case Csin:
+ p = cb->f[1];
+ m = (strlen(p)*3)/2 + 1;
+ x = secalloc(m);
+ t = dec64(x, m, p, strlen(p));
+ memset(p, 0, strlen(p));
+ if(t <= 0){+ secfree(x);
+ error(Ebadarg);
+ }
+ setsecret(&s->in, x, t);
+ secfree(x);
+ break;
+ case Csout:
+ p = cb->f[1];
+ m = (strlen(p)*3)/2 + 1;
+ x = secalloc(m);
+ t = dec64(x, m, p, strlen(p));
+ memset(p, 0, strlen(p));
+ if(t <= 0){+ secfree(x);
+ error(Ebadarg);
+ }
+ setsecret(&s->out, x, t);
+ secfree(x);
+ break;
+ }
+ poperror();
+ free(cb);
+
+out:
+ qunlock(&s->in.ctlq);
+ qunlock(&s->out.q);
+ poperror();
+ return n;
+}
+
+static void
+sslinit(void)
+{+ struct Encalg *e;
+ struct Hashalg *h;
+ int n;
+ char *cp;
+
+ n = 1;
+ for(e = encrypttab; e->name != nil; e++)
+ n += strlen(e->name) + 1;
+ cp = encalgs = smalloc(n);
+ for(e = encrypttab;;){+ strcpy(cp, e->name);
+ cp += strlen(e->name);
+ e++;
+ if(e->name == nil)
+ break;
+ *cp++ = ' ';
+ }
+ *cp = 0;
+
+ n = 1;
+ for(h = hashtab; h->name != nil; h++)
+ n += strlen(h->name) + 1;
+ cp = hashalgs = smalloc(n);
+ for(h = hashtab;;){+ strcpy(cp, h->name);
+ cp += strlen(h->name);
+ h++;
+ if(h->name == nil)
+ break;
+ *cp++ = ' ';
+ }
+ *cp = 0;
+}
+
+Dev ssldevtab = {+ 'D',
+ "ssl",
+
+ devreset,
+ sslinit,
+ devshutdown,
+ sslattach,
+ sslwalk,
+ sslstat,
+ sslopen,
+ devcreate,
+ sslclose,
+ sslread,
+ sslbread,
+ sslwrite,
+ sslbwrite,
+ devremove,
+ sslwstat,
+};
+
+static Block*
+encryptb(Dstate *s, Block *b, int offset)
+{+ uchar *p, *ep, *p2, *ip, *eip;
+ DESstate *ds;
+
+ switch(s->encryptalg){+ case DESECB:
+ ds = s->out.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp + offset; p < ep; p += 8)
+ block_cipher(ds->expanded, p, 0);
+ break;
+ case DESCBC:
+ ds = s->out.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp + offset; p < ep; p += 8){+ p2 = p;
+ ip = ds->ivec;
+ for(eip = ip+8; ip < eip; )
+ *p2++ ^= *ip++;
+ block_cipher(ds->expanded, p, 0);
+ memmove(ds->ivec, p, 8);
+ }
+ break;
+ case RC4:
+ rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
+ break;
+ }
+ return b;
+}
+
+static Block*
+decryptb(Dstate *s, Block *bin)
+{+ Block *b, **l;
+ uchar *p, *ep, *tp, *ip, *eip;
+ DESstate *ds;
+ uchar tmp[8];
+ int i;
+
+ l = &bin;
+ for(b = bin; b; b = b->next){+ /* make sure we have a multiple of s->blocklen */
+ if(s->blocklen > 1){+ i = BLEN(b);
+ if(i % s->blocklen){+ *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
+ if(b == 0)
+ error("ssl encrypted message too short");+ }
+ }
+ l = &b->next;
+
+ /* decrypt */
+ switch(s->encryptalg){+ case DESECB:
+ ds = s->in.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp; p < ep; p += 8)
+ block_cipher(ds->expanded, p, 1);
+ break;
+ case DESCBC:
+ ds = s->in.state;
+ ep = b->rp + BLEN(b);
+ for(p = b->rp; p < ep;){+ memmove(tmp, p, 8);
+ block_cipher(ds->expanded, p, 1);
+ tp = tmp;
+ ip = ds->ivec;
+ for(eip = ip+8; ip < eip; ){+ *p++ ^= *ip;
+ *ip++ = *tp++;
+ }
+ }
+ break;
+ case RC4:
+ rc4(s->in.state, b->rp, BLEN(b));
+ break;
+ }
+ }
+ return bin;
+}
+
+static Block*
+digestb(Dstate *s, Block *b, int offset)
+{+ uchar *p;
+ DigestState ss;
+ uchar msgid[4];
+ ulong n, h;
+ OneWay *w;
+
+ w = &s->out;
+
+ memset(&ss, 0, sizeof(ss));
+ h = s->diglen + offset;
+ n = BLEN(b) - h;
+
+ /* hash secret + message */
+ (*s->hf)(w->secret, w->slen, 0, &ss);
+ (*s->hf)(b->rp + h, n, 0, &ss);
+
+ /* hash message id */
+ p = msgid;
+ n = w->mid;
+ *p++ = n>>24;
+ *p++ = n>>16;
+ *p++ = n>>8;
+ *p = n;
+ (*s->hf)(msgid, 4, b->rp + offset, &ss);
+
+ return b;
+}
+
+static void
+checkdigestb(Dstate *s, Block *bin)
+{+ uchar *p;
+ DigestState ss;
+ uchar msgid[4];
+ int n, h;
+ OneWay *w;
+ uchar digest[128];
+ Block *b;
+
+ w = &s->in;
+
+ memset(&ss, 0, sizeof(ss));
+
+ /* hash secret */
+ (*s->hf)(w->secret, w->slen, 0, &ss);
+
+ /* hash message */
+ h = s->diglen;
+ for(b = bin; b; b = b->next){+ n = BLEN(b) - h;
+ if(n < 0)
+ panic("checkdigestb");+ (*s->hf)(b->rp + h, n, 0, &ss);
+ h = 0;
+ }
+
+ /* hash message id */
+ p = msgid;
+ n = w->mid;
+ *p++ = n>>24;
+ *p++ = n>>16;
+ *p++ = n>>8;
+ *p = n;
+ (*s->hf)(msgid, 4, digest, &ss);
+
+ if(tsmemcmp(digest, bin->rp, s->diglen) != 0)
+ error("bad digest");+}
+
+/* get channel associated with an fd */
+static Chan*
+buftochan(char *p)
+{+ Chan *c;
+ int fd;
+
+ if(p == 0)
+ error(Ebadarg);
+ fd = strtoul(p, 0, 0);
+ if(fd < 0)
+ error(Ebadarg);
+ c = fdtochan(fd, ORDWR, 1, 1); /* error check and inc ref */
+ if(devtab[c->type] == &ssldevtab){+ cclose(c);
+ error("cannot ssl encrypt devssl files");+ }
+ return c;
+}
+
+/* hand up a digest connection */
+static void
+sslhangup(Dstate *s)
+{+ Block *b;
+
+ qlock(&s->in.q);
+ for(b = s->processed; b; b = s->processed){+ s->processed = b->next;
+ freeb(b);
+ }
+ if(s->unprocessed){+ freeb(s->unprocessed);
+ s->unprocessed = 0;
+ }
+ s->state = Sincomplete;
+ qunlock(&s->in.q);
+}
+
+static Dstate*
+dsclone(Chan *ch)
+{+ int i;
+ Dstate *ret;
+
+ if(waserror()) {+ unlock(&dslock);
+ nexterror();
+ }
+ lock(&dslock);
+ ret = nil;
+ for(i=0; i<Maxdstate; i++){+ if(dstate[i] == nil){+ dsnew(ch, &dstate[i]);
+ ret = dstate[i];
+ break;
+ }
+ }
+ unlock(&dslock);
+ poperror();
+ return ret;
+}
+
+static void
+dsnew(Chan *ch, Dstate **pp)
+{+ Dstate *s;
+ int t;
+
+ *pp = s = malloc(sizeof(*s));
+ if(!s)
+ error(Enomem);
+ if(pp - dstate >= dshiwat)
+ dshiwat++;
+ memset(s, 0, sizeof(*s));
+ s->state = Sincomplete;
+ s->ref = 1;
+ kstrdup(&s->user, up->user);
+ s->perm = 0660;
+ t = TYPE(ch->qid);
+ if(t == Qclonus)
+ t = Qctl;
+ ch->qid.path = QID(pp - dstate, t);
+ ch->qid.vers = 0;
+}
--- a/kern/devtab.c
+++ b/kern/devtab.c
@@ -7,6 +7,8 @@
extern Dev consdevtab;
extern Dev rootdevtab;
extern Dev pipedevtab;
+extern Dev ssldevtab;
+extern Dev tlsdevtab;
extern Dev fsdevtab;
extern Dev lfddevtab;
extern Dev ipdevtab;
@@ -14,11 +16,14 @@
extern Dev lfddevtab;
extern Dev cmddevtab;
extern Dev envdevtab;
+extern Dev procdevtab;
Dev *devtab[] = {&rootdevtab,
&consdevtab,
&pipedevtab,
+ &ssldevtab,
+ &tlsdevtab,
&fsdevtab,
&lfddevtab,
&ipdevtab,
@@ -26,6 +31,7 @@
&lfddevtab,
&cmddevtab,
&envdevtab,
+ &procdevtab,
0
};
--- /dev/null
+++ b/kern/devtls.c
@@ -1,0 +1,2380 @@
+/*
+ * devtls - record layer for transport layer security 1.2 and secure sockets layer 3.0
+ */
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <libsec.h>
+
+typedef struct OneWay OneWay;
+typedef struct Secret Secret;
+typedef struct TlsRec TlsRec;
+typedef struct TlsErrs TlsErrs;
+
+enum {+ Statlen= 1024, /* max. length of status or stats message */
+ /* buffer limits */
+ MaxRecLen = 1<<14, /* max payload length of a record layer message */
+ MaxCipherRecLen = MaxRecLen + 2048,
+ RecHdrLen = 5,
+ MaxMacLen = SHA2_256dlen,
+
+ /* protocol versions we can accept */
+ SSL3Version = 0x0300,
+ TLS10Version = 0x0301,
+ TLS11Version = 0x0302,
+ TLS12Version = 0x0303,
+ MinProtoVersion = 0x0300, /* limits on version we accept */
+ MaxProtoVersion = 0x03ff,
+
+ /* connection states */
+ SHandshake = 1 << 0, /* doing handshake */
+ SOpen = 1 << 1, /* application data can be sent */
+ SRClose = 1 << 2, /* remote side has closed down */
+ SLClose = 1 << 3, /* sent a close notify alert */
+ SAlert = 1 << 5, /* sending or sent a fatal alert */
+ SError = 1 << 6, /* some sort of error has occured */
+ SClosed = 1 << 7, /* it is all over */
+
+ /* record types */
+ RChangeCipherSpec = 20,
+ RAlert,
+ RHandshake,
+ RApplication,
+
+ SSL2ClientHello = 1,
+ HSSL2ClientHello = 9, /* local convention; see tlshand.c */
+
+ /* alerts */
+ ECloseNotify = 0,
+ EUnexpectedMessage = 10,
+ EBadRecordMac = 20,
+ EDecryptionFailed = 21,
+ ERecordOverflow = 22,
+ EDecompressionFailure = 30,
+ EHandshakeFailure = 40,
+ ENoCertificate = 41,
+ EBadCertificate = 42,
+ EUnsupportedCertificate = 43,
+ ECertificateRevoked = 44,
+ ECertificateExpired = 45,
+ ECertificateUnknown = 46,
+ EIllegalParameter = 47,
+ EUnknownCa = 48,
+ EAccessDenied = 49,
+ EDecodeError = 50,
+ EDecryptError = 51,
+ EExportRestriction = 60,
+ EProtocolVersion = 70,
+ EInsufficientSecurity = 71,
+ EInternalError = 80,
+ EUserCanceled = 90,
+ ENoRenegotiation = 100,
+ EUnrecognizedName = 112,
+
+ EMAX = 256
+};
+
+struct Secret
+{+ char *encalg; /* name of encryption alg */
+ char *hashalg; /* name of hash alg */
+
+ int (*aead_enc)(Secret*, uchar*, int, uchar*, uchar*, int);
+ int (*aead_dec)(Secret*, uchar*, int, uchar*, uchar*, int);
+
+ int (*enc)(Secret*, uchar*, int);
+ int (*dec)(Secret*, uchar*, int);
+ int (*unpad)(uchar*, int, int);
+ DigestState* (*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+
+ int block; /* encryption block len, 0 if none */
+ int maclen; /* # bytes of record mac / authentication tag */
+ int recivlen; /* # bytes of record iv for AEAD ciphers */
+ void *enckey;
+ uchar mackey[MaxMacLen];
+};
+
+struct OneWay
+{+ QLock io; /* locks io access */
+ QLock seclock; /* locks secret paramaters */
+ u64int seq;
+ Secret *sec; /* cipher in use */
+ Secret *new; /* cipher waiting for enable */
+};
+
+struct TlsRec
+{+ Chan *c; /* io channel */
+ int ref; /* serialized by tdlock for atomic destroy */
+ int version; /* version of the protocol we are speaking */
+ char verset; /* version has been set */
+ char opened; /* opened command every issued? */
+ char err[ERRMAX]; /* error message to return to handshake requests */
+ vlong handin; /* bytes communicated by the record layer */
+ vlong handout;
+ vlong datain;
+ vlong dataout;
+
+ Lock statelk;
+ int state;
+ int debug;
+
+ /*
+ * function to genrate authenticated data blob for different
+ * protocol versions
+ */
+ int (*packAAD)(u64int, uchar*, uchar*);
+
+ /* input side -- protected by in.io */
+ OneWay in;
+ Block *processed; /* next bunch of application data */
+ Block *unprocessed; /* data read from c but not parsed into records */
+
+ /* handshake queue */
+ Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */
+ int hqref;
+ Queue *handq; /* queue of handshake messages */
+ Block *hprocessed; /* remainder of last block read from handq */
+ QLock hqread; /* protects reads for hprocessed, handq */
+
+ /* output side */
+ OneWay out;
+
+ /* protections */
+ char *user;
+ int perm;
+};
+
+struct TlsErrs{+ int err;
+ int sslerr;
+ int tlserr;
+ int fatal;
+ char *msg;
+};
+
+static TlsErrs tlserrs[] = {+ {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"},+ {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"},+ {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"},+ {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"},+ {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"},+ {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"},+ {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security parameters"},+ {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"},+ {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"},+ {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"},+ {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"},+ {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"},+ {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"},+ {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"},+ {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"},+ {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"},+ {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"},+ {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"},+ {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"},+ {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"},+ {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"},+ {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"},+ {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"},+ {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"},+};
+
+enum
+{+ /* max. open tls connections */
+ MaxTlsDevs = 1024
+};
+
+static Lock tdlock;
+static int tdhiwat;
+static int maxtlsdevs = 128;
+static TlsRec **tlsdevs;
+static char **trnames;
+static char *encalgs;
+static char *hashalgs;
+
+enum{+ Qtopdir = 1, /* top level directory */
+ Qprotodir,
+ Qclonus,
+ Qencalgs,
+ Qhashalgs,
+ Qconvdir, /* directory for a conversation */
+ Qdata,
+ Qctl,
+ Qhand,
+ Qstatus,
+ Qstats,
+};
+
+#define TYPE(x) ((x).path & 0xf)
+#define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1))
+#define QID(c, y) (((c)<<5) | (y))
+
+static void checkstate(TlsRec *, int, int);
+static void ensure(TlsRec*, Block**, int);
+static void consume(Block**, uchar*, int);
+static Chan* buftochan(char*);
+static void tlshangup(TlsRec*);
+static void tlsError(TlsRec*, char *);
+static void alertHand(TlsRec*, char *);
+static TlsRec *newtls(Chan *c);
+static TlsRec *mktlsrec(void);
+static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
+static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
+static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
+static int sslPackAAD(u64int, uchar*, uchar*);
+static int tlsPackAAD(u64int, uchar*, uchar*);
+static void packMac(Secret*, uchar*, int, uchar*, int, uchar*);
+static void put64(uchar *p, u64int);
+static void put32(uchar *p, u32int);
+static void put24(uchar *p, int);
+static void put16(uchar *p, int);
+static int get16(uchar *p);
+static void tlsSetState(TlsRec *tr, int new, int old);
+static void rcvAlert(TlsRec *tr, int err);
+static void sendAlert(TlsRec *tr, int err);
+static void rcvError(TlsRec *tr, int err, char *msg, ...);
+static int rc4enc(Secret *sec, uchar *buf, int n);
+static int des3enc(Secret *sec, uchar *buf, int n);
+static int des3dec(Secret *sec, uchar *buf, int n);
+static int aesenc(Secret *sec, uchar *buf, int n);
+static int aesdec(Secret *sec, uchar *buf, int n);
+static int ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int aesgcm_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int aesgcm_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int noenc(Secret *sec, uchar *buf, int n);
+static int sslunpad(uchar *buf, int n, int block);
+static int tlsunpad(uchar *buf, int n, int block);
+static void freeSec(Secret *sec);
+static char *tlsstate(int s);
+static void pdump(int, void*, char*);
+
+static char *tlsnames[] = {+[Qclonus] "clone",
+[Qencalgs] "encalgs",
+[Qhashalgs] "hashalgs",
+[Qdata] "data",
+[Qctl] "ctl",
+[Qhand] "hand",
+[Qstatus] "status",
+[Qstats] "stats",
+};
+
+static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };+
+static int
+tlsgen(Chan *c, char *unused1, Dirtab *unused2, int unused3, int s, Dir *dp)
+{+ Qid q;
+ TlsRec *tr;
+ char *name, *nm;
+ int perm, t;
+
+ q.vers = 0;
+ q.type = QTFILE;
+
+ t = TYPE(c->qid);
+ switch(t) {+ case Qtopdir:
+ if(s == DEVDOTDOT){+ q.path = QID(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, "#a", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s > 0)
+ return -1;
+ q.path = QID(0, Qprotodir);
+ q.type = QTDIR;
+ devdir(c, q, "tls", 0, eve, 0555, dp);
+ return 1;
+ case Qprotodir:
+ if(s == DEVDOTDOT){+ q.path = QID(0, Qtopdir);
+ q.type = QTDIR;
+ devdir(c, q, ".", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < 3){+ switch(s) {+ default:
+ return -1;
+ case 0:
+ q.path = QID(0, Qclonus);
+ break;
+ case 1:
+ q.path = QID(0, Qencalgs);
+ break;
+ case 2:
+ q.path = QID(0, Qhashalgs);
+ break;
+ }
+ perm = 0444;
+ if(TYPE(q) == Qclonus)
+ perm = 0555;
+ devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
+ return 1;
+ }
+ s -= 3;
+ if(s >= tdhiwat)
+ return -1;
+ q.path = QID(s, Qconvdir);
+ q.type = QTDIR;
+ lock(&tdlock);
+ tr = tlsdevs[s];
+ if(tr != nil)
+ nm = tr->user;
+ else
+ nm = eve;
+ if((name = trnames[s]) == nil){+ name = trnames[s] = smalloc(16);
+ sprint(name, "%d", s);
+ }
+ devdir(c, q, name, 0, nm, 0555, dp);
+ unlock(&tdlock);
+ return 1;
+ case Qconvdir:
+ if(s == DEVDOTDOT){+ q.path = QID(0, Qprotodir);
+ q.type = QTDIR;
+ devdir(c, q, "tls", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < 0 || s >= nelem(convdir))
+ return -1;
+ lock(&tdlock);
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr != nil){+ nm = tr->user;
+ perm = tr->perm;
+ }else{+ perm = 0;
+ nm = eve;
+ }
+ t = convdir[s];
+ if(t == Qstatus || t == Qstats)
+ perm &= 0444;
+ q.path = QID(CONV(c->qid), t);
+ devdir(c, q, tlsnames[t], 0, nm, perm, dp);
+ unlock(&tdlock);
+ return 1;
+ case Qclonus:
+ case Qencalgs:
+ case Qhashalgs:
+ perm = 0444;
+ if(t == Qclonus)
+ perm = 0555;
+ devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
+ return 1;
+ default:
+ lock(&tdlock);
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr != nil){+ nm = tr->user;
+ perm = tr->perm;
+ }else{+ perm = 0;
+ nm = eve;
+ }
+ if(t == Qstatus || t == Qstats)
+ perm &= 0444;
+ devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
+ unlock(&tdlock);
+ return 1;
+ }
+}
+
+static Chan*
+tlsattach(char *spec)
+{+ Chan *c;
+
+ c = devattach('a', spec);+ c->qid.path = QID(0, Qtopdir);
+ c->qid.type = QTDIR;
+ c->qid.vers = 0;
+ return c;
+}
+
+static Walkqid*
+tlswalk(Chan *c, Chan *nc, char **name, int nname)
+{+ return devwalk(c, nc, name, nname, nil, 0, tlsgen);
+}
+
+static int
+tlsstat(Chan *c, uchar *db, int n)
+{+ return devstat(c, db, n, nil, 0, tlsgen);
+}
+
+static Chan*
+tlsopen(Chan *c, int omode)
+{+ TlsRec *tr, **pp;
+ int t;
+
+ t = TYPE(c->qid);
+ switch(t) {+ default:
+ panic("tlsopen");+ case Qtopdir:
+ case Qprotodir:
+ case Qconvdir:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ case Qclonus:
+ tr = newtls(c);
+ if(tr == nil)
+ error(Enodev);
+ break;
+ case Qctl:
+ case Qdata:
+ case Qhand:
+ case Qstatus:
+ case Qstats:
+ if((t == Qstatus || t == Qstats) && omode != OREAD)
+ error(Eperm);
+ if(waserror()) {+ unlock(&tdlock);
+ nexterror();
+ }
+ lock(&tdlock);
+ pp = &tlsdevs[CONV(c->qid)];
+ tr = *pp;
+ if(tr == nil)
+ error("must open connection using clone");+ devpermcheck(tr->user, tr->perm, omode);
+ if(t == Qhand){+ if(waserror()){+ unlock(&tr->hqlock);
+ nexterror();
+ }
+ lock(&tr->hqlock);
+ if(tr->handq != nil)
+ error(Einuse);
+ tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
+ if(tr->handq == nil)
+ error("cannot allocate handshake queue");+ tr->hqref = 1;
+ unlock(&tr->hqlock);
+ poperror();
+ }
+ tr->ref++;
+ unlock(&tdlock);
+ poperror();
+ break;
+ case Qencalgs:
+ case Qhashalgs:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+ }
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ switch(t){+ case Qdata:
+ c->iounit = qiomaxatomic;
+ break;
+ case Qhand:
+ c->iounit = MaxRecLen;
+ break;
+ default:
+ c->iounit = 0;
+ break;
+ }
+ return c;
+}
+
+static int
+tlswstat(Chan *c, uchar *dp, int n)
+{+ Dir *d;
+ TlsRec *tr;
+ int rv;
+
+ d = nil;
+ if(waserror()){+ free(d);
+ unlock(&tdlock);
+ nexterror();
+ }
+
+ lock(&tdlock);
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr == nil)
+ error(Ebadusefd);
+ if(strcmp(tr->user, up->user) != 0)
+ error(Eperm);
+
+ d = smalloc(n + sizeof *d);
+ rv = convM2D(dp, n, &d[0], (char*) &d[1]);
+ if(rv == 0)
+ error(Eshortstat);
+ if(!emptystr(d->uid))
+ kstrdup(&tr->user, d->uid);
+ if(d->mode != -1)
+ tr->perm = d->mode;
+
+ free(d);
+ poperror();
+ unlock(&tdlock);
+
+ return rv;
+}
+
+static void
+dechandq(TlsRec *tr)
+{+ lock(&tr->hqlock);
+ if(--tr->hqref == 0){+ if(tr->handq != nil){+ qfree(tr->handq);
+ tr->handq = nil;
+ }
+ if(tr->hprocessed != nil){+ freeb(tr->hprocessed);
+ tr->hprocessed = nil;
+ }
+ }
+ unlock(&tr->hqlock);
+}
+
+static void
+tlsclose(Chan *c)
+{+ TlsRec *tr;
+ int t;
+
+ t = TYPE(c->qid);
+ switch(t) {+ case Qctl:
+ case Qdata:
+ case Qhand:
+ case Qstatus:
+ case Qstats:
+ if((c->flag & COPEN) == 0)
+ break;
+
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr == nil)
+ break;
+
+ if(t == Qhand)
+ dechandq(tr);
+
+ lock(&tdlock);
+ if(--tr->ref > 0) {+ unlock(&tdlock);
+ return;
+ }
+ tlsdevs[CONV(c->qid)] = nil;
+ unlock(&tdlock);
+
+ if(tr->c != nil && !waserror()){+ checkstate(tr, 0, SOpen|SHandshake|SRClose);
+ sendAlert(tr, ECloseNotify);
+ poperror();
+ }
+ tlshangup(tr);
+ if(tr->c != nil)
+ cclose(tr->c);
+ freeSec(tr->in.sec);
+ freeSec(tr->in.new);
+ freeSec(tr->out.sec);
+ freeSec(tr->out.new);
+ free(tr->user);
+ free(tr);
+ break;
+ }
+}
+
+/*
+ * make sure we have at least 'n' bytes in list 'l'
+ */
+static void
+ensure(TlsRec *s, Block **l, int n)
+{+ int sofar, i;
+ Block *b, *bl;
+
+ sofar = 0;
+ for(b = *l; b; b = b->next){+ sofar += BLEN(b);
+ if(sofar >= n)
+ return;
+ l = &b->next;
+ }
+
+ while(sofar < n){+ bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
+ if(bl == 0)
+ error(Ehungup);
+ *l = bl;
+ i = 0;
+ for(b = bl; b; b = b->next){+ i += BLEN(b);
+ l = &b->next;
+ }
+ if(i == 0)
+ error(Ehungup);
+ sofar += i;
+ }
+if(s->debug) pprint("ensure read %d\n", sofar);+}
+
+/*
+ * copy 'n' bytes from 'l' into 'p' and free
+ * the bytes in 'l'
+ */
+static void
+consume(Block **l, uchar *p, int n)
+{+ Block *b;
+ int i;
+
+ for(; *l && n > 0; n -= i){+ b = *l;
+ i = BLEN(b);
+ if(i > n)
+ i = n;
+ memmove(p, b->rp, i);
+ b->rp += i;
+ p += i;
+ if(BLEN(b) < 0)
+ panic("consume");+ if(BLEN(b))
+ break;
+ *l = b->next;
+ freeb(b);
+ }
+}
+
+/*
+ * give back n bytes
+ */
+static void
+regurgitate(TlsRec *s, uchar *p, int n)
+{+ Block *b;
+
+ if(n <= 0)
+ return;
+ b = s->unprocessed;
+ if(s->unprocessed == nil || b->rp - b->base < n) {+ b = allocb(n);
+ memmove(b->wp, p, n);
+ b->wp += n;
+ b->next = s->unprocessed;
+ s->unprocessed = b;
+ } else {+ b->rp -= n;
+ memmove(b->rp, p, n);
+ }
+}
+
+/*
+ * remove at most n bytes from the queue
+ */
+static Block*
+qgrab(Block **l, int n)
+{+ Block *bb, *b;
+ int i;
+
+ b = *l;
+ if(BLEN(b) == n){+ *l = b->next;
+ b->next = nil;
+ return b;
+ }
+
+ i = 0;
+ for(bb = b; bb != nil && i < n; bb = bb->next)
+ i += BLEN(bb);
+ if(i > n)
+ i = n;
+
+ bb = allocb(i);
+ consume(l, bb->wp, i);
+ bb->wp += i;
+ return bb;
+}
+
+static void
+tlsclosed(TlsRec *tr, int new)
+{+ lock(&tr->statelk);
+ if(tr->state == SOpen || tr->state == SHandshake)
+ tr->state = new;
+ else if((new | tr->state) == (SRClose|SLClose))
+ tr->state = SClosed;
+ unlock(&tr->statelk);
+ alertHand(tr, "close notify");
+}
+
+/*
+ * read and process one tls record layer message
+ * must be called with tr->in.io held
+ * We can't let Eintrs lose data, since doing so will get
+ * us out of sync with the sender and break the reliablity
+ * of the channel. Eintr only happens during the reads in
+ * consume. Therefore we put back any bytes consumed before
+ * the last call to ensure.
+ */
+static void
+tlsrecread(TlsRec *tr)
+{+ OneWay *volatile in;
+ Block *volatile b;
+ uchar *p, aad[8+RecHdrLen], header[RecHdrLen], hmac[MaxMacLen];
+ int volatile nconsumed;
+ int len, type, ver, unpad_len, aadlen, ivlen;
+ Secret *sec;
+
+ nconsumed = 0;
+ if(waserror()){+ if(strcmp(up->errstr, Eintr) == 0 && !waserror()){+ regurgitate(tr, header, nconsumed);
+ poperror();
+ }else
+ tlsError(tr, "channel error");
+ nexterror();
+ }
+ ensure(tr, &tr->unprocessed, RecHdrLen);
+ consume(&tr->unprocessed, header, RecHdrLen);
+if(tr->debug)pprint("consumed %d header\n", RecHdrLen);+ nconsumed = RecHdrLen;
+
+ if((tr->handin == 0) && (header[0] & 0x80)){+ /* Cope with an SSL3 ClientHello expressed in SSL2 record format.
+ This is sent by some clients that we must interoperate
+ with, such as Java's JSSE and Microsoft's Internet Explorer. */
+ len = (get16(header) & ~0x8000) - 3;
+ type = header[2];
+ ver = get16(header + 3);
+ if(type != SSL2ClientHello || len < 22)
+ rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
+ }else{ /* normal SSL3 record format */+ type = header[0];
+ ver = get16(header+1);
+ len = get16(header+3);
+ }
+ if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
+ rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
+ tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
+ if(len > MaxCipherRecLen || len <= 0)
+ rcvError(tr, ERecordOverflow, "bad record message length %d", len);
+ ensure(tr, &tr->unprocessed, len);
+ nconsumed = 0;
+ poperror();
+
+ /*
+ * If an Eintr happens after this, we'll get out of sync.
+ * Make sure nothing we call can sleep.
+ * Errors are ok, as they kill the connection.
+ * Luckily, allocb won't sleep, it'll just error out.
+ */
+ b = nil;
+ if(waserror()){+ if(b != nil)
+ freeb(b);
+ tlsError(tr, "channel error");
+ nexterror();
+ }
+ b = qgrab(&tr->unprocessed, len);
+if(tr->debug) pprint("consumed unprocessed %d\n", len);+
+ in = &tr->in;
+ if(waserror()){+ qunlock(&in->seclock);
+ nexterror();
+ }
+ qlock(&in->seclock);
+ p = b->rp;
+ sec = in->sec;
+ if(sec != nil) {+ /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
+ should look alike, including timing of the response. */
+ if(sec->aead_dec != nil)
+ unpad_len = len;
+ else {+ unpad_len = (*sec->dec)(sec, p, len);
+if(tr->debug) pprint("decrypted %d\n", unpad_len);+if(tr->debug) pdump(unpad_len, p, "decrypted:");
+ }
+
+ ivlen = sec->recivlen;
+ if(tr->version >= TLS11Version){+ if(ivlen == 0)
+ ivlen = sec->block;
+ }
+ len -= ivlen;
+ if(len < 0)
+ rcvError(tr, EDecodeError, "runt record message");
+ unpad_len -= ivlen;
+ p += ivlen;
+
+ if(unpad_len >= sec->maclen)
+ len = unpad_len - sec->maclen;
+
+ /* update length */
+ put16(header+3, len);
+ aadlen = (*tr->packAAD)(in->seq++, header, aad);
+ if(sec->aead_dec != nil) {+ len = (*sec->aead_dec)(sec, aad, aadlen, p - ivlen, p, unpad_len);
+ if(len < 0)
+ rcvError(tr, EBadRecordMac, "record mac mismatch");
+ } else {+ packMac(sec, aad, aadlen, p, len, hmac);
+ if(unpad_len < sec->maclen)
+ rcvError(tr, EBadRecordMac, "short record mac");
+ if(tsmemcmp(hmac, p + len, sec->maclen) != 0)
+ rcvError(tr, EBadRecordMac, "record mac mismatch");
+ }
+ b->rp = p;
+ b->wp = p+len;
+ }
+ qunlock(&in->seclock);
+ poperror();
+ if(len < 0)
+ rcvError(tr, EDecodeError, "runt record message");
+
+ switch(type) {+ default:
+ rcvError(tr, EIllegalParameter, "invalid record message %#x", type);
+ break;
+ case RChangeCipherSpec:
+ if(len != 1 || p[0] != 1)
+ rcvError(tr, EDecodeError, "invalid change cipher spec");
+ qlock(&in->seclock);
+ if(in->new == nil){+ qunlock(&in->seclock);
+ rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
+ }
+ freeSec(in->sec);
+ in->sec = in->new;
+ in->new = nil;
+ in->seq = 0;
+ qunlock(&in->seclock);
+ break;
+ case RAlert:
+ if(len != 2)
+ rcvError(tr, EDecodeError, "invalid alert");
+ if(p[0] == 2)
+ rcvAlert(tr, p[1]);
+ if(p[0] != 1)
+ rcvError(tr, EIllegalParameter, "invalid alert fatal code");
+
+ /*
+ * propagate non-fatal alerts to handshaker
+ */
+ switch(p[1]){+ case ECloseNotify:
+ tlsclosed(tr, SRClose);
+ if(tr->opened)
+ error("tls hungup");+ error("close notify");+ break;
+ case ENoRenegotiation:
+ alertHand(tr, "no renegotiation");
+ break;
+ case EUserCanceled:
+ alertHand(tr, "handshake canceled by user");
+ break;
+ case EUnrecognizedName:
+ /* happens in response to SNI, can be ignored. */
+ break;
+ default:
+ rcvError(tr, EIllegalParameter, "invalid alert code");
+ }
+ break;
+ case RHandshake:
+ /*
+ * don't worry about dropping the block
+ * qbwrite always queues even if flow controlled and interrupted.
+ *
+ * if there isn't any handshaker, ignore the request,
+ * but notify the other side we are doing so.
+ */
+ lock(&tr->hqlock);
+ if(tr->handq != nil){+ tr->hqref++;
+ unlock(&tr->hqlock);
+ if(waserror()){+ dechandq(tr);
+ nexterror();
+ }
+ b = padblock(b, 1);
+ *b->rp = RHandshake;
+ qbwrite(tr->handq, b);
+ b = nil;
+ poperror();
+ dechandq(tr);
+ }else{+ unlock(&tr->hqlock);
+ if(tr->verset && tr->version != SSL3Version && !waserror()){+ sendAlert(tr, ENoRenegotiation);
+ poperror();
+ }
+ }
+ break;
+ case SSL2ClientHello:
+ lock(&tr->hqlock);
+ if(tr->handq != nil){+ tr->hqref++;
+ unlock(&tr->hqlock);
+ if(waserror()){+ dechandq(tr);
+ nexterror();
+ }
+ /* Pass the SSL2 format data, so that the handshake code can compute
+ the correct checksums. HSSL2ClientHello = HandshakeType 9 is
+ unused in RFC2246. */
+ b = padblock(b, 8);
+ b->rp[0] = RHandshake;
+ b->rp[1] = HSSL2ClientHello;
+ put24(&b->rp[2], len+3);
+ b->rp[5] = SSL2ClientHello;
+ put16(&b->rp[6], ver);
+ qbwrite(tr->handq, b);
+ b = nil;
+ poperror();
+ dechandq(tr);
+ }else{+ unlock(&tr->hqlock);
+ if(tr->verset && tr->version != SSL3Version && !waserror()){+ sendAlert(tr, ENoRenegotiation);
+ poperror();
+ }
+ }
+ break;
+ case RApplication:
+ if(!tr->opened)
+ rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
+ if(BLEN(b) > 0){+ tr->processed = b;
+ b = nil;
+ }
+ break;
+ }
+ if(b != nil)
+ freeb(b);
+ poperror();
+}
+
+/*
+ * got a fatal alert message
+ */
+static void
+rcvAlert(TlsRec *tr, int err)
+{+ char *s;
+ int i;
+
+ s = "unknown error";
+ for(i=0; i < nelem(tlserrs); i++){+ if(tlserrs[i].err == err){+ s = tlserrs[i].msg;
+ break;
+ }
+ }
+if(tr->debug) pprint("rcvAlert: %s\n", s);+
+ tlsError(tr, s);
+ if(!tr->opened)
+ error(s);
+ error("tls error");+}
+
+/*
+ * found an error while decoding the input stream
+ */
+static void
+rcvError(TlsRec *tr, int err, char *fmt, ...)
+{+ char msg[ERRMAX];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(msg, msg+sizeof(msg), fmt, arg);
+ va_end(arg);
+if(tr->debug) pprint("rcvError: %s\n", msg);+
+ sendAlert(tr, err);
+
+ if(!tr->opened)
+ error(msg);
+ error("tls error");+}
+
+/*
+ * make sure the next hand operation returns with a 'msg' error
+ */
+static void
+alertHand(TlsRec *tr, char *msg)
+{+ Block *b;
+ int n;
+
+ lock(&tr->hqlock);
+ if(tr->handq == nil){+ unlock(&tr->hqlock);
+ return;
+ }
+ tr->hqref++;
+ unlock(&tr->hqlock);
+
+ n = strlen(msg);
+ if(waserror()){+ dechandq(tr);
+ nexterror();
+ }
+ b = allocb(n + 2);
+ *b->wp++ = RAlert;
+ memmove(b->wp, msg, n + 1);
+ b->wp += n + 1;
+
+ qbwrite(tr->handq, b);
+
+ poperror();
+ dechandq(tr);
+}
+
+static void
+checkstate(TlsRec *tr, int ishand, int ok)
+{+ int state;
+
+ lock(&tr->statelk);
+ state = tr->state;
+ unlock(&tr->statelk);
+ if(state & ok)
+ return;
+ switch(state){+ case SHandshake:
+ case SOpen:
+ break;
+ case SError:
+ case SAlert:
+ if(ishand)
+ error(tr->err);
+ error("tls error");+ case SRClose:
+ case SLClose:
+ case SClosed:
+ error("tls hungup");+ }
+ error("tls improperly configured");+}
+
+static Block*
+tlsbread(Chan *c, long n, ulong offset)
+{+ int ty;
+ Block *b;
+ TlsRec *volatile tr;
+
+ ty = TYPE(c->qid);
+ switch(ty) {+ default:
+ return devbread(c, n, offset);
+ case Qhand:
+ case Qdata:
+ break;
+ }
+
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr == nil)
+ panic("tlsbread");+
+ if(waserror()){+ qunlock(&tr->in.io);
+ nexterror();
+ }
+ qlock(&tr->in.io);
+ if(ty == Qdata){+ checkstate(tr, 0, SOpen);
+ while(tr->processed == nil)
+ tlsrecread(tr);
+
+ /* return at most what was asked for */
+ b = qgrab(&tr->processed, n);
+if(tr->debug) pprint("consumed processed %zd\n", BLEN(b));+if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
+ qunlock(&tr->in.io);
+ poperror();
+ tr->datain += BLEN(b);
+ }else{+ checkstate(tr, 1, SOpen|SHandshake|SLClose);
+
+ /*
+ * it's ok to look at state without the lock
+ * since it only protects reading records,
+ * and we have that tr->in.io held.
+ */
+ while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
+ tlsrecread(tr);
+
+ qunlock(&tr->in.io);
+ poperror();
+
+ if(waserror()){+ qunlock(&tr->hqread);
+ nexterror();
+ }
+ qlock(&tr->hqread);
+ if(tr->hprocessed == nil){+ b = qbread(tr->handq, MaxRecLen + 1);
+ if(*b->rp++ == RAlert){+ kstrcpy(up->errstr, (char*)b->rp, ERRMAX);
+ freeb(b);
+ nexterror();
+ }
+ tr->hprocessed = b;
+ }
+ b = qgrab(&tr->hprocessed, n);
+ poperror();
+ qunlock(&tr->hqread);
+ tr->handin += BLEN(b);
+ }
+
+ return b;
+}
+
+static long
+tlsread(Chan *c, void *a, long n, vlong off)
+{+ Block *volatile b;
+ Block *nb;
+ uchar *va;
+ int i, ty;
+ char *buf, *s, *e;
+ ulong offset = off;
+ TlsRec * tr;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, tlsgen);
+
+ tr = tlsdevs[CONV(c->qid)];
+ ty = TYPE(c->qid);
+ switch(ty) {+ default:
+ error(Ebadusefd);
+ case Qstatus:
+ buf = smalloc(Statlen);
+ qlock(&tr->in.seclock);
+ qlock(&tr->out.seclock);
+ s = buf;
+ e = buf + Statlen;
+ s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
+ s = seprint(s, e, "Version: %#x\n", tr->version);
+ if(tr->in.sec != nil)
+ s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
+ if(tr->in.new != nil)
+ s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
+ if(tr->out.sec != nil)
+ s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
+ if(tr->out.new != nil)
+ s = seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
+ if(tr->c != nil)
+ seprint(s, e, "Chan: %s\n", chanpath(tr->c));
+ qunlock(&tr->in.seclock);
+ qunlock(&tr->out.seclock);
+ n = readstr(offset, a, n, buf);
+ free(buf);
+ return n;
+ case Qstats:
+ buf = smalloc(Statlen);
+ s = buf;
+ e = buf + Statlen;
+ s = seprint(s, e, "DataIn: %lld\n", tr->datain);
+ s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
+ s = seprint(s, e, "HandIn: %lld\n", tr->handin);
+ seprint(s, e, "HandOut: %lld\n", tr->handout);
+ n = readstr(offset, a, n, buf);
+ free(buf);
+ return n;
+ case Qctl:
+ buf = smalloc(Statlen);
+ snprint(buf, Statlen, "%llud", CONV(c->qid));
+ n = readstr(offset, a, n, buf);
+ free(buf);
+ return n;
+ case Qdata:
+ case Qhand:
+ b = tlsbread(c, n, offset);
+ break;
+ case Qencalgs:
+ return readstr(offset, a, n, encalgs);
+ case Qhashalgs:
+ return readstr(offset, a, n, hashalgs);
+ }
+
+ if(waserror()){+ freeblist(b);
+ nexterror();
+ }
+
+ n = 0;
+ va = a;
+ for(nb = b; nb; nb = nb->next){+ i = BLEN(nb);
+ memmove(va+n, nb->rp, i);
+ n += i;
+ }
+
+ freeblist(b);
+ poperror();
+
+ return n;
+}
+
+/*
+ * write a block in tls records
+ */
+static void
+tlsrecwrite(TlsRec *tr, int type, Block *b)
+{+ Block *volatile bb;
+ Block *nb;
+ uchar *p, aad[8+RecHdrLen];
+ OneWay *volatile out;
+ int n, ivlen, maclen, aadlen, pad, ok;
+ Secret *sec;
+
+ out = &tr->out;
+ bb = b;
+ if(waserror()){+ qunlock(&out->io);
+ if(bb != nil)
+ freeb(bb);
+ nexterror();
+ }
+ qlock(&out->io);
+if(tr->debug)pprint("send %zd\n", BLEN(b));+if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
+
+
+ if(type == RApplication)
+ checkstate(tr, 0, SOpen);
+
+ ok = SHandshake|SOpen|SRClose;
+ if(type == RAlert)
+ ok |= SAlert;
+ while(bb != nil){+ checkstate(tr, type != RApplication, ok);
+
+ /*
+ * get at most one maximal record's input,
+ * with padding on the front for header and
+ * back for mac and maximal block padding.
+ */
+ if(waserror()){+ qunlock(&out->seclock);
+ nexterror();
+ }
+ qlock(&out->seclock);
+ maclen = 0;
+ pad = 0;
+ ivlen = 0;
+ sec = out->sec;
+ if(sec != nil){+ maclen = sec->maclen;
+ pad = maclen + sec->block;
+ ivlen = sec->recivlen;
+ if(tr->version >= TLS11Version){+ if(ivlen == 0)
+ ivlen = sec->block;
+ }
+ }
+ n = BLEN(bb);
+ if(n > MaxRecLen){+ n = MaxRecLen;
+ nb = allocb(RecHdrLen + ivlen + n + pad);
+ memmove(nb->wp + RecHdrLen + ivlen, bb->rp, n);
+ bb->rp += n;
+ }else{+ /*
+ * carefully reuse bb so it will get freed if we're out of memory
+ */
+ bb = padblock(bb, RecHdrLen + ivlen);
+ if(pad)
+ nb = padblock(bb, -pad);
+ else
+ nb = bb;
+ bb = nil;
+ }
+
+ p = nb->rp;
+ p[0] = type;
+ put16(p+1, tr->version);
+ put16(p+3, n);
+
+ if(sec != nil){+ aadlen = (*tr->packAAD)(out->seq++, p, aad);
+ if(sec->aead_enc != nil)
+ n = (*sec->aead_enc)(sec, aad, aadlen, p + RecHdrLen, p + RecHdrLen + ivlen, n) + ivlen;
+ else {+ if(ivlen > 0)
+ prng(p + RecHdrLen, ivlen);
+ packMac(sec, aad, aadlen, p + RecHdrLen + ivlen, n, p + RecHdrLen + ivlen + n);
+ n = (*sec->enc)(sec, p + RecHdrLen, ivlen + n + maclen);
+ }
+ nb->wp = p + RecHdrLen + n;
+
+ /* update length */
+ put16(p+3, n);
+ }
+ if(type == RChangeCipherSpec){+ if(out->new == nil)
+ error("change cipher without a new cipher");+ freeSec(out->sec);
+ out->sec = out->new;
+ out->new = nil;
+ out->seq = 0;
+ }
+ qunlock(&out->seclock);
+ poperror();
+
+ /*
+ * if bwrite error's, we assume the block is queued.
+ * if not, we're out of sync with the receiver and will not recover.
+ */
+ if(waserror()){+ if(strcmp(up->errstr, "interrupted") != 0)
+ tlsError(tr, "channel error");
+ else if(bb != nil)
+ continue;
+ nexterror();
+ }
+ devtab[tr->c->type]->bwrite(tr->c, nb, 0);
+ poperror();
+ }
+ qunlock(&out->io);
+ poperror();
+}
+
+static long
+tlsbwrite(Chan *c, Block *b, ulong offset)
+{+ int ty;
+ ulong n;
+ TlsRec *tr;
+
+ n = BLEN(b);
+
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr == nil)
+ panic("tlsbwrite");+
+ ty = TYPE(c->qid);
+ switch(ty) {+ default:
+ return devbwrite(c, b, offset);
+ case Qhand:
+ tlsrecwrite(tr, RHandshake, b);
+ tr->handout += n;
+ break;
+ case Qdata:
+ tlsrecwrite(tr, RApplication, b);
+ tr->dataout += n;
+ break;
+ }
+
+ return n;
+}
+
+typedef struct Hashalg Hashalg;
+struct Hashalg
+{+ char *name;
+ int maclen;
+ void (*initkey)(Hashalg *, int, Secret *, uchar*);
+};
+
+static void
+initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
+{+ s->maclen = ha->maclen;
+ if(version == SSL3Version)
+ s->mac = sslmac_md5;
+ else
+ s->mac = hmac_md5;
+ memmove(s->mackey, p, ha->maclen);
+}
+
+static void
+initclearmac(Hashalg *ha, int version, Secret *s, uchar *p)
+{+ s->mac = nomac;
+}
+
+static void
+initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
+{+ s->maclen = ha->maclen;
+ if(version == SSL3Version)
+ s->mac = sslmac_sha1;
+ else
+ s->mac = hmac_sha1;
+ memmove(s->mackey, p, ha->maclen);
+}
+
+static void
+initsha2_256key(Hashalg *ha, int version, Secret *s, uchar *p)
+{+ if(version == SSL3Version)
+ error("sha256 cannot be used with SSL");+ s->maclen = ha->maclen;
+ s->mac = hmac_sha2_256;
+ memmove(s->mackey, p, ha->maclen);
+}
+
+static Hashalg hashtab[] =
+{+ { "clear", 0, initclearmac, },+ { "md5", MD5dlen, initmd5key, },+ { "sha1", SHA1dlen, initsha1key, },+ { "sha256", SHA2_256dlen, initsha2_256key, },+ { 0 }+};
+
+static Hashalg*
+parsehashalg(char *p)
+{+ Hashalg *ha;
+
+ for(ha = hashtab; ha->name; ha++)
+ if(strcmp(p, ha->name) == 0)
+ return ha;
+ error("unsupported hash algorithm");+ return nil;
+}
+
+typedef struct Encalg Encalg;
+struct Encalg
+{+ char *name;
+ int keylen;
+ int ivlen;
+ void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
+};
+
+static void
+initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{+ s->enckey = secalloc(sizeof(RC4state));
+ s->enc = rc4enc;
+ s->dec = rc4enc;
+ setupRC4state(s->enckey, p, ea->keylen);
+}
+
+static void
+initDES3key(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{+ s->enckey = secalloc(sizeof(DES3state));
+ s->enc = des3enc;
+ s->dec = des3dec;
+ s->block = 8;
+ setupDES3state(s->enckey, (uchar(*)[8])p, iv);
+}
+
+static void
+initAESkey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{+ s->enckey = secalloc(sizeof(AESstate));
+ s->enc = aesenc;
+ s->dec = aesdec;
+ s->block = 16;
+ setupAESstate(s->enckey, p, ea->keylen, iv);
+}
+
+static void
+initccpolykey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{+ s->enckey = secalloc(sizeof(Chachastate));
+ s->aead_enc = ccpoly_aead_enc;
+ s->aead_dec = ccpoly_aead_dec;
+ s->maclen = Poly1305dlen;
+ if(ea->ivlen == 0) {+ /* older draft version, iv is 64-bit sequence number */
+ setupChachastate(s->enckey, p, ea->keylen, nil, 64/8, 20);
+ } else {+ /* IETF standard, 96-bit iv xored with sequence number */
+ memmove(s->mackey, iv, ea->ivlen);
+ setupChachastate(s->enckey, p, ea->keylen, iv, ea->ivlen, 20);
+ }
+}
+
+static void
+initaesgcmkey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{+ s->enckey = secalloc(sizeof(AESGCMstate));
+ s->aead_enc = aesgcm_aead_enc;
+ s->aead_dec = aesgcm_aead_dec;
+ s->maclen = 16;
+ s->recivlen = 8;
+ memmove(s->mackey, iv, ea->ivlen);
+ prng(s->mackey + ea->ivlen, s->recivlen);
+ setupAESGCMstate(s->enckey, p, ea->keylen, nil, 0);
+}
+
+static void
+initclearenc(Encalg *ea, Secret *s, uchar *key, uchar *iv)
+{+ s->enc = noenc;
+ s->dec = noenc;
+}
+
+static Encalg encrypttab[] =
+{+ { "clear", 0, 0, initclearenc },+ { "rc4_128", 128/8, 0, initRC4key },+ { "3des_ede_cbc", 3 * 8, 8, initDES3key },+ { "aes_128_cbc", 128/8, 16, initAESkey },+ { "aes_256_cbc", 256/8, 16, initAESkey },+ { "ccpoly64_aead", 256/8, 0, initccpolykey },+ { "ccpoly96_aead", 256/8, 96/8, initccpolykey },+ { "aes_128_gcm_aead", 128/8, 4, initaesgcmkey },+ { "aes_256_gcm_aead", 256/8, 4, initaesgcmkey },+ { 0 }+};
+
+static Encalg*
+parseencalg(char *p)
+{+ Encalg *ea;
+
+ for(ea = encrypttab; ea->name; ea++)
+ if(strcmp(p, ea->name) == 0)
+ return ea;
+ error("unsupported encryption algorithm");+ return nil;
+}
+
+static long
+tlswrite(Chan *c, void *a, long n, vlong off)
+{+ Encalg *ea;
+ Hashalg *ha;
+ TlsRec *volatile tr;
+ Secret *volatile tos, *volatile toc;
+ Block *volatile b;
+ Cmdbuf *volatile cb;
+ int m, ty;
+ char *p, *e;
+ uchar *volatile x;
+ ulong offset = off;
+
+ tr = tlsdevs[CONV(c->qid)];
+ if(tr == nil)
+ panic("tlswrite");+
+ ty = TYPE(c->qid);
+ switch(ty){+ case Qdata:
+ case Qhand:
+ p = a;
+ e = p + n;
+ do{+ m = e - p;
+ if(m > c->iounit)
+ m = c->iounit;
+
+ b = allocb(m);
+ if(waserror()){+ freeb(b);
+ nexterror();
+ }
+ memmove(b->wp, p, m);
+ poperror();
+ b->wp += m;
+
+ tlsbwrite(c, b, offset);
+ offset += m;
+
+ p += m;
+ }while(p < e);
+ return n;
+ case Qctl:
+ break;
+ default:
+ error(Ebadusefd);
+ return -1;
+ }
+
+ cb = parsecmd(a, n);
+ if(waserror()){+ free(cb);
+ nexterror();
+ }
+ if(cb->nf < 1)
+ error("short control request");+
+ /* mutex with operations using what we're about to change */
+ if(waserror()){+ qunlock(&tr->in.seclock);
+ qunlock(&tr->out.seclock);
+ nexterror();
+ }
+ qlock(&tr->in.seclock);
+ qlock(&tr->out.seclock);
+
+ if(strcmp(cb->f[0], "fd") == 0){+ if(cb->nf != 3)
+ error("usage: fd open-fd version");+ if(tr->c != nil)
+ error(Einuse);
+ m = strtol(cb->f[2], nil, 0);
+ if(m < MinProtoVersion || m > MaxProtoVersion)
+ error("unsupported version");+ tr->c = buftochan(cb->f[1]);
+ tr->version = m;
+ tlsSetState(tr, SHandshake, SClosed);
+ }else if(strcmp(cb->f[0], "version") == 0){+ if(cb->nf != 2)
+ error("usage: version vers");+ if(tr->c == nil)
+ error("must set fd before version");+ if(tr->verset)
+ error("version already set");+ m = strtol(cb->f[1], nil, 0);
+ if(m < MinProtoVersion || m > MaxProtoVersion)
+ error("unsupported version");+ if(m == SSL3Version)
+ tr->packAAD = sslPackAAD;
+ else
+ tr->packAAD = tlsPackAAD;
+ tr->verset = 1;
+ tr->version = m;
+ }else if(strcmp(cb->f[0], "secret") == 0){+ if(cb->nf != 5)
+ error("usage: secret hashalg encalg isclient secretdata");+ if(tr->c == nil || !tr->verset)
+ error("must set fd and version before secrets");+
+ if(tr->in.new != nil){+ freeSec(tr->in.new);
+ tr->in.new = nil;
+ }
+ if(tr->out.new != nil){+ freeSec(tr->out.new);
+ tr->out.new = nil;
+ }
+
+ ha = parsehashalg(cb->f[1]);
+ ea = parseencalg(cb->f[2]);
+
+ p = cb->f[4];
+ m = (strlen(p)*3)/2 + 1;
+ x = secalloc(m);
+ tos = secalloc(sizeof(Secret));
+ toc = secalloc(sizeof(Secret));
+ if(waserror()){+ secfree(x);
+ freeSec(tos);
+ freeSec(toc);
+ nexterror();
+ }
+
+ m = dec64(x, m, p, strlen(p));
+ memset(p, 0, strlen(p));
+ if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
+ error("not enough secret data provided");+
+ if(!ha->initkey || !ea->initkey)
+ error("misimplemented secret algorithm");+
+ (*ha->initkey)(ha, tr->version, tos, &x[0]);
+ (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
+ (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
+ (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
+
+ if(!tos->aead_enc || !tos->aead_dec || !toc->aead_enc || !toc->aead_dec)
+ if(!tos->mac || !tos->enc || !tos->dec || !toc->mac || !toc->enc || !toc->dec)
+ error("missing algorithm implementations");+
+ if(strtol(cb->f[3], nil, 0) == 0){+ tr->in.new = tos;
+ tr->out.new = toc;
+ }else{+ tr->in.new = toc;
+ tr->out.new = tos;
+ }
+ if(tr->version == SSL3Version){+ toc->unpad = sslunpad;
+ tos->unpad = sslunpad;
+ }else{+ toc->unpad = tlsunpad;
+ tos->unpad = tlsunpad;
+ }
+ toc->encalg = ea->name;
+ toc->hashalg = ha->name;
+ tos->encalg = ea->name;
+ tos->hashalg = ha->name;
+
+ secfree(x);
+ poperror();
+ }else if(strcmp(cb->f[0], "changecipher") == 0){+ if(cb->nf != 1)
+ error("usage: changecipher");+ if(tr->out.new == nil)
+ error("cannot change cipher spec without setting secret");+
+ qunlock(&tr->in.seclock);
+ qunlock(&tr->out.seclock);
+ poperror();
+ free(cb);
+ poperror();
+
+ /*
+ * the real work is done as the message is written
+ * so the stream is encrypted in sync.
+ */
+ b = allocb(1);
+ *b->wp++ = 1;
+ tlsrecwrite(tr, RChangeCipherSpec, b);
+ return n;
+ }else if(strcmp(cb->f[0], "opened") == 0){+ if(cb->nf != 1)
+ error("usage: opened");+ if(tr->in.sec == nil || tr->out.sec == nil)
+ error("cipher must be configured before enabling data messages");+ lock(&tr->statelk);
+ if(tr->state != SHandshake && tr->state != SOpen){+ unlock(&tr->statelk);
+ error("cannot enable data messages");+ }
+ tr->state = SOpen;
+ unlock(&tr->statelk);
+ tr->opened = 1;
+ }else if(strcmp(cb->f[0], "alert") == 0){+ if(cb->nf != 2)
+ error("usage: alert n");+ m = strtol(cb->f[1], nil, 0);
+ Hangup:
+ if(tr->c == nil)
+ error("must set fd before sending alerts");+ qunlock(&tr->in.seclock);
+ qunlock(&tr->out.seclock);
+ poperror();
+ free(cb);
+ poperror();
+
+ sendAlert(tr, m);
+
+ if(m == ECloseNotify)
+ tlsclosed(tr, SLClose);
+
+ return n;
+ } else if(strcmp(cb->f[0], "hangup") == 0){+ m = ECloseNotify;
+ goto Hangup;
+ } else if(strcmp(cb->f[0], "debug") == 0){+ if(cb->nf == 2){+ if(strcmp(cb->f[1], "on") == 0)
+ tr->debug = 1;
+ else
+ tr->debug = 0;
+ } else
+ tr->debug = 1;
+ } else
+ error(Ebadarg);
+
+ qunlock(&tr->in.seclock);
+ qunlock(&tr->out.seclock);
+ poperror();
+ free(cb);
+ poperror();
+
+ return n;
+}
+
+static void
+tlsinit(void)
+{+ struct Encalg *e;
+ struct Hashalg *h;
+ int n;
+ char *cp;
+
+ fmtinstall('H', encodefmt);+
+ tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
+ trnames = smalloc((sizeof *trnames) * maxtlsdevs);
+
+ n = 1;
+ for(e = encrypttab; e->name != nil; e++)
+ n += strlen(e->name) + 1;
+ cp = encalgs = smalloc(n);
+ for(e = encrypttab;;){+ strcpy(cp, e->name);
+ cp += strlen(e->name);
+ e++;
+ if(e->name == nil)
+ break;
+ *cp++ = ' ';
+ }
+ *cp = 0;
+
+ n = 1;
+ for(h = hashtab; h->name != nil; h++)
+ n += strlen(h->name) + 1;
+ cp = hashalgs = smalloc(n);
+ for(h = hashtab;;){+ strcpy(cp, h->name);
+ cp += strlen(h->name);
+ h++;
+ if(h->name == nil)
+ break;
+ *cp++ = ' ';
+ }
+ *cp = 0;
+}
+
+Dev tlsdevtab = {+ 'a',
+ "tls",
+
+ devreset,
+ tlsinit,
+ devshutdown,
+ tlsattach,
+ tlswalk,
+ tlsstat,
+ tlsopen,
+ devcreate,
+ tlsclose,
+ tlsread,
+ tlsbread,
+ tlswrite,
+ tlsbwrite,
+ devremove,
+ tlswstat,
+};
+
+/* get channel associated with an fd */
+static Chan*
+buftochan(char *p)
+{+ Chan *c;
+ int fd;
+
+ if(p == 0)
+ error(Ebadarg);
+ fd = strtoul(p, 0, 0);
+ if(fd < 0)
+ error(Ebadarg);
+ c = fdtochan(fd, ORDWR, 1, 1); /* error check and inc ref */
+ return c;
+}
+
+static void
+sendAlert(TlsRec *tr, int err)
+{+ Block *b;
+ int i, fatal;
+ char *msg;
+
+if(tr->debug)pprint("sendAlert %d\n", err);+ fatal = 1;
+ msg = "tls unknown alert";
+ for(i=0; i < nelem(tlserrs); i++) {+ if(tlserrs[i].err == err) {+ msg = tlserrs[i].msg;
+ if(tr->version == SSL3Version)
+ err = tlserrs[i].sslerr;
+ else
+ err = tlserrs[i].tlserr;
+ fatal = tlserrs[i].fatal;
+ break;
+ }
+ }
+
+ if(!waserror()){+ b = allocb(2);
+ *b->wp++ = fatal + 1;
+ *b->wp++ = err;
+ if(fatal)
+ tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
+ tlsrecwrite(tr, RAlert, b);
+ poperror();
+ }
+ if(fatal)
+ tlsError(tr, msg);
+}
+
+static void
+tlsError(TlsRec *tr, char *msg)
+{+ int s;
+
+if(tr->debug)pprint("tlsError %s\n", msg);+ lock(&tr->statelk);
+ s = tr->state;
+ tr->state = SError;
+ if(s != SError){+ strncpy(tr->err, msg, ERRMAX - 1);
+ tr->err[ERRMAX - 1] = '\0';
+ }
+ unlock(&tr->statelk);
+ if(s != SError)
+ alertHand(tr, msg);
+}
+
+static void
+tlsSetState(TlsRec *tr, int new, int old)
+{+ lock(&tr->statelk);
+ if(tr->state & old)
+ tr->state = new;
+ unlock(&tr->statelk);
+}
+
+/* hand up a digest connection */
+static void
+tlshangup(TlsRec *tr)
+{+ Block *b;
+
+ qlock(&tr->in.io);
+ for(b = tr->processed; b; b = tr->processed){+ tr->processed = b->next;
+ freeb(b);
+ }
+ if(tr->unprocessed != nil){+ freeb(tr->unprocessed);
+ tr->unprocessed = nil;
+ }
+ qunlock(&tr->in.io);
+
+ tlsSetState(tr, SClosed, ~0);
+}
+
+static TlsRec*
+newtls(Chan *ch)
+{+ TlsRec **pp, **ep, **np;
+ char **nmp;
+ int t, newmax;
+
+ if(waserror()) {+ unlock(&tdlock);
+ nexterror();
+ }
+ lock(&tdlock);
+ ep = &tlsdevs[maxtlsdevs];
+ for(pp = tlsdevs; pp < ep; pp++)
+ if(*pp == nil)
+ break;
+ if(pp >= ep) {+ if(maxtlsdevs >= MaxTlsDevs) {+ unlock(&tdlock);
+ poperror();
+ return nil;
+ }
+ newmax = 2 * maxtlsdevs;
+ if(newmax > MaxTlsDevs)
+ newmax = MaxTlsDevs;
+ np = smalloc(sizeof(TlsRec*) * newmax);
+ memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
+ tlsdevs = np;
+ pp = &tlsdevs[maxtlsdevs];
+ memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
+
+ nmp = smalloc(sizeof *nmp * newmax);
+ memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
+ trnames = nmp;
+
+ maxtlsdevs = newmax;
+ }
+ *pp = mktlsrec();
+ if(pp - tlsdevs >= tdhiwat)
+ tdhiwat++;
+ t = TYPE(ch->qid);
+ if(t == Qclonus)
+ t = Qctl;
+ ch->qid.path = QID(pp - tlsdevs, t);
+ ch->qid.vers = 0;
+ unlock(&tdlock);
+ poperror();
+ return *pp;
+}
+
+static TlsRec *
+mktlsrec(void)
+{+ TlsRec *tr;
+
+ tr = mallocz(sizeof(*tr), 1);
+ if(tr == nil)
+ error(Enomem);
+ tr->state = SClosed;
+ tr->ref = 1;
+ kstrdup(&tr->user, up->user);
+ tr->perm = 0660;
+ return tr;
+}
+
+static char*
+tlsstate(int s)
+{+ switch(s){+ case SHandshake:
+ return "Handshaking";
+ case SOpen:
+ return "Established";
+ case SRClose:
+ return "RemoteClosed";
+ case SLClose:
+ return "LocalClosed";
+ case SAlert:
+ return "Alerting";
+ case SError:
+ return "Errored";
+ case SClosed:
+ return "Closed";
+ }
+ return "Unknown";
+}
+
+static void
+freeSec(Secret *s)
+{+ if(s == nil)
+ return;
+ secfree(s->enckey);
+ secfree(s);
+}
+
+static int
+noenc(Secret *sec, uchar *buf, int n)
+{+ return n;
+}
+
+static int
+rc4enc(Secret *sec, uchar *buf, int n)
+{+ rc4(sec->enckey, buf, n);
+ return n;
+}
+
+static int
+tlsunpad(uchar *buf, int n, int block)
+{+ int pad, nn;
+
+ pad = buf[n - 1];
+ nn = n - 1 - pad;
+ if(nn <= 0 || n % block)
+ return -1;
+ while(--n > nn)
+ if(pad != buf[n - 1])
+ return -1;
+ return nn;
+}
+
+static int
+sslunpad(uchar *buf, int n, int block)
+{+ int pad, nn;
+
+ pad = buf[n - 1];
+ nn = n - 1 - pad;
+ if(nn <= 0 || n % block)
+ return -1;
+ return nn;
+}
+
+static int
+blockpad(uchar *buf, int n, int block)
+{+ int pad, nn;
+
+ nn = n + block;
+ nn -= nn % block;
+ pad = nn - (n + 1);
+ while(n < nn)
+ buf[n++] = pad;
+ return nn;
+}
+
+static int
+des3enc(Secret *sec, uchar *buf, int n)
+{+ n = blockpad(buf, n, 8);
+ des3CBCencrypt(buf, n, sec->enckey);
+ return n;
+}
+
+static int
+des3dec(Secret *sec, uchar *buf, int n)
+{+ des3CBCdecrypt(buf, n, sec->enckey);
+ return (*sec->unpad)(buf, n, 8);
+}
+
+static int
+aesenc(Secret *sec, uchar *buf, int n)
+{+ n = blockpad(buf, n, 16);
+ aesCBCencrypt(buf, n, sec->enckey);
+ return n;
+}
+
+static int
+aesdec(Secret *sec, uchar *buf, int n)
+{+ aesCBCdecrypt(buf, n, sec->enckey);
+ return (*sec->unpad)(buf, n, 16);
+}
+
+static void
+ccpoly_aead_setiv(Secret *sec, uchar seq[8])
+{+ uchar iv[ChachaIVlen];
+ Chachastate *cs;
+ int i;
+
+ cs = (Chachastate*)sec->enckey;
+ if(cs->ivwords == 2){+ chacha_setiv(cs, seq);
+ return;
+ }
+
+ memmove(iv, sec->mackey, ChachaIVlen);
+ for(i=0; i<8; i++)
+ iv[i+(ChachaIVlen-8)] ^= seq[i];
+
+ chacha_setiv(cs, iv);
+
+ memset(iv, 0, sizeof(iv));
+}
+
+static int
+ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
+{+ USED(reciv);
+ ccpoly_aead_setiv(sec, aad);
+ ccpoly_encrypt(data, len, aad, aadlen, data+len, sec->enckey);
+ return len + sec->maclen;
+}
+
+static int
+ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
+{+ USED(reciv);
+ len -= sec->maclen;
+ if(len < 0)
+ return -1;
+ ccpoly_aead_setiv(sec, aad);
+ if(ccpoly_decrypt(data, len, aad, aadlen, data+len, sec->enckey) != 0)
+ return -1;
+ return len;
+}
+
+static int
+aesgcm_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
+{+ uchar iv[12];
+ int i;
+
+ memmove(iv, sec->mackey, 4+8);
+ for(i=0; i<8; i++) iv[4+i] ^= aad[i];
+ memmove(reciv, iv+4, 8);
+ aesgcm_setiv(sec->enckey, iv, 12);
+ memset(iv, 0, sizeof(iv));
+ aesgcm_encrypt(data, len, aad, aadlen, data+len, sec->enckey);
+ return len + sec->maclen;
+}
+
+static int
+aesgcm_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
+{+ uchar iv[12];
+
+ len -= sec->maclen;
+ if(len < 0)
+ return -1;
+ memmove(iv, sec->mackey, 4);
+ memmove(iv+4, reciv, 8);
+ aesgcm_setiv(sec->enckey, iv, 12);
+ memset(iv, 0, sizeof(iv));
+ if(aesgcm_decrypt(data, len, aad, aadlen, data+len, sec->enckey) != 0)
+ return -1;
+ return len;
+}
+
+
+static DigestState*
+nomac(uchar *p, ulong len, uchar *key, ulong keylen, uchar *digest, DigestState *s)
+{+ return nil;
+}
+
+/*
+ * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
+ */
+static DigestState*
+sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
+ DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
+{+ int i;
+ uchar pad[48], innerdigest[20];
+
+ if(xlen > sizeof(innerdigest)
+ || padlen > sizeof(pad))
+ return nil;
+
+ if(klen>64)
+ return nil;
+
+ /* first time through */
+ if(s == nil){+ for(i=0; i<padlen; i++)
+ pad[i] = 0x36;
+ s = (*x)(key, klen, nil, nil);
+ s = (*x)(pad, padlen, nil, s);
+ if(s == nil)
+ return nil;
+ }
+
+ s = (*x)(p, len, nil, s);
+ if(digest == nil)
+ return s;
+
+ /* last time through */
+ for(i=0; i<padlen; i++)
+ pad[i] = 0x5c;
+ (*x)(nil, 0, innerdigest, s);
+ s = (*x)(key, klen, nil, nil);
+ s = (*x)(pad, padlen, nil, s);
+ (*x)(innerdigest, xlen, digest, s);
+ return nil;
+}
+
+static DigestState*
+sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
+{+ return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
+}
+
+static DigestState*
+sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
+{+ return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
+}
+
+static int
+sslPackAAD(u64int seq, uchar *hdr, uchar *aad)
+{+ put64(aad, seq);
+ aad[8] = hdr[0];
+ aad[9] = hdr[3];
+ aad[10] = hdr[4];
+ return 11;
+}
+
+static int
+tlsPackAAD(u64int seq, uchar *hdr, uchar *aad)
+{+ put64(aad, seq);
+ aad[8] = hdr[0];
+ aad[9] = hdr[1];
+ aad[10] = hdr[2];
+ aad[11] = hdr[3];
+ aad[12] = hdr[4];
+ return 13;
+}
+
+static void
+packMac(Secret *sec, uchar *aad, int aadlen, uchar *body, int bodylen, uchar *mac)
+{+ DigestState *s;
+
+ s = (*sec->mac)(aad, aadlen, sec->mackey, sec->maclen, nil, nil);
+ (*sec->mac)(body, bodylen, sec->mackey, sec->maclen, mac, s);
+}
+
+static void
+put32(uchar *p, u32int x)
+{+ p[0] = x>>24;
+ p[1] = x>>16;
+ p[2] = x>>8;
+ p[3] = x;
+}
+
+static void
+put64(uchar *p, u64int x)
+{+ put32(p, x >> 32);
+ put32(p+4, x);
+}
+
+static void
+put24(uchar *p, int x)
+{+ p[0] = x>>16;
+ p[1] = x>>8;
+ p[2] = x;
+}
+
+static void
+put16(uchar *p, int x)
+{+ p[0] = x>>8;
+ p[1] = x;
+}
+
+static int
+get16(uchar *p)
+{+ return (p[0]<<8)|p[1];
+}
+
+static char *charmap = "0123456789abcdef";
+
+static void
+pdump(int len, void *a, char *tag)
+{+ uchar *p;
+ int i;
+ char buf[65+32];
+ char *q;
+
+ p = a;
+ strcpy(buf, tag);
+ while(len > 0){+ q = buf + strlen(tag);
+ for(i = 0; len > 0 && i < 32; i++){+ if(*p >= ' ' && *p < 0x7f){+ *q++ = ' ';
+ *q++ = *p;
+ } else {+ *q++ = charmap[*p>>4];
+ *q++ = charmap[*p & 0xf];
+ }
+ len--;
+ p++;
+ }
+ *q = 0;
+
+ if(len > 0)
+ pprint("%s...\n", buf);+ else
+ pprint("%s\n", buf);+ }
+}
--- a/kern/seg.c
+++ b/kern/seg.c
@@ -7,7 +7,8 @@
newseg(u32int start, u32int size, int idx)
{Segment *s;
-
+ if(debug)
+ print("newseg: size=%.8ux start=%.8ux idx=%d\n", size, start, idx);s = malloc(sizeof *s);
if(s == nil)
panic("%r");@@ -48,6 +49,7 @@
vaddr(u32int addr, u32int len, Segment **seg)
{Segment **ss, *s;
+
for (ss = up->S; ss < up->S + SEGNUM; ss++) {if (*ss == nil)
continue;
@@ -58,10 +60,12 @@
if (s->flags & SEGFLLOCK)
rlock(&s->rw);
*seg = s;
+// if(debug)
+// print("vaddr addr=%.8ux, (%d), PC=%.8ux index=%.8ux\n", addr, len, up->R[15], s->start);return (char *)s->data + (addr - s->start);
}
}
- panic("fault addr=%.8ux, (%d), PC=%.8ux\n", addr, len, up->R[15]);+ panic("fault addr=%.8ux, (%d), PC=%.8ux", addr, len, up->R[15]);return nil;
}
--- a/kern/syscall.c
+++ b/kern/syscall.c
@@ -7,6 +7,32 @@
#include "sys.h"
#include "proc.h"
+static void *
+emalloc(int n)
+{+ void *p;
+ if(n==0)
+ n=1;
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("out of memory");+ memset(p, 0, n);
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+static char*
+estrdup(char *s)
+{+ char *d;
+ int n;
+
+ n = strlen(s)+1;
+ d = emalloc(n);
+ memmove(d, s, n);
+ return d;
+}
+
static u32int
arg(int n)
{@@ -45,11 +71,12 @@
u32int name, flags;
char *namet;
int fd, copied;
-
name = arg(0);
flags = arg(1);
namet = copyifnec(name, -1, &copied);
fd = open(namet, flags);
+ if(debug)
+ print("sysopen: fd=%.8ux name=%s flags=%.8ux\n", fd, namet, flags);if(copied)
free(namet);
if(fd < 0) {@@ -88,6 +115,8 @@
u32int fd;
fd = arg(0);
+ if(debug)
+ print("sysclose: fd=%.8ux\n", fd);up->R[0] = noteerr(close(fd), 0);
}
@@ -103,6 +132,8 @@
buf = arg(1);
size = arg(2);
off = argv(3);
+ if(debug)
+ print("syspread: size=0x%.8ux off=0x%.8ux\n", size, off);targ = bufifnec(buf, size, &buffered);
up->R[0] = noteerr(pread(fd, targ, size, off), size);
if(buffered)
@@ -122,6 +153,8 @@
size = arg(2);
off = argv(3);
buft = copyifnec(buf, size, &copied);
+ if(debug)
+ print("syspwrite: buf=%s size=%.8ux off=%.8ux\n", buft, size, off);up->R[0] = noteerr(pwrite(fd, buft, size, off), size);
if(copied)
free(buft);
@@ -138,6 +171,8 @@
fd = arg(1);
n = argv(2);
type = arg(4);
+ if(debug)
+ print("sysseek: to=%d whence=%.4ux\n", n, type);*ret = seek(fd, n, type);
if(*ret < 0) noteerr(0, 1);
segunlock(seg);
@@ -170,7 +205,8 @@
print("sysstat\n");name = arg(0); // R0
- print("sysstat: name=%.8ux, R13=%.8ux\n", name, up->R[13]);+ if(debug)
+ print("sysstat: name=%.8ux, R13=%.8ux\n", name, up->R[13]); if (!vaddr(name, 1, &seg)) { print("sysstat: invalid name pointer %.8ux\n", name); cherrstr("invalid name pointer");@@ -180,7 +216,8 @@
namet = copyifnec(name, -1, &copied);
edir = arg(1); // R1
nedir = arg(2); // R2
- print("sysstat: edir=%.8ux, nedir=%d\n", edir, nedir);+ if(debug)
+ print("sysstat: edir=%.8ux, nedir=%d\n", edir, nedir); if (nedir > 0 && !vaddr(edir, nedir, &seg)) { print("sysstat: invalid edir pointer %.8ux\n", edir); cherrstr("invalid edir pointer");@@ -203,7 +240,8 @@
u32int fd, edir, nedir;
void *edirt;
int buffered;
- print("sysfstat\n");+ if(debug)
+ print("sysfstat\n");fd = arg(0);
edir = arg(1);
nedir = arg(2);
@@ -252,7 +290,6 @@
static void
_sysexits(void)
{- print("Exiting\n");if(arg(0) == 0)
exits(nil);
else
@@ -264,7 +301,8 @@
{ulong v;
Segment *s;
-
+ if(debug)
+ print("sysbrk\n");v = arg(0);
v = v + 7 & -8;
if(v >= up->S[SEGSTACK]->start)
@@ -676,7 +714,6 @@
[NOTED] = _sysnoted,
[RFORK] = _sysrfork,
[EXEC] = _sysexec,
- [AWAIT] = _sysawait,
[PIPE] = _syspipe,
[SLEEP] = _syssleep,
[RENDEZVOUS]= _sysrendezvous,
@@ -689,10 +726,8 @@
[SEMACQUIRE]= _syssemacquire,
[SEMRELEASE]= _syssemrelease,
};
-
+
n = up->R[0];
- print("syscall: calling handler for syscall %d\n", n);calls[n]();
- print("syscall: handler for syscall %d completed\n", n);}
\ No newline at end of file
--- a/kern/sysproc.c
+++ b/kern/sysproc.c
@@ -14,7 +14,9 @@
#undef rfork
#undef read
+
jmp_buf exec_jmp;
+#define pgsize 0x1000
static void
nop(int x)
@@ -25,10 +27,22 @@
static u32int
arg(int n)
{+ USED(n);
/* no locking necessary, since we're on the stack */
return *(u32int*) vaddrnol(up->R[13] + 4 + 4 * n, 4);
}
+static uvlong
+_round(uvlong a, ulong b)
+{+ uvlong w;
+
+ w = (a/b)*b;
+ if (a!=w)
+ w += b;
+ return(w);
+}
+
int
sysrfork(int flags)
{@@ -133,24 +147,6 @@
return 0;
}
-static int
-shargs(char *s, int n, char **ap, int nap)
-{- char *p;
- int i;
-
- if(n <= 2 || s[0] != '#' || s[1] != '!')
- return -1;
- s += 2;
- n -= 2;
- if((p = memchr(s, '\n', n)) == nil)
- return 0;
- *p = 0;
- i = tokenize(s, ap, nap-1);
- ap[i] = nil;
- return i;
-}
-
ulong
beswal(ulong l)
{@@ -261,11 +257,12 @@
tos = (USTKTOP & ~7) - sizeof(Tos) * 2;
sp = tos;
-
+ if(debug)
+ print("initstack: tos=%.8ux tossz=%.8ux USTKTOP=%.8ux\n", tos, sizeof(Tos), USTKTOP);size = 8;
for(i = 0; i < argc; i++)
size += strlen(argv[i]) + 5;
-
+
sp -= size;
sp &= ~7;
up->R[0] = tos;
@@ -275,6 +272,8 @@
*(ulong *) vaddrnol(sp, 4) = argc;
sp += 4;
ap = sp + (argc + 1) * 4;
+ if(debug)
+ print("initstack: argc=%d sp=%.8ux ap=%.8ux\n", argc, sp, ap); for(i = 0; i < argc; i++) {*(ulong *) vaddrnol(sp, 4) = ap;
sp += 4;
@@ -297,132 +296,109 @@
}
static int
-loadscript(Chan *tc, char *file, int argc, char **argv)
+loadscript(int fd, char *file, int argc, char **argv)
{- char buf[513], *p, **q, **nargv;
- int rc, nargc, i;
-
- rc = devtab[tc->type]->read(tc, &buf, 512, 0);
- if(rc <= 0)
- goto invalid;
+ char buf[513], *p, **q, **nargv;
+ int rc, nargc, i;
+
+ seek(fd, 0, 0);
+ rc = readn(fd, buf, 512);
+ if(rc <= 0)
+ goto invalid;
+ close(fd);
+ buf[rc] = 0;
+ p = strchr(buf, '\n');
+ if(p == nil)
+ goto invalid;
+ *p = 0;
+ while(isspace(*--p))
+ *p = 0;
+ nargc = 0;
+ p = buf + 2;
+ while(*p) {+ while(*p && isspace(*p))
+ p++;
+ nargc++;
+ while(*p && !isspace(*p))
+ p++;
+ }
+ if(nargc == 0)
+ goto invalid;
+ nargv = malloc(sizeof(char *) * (nargc + argc));
+ q = nargv;
+ p = buf + 2;
+ while(*p) {+ while(*p && isspace(*p))
+ p++;
+ *(p-1) = 0;
+ *q++ = p;
+ while(*p && !isspace(*p))
+ p++;
+ }
+ *q++ = file;
+ for(i = 1; i < argc; i++)
+ *q++ = argv[i];
+ rc = loadtext(*nargv, argc + nargc, nargv);
+ free(nargv);
+ return rc;
- buf[rc] = 0;
- p = strchr(buf, '\n');
- if(p == nil)
- goto invalid;
- *p = 0;
- while(isspace(*--p))
- *p = 0;
- nargc = 0;
- p = buf + 2;
- while(*p) {- while(*p && isspace(*p))
- p++;
- nargc++;
- while(*p && !isspace(*p))
- p++;
- }
- if(nargc == 0)
- goto invalid;
- nargv = mallocz(sizeof(char *) * (nargc + argc), 1);
- q = nargv;
- p = buf + 2;
- while(*p) {- while(*p && isspace(*p))
- p++;
- *(p-1) = 0;
- *q++ = p;
- while(*p && !isspace(*p))
- p++;
- }
- *q++ = file;
- for(i = 1; i < argc; i++)
- *q++ = argv[i];
- rc = loadtext(*nargv, argc + nargc, nargv);
- free(nargv);
- return rc;
-
invalid:
- werrstr("exec header invalid");- return -1;
+ werrstr("exec header invalid");+ return -1;
}
int
loadtext(char *file, int argc, char **argv)
{- Chan *tc;
Segment *text, *data, *bss;
+ uvlong txtaddr, txtsz, txtoff, dataddr, datsz, datoff, bsssz, hdrsz;
Exec hdr;
char buf[2];
- vlong off;
- int n;
+ int fd;
- /* Open the file using namespaced access */
- tc = namec(file, Aopen, OEXEC, 0);
- if(tc == nil) {- print("cannot open %s: %r", file);- return -1;
- }
-
- /* Peek the first two bytes */
- devtab[tc->type]->read(tc, &buf, 2, 0);
- if(buf[0] =='#' && buf[1] == '!') {- return loadscript(tc, file, argc, argv);
- }
+ fd = open(file, OREAD);
+ if(fd < 0) return -1;
+ if(pread(fd, buf, 2, 0) == 2 && buf[0] == '#' && buf[1] == '!')
+ return loadscript(fd, file, argc, argv);
+ seek(fd, 0, 0);
+ if(pread(fd, &hdr, sizeof(Exec), 0) != sizeof(Exec))
+ panic("Short read on header\n");- n = devtab[tc->type]->read(tc, &hdr, sizeof(Exec), 0);
- if(n != sizeof(Exec)) {- print("short read on exec header");- cclose(tc);
- return -1;
- }
-
- if(beswal(hdr.magic) != E_MAGIC) {- print("bad magic number in exec header: got 0x%lux, want 0x%lux", beswal(hdr.magic), E_MAGIC);- cclose(tc);
- return -1;
- }
+ if(beswal(hdr.magic) != E_MAGIC)
+ panic("bad magic number in exec header: got 0x%lux, want 0x%lux", beswal(hdr.magic), E_MAGIC);freesegs();
up->CPSR = 0;
+ hdrsz = sizeof(Exec);
+ txtaddr = pgsize + hdrsz;
+ txtsz = beswal(hdr.text);
+ txtoff = sizeof(Exec);
+ dataddr = _round(pgsize+txtsz+hdrsz, pgsize);
+ datsz = beswal(hdr.data);
+ datoff = txtsz + hdrsz;
+ bsssz = beswal(hdr.bss);
+
/* Create segments: text, data, bss, stack */
- text = newseg(beswal(hdr.entry), beswal(hdr.text) + sizeof(Exec), SEGTEXT);
- data = newseg(beswal(hdr.entry) + beswal(hdr.text) + sizeof(Exec), beswal(hdr.data), SEGDATA);
- bss = newseg(beswal(hdr.entry) + beswal(hdr.text) + sizeof(Exec) + beswal(hdr.data), beswal(hdr.bss), SEGBSS);
+ text = newseg(txtaddr - hdrsz, txtsz + hdrsz, SEGTEXT);
+ data = newseg(dataddr, datsz, SEGDATA);
+ bss = newseg(dataddr + datsz, bsssz, SEGBSS);
newseg((USTKTOP & ~7) - USTKSIZE, USTKSIZE, SEGSTACK);
+ seek(fd, txtoff - hdrsz, 0);
+ if(readn(fd, text->data, txtsz + hdrsz) < txtsz + hdrsz)
+ panic("%r");+ seek(fd, datoff, 0);
+ if(readn(fd, data->data, datsz) < datsz)
+ panic("%r");- /* Read text segment (including header) */
- off = sizeof(Exec);
- n = devtab[tc->type]->read(tc, text->data, beswal(hdr.text) + sizeof(Exec), off);
- if(n != beswal(hdr.text) + sizeof(Exec)) {- freesegs();
- werrstr("short read on text segment: got %d, want %lud", n, beswal(hdr.text) + sizeof(Exec));- cclose(tc);
- return -1;
- }
-
- /* Read data segment */
- off += beswal(hdr.text);
- n = devtab[tc->type]->read(tc, data->data, beswal(hdr.data), off);
- if(n != beswal(hdr.data)) {- freesegs();
- werrstr("short read on data segment: got %d, want %lud", n, beswal(hdr.data));- cclose(tc);
- return -1;
- }
-
memset(bss->data, 0, bss->size);
up->R[15] = beswal(hdr.entry);
-
+ close(fd);
initstack(argc, argv);
- //if(debug)
+ if(debug)
print("loadtext: PC=%.8ux, R1=%.8ux, R13=%.8ux\n", up->R[15], up->R[1], up->R[13]);- /* Close the channel */
- cclose(tc);
resetvfp();
-
return 0;
}
\ No newline at end of file
--- /dev/null
+++ b/libauth/Makefile
@@ -1,0 +1,18 @@
+ROOT=..
+include ../Make.config
+
+LIB=libauth.a
+OFILES=\
+ attr.$O\
+ auth_attr.$O\
+ auth_proxy.$O\
+ auth_rpc.$O\
+
+default: $(LIB)
+$(LIB): $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
+%.$O: %.c
+ $(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libauth/attr.c
@@ -1,0 +1,164 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int
+_attrfmt(Fmt *fmt)
+{+ Attr *a;
+ int first = 1;
+
+ for(a=va_arg(fmt->args, Attr*); a != nil; a=a->next){+ if(a->name == nil)
+ continue;
+ switch(a->type){+ default:
+ continue;
+ case AttrQuery:
+ fmtprint(fmt, first+" %q?", a->name);
+ break;
+ case AttrNameval:
+ case AttrDefault:
+ fmtprint(fmt, first+" %q=%q", a->name, a->val);
+ break;
+ }
+ first = 0;
+ }
+ return 0;
+}
+
+Attr*
+_copyattr(Attr *a)
+{+ Attr **la, *na;
+
+ na = nil;
+ la = &na;
+ for(; a; a=a->next){+ *la = _mkattr(a->type, a->name, a->val, nil);
+ setmalloctag(*la, getcallerpc(&a));
+ la = &(*la)->next;
+ }
+ *la = nil;
+ return na;
+}
+
+Attr*
+_delattr(Attr *a, char *name)
+{+ Attr *fa;
+ Attr **la;
+
+ for(la=&a; *la; ){+ if(strcmp((*la)->name, name) == 0){+ fa = *la;
+ *la = (*la)->next;
+ fa->next = nil;
+ _freeattr(fa);
+ }else
+ la=&(*la)->next;
+ }
+ return a;
+}
+
+Attr*
+_findattr(Attr *a, char *n)
+{+ for(; a; a=a->next)
+ if(strcmp(a->name, n) == 0 && a->type != AttrQuery)
+ return a;
+ return nil;
+}
+
+void
+_freeattr(Attr *a)
+{+ Attr *anext;
+
+ for(; a; a=anext){+ anext = a->next;
+ free(a->name);
+ free(a->val);
+ a->name = (void*)~0;
+ a->val = (void*)~0;
+ a->next = (void*)~0;
+ free(a);
+ }
+}
+
+Attr*
+_mkattr(int type, char *name, char *val, Attr *next)
+{+ Attr *a;
+
+ a = malloc(sizeof(*a));
+ if(a==nil)
+ sysfatal("_mkattr malloc: %r");+ a->type = type;
+ a->name = strdup(name);
+ a->val = strdup(val);
+ if(a->name==nil || a->val==nil)
+ sysfatal("_mkattr malloc: %r");+ a->next = next;
+ setmalloctag(a, getcallerpc(&type));
+ return a;
+}
+
+static Attr*
+cleanattr(Attr *a)
+{+ Attr *fa;
+ Attr **la;
+
+ for(la=&a; *la; ){+ if((*la)->type==AttrQuery && _findattr(a, (*la)->name)){+ fa = *la;
+ *la = (*la)->next;
+ fa->next = nil;
+ _freeattr(fa);
+ }else
+ la=&(*la)->next;
+ }
+ return a;
+}
+
+Attr*
+_parseattr(char *s)
+{+ char *p, *t, *tok[256];
+ int i, ntok;
+ Attr *a;
+
+ s = strdup(s);
+ if(s == nil)
+ sysfatal("_parseattr strdup: %r");+
+ ntok = tokenize(s, tok, nelem(tok));
+ a = nil;
+ for(i=ntok-1; i>=0; i--){+ t = tok[i];
+ if((p = strchr(t, '=')) != nil){+ *p++ = '\0';
+ a = _mkattr(AttrNameval, t, p, a);
+ }else if((p = strchr(t, '\0')-1) >= t && *p == '?'){+ *p = '\0';
+ a = _mkattr(AttrQuery, t, "", a);
+ }else{+ /* really a syntax error, but better to provide some indication */
+ a = _mkattr(AttrNameval, t, "", a);
+ }
+ setmalloctag(a, getcallerpc(&s));
+ }
+ free(s);
+ return cleanattr(a);
+}
+
+char*
+_strfindattr(Attr *a, char *n)
+{+ a = _findattr(a, n);
+ if(a == nil)
+ return nil;
+ return a->val;
+}
+
--- /dev/null
+++ b/libauth/auth_attr.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "authlocal.h"
+
+Attr*
+auth_attr(AuthRpc *rpc)
+{+ if(auth_rpc(rpc, "attr", nil, 0) != ARok)
+ return nil;
+ return _parseattr(rpc->arg);
+}
--- /dev/null
+++ b/libauth/auth_proxy.c
@@ -1,0 +1,218 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <auth.h>
+#include "authlocal.h"
+
+enum {+ ARgiveup = 100,
+};
+
+static uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{+ uint n;
+
+ if(p == nil)
+ return nil;
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ;
+ if(p+n > ep)
+ return nil;
+ *s = malloc(n+1);
+ memmove((*s), p, n);
+ (*s)[n] = '\0';
+ p += n;
+ return p;
+}
+
+static uchar*
+gcarray(uchar *p, uchar *ep, uchar **s, int *np)
+{+ uint n;
+
+ if(p == nil)
+ return nil;
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ;
+ if(p+n > ep)
+ return nil;
+ *s = malloc(n);
+ if(*s == nil)
+ return nil;
+ memmove((*s), p, n);
+ *np = n;
+ p += n;
+ return p;
+}
+
+void
+auth_freeAI(AuthInfo *ai)
+{+ if(ai == nil)
+ return;
+ free(ai->cuid);
+ free(ai->suid);
+ free(ai->cap);
+ free(ai->secret);
+ free(ai);
+}
+
+static uchar*
+convM2AI(uchar *p, int n, AuthInfo **aip)
+{+ uchar *e = p+n;
+ AuthInfo *ai;
+
+ ai = mallocz(sizeof(*ai), 1);
+ if(ai == nil)
+ return nil;
+
+ p = gstring(p, e, &ai->cuid);
+ p = gstring(p, e, &ai->suid);
+ p = gstring(p, e, &ai->cap);
+ p = gcarray(p, e, &ai->secret, &ai->nsecret);
+ if(p == nil)
+ auth_freeAI(ai);
+ else
+ *aip = ai;
+ return p;
+}
+
+AuthInfo*
+auth_getinfo(AuthRpc *rpc)
+{+ AuthInfo *a;
+
+ if(auth_rpc(rpc, "authinfo", nil, 0) != ARok)
+ return nil;
+ if(convM2AI((uchar*)rpc->arg, rpc->narg, &a) == nil){+ werrstr("bad auth info from factotum");+ return nil;
+ }
+ return a;
+}
+
+static int
+dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey)
+{+ int ret;
+
+ for(;;){+ if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey)
+ return ret;
+ if(getkey == nil)
+ return ARgiveup; /* don't know how */
+ if((*getkey)(rpc->arg) < 0)
+ return ARgiveup; /* user punted */
+ }
+}
+
+/*
+ * this just proxies what the factotum tells it to.
+ */
+AuthInfo*
+fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params)
+{+ char *buf;
+ int m, n, ret;
+ AuthInfo *a;
+ char oerr[ERRMAX];
+
+ if(rpc == nil){+ werrstr("fauth_proxy - no factotum");+ return nil;
+ }
+
+ strcpy(oerr, "UNKNOWN AUTH ERROR");
+ errstr(oerr, sizeof oerr);
+
+ if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok){+ werrstr("fauth_proxy start: %r");+ return nil;
+ }
+
+ buf = malloc(AuthRpcMax);
+ if(buf == nil)
+ return nil;
+ for(;;){+ switch(dorpc(rpc, "read", nil, 0, getkey)){+ case ARdone:
+ free(buf);
+ a = auth_getinfo(rpc);
+ /* no error, restore whatever was there */
+ errstr(oerr, sizeof oerr);
+ return a;
+ case ARok:
+ if(write(fd, rpc->arg, rpc->narg) != rpc->narg){+ werrstr("auth_proxy write fd: %r");+ goto Error;
+ }
+ break;
+ case ARphase:
+ n = 0;
+ memset(buf, 0, AuthRpcMax);
+ while((ret = dorpc(rpc, "write", buf, n, getkey)) == ARtoosmall){+ m = atoi(rpc->arg);
+ if(m <= n || m > AuthRpcMax)
+ break;
+ m = read(fd, buf + n, m - n);
+ if(m <= 0){+ if(m == 0)
+ werrstr("auth_proxy short read");+ else
+ werrstr("auth_proxy read fd: %r");+ goto Error;
+ }
+ n += m;
+ }
+ if(ret != ARok){+ werrstr("auth_proxy rpc write: %r");+ goto Error;
+ }
+ break;
+ default:
+ werrstr("auth_proxy rpc: %r");+ goto Error;
+ }
+ }
+Error:
+ free(buf);
+ return nil;
+}
+
+AuthInfo*
+auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...)
+{+ int afd;
+ char *p;
+ va_list arg;
+ AuthInfo *ai;
+ AuthRpc *rpc;
+
+ quotefmtinstall(); /* just in case */
+ va_start(arg, fmt);
+ p = vsmprint(fmt, arg);
+ va_end(arg);
+
+ ai = nil;
+ afd = open("/mnt/factotum/rpc", ORDWR);+ if(afd < 0){+ werrstr("opening /mnt/factotum/rpc: %r");+ free(p);
+ return nil;
+ }
+
+ rpc = auth_allocrpc(afd);
+ if(rpc){+ ai = fauth_proxy(fd, rpc, getkey, p);
+ auth_freerpc(rpc);
+ }
+ close(afd);
+ free(p);
+ return ai;
+}
--- /dev/null
+++ b/libauth/auth_rpc.c
@@ -1,0 +1,116 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "authlocal.h"
+
+static struct {+ char *verb;
+ int val;
+} tab[] = {+ "ok", ARok,
+ "done", ARdone,
+ "error", ARerror,
+ "needkey", ARneedkey,
+ "badkey", ARbadkey,
+ "phase", ARphase,
+ "toosmall", ARtoosmall,
+ "error", ARerror,
+};
+
+static int
+classify(char *buf, uint n, AuthRpc *rpc)
+{+ int i, len;
+
+ for(i=0; i<nelem(tab); i++){+ len = strlen(tab[i].verb);
+ if(n >= len && memcmp(buf, tab[i].verb, len) == 0 && (n==len || buf[len]==' ')){+ if(n==len){+ rpc->narg = 0;
+ rpc->arg = "";
+ }else{+ rpc->narg = n - (len+1);
+ rpc->arg = (char*)buf+len+1;
+ }
+ return tab[i].val;
+ }
+ }
+ werrstr("malformed rpc response: %s", buf);+ return ARrpcfailure;
+}
+
+AuthRpc*
+auth_allocrpc(int afd)
+{+ AuthRpc *rpc;
+
+ rpc = mallocz(sizeof(*rpc), 1);
+ if(rpc == nil)
+ return nil;
+ rpc->afd = afd;
+ return rpc;
+}
+
+void
+auth_freerpc(AuthRpc *rpc)
+{+ free(rpc);
+}
+
+uint
+auth_rpc(AuthRpc *rpc, char *verb, void *a, int na)
+{+ int l, n, type;
+ char *f[4];
+
+ l = strlen(verb);
+ if(na+l+1 > AuthRpcMax){+ werrstr("rpc too big");+ return ARtoobig;
+ }
+
+ memmove(rpc->obuf, verb, l);
+ rpc->obuf[l] = ' ';
+ memmove(rpc->obuf+l+1, a, na);
+ if((n=write(rpc->afd, rpc->obuf, l+1+na)) != l+1+na){+ if(n >= 0)
+ werrstr("auth_rpc short write");+ return ARrpcfailure;
+ }
+
+ if((n=read(rpc->afd, rpc->ibuf, AuthRpcMax)) < 0)
+ return ARrpcfailure;
+ rpc->ibuf[n] = '\0';
+
+ /*
+ * Set error string for good default behavior.
+ */
+ switch(type = classify(rpc->ibuf, n, rpc)){+ default:
+ werrstr("unknown rpc type %d (bug in auth_rpc.c)", type);+ break;
+ case ARok:
+ break;
+ case ARrpcfailure:
+ break;
+ case ARerror:
+ if(rpc->narg == 0)
+ werrstr("unspecified rpc error");+ else
+ werrstr("%s", rpc->arg);+ break;
+ case ARneedkey:
+ werrstr("needkey %s", rpc->arg);+ break;
+ case ARbadkey:
+ if(getfields(rpc->arg, f, nelem(f), 0, "\n") < 2)
+ werrstr("badkey %s", rpc->arg);+ else
+ werrstr("badkey %s", f[1]);+ break;
+ case ARphase:
+ werrstr("phase error %s", rpc->arg);+ break;
+ }
+ return type;
+}
--- /dev/null
+++ b/libauth/authlocal.h
@@ -1,0 +1,1 @@
+extern AuthInfo* _fauth_proxy(int fd, AuthRpc *rpc, AuthGetkey *getkey, char *params);
--- /dev/null
+++ b/libauthsrv/Makefile
@@ -1,0 +1,35 @@
+ROOT=..
+include ../Make.config
+LIB=libauthsrv.a
+
+OFILES=\
+ _asgetticket.$O\
+ _asrdresp.$O\
+ convA2M.$O\
+ convM2A.$O\
+ convM2PR.$O\
+ convM2T.$O\
+ convM2TR.$O\
+ convPR2M.$O\
+ convT2M.$O\
+ convTR2M.$O\
+ nvcsum.$O\
+ passtokey.$O\
+ readcons.$O\
+ _asgetpakkey.$O\
+ _asgetresp.$O\
+ _asrequest.$O\
+ authpak.$O\
+ form1.$O\
+
+
+default: $(LIB)
+$(LIB): $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
+authpak.$O: msqrt.mpc edwards.mpc ed448.mpc decaf.mpc elligator2.mpc spake2ee.mpc
+
+%.$O: %.c
+ $(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libauthsrv/_asgetpakkey.c
@@ -1,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asgetpakkey(int fd, Ticketreq *tr, Authkey *a)
+{+ uchar y[PAKYLEN];
+ PAKpriv p;
+ int type;
+
+ type = tr->type;
+ tr->type = AuthPAK;
+ if(_asrequest(fd, tr) != 0){+ tr->type = type;
+ return -1;
+ }
+ tr->type = type;
+ authpak_new(&p, a, y, 1);
+ if(write(fd, y, PAKYLEN) != PAKYLEN
+ || _asrdresp(fd, (char*)y, PAKYLEN) != PAKYLEN){+ memset(&p, 0, sizeof(p));
+ return -1;
+ }
+ return authpak_finish(&p, a, y);
+}
--- /dev/null
+++ b/libauthsrv/_asgetresp.c
@@ -1,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asgetresp(int fd, Ticket *t, Authenticator *a, Authkey *k)
+{+ char buf[MAXTICKETLEN+MAXAUTHENTLEN], err[ERRMAX];
+ int n, m;
+
+ memset(t, 0, sizeof(Ticket));
+ if(a != nil)
+ memset(a, 0, sizeof(Authenticator));
+
+ strcpy(err, "AS protocol botch");
+ errstr(err, ERRMAX);
+
+ if(_asrdresp(fd, buf, 0) < 0)
+ return -1;
+
+ for(n = 0; (m = convM2T(buf, n, t, k)) <= 0; n += m){+ m = -m;
+ if(m <= n || m > sizeof(buf))
+ return -1;
+ m -= n;
+ if(readn(fd, buf+n, m) != m)
+ return -1;
+ }
+
+ if(a != nil){+ for(n = 0; (m = convM2A(buf, n, a, t)) <= 0; n += m){+ m = -m;
+ if(m <= n || m > sizeof(buf))
+ return -1;
+ m -= n;
+ if(readn(fd, buf+n, m) != m)
+ return -1;
+ }
+ }
+
+ errstr(err, ERRMAX);
+
+ return 0;
+}
--- /dev/null
+++ b/libauthsrv/_asgetticket.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asgetticket(int fd, Ticketreq *tr, char *tbuf, int tbuflen)
+{+ char err[ERRMAX];
+ int i, n, m, r;
+
+ strcpy(err, "AS protocol botch");
+ errstr(err, ERRMAX);
+
+ if(_asrequest(fd, tr) < 0)
+ return -1;
+ if(_asrdresp(fd, tbuf, 0) < 0)
+ return -1;
+
+ r = 0;
+ for(i = 0; i<2; i++){+ for(n=0; (m = convM2T(tbuf, n, nil, nil)) <= 0; n += m){+ m = -m;
+ if(m <= n || m > tbuflen)
+ return -1;
+ m -= n;
+ if(readn(fd, tbuf+n, m) != m)
+ return -1;
+ }
+ r += n;
+ tbuf += n;
+ tbuflen -= n;
+ }
+
+ errstr(err, ERRMAX);
+
+ return r;
+}
--- /dev/null
+++ b/libauthsrv/_asrdresp.c
@@ -1,0 +1,56 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+static char *pbmsg = "AS protocol botch";
+
+int
+_asrdresp(int fd, char *buf, int len)
+{+ int n;
+ char error[64];
+
+ if(read(fd, buf, 1) != 1){+ werrstr(pbmsg);
+ return -1;
+ }
+
+ n = len;
+ switch(buf[0]){+ case AuthOK:
+ if(readn(fd, buf, len) != len){+ werrstr(pbmsg);
+ return -1;
+ }
+ break;
+ case AuthErr:
+ if(readn(fd, error, sizeof error) != sizeof error){+ werrstr(pbmsg);
+ return -1;
+ }
+ error[sizeof error-1] = '\0';
+ werrstr("remote: %s", error);+ return -1;
+ case AuthOKvar:
+ if(readn(fd, error, 5) != 5){+ werrstr(pbmsg);
+ return -1;
+ }
+ error[5] = 0;
+ n = atoi(error);
+ if(n <= 0 || n > len){+ werrstr(pbmsg);
+ return -1;
+ }
+ memset(buf, 0, len);
+ if(readn(fd, buf, n) != n){+ werrstr(pbmsg);
+ return -1;
+ }
+ break;
+ default:
+ werrstr(pbmsg);
+ return -1;
+ }
+ return n;
+}
--- /dev/null
+++ b/libauthsrv/_asrequest.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+_asrequest(int fd, Ticketreq *tr)
+{+ char trbuf[TICKREQLEN];
+ int n;
+
+ n = convTR2M(tr, trbuf, sizeof(trbuf));
+ if(write(fd, trbuf, n) != n)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+++ b/libauthsrv/authdial.c
@@ -1,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <bio.h>
+#include <ndb.h>
+
+int
+authdial(char *netroot, char *dom)
+{+ Ndbtuple *t, *nt;
+ char *p;
+ int rv;
+
+ if(dom == nil)
+ /* look for one relative to my machine */
+ return dial(netmkaddr("$auth", nil, "ticket"), nil, nil, nil);+
+ /* look up an auth server in an authentication domain */
+ p = csgetvalue(netroot, "authdom", dom, "auth", &t);
+
+ /* if that didn't work, just try the IP domain */
+ if(p == nil)
+ p = csgetvalue(netroot, "dom", dom, "auth", &t);
+
+ /*
+ * if that didn't work, try p9auth.$dom. this is very helpful if
+ * you can't edit /lib/ndb.
+ */
+ if(p == nil) {+ p = smprint("p9auth.%s", dom);+ t = ndbnew("auth", p);+ }
+ free(p);
+
+ /*
+ * allow multiple auth= attributes for backup auth servers,
+ * try each one in order.
+ */
+ rv = -1;
+ for(nt = t; nt != nil; nt = nt->entry) {+ if(strcmp(nt->attr, "auth") == 0) {+ rv = dial(netmkaddr(nt->val, nil, "ticket"), nil, nil, nil);
+ if(rv >= 0)
+ break;
+ }
+ }
+ ndbfree(t);
+
+ return rv;
+}
--- /dev/null
+++ b/libauthsrv/authpak.c
@@ -1,0 +1,214 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+#include <authsrv.h>
+
+#include "msqrt.mpc"
+#include "decaf.mpc"
+#include "edwards.mpc"
+#include "elligator2.mpc"
+#include "spake2ee.mpc"
+#include "ed448.mpc"
+
+typedef struct PAKcurve PAKcurve;
+struct PAKcurve
+{+ Lock lk;
+ mpint *P;
+ mpint *A;
+ mpint *D;
+ mpint *X;
+ mpint *Y;
+};
+
+static PAKcurve*
+authpak_curve(void)
+{+ static PAKcurve a;
+
+ lock(&a.lk);
+ if(a.P == nil){+ a.P = mpnew(0);
+ a.A = mpnew(0);
+ a.D = mpnew(0);
+ a.X = mpnew(0);
+ a.Y = mpnew(0);
+ ed448_curve(a.P, a.A, a.D, a.X, a.Y);
+ a.P = mpfield(a.P);
+ }
+ unlock(&a.lk);
+ return &a;
+}
+
+void
+authpak_hash(Authkey *k, char *u)
+{+ static char info[] = "Plan 9 AuthPAK hash";
+ uchar *bp, salt[SHA2_256dlen], h[2*PAKSLEN];
+ mpint *H, *PX,*PY,*PZ,*PT;
+ PAKcurve *c;
+
+ H = mpnew(0);
+ PX = mpnew(0);
+ PY = mpnew(0);
+ PZ = mpnew(0);
+ PT = mpnew(0);
+
+ sha2_256((uchar*)u, strlen(u), salt, nil);
+
+ hkdf_x( salt, SHA2_256dlen,
+ (uchar*)info, sizeof(info)-1,
+ k->aes, AESKEYLEN,
+ h, sizeof(h),
+ hmac_sha2_256, SHA2_256dlen);
+
+ c = authpak_curve();
+
+ betomp(h + 0*PAKSLEN, PAKSLEN, H); /* HM */
+ spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT); /* PM */
+
+ bp = k->pakhash;
+ mptober(PX, bp, PAKSLEN), bp += PAKSLEN;
+ mptober(PY, bp, PAKSLEN), bp += PAKSLEN;
+ mptober(PZ, bp, PAKSLEN), bp += PAKSLEN;
+ mptober(PT, bp, PAKSLEN), bp += PAKSLEN;
+
+ betomp(h + 1*PAKSLEN, PAKSLEN, H); /* HN */
+ spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT); /* PN */
+
+ mptober(PX, bp, PAKSLEN), bp += PAKSLEN;
+ mptober(PY, bp, PAKSLEN), bp += PAKSLEN;
+ mptober(PZ, bp, PAKSLEN), bp += PAKSLEN;
+ mptober(PT, bp, PAKSLEN);
+
+ mpfree(PX);
+ mpfree(PY);
+ mpfree(PZ);
+ mpfree(PT);
+ mpfree(H);
+}
+
+void
+authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient)
+{+ mpint *PX,*PY,*PZ,*PT, *X, *Y;
+ PAKcurve *c;
+ uchar *bp;
+
+ memset(p, 0, sizeof(PAKpriv));
+ p->isclient = isclient != 0;
+
+ X = mpnew(0);
+ Y = mpnew(0);
+
+ PX = mpnew(0);
+ PY = mpnew(0);
+ PZ = mpnew(0);
+ PT = mpnew(0);
+
+ PX->flags |= MPtimesafe;
+ PY->flags |= MPtimesafe;
+ PZ->flags |= MPtimesafe;
+ PT->flags |= MPtimesafe;
+
+ bp = k->pakhash + PAKPLEN*(p->isclient == 0);
+ betomp(bp, PAKSLEN, PX), bp += PAKSLEN;
+ betomp(bp, PAKSLEN, PY), bp += PAKSLEN;
+ betomp(bp, PAKSLEN, PZ), bp += PAKSLEN;
+ betomp(bp, PAKSLEN, PT);
+
+ c = authpak_curve();
+
+ X->flags |= MPtimesafe;
+ mpnrand(c->P, genrandom, X);
+
+ spake2ee_1(c->P,c->A,c->D, X, c->X,c->Y, PX,PY,PZ,PT, Y);
+
+ mptober(X, p->x, PAKXLEN);
+ mptober(Y, p->y, PAKYLEN);
+
+ memmove(y, p->y, PAKYLEN);
+
+ mpfree(PX);
+ mpfree(PY);
+ mpfree(PZ);
+ mpfree(PT);
+
+ mpfree(X);
+ mpfree(Y);
+}
+
+int
+authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN])
+{+ static char info[] = "Plan 9 AuthPAK key";
+ uchar *bp, z[PAKSLEN], salt[SHA2_256dlen];
+ mpint *PX,*PY,*PZ,*PT, *X, *Y, *Z, *ok;
+ DigestState *s;
+ PAKcurve *c;
+ int ret;
+
+ X = mpnew(0);
+ Y = mpnew(0);
+ Z = mpnew(0);
+ ok = mpnew(0);
+
+ PX = mpnew(0);
+ PY = mpnew(0);
+ PZ = mpnew(0);
+ PT = mpnew(0);
+
+ PX->flags |= MPtimesafe;
+ PY->flags |= MPtimesafe;
+ PZ->flags |= MPtimesafe;
+ PT->flags |= MPtimesafe;
+
+ bp = k->pakhash + PAKPLEN*(p->isclient != 0);
+ betomp(bp, PAKSLEN, PX), bp += PAKSLEN;
+ betomp(bp, PAKSLEN, PY), bp += PAKSLEN;
+ betomp(bp, PAKSLEN, PZ), bp += PAKSLEN;
+ betomp(bp, PAKSLEN, PT);
+
+ Z->flags |= MPtimesafe;
+ X->flags |= MPtimesafe;
+ betomp(p->x, PAKXLEN, X);
+
+ betomp(y, PAKYLEN, Y);
+
+ c = authpak_curve();
+ spake2ee_2(c->P,c->A,c->D, PX,PY,PZ,PT, X, Y, ok, Z);
+
+ if(mpcmp(ok, mpzero) == 0){+ ret = -1;
+ goto out;
+ }
+
+ mptober(Z, z, sizeof(z));
+
+ s = sha2_256(p->isclient ? p->y : y, PAKYLEN, nil, nil);
+ sha2_256(p->isclient ? y : p->y, PAKYLEN, salt, s);
+
+ hkdf_x( salt, SHA2_256dlen,
+ (uchar*)info, sizeof(info)-1,
+ z, sizeof(z),
+ k->pakkey, PAKKEYLEN,
+ hmac_sha2_256, SHA2_256dlen);
+
+ ret = 0;
+out:
+ memset(z, 0, sizeof(z));
+ memset(p, 0, sizeof(PAKpriv));
+
+ mpfree(PX);
+ mpfree(PY);
+ mpfree(PZ);
+ mpfree(PT);
+
+ mpfree(X);
+ mpfree(Y);
+ mpfree(Z);
+ mpfree(ok);
+
+ return ret;
+}
--- /dev/null
+++ b/libauthsrv/convA2M.c
@@ -1,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1B2M(char *ap, int n, uchar key[32]);
+
+int
+convA2M(Authenticator *f, char *ap, int n, Ticket *t)
+{+ uchar *p;
+
+ if(n < 1+CHALLEN)
+ return 0;
+
+ p = (uchar*)ap;
+ *p++ = f->num;
+ memmove(p, f->chal, CHALLEN), p += CHALLEN;
+ switch(t->form){+ case 0:
+ if(n < 1+CHALLEN+4)
+ return 0;
+
+ memset(p, 0, 4), p += 4; /* unused id field */
+ n = p - (uchar*)ap;
+ encrypt(t->key, ap, n);
+ return n;
+ case 1:
+ if(n < 12+CHALLEN+NONCELEN+16)
+ return 0;
+
+ memmove(p, f->rand, NONCELEN), p += NONCELEN;
+ return form1B2M(ap, (char*)p - ap, t->key);
+ }
+
+ return 0;
+}
--- /dev/null
+++ b/libauthsrv/convM2A.c
@@ -1,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1M2B(char *ap, int n, uchar key[32]);
+
+int
+convM2A(char *ap, int n, Authenticator *f, Ticket *t)
+{+ uchar buf[MAXAUTHENTLEN], *p;
+ int m;
+
+ memset(f, 0, sizeof(Authenticator));
+ if(t->form == 0){+ m = 1+CHALLEN+4;
+ if(n < m)
+ return -m;
+ memmove(buf, ap, m);
+ decrypt(t->key, buf, m);
+ } else {+ m = 12+CHALLEN+NONCELEN+16;
+ if(n < m)
+ return -m;
+ memmove(buf, ap, m);
+ if(form1M2B((char*)buf, m, t->key) < 0)
+ return m;
+ }
+ p = buf;
+ f->num = *p++;
+ memmove(f->chal, p, CHALLEN);
+ p += CHALLEN;
+ if(t->form == 1)
+ memmove(f->rand, p, NONCELEN);
+
+ return m;
+}
--- /dev/null
+++ b/libauthsrv/convM2PR.c
@@ -1,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1M2B(char *ap, int n, uchar key[32]);
+
+int
+convM2PR(char *ap, int n, Passwordreq *f, Ticket *t)
+{+ uchar *p, buf[MAXPASSREQLEN];
+ int m;
+
+ memset(f, 0, sizeof(Passwordreq));
+ if(t->form == 0){+ m = 1+2*PASSWDLEN+1+SECRETLEN;
+ if(n < m)
+ return -m;
+ memmove(buf, ap, m);
+ decrypt(t->key, buf, m);
+ } else {+ m = 12+2*PASSWDLEN+1+SECRETLEN+16;
+ if(n < m)
+ return -m;
+ memmove(buf, ap, m);
+ if(form1M2B((char*)buf, m, t->key) < 0)
+ return m;
+ }
+ p = buf;
+ f->num = *p++;
+ memmove(f->old, p, PASSWDLEN), p += PASSWDLEN;
+ memmove(f->new, p, PASSWDLEN), p += PASSWDLEN;
+ f->changesecret = *p++;
+ memmove(f->secret, p, SECRETLEN);
+ f->old[PASSWDLEN-1] = 0;
+ f->new[PASSWDLEN-1] = 0;
+ f->secret[SECRETLEN-1] = 0;
+
+ return m;
+}
--- /dev/null
+++ b/libauthsrv/convM2T.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1check(char *ap, int n);
+extern int form1M2B(char *ap, int n, uchar key[32]);
+
+int
+convM2T(char *ap, int n, Ticket *f, Authkey *k)
+{+ uchar buf[MAXTICKETLEN], *p;
+ int m;
+
+ if(f != nil)
+ memset(f, 0, sizeof(Ticket));
+
+ if(n < 8)
+ return -8;
+
+ if(form1check(ap, n) < 0){+ m = 1+CHALLEN+2*ANAMELEN+DESKEYLEN;
+ if(n < m)
+ return -m;
+ if(f == nil || k == nil)
+ return m;
+ f->form = 0;
+ memmove(buf, ap, m);
+ decrypt(k->des, buf, m);
+ } else {+ m = 12+CHALLEN+2*ANAMELEN+NONCELEN+16;
+ if(n < m)
+ return -m;
+ if(f == nil || k == nil)
+ return m;
+ f->form = 1;
+ memmove(buf, ap, m);
+ if(form1M2B((char*)buf, m, k->pakkey) < 0)
+ return m;
+ }
+ p = buf;
+ f->num = *p++;
+ memmove(f->chal, p, CHALLEN), p += CHALLEN;
+ memmove(f->cuid, p, ANAMELEN), p += ANAMELEN;
+ memmove(f->suid, p, ANAMELEN), p += ANAMELEN;
+ memmove(f->key, p, f->form == 0 ? DESKEYLEN : NONCELEN);
+
+ f->cuid[ANAMELEN-1] = 0;
+ f->suid[ANAMELEN-1] = 0;
+
+ return m;
+}
--- /dev/null
+++ b/libauthsrv/convM2TR.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+convM2TR(char *ap, int n, Ticketreq *f)
+{+ uchar *p;
+
+ memset(f, 0, sizeof(Ticketreq));
+ if(n < TICKREQLEN)
+ return -TICKREQLEN;
+
+ p = (uchar*)ap;
+ f->type = *p++;
+ memmove(f->authid, p, ANAMELEN), p += ANAMELEN;
+ memmove(f->authdom, p, DOMLEN), p += DOMLEN;
+ memmove(f->chal, p, CHALLEN), p += CHALLEN;
+ memmove(f->hostid, p, ANAMELEN), p += ANAMELEN;
+ memmove(f->uid, p, ANAMELEN), p += ANAMELEN;
+
+ f->authid[ANAMELEN-1] = 0;
+ f->authdom[DOMLEN-1] = 0;
+ f->hostid[ANAMELEN-1] = 0;
+ f->uid[ANAMELEN-1] = 0;
+ n = p - (uchar*)ap;
+
+ return n;
+}
--- /dev/null
+++ b/libauthsrv/convPR2M.c
@@ -1,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+extern int form1B2M(char *ap, int n, uchar key[32]);
+
+int
+convPR2M(Passwordreq *f, char *ap, int n, Ticket *t)
+{+ uchar *p;
+
+ if(n < 1+2*PASSWDLEN+1+SECRETLEN)
+ return 0;
+
+ p = (uchar*)ap;
+ *p++ = f->num;
+ memmove(p, f->old, PASSWDLEN), p += PASSWDLEN;
+ memmove(p, f->new, PASSWDLEN), p += PASSWDLEN;
+ *p++ = f->changesecret;
+ memmove(p, f->secret, SECRETLEN), p += SECRETLEN;
+ switch(t->form){+ case 0:
+ n = p - (uchar*)ap;
+ encrypt(t->key, ap, n);
+ return n;
+ case 1:
+ if(n < 12+2*PASSWDLEN+1+SECRETLEN+16)
+ return 0;
+ return form1B2M(ap, p - (uchar*)ap, t->key);
+ }
+
+ return 0;
+}
+
--- /dev/null
+++ b/libauthsrv/convT2M.c
@@ -1,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+extern int form1B2M(char *ap, int n, uchar key[32]);
+
+int
+convT2M(Ticket *f, char *ap, int n, Authkey *key)
+{+ uchar *p;
+
+ if(n < 1+CHALLEN+2*ANAMELEN)
+ return 0;
+
+ p = (uchar*)ap;
+ *p++ = f->num;
+ memmove(p, f->chal, CHALLEN), p += CHALLEN;
+ memmove(p, f->cuid, ANAMELEN), p += ANAMELEN;
+ memmove(p, f->suid, ANAMELEN), p += ANAMELEN;
+ switch(f->form){+ case 0:
+ if(n < 1+CHALLEN+2*ANAMELEN+DESKEYLEN)
+ return 0;
+
+ memmove(p, f->key, DESKEYLEN), p += DESKEYLEN;
+ n = p - (uchar*)ap;
+ encrypt(key->des, ap, n);
+ return n;
+ case 1:
+ if(n < 12+CHALLEN+2*ANAMELEN+NONCELEN+16)
+ return 0;
+
+ memmove(p, f->key, NONCELEN), p += NONCELEN;
+ return form1B2M(ap, p - (uchar*)ap, key->pakkey);
+ }
+
+ return 0;
+}
--- /dev/null
+++ b/libauthsrv/convTR2M.c
@@ -1,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+int
+convTR2M(Ticketreq *f, char *ap, int n)
+{+ uchar *p;
+
+ if(n < TICKREQLEN)
+ return 0;
+
+ p = (uchar*)ap;
+ *p++ = f->type;
+ memmove(p, f->authid, ANAMELEN), p += ANAMELEN;
+ memmove(p, f->authdom, DOMLEN), p += DOMLEN;
+ memmove(p, f->chal, CHALLEN), p += CHALLEN;
+ memmove(p, f->hostid, ANAMELEN), p += ANAMELEN;
+ memmove(p, f->uid, ANAMELEN), p += ANAMELEN;
+ n = p - (uchar*)ap;
+
+ return n;
+}
--- /dev/null
+++ b/libauthsrv/decaf.mpc
@@ -1,0 +1,130 @@
+void decaf_neg(mpint *p, mpint *n, mpint *r){+ mpint *m = mpnew(0);
+ mpmodsub(mpzero, r, p, m);
+ mpint *tmp1 = mpnew(0);
+ mpsub(p, mpone, tmp1);
+ mpright(tmp1, 1, tmp1);
+ mpsel(-mpcmp(n, tmp1) >> (sizeof(int)*8-1), m, r, r);
+ mpfree(tmp1);
+ mpfree(m);
+ }
+void decaf_encode(mpint *p, mpint *a, mpint *d, mpint *X, mpint *Y, mpint *Z, mpint *T, mpint *s){+ mpint *u = mpnew(0);
+ mpint *r = mpnew(0);
+ mpint *tmp1 = mpnew(0);
+ mpint *tmp2 = mpnew(0);
+ mpint *tmp3 = mpnew(0);
+ mpmodsub(a, d, p, tmp3);
+ mpint *tmp4 = mpnew(0);
+ mpmodadd(Z, Y, p, tmp4);
+ mpmodmul(tmp3, tmp4, p, tmp2);
+ mpfree(tmp3);
+ mpfree(tmp4);
+ tmp4 = mpnew(0);
+ mpmodsub(Z, Y, p, tmp4);
+ mpmodmul(tmp2, tmp4, p, tmp1);
+ mpfree(tmp2);
+ mpfree(tmp4);
+ misqrt(tmp1, p, r);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpmodsub(a, d, p, tmp1);
+ mpmodmul(tmp1, r, p, u);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ tmp4 = mpnew(0);
+ mpmodadd(u, u, p, tmp4); // 2*u
+ mpmodmul(tmp4, Z, p, tmp1);
+ mpfree(tmp4);
+ mpmodsub(mpzero, tmp1, p, tmp1);
+ decaf_neg(p, tmp1, r);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ tmp4 = mpnew(0);
+ tmp2 = mpnew(0);
+ tmp3 = mpnew(0);
+ mpmodmul(a, Z, p, tmp3);
+ mpmodmul(tmp3, X, p, tmp2);
+ mpfree(tmp3);
+ tmp3 = mpnew(0);
+ mpint *tmp5 = mpnew(0);
+ mpmodmul(d, Y, p, tmp5);
+ mpmodmul(tmp5, T, p, tmp3);
+ mpfree(tmp5);
+ mpmodsub(tmp2, tmp3, p, tmp2);
+ mpfree(tmp3);
+ mpmodmul(r, tmp2, p, tmp4);
+ mpfree(tmp2);
+ mpmodadd(tmp4, Y, p, tmp4);
+ mpmodmul(u, tmp4, p, tmp1);
+ mpfree(tmp4);
+ tmp4 = mpnew(0);
+ mpinvert(a, p, tmp4);
+ mpmodmul(tmp1, tmp4, p, s);
+ mpfree(tmp4);
+ mpfree(tmp1);
+ decaf_neg(p, s, s);
+ mpfree(u);
+ mpfree(r);
+ }
+void decaf_decode(mpint *p, mpint *a, mpint *d, mpint *s, mpint *ok, mpint *X, mpint *Y, mpint *Z, mpint *T){+ mpint *w = mpnew(0);
+ mpint *v = mpnew(0);
+ mpint *u = mpnew(0);
+ mpint *ss = mpnew(0);
+ mpint *tmp1 = mpnew(0);
+ mpsub(p, mpone, tmp1);
+ mpright(tmp1, 1, tmp1);
+ if(mpcmp(s, tmp1) > 0){+ mpassign(mpzero, ok);
+ }else{+ mpmodmul(s, s, p, ss);
+ mpmodmul(a, ss, p, Z);
+ mpmodadd(mpone, Z, p, Z);
+ mpmodmul(Z, Z, p, u);
+ mpint *tmp2 = mpnew(0);
+ mpint *tmp3 = mpnew(0);
+ mpint *tmp4 = mpnew(0);
+ uitomp(4UL, tmp4);
+ mpmodmul(tmp4, d, p, tmp3);
+ mpfree(tmp4);
+ mpmodmul(tmp3, ss, p, tmp2);
+ mpfree(tmp3);
+ mpmodsub(u, tmp2, p, u);
+ mpfree(tmp2);
+ mpmodmul(u, ss, p, v);
+ if(mpcmp(v, mpzero) == 0){+ mpassign(mpone, ok);
+ }else{+ msqrt(v, p, ok);
+ if(mpcmp(ok, mpzero) != 0){+ mpinvert(ok, p, v);
+ mpassign(mpone, ok);
+ }
+ }
+ if(mpcmp(ok, mpzero) != 0){+ mpint *tmp5 = mpnew(0);
+ mpmodmul(u, v, p, tmp5);
+ decaf_neg(p, tmp5, v);
+ mpfree(tmp5);
+ tmp5 = mpnew(0);
+ mpmodmul(v, s, p, tmp5);
+ mpint *tmp6 = mpnew(0);
+ mpmodsub(mptwo, Z, p, tmp6);
+ mpmodmul(tmp5, tmp6, p, w);
+ mpfree(tmp5);
+ mpfree(tmp6);
+ if(mpcmp(s, mpzero) == 0){+ mpmodadd(w, mpone, p, w);
+ }
+ mpmodadd(s, s, p, X); // 2*s
+ mpmodmul(w, Z, p, Y);
+ mpmodmul(w, X, p, T);
+ }
+ }
+ mpfree(tmp1);
+ mpfree(w);
+ mpfree(v);
+ mpfree(u);
+ mpfree(ss);
+ }
--- /dev/null
+++ b/libauthsrv/ed448.mpc
@@ -1,0 +1,8 @@
+void ed448_curve(mpint *p, mpint *a, mpint *d, mpint *x, mpint *y){+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", nil, 16, p);+ mpassign(mpone, a);
+ uitomp(39081UL, d);
+ d->sign = -1;
+ strtomp("297EA0EA2692FF1B4FAFF46098453A6A26ADF733245F065C3C59D0709CECFA96147EAAF3932D94C63D96C170033F4BA0C7F0DE840AED939F", nil, 16, x);+ uitomp(19UL, y);
+ }
--- /dev/null
+++ b/libauthsrv/edwards.mpc
@@ -1,0 +1,98 @@
+void edwards_add(mpint *p, mpint *a, mpint *d, mpint *X1, mpint *Y1, mpint *Z1, mpint *T1, mpint *X2, mpint *Y2, mpint *Z2, mpint *T2, mpint *X3, mpint *Y3, mpint *Z3, mpint *T3){+ mpint *H = mpnew(0);
+ mpint *G = mpnew(0);
+ mpint *F = mpnew(0);
+ mpint *E = mpnew(0);
+ mpint *D = mpnew(0);
+ mpint *C = mpnew(0);
+ mpint *B = mpnew(0);
+ mpint *A = mpnew(0);
+ mpmodmul(X1, X2, p, A);
+ mpmodmul(Y1, Y2, p, B);
+ mpint *tmp1 = mpnew(0);
+ mpmodmul(d, T1, p, tmp1);
+ mpmodmul(tmp1, T2, p, C);
+ mpfree(tmp1);
+ mpmodmul(Z1, Z2, p, D);
+ tmp1 = mpnew(0);
+ mpmodadd(X1, Y1, p, tmp1);
+ mpint *tmp2 = mpnew(0);
+ mpmodadd(X2, Y2, p, tmp2);
+ mpmodmul(tmp1, tmp2, p, E);
+ mpfree(tmp1);
+ mpfree(tmp2);
+ mpmodsub(E, A, p, E);
+ mpmodsub(E, B, p, E);
+ mpmodsub(D, C, p, F);
+ mpmodadd(D, C, p, G);
+ mpmodmul(a, A, p, H);
+ mpmodsub(B, H, p, H);
+ mpmodmul(E, F, p, X3);
+ mpmodmul(G, H, p, Y3);
+ mpmodmul(F, G, p, Z3);
+ mpmodmul(E, H, p, T3);
+ mpfree(H);
+ mpfree(G);
+ mpfree(F);
+ mpfree(E);
+ mpfree(D);
+ mpfree(C);
+ mpfree(B);
+ mpfree(A);
+ }
+void edwards_sel(mpint *s, mpint *X1, mpint *Y1, mpint *Z1, mpint *T1, mpint *X2, mpint *Y2, mpint *Z2, mpint *T2, mpint *X3, mpint *Y3, mpint *Z3, mpint *T3){+ mpsel(mpcmp(s, mpzero), X1, X2, X3);
+ mpsel(mpcmp(s, mpzero), Y1, Y2, Y3);
+ mpsel(mpcmp(s, mpzero), Z1, Z2, Z3);
+ mpsel(mpcmp(s, mpzero), T1, T2, T3);
+ }
+void edwards_new(mpint *x, mpint *y, mpint *z, mpint *t, mpint *X, mpint *Y, mpint *Z, mpint *T){+ mpassign(x, X);
+ mpassign(y, Y);
+ mpassign(z, Z);
+ mpassign(t, T);
+ }
+void edwards_scale(mpint *p, mpint *a, mpint *d, mpint *s, mpint *X1, mpint *Y1, mpint *Z1, mpint *T1, mpint *X3, mpint *Y3, mpint *Z3, mpint *T3){+ mpint *j = mpnew(0);
+ mpint *k = mpnew(0);
+ mpint *T4 = mpnew(0);
+ mpint *Z4 = mpnew(0);
+ mpint *Y4 = mpnew(0);
+ mpint *X4 = mpnew(0);
+ mpint *T2 = mpnew(0);
+ mpint *Z2 = mpnew(0);
+ mpint *Y2 = mpnew(0);
+ mpint *X2 = mpnew(0);
+ edwards_new(X1, Y1, Z1, T1, X2, Y2, Z2, T2);
+ edwards_new(mpzero, mpone, mpone, mpzero, X4, Y4, Z4, T4);
+ mpint *tmp1 = mpnew(0);
+ mpmod(s, mptwo, tmp1);
+ edwards_sel(tmp1, X2, Y2, Z2, T2, X4, Y4, Z4, T4, X3, Y3, Z3, T3);
+ mpfree(tmp1);
+ mpright(s, 1, k);
+ mpright(p, 1, j);
+ for(;;){+ if(mpcmp(j, mpzero) != 0){+ edwards_add(p, a, d, X2, Y2, Z2, T2, X2, Y2, Z2, T2, X2, Y2, Z2, T2);
+ edwards_add(p, a, d, X2, Y2, Z2, T2, X3, Y3, Z3, T3, X4, Y4, Z4, T4);
+ mpint *tmp2 = mpnew(0);
+ mpmod(k, mptwo, tmp2);
+ edwards_sel(tmp2, X4, Y4, Z4, T4, X3, Y3, Z3, T3, X3, Y3, Z3, T3);
+ mpfree(tmp2);
+ mpright(k, 1, k);
+ mpright(j, 1, j);
+ }else{+ break;
+ }
+ }
+ mpfree(j);
+ mpfree(k);
+ mpfree(T4);
+ mpfree(Z4);
+ mpfree(Y4);
+ mpfree(X4);
+ mpfree(T2);
+ mpfree(Z2);
+ mpfree(Y2);
+ mpfree(X2);
+ }
--- /dev/null
+++ b/libauthsrv/elligator2.mpc
@@ -1,0 +1,129 @@
+void elligator2(mpint *p, mpint *a, mpint *d, mpint *n, mpint *r0, mpint *X, mpint *Y, mpint *Z, mpint *T){+ mpint *t = mpnew(0);
+ mpint *s = mpnew(0);
+ mpint *e = mpnew(0);
+ mpint *c = mpnew(0);
+ mpint *ND = mpnew(0);
+ mpint *N = mpnew(0);
+ mpint *D = mpnew(0);
+ mpint *r = mpnew(0);
+ mpint *tmp1 = mpnew(0);
+ mpmodmul(n, r0, p, tmp1);
+ mpmodmul(tmp1, r0, p, r);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpmodmul(d, r, p, tmp1);
+ mpmodadd(tmp1, a, p, tmp1);
+ mpmodsub(tmp1, d, p, tmp1);
+ mpint *tmp2 = mpnew(0);
+ mpmodmul(d, r, p, tmp2);
+ mpint *tmp3 = mpnew(0);
+ mpmodmul(a, r, p, tmp3);
+ mpmodsub(tmp2, tmp3, p, tmp2);
+ mpfree(tmp3);
+ mpmodsub(tmp2, d, p, tmp2);
+ mpmodmul(tmp1, tmp2, p, D);
+ mpfree(tmp1);
+ mpfree(tmp2);
+ tmp2 = mpnew(0);
+ mpmodadd(r, mpone, p, tmp2);
+ tmp1 = mpnew(0);
+ mpmodadd(d, d, p, tmp1); // 2*d
+ mpmodsub(a, tmp1, p, tmp1);
+ mpmodmul(tmp2, tmp1, p, N);
+ mpfree(tmp2);
+ mpfree(tmp1);
+ mpmodmul(N, D, p, ND);
+ if(mpcmp(ND, mpzero) == 0){+ mpassign(mpone, c);
+ mpassign(mpzero, e);
+ }else{+ msqrt(ND, p, e);
+ if(mpcmp(e, mpzero) != 0){+ mpassign(mpone, c);
+ mpinvert(e, p, e);
+ }else{+ mpmodsub(mpzero, mpone, p, c);
+ mpint *tmp4 = mpnew(0);
+ mpmodmul(n, r0, p, tmp4);
+ mpint *tmp5 = mpnew(0);
+ mpint *tmp6 = mpnew(0);
+ mpmodmul(n, ND, p, tmp6);
+ misqrt(tmp6, p, tmp5);
+ mpfree(tmp6);
+ mpmodmul(tmp4, tmp5, p, e);
+ mpfree(tmp4);
+ mpfree(tmp5);
+ }
+ }
+ tmp1 = mpnew(0);
+ mpmodmul(c, N, p, tmp1);
+ mpmodmul(tmp1, e, p, s);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ tmp2 = mpnew(0);
+ mpmodmul(c, N, p, tmp2);
+ tmp3 = mpnew(0);
+ mpmodsub(r, mpone, p, tmp3);
+ mpmodmul(tmp2, tmp3, p, tmp1);
+ mpfree(tmp2);
+ mpfree(tmp3);
+ tmp3 = mpnew(0);
+ tmp2 = mpnew(0);
+ mpmodadd(d, d, p, tmp2); // 2*d
+ mpmodsub(a, tmp2, p, tmp2);
+ mpmodmul(tmp2, e, p, tmp3);
+ mpfree(tmp2);
+ mpmodmul(tmp3, tmp3, p, tmp3);
+ mpmodmul(tmp1, tmp3, p, t);
+ mpfree(tmp1);
+ mpfree(tmp3);
+ mpmodsub(mpzero, t, p, t);
+ mpmodsub(t, mpone, p, t);
+ tmp3 = mpnew(0);
+ mpmodadd(s, s, p, tmp3); // 2*s
+ mpmodmul(tmp3, t, p, X);
+ mpfree(tmp3);
+ tmp3 = mpnew(0);
+ tmp1 = mpnew(0);
+ mpmodmul(a, s, p, tmp1);
+ mpmodmul(tmp1, s, p, tmp3);
+ mpfree(tmp1);
+ mpmodsub(mpone, tmp3, p, tmp3);
+ tmp1 = mpnew(0);
+ tmp2 = mpnew(0);
+ mpmodmul(a, s, p, tmp2);
+ mpmodmul(tmp2, s, p, tmp1);
+ mpfree(tmp2);
+ mpmodadd(mpone, tmp1, p, tmp1);
+ mpmodmul(tmp3, tmp1, p, Y);
+ mpfree(tmp3);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ tmp3 = mpnew(0);
+ mpmodmul(a, s, p, tmp3);
+ mpmodmul(tmp3, s, p, tmp1);
+ mpfree(tmp3);
+ mpmodadd(mpone, tmp1, p, tmp1);
+ mpmodmul(tmp1, t, p, Z);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpmodadd(s, s, p, tmp1); // 2*s
+ tmp3 = mpnew(0);
+ tmp2 = mpnew(0);
+ mpmodmul(a, s, p, tmp2);
+ mpmodmul(tmp2, s, p, tmp3);
+ mpfree(tmp2);
+ mpmodsub(mpone, tmp3, p, tmp3);
+ mpmodmul(tmp1, tmp3, p, T);
+ mpfree(tmp1);
+ mpfree(tmp3);
+ mpfree(t);
+ mpfree(s);
+ mpfree(e);
+ mpfree(c);
+ mpfree(ND);
+ mpfree(N);
+ mpfree(D);
+ mpfree(r);
+ }
--- /dev/null
+++ b/libauthsrv/form1.c
@@ -1,0 +1,90 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+/*
+ * new ticket format: the reply protector/type is replaced by a
+ * 8 byte signature and a 4 byte counter forming the 12 byte
+ * nonce for chacha20/poly1305 encryption. a 16 byte poly1305
+ * authentication tag is appended for message authentication.
+ * the counter is needed for the AuthPass message which uses
+ * the same key for several messages.
+ */
+
+static struct {+ char num;
+ char sig[8];
+} form1sig[] = {+ AuthPass, "form1 PR", /* password change request encrypted with ticket key */
+ AuthTs, "form1 Ts", /* ticket encrypted with server's key */
+ AuthTc, "form1 Tc", /* ticket encrypted with client's key */
+ AuthAs, "form1 As", /* server generated authenticator */
+ AuthAc, "form1 Ac", /* client generated authenticator */
+ AuthTp, "form1 Tp", /* ticket encrypted with client's key for password change */
+ AuthHr, "form1 Hr", /* http reply */
+};
+
+int
+form1check(char *ap, int n)
+{+ if(n < 8)
+ return -1;
+
+ for(n=0; n<nelem(form1sig); n++)
+ if(memcmp(form1sig[n].sig, ap, 8) == 0)
+ return form1sig[n].num;
+
+ return -1;
+}
+
+int
+form1B2M(char *ap, int n, uchar key[32])
+{+ static u32int counter;
+ Chachastate s;
+ uchar *p;
+ int i;
+
+ for(i=nelem(form1sig)-1; i>=0; i--)
+ if(form1sig[i].num == *ap)
+ break;
+ if(i < 0)
+ abort();
+
+ p = (uchar*)ap + 12;
+ memmove(p, ap+1, --n);
+
+ /* nonce[12] = sig[8] | counter[4] */
+ memmove(ap, form1sig[i].sig, 8);
+ i = counter++;
+ ap[8] = i, ap[9] = i>>8, ap[10] = i>>16, ap[11] = i>>24;
+
+ setupChachastate(&s, key, 32, (uchar*)ap, 12, 20);
+ ccpoly_encrypt(p, n, nil, 0, p+n, &s);
+ return 12+16 + n;
+}
+
+int
+form1M2B(char *ap, int n, uchar key[32])
+{+ Chachastate s;
+ uchar *p;
+ int num;
+
+ num = form1check(ap, n);
+ if(num < 0)
+ return -1;
+ n -= 12+16;
+ if(n <= 0)
+ return -1;
+
+ p = (uchar*)ap + 12;
+ setupChachastate(&s, key, 32, (uchar*)ap, 12, 20);
+ if(ccpoly_decrypt(p, n, nil, 0, p+n, &s))
+ return -1;
+
+ memmove(ap+1, p, n);
+ ap[0] = num;
+ return n+1;
+}
--- /dev/null
+++ b/libauthsrv/msqrt.mpc
@@ -1,0 +1,149 @@
+void legendresymbol(mpint *a, mpint *p, mpint *r){+ mpint *pm1 = mpnew(0);
+ mpsub(p, mpone, pm1);
+ mpright(pm1, 1, r);
+ mpexp(a, r, p, r);
+ if(mpcmp(r, pm1) == 0){+ mpassign(mpone, r);
+ r->sign = -1;
+ }
+ mpfree(pm1);
+ }
+void msqrt(mpint *a, mpint *p, mpint *r){+ mpint *gs = mpnew(0);
+ mpint *m = mpnew(0);
+ mpint *t = mpnew(0);
+ mpint *g = mpnew(0);
+ mpint *b = mpnew(0);
+ mpint *x = mpnew(0);
+ mpint *n = mpnew(0);
+ mpint *s = mpnew(0);
+ mpint *e = mpnew(0);
+ mpint *tmp1 = mpnew(0);
+ legendresymbol(a, p, tmp1);
+ if(mpcmp(tmp1, mpone) != 0){+ mpassign(mpzero, r);
+ }else{+ if(mpcmp(a, mpzero) == 0){+ mpassign(mpzero, r);
+ }else{+ if(mpcmp(p, mptwo) == 0){+ mpassign(a, r);
+ }else{+ mpint *tmp2 = mpnew(0);
+ uitomp(4UL, tmp2);
+ mpmod(p, tmp2, tmp2);
+ mpint *tmp3 = mpnew(0);
+ uitomp(3UL, tmp3);
+ if(mpcmp(tmp2, tmp3) == 0){+ mpadd(p, mpone, e);
+ mpright(e, 2, e);
+ mpexp(a, e, p, r);
+ }else{+ mpsub(p, mpone, s);
+ mpassign(mpzero, e);
+ for(;;){+ mpint *tmp4 = mpnew(0);
+ mpmod(s, mptwo, tmp4);
+ if(mpcmp(tmp4, mpzero) == 0){+ mpright(s, 1, s);
+ mpadd(e, mpone, e);
+ }else{+ mpfree(tmp4);
+ break;
+ }
+ mpfree(tmp4);
+ }
+ mpassign(mptwo, n);
+ for(;;){+ mpint *tmp5 = mpnew(0);
+ legendresymbol(n, p, tmp5);
+ mpint *tmp6 = mpnew(0);
+ mpassign(mpone, tmp6);
+ tmp6->sign = -1;
+ if(mpcmp(tmp5, tmp6) != 0){+ mpadd(n, mpone, n);
+ }else{+ mpfree(tmp6);
+ mpfree(tmp5);
+ break;
+ }
+ mpfree(tmp5);
+ mpfree(tmp6);
+ }
+ mpmodadd(s, mpone, p, x);
+ mpright(x, 1, x);
+ mpexp(a, x, p, x);
+ mpexp(a, s, p, b);
+ mpexp(n, s, p, g);
+ for(;;){+ if(0 == 0){+ mpassign(b, t);
+ mpassign(mpzero, m);
+ for(;;){+ if(mpcmp(m, e) < 0){+ if(mpcmp(t, mpone) == 0){+ break;
+ }
+ mpmul(t, t, t);
+ mpmod(t, p, t);
+ mpadd(m, mpone, m);
+ }else{+ break;
+ }
+ }
+ if(mpcmp(m, mpzero) == 0){+ mpassign(x, r);
+ break;
+ }
+ mpsub(e, m, t);
+ mpsub(t, mpone, t);
+ mpexp(mptwo, t, nil, t);
+ mpexp(g, t, p, gs);
+ mpmodmul(gs, gs, p, g);
+ mpmodmul(x, gs, p, x);
+ mpmodmul(b, g, p, b);
+ mpassign(m, e);
+ }else{+ break;
+ }
+ }
+ }
+ mpfree(tmp2);
+ mpfree(tmp3);
+ }
+ }
+ }
+ mpfree(tmp1);
+ mpfree(gs);
+ mpfree(m);
+ mpfree(t);
+ mpfree(g);
+ mpfree(b);
+ mpfree(x);
+ mpfree(n);
+ mpfree(s);
+ mpfree(e);
+ }
+void misqrt(mpint *a, mpint *p, mpint *r){+ mpint *e = mpnew(0);
+ mpint *tmp1 = mpnew(0);
+ uitomp(4UL, tmp1);
+ mpmod(p, tmp1, tmp1);
+ mpint *tmp2 = mpnew(0);
+ uitomp(3UL, tmp2);
+ if(mpcmp(tmp1, tmp2) == 0){+ uitomp(3UL, e);
+ mpsub(p, e, e);
+ mpright(e, 2, e);
+ mpexp(a, e, p, r);
+ }else{+ msqrt(a, p, r);
+ if(mpcmp(r, mpzero) != 0){+ mpinvert(r, p, r);
+ }
+ }
+ mpfree(tmp1);
+ mpfree(tmp2);
+ mpfree(e);
+ }
--- /dev/null
+++ b/libauthsrv/nvcsum.c
@@ -1,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+uchar
+nvcsum(void *vmem, int n)
+{+ uchar *mem, sum;
+ int i;
+
+ sum = 9;
+ mem = vmem;
+ for(i = 0; i < n; i++)
+ sum += mem[i];
+ return sum;
+}
--- /dev/null
+++ b/libauthsrv/passtokey.c
@@ -1,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+void
+passtodeskey(char key[DESKEYLEN], char *p)
+{+ uchar buf[PASSWDLEN], *t;
+ int i, n;
+
+ memset(buf, ' ', 8);
+ n = strlen(p);
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ memmove(buf, p, n);
+ buf[n] = 0;
+ memset(key, 0, DESKEYLEN);
+ t = buf;
+ for(;;){+ for(i = 0; i < DESKEYLEN; i++)
+ key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1)));
+ if(n <= 8)
+ return;
+ n -= 8;
+ t += 8;
+ if(n < 8){+ t -= 8 - n;
+ n = 8;
+ }
+ encrypt(key, t, 8);
+ }
+}
+
+void
+passtoaeskey(uchar key[AESKEYLEN], char *p)
+{+ static char salt[] = "Plan 9 key derivation";
+ pbkdf2_x((uchar*)p, strlen(p), (uchar*)salt, sizeof(salt)-1, 9001, key, AESKEYLEN, hmac_sha1, SHA1dlen);
+}
+
+void
+passtokey(Authkey *key, char *pw)
+{+ memset(key, 0, sizeof(Authkey));
+ passtodeskey(key->des, pw);
+ passtoaeskey(key->aes, pw);
+}
--- /dev/null
+++ b/libauthsrv/readcons.c
@@ -1,0 +1,82 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * prompt for a string with a possible default response
+ */
+char*
+readcons(char *prompt, char *def, int raw)
+{+ int fdin, fdout, ctl, n;
+ char *s, *p;
+
+ s = p = nil;
+ fdout = ctl = -1;
+
+ if((fdin = open("/dev/cons", OREAD)) < 0)+ goto Out;
+ if((fdout = open("/dev/cons", OWRITE)) < 0)+ goto Out;
+
+ if(raw){+ if((ctl = open("/dev/consctl", OWRITE)) < 0)+ goto Out;
+ write(ctl, "rawon", 5);
+ }
+
+ if(def != nil)
+ fprint(fdout, "%s[%s]: ", prompt, def);
+ else
+ fprint(fdout, "%s: ", prompt);
+
+ for(;;){+ n = p - s;
+ if((n % 32) == 0){+ if((p = realloc(s, n+32)) == nil)
+ break;
+ s = p, p += n;
+ }
+
+ if(read(fdin, p, 1) <= 0 || *p == 0x7f)
+ break;
+
+ if(*p == '\n' || *p == '\r'){+ if(p == s && def != nil){+ free(s);
+ s = strdup(def);
+ } else
+ *p = 0;
+ if(raw)
+ write(fdout, "\n", 1);
+ goto Out;
+ } else if(*p == '\b') {+ while(p > s && (p[-1] & 0xc0) == 0x80)
+ *p-- = 0;
+ if(p > s)
+ *p-- = 0;
+ } else if(*p == 0x15) { /* ^U: line kill */+ if(def != nil)
+ fprint(fdout, "\n%s[%s]: ", prompt, def);
+ else
+ fprint(fdout, "\n%s: ", prompt);
+ while(p > s)
+ *p-- = 0;
+ } else if((*p & 0xff) >= ' ')
+ p++;
+ }
+ free(s);
+ s = nil;
+ if(raw)
+ write(fdout, "\n", 1);
+Out:
+ if(ctl >= 0){+ write(ctl, "rawoff", 6);
+ close(ctl);
+ }
+ if(fdin >= 0)
+ close(fdin);
+ if(fdout >= 0)
+ close(fdout);
+
+ return s;
+}
--- /dev/null
+++ b/libauthsrv/readnvram.c
@@ -1,0 +1,419 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+
+static long finddosfile(int, char*);
+
+static int
+check(void *x, int len, uchar sum, char *msg)
+{+ if(nvcsum(x, len) == sum)
+ return 0;
+ memset(x, 0, len);
+ fprint(2, "%s\n", msg);
+ return 1;
+}
+
+/*
+ * get key info out of nvram. since there isn't room in the PC's nvram use
+ * a disk partition there.
+ */
+static struct {+ char *cputype;
+ char *file;
+ int off;
+ int len;
+} nvtab[] = {+ "sparc", "#r/nvram", 1024+850, sizeof(Nvrsafe),
+ "pc", "#S/sdC0/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sdC0/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sdC1/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sdC1/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sdD0/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sdD0/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sdE0/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sdE0/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sdF0/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sdF0/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sd00/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sd00/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sd01/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sd01/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#S/sd10/nvram", 0, sizeof(Nvrsafe),
+ "pc", "#S/sd10/9fat", -1, sizeof(Nvrsafe),
+ "pc", "#f/fd0disk", -1, 512, /* 512: #f requires whole sector reads */
+ "pc", "#f/fd1disk", -1, 512,
+ "mips", "#r/nvram", 1024+900, sizeof(Nvrsafe),
+ "power", "#F/flash/flash0", 0x440000, sizeof(Nvrsafe),
+ "power", "#F/flash/flash", 0x440000, sizeof(Nvrsafe),
+ "power", "#r/nvram", 4352, sizeof(Nvrsafe), /* OK for MTX-604e */
+ "power", "/nvram", 0, sizeof(Nvrsafe), /* OK for Ucu */
+ "arm", "#F/flash/flash0", 0x100000, sizeof(Nvrsafe),
+ "arm", "#F/flash/flash", 0x100000, sizeof(Nvrsafe),
+ "debug", "/tmp/nvram", 0, sizeof(Nvrsafe),
+};
+
+typedef struct {+ int fd;
+ int safelen;
+ vlong safeoff;
+} Nvrwhere;
+
+static char *nvrfile = nil, *cputype = nil;
+
+/* returns with *locp filled in and locp->fd open, if possible */
+static void
+findnvram(Nvrwhere *locp)
+{+ char *nvrlen, *nvroff, *v[2];
+ int fd, i, safelen;
+ vlong safeoff;
+
+ if (nvrfile == nil)
+ nvrfile = getenv("nvram");+ if (cputype == nil)
+ cputype = getenv("cputype");+ if(cputype == nil)
+ cputype = strdup("mips");+ if(strcmp(cputype, "386")==0 || strcmp(cputype, "amd64")==0 || strcmp(cputype, "alpha")==0) {+ free(cputype);
+ cputype = strdup("pc");+ }
+
+ fd = -1;
+ safeoff = -1;
+ safelen = -1;
+ if(nvrfile != nil && *nvrfile != '\0'){+ /* accept device and device!file */
+ i = gettokens(nvrfile, v, nelem(v), "!");
+ if (i < 1) {+ i = 1;
+ v[0] = "";
+ v[1] = nil;
+ }
+ fd = open(v[0], ORDWR);
+ if (fd < 0)
+ fd = open(v[0], OREAD);
+ safelen = sizeof(Nvrsafe);
+ if(strstr(v[0], "/9fat") == nil)
+ safeoff = 0;
+ nvrlen = getenv("nvrlen");+ if(nvrlen != nil)
+ safelen = strtol(nvrlen, 0, 0);
+ nvroff = getenv("nvroff");+ if(nvroff != nil)
+ if(strcmp(nvroff, "dos") == 0)
+ safeoff = -1;
+ else
+ safeoff = strtoll(nvroff, 0, 0);
+ if(safeoff < 0 && fd >= 0){+ safelen = 512;
+ safeoff = finddosfile(fd, i == 2? v[1]: "plan9.nvr");
+ if(safeoff < 0){ /* didn't find plan9.nvr? */+ close(fd);
+ fd = -1;
+ }
+ }
+ free(nvroff);
+ free(nvrlen);
+ }else
+ for(i=0; i<nelem(nvtab); i++){+ if(strcmp(cputype, nvtab[i].cputype) != 0)
+ continue;
+ if((fd = open(nvtab[i].file, ORDWR)) < 0)
+ continue;
+ safeoff = nvtab[i].off;
+ safelen = nvtab[i].len;
+ if(safeoff == -1){+ safeoff = finddosfile(fd, "plan9.nvr");
+ if(safeoff < 0){ /* didn't find plan9.nvr? */+ close(fd);
+ fd = -1;
+ continue;
+ }
+ }
+ break;
+ }
+ locp->fd = fd;
+ locp->safelen = safelen;
+ locp->safeoff = safeoff;
+}
+
+static int
+ask(char *prompt, char *buf, int len, int raw)
+{+ char *s;
+ int n;
+
+ memset(buf, 0, len);
+ for(;;){+ if((s = readcons(prompt, nil, raw)) == nil)
+ return -1;
+ if((n = strlen(s)) >= len)
+ fprint(2, "%s longer than %d characters; try again\n", prompt, len-1);
+ else {+ memmove(buf, s, n);
+ memset(s, 0, n);
+ free(s);
+ return 0;
+ }
+ memset(s, 0, n);
+ free(s);
+ }
+}
+
+/*
+ * get key info out of nvram. since there isn't room in the PC's nvram use
+ * a disk partition there.
+ */
+int
+readnvram(Nvrsafe *safep, int flag)
+{+ int err;
+ char buf[512]; /* 512 for floppy i/o */
+ Nvrsafe *safe;
+ Nvrwhere loc;
+
+ err = 0;
+ safe = (Nvrsafe*)buf;
+ memset(&loc, 0, sizeof loc);
+ findnvram(&loc);
+ if (loc.safelen < 0)
+ loc.safelen = sizeof *safe;
+ else if (loc.safelen > sizeof buf)
+ loc.safelen = sizeof buf;
+ if (loc.safeoff < 0) {+ fprint(2, "readnvram: couldn't find nvram\n");
+ if(!(flag&NVwritemem))
+ memset(safep, 0, sizeof(*safep));
+ safe = safep;
+ /*
+ * allow user to type the data for authentication,
+ * even if there's no nvram to store it in.
+ */
+ }
+
+ if(flag&NVwritemem)
+ safe = safep;
+ else {+ memset(safep, 0, sizeof(*safep));
+ if(loc.fd < 0
+ || seek(loc.fd, loc.safeoff, 0) < 0
+ || read(loc.fd, buf, loc.safelen) != loc.safelen){+ err = 1;
+ if(flag&(NVwrite|NVwriteonerr))
+ if(loc.fd < 0)
+ fprint(2, "can't open %s: %r\n", nvrfile);
+ else if (seek(loc.fd, loc.safeoff, 0) < 0)
+ fprint(2, "can't seek %s to %lld: %r\n",
+ nvrfile, loc.safeoff);
+ else
+ fprint(2, "can't read %d bytes from %s: %r\n",
+ loc.safelen, nvrfile);
+ /* start from scratch */
+ memset(safep, 0, sizeof(*safep));
+ safe = safep;
+ }else{+ *safep = *safe; /* overwrite arg with data read */
+ safe = safep;
+
+ /* verify data read */
+ err |= check(safe->machkey, DESKEYLEN, safe->machsum,
+ "bad nvram des key");
+ err |= check(safe->authid, ANAMELEN, safe->authidsum,
+ "bad authentication id");
+ err |= check(safe->authdom, DOMLEN, safe->authdomsum,
+ "bad authentication domain");
+ if(0){+ err |= check(safe->config, CONFIGLEN, safe->configsum,
+ "bad secstore key");
+ err |= check(safe->aesmachkey, AESKEYLEN, safe->aesmachsum,
+ "bad nvram aes key");
+ } else {+ if(nvcsum(safe->config, CONFIGLEN) != safe->configsum)
+ memset(safe->config, 0, CONFIGLEN);
+ if(nvcsum(safe->aesmachkey, AESKEYLEN) != safe->aesmachsum)
+ memset(safe->aesmachkey, 0, AESKEYLEN);
+ }
+ if(err == 0)
+ if(safe->authid[0]==0 || safe->authdom[0]==0){+ fprint(2, "empty nvram authid or authdom\n");
+ err = 1;
+ }
+ }
+ }
+
+ if((flag&(NVwrite|NVwritemem)) || (err && (flag&NVwriteonerr))){+ if (!(flag&NVwritemem)) {+ char pass[PASSWDLEN];
+ Authkey k;
+
+ if(ask("authid", safe->authid, sizeof safe->authid, 0))+ goto Out;
+ if(ask("authdom", safe->authdom, sizeof safe->authdom, 0))+ goto Out;
+ if(ask("secstore key", safe->config, sizeof safe->config, 1))+ goto Out;
+ if(ask("password", pass, sizeof pass, 1))+ goto Out;
+ passtokey(&k, pass);
+ memset(pass, 0, sizeof pass);
+ memmove(safe->machkey, k.des, DESKEYLEN);
+ memmove(safe->aesmachkey, k.aes, AESKEYLEN);
+ memset(&k, 0, sizeof k);
+ }
+
+ safe->machsum = nvcsum(safe->machkey, DESKEYLEN);
+ // safe->authsum = nvcsum(safe->authkey, DESKEYLEN);
+ safe->configsum = nvcsum(safe->config, CONFIGLEN);
+ safe->authidsum = nvcsum(safe->authid, sizeof safe->authid);
+ safe->authdomsum = nvcsum(safe->authdom, sizeof safe->authdom);
+ safe->aesmachsum = nvcsum(safe->aesmachkey, AESKEYLEN);
+
+ *(Nvrsafe*)buf = *safe;
+ if(loc.fd < 0
+ || seek(loc.fd, loc.safeoff, 0) < 0
+ || write(loc.fd, buf, loc.safelen) != loc.safelen){+ fprint(2, "can't write key to nvram: %r\n");
+ err = 1;
+ }else
+ err = 0;
+ }
+Out:
+ if (loc.fd >= 0)
+ close(loc.fd);
+ return err? -1: 0;
+}
+
+typedef struct Dosboot Dosboot;
+struct Dosboot{+ uchar magic[3]; /* really an xx86 JMP instruction */
+ uchar version[8];
+ uchar sectsize[2];
+ uchar clustsize;
+ uchar nresrv[2];
+ uchar nfats;
+ uchar rootsize[2];
+ uchar volsize[2];
+ uchar mediadesc;
+ uchar fatsize[2];
+ uchar trksize[2];
+ uchar nheads[2];
+ uchar nhidden[4];
+ uchar bigvolsize[4];
+ uchar driveno;
+ uchar reserved0;
+ uchar bootsig;
+ uchar volid[4];
+ uchar label[11];
+ uchar type[8];
+};
+#define GETSHORT(p) (((p)[1]<<8) | (p)[0])
+#define GETLONG(p) ((GETSHORT((p)+2) << 16) | GETSHORT((p)))
+
+typedef struct Dosdir Dosdir;
+struct Dosdir
+{+ char name[8];
+ char ext[3];
+ uchar attr;
+ uchar reserved[10];
+ uchar time[2];
+ uchar date[2];
+ uchar start[2];
+ uchar length[4];
+};
+
+static char*
+dosparse(char *from, char *to, int len)
+{+ char c;
+
+ memset(to, ' ', len);
+ if(from == 0)
+ return 0;
+ while(len-- > 0){+ c = *from++;
+ if(c == '.')
+ return from;
+ if(c == 0)
+ break;
+ if(c >= 'a' && c <= 'z')
+ *to++ = c + 'A' - 'a';
+ else
+ *to++ = c;
+ }
+ return 0;
+}
+
+/*
+ * return offset of first file block
+ *
+ * This is a very simplistic dos file system. It only
+ * works on floppies, only looks in the root, and only
+ * returns a pointer to the first block of a file.
+ *
+ * This exists for cpu servers that have no hard disk
+ * or nvram to store the key on.
+ *
+ * Please don't make this any smarter: it stays resident
+ * and I'ld prefer not to waste the space on something that
+ * runs only at boottime -- presotto.
+ */
+static long
+finddosfile(int fd, char *file)
+{+ uchar secbuf[512];
+ char name[8];
+ char ext[3];
+ Dosboot *b;
+ Dosdir *root, *dp;
+ int nroot, sectsize, rootoff, rootsects, n;
+
+ /* dos'ize file name */
+ file = dosparse(file, name, 8);
+ dosparse(file, ext, 3);
+
+ /* read boot block, check for sanity */
+ b = (Dosboot*)secbuf;
+ if(read(fd, secbuf, sizeof(secbuf)) != sizeof(secbuf))
+ return -1;
+ if(b->magic[0] != 0xEB || b->magic[1] != 0x3C || b->magic[2] != 0x90)
+ return -1;
+ sectsize = GETSHORT(b->sectsize);
+ if(sectsize != 512)
+ return -1;
+ rootoff = (GETSHORT(b->nresrv) + b->nfats*GETSHORT(b->fatsize)) * sectsize;
+ if(seek(fd, rootoff, 0) < 0)
+ return -1;
+ nroot = GETSHORT(b->rootsize);
+ rootsects = (nroot*sizeof(Dosdir)+sectsize-1)/sectsize;
+ if(rootsects <= 0 || rootsects > 64)
+ return -1;
+
+ /*
+ * read root. it is contiguous to make stuff like
+ * this easier
+ */
+ root = malloc(rootsects*sectsize);
+ if(read(fd, root, rootsects*sectsize) != rootsects*sectsize)
+ return -1;
+ n = -1;
+ for(dp = root; dp < &root[nroot]; dp++)
+ if(memcmp(name, dp->name, 8) == 0 && memcmp(ext, dp->ext, 3) == 0){+ n = GETSHORT(dp->start);
+ break;
+ }
+ free(root);
+
+ if(n < 0)
+ return -1;
+
+ /*
+ * dp->start is in cluster units, not sectors. The first
+ * cluster is cluster 2 which starts immediately after the
+ * root directory
+ */
+ return rootoff + rootsects*sectsize + (n-2)*sectsize*b->clustsize;
+}
+
--- /dev/null
+++ b/libauthsrv/spake2ee.mpc
@@ -1,0 +1,63 @@
+void spake2ee_h2P(mpint *p, mpint *a, mpint *d, mpint *h, mpint *PX, mpint *PY, mpint *PZ, mpint *PT){+ mpint *n = mpnew(0);
+ mpassign(mptwo, n);
+ for(;;){+ mpint *tmp1 = mpnew(0);
+ legendresymbol(n, p, tmp1);
+ mpint *tmp2 = mpnew(0);
+ mpassign(mpone, tmp2);
+ tmp2->sign = -1;
+ if(mpcmp(tmp1, tmp2) != 0){+ mpadd(n, mpone, n);
+ }else{+ mpfree(tmp2);
+ mpfree(tmp1);
+ break;
+ }
+ mpfree(tmp1);
+ mpfree(tmp2);
+ }
+ mpint *tmp3 = mpnew(0);
+ mpmod(h, p, tmp3);
+ elligator2(p, a, d, n, tmp3, PX, PY, PZ, PT);
+ mpfree(tmp3);
+ mpfree(n);
+ }
+void spake2ee_1(mpint *p, mpint *a, mpint *d, mpint *x, mpint *GX, mpint *GY, mpint *PX, mpint *PY, mpint *PZ, mpint *PT, mpint *y){+ mpint *T = mpnew(0);
+ mpint *Z = mpnew(0);
+ mpint *Y = mpnew(0);
+ mpint *X = mpnew(0);
+ mpint *tmp1 = mpnew(0);
+ mpmodmul(GX, GY, p, tmp1);
+ edwards_scale(p, a, d, x, GX, GY, mpone, tmp1, X, Y, Z, T);
+ mpfree(tmp1);
+ edwards_add(p, a, d, X, Y, Z, T, PX, PY, PZ, PT, X, Y, Z, T);
+ decaf_encode(p, a, d, X, Y, Z, T, y);
+ mpfree(T);
+ mpfree(Z);
+ mpfree(Y);
+ mpfree(X);
+ }
+void spake2ee_2(mpint *p, mpint *a, mpint *d, mpint *PX, mpint *PY, mpint *PZ, mpint *PT, mpint *x, mpint *y, mpint *ok, mpint *z){+ mpint *T = mpnew(0);
+ mpint *Z = mpnew(0);
+ mpint *Y = mpnew(0);
+ mpint *X = mpnew(0);
+ decaf_decode(p, a, d, y, ok, X, Y, Z, T);
+ if(mpcmp(ok, mpzero) != 0){+ mpint *tmp1 = mpnew(0);
+ mpmodsub(mpzero, PX, p, tmp1);
+ mpint *tmp2 = mpnew(0);
+ mpmodsub(mpzero, PT, p, tmp2);
+ edwards_add(p, a, d, X, Y, Z, T, tmp1, PY, PZ, tmp2, X, Y, Z, T);
+ mpfree(tmp1);
+ mpfree(tmp2);
+ edwards_scale(p, a, d, x, X, Y, Z, T, X, Y, Z, T);
+ decaf_encode(p, a, d, X, Y, Z, T, z);
+ }
+ mpfree(T);
+ mpfree(Z);
+ mpfree(Y);
+ mpfree(X);
+ }
--- /dev/null
+++ b/libmp/Makefile
@@ -1,0 +1,56 @@
+ROOT=..
+include ../Make.config
+# N.B. This is used only for secstore. It needn't be fast.
+
+LIB=libmp.a
+
+OFILES=\
+ betomp.$O\
+ cnfield.$O\
+ crt.$O\
+ gmfield.$O\
+ letomp.$O\
+ mpadd.$O\
+ mpaux.$O\
+ mpcmp.$O\
+ mpdigdiv.$O\
+ mpdiv.$O\
+ mpexp.$O\
+ mpextendedgcd.$O\
+ mpfactorial.$O\
+ mpfield.$O\
+ mpfmt.$O\
+ mpinvert.$O\
+ mpleft.$O\
+ mplogic.$O\
+ mpmod.$O\
+ mpmodop.$O\
+ mpmul.$O\
+ mpnrand.$O\
+ mprand.$O\
+ mpright.$O\
+ mpsel.$O\
+ mpsub.$O\
+ mptobe.$O\
+ mptober.$O\
+ mptoi.$O\
+ mptole.$O\
+ mptolel.$O\
+ mptoui.$O\
+ mptouv.$O\
+ mptov.$O\
+ mpvecadd.$O\
+ mpveccmp.$O\
+ mpvecdigmuladd.$O\
+ mpvecsub.$O\
+ mpvectscmp.$O\
+ strtomp.$O
+
+default: $(LIB)
+$(LIB): $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
+%.$O: %.c
+ $(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libmp/betomp.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// convert a big-endian byte array (most significant byte first) to an mpint
+mpint*
+betomp(uchar *p, uint n, mpint *b)
+{+ int m, s;
+ mpdigit x;
+
+ if(b == nil){+ b = mpnew(0);
+ setmalloctag(b, getcallerpc(&p));
+ }
+ mpbits(b, n*8);
+
+ m = DIGITS(n*8);
+ b->top = m--;
+ b->sign = 1;
+
+ s = ((n-1)*8)%Dbits;
+ x = 0;
+ for(; n > 0; n--){+ x |= ((mpdigit)(*p++)) << s;
+ s -= 8;
+ if(s < 0){+ b->p[m--] = x;
+ s = Dbits-8;
+ x = 0;
+ }
+ }
+ return mpnorm(b);
+}
--- /dev/null
+++ b/libmp/cnfield.c
@@ -1,0 +1,114 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ * fast reduction for crandall numbers of the form: 2^n - c
+ */
+
+enum {+ MAXDIG = 1024 / Dbits,
+};
+
+typedef struct CNfield CNfield;
+struct CNfield
+{+ Mfield f;
+
+ mpint m[1];
+
+ int s;
+ mpdigit c;
+};
+
+static int
+cnreduce(Mfield *m, mpint *a, mpint *r)
+{+ mpdigit q[MAXDIG-1], t[MAXDIG], d;
+ CNfield *f = (CNfield*)m;
+ int qn, tn, k;
+
+ k = f->f.m.top;
+ if((a->top - k) >= MAXDIG)
+ return -1;
+
+ mpleft(a, f->s, r);
+ if(r->top <= k)
+ mpbits(r, (k+1)*Dbits);
+
+ /* q = hi(r) */
+ qn = r->top - k;
+ memmove(q, r->p+k, qn*Dbytes);
+
+ /* r = lo(r) */
+ r->top = k;
+ r->sign = 1;
+
+ do {+ /* t = q*c */
+ tn = qn+1;
+ memset(t, 0, tn*Dbytes);
+ mpvecdigmuladd(q, qn, f->c, t);
+
+ /* q = hi(t) */
+ qn = tn - k;
+ if(qn <= 0) qn = 0;
+ else memmove(q, t+k, qn*Dbytes);
+
+ /* r += lo(t) */
+ if(tn > k)
+ tn = k;
+ mpvecadd(r->p, k, t, tn, r->p);
+
+ /* if(r >= m) r -= m */
+ mpvecsub(r->p, k+1, f->m->p, k, t);
+ d = t[k];
+ for(tn = 0; tn < k; tn++)
+ r->p[tn] = (r->p[tn] & d) | (t[tn] & ~d);
+ } while(qn > 0);
+
+ if(f->s != 0)
+ mpright(r, f->s, r);
+ mpnorm(r);
+
+ return 0;
+}
+
+Mfield*
+cnfield(mpint *N)
+{+ mpint *M, *C;
+ CNfield *f;
+ mpdigit d;
+ int s;
+
+ if(N->top <= 2 || N->top >= MAXDIG)
+ return nil;
+ f = nil;
+ d = N->p[N->top-1];
+ for(s = 0; (d & (mpdigit)1<<(Dbits-1)) == 0; s++)
+ d <<= 1;
+ C = mpnew(0);
+ M = mpcopy(N);
+ mpleft(N, s, M);
+ mpleft(mpone, M->top*Dbits, C);
+ mpsub(C, M, C);
+ if(C->top != 1)
+ goto out;
+ f = mallocz(sizeof(CNfield) + M->top*sizeof(mpdigit), 1);
+ if(f == nil)
+ goto out;
+ f->s = s;
+ f->c = C->p[0];
+ f->m->size = M->top;
+ f->m->p = (mpdigit*)&f[1];
+ mpassign(M, f->m);
+ mpassign(N, (mpint*)f);
+ f->f.reduce = cnreduce;
+ f->f.m.flags |= MPfield;
+out:
+ mpfree(M);
+ mpfree(C);
+
+ return (Mfield*)f;
+}
--- /dev/null
+++ b/libmp/crt.c
@@ -1,0 +1,121 @@
+#include "os.h"
+#include <mp.h>
+
+// chinese remainder theorem
+//
+// handbook of applied cryptography, menezes et al, 1997, pp 610 - 613
+
+struct CRTpre
+{+ int n; // number of moduli
+ mpint **m; // pointer to moduli
+ mpint **c; // precomputed coefficients
+ mpint **p; // precomputed products
+ mpint *a[1]; // local storage
+};
+
+// setup crt info, returns a newly created structure
+CRTpre*
+crtpre(int n, mpint **m)
+{+ CRTpre *crt;
+ int i, j;
+ mpint *u;
+
+ crt = malloc(sizeof(CRTpre)+sizeof(mpint)*3*n);
+ if(crt == nil)
+ sysfatal("crtpre: %r");+ crt->m = crt->a;
+ crt->c = crt->a+n;
+ crt->p = crt->c+n;
+ crt->n = n;
+
+ // make a copy of the moduli
+ for(i = 0; i < n; i++)
+ crt->m[i] = mpcopy(m[i]);
+
+ // precompute the products
+ u = mpcopy(mpone);
+ for(i = 0; i < n; i++){+ mpmul(u, m[i], u);
+ crt->p[i] = mpcopy(u);
+ }
+
+ // precompute the coefficients
+ for(i = 1; i < n; i++){+ crt->c[i] = mpcopy(mpone);
+ for(j = 0; j < i; j++){+ mpinvert(m[j], m[i], u);
+ mpmul(u, crt->c[i], u);
+ mpmod(u, m[i], crt->c[i]);
+ }
+ }
+
+ mpfree(u);
+
+ return crt;
+}
+
+void
+crtprefree(CRTpre *crt)
+{+ int i;
+
+ for(i = 0; i < crt->n; i++){+ if(i != 0)
+ mpfree(crt->c[i]);
+ mpfree(crt->p[i]);
+ mpfree(crt->m[i]);
+ }
+ free(crt);
+}
+
+// convert to residues, returns a newly created structure
+CRTres*
+crtin(CRTpre *crt, mpint *x)
+{+ int i;
+ CRTres *res;
+
+ res = malloc(sizeof(CRTres)+sizeof(mpint)*crt->n);
+ if(res == nil)
+ sysfatal("crtin: %r");+ res->n = crt->n;
+ for(i = 0; i < res->n; i++){+ res->r[i] = mpnew(0);
+ mpmod(x, crt->m[i], res->r[i]);
+ }
+ return res;
+}
+
+// garners algorithm for converting residue form to linear
+void
+crtout(CRTpre *crt, CRTres *res, mpint *x)
+{+ mpint *u;
+ int i;
+
+ u = mpnew(0);
+ mpassign(res->r[0], x);
+
+ for(i = 1; i < crt->n; i++){+ mpsub(res->r[i], x, u);
+ mpmul(u, crt->c[i], u);
+ mpmod(u, crt->m[i], u);
+ mpmul(u, crt->p[i-1], u);
+ mpadd(x, u, x);
+ }
+
+ mpfree(u);
+}
+
+// free the residue
+void
+crtresfree(CRTres *res)
+{+ int i;
+
+ for(i = 0; i < res->n; i++)
+ mpfree(res->r[i]);
+ free(res);
+}
--- /dev/null
+++ b/libmp/crttest.c
@@ -1,0 +1,52 @@
+#include "os.h"
+#include <mp.h>
+
+void
+testcrt(mpint **p)
+{+ CRTpre *crt;
+ CRTres *res;
+ mpint *m, *x, *y;
+
+ fmtinstall('B', mpfmt);+
+ // get a modulus and a test number
+ m = mpnew(1024+160);
+ mpmul(p[0], p[1], m);
+ x = mpnew(1024+160);
+ mpadd(m, mpone, x);
+
+ // do the precomputation for crt conversion
+ crt = crtpre(2, p);
+
+ // convert x to residues
+ res = crtin(crt, x);
+
+ // convert back
+ y = mpnew(1024+160);
+ crtout(crt, res, y);
+ print("x %B\ny %B\n", x, y);+ mpfree(m);
+ mpfree(x);
+ mpfree(y);
+}
+
+void
+main(void)
+{+ int i;
+ mpint *p[2];
+ long start;
+
+ start = time(0);
+ for(i = 0; i < 10; i++){+ p[0] = mpnew(1024);
+ p[1] = mpnew(1024);
+ DSAprimes(p[0], p[1], nil);
+ testcrt(p);
+ mpfree(p[0]);
+ mpfree(p[1]);
+ }
+ print("%ld secs with more\n", time(0)-start);+ exits(0);
+}
--- /dev/null
+++ b/libmp/dat.h
@@ -1,0 +1,12 @@
+#define mpdighi (mpdigit)(1<<(Dbits-1))
+#define DIGITS(x) ((Dbits - 1 + (x))/Dbits)
+
+// for converting between int's and mpint's
+#define MAXUINT ((uint)-1)
+#define MAXINT (MAXUINT>>1)
+#define MININT (MAXINT+1)
+
+// for converting between vlongs's and mpint's
+#define MAXUVLONG (~0ULL)
+#define MAXVLONG (MAXUVLONG>>1)
+#define MINVLONG (MAXVLONG+1ULL)
--- /dev/null
+++ b/libmp/gmfield.c
@@ -1,0 +1,173 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ * fast reduction for generalized mersenne numbers (GM)
+ * using a series of additions and subtractions.
+ */
+
+enum {+ MAXDIG = 1024/Dbits,
+};
+
+typedef struct GMfield GMfield;
+struct GMfield
+{+ Mfield f;
+
+ mpint m2[1];
+
+ int nadd;
+ int nsub;
+ int indx[256];
+};
+
+static int
+gmreduce(Mfield *m, mpint *a, mpint *r)
+{+ GMfield *g = (GMfield*)m;
+ mpdigit d0, t[MAXDIG];
+ int i, j, d, *x;
+
+ if(mpmagcmp(a, g->m2) >= 0)
+ return -1;
+
+ if(a != r)
+ mpassign(a, r);
+
+ d = g->f.m.top;
+ mpbits(r, (d+1)*Dbits*2);
+ memmove(t+d, r->p+d, d*Dbytes);
+
+ r->sign = 1;
+ r->top = d;
+ r->p[d] = 0;
+
+ if(g->nsub > 0)
+ mpvecdigmuladd(g->f.m.p, d, g->nsub, r->p);
+
+ x = g->indx;
+ for(i=0; i<g->nadd; i++){+ t[0] = 0;
+ d0 = t[*x++];
+ for(j=1; j<d; j++)
+ t[j] = t[*x++];
+ t[0] = d0;
+
+ mpvecadd(r->p, d+1, t, d, r->p);
+ }
+
+ for(i=0; i<g->nsub; i++){+ t[0] = 0;
+ d0 = t[*x++];
+ for(j=1; j<d; j++)
+ t[j] = t[*x++];
+ t[0] = d0;
+
+ mpvecsub(r->p, d+1, t, d, r->p);
+ }
+
+ mpvecdigmulsub(g->f.m.p, d, r->p[d], r->p);
+ r->p[d] = 0;
+
+ mpvecsub(r->p, d+1, g->f.m.p, d, r->p+d+1);
+ d0 = r->p[2*d+1];
+ for(j=0; j<d; j++)
+ r->p[j] = (r->p[j] & d0) | (r->p[j+d+1] & ~d0);
+
+ mpnorm(r);
+
+ return 0;
+}
+
+Mfield*
+gmfield(mpint *N)
+{+ int i,j,d, s, *C, *X, *x, *e;
+ mpint *M, *T;
+ GMfield *g;
+
+ d = N->top;
+ if(d <= 2 || d > MAXDIG/2 || (mpsignif(N) % Dbits) != 0)
+ return nil;
+ g = nil;
+ T = mpnew(0);
+ M = mpcopy(N);
+ C = malloc(sizeof(int)*(d+1));
+ X = malloc(sizeof(int)*(d*d));
+ if(C == nil || X == nil)
+ goto out;
+
+ for(i=0; i<=d; i++){+ if((M->p[i]>>8) != 0 && (~M->p[i]>>8) != 0)
+ goto out;
+ j = M->p[i];
+ C[d - i] = -j;
+ itomp(j, T);
+ mpleft(T, i*Dbits, T);
+ mpsub(M, T, M);
+ }
+ for(j=0; j<d; j++)
+ X[j] = C[d-j];
+ for(i=1; i<d; i++){+ X[d*i] = X[d*(i-1) + d-1]*C[d];
+ for(j=1; j<d; j++)
+ X[d*i + j] = X[d*(i-1) + j-1] + X[d*(i-1) + d-1]*C[d-j];
+ }
+ g = mallocz(sizeof(GMfield) + (d+1)*sizeof(mpdigit)*2, 1);
+ if(g == nil)
+ goto out;
+
+ g->m2->p = (mpdigit*)&g[1];
+ g->m2->size = d*2+1;
+ mpmul(N, N, g->m2);
+ mpassign(N, (mpint*)g);
+ g->f.reduce = gmreduce;
+ g->f.m.flags |= MPfield;
+
+ s = 0;
+ x = g->indx;
+ e = x + nelem(g->indx) - d;
+ for(g->nadd=0; x <= e; x += d, g->nadd++){+ s = 0;
+ for(i=0; i<d; i++){+ for(j=0; j<d; j++){+ if(X[d*i+j] > 0 && x[j] == 0){+ X[d*i+j]--;
+ x[j] = d+i;
+ s = 1;
+ break;
+ }
+ }
+ }
+ if(s == 0)
+ break;
+ }
+ for(g->nsub=0; x <= e; x += d, g->nsub++){+ s = 0;
+ for(i=0; i<d; i++){+ for(j=0; j<d; j++){+ if(X[d*i+j] < 0 && x[j] == 0){+ X[d*i+j]++;
+ x[j] = d+i;
+ s = 1;
+ break;
+ }
+ }
+ }
+ if(s == 0)
+ break;
+ }
+ if(s != 0){+ mpfree((mpint*)g);
+ g = nil;
+ }
+out:
+ free(C);
+ free(X);
+ mpfree(M);
+ mpfree(T);
+ return (Mfield*)g;
+}
+
--- /dev/null
+++ b/libmp/letomp.c
@@ -1,0 +1,31 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// convert a little endian byte array (least significant byte first) to an mpint
+mpint*
+letomp(uchar *s, uint n, mpint *b)
+{+ int i=0, m = 0;
+ mpdigit x=0;
+
+ if(b == nil){+ b = mpnew(0);
+ setmalloctag(b, getcallerpc(&s));
+ }
+ mpbits(b, 8*n);
+ for(; n > 0; n--){+ x |= ((mpdigit)(*s++)) << i;
+ i += 8;
+ if(i == Dbits){+ b->p[m++] = x;
+ i = 0;
+ x = 0;
+ }
+ }
+ if(i > 0)
+ b->p[m++] = x;
+ b->top = m;
+ b->sign = 1;
+ return mpnorm(b);
+}
--- /dev/null
+++ b/libmp/mpadd.c
@@ -1,0 +1,58 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// sum = abs(b1) + abs(b2), i.e., add the magnitudes
+void
+mpmagadd(mpint *b1, mpint *b2, mpint *sum)
+{+ int m, n;
+ mpint *t;
+
+ sum->flags |= (b1->flags | b2->flags) & MPtimesafe;
+
+ // get the sizes right
+ if(b2->top > b1->top){+ t = b1;
+ b1 = b2;
+ b2 = t;
+ }
+ n = b1->top;
+ m = b2->top;
+ if(n == 0){+ mpassign(mpzero, sum);
+ return;
+ }
+ if(m == 0){+ mpassign(b1, sum);
+ sum->sign = 1;
+ return;
+ }
+ mpbits(sum, (n+1)*Dbits);
+ sum->top = n+1;
+
+ mpvecadd(b1->p, n, b2->p, m, sum->p);
+ sum->sign = 1;
+
+ mpnorm(sum);
+}
+
+// sum = b1 + b2
+void
+mpadd(mpint *b1, mpint *b2, mpint *sum)
+{+ int sign;
+
+ if(b1->sign != b2->sign){+ assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
+ if(b1->sign < 0)
+ mpmagsub(b2, b1, sum);
+ else
+ mpmagsub(b1, b2, sum);
+ } else {+ sign = b1->sign;
+ mpmagadd(b1, b2, sum);
+ if(sum->top != 0)
+ sum->sign = sign;
+ }
+}
--- /dev/null
+++ b/libmp/mpaux.c
@@ -1,0 +1,205 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+static mpdigit _mptwodata[1] = { 2 };+static mpint _mptwo =
+{+ 1, 1, 1,
+ _mptwodata,
+ MPstatic|MPnorm
+};
+mpint *mptwo = &_mptwo;
+
+static mpdigit _mponedata[1] = { 1 };+static mpint _mpone =
+{+ 1, 1, 1,
+ _mponedata,
+ MPstatic|MPnorm
+};
+mpint *mpone = &_mpone;
+
+static mpdigit _mpzerodata[1] = { 0 };+static mpint _mpzero =
+{+ 1, 1, 0,
+ _mpzerodata,
+ MPstatic|MPnorm
+};
+mpint *mpzero = &_mpzero;
+
+static int mpmindigits = 33;
+
+// set minimum digit allocation
+void
+mpsetminbits(int n)
+{+ if(n < 0)
+ sysfatal("mpsetminbits: n < 0");+ if(n == 0)
+ n = 1;
+ mpmindigits = DIGITS(n);
+}
+
+// allocate an n bit 0'd number
+mpint*
+mpnew(int n)
+{+ mpint *b;
+
+ if(n < 0)
+ sysfatal("mpsetminbits: n < 0");+
+ n = DIGITS(n);
+ if(n < mpmindigits)
+ n = mpmindigits;
+ b = mallocz(sizeof(mpint) + n*Dbytes, 1);
+ if(b == nil)
+ sysfatal("mpnew: %r");+ setmalloctag(b, getcallerpc(&n));
+ b->p = (mpdigit*)&b[1];
+ b->size = n;
+ b->sign = 1;
+ b->flags = MPnorm;
+
+ return b;
+}
+
+// guarantee at least n significant bits
+void
+mpbits(mpint *b, int m)
+{+ int n;
+
+ n = DIGITS(m);
+ if(b->size >= n){+ if(b->top >= n)
+ return;
+ } else {+ if(b->p == (mpdigit*)&b[1]){+ b->p = (mpdigit*)mallocz(n*Dbytes, 0);
+ if(b->p == nil)
+ sysfatal("mpbits: %r");+ memmove(b->p, &b[1], Dbytes*b->top);
+ memset(&b[1], 0, Dbytes*b->size);
+ } else {+ b->p = (mpdigit*)realloc(b->p, n*Dbytes);
+ if(b->p == nil)
+ sysfatal("mpbits: %r");+ }
+ b->size = n;
+ }
+ memset(&b->p[b->top], 0, Dbytes*(n - b->top));
+ b->top = n;
+ b->flags &= ~MPnorm;
+}
+
+void
+mpfree(mpint *b)
+{+ if(b == nil)
+ return;
+ if(b->flags & MPstatic)
+ sysfatal("freeing mp constant");+ memset(b->p, 0, b->size*Dbytes);
+ if(b->p != (mpdigit*)&b[1])
+ free(b->p);
+ free(b);
+}
+
+mpint*
+mpnorm(mpint *b)
+{+ int i;
+
+ if(b->flags & MPtimesafe){+ assert(b->sign == 1);
+ b->flags &= ~MPnorm;
+ return b;
+ }
+ for(i = b->top-1; i >= 0; i--)
+ if(b->p[i] != 0)
+ break;
+ b->top = i+1;
+ if(b->top == 0)
+ b->sign = 1;
+ b->flags |= MPnorm;
+ return b;
+}
+
+mpint*
+mpcopy(mpint *old)
+{+ mpint *new;
+
+ new = mpnew(Dbits*old->size);
+ setmalloctag(new, getcallerpc(&old));
+ new->sign = old->sign;
+ new->top = old->top;
+ new->flags = old->flags & ~(MPstatic|MPfield);
+ memmove(new->p, old->p, Dbytes*old->top);
+ return new;
+}
+
+void
+mpassign(mpint *old, mpint *new)
+{+ if(new == nil || old == new)
+ return;
+ new->top = 0;
+ mpbits(new, Dbits*old->top);
+ new->sign = old->sign;
+ new->top = old->top;
+ new->flags &= ~MPnorm;
+ new->flags |= old->flags & ~(MPstatic|MPfield);
+ memmove(new->p, old->p, Dbytes*old->top);
+}
+
+// number of significant bits in mantissa
+int
+mpsignif(mpint *n)
+{+ int i, j;
+ mpdigit d;
+
+ if(n->top == 0)
+ return 0;
+ for(i = n->top-1; i >= 0; i--){+ d = n->p[i];
+ for(j = Dbits-1; j >= 0; j--){+ if(d & (((mpdigit)1)<<j))
+ return i*Dbits + j + 1;
+ }
+ }
+ return 0;
+}
+
+// k, where n = 2**k * q for odd q
+int
+mplowbits0(mpint *n)
+{+ int k, bit, digit;
+ mpdigit d;
+
+ assert(n->flags & MPnorm);
+ if(n->top==0)
+ return 0;
+ k = 0;
+ bit = 0;
+ digit = 0;
+ d = n->p[0];
+ for(;;){+ if(d & (1<<bit))
+ break;
+ k++;
+ bit++;
+ if(bit==Dbits){+ if(++digit >= n->top)
+ return 0;
+ d = n->p[digit];
+ bit = 0;
+ }
+ }
+ return k;
+}
--- /dev/null
+++ b/libmp/mpcmp.c
@@ -1,0 +1,30 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// return neg, 0, pos as abs(b1)-abs(b2) is neg, 0, pos
+int
+mpmagcmp(mpint *b1, mpint *b2)
+{+ int i;
+
+ i = b1->flags | b2->flags;
+ if(i & MPtimesafe)
+ return mpvectscmp(b1->p, b1->top, b2->p, b2->top);
+ if(i & MPnorm){+ i = b1->top - b2->top;
+ if(i)
+ return i;
+ }
+ return mpveccmp(b1->p, b1->top, b2->p, b2->top);
+}
+
+// return neg, 0, pos as b1-b2 is neg, 0, pos
+int
+mpcmp(mpint *b1, mpint *b2)
+{+ int sign;
+
+ sign = (b1->sign - b2->sign) >> 1; // -1, 0, 1
+ return sign | (((sign&1)-1) & mpmagcmp(b1, b2)*b1->sign);
+}
--- /dev/null
+++ b/libmp/mpdigdiv.c
@@ -1,0 +1,56 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+//
+// divide two digits by one and return quotient
+//
+void
+mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient)
+{+ mpdigit hi, lo, q, x, y;
+ int i;
+
+ hi = dividend[1];
+ lo = dividend[0];
+
+ // return highest digit value if the result >= 2**32
+ if(hi >= divisor || divisor == 0){+ divisor = 0;
+ *quotient = ~divisor;
+ return;
+ }
+
+ // very common case
+ if(~divisor == 0){+ lo += hi;
+ if(lo < hi){+ hi++;
+ lo++;
+ }
+ if(lo+1 == 0)
+ hi++;
+ *quotient = hi;
+ return;
+ }
+
+ // at this point we know that hi < divisor
+ // just shift and subtract till we're done
+ q = 0;
+ x = divisor;
+ for(i = Dbits-1; hi > 0 && i >= 0; i--){+ x >>= 1;
+ if(x > hi)
+ continue;
+ y = divisor<<i;
+ if(x == hi && y > lo)
+ continue;
+ if(y > lo)
+ hi--;
+ lo -= y;
+ hi -= x;
+ q |= 1<<i;
+ }
+ q += lo/divisor;
+ *quotient = q;
+}
--- /dev/null
+++ b/libmp/mpdiv.c
@@ -1,0 +1,142 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// division ala knuth, seminumerical algorithms, pp 237-238
+// the numbers are stored backwards to what knuth expects so j
+// counts down rather than up.
+
+void
+mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder)
+{+ int j, s, vn, sign, qsign, rsign;
+ mpdigit qd, *up, *vp, *qp;
+ mpint *u, *v, *t;
+
+ assert(quotient != remainder);
+ assert(divisor->flags & MPnorm);
+
+ // divide bv zero
+ if(divisor->top == 0)
+ abort();
+
+ // division by one or small powers of two
+ if(divisor->top == 1 && (divisor->p[0] & (divisor->p[0]-1)) == 0){+ vlong r = 0;
+ if(dividend->top > 0)
+ r = (vlong)dividend->sign * (dividend->p[0] & (divisor->p[0]-1));
+ if(quotient != nil){+ sign = divisor->sign;
+ for(s = 0; ((divisor->p[0] >> s) & 1) == 0; s++)
+ ;
+ mpright(dividend, s, quotient);
+ if(sign < 0)
+ quotient->sign ^= (-mpmagcmp(quotient, mpzero) >> 31) << 1;
+ }
+ if(remainder != nil){+ remainder->flags |= dividend->flags & MPtimesafe;
+ vtomp(r, remainder);
+ }
+ return;
+ }
+ assert((dividend->flags & MPtimesafe) == 0);
+
+ // quick check
+ if(mpmagcmp(dividend, divisor) < 0){+ if(remainder != nil)
+ mpassign(dividend, remainder);
+ if(quotient != nil)
+ mpassign(mpzero, quotient);
+ return;
+ }
+
+ qsign = divisor->sign * dividend->sign;
+ rsign = dividend->sign;
+
+ // D1: shift until divisor, v, has hi bit set (needed to make trial
+ // divisor accurate)
+ qd = divisor->p[divisor->top-1];
+ for(s = 0; (qd & mpdighi) == 0; s++)
+ qd <<= 1;
+ u = mpnew((dividend->top+2)*Dbits + s);
+ if(s == 0 && divisor != quotient && divisor != remainder) {+ mpassign(dividend, u);
+ v = divisor;
+ } else {+ mpleft(dividend, s, u);
+ v = mpnew(divisor->top*Dbits);
+ mpleft(divisor, s, v);
+ }
+ up = u->p+u->top-1;
+ vp = v->p+v->top-1;
+ vn = v->top;
+
+ // D1a: make sure high digit of dividend is less than high digit of divisor
+ if(*up >= *vp){+ *++up = 0;
+ u->top++;
+ }
+
+ // storage for multiplies
+ t = mpnew(4*Dbits);
+
+ qp = nil;
+ if(quotient != nil){+ mpbits(quotient, (u->top - v->top)*Dbits);
+ quotient->top = u->top - v->top;
+ qp = quotient->p+quotient->top-1;
+ }
+
+ // D2, D7: loop on length of dividend
+ for(j = u->top; j > vn; j--){+
+ // D3: calculate trial divisor
+ mpdigdiv(up-1, *vp, &qd);
+
+ // D3a: rule out trial divisors 2 greater than real divisor
+ if(vn > 1) for(;;){+ memset(t->p, 0, 3*Dbytes); // mpvecdigmuladd adds to what's there
+ mpvecdigmuladd(vp-1, 2, qd, t->p);
+ if(mpveccmp(t->p, 3, up-2, 3) > 0)
+ qd--;
+ else
+ break;
+ }
+
+ // D4: u -= v*qd << j*Dbits
+ sign = mpvecdigmulsub(v->p, vn, qd, up-vn);
+ if(sign < 0){+
+ // D6: trial divisor was too high, add back borrowed
+ // value and decrease divisor
+ mpvecadd(up-vn, vn+1, v->p, vn, up-vn);
+ qd--;
+ }
+
+ // D5: save quotient digit
+ if(qp != nil)
+ *qp-- = qd;
+
+ // push top of u down one
+ u->top--;
+ *up-- = 0;
+ }
+ if(qp != nil){+ assert((quotient->flags & MPtimesafe) == 0);
+ mpnorm(quotient);
+ if(quotient->top != 0)
+ quotient->sign = qsign;
+ }
+
+ if(remainder != nil){+ assert((remainder->flags & MPtimesafe) == 0);
+ mpright(u, s, remainder); // u is the remainder shifted
+ if(remainder->top != 0)
+ remainder->sign = rsign;
+ }
+
+ mpfree(t);
+ mpfree(u);
+ if(v != divisor)
+ mpfree(v);
+}
--- /dev/null
+++ b/libmp/mpexp.c
@@ -1,0 +1,96 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = b**e
+//
+// knuth, vol 2, pp 398-400
+
+enum {+ Freeb= 0x1,
+ Freee= 0x2,
+ Freem= 0x4,
+};
+
+//int expdebug;
+
+void
+mpexp(mpint *b, mpint *e, mpint *m, mpint *res)
+{+ mpint *t[2];
+ int tofree;
+ mpdigit d, bit;
+ int i, j;
+
+ assert(m == nil || m->flags & MPnorm);
+ assert((e->flags & MPtimesafe) == 0);
+ res->flags |= b->flags & MPtimesafe;
+
+ i = mpcmp(e,mpzero);
+ if(i==0){+ mpassign(mpone, res);
+ return;
+ }
+ if(i<0)
+ sysfatal("mpexp: negative exponent");+
+ t[0] = mpcopy(b);
+ t[1] = res;
+
+ tofree = 0;
+ if(res == b){+ b = mpcopy(b);
+ tofree |= Freeb;
+ }
+ if(res == e){+ e = mpcopy(e);
+ tofree |= Freee;
+ }
+ if(res == m){+ m = mpcopy(m);
+ tofree |= Freem;
+ }
+
+ // skip first bit
+ i = e->top-1;
+ d = e->p[i];
+ for(bit = mpdighi; (bit & d) == 0; bit >>= 1)
+ ;
+ bit >>= 1;
+
+ j = 0;
+ for(;;){+ for(; bit != 0; bit >>= 1){+ if(m != nil)
+ mpmodmul(t[j], t[j], m, t[j^1]);
+ else
+ mpmul(t[j], t[j], t[j^1]);
+ if(bit & d) {+ if(m != nil)
+ mpmodmul(t[j^1], b, m, t[j]);
+ else
+ mpmul(t[j^1], b, t[j]);
+ } else
+ j ^= 1;
+ }
+ if(--i < 0)
+ break;
+ bit = mpdighi;
+ d = e->p[i];
+ }
+ if(t[j] == res){+ mpfree(t[j^1]);
+ } else {+ mpassign(t[j], res);
+ mpfree(t[j]);
+ }
+
+ if(tofree){+ if(tofree & Freeb)
+ mpfree(b);
+ if(tofree & Freee)
+ mpfree(e);
+ if(tofree & Freem)
+ mpfree(m);
+ }
+}
--- /dev/null
+++ b/libmp/mpextendedgcd.c
@@ -1,0 +1,115 @@
+#include "os.h"
+#include <mp.h>
+
+#define iseven(a) (((a)->p[0] & 1) == 0)
+
+// extended binary gcd
+//
+// For a and b it solves, v = gcd(a,b) and finds x and y s.t.
+// ax + by = v
+//
+// Handbook of Applied Cryptography, Menezes et al, 1997, pg 608.
+void
+mpextendedgcd(mpint *a, mpint *b, mpint *v, mpint *x, mpint *y)
+{+ mpint *u, *A, *B, *C, *D;
+ int g;
+
+ if(v == nil){+ v = mpnew(0);
+ mpextendedgcd(a, b, v, x, y);
+ mpfree(v);
+ return;
+ }
+ assert(x == nil || (x->flags & MPtimesafe) == 0);
+ assert(y == nil || (y->flags & MPtimesafe) == 0);
+ assert((a->flags&b->flags) & MPnorm);
+ assert(((a->flags|b->flags|v->flags) & MPtimesafe) == 0);
+
+ if(a->sign < 0 || b->sign < 0){+ mpassign(mpzero, v);
+ mpassign(mpzero, y);
+ mpassign(mpzero, x);
+ return;
+ }
+
+ if(a->top == 0){+ mpassign(b, v);
+ mpassign(mpone, y);
+ mpassign(mpzero, x);
+ return;
+ }
+ if(b->top == 0){+ mpassign(a, v);
+ mpassign(mpone, x);
+ mpassign(mpzero, y);
+ return;
+ }
+
+ g = 0;
+ a = mpcopy(a);
+ b = mpcopy(b);
+
+ while(iseven(a) && iseven(b)){+ mpright(a, 1, a);
+ mpright(b, 1, b);
+ g++;
+ }
+
+ u = mpcopy(a);
+ mpassign(b, v);
+ A = mpcopy(mpone);
+ B = mpcopy(mpzero);
+ C = mpcopy(mpzero);
+ D = mpcopy(mpone);
+
+ for(;;) {+// print("%B %B %B %B %B %B\n", u, v, A, B, C, D);+ while(iseven(u)){+ mpright(u, 1, u);
+ if(!iseven(A) || !iseven(B)) {+ mpadd(A, b, A);
+ mpsub(B, a, B);
+ }
+ mpright(A, 1, A);
+ mpright(B, 1, B);
+ }
+
+// print("%B %B %B %B %B %B\n", u, v, A, B, C, D);+ while(iseven(v)){+ mpright(v, 1, v);
+ if(!iseven(C) || !iseven(D)) {+ mpadd(C, b, C);
+ mpsub(D, a, D);
+ }
+ mpright(C, 1, C);
+ mpright(D, 1, D);
+ }
+
+// print("%B %B %B %B %B %B\n", u, v, A, B, C, D);+ if(mpcmp(u, v) >= 0){+ mpsub(u, v, u);
+ mpsub(A, C, A);
+ mpsub(B, D, B);
+ } else {+ mpsub(v, u, v);
+ mpsub(C, A, C);
+ mpsub(D, B, D);
+ }
+
+ if(u->top == 0)
+ break;
+
+ }
+ mpassign(C, x);
+ mpassign(D, y);
+ mpleft(v, g, v);
+
+ mpfree(A);
+ mpfree(B);
+ mpfree(C);
+ mpfree(D);
+ mpfree(u);
+ mpfree(a);
+ mpfree(b);
+}
--- /dev/null
+++ b/libmp/mpfactorial.c
@@ -1,0 +1,74 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+mpint*
+mpfactorial(ulong n)
+{+ int i;
+ ulong k;
+ unsigned cnt;
+ int max, mmax;
+ mpdigit p, pp[2];
+ mpint *r, *s, *stk[31];
+
+ cnt = 0;
+ max = mmax = -1;
+ p = 1;
+ r = mpnew(0);
+ for(k=2; k<=n; k++){+ pp[0] = 0;
+ pp[1] = 0;
+ mpvecdigmuladd(&p, 1, (mpdigit)k, pp);
+ if(pp[1] == 0) /* !overflow */
+ p = pp[0];
+ else{+ cnt++;
+ if((cnt & 1) == 0){+ s = stk[max];
+ mpbits(r, Dbits*(s->top+1+1));
+ memset(r->p, 0, Dbytes*(s->top+1+1));
+ mpvecmul(s->p, s->top, &p, 1, r->p);
+ r->sign = 1;
+ r->top = s->top+1+1; /* XXX: norm */
+ mpassign(r, s);
+ for(i=4; (cnt & (i-1)) == 0; i=i<<1){+ mpmul(stk[max], stk[max-1], r);
+ mpassign(r, stk[max-1]);
+ max--;
+ }
+ }else{+ max++;
+ if(max > mmax){+ mmax++;
+ if(max > nelem(stk))
+ abort();
+ stk[max] = mpnew(Dbits);
+ }
+ stk[max]->top = 1;
+ stk[max]->p[0] = p;
+ }
+ p = (mpdigit)k;
+ }
+ }
+ if(max < 0){+ mpbits(r, Dbits);
+ r->top = 1;
+ r->sign = 1;
+ r->p[0] = p;
+ }else{+ s = stk[max--];
+ mpbits(r, Dbits*(s->top+1+1));
+ memset(r->p, 0, Dbytes*(s->top+1+1));
+ mpvecmul(s->p, s->top, &p, 1, r->p);
+ r->sign = 1;
+ r->top = s->top+1+1; /* XXX: norm */
+ }
+
+ while(max >= 0)
+ mpmul(r, stk[max--], r);
+ for(max=mmax; max>=0; max--)
+ mpfree(stk[max]);
+ mpnorm(r);
+ return r;
+}
--- /dev/null
+++ b/libmp/mpfield.c
@@ -1,0 +1,21 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+mpint*
+mpfield(mpint *N)
+{+ Mfield *f;
+
+ if(N == nil || N->flags & (MPfield|MPstatic))
+ return N;
+ if((f = cnfield(N)) != nil)
+ goto Exchange;
+ if((f = gmfield(N)) != nil)
+ goto Exchange;
+ return N;
+Exchange:
+ setmalloctag(f, getcallerpc(&N));
+ mpfree(N);
+ return (mpint*)f;
+}
--- /dev/null
+++ b/libmp/mpfmt.c
@@ -1,0 +1,254 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+static int
+toencx(mpint *b, char *buf, int len, int (*enc)(char*, int, uchar*, int))
+{+ uchar *p;
+ int n, rv;
+
+ p = nil;
+ n = mptobe(b, nil, 0, &p);
+ if(n < 0)
+ return -1;
+ rv = (*enc)(buf, len, p, n);
+ free(p);
+ return rv;
+}
+
+static int
+topow2(mpint *b, char *buf, int len, int s)
+{+ mpdigit *p, x;
+ int i, j, sn;
+ char *out, *eout;
+
+ if(len < 1)
+ return -1;
+
+ sn = 1<<s;
+ out = buf;
+ eout = buf+len;
+ for(p = &b->p[b->top-1]; p >= b->p; p--){+ x = *p;
+ for(i = Dbits-s; i >= 0; i -= s){+ j = (x >> i) & (sn - 1);
+ if(j != 0 || out != buf){+ if(out >= eout)
+ return -1;
+ *out++ = enc16chr(j);
+ }
+ }
+ }
+ if(out == buf)
+ *out++ = '0';
+ if(out >= eout)
+ return -1;
+ *out = 0;
+ return 0;
+}
+
+static char*
+modbillion(int rem, ulong r, char *out, char *buf)
+{+ ulong rr;
+ int i;
+
+ for(i = 0; i < 9; i++){+ rr = r%10;
+ r /= 10;
+ if(out <= buf)
+ return nil;
+ *--out = '0' + rr;
+ if(rem == 0 && r == 0)
+ break;
+ }
+ return out;
+}
+
+static int
+to10(mpint *b, char *buf, int len)
+{+ mpint *d, *r, *billion;
+ char *out;
+
+ if(len < 1)
+ return -1;
+
+ d = mpcopy(b);
+ d->flags &= ~MPtimesafe;
+ mpnorm(d);
+ r = mpnew(0);
+ billion = uitomp(1000000000, nil);
+ out = buf+len;
+ *--out = 0;
+ do {+ mpdiv(d, billion, d, r);
+ out = modbillion(d->top, r->p[0], out, buf);
+ if(out == nil)
+ break;
+ } while(d->top != 0);
+ mpfree(d);
+ mpfree(r);
+ mpfree(billion);
+
+ if(out == nil)
+ return -1;
+ len -= out-buf;
+ if(out != buf)
+ memmove(buf, out, len);
+ return 0;
+}
+
+static int
+to8(mpint *b, char *buf, int len)
+{+ mpdigit x, y;
+ char *out;
+ int i, j;
+
+ if(len < 2)
+ return -1;
+
+ out = buf+len;
+ *--out = 0;
+
+ i = j = 0;
+ x = y = 0;
+ while(j < b->top){+ y = b->p[j++];
+ if(i > 0)
+ x |= y << i;
+ else
+ x = y;
+ i += Dbits;
+ while(i >= 3){+Digout: i -= 3;
+ if(out > buf)
+ out--;
+ else if(x != 0)
+ return -1;
+ *out = '0' + (x & 7);
+ x = y >> (Dbits-i);
+ }
+ }
+ if(i > 0)
+ goto Digout;
+
+ while(*out == '0') out++;
+ if(*out == '\0')
+ *--out = '0';
+
+ len -= out-buf;
+ if(out != buf)
+ memmove(buf, out, len);
+ return 0;
+}
+
+int
+mpfmt(Fmt *fmt)
+{+ mpint *b;
+ char *x, *p;
+ int base;
+
+ b = va_arg(fmt->args, mpint*);
+ if(b == nil)
+ return fmtstrcpy(fmt, "*");
+
+ base = fmt->prec;
+ if(base == 0)
+ base = 16; /* default */
+ fmt->flags &= ~FmtPrec;
+ p = mptoa(b, base, nil, 0);
+ if(p == nil)
+ return fmtstrcpy(fmt, "*");
+ else{+ if((fmt->flags & FmtSharp) != 0){+ switch(base){+ case 16:
+ x = "0x";
+ break;
+ case 8:
+ x = "0";
+ break;
+ case 2:
+ x = "0b";
+ break;
+ default:
+ x = "";
+ }
+ if(*p == '-')
+ fmtprint(fmt, "-%s%s", x, p + 1);
+ else
+ fmtprint(fmt, "%s%s", x, p);
+ }
+ else
+ fmtstrcpy(fmt, p);
+ free(p);
+ return 0;
+ }
+}
+
+char*
+mptoa(mpint *b, int base, char *buf, int len)
+{+ char *out;
+ int rv, alloced;
+
+ if(base == 0)
+ base = 16; /* default */
+ alloced = 0;
+ if(buf == nil){+ /* rv <= log₂(base) */
+ for(rv=1; (base >> rv) > 1; rv++)
+ ;
+ len = 10 + (b->top*Dbits / rv);
+ buf = malloc(len);
+ if(buf == nil)
+ return nil;
+ alloced = 1;
+ }
+
+ if(len < 2)
+ return nil;
+
+ out = buf;
+ if(b->sign < 0){+ *out++ = '-';
+ len--;
+ }
+ switch(base){+ case 64:
+ rv = toencx(b, out, len, enc64);
+ break;
+ case 32:
+ rv = toencx(b, out, len, enc32);
+ break;
+ case 16:
+ rv = topow2(b, out, len, 4);
+ break;
+ case 10:
+ rv = to10(b, out, len);
+ break;
+ case 8:
+ rv = to8(b, out, len);
+ break;
+ case 4:
+ rv = topow2(b, out, len, 2);
+ break;
+ case 2:
+ rv = topow2(b, out, len, 1);
+ break;
+ default:
+ abort();
+ return nil;
+ }
+ if(rv < 0){+ if(alloced)
+ free(buf);
+ return nil;
+ }
+ return buf;
+}
--- /dev/null
+++ b/libmp/mpinvert.c
@@ -1,0 +1,17 @@
+#include "os.h"
+#include <mp.h>
+
+// use extended gcd to find the multiplicative inverse
+// res = b**-1 mod m
+void
+mpinvert(mpint *b, mpint *m, mpint *res)
+{+ mpint *v;
+
+ v = mpnew(0);
+ mpextendedgcd(b, m, v, res, nil);
+ if(mpcmp(v, mpone) != 0)
+ abort();
+ mpfree(v);
+ mpmod(res, m, res);
+}
--- /dev/null
+++ b/libmp/mpleft.c
@@ -1,0 +1,51 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = b << shift
+void
+mpleft(mpint *b, int shift, mpint *res)
+{+ int d, l, r, i, otop;
+ mpdigit this, last;
+
+ res->sign = b->sign;
+ if(b->top==0){+ res->top = 0;
+ return;
+ }
+
+ // a zero or negative left shift is a right shift
+ if(shift <= 0){+ mpright(b, -shift, res);
+ return;
+ }
+
+ // b and res may be the same so remember the old top
+ otop = b->top;
+
+ // shift
+ mpbits(res, otop*Dbits + shift); // overkill
+ res->top = DIGITS(otop*Dbits + shift);
+ d = shift/Dbits;
+ l = shift - d*Dbits;
+ r = Dbits - l;
+
+ if(l == 0){+ for(i = otop-1; i >= 0; i--)
+ res->p[i+d] = b->p[i];
+ } else {+ last = 0;
+ for(i = otop-1; i >= 0; i--) {+ this = b->p[i];
+ res->p[i+d+1] = (last<<l) | (this>>r);
+ last = this;
+ }
+ res->p[d] = last<<l;
+ }
+ for(i = 0; i < d; i++)
+ res->p[i] = 0;
+
+ res->flags |= b->flags & MPtimesafe;
+ mpnorm(res);
+}
--- /dev/null
+++ b/libmp/mplogic.c
@@ -1,0 +1,212 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ mplogic calculates b1|b2 subject to the
+ following flag bits (fl)
+
+ bit 0: subtract 1 from b1
+ bit 1: invert b1
+ bit 2: subtract 1 from b2
+ bit 3: invert b2
+ bit 4: add 1 to output
+ bit 5: invert output
+
+ it inverts appropriate bits automatically
+ depending on the signs of the inputs
+*/
+
+static void
+mplogic(mpint *b1, mpint *b2, mpint *sum, int fl)
+{+ mpint *t;
+ mpdigit *dp1, *dp2, *dpo, d1, d2, d;
+ int c1, c2, co;
+ int i;
+
+ assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
+ if(b1->sign < 0) fl ^= 0x03;
+ if(b2->sign < 0) fl ^= 0x0c;
+ sum->sign = (int)(((fl|fl>>2)^fl>>4)<<30)>>31|1;
+ if(sum->sign < 0) fl ^= 0x30;
+ if(b2->top > b1->top){+ t = b1;
+ b1 = b2;
+ b2 = t;
+ fl = ((fl >> 2) & 0x03) | ((fl << 2) & 0x0c) | (fl & 0x30);
+ }
+ mpbits(sum, b1->top*Dbits+1);
+ dp1 = b1->p;
+ dp2 = b2->p;
+ dpo = sum->p;
+ c1 = fl & 1;
+ c2 = fl >> 2 & 1;
+ co = fl >> 4 & 1;
+ for(i = 0; i < b1->top; i++){+ d1 = dp1[i] - c1;
+ if(i < b2->top)
+ d2 = dp2[i] - c2;
+ else
+ d2 = 0;
+ if(d1 != (mpdigit)-1) c1 = 0;
+ if(d2 != (mpdigit)-1) c2 = 0;
+ if((fl & 2) != 0) d1 ^= -1;
+ if((fl & 8) != 0) d2 ^= -1;
+ d = d1 | d2;
+ if((fl & 32) != 0) d ^= -1;
+ d += co;
+ if(d != 0) co = 0;
+ dpo[i] = d;
+ }
+ sum->top = i;
+ if(co)
+ dpo[sum->top++] = co;
+ mpnorm(sum);
+}
+
+void
+mpor(mpint *b1, mpint *b2, mpint *sum)
+{+ mplogic(b1, b2, sum, 0);
+}
+
+void
+mpand(mpint *b1, mpint *b2, mpint *sum)
+{+ mplogic(b1, b2, sum, 0x2a);
+}
+
+void
+mpbic(mpint *b1, mpint *b2, mpint *sum)
+{+ mplogic(b1, b2, sum, 0x22);
+}
+
+void
+mpnot(mpint *b, mpint *r)
+{+ mpadd(b, mpone, r);
+ if(r->top != 0)
+ r->sign ^= -2;
+}
+
+void
+mpxor(mpint *b1, mpint *b2, mpint *sum)
+{+ mpint *t;
+ mpdigit *dp1, *dp2, *dpo, d1, d2, d;
+ int c1, c2, co;
+ int i, fl;
+
+ assert(((b1->flags | b2->flags | sum->flags) & MPtimesafe) == 0);
+ if(b2->top > b1->top){+ t = b1;
+ b1 = b2;
+ b2 = t;
+ }
+ fl = (b1->sign & 10) ^ (b2->sign & 12);
+ sum->sign = (int)(fl << 28) >> 31 | 1;
+ mpbits(sum, b1->top*Dbits+1);
+ dp1 = b1->p;
+ dp2 = b2->p;
+ dpo = sum->p;
+ c1 = fl >> 1 & 1;
+ c2 = fl >> 2 & 1;
+ co = fl >> 3 & 1;
+ for(i = 0; i < b1->top; i++){+ d1 = dp1[i] - c1;
+ if(i < b2->top)
+ d2 = dp2[i] - c2;
+ else
+ d2 = 0;
+ if(d1 != (mpdigit)-1) c1 = 0;
+ if(d2 != (mpdigit)-1) c2 = 0;
+ d = d1 ^ d2;
+ d += co;
+ if(d != 0) co = 0;
+ dpo[i] = d;
+ }
+ sum->top = i;
+ if(co)
+ dpo[sum->top++] = co;
+ mpnorm(sum);
+}
+
+void
+mptrunc(mpint *b, int n, mpint *r)
+{+ int d, m, i, c;
+
+ assert(((b->flags | r->flags) & MPtimesafe) == 0);
+ mpbits(r, n);
+ r->top = DIGITS(n);
+ d = n / Dbits;
+ m = n % Dbits;
+ if(b->sign == -1){+ c = 1;
+ for(i = 0; i < r->top; i++){+ if(i < b->top)
+ r->p[i] = ~(b->p[i] - c);
+ else
+ r->p[i] = -1;
+ if(r->p[i] != 0)
+ c = 0;
+ }
+ if(m != 0)
+ r->p[d] &= (1<<m) - 1;
+ }else if(b->sign == 1){+ if(d >= b->top){+ mpassign(b, r);
+ mpnorm(r);
+ return;
+ }
+ if(b != r)
+ for(i = 0; i < d; i++)
+ r->p[i] = b->p[i];
+ if(m != 0)
+ r->p[d] = b->p[d] & ((1<<m)-1);
+ }
+ r->sign = 1;
+ mpnorm(r);
+}
+
+void
+mpxtend(mpint *b, int n, mpint *r)
+{+ int d, m, c, i;
+
+ d = (n - 1) / Dbits;
+ m = (n - 1) % Dbits;
+ if(d >= b->top){+ mpassign(b, r);
+ return;
+ }
+ mptrunc(b, n, r);
+ mpbits(r, n);
+ if((r->p[d] & 1<<m) == 0){+ mpnorm(r);
+ return;
+ }
+ r->p[d] |= -(1<<m);
+ r->sign = -1;
+ c = 1;
+ for(i = 0; i < r->top; i++){+ r->p[i] = ~(r->p[i] - c);
+ if(r->p[i] != 0)
+ c = 0;
+ }
+ mpnorm(r);
+}
+
+void
+mpasr(mpint *b, int n, mpint *r)
+{+ if(b->sign > 0 || n <= 0){+ mpright(b, n, r);
+ return;
+ }
+ mpadd(b, mpone, r);
+ mpright(r, n, r);
+ mpsub(r, mpone, r);
+}
--- /dev/null
+++ b/libmp/mpmod.c
@@ -1,0 +1,20 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+void
+mpmod(mpint *x, mpint *n, mpint *r)
+{+ int sign;
+ mpint *ns;
+
+ sign = x->sign;
+ ns = sign < 0 && n == r ? mpcopy(n) : n;
+ if((n->flags & MPfield) == 0
+ || ((Mfield*)n)->reduce((Mfield*)n, x, r) != 0)
+ mpdiv(x, n, nil, r);
+ if(sign < 0){+ mpmagsub(ns, r, r);
+ if(ns != n) mpfree(ns);
+ }
+}
--- /dev/null
+++ b/libmp/mpmodop.c
@@ -1,0 +1,95 @@
+#include "os.h"
+#include <mp.h>
+
+/* operands need to have m->top+1 digits of space and satisfy 0 ≤ a ≤ m-1 */
+static mpint*
+modarg(mpint *a, mpint *m)
+{+ if(a->size <= m->top || a->sign < 0 || mpmagcmp(a, m) >= 0){+ a = mpcopy(a);
+ mpmod(a, m, a);
+ mpbits(a, Dbits*(m->top+1));
+ a->top = m->top;
+ } else if(a->top < m->top){+ memset(&a->p[a->top], 0, (m->top - a->top)*Dbytes);
+ }
+ return a;
+}
+
+void
+mpmodadd(mpint *b1, mpint *b2, mpint *m, mpint *sum)
+{+ mpint *a, *b;
+ mpdigit d;
+ int i, j;
+
+ a = modarg(b1, m);
+ b = modarg(b2, m);
+
+ sum->flags |= (a->flags | b->flags) & MPtimesafe;
+ mpbits(sum, Dbits*2*(m->top+1));
+
+ mpvecadd(a->p, m->top, b->p, m->top, sum->p);
+ mpvecsub(sum->p, m->top+1, m->p, m->top, sum->p+m->top+1);
+
+ d = sum->p[2*m->top+1];
+ for(i = 0, j = m->top+1; i < m->top; i++, j++)
+ sum->p[i] = (sum->p[i] & d) | (sum->p[j] & ~d);
+
+ sum->top = m->top;
+ sum->sign = 1;
+ mpnorm(sum);
+
+ if(a != b1)
+ mpfree(a);
+ if(b != b2)
+ mpfree(b);
+}
+
+void
+mpmodsub(mpint *b1, mpint *b2, mpint *m, mpint *diff)
+{+ mpint *a, *b;
+ mpdigit d;
+ int i, j;
+
+ a = modarg(b1, m);
+ b = modarg(b2, m);
+
+ diff->flags |= (a->flags | b->flags) & MPtimesafe;
+ mpbits(diff, Dbits*2*(m->top+1));
+
+ a->p[m->top] = 0;
+ mpvecsub(a->p, m->top+1, b->p, m->top, diff->p);
+ mpvecadd(diff->p, m->top, m->p, m->top, diff->p+m->top+1);
+
+ d = ~diff->p[m->top];
+ for(i = 0, j = m->top+1; i < m->top; i++, j++)
+ diff->p[i] = (diff->p[i] & d) | (diff->p[j] & ~d);
+
+ diff->top = m->top;
+ diff->sign = 1;
+ mpnorm(diff);
+
+ if(a != b1)
+ mpfree(a);
+ if(b != b2)
+ mpfree(b);
+}
+
+void
+mpmodmul(mpint *b1, mpint *b2, mpint *m, mpint *prod)
+{+ mpint *a, *b;
+
+ a = modarg(b1, m);
+ b = modarg(b2, m);
+
+ mpmul(a, b, prod);
+ mpmod(prod, m, prod);
+
+ if(a != b1)
+ mpfree(a);
+ if(b != b2)
+ mpfree(b);
+}
--- /dev/null
+++ b/libmp/mpmul.c
@@ -1,0 +1,176 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+//
+// from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
+//
+// mpvecmul is an assembly language routine that performs the inner
+// loop.
+//
+// the karatsuba trade off is set empiricly by measuring the algs on
+// a 400 MHz Pentium II.
+//
+
+// karatsuba like (see knuth pg 258)
+// prereq: p is already zeroed
+static void
+mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{+ mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod;
+ int u0len, u1len, v0len, v1len, reslen;
+ int sign, n;
+
+ // divide each piece in half
+ n = alen/2;
+ if(alen&1)
+ n++;
+ u0len = n;
+ u1len = alen-n;
+ if(blen > n){+ v0len = n;
+ v1len = blen-n;
+ } else {+ v0len = blen;
+ v1len = 0;
+ }
+ u0 = a;
+ u1 = a + u0len;
+ v0 = b;
+ v1 = b + v0len;
+
+ // room for the partial products
+ t = mallocz(Dbytes*5*(2*n+1), 1);
+ if(t == nil)
+ sysfatal("mpkaratsuba: %r");+ u0v0 = t;
+ u1v1 = t + (2*n+1);
+ diffprod = t + 2*(2*n+1);
+ res = t + 3*(2*n+1);
+ reslen = 4*n+1;
+
+ // t[0] = (u1-u0)
+ sign = 1;
+ if(mpveccmp(u1, u1len, u0, u0len) < 0){+ sign = -1;
+ mpvecsub(u0, u0len, u1, u1len, u0v0);
+ } else
+ mpvecsub(u1, u1len, u0, u1len, u0v0);
+
+ // t[1] = (v0-v1)
+ if(mpveccmp(v0, v0len, v1, v1len) < 0){+ sign *= -1;
+ mpvecsub(v1, v1len, v0, v1len, u1v1);
+ } else
+ mpvecsub(v0, v0len, v1, v1len, u1v1);
+
+ // t[4:5] = (u1-u0)*(v0-v1)
+ mpvecmul(u0v0, u0len, u1v1, v0len, diffprod);
+
+ // t[0:1] = u1*v1
+ memset(t, 0, 2*(2*n+1)*Dbytes);
+ if(v1len > 0)
+ mpvecmul(u1, u1len, v1, v1len, u1v1);
+
+ // t[2:3] = u0v0
+ mpvecmul(u0, u0len, v0, v0len, u0v0);
+
+ // res = u0*v0<<n + u0*v0
+ mpvecadd(res, reslen, u0v0, u0len+v0len, res);
+ mpvecadd(res+n, reslen-n, u0v0, u0len+v0len, res+n);
+
+ // res += u1*v1<<n + u1*v1<<2*n
+ if(v1len > 0){+ mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n);
+ mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n);
+ }
+
+ // res += (u1-u0)*(v0-v1)<<n
+ if(sign < 0)
+ mpvecsub(res+n, reslen-n, diffprod, u0len+v0len, res+n);
+ else
+ mpvecadd(res+n, reslen-n, diffprod, u0len+v0len, res+n);
+ memmove(p, res, (alen+blen)*Dbytes);
+
+ free(t);
+}
+
+#define KARATSUBAMIN 32
+
+void
+mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{+ int i;
+ mpdigit d;
+ mpdigit *t;
+
+ // both mpvecdigmuladd and karatsuba are fastest when a is the longer vector
+ if(alen < blen){+ i = alen;
+ alen = blen;
+ blen = i;
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ if(alen >= KARATSUBAMIN && blen > 1){+ // O(n^1.585)
+ mpkaratsuba(a, alen, b, blen, p);
+ } else {+ // O(n^2)
+ for(i = 0; i < blen; i++){+ d = b[i];
+ if(d != 0)
+ mpvecdigmuladd(a, alen, d, &p[i]);
+ }
+ }
+}
+
+void
+mpvectsmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
+{+ int i;
+ mpdigit *t;
+
+ if(alen < blen){+ i = alen;
+ alen = blen;
+ blen = i;
+ t = a;
+ a = b;
+ b = t;
+ }
+ if(blen == 0)
+ return;
+ for(i = 0; i < blen; i++)
+ mpvecdigmuladd(a, alen, b[i], &p[i]);
+}
+
+void
+mpmul(mpint *b1, mpint *b2, mpint *prod)
+{+ mpint *oprod;
+
+ oprod = prod;
+ if(prod == b1 || prod == b2){+ prod = mpnew(0);
+ prod->flags = oprod->flags;
+ }
+ prod->flags |= (b1->flags | b2->flags) & MPtimesafe;
+
+ prod->top = 0;
+ mpbits(prod, (b1->top+b2->top+1)*Dbits);
+ if(prod->flags & MPtimesafe)
+ mpvectsmul(b1->p, b1->top, b2->p, b2->top, prod->p);
+ else
+ mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
+ prod->top = b1->top+b2->top+1;
+ prod->sign = b1->sign*b2->sign;
+ mpnorm(prod);
+
+ if(oprod != prod){+ mpassign(prod, oprod);
+ mpfree(prod);
+ }
+}
--- /dev/null
+++ b/libmp/mpnrand.c
@@ -1,0 +1,23 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/* return uniform random [0..n-1] */
+mpint*
+mpnrand(mpint *n, void (*gen)(uchar*, int), mpint *b)
+{+ int bits;
+
+ bits = mpsignif(n);
+ if(bits == 0)
+ abort();
+ if(b == nil){+ b = mpnew(bits);
+ setmalloctag(b, getcallerpc(&n));
+ }
+ do {+ mprand(bits, gen, b);
+ } while(mpmagcmp(b, n) >= 0);
+
+ return b;
+}
--- /dev/null
+++ b/libmp/mprand.c
@@ -1,0 +1,25 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+mpint*
+mprand(int bits, void (*gen)(uchar*, int), mpint *b)
+{+ mpdigit mask;
+
+ if(b == nil){+ b = mpnew(bits);
+ setmalloctag(b, getcallerpc(&bits));
+ }else
+ mpbits(b, bits);
+
+ b->sign = 1;
+ b->top = DIGITS(bits);
+ (*gen)((uchar*)b->p, b->top*Dbytes);
+
+ mask = ((mpdigit)1 << (bits%Dbits))-1;
+ if(mask != 0)
+ b->p[b->top-1] &= mask;
+
+ return mpnorm(b);
+}
--- /dev/null
+++ b/libmp/mpright.c
@@ -1,0 +1,57 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = b >> shift
+void
+mpright(mpint *b, int shift, mpint *res)
+{+ int d, l, r, i;
+ mpdigit this, last;
+
+ res->sign = b->sign;
+ if(b->top==0){+ res->top = 0;
+ return;
+ }
+
+ // a negative right shift is a left shift
+ if(shift < 0){+ mpleft(b, -shift, res);
+ return;
+ }
+
+ if(res != b)
+ mpbits(res, b->top*Dbits - shift);
+ else if(shift == 0)
+ return;
+
+ d = shift/Dbits;
+ r = shift - d*Dbits;
+ l = Dbits - r;
+
+ // shift all the bits out == zero
+ if(d>=b->top){+ res->sign = 1;
+ res->top = 0;
+ return;
+ }
+
+ // special case digit shifts
+ if(r == 0){+ for(i = 0; i < b->top-d; i++)
+ res->p[i] = b->p[i+d];
+ } else {+ last = b->p[d];
+ for(i = 0; i < b->top-d-1; i++){+ this = b->p[i+d+1];
+ res->p[i] = (this<<l) | (last>>r);
+ last = this;
+ }
+ res->p[i++] = last>>r;
+ }
+
+ res->top = i;
+ res->flags |= b->flags & MPtimesafe;
+ mpnorm(res);
+}
--- /dev/null
+++ b/libmp/mpsel.c
@@ -1,0 +1,42 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// res = s != 0 ? b1 : b2
+void
+mpsel(int s, mpint *b1, mpint *b2, mpint *res)
+{+ mpdigit d;
+ int n, m, i;
+
+ res->flags |= (b1->flags | b2->flags) & MPtimesafe;
+ if((res->flags & MPtimesafe) == 0){+ mpassign(s ? b1 : b2, res);
+ return;
+ }
+ res->flags &= ~MPnorm;
+
+ n = b1->top;
+ m = b2->top;
+ mpbits(res, Dbits*(n >= m ? n : m));
+ res->top = n >= m ? n : m;
+
+ s = ((-s^s)|s)>>(sizeof(s)*8-1);
+ res->sign = (b1->sign & s) | (b2->sign & ~s);
+
+ d = -((mpdigit)s & 1);
+
+ i = 0;
+ while(i < n && i < m){+ res->p[i] = (b1->p[i] & d) | (b2->p[i] & ~d);
+ i++;
+ }
+ while(i < n){+ res->p[i] = b1->p[i] & d;
+ i++;
+ }
+ while(i < m){+ res->p[i] = b2->p[i] & ~d;
+ i++;
+ }
+}
--- /dev/null
+++ b/libmp/mpsub.c
@@ -1,0 +1,56 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// diff = abs(b1) - abs(b2), i.e., subtract the magnitudes
+void
+mpmagsub(mpint *b1, mpint *b2, mpint *diff)
+{+ int n, m, sign;
+ mpint *t;
+
+ // get the sizes right
+ if(mpmagcmp(b1, b2) < 0){+ assert(((b1->flags | b2->flags | diff->flags) & MPtimesafe) == 0);
+ sign = -1;
+ t = b1;
+ b1 = b2;
+ b2 = t;
+ } else {+ diff->flags |= (b1->flags | b2->flags) & MPtimesafe;
+ sign = 1;
+ }
+ n = b1->top;
+ m = b2->top;
+ if(m == 0){+ mpassign(b1, diff);
+ diff->sign = sign;
+ return;
+ }
+ mpbits(diff, n*Dbits);
+
+ mpvecsub(b1->p, n, b2->p, m, diff->p);
+ diff->sign = sign;
+ diff->top = n;
+ mpnorm(diff);
+}
+
+// diff = b1 - b2
+void
+mpsub(mpint *b1, mpint *b2, mpint *diff)
+{+ int sign;
+
+ if(b1->sign != b2->sign){+ assert(((b1->flags | b2->flags | diff->flags) & MPtimesafe) == 0);
+ sign = b1->sign;
+ mpmagadd(b1, b2, diff);
+ diff->sign = sign;
+ return;
+ }
+
+ sign = b1->sign;
+ mpmagsub(b1, b2, diff);
+ if(diff->top != 0)
+ diff->sign *= sign;
+}
--- /dev/null
+++ b/libmp/mptobe.c
@@ -1,0 +1,32 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// convert an mpint into a big endian byte array (most significant byte first; left adjusted)
+// return number of bytes converted
+// if p == nil, allocate and result array
+int
+mptobe(mpint *b, uchar *p, uint n, uchar **pp)
+{+ int m;
+
+ m = (mpsignif(b)+7)/8;
+ if(m == 0)
+ m++;
+ if(p == nil){+ n = m;
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("mptobe: %r");+ setmalloctag(p, getcallerpc(&b));
+ } else {+ if(n < m)
+ return -1;
+ if(n > m)
+ memset(p+m, 0, n-m);
+ }
+ if(pp != nil)
+ *pp = p;
+ mptober(b, p, m);
+ return m;
+}
--- /dev/null
+++ b/libmp/mptober.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+void
+mptober(mpint *b, uchar *p, int n)
+{+ int i, j, m;
+ mpdigit x;
+
+ memset(p, 0, n);
+
+ p += n;
+ m = b->top*Dbytes;
+ if(m < n)
+ n = m;
+
+ i = 0;
+ while(n >= Dbytes){+ n -= Dbytes;
+ x = b->p[i++];
+ for(j = 0; j < Dbytes; j++){+ *--p = x;
+ x >>= 8;
+ }
+ }
+ if(n > 0){+ x = b->p[i];
+ for(j = 0; j < n; j++){+ *--p = x;
+ x >>= 8;
+ }
+ }
+}
--- /dev/null
+++ b/libmp/mptoi.c
@@ -1,0 +1,44 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ * this code assumes that mpdigit is at least as
+ * big as an int.
+ */
+
+mpint*
+itomp(int i, mpint *b)
+{+ if(b == nil){+ b = mpnew(0);
+ setmalloctag(b, getcallerpc(&i));
+ }
+ b->sign = (i >> (sizeof(i)*8 - 1)) | 1;
+ i *= b->sign;
+ *b->p = i;
+ b->top = 1;
+ return mpnorm(b);
+}
+
+int
+mptoi(mpint *b)
+{+ uint x;
+
+ if(b->top==0)
+ return 0;
+ x = *b->p;
+ if(b->sign > 0){+ if(b->top > 1 || (x > MAXINT))
+ x = (int)MAXINT;
+ else
+ x = (int)x;
+ } else {+ if(b->top > 1 || x > MAXINT+1)
+ x = (int)MININT;
+ else
+ x = -(int)x;
+ }
+ return x;
+}
--- /dev/null
+++ b/libmp/mptole.c
@@ -1,0 +1,28 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// convert an mpint into a little endian byte array (least significant byte first)
+// return number of bytes converted
+// if p == nil, allocate and result array
+int
+mptole(mpint *b, uchar *p, uint n, uchar **pp)
+{+ int m;
+
+ m = (mpsignif(b)+7)/8;
+ if(m == 0)
+ m++;
+ if(p == nil){+ n = m;
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("mptole: %r");+ setmalloctag(p, getcallerpc(&b));
+ } else if(n < m)
+ return -1;
+ if(pp != nil)
+ *pp = p;
+ mptolel(b, p, n);
+ return m;
+}
--- /dev/null
+++ b/libmp/mptolel.c
@@ -1,0 +1,33 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+void
+mptolel(mpint *b, uchar *p, int n)
+{+ int i, j, m;
+ mpdigit x;
+
+ memset(p, 0, n);
+
+ m = b->top*Dbytes;
+ if(m < n)
+ n = m;
+
+ i = 0;
+ while(n >= Dbytes){+ n -= Dbytes;
+ x = b->p[i++];
+ for(j = 0; j < Dbytes; j++){+ *p++ = x;
+ x >>= 8;
+ }
+ }
+ if(n > 0){+ x = b->p[i];
+ for(j = 0; j < n; j++){+ *p++ = x;
+ x >>= 8;
+ }
+ }
+}
--- /dev/null
+++ b/libmp/mptoui.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+/*
+ * this code assumes that mpdigit is at least as
+ * big as an int.
+ */
+
+mpint*
+uitomp(uint i, mpint *b)
+{+ if(b == nil){+ b = mpnew(0);
+ setmalloctag(b, getcallerpc(&i));
+ }
+ *b->p = i;
+ b->top = 1;
+ b->sign = 1;
+ return mpnorm(b);
+}
+
+uint
+mptoui(mpint *b)
+{+ uint x;
+
+ x = *b->p;
+ if(b->sign < 0)
+ x = 0;
+ else if(b->top > 1 || (sizeof(mpdigit) > sizeof(uint) && x > MAXUINT))
+ x = MAXUINT;
+ return x;
+}
--- /dev/null
+++ b/libmp/mptouv.c
@@ -1,0 +1,47 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+#define VLDIGITS (sizeof(vlong)/sizeof(mpdigit))
+
+/*
+ * this code assumes that a vlong is an integral number of
+ * mpdigits long.
+ */
+mpint*
+uvtomp(uvlong v, mpint *b)
+{+ int s;
+
+ if(b == nil){+ b = mpnew(VLDIGITS*Dbits);
+ setmalloctag(b, getcallerpc(&v));
+ }else
+ mpbits(b, VLDIGITS*Dbits);
+ b->sign = 1;
+ for(s = 0; s < VLDIGITS; s++){+ b->p[s] = v;
+ v >>= sizeof(mpdigit)*8;
+ }
+ b->top = s;
+ return mpnorm(b);
+}
+
+uvlong
+mptouv(mpint *b)
+{+ uvlong v;
+ int s;
+
+ if(b->top == 0 || b->sign < 0)
+ return 0LL;
+
+ if(b->top > VLDIGITS)
+ return -1LL;
+
+ v = 0ULL;
+ for(s = 0; s < b->top; s++)
+ v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
+
+ return v;
+}
--- /dev/null
+++ b/libmp/mptov.c
@@ -1,0 +1,63 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+#define VLDIGITS (sizeof(vlong)/sizeof(mpdigit))
+
+/*
+ * this code assumes that a vlong is an integral number of
+ * mpdigits long.
+ */
+mpint*
+vtomp(vlong v, mpint *b)
+{+ int s;
+ uvlong uv;
+
+ if(b == nil){+ b = mpnew(VLDIGITS*Dbits);
+ setmalloctag(b, getcallerpc(&v));
+ }else
+ mpbits(b, VLDIGITS*Dbits);
+ b->sign = (v >> (sizeof(v)*8 - 1)) | 1;
+ uv = v * b->sign;
+ for(s = 0; s < VLDIGITS; s++){+ b->p[s] = uv;
+ uv >>= sizeof(mpdigit)*8;
+ }
+ b->top = s;
+ return mpnorm(b);
+}
+
+vlong
+mptov(mpint *b)
+{+ uvlong v;
+ int s;
+
+ if(b->top == 0)
+ return 0LL;
+
+ if(b->top > VLDIGITS){+ if(b->sign > 0)
+ return (vlong)MAXVLONG;
+ else
+ return (vlong)MINVLONG;
+ }
+
+ v = 0ULL;
+ for(s = 0; s < b->top; s++)
+ v |= (uvlong)b->p[s]<<(s*sizeof(mpdigit)*8);
+
+ if(b->sign > 0){+ if(v > MAXVLONG)
+ v = MAXVLONG;
+ } else {+ if(v > MINVLONG)
+ v = MINVLONG;
+ else
+ v = -(vlong)v;
+ }
+
+ return (vlong)v;
+}
--- /dev/null
+++ b/libmp/mpvecadd.c
@@ -1,0 +1,35 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// prereq: alen >= blen, sum has at least blen+1 digits
+void
+mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum)
+{+ int i, carry;
+ mpdigit x, y;
+
+ carry = 0;
+ for(i = 0; i < blen; i++){+ x = *a++;
+ y = *b++;
+ x += carry;
+ if(x < carry)
+ carry = 1;
+ else
+ carry = 0;
+ x += y;
+ if(x < y)
+ carry++;
+ *sum++ = x;
+ }
+ for(; i < alen; i++){+ x = *a++ + carry;
+ if(x < carry)
+ carry = 1;
+ else
+ carry = 0;
+ *sum++ = x;
+ }
+ *sum = carry;
+}
--- /dev/null
+++ b/libmp/mpveccmp.c
@@ -1,0 +1,27 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+int
+mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen)
+{+ mpdigit x;
+
+ while(alen > blen)
+ if(a[--alen] != 0)
+ return 1;
+ while(blen > alen)
+ if(b[--blen] != 0)
+ return -1;
+ while(alen > 0){+ --alen;
+ x = a[alen] - b[alen];
+ if(x == 0)
+ continue;
+ if(x > a[alen])
+ return -1;
+ else
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+++ b/libmp/mpvecdigmuladd.c
@@ -1,0 +1,103 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+#define LO(x) ((x) & ((1<<(Dbits/2))-1))
+#define HI(x) ((x) >> (Dbits/2))
+
+static void
+mpdigmul(mpdigit a, mpdigit b, mpdigit *p)
+{+ mpdigit x, ah, al, bh, bl, p1, p2, p3, p4;
+ int carry;
+
+ // half digits
+ ah = HI(a);
+ al = LO(a);
+ bh = HI(b);
+ bl = LO(b);
+
+ // partial products
+ p1 = ah*bl;
+ p2 = bh*al;
+ p3 = bl*al;
+ p4 = ah*bh;
+
+ // p = ((p1+p2)<<(Dbits/2)) + (p4<<Dbits) + p3
+ carry = 0;
+ x = p1<<(Dbits/2);
+ p3 += x;
+ if(p3 < x)
+ carry++;
+ x = p2<<(Dbits/2);
+ p3 += x;
+ if(p3 < x)
+ carry++;
+ p4 += carry + HI(p1) + HI(p2); // can't carry out of the high digit
+ p[0] = p3;
+ p[1] = p4;
+}
+
+// prereq: p must have room for n+1 digits
+void
+mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p)
+{+ int i;
+ mpdigit carry, x, y, part[2];
+
+ carry = 0;
+ part[1] = 0;
+ for(i = 0; i < n; i++){+ x = part[1] + carry;
+ if(x < carry)
+ carry = 1;
+ else
+ carry = 0;
+ y = *p;
+ mpdigmul(*b++, m, part);
+ x += part[0];
+ if(x < part[0])
+ carry++;
+ x += y;
+ if(x < y)
+ carry++;
+ *p++ = x;
+ }
+ *p = part[1] + carry;
+}
+
+// prereq: p must have room for n+1 digits
+int
+mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p)
+{+ int i;
+ mpdigit x, y, part[2], borrow;
+
+ borrow = 0;
+ part[1] = 0;
+ for(i = 0; i < n; i++){+ x = *p;
+ y = x - borrow;
+ if(y > x)
+ borrow = 1;
+ else
+ borrow = 0;
+ x = part[1];
+ mpdigmul(*b++, m, part);
+ x += part[0];
+ if(x < part[0])
+ borrow++;
+ x = y - x;
+ if(x > y)
+ borrow++;
+ *p++ = x;
+ }
+
+ x = *p;
+ y = x - borrow - part[1];
+ *p = y;
+ if(y > x)
+ return -1;
+ else
+ return 1;
+}
--- /dev/null
+++ b/libmp/mpvecsub.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+// prereq: a >= b, alen >= blen, diff has at least alen digits
+void
+mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff)
+{+ int i, borrow;
+ mpdigit x, y;
+
+ borrow = 0;
+ for(i = 0; i < blen; i++){+ x = *a++;
+ y = *b++;
+ y += borrow;
+ if(y < borrow)
+ borrow = 1;
+ else
+ borrow = 0;
+ if(x < y)
+ borrow++;
+ *diff++ = x - y;
+ }
+ for(; i < alen; i++){+ x = *a++;
+ y = x - borrow;
+ if(y > x)
+ borrow = 1;
+ else
+ borrow = 0;
+ *diff++ = y;
+ }
+}
--- /dev/null
+++ b/libmp/mpvectscmp.c
@@ -1,0 +1,34 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+int
+mpvectscmp(mpdigit *a, int alen, mpdigit *b, int blen)
+{+ mpdigit x, y, z, v;
+ int m, p;
+
+ if(alen > blen){+ v = 0;
+ while(alen > blen)
+ v |= a[--alen];
+ m = p = ((-v^v)|v)>>(Dbits-1);
+ } else if(blen > alen){+ v = 0;
+ while(blen > alen)
+ v |= b[--blen];
+ m = ((-v^v)|v)>>(Dbits-1);
+ p = m^1;
+ } else
+ m = p = 0;
+ while(alen-- > 0){+ x = a[alen];
+ y = b[alen];
+ z = x - y;
+ x = ~x;
+ v = (((-z^z)|z)>>(Dbits-1)) & ~m;
+ p = ((~((x&y)|(x&z)|(y&z))>>(Dbits-1)) & v) | (p & ~v);
+ m |= v;
+ }
+ return (p-m) | m;
+}
--- /dev/null
+++ b/libmp/os.h
@@ -1,0 +1,3 @@
+#include <u.h>
+#include <libc.h>
+
--- /dev/null
+++ b/libmp/reduce
@@ -1,0 +1,16 @@
+O=$1
+shift
+objtype=$1
+shift
+
+ls -p ../$objtype/*.[cs] >[2]/dev/null | sed 's/..$//' > /tmp/reduce.$pid
+#
+# if empty directory, just return the input files
+#
+if (! ~ $status '|') {+ echo $*
+ rm /tmp/reduce.$pid
+ exit 0
+}
+echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' '
+rm /tmp/reduce.$pid
--- /dev/null
+++ b/libmp/strtomp.c
@@ -1,0 +1,206 @@
+#include "os.h"
+#include <mp.h>
+#include "dat.h"
+
+static char*
+frompow2(char *a, mpint *b, int s)
+{+ char *p, *next;
+ mpdigit x;
+ int i;
+
+ i = 1<<s;
+ for(p = a; (dec16chr(*p) & 255) < i; p++)
+ ;
+
+ mpbits(b, (p-a)*s);
+ b->top = 0;
+ next = p;
+
+ while(p > a){+ x = 0;
+ for(i = 0; i < Dbits; i += s){+ if(p <= a)
+ break;
+ x |= dec16chr(*--p)<<i;
+ }
+ b->p[b->top++] = x;
+ }
+ return next;
+}
+
+static char*
+from8(char *a, mpint *b)
+{+ char *p, *next;
+ mpdigit x, y;
+ int i;
+
+ for(p = a; ((*p - '0') & 255) < 8; p++)
+ ;
+
+ mpbits(b, (p-a)*3);
+ b->top = 0;
+ next = p;
+
+ i = 0;
+ x = y = 0;
+ while(p > a){+ y = *--p - '0';
+ x |= y << i;
+ i += 3;
+ if(i >= Dbits){+Digout:
+ i -= Dbits;
+ b->p[b->top++] = x;
+ x = y >> (3-i);
+ }
+ }
+ if(i > 0)
+ goto Digout;
+
+ return next;
+}
+
+static ulong mppow10[] = {+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
+};
+
+static char*
+from10(char *a, mpint *b)
+{+ ulong x, y;
+ mpint *pow, *r;
+ int i;
+
+ pow = mpnew(0);
+ r = mpnew(0);
+
+ b->top = 0;
+ for(;;){+ // do a billion at a time in native arithmetic
+ x = 0;
+ for(i = 0; i < 9; i++){+ y = *a - '0';
+ if(y > 9)
+ break;
+ a++;
+ x *= 10;
+ x += y;
+ }
+ if(i == 0)
+ break;
+
+ // accumulate into mpint
+ uitomp(mppow10[i], pow);
+ uitomp(x, r);
+ mpmul(b, pow, b);
+ mpadd(b, r, b);
+ if(i < 9)
+ break;
+ }
+ mpfree(pow);
+ mpfree(r);
+ return a;
+}
+
+static char*
+fromdecx(char *a, mpint *b, int (*chr)(int), int (*dec)(uchar*, int, char*, int))
+{+ char *buf = a;
+ uchar *p;
+ int n, m;
+
+ b->top = 0;
+ for(; (*chr)(*a) >= 0; a++)
+ ;
+ n = a-buf;
+ if(n > 0){+ p = malloc(n);
+ if(p == nil)
+ sysfatal("malloc: %r");+ m = (*dec)(p, n, buf, n);
+ if(m > 0)
+ betomp(p, m, b);
+ free(p);
+ }
+ return a;
+}
+
+mpint*
+strtomp(char *a, char **pp, int base, mpint *b)
+{+ int sign;
+ char *e;
+
+ if(b == nil){+ b = mpnew(0);
+ setmalloctag(b, getcallerpc(&a));
+ }
+
+ while(*a==' ' || *a=='\t')
+ a++;
+
+ sign = 1;
+ for(;; a++){+ switch(*a){+ case '-':
+ sign *= -1;
+ continue;
+ }
+ break;
+ }
+
+ if(base == 0){+ base = 10;
+ if(a[0] == '0'){+ if(a[1] == 'x' || a[1] == 'X') {+ a += 2;
+ base = 16;
+ } else if(a[1] == 'b' || a[1] == 'B') {+ a += 2;
+ base = 2;
+ } else if(a[1] >= '0' && a[1] <= '7') {+ a++;
+ base = 8;
+ }
+ }
+ }
+
+ switch(base){+ case 2:
+ e = frompow2(a, b, 1);
+ break;
+ case 4:
+ e = frompow2(a, b, 2);
+ break;
+ case 8:
+ e = from8(a, b);
+ break;
+ case 10:
+ e = from10(a, b);
+ break;
+ case 16:
+ e = frompow2(a, b, 4);
+ break;
+ case 32:
+ e = fromdecx(a, b, dec32chr, dec32);
+ break;
+ case 64:
+ e = fromdecx(a, b, dec64chr, dec64);
+ break;
+ default:
+ abort();
+ return nil;
+ }
+
+ if(pp != nil)
+ *pp = e;
+
+ // if no characters parsed, there wasn't a number to convert
+ if(e == a)
+ return nil;
+
+ b->sign = sign;
+ return mpnorm(b);
+}
--- /dev/null
+++ b/libsec/Makefile
@@ -1,0 +1,28 @@
+ROOT=..
+include ../Make.config
+LIB=libsec.a
+
+OFILES=\
+ aes.$O aesni.$O aesCBC.$O aes_gcm.$O\
+ poly1305.$O chacha.$O chachablock.$O ccpoly.$O\
+ des.$O des3CBC.$O desmodes.$O\
+ ecc.$O jacobian.$O secp256k1.$O secp256r1.$O secp384r1.$O\
+ curve25519.$O curve25519_dh.$O\
+ genrandom.$O fastrand.$O nfastrand.$O prng.$O\
+ hmac.$O hkdf.$O pbkdf2.$O\
+ rsaalloc.$O rsadecrypt.$O rsaencrypt.$O\
+ dh.$O\
+ rc4.$O md5.$O md5block.$O\
+ sha1.$O sha2_128.$O sha2_64.$O\
+ sha1block.$O sha2block128.$O sha2block64.$O\
+ tlshand.$O x509.$O\
+ tsmemcmp.$O\
+
+default: $(LIB)
+$(LIB): $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
+%.$O: %.c
+ $(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libsec/aes.c
@@ -1,0 +1,1257 @@
+/*
+ * this code is derived from the following source,
+ * and modified to fit into the plan 9 libsec interface.
+ * most of the changes are confined to the top section,
+ * with the exception of converting Te4 and Td4 into u8 rather than u32 arrays.
+ *
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "os.h"
+#include <libsec.h>
+
+typedef uchar u8;
+typedef ulong u32;
+
+#define GETU32(pt) (((u32)(pt)[0]<<24) ^ ((u32)(pt)[1]<<16) ^ \
+ ((u32)(pt)[2]<< 8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { (ct)[0] = (u8)((st)>>24); (ct)[1] = (u8)((st)>>16); \+ (ct)[2] = (u8)((st)>> 8); (ct)[3] = (u8)(st); }
+
+#define FULL_UNROLL
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x]
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x]
+*/
+
+static u32 Te0[256] = {+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static u32 Te1[256] = {+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static u32 Te2[256] = {+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static u32 Te3[256] = {+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static u8 Te4[256] = {+ 0x63U, 0x7cU, 0x77U, 0x7bU,
+ 0xf2U, 0x6bU, 0x6fU, 0xc5U,
+ 0x30U, 0x01U, 0x67U, 0x2bU,
+ 0xfeU, 0xd7U, 0xabU, 0x76U,
+ 0xcaU, 0x82U, 0xc9U, 0x7dU,
+ 0xfaU, 0x59U, 0x47U, 0xf0U,
+ 0xadU, 0xd4U, 0xa2U, 0xafU,
+ 0x9cU, 0xa4U, 0x72U, 0xc0U,
+ 0xb7U, 0xfdU, 0x93U, 0x26U,
+ 0x36U, 0x3fU, 0xf7U, 0xccU,
+ 0x34U, 0xa5U, 0xe5U, 0xf1U,
+ 0x71U, 0xd8U, 0x31U, 0x15U,
+ 0x04U, 0xc7U, 0x23U, 0xc3U,
+ 0x18U, 0x96U, 0x05U, 0x9aU,
+ 0x07U, 0x12U, 0x80U, 0xe2U,
+ 0xebU, 0x27U, 0xb2U, 0x75U,
+ 0x09U, 0x83U, 0x2cU, 0x1aU,
+ 0x1bU, 0x6eU, 0x5aU, 0xa0U,
+ 0x52U, 0x3bU, 0xd6U, 0xb3U,
+ 0x29U, 0xe3U, 0x2fU, 0x84U,
+ 0x53U, 0xd1U, 0x00U, 0xedU,
+ 0x20U, 0xfcU, 0xb1U, 0x5bU,
+ 0x6aU, 0xcbU, 0xbeU, 0x39U,
+ 0x4aU, 0x4cU, 0x58U, 0xcfU,
+ 0xd0U, 0xefU, 0xaaU, 0xfbU,
+ 0x43U, 0x4dU, 0x33U, 0x85U,
+ 0x45U, 0xf9U, 0x02U, 0x7fU,
+ 0x50U, 0x3cU, 0x9fU, 0xa8U,
+ 0x51U, 0xa3U, 0x40U, 0x8fU,
+ 0x92U, 0x9dU, 0x38U, 0xf5U,
+ 0xbcU, 0xb6U, 0xdaU, 0x21U,
+ 0x10U, 0xffU, 0xf3U, 0xd2U,
+ 0xcdU, 0x0cU, 0x13U, 0xecU,
+ 0x5fU, 0x97U, 0x44U, 0x17U,
+ 0xc4U, 0xa7U, 0x7eU, 0x3dU,
+ 0x64U, 0x5dU, 0x19U, 0x73U,
+ 0x60U, 0x81U, 0x4fU, 0xdcU,
+ 0x22U, 0x2aU, 0x90U, 0x88U,
+ 0x46U, 0xeeU, 0xb8U, 0x14U,
+ 0xdeU, 0x5eU, 0x0bU, 0xdbU,
+ 0xe0U, 0x32U, 0x3aU, 0x0aU,
+ 0x49U, 0x06U, 0x24U, 0x5cU,
+ 0xc2U, 0xd3U, 0xacU, 0x62U,
+ 0x91U, 0x95U, 0xe4U, 0x79U,
+ 0xe7U, 0xc8U, 0x37U, 0x6dU,
+ 0x8dU, 0xd5U, 0x4eU, 0xa9U,
+ 0x6cU, 0x56U, 0xf4U, 0xeaU,
+ 0x65U, 0x7aU, 0xaeU, 0x08U,
+ 0xbaU, 0x78U, 0x25U, 0x2eU,
+ 0x1cU, 0xa6U, 0xb4U, 0xc6U,
+ 0xe8U, 0xddU, 0x74U, 0x1fU,
+ 0x4bU, 0xbdU, 0x8bU, 0x8aU,
+ 0x70U, 0x3eU, 0xb5U, 0x66U,
+ 0x48U, 0x03U, 0xf6U, 0x0eU,
+ 0x61U, 0x35U, 0x57U, 0xb9U,
+ 0x86U, 0xc1U, 0x1dU, 0x9eU,
+ 0xe1U, 0xf8U, 0x98U, 0x11U,
+ 0x69U, 0xd9U, 0x8eU, 0x94U,
+ 0x9bU, 0x1eU, 0x87U, 0xe9U,
+ 0xceU, 0x55U, 0x28U, 0xdfU,
+ 0x8cU, 0xa1U, 0x89U, 0x0dU,
+ 0xbfU, 0xe6U, 0x42U, 0x68U,
+ 0x41U, 0x99U, 0x2dU, 0x0fU,
+ 0xb0U, 0x54U, 0xbbU, 0x16U,
+};
+static u32 Td0[256] = {+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static u32 Td1[256] = {+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static u32 Td2[256] = {+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static u32 Td3[256] = {+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static u8 Td4[256] = {+ 0x52U, 0x09U, 0x6aU, 0xd5U,
+ 0x30U, 0x36U, 0xa5U, 0x38U,
+ 0xbfU, 0x40U, 0xa3U, 0x9eU,
+ 0x81U, 0xf3U, 0xd7U, 0xfbU,
+ 0x7cU, 0xe3U, 0x39U, 0x82U,
+ 0x9bU, 0x2fU, 0xffU, 0x87U,
+ 0x34U, 0x8eU, 0x43U, 0x44U,
+ 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+ 0x54U, 0x7bU, 0x94U, 0x32U,
+ 0xa6U, 0xc2U, 0x23U, 0x3dU,
+ 0xeeU, 0x4cU, 0x95U, 0x0bU,
+ 0x42U, 0xfaU, 0xc3U, 0x4eU,
+ 0x08U, 0x2eU, 0xa1U, 0x66U,
+ 0x28U, 0xd9U, 0x24U, 0xb2U,
+ 0x76U, 0x5bU, 0xa2U, 0x49U,
+ 0x6dU, 0x8bU, 0xd1U, 0x25U,
+ 0x72U, 0xf8U, 0xf6U, 0x64U,
+ 0x86U, 0x68U, 0x98U, 0x16U,
+ 0xd4U, 0xa4U, 0x5cU, 0xccU,
+ 0x5dU, 0x65U, 0xb6U, 0x92U,
+ 0x6cU, 0x70U, 0x48U, 0x50U,
+ 0xfdU, 0xedU, 0xb9U, 0xdaU,
+ 0x5eU, 0x15U, 0x46U, 0x57U,
+ 0xa7U, 0x8dU, 0x9dU, 0x84U,
+ 0x90U, 0xd8U, 0xabU, 0x00U,
+ 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+ 0xf7U, 0xe4U, 0x58U, 0x05U,
+ 0xb8U, 0xb3U, 0x45U, 0x06U,
+ 0xd0U, 0x2cU, 0x1eU, 0x8fU,
+ 0xcaU, 0x3fU, 0x0fU, 0x02U,
+ 0xc1U, 0xafU, 0xbdU, 0x03U,
+ 0x01U, 0x13U, 0x8aU, 0x6bU,
+ 0x3aU, 0x91U, 0x11U, 0x41U,
+ 0x4fU, 0x67U, 0xdcU, 0xeaU,
+ 0x97U, 0xf2U, 0xcfU, 0xceU,
+ 0xf0U, 0xb4U, 0xe6U, 0x73U,
+ 0x96U, 0xacU, 0x74U, 0x22U,
+ 0xe7U, 0xadU, 0x35U, 0x85U,
+ 0xe2U, 0xf9U, 0x37U, 0xe8U,
+ 0x1cU, 0x75U, 0xdfU, 0x6eU,
+ 0x47U, 0xf1U, 0x1aU, 0x71U,
+ 0x1dU, 0x29U, 0xc5U, 0x89U,
+ 0x6fU, 0xb7U, 0x62U, 0x0eU,
+ 0xaaU, 0x18U, 0xbeU, 0x1bU,
+ 0xfcU, 0x56U, 0x3eU, 0x4bU,
+ 0xc6U, 0xd2U, 0x79U, 0x20U,
+ 0x9aU, 0xdbU, 0xc0U, 0xfeU,
+ 0x78U, 0xcdU, 0x5aU, 0xf4U,
+ 0x1fU, 0xddU, 0xa8U, 0x33U,
+ 0x88U, 0x07U, 0xc7U, 0x31U,
+ 0xb1U, 0x12U, 0x10U, 0x59U,
+ 0x27U, 0x80U, 0xecU, 0x5fU,
+ 0x60U, 0x51U, 0x7fU, 0xa9U,
+ 0x19U, 0xb5U, 0x4aU, 0x0dU,
+ 0x2dU, 0xe5U, 0x7aU, 0x9fU,
+ 0x93U, 0xc9U, 0x9cU, 0xefU,
+ 0xa0U, 0xe0U, 0x3bU, 0x4dU,
+ 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+ 0xc8U, 0xebU, 0xbbU, 0x3cU,
+ 0x83U, 0x53U, 0x99U, 0x61U,
+ 0x17U, 0x2bU, 0x04U, 0x7eU,
+ 0xbaU, 0x77U, 0xd6U, 0x26U,
+ 0xe1U, 0x69U, 0x14U, 0x63U,
+ 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+static u32 rcon[] = {+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000,
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/*
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+static int
+setupEnc(ulong rk[/*4*(Nr + 1)*/], uchar key[], int nkey)
+{+ int i = 0;
+ u32 temp;
+
+ rk[0] = GETU32(key );
+ rk[1] = GETU32(key + 4);
+ rk[2] = GETU32(key + 8);
+ rk[3] = GETU32(key + 12);
+ if (nkey == 16) {+ for (;;) {+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (Te4[(temp >> 16) & 0xff] << 24) ^
+ (Te4[(temp >> 8) & 0xff] << 16) ^
+ (Te4[(temp ) & 0xff] << 8) ^
+ (Te4[(temp >> 24) ] ) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {+ return 10;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(key + 16);
+ rk[5] = GETU32(key + 20);
+ if (nkey == 24) {+ for (;;) {+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] << 24) ^
+ (Te4[(temp >> 8) & 0xff] << 16) ^
+ (Te4[(temp ) & 0xff] << 8) ^
+ (Te4[(temp >> 24) ] ) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {+ return 12;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(key + 24);
+ rk[7] = GETU32(key + 28);
+ if (nkey == 32) {+ for (;;) {+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] << 24) ^
+ (Te4[(temp >> 8) & 0xff] << 16) ^
+ (Te4[(temp ) & 0xff] << 8) ^
+ (Te4[(temp >> 24) ] ) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {+ return 14;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (Te4[(temp >> 24) ] << 24) ^
+ (Te4[(temp >> 16) & 0xff] << 16) ^
+ (Te4[(temp >> 8) & 0xff] << 8) ^
+ (Te4[(temp ) & 0xff] );
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Expand the cipher key into the encryption and decryption key schedules.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+static int
+AESsetup(ulong erk[/* 4*(Nr + 1) */], ulong drk[/* 4*(Nr + 1) */], uchar key[], int nkey)
+{+ int Nr, i;
+
+ /* expand the cipher key: */
+ Nr = setupEnc(erk, key, nkey);
+
+ /*
+ * invert the order of the round keys and apply the inverse MixColumn
+ * transform to all round keys but the first and the last
+ */
+ drk[0 ] = erk[4*Nr ];
+ drk[1 ] = erk[4*Nr + 1];
+ drk[2 ] = erk[4*Nr + 2];
+ drk[3 ] = erk[4*Nr + 3];
+ drk[4*Nr ] = erk[0 ];
+ drk[4*Nr + 1] = erk[1 ];
+ drk[4*Nr + 2] = erk[2 ];
+ drk[4*Nr + 3] = erk[3 ];
+ erk += 4 * Nr;
+ for (i = 1; i < Nr; i++) {+ drk += 4;
+ erk -= 4;
+ drk[0] =
+ Td0[Te4[(erk[0] >> 24) ]] ^
+ Td1[Te4[(erk[0] >> 16) & 0xff]] ^
+ Td2[Te4[(erk[0] >> 8) & 0xff]] ^
+ Td3[Te4[(erk[0] ) & 0xff]];
+ drk[1] =
+ Td0[Te4[(erk[1] >> 24) ]] ^
+ Td1[Te4[(erk[1] >> 16) & 0xff]] ^
+ Td2[Te4[(erk[1] >> 8) & 0xff]] ^
+ Td3[Te4[(erk[1] ) & 0xff]];
+ drk[2] =
+ Td0[Te4[(erk[2] >> 24) ]] ^
+ Td1[Te4[(erk[2] >> 16) & 0xff]] ^
+ Td2[Te4[(erk[2] >> 8) & 0xff]] ^
+ Td3[Te4[(erk[2] ) & 0xff]];
+ drk[3] =
+ Td0[Te4[(erk[3] >> 24) ]] ^
+ Td1[Te4[(erk[3] >> 16) & 0xff]] ^
+ Td2[Te4[(erk[3] >> 8) & 0xff]] ^
+ Td3[Te4[(erk[3] ) & 0xff]];
+ }
+ return Nr;
+}
+
+/* using round keys in rk, perform Nr rounds of encrypting pt into ct */
+static void
+AESencrypt(ulong rk[/* 4*(Nr + 1) */], int Nr, uchar pt[16], uchar ct[16])
+{+ ulong s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(pt ) ^ rk[0];
+ s1 = GETU32(pt + 4) ^ rk[1];
+ s2 = GETU32(pt + 8) ^ rk[2];
+ s3 = GETU32(pt + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+ if (Nr > 10) {+ /* round 10: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+ if (Nr > 12) {+ /* round 12: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+ }
+ }
+ rk += Nr << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {+ t0 =
+ Te0[(s0 >> 24) ] ^
+ Te1[(s1 >> 16) & 0xff] ^
+ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Te0[(s1 >> 24) ] ^
+ Te1[(s2 >> 16) & 0xff] ^
+ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Te0[(s2 >> 24) ] ^
+ Te1[(s3 >> 16) & 0xff] ^
+ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Te0[(s3 >> 24) ] ^
+ Te1[(s0 >> 16) & 0xff] ^
+ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0)
+ break;
+
+ s0 =
+ Te0[(t0 >> 24) ] ^
+ Te1[(t1 >> 16) & 0xff] ^
+ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Te0[(t1 >> 24) ] ^
+ Te1[(t2 >> 16) & 0xff] ^
+ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Te0[(t2 >> 24) ] ^
+ Te1[(t3 >> 16) & 0xff] ^
+ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Te0[(t3 >> 24) ] ^
+ Te1[(t0 >> 16) & 0xff] ^
+ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4[(t0 >> 24) ] << 24) ^
+ (Te4[(t1 >> 16) & 0xff] << 16) ^
+ (Te4[(t2 >> 8) & 0xff] << 8) ^
+ (Te4[(t3 ) & 0xff] ) ^
+ rk[0];
+ PUTU32(ct , s0);
+ s1 =
+ (Te4[(t1 >> 24) ] << 24) ^
+ (Te4[(t2 >> 16) & 0xff] << 16) ^
+ (Te4[(t3 >> 8) & 0xff] << 8) ^
+ (Te4[(t0 ) & 0xff] ) ^
+ rk[1];
+ PUTU32(ct + 4, s1);
+ s2 =
+ (Te4[(t2 >> 24) ] << 24) ^
+ (Te4[(t3 >> 16) & 0xff] << 16) ^
+ (Te4[(t0 >> 8) & 0xff] << 8) ^
+ (Te4[(t1 ) & 0xff] ) ^
+ rk[2];
+ PUTU32(ct + 8, s2);
+ s3 =
+ (Te4[(t3 >> 24) ] << 24) ^
+ (Te4[(t0 >> 16) & 0xff] << 16) ^
+ (Te4[(t1 >> 8) & 0xff] << 8) ^
+ (Te4[(t2 ) & 0xff] ) ^
+ rk[3];
+ PUTU32(ct + 12, s3);
+}
+
+static void
+AESdecrypt(ulong rk[/* 4*(Nr + 1) */], int Nr, uchar ct[16], uchar pt[16])
+{+ ulong s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(ct ) ^ rk[0];
+ s1 = GETU32(ct + 4) ^ rk[1];
+ s2 = GETU32(ct + 8) ^ rk[2];
+ s3 = GETU32(ct + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+ if (Nr > 10) {+ /* round 10: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+ if (Nr > 12) {+ /* round 12: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+ }
+ }
+ rk += Nr << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {+ t0 =
+ Td0[(s0 >> 24) ] ^
+ Td1[(s3 >> 16) & 0xff] ^
+ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Td0[(s1 >> 24) ] ^
+ Td1[(s0 >> 16) & 0xff] ^
+ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Td0[(s2 >> 24) ] ^
+ Td1[(s1 >> 16) & 0xff] ^
+ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Td0[(s3 >> 24) ] ^
+ Td1[(s2 >> 16) & 0xff] ^
+ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0)
+ break;
+
+ s0 =
+ Td0[(t0 >> 24) ] ^
+ Td1[(t3 >> 16) & 0xff] ^
+ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Td0[(t1 >> 24) ] ^
+ Td1[(t0 >> 16) & 0xff] ^
+ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Td0[(t2 >> 24) ] ^
+ Td1[(t1 >> 16) & 0xff] ^
+ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Td0[(t3 >> 24) ] ^
+ Td1[(t2 >> 16) & 0xff] ^
+ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[(t0 >> 24) ] << 24) ^
+ (Td4[(t3 >> 16) & 0xff] << 16) ^
+ (Td4[(t2 >> 8) & 0xff] << 8) ^
+ (Td4[(t1 ) & 0xff] ) ^
+ rk[0];
+ PUTU32(pt , s0);
+ s1 =
+ (Td4[(t1 >> 24) ] << 24) ^
+ (Td4[(t0 >> 16) & 0xff] << 16) ^
+ (Td4[(t3 >> 8) & 0xff] << 8) ^
+ (Td4[(t2 ) & 0xff] ) ^
+ rk[1];
+ PUTU32(pt + 4, s1);
+ s2 =
+ (Td4[(t2 >> 24) ] << 24) ^
+ (Td4[(t1 >> 16) & 0xff] << 16) ^
+ (Td4[(t0 >> 8) & 0xff] << 8) ^
+ (Td4[(t3 ) & 0xff] ) ^
+ rk[2];
+ PUTU32(pt + 8, s2);
+ s3 =
+ (Td4[(t3 >> 24) ] << 24) ^
+ (Td4[(t2 >> 16) & 0xff] << 16) ^
+ (Td4[(t1 >> 8) & 0xff] << 8) ^
+ (Td4[(t0 ) & 0xff] ) ^
+ rk[3];
+ PUTU32(pt + 12, s3);
+}
+
+void (*aes_encrypt)(ulong rk[], int Nr, uchar pt[16], uchar ct[16]) = AESencrypt;
+void (*aes_decrypt)(ulong rk[], int Nr, uchar ct[16], uchar pt[16]) = AESdecrypt;
+
+void
+setupAESstate(AESstate *s, uchar key[], int nkey, uchar *ivec)
+{+ static int (*aes_setup)(ulong erk[/* 4*(Nr + 1) */], ulong drk[/* 4*(Nr + 1) */], uchar key[], int nkey);
+
+ if(aes_setup == nil){+ extern void *aesni_init(void);
+ if((aes_setup = aesni_init()) == nil)
+ aes_setup = AESsetup;
+ }
+ memset(s, 0, sizeof(*s));
+ if(nkey > AESmaxkey)
+ nkey = AESmaxkey;
+ memmove(s->key, key, nkey);
+ s->keybytes = nkey;
+ s->ekey = s->storage+16 - ((s->storage - (uchar*)0) & 15);
+ s->dkey = (uchar*)s->ekey + 16*(AESmaxrounds+1);
+ s->rounds = (*aes_setup)(s->ekey, s->dkey, s->key, nkey);
+ if(ivec != nil)
+ memmove(s->ivec, ivec, AESbsize);
+ if(s->rounds != 0)
+ s->setup = 0xcafebabe;
+}
--- /dev/null
+++ b/libsec/aesCBC.c
@@ -1,0 +1,60 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * Define by analogy with desCBCencrypt; AES modes are not standardized yet.
+ * Because of the way that non-multiple-of-16 buffers are handled,
+ * the decryptor must be fed buffers of the same size as the encryptor.
+ */
+void
+aesCBCencrypt(uchar *p, int len, AESstate *s)
+{+ uchar *p2, *ip, *eip;
+ uchar q[AESbsize];
+
+ for(; len >= AESbsize; len -= AESbsize){+ p2 = p;
+ ip = s->ivec;
+ for(eip = ip+AESbsize; ip < eip; )
+ *p2++ ^= *ip++;
+ aes_encrypt(s->ekey, s->rounds, p, q);
+ memmove(s->ivec, q, AESbsize);
+ memmove(p, q, AESbsize);
+ p += AESbsize;
+ }
+
+ if(len > 0){+ ip = s->ivec;
+ aes_encrypt(s->ekey, s->rounds, ip, q);
+ memmove(s->ivec, q, AESbsize);
+ for(eip = ip+len; ip < eip; )
+ *p++ ^= *ip++;
+ }
+}
+
+void
+aesCBCdecrypt(uchar *p, int len, AESstate *s)
+{+ uchar *ip, *eip, *tp;
+ uchar tmp[AESbsize], q[AESbsize];
+
+ for(; len >= AESbsize; len -= AESbsize){+ memmove(tmp, p, AESbsize);
+ aes_decrypt(s->dkey, s->rounds, p, q);
+ memmove(p, q, AESbsize);
+ tp = tmp;
+ ip = s->ivec;
+ for(eip = ip+AESbsize; ip < eip; ){+ *p++ ^= *ip;
+ *ip++ = *tp++;
+ }
+ }
+
+ if(len > 0){+ ip = s->ivec;
+ aes_encrypt(s->ekey, s->rounds, ip, q);
+ memmove(s->ivec, q, AESbsize);
+ for(eip = ip+len; ip < eip; )
+ *p++ ^= *ip++;
+ }
+}
--- /dev/null
+++ b/libsec/aesCFB.c
@@ -1,0 +1,50 @@
+#include "os.h"
+#include <libsec.h>
+
+typedef ulong u32;
+
+void
+aesCFBencrypt(uchar *p, int len, AESstate *s)
+{+ u32 a, o = s->offset;
+
+ while(len > 0){+ if(o % 16){+ Odd:
+ a = (s->ivec[o++ % 16] ^= *p), *p++ = a, len--;
+ continue;
+ }
+ aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec);
+ if(len < 16 || ((p-(uchar*)0) & 3) != 0)
+ goto Odd;
+ ((u32*)p)[0] = (((u32*)s->ivec)[0] ^= ((u32*)p)[0]);
+ ((u32*)p)[1] = (((u32*)s->ivec)[1] ^= ((u32*)p)[1]);
+ ((u32*)p)[2] = (((u32*)s->ivec)[2] ^= ((u32*)p)[2]);
+ ((u32*)p)[3] = (((u32*)s->ivec)[3] ^= ((u32*)p)[3]);
+ o += 16, p += 16, len -= 16;
+ }
+ s->offset = o;
+}
+
+void
+aesCFBdecrypt(uchar *p, int len, AESstate *s)
+{+ u32 a, o = s->offset;
+
+ while(len > 0){+ if(o % 16){+ Odd:
+ a = *p, *p++ ^= s->ivec[o % 16], s->ivec[o++ % 16] = a, len--;
+ continue;
+ }
+ aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec);
+ if(len < 16 || ((p-(uchar*)0) & 3) != 0)
+ goto Odd;
+ a = ((u32*)p)[0], ((u32*)p)[0] ^= ((u32*)s->ivec)[0], ((u32*)s->ivec)[0] = a;
+ a = ((u32*)p)[1], ((u32*)p)[1] ^= ((u32*)s->ivec)[1], ((u32*)s->ivec)[1] = a;
+ a = ((u32*)p)[2], ((u32*)p)[2] ^= ((u32*)s->ivec)[2], ((u32*)s->ivec)[2] = a;
+ a = ((u32*)p)[3], ((u32*)p)[3] ^= ((u32*)s->ivec)[3], ((u32*)s->ivec)[3] = a;
+ o += 16, p += 16, len -= 16;
+ }
+ s->offset = o;
+}
--- /dev/null
+++ b/libsec/aesOFB.c
@@ -1,0 +1,28 @@
+#include "os.h"
+#include <libsec.h>
+
+typedef ulong u32;
+
+void
+aesOFBencrypt(uchar *p, int len, AESstate *s)
+{+ u32 o = s->offset;
+
+ while(len > 0){+ if(o % 16){+ Odd:
+ *p++ ^= s->ivec[o++ % 16], len--;
+ continue;
+ }
+ aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec);
+ if(len < 16 || ((p-(uchar*)0) & 3) != 0)
+ goto Odd;
+ ((u32*)p)[0] ^= ((u32*)s->ivec)[0];
+ ((u32*)p)[1] ^= ((u32*)s->ivec)[1];
+ ((u32*)p)[2] ^= ((u32*)s->ivec)[2];
+ ((u32*)p)[3] ^= ((u32*)s->ivec)[3];
+ o += 16, p += 16, len -= 16;
+ }
+ s->offset = o;
+}
+
--- /dev/null
+++ b/libsec/aes_gcm.c
@@ -1,0 +1,199 @@
+#include "os.h"
+#include <libsec.h>
+
+static void
+load128(uchar b[16], ulong W[4])
+{+ W[0] = (ulong)b[15] | (ulong)b[14]<<8 | (ulong)b[13]<<16 | (ulong)b[12]<<24;
+ W[1] = (ulong)b[11] | (ulong)b[10]<<8 | (ulong)b[ 9]<<16 | (ulong)b[ 8]<<24;
+ W[2] = (ulong)b[ 7] | (ulong)b[ 6]<<8 | (ulong)b[ 5]<<16 | (ulong)b[ 4]<<24;
+ W[3] = (ulong)b[ 3] | (ulong)b[ 2]<<8 | (ulong)b[ 1]<<16 | (ulong)b[ 0]<<24;
+}
+
+static void
+store128(ulong W[4], uchar b[16])
+{+ b[15] = W[0], b[14] = W[0]>>8, b[13] = W[0]>>16, b[12] = W[0]>>24;
+ b[11] = W[1], b[10] = W[1]>>8, b[ 9] = W[1]>>16, b[ 8] = W[1]>>24;
+ b[ 7] = W[2], b[ 6] = W[2]>>8, b[ 5] = W[2]>>16, b[ 4] = W[2]>>24;
+ b[ 3] = W[3], b[ 2] = W[3]>>8, b[ 1] = W[3]>>16, b[ 0] = W[3]>>24;
+}
+
+static void
+gfmul(ulong X[4], ulong Y[4], ulong Z[4])
+{+ long m, i;
+
+ Z[0] = Z[1] = Z[2] = Z[3] = 0;
+ for(i=127; i>=0; i--){+ m = ((long)Y[i>>5] << (31-(i&31))) >> 31;
+ Z[0] ^= X[0] & m;
+ Z[1] ^= X[1] & m;
+ Z[2] ^= X[2] & m;
+ Z[3] ^= X[3] & m;
+ m = ((long)X[0]<<31) >> 31;
+ X[0] = X[0]>>1 | X[1]<<31;
+ X[1] = X[1]>>1 | X[2]<<31;
+ X[2] = X[2]>>1 | X[3]<<31;
+ X[3] = X[3]>>1 ^ (0xE1000000 & m);
+ }
+}
+
+static void
+prepareM(ulong H[4], ulong M[16][256][4])
+{+ ulong X[4], i, j;
+
+ for(i=0; i<16; i++){+ for(j=0; j<256; j++){+ X[0] = X[1] = X[2] = X[3] = 0;
+ X[i>>2] = j<<((i&3)<<3);
+ gfmul(X, H, M[i][j]);
+ }
+ }
+}
+
+static void
+ghash1(AESGCMstate *s, ulong X[4], ulong Y[4])
+{+ ulong *Xi, i;
+
+ X[0] ^= Y[0], X[1] ^= Y[1], X[2] ^= Y[2], X[3] ^= Y[3];
+ if(0){+ gfmul(X, s->H, Y);
+ return;
+ }
+
+ Y[0] = Y[1] = Y[2] = Y[3] = 0;
+ for(i=0; i<16; i++){+ Xi = s->M[i][(X[i>>2]>>((i&3)<<3))&0xFF];
+ Y[0] ^= Xi[0];
+ Y[1] ^= Xi[1];
+ Y[2] ^= Xi[2];
+ Y[3] ^= Xi[3];
+ }
+}
+
+static void
+ghashn(AESGCMstate *s, uchar *dat, ulong len, ulong Y[4])
+{+ uchar tmp[16];
+ ulong X[4];
+
+ while(len >= 16){+ load128(dat, X);
+ ghash1(s, X, Y);
+ dat += 16, len -= 16;
+ }
+ if(len > 0){+ memmove(tmp, dat, len);
+ memset(tmp+len, 0, 16-len);
+ load128(tmp, X);
+ ghash1(s, X, Y);
+ }
+}
+
+static ulong
+aesxctr1(AESstate *s, uchar ctr[AESbsize], uchar *dat, ulong len)
+{+ uchar tmp[AESbsize];
+ ulong i;
+
+ aes_encrypt(s->ekey, s->rounds, ctr, tmp);
+ if(len > AESbsize)
+ len = AESbsize;
+ for(i=0; i<len; i++)
+ dat[i] ^= tmp[i];
+ return len;
+}
+
+static void
+aesxctrn(AESstate *s, uchar *dat, ulong len)
+{+ uchar ctr[AESbsize];
+ ulong i;
+
+ memmove(ctr, s->ivec, AESbsize);
+ while(len > 0){+ for(i=AESbsize-1; i>=AESbsize-4; i--)
+ if(++ctr[i] != 0)
+ break;
+
+ if(aesxctr1(s, ctr, dat, len) < AESbsize)
+ break;
+ dat += AESbsize;
+ len -= AESbsize;
+ }
+}
+
+void
+aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen)
+{+ if(ivlen == 96/8){+ memmove(s->a.ivec, iv, ivlen);
+ memset(s->a.ivec+ivlen, 0, AESbsize-ivlen);
+ s->a.ivec[AESbsize-1] = 1;
+ } else {+ ulong L[4], Y[4] = {0};+
+ ghashn(s, iv, ivlen, Y);
+ L[0] = ivlen << 3;
+ L[1] = ivlen >> 29;
+ L[2] = L[3] = 0;
+ ghash1(s, L, Y);
+ store128(Y, s->a.ivec);
+ }
+}
+
+void
+setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen)
+{+ setupAESstate(&s->a, key, keylen, nil);
+
+ memset(s->a.ivec, 0, AESbsize);
+ aes_encrypt(s->a.ekey, s->a.rounds, s->a.ivec, s->a.ivec);
+ load128(s->a.ivec, s->H);
+ memset(s->a.ivec, 0, AESbsize);
+ prepareM(s->H, s->M);
+
+ if(iv != nil && ivlen > 0)
+ aesgcm_setiv(s, iv, ivlen);
+}
+
+void
+aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+{+ ulong L[4], Y[4] = {0};+
+ ghashn(s, aad, naad, Y);
+ aesxctrn(&s->a, dat, ndat);
+ ghashn(s, dat, ndat, Y);
+ L[0] = ndat << 3;
+ L[1] = ndat >> 29;
+ L[2] = naad << 3;
+ L[3] = naad >> 29;
+ ghash1(s, L, Y);
+ store128(Y, tag);
+ aesxctr1(&s->a, s->a.ivec, tag, 16);
+}
+
+int
+aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+{+ ulong L[4], Y[4] = {0};+ uchar tmp[16];
+
+ ghashn(s, aad, naad, Y);
+ ghashn(s, dat, ndat, Y);
+ L[0] = ndat << 3;
+ L[1] = ndat >> 29;
+ L[2] = naad << 3;
+ L[3] = naad >> 29;
+ ghash1(s, L, Y);
+ store128(Y, tmp);
+ aesxctr1(&s->a, s->a.ivec, tmp, 16);
+ if(tsmemcmp(tag, tmp, 16) != 0)
+ return -1;
+ aesxctrn(&s->a, dat, ndat);
+ return 0;
+}
--- /dev/null
+++ b/libsec/aes_xts.c
@@ -1,0 +1,83 @@
+#include "os.h"
+#include <libsec.h>
+
+/* little-endian data order */
+#define GET4(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define PUT4(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+
+static void
+gf_mulx(uchar *x)
+{+ ulong t0, t1, t2, t3, t4;
+
+ t0 = GET4(x);
+ t1 = GET4(x+4);
+ t2 = GET4(x+8);
+ t3 = GET4(x+12);
+
+ t4 = (t3 >> 31);
+ t3 = (t3 << 1) | (t2 >> 31);
+ t2 = (t2 << 1) | (t1 >> 31);
+ t1 = (t1 << 1) | (t0 >> 31);
+ t0 = (t0 << 1) ^ (t4*135);
+
+ PUT4(x, t0);
+ PUT4(x+4, t1);
+ PUT4(x+8, t2);
+ PUT4(x+12, t3);
+}
+
+static void
+xor128(uchar *o, uchar *i1, uchar *i2)
+{+ int i;
+
+ for(i=0; i<16; i++)
+ o[i] = i1[i] ^ i2[i];
+}
+
+static void
+setupT(AESstate *tweak, uvlong sectorNumber, uchar T[AESbsize])
+{+ PUT4(T+0, (ulong)sectorNumber), sectorNumber >>= 32;
+ PUT4(T+4, (ulong)sectorNumber);
+ PUT4(T+8, 0);
+ PUT4(T+12, 0);
+ aes_encrypt(tweak->ekey, tweak->rounds, T, T);
+}
+
+void
+aes_xts_encrypt(AESstate *tweak, AESstate *ecb,
+ uvlong sectorNumber, uchar *input, uchar *output, ulong len)
+{+ uchar T[AESbsize], x[AESbsize];
+
+ if(len % AESbsize)
+ abort();
+
+ setupT(tweak, sectorNumber, T);
+ for (; len > 0; len -= AESbsize, input += AESbsize, output += AESbsize) {+ xor128(x, input, T);
+ aes_encrypt(ecb->ekey, ecb->rounds, x, x);
+ xor128(output, x, T);
+ gf_mulx(T);
+ }
+}
+
+void
+aes_xts_decrypt(AESstate *tweak, AESstate *ecb,
+ uvlong sectorNumber, uchar *input, uchar *output, ulong len)
+{+ uchar T[AESbsize], x[AESbsize];
+
+ if(len % AESbsize)
+ abort();
+
+ setupT(tweak, sectorNumber, T);
+ for (; len > 0; len -= AESbsize, input += AESbsize, output += AESbsize) {+ xor128(x, input, T);
+ aes_decrypt(ecb->dkey, ecb->rounds, x, x);
+ xor128(output, x, T);
+ gf_mulx(T);
+ }
+}
--- /dev/null
+++ b/libsec/aesni.c
@@ -1,0 +1,5 @@
+void*
+aesni_init(void)
+{+ return 0;
+}
--- /dev/null
+++ b/libsec/blowfish.c
@@ -1,0 +1,524 @@
+#include "os.h"
+#include <libsec.h>
+
+// Blowfish block cipher. See:
+// Lecture Notes in Computer Science 809
+// Fast Software Encryption
+// Cambridge Security Workshop, Cambridge, England (1993)
+
+static u32int sbox[1024];
+static u32int pbox[BFrounds+2];
+
+static void bfencrypt(u32int *, BFstate *);
+static void bfdecrypt(u32int *, BFstate *);
+
+/*
+ * Endianess agnostic functions to convert a
+ * block (8-byte buffer) to a u32int array and
+ * viceversa.
+ */
+
+static void
+buf2ints(uchar *p, u32int *b)
+{+ b[0] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
+ b[1] = p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7];
+}
+
+static void
+ints2buf(u32int *b, uchar *p)
+{+ u32int u;
+
+ u = b[0];
+ p[0] = u>>24;
+ p[1] = u>>16;
+ p[2] = u>>8;
+ p[3] = u;
+
+ u = b[1];
+ p[4] = u>>24;
+ p[5] = u>>16;
+ p[6] = u>>8;
+ p[7] = u;
+}
+
+void
+setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec)
+{+ int i, j;
+ u32int n, buf[2];
+
+ memset(s, 0, sizeof(*s));
+ memset(buf, 0, sizeof buf);
+
+ if (keybytes > sizeof(s->key))
+ keybytes = sizeof(s->key);
+
+ memmove(s->key, key, keybytes);
+
+ if (ivec != nil)
+ memmove(s->ivec, ivec, sizeof(s->ivec));
+ else
+ memset(s->ivec, 0, sizeof(s->ivec));
+
+ memmove(s->pbox, pbox, sizeof(pbox));
+ memmove(s->sbox, sbox, sizeof(sbox));
+
+ if (keybytes > 4*(BFrounds + 2))
+ keybytes = 4*(BFrounds + 2);
+
+ for(i=j=0; i < BFrounds+2; i++) {+ n = key[j];
+ j = (j+1) % keybytes;
+
+ n <<= 8;
+ n |= key[j];
+ j = (j+1) % keybytes;
+
+ n <<= 8;
+ n |= key[j];
+ j = (j+1) % keybytes;
+
+ n <<= 8;
+ n |= key[j];
+ j = (j+1) % keybytes;
+
+ s->pbox[i] ^= n;
+ }
+
+ for(i=0; i < BFrounds+2; i += 2) {+ bfencrypt(buf, s);
+ s->pbox[i] = buf[0];
+ s->pbox[i+1] = buf[1];
+ }
+
+ for(i=0; i < 1024; i += 2) {+ bfencrypt(buf, s);
+ s->sbox[i] = buf[0];
+ s->sbox[i+1] = buf[1];
+ }
+
+ s->setup = 0xcafebabe;
+}
+
+void
+bfCBCencrypt(uchar *buf, int n, BFstate *s)
+{+ int i;
+ u32int bo[2], bi[2];
+
+ assert((n & 7) == 0);
+
+ buf2ints(s->ivec, bo);
+ for(i=0; i < n; i += 8, buf += 8) {+ buf2ints(buf, bi);
+
+ bi[0] ^= bo[0];
+ bi[1] ^= bo[1];
+
+ bfencrypt(bi, s);
+
+ bo[0] = bi[0];
+ bo[1] = bi[1];
+
+ ints2buf(bi, buf);
+ }
+ ints2buf(bo, s->ivec);
+ return;
+}
+
+void
+bfCBCdecrypt(uchar *buf, int n, BFstate *s)
+{+ int i;
+ u32int bo[2], bi[2], xr[2];
+
+ assert((n & 7) == 0);
+
+ buf2ints(s->ivec, bo);
+ for(i=0; i < n; i += 8, buf += 8) {+ buf2ints(buf, bi);
+
+ xr[0] = bi[0];
+ xr[1] = bi[1];
+
+ bfdecrypt(bi, s);
+
+ bo[0] ^= bi[0];
+ bo[1] ^= bi[1];
+
+ ints2buf(bo, buf);
+
+ bo[0] = xr[0];
+ bo[1] = xr[1];
+ }
+ ints2buf(bo, s->ivec);
+ return;
+}
+
+void
+bfECBencrypt(uchar *buf, int n, BFstate *s)
+{+ int i;
+ u32int b[2];
+
+ for(i=0; i < n; i += 8, buf += 8) {+ buf2ints(buf, b);
+ bfencrypt(b, s);
+ ints2buf(b, buf);
+ }
+
+ return;
+}
+
+void
+bfECBdecrypt(uchar *buf, int n, BFstate *s)
+{+ int i;
+ u32int b[2];
+
+ for(i=0; i < n; i += 8, buf += 8) {+ buf2ints(buf, b);
+ bfdecrypt(b, s);
+ ints2buf(b, buf);
+ }
+
+ return;
+}
+
+static void
+bfencrypt(u32int *b, BFstate *s)
+{+ int i;
+ u32int l, r;
+ u32int *pb, *sb;
+
+ l = b[0];
+ r = b[1];
+
+ pb = s->pbox;
+ sb = s->sbox;
+
+ l ^= pb[0];
+
+ for(i=1; i<16; i += 2) {+ r ^= pb[i];
+ r ^= ( (sb[ (uchar) (l>>24)] + sb[256 + ((uchar) (l>>16))]) ^
+ sb[512 + ((uchar) (l>>8))]) + sb[768 +((uchar) l)];
+
+ l ^= pb[i+1];
+ l ^= ( (sb[ (uchar) (r>>24)] + sb[256 + ((uchar) (r>>16))]) ^
+ sb[512 + ((uchar) (r>>8))]) + sb[768 +((uchar) r)];
+ }
+
+ r ^= pb[BFrounds+1];
+
+ /* sic */
+ b[0] = r;
+ b[1] = l;
+
+ return;
+}
+
+static void
+bfdecrypt(u32int *b, BFstate *s)
+{+ int i;
+ u32int l, r;
+ u32int *pb, *sb;
+
+ l = b[0];
+ r = b[1];
+
+ pb = s->pbox;
+ sb = s->sbox;
+
+ l ^= pb[BFrounds+1];
+
+ for(i=16; i > 0; i -= 2) {+ r ^= pb[i];
+ r ^= ( (sb[ (uchar) (l>>24)] + sb[256 + ((uchar) (l>>16))]) ^
+ sb[512 + ((uchar) (l>>8))]) + sb[768 +((uchar) l)];
+
+ l ^= pb[i-1];
+ l ^= ( (sb[ (uchar) (r>>24)] + sb[256 + ((uchar) (r>>16))]) ^
+ sb[512 + ((uchar) (r>>8))]) + sb[768 +((uchar) r)];
+ }
+
+ r ^= pb[0];
+
+ /* sic */
+ b[0] = r;
+ b[1] = l;
+
+ return;
+}
+
+static u32int pbox[BFrounds+2] = {+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+};
+
+static u32int sbox[1024] = {+ 0xd1310ba6L, 0x98dfb5acL, 0x2ffd72dbL, 0xd01adfb7L,
+ 0xb8e1afedL, 0x6a267e96L, 0xba7c9045L, 0xf12c7f99L,
+ 0x24a19947L, 0xb3916cf7L, 0x0801f2e2L, 0x858efc16L,
+ 0x636920d8L, 0x71574e69L, 0xa458fea3L, 0xf4933d7eL,
+ 0x0d95748fL, 0x728eb658L, 0x718bcd58L, 0x82154aeeL,
+ 0x7b54a41dL, 0xc25a59b5L, 0x9c30d539L, 0x2af26013L,
+ 0xc5d1b023L, 0x286085f0L, 0xca417918L, 0xb8db38efL,
+ 0x8e79dcb0L, 0x603a180eL, 0x6c9e0e8bL, 0xb01e8a3eL,
+ 0xd71577c1L, 0xbd314b27L, 0x78af2fdaL, 0x55605c60L,
+ 0xe65525f3L, 0xaa55ab94L, 0x57489862L, 0x63e81440L,
+ 0x55ca396aL, 0x2aab10b6L, 0xb4cc5c34L, 0x1141e8ceL,
+ 0xa15486afL, 0x7c72e993L, 0xb3ee1411L, 0x636fbc2aL,
+ 0x2ba9c55dL, 0x741831f6L, 0xce5c3e16L, 0x9b87931eL,
+ 0xafd6ba33L, 0x6c24cf5cL, 0x7a325381L, 0x28958677L,
+ 0x3b8f4898L, 0x6b4bb9afL, 0xc4bfe81bL, 0x66282193L,
+ 0x61d809ccL, 0xfb21a991L, 0x487cac60L, 0x5dec8032L,
+ 0xef845d5dL, 0xe98575b1L, 0xdc262302L, 0xeb651b88L,
+ 0x23893e81L, 0xd396acc5L, 0x0f6d6ff3L, 0x83f44239L,
+ 0x2e0b4482L, 0xa4842004L, 0x69c8f04aL, 0x9e1f9b5eL,
+ 0x21c66842L, 0xf6e96c9aL, 0x670c9c61L, 0xabd388f0L,
+ 0x6a51a0d2L, 0xd8542f68L, 0x960fa728L, 0xab5133a3L,
+ 0x6eef0b6cL, 0x137a3be4L, 0xba3bf050L, 0x7efb2a98L,
+ 0xa1f1651dL, 0x39af0176L, 0x66ca593eL, 0x82430e88L,
+ 0x8cee8619L, 0x456f9fb4L, 0x7d84a5c3L, 0x3b8b5ebeL,
+ 0xe06f75d8L, 0x85c12073L, 0x401a449fL, 0x56c16aa6L,
+ 0x4ed3aa62L, 0x363f7706L, 0x1bfedf72L, 0x429b023dL,
+ 0x37d0d724L, 0xd00a1248L, 0xdb0fead3L, 0x49f1c09bL,
+ 0x075372c9L, 0x80991b7bL, 0x25d479d8L, 0xf6e8def7L,
+ 0xe3fe501aL, 0xb6794c3bL, 0x976ce0bdL, 0x04c006baL,
+ 0xc1a94fb6L, 0x409f60c4L, 0x5e5c9ec2L, 0x196a2463L,
+ 0x68fb6fafL, 0x3e6c53b5L, 0x1339b2ebL, 0x3b52ec6fL,
+ 0x6dfc511fL, 0x9b30952cL, 0xcc814544L, 0xaf5ebd09L,
+ 0xbee3d004L, 0xde334afdL, 0x660f2807L, 0x192e4bb3L,
+ 0xc0cba857L, 0x45c8740fL, 0xd20b5f39L, 0xb9d3fbdbL,
+ 0x5579c0bdL, 0x1a60320aL, 0xd6a100c6L, 0x402c7279L,
+ 0x679f25feL, 0xfb1fa3ccL, 0x8ea5e9f8L, 0xdb3222f8L,
+ 0x3c7516dfL, 0xfd616b15L, 0x2f501ec8L, 0xad0552abL,
+ 0x323db5faL, 0xfd238760L, 0x53317b48L, 0x3e00df82L,
+ 0x9e5c57bbL, 0xca6f8ca0L, 0x1a87562eL, 0xdf1769dbL,
+ 0xd542a8f6L, 0x287effc3L, 0xac6732c6L, 0x8c4f5573L,
+ 0x695b27b0L, 0xbbca58c8L, 0xe1ffa35dL, 0xb8f011a0L,
+ 0x10fa3d98L, 0xfd2183b8L, 0x4afcb56cL, 0x2dd1d35bL,
+ 0x9a53e479L, 0xb6f84565L, 0xd28e49bcL, 0x4bfb9790L,
+ 0xe1ddf2daL, 0xa4cb7e33L, 0x62fb1341L, 0xcee4c6e8L,
+ 0xef20cadaL, 0x36774c01L, 0xd07e9efeL, 0x2bf11fb4L,
+ 0x95dbda4dL, 0xae909198L, 0xeaad8e71L, 0x6b93d5a0L,
+ 0xd08ed1d0L, 0xafc725e0L, 0x8e3c5b2fL, 0x8e7594b7L,
+ 0x8ff6e2fbL, 0xf2122b64L, 0x8888b812L, 0x900df01cL,
+ 0x4fad5ea0L, 0x688fc31cL, 0xd1cff191L, 0xb3a8c1adL,
+ 0x2f2f2218L, 0xbe0e1777L, 0xea752dfeL, 0x8b021fa1L,
+ 0xe5a0cc0fL, 0xb56f74e8L, 0x18acf3d6L, 0xce89e299L,
+ 0xb4a84fe0L, 0xfd13e0b7L, 0x7cc43b81L, 0xd2ada8d9L,
+ 0x165fa266L, 0x80957705L, 0x93cc7314L, 0x211a1477L,
+ 0xe6ad2065L, 0x77b5fa86L, 0xc75442f5L, 0xfb9d35cfL,
+ 0xebcdaf0cL, 0x7b3e89a0L, 0xd6411bd3L, 0xae1e7e49L,
+ 0x00250e2dL, 0x2071b35eL, 0x226800bbL, 0x57b8e0afL,
+ 0x2464369bL, 0xf009b91eL, 0x5563911dL, 0x59dfa6aaL,
+ 0x78c14389L, 0xd95a537fL, 0x207d5ba2L, 0x02e5b9c5L,
+ 0x83260376L, 0x6295cfa9L, 0x11c81968L, 0x4e734a41L,
+ 0xb3472dcaL, 0x7b14a94aL, 0x1b510052L, 0x9a532915L,
+ 0xd60f573fL, 0xbc9bc6e4L, 0x2b60a476L, 0x81e67400L,
+ 0x08ba6fb5L, 0x571be91fL, 0xf296ec6bL, 0x2a0dd915L,
+ 0xb6636521L, 0xe7b9f9b6L, 0xff34052eL, 0xc5855664L,
+ 0x53b02d5dL, 0xa99f8fa1L, 0x08ba4799L, 0x6e85076aL,
+ 0x4b7a70e9L, 0xb5b32944L, 0xdb75092eL, 0xc4192623L,
+ 0xad6ea6b0L, 0x49a7df7dL, 0x9cee60b8L, 0x8fedb266L,
+ 0xecaa8c71L, 0x699a17ffL, 0x5664526cL, 0xc2b19ee1L,
+ 0x193602a5L, 0x75094c29L, 0xa0591340L, 0xe4183a3eL,
+ 0x3f54989aL, 0x5b429d65L, 0x6b8fe4d6L, 0x99f73fd6L,
+ 0xa1d29c07L, 0xefe830f5L, 0x4d2d38e6L, 0xf0255dc1L,
+ 0x4cdd2086L, 0x8470eb26L, 0x6382e9c6L, 0x021ecc5eL,
+ 0x09686b3fL, 0x3ebaefc9L, 0x3c971814L, 0x6b6a70a1L,
+ 0x687f3584L, 0x52a0e286L, 0xb79c5305L, 0xaa500737L,
+ 0x3e07841cL, 0x7fdeae5cL, 0x8e7d44ecL, 0x5716f2b8L,
+ 0xb03ada37L, 0xf0500c0dL, 0xf01c1f04L, 0x0200b3ffL,
+ 0xae0cf51aL, 0x3cb574b2L, 0x25837a58L, 0xdc0921bdL,
+ 0xd19113f9L, 0x7ca92ff6L, 0x94324773L, 0x22f54701L,
+ 0x3ae5e581L, 0x37c2dadcL, 0xc8b57634L, 0x9af3dda7L,
+ 0xa9446146L, 0x0fd0030eL, 0xecc8c73eL, 0xa4751e41L,
+ 0xe238cd99L, 0x3bea0e2fL, 0x3280bba1L, 0x183eb331L,
+ 0x4e548b38L, 0x4f6db908L, 0x6f420d03L, 0xf60a04bfL,
+ 0x2cb81290L, 0x24977c79L, 0x5679b072L, 0xbcaf89afL,
+ 0xde9a771fL, 0xd9930810L, 0xb38bae12L, 0xdccf3f2eL,
+ 0x5512721fL, 0x2e6b7124L, 0x501adde6L, 0x9f84cd87L,
+ 0x7a584718L, 0x7408da17L, 0xbc9f9abcL, 0xe94b7d8cL,
+ 0xec7aec3aL, 0xdb851dfaL, 0x63094366L, 0xc464c3d2L,
+ 0xef1c1847L, 0x3215d908L, 0xdd433b37L, 0x24c2ba16L,
+ 0x12a14d43L, 0x2a65c451L, 0x50940002L, 0x133ae4ddL,
+ 0x71dff89eL, 0x10314e55L, 0x81ac77d6L, 0x5f11199bL,
+ 0x043556f1L, 0xd7a3c76bL, 0x3c11183bL, 0x5924a509L,
+ 0xf28fe6edL, 0x97f1fbfaL, 0x9ebabf2cL, 0x1e153c6eL,
+ 0x86e34570L, 0xeae96fb1L, 0x860e5e0aL, 0x5a3e2ab3L,
+ 0x771fe71cL, 0x4e3d06faL, 0x2965dcb9L, 0x99e71d0fL,
+ 0x803e89d6L, 0x5266c825L, 0x2e4cc978L, 0x9c10b36aL,
+ 0xc6150ebaL, 0x94e2ea78L, 0xa5fc3c53L, 0x1e0a2df4L,
+ 0xf2f74ea7L, 0x361d2b3dL, 0x1939260fL, 0x19c27960L,
+ 0x5223a708L, 0xf71312b6L, 0xebadfe6eL, 0xeac31f66L,
+ 0xe3bc4595L, 0xa67bc883L, 0xb17f37d1L, 0x018cff28L,
+ 0xc332ddefL, 0xbe6c5aa5L, 0x65582185L, 0x68ab9802L,
+ 0xeecea50fL, 0xdb2f953bL, 0x2aef7dadL, 0x5b6e2f84L,
+ 0x1521b628L, 0x29076170L, 0xecdd4775L, 0x619f1510L,
+ 0x13cca830L, 0xeb61bd96L, 0x0334fe1eL, 0xaa0363cfL,
+ 0xb5735c90L, 0x4c70a239L, 0xd59e9e0bL, 0xcbaade14L,
+ 0xeecc86bcL, 0x60622ca7L, 0x9cab5cabL, 0xb2f3846eL,
+ 0x648b1eafL, 0x19bdf0caL, 0xa02369b9L, 0x655abb50L,
+ 0x40685a32L, 0x3c2ab4b3L, 0x319ee9d5L, 0xc021b8f7L,
+ 0x9b540b19L, 0x875fa099L, 0x95f7997eL, 0x623d7da8L,
+ 0xf837889aL, 0x97e32d77L, 0x11ed935fL, 0x16681281L,
+ 0x0e358829L, 0xc7e61fd6L, 0x96dedfa1L, 0x7858ba99L,
+ 0x57f584a5L, 0x1b227263L, 0x9b83c3ffL, 0x1ac24696L,
+ 0xcdb30aebL, 0x532e3054L, 0x8fd948e4L, 0x6dbc3128L,
+ 0x58ebf2efL, 0x34c6ffeaL, 0xfe28ed61L, 0xee7c3c73L,
+ 0x5d4a14d9L, 0xe864b7e3L, 0x42105d14L, 0x203e13e0L,
+ 0x45eee2b6L, 0xa3aaabeaL, 0xdb6c4f15L, 0xfacb4fd0L,
+ 0xc742f442L, 0xef6abbb5L, 0x654f3b1dL, 0x41cd2105L,
+ 0xd81e799eL, 0x86854dc7L, 0xe44b476aL, 0x3d816250L,
+ 0xcf62a1f2L, 0x5b8d2646L, 0xfc8883a0L, 0xc1c7b6a3L,
+ 0x7f1524c3L, 0x69cb7492L, 0x47848a0bL, 0x5692b285L,
+ 0x095bbf00L, 0xad19489dL, 0x1462b174L, 0x23820e00L,
+ 0x58428d2aL, 0x0c55f5eaL, 0x1dadf43eL, 0x233f7061L,
+ 0x3372f092L, 0x8d937e41L, 0xd65fecf1L, 0x6c223bdbL,
+ 0x7cde3759L, 0xcbee7460L, 0x4085f2a7L, 0xce77326eL,
+ 0xa6078084L, 0x19f8509eL, 0xe8efd855L, 0x61d99735L,
+ 0xa969a7aaL, 0xc50c06c2L, 0x5a04abfcL, 0x800bcadcL,
+ 0x9e447a2eL, 0xc3453484L, 0xfdd56705L, 0x0e1e9ec9L,
+ 0xdb73dbd3L, 0x105588cdL, 0x675fda79L, 0xe3674340L,
+ 0xc5c43465L, 0x713e38d8L, 0x3d28f89eL, 0xf16dff20L,
+ 0x153e21e7L, 0x8fb03d4aL, 0xe6e39f2bL, 0xdb83adf7L,
+ 0xe93d5a68L, 0x948140f7L, 0xf64c261cL, 0x94692934L,
+ 0x411520f7L, 0x7602d4f7L, 0xbcf46b2eL, 0xd4a20068L,
+ 0xd4082471L, 0x3320f46aL, 0x43b7d4b7L, 0x500061afL,
+ 0x1e39f62eL, 0x97244546L, 0x14214f74L, 0xbf8b8840L,
+ 0x4d95fc1dL, 0x96b591afL, 0x70f4ddd3L, 0x66a02f45L,
+ 0xbfbc09ecL, 0x03bd9785L, 0x7fac6dd0L, 0x31cb8504L,
+ 0x96eb27b3L, 0x55fd3941L, 0xda2547e6L, 0xabca0a9aL,
+ 0x28507825L, 0x530429f4L, 0x0a2c86daL, 0xe9b66dfbL,
+ 0x68dc1462L, 0xd7486900L, 0x680ec0a4L, 0x27a18deeL,
+ 0x4f3ffea2L, 0xe887ad8cL, 0xb58ce006L, 0x7af4d6b6L,
+ 0xaace1e7cL, 0xd3375fecL, 0xce78a399L, 0x406b2a42L,
+ 0x20fe9e35L, 0xd9f385b9L, 0xee39d7abL, 0x3b124e8bL,
+ 0x1dc9faf7L, 0x4b6d1856L, 0x26a36631L, 0xeae397b2L,
+ 0x3a6efa74L, 0xdd5b4332L, 0x6841e7f7L, 0xca7820fbL,
+ 0xfb0af54eL, 0xd8feb397L, 0x454056acL, 0xba489527L,
+ 0x55533a3aL, 0x20838d87L, 0xfe6ba9b7L, 0xd096954bL,
+ 0x55a867bcL, 0xa1159a58L, 0xcca92963L, 0x99e1db33L,
+ 0xa62a4a56L, 0x3f3125f9L, 0x5ef47e1cL, 0x9029317cL,
+ 0xfdf8e802L, 0x04272f70L, 0x80bb155cL, 0x05282ce3L,
+ 0x95c11548L, 0xe4c66d22L, 0x48c1133fL, 0xc70f86dcL,
+ 0x07f9c9eeL, 0x41041f0fL, 0x404779a4L, 0x5d886e17L,
+ 0x325f51ebL, 0xd59bc0d1L, 0xf2bcc18fL, 0x41113564L,
+ 0x257b7834L, 0x602a9c60L, 0xdff8e8a3L, 0x1f636c1bL,
+ 0x0e12b4c2L, 0x02e1329eL, 0xaf664fd1L, 0xcad18115L,
+ 0x6b2395e0L, 0x333e92e1L, 0x3b240b62L, 0xeebeb922L,
+ 0x85b2a20eL, 0xe6ba0d99L, 0xde720c8cL, 0x2da2f728L,
+ 0xd0127845L, 0x95b794fdL, 0x647d0862L, 0xe7ccf5f0L,
+ 0x5449a36fL, 0x877d48faL, 0xc39dfd27L, 0xf33e8d1eL,
+ 0x0a476341L, 0x992eff74L, 0x3a6f6eabL, 0xf4f8fd37L,
+ 0xa812dc60L, 0xa1ebddf8L, 0x991be14cL, 0xdb6e6b0dL,
+ 0xc67b5510L, 0x6d672c37L, 0x2765d43bL, 0xdcd0e804L,
+ 0xf1290dc7L, 0xcc00ffa3L, 0xb5390f92L, 0x690fed0bL,
+ 0x667b9ffbL, 0xcedb7d9cL, 0xa091cf0bL, 0xd9155ea3L,
+ 0xbb132f88L, 0x515bad24L, 0x7b9479bfL, 0x763bd6ebL,
+ 0x37392eb3L, 0xcc115979L, 0x8026e297L, 0xf42e312dL,
+ 0x6842ada7L, 0xc66a2b3bL, 0x12754cccL, 0x782ef11cL,
+ 0x6a124237L, 0xb79251e7L, 0x06a1bbe6L, 0x4bfb6350L,
+ 0x1a6b1018L, 0x11caedfaL, 0x3d25bdd8L, 0xe2e1c3c9L,
+ 0x44421659L, 0x0a121386L, 0xd90cec6eL, 0xd5abea2aL,
+ 0x64af674eL, 0xda86a85fL, 0xbebfe988L, 0x64e4c3feL,
+ 0x9dbc8057L, 0xf0f7c086L, 0x60787bf8L, 0x6003604dL,
+ 0xd1fd8346L, 0xf6381fb0L, 0x7745ae04L, 0xd736fcccL,
+ 0x83426b33L, 0xf01eab71L, 0xb0804187L, 0x3c005e5fL,
+ 0x77a057beL, 0xbde8ae24L, 0x55464299L, 0xbf582e61L,
+ 0x4e58f48fL, 0xf2ddfda2L, 0xf474ef38L, 0x8789bdc2L,
+ 0x5366f9c3L, 0xc8b38e74L, 0xb475f255L, 0x46fcd9b9L,
+ 0x7aeb2661L, 0x8b1ddf84L, 0x846a0e79L, 0x915f95e2L,
+ 0x466e598eL, 0x20b45770L, 0x8cd55591L, 0xc902de4cL,
+ 0xb90bace1L, 0xbb8205d0L, 0x11a86248L, 0x7574a99eL,
+ 0xb77f19b6L, 0xe0a9dc09L, 0x662d09a1L, 0xc4324633L,
+ 0xe85a1f02L, 0x09f0be8cL, 0x4a99a025L, 0x1d6efe10L,
+ 0x1ab93d1dL, 0x0ba5a4dfL, 0xa186f20fL, 0x2868f169L,
+ 0xdcb7da83L, 0x573906feL, 0xa1e2ce9bL, 0x4fcd7f52L,
+ 0x50115e01L, 0xa70683faL, 0xa002b5c4L, 0x0de6d027L,
+ 0x9af88c27L, 0x773f8641L, 0xc3604c06L, 0x61a806b5L,
+ 0xf0177a28L, 0xc0f586e0L, 0x006058aaL, 0x30dc7d62L,
+ 0x11e69ed7L, 0x2338ea63L, 0x53c2dd94L, 0xc2c21634L,
+ 0xbbcbee56L, 0x90bcb6deL, 0xebfc7da1L, 0xce591d76L,
+ 0x6f05e409L, 0x4b7c0188L, 0x39720a3dL, 0x7c927c24L,
+ 0x86e3725fL, 0x724d9db9L, 0x1ac15bb4L, 0xd39eb8fcL,
+ 0xed545578L, 0x08fca5b5L, 0xd83d7cd3L, 0x4dad0fc4L,
+ 0x1e50ef5eL, 0xb161e6f8L, 0xa28514d9L, 0x6c51133cL,
+ 0x6fd5c7e7L, 0x56e14ec4L, 0x362abfceL, 0xddc6c837L,
+ 0xd79a3234L, 0x92638212L, 0x670efa8eL, 0x406000e0L,
+ 0x3a39ce37L, 0xd3faf5cfL, 0xabc27737L, 0x5ac52d1bL,
+ 0x5cb0679eL, 0x4fa33742L, 0xd3822740L, 0x99bc9bbeL,
+ 0xd5118e9dL, 0xbf0f7315L, 0xd62d1c7eL, 0xc700c47bL,
+ 0xb78c1b6bL, 0x21a19045L, 0xb26eb1beL, 0x6a366eb4L,
+ 0x5748ab2fL, 0xbc946e79L, 0xc6a376d2L, 0x6549c2c8L,
+ 0x530ff8eeL, 0x468dde7dL, 0xd5730a1dL, 0x4cd04dc6L,
+ 0x2939bbdbL, 0xa9ba4650L, 0xac9526e8L, 0xbe5ee304L,
+ 0xa1fad5f0L, 0x6a2d519aL, 0x63ef8ce2L, 0x9a86ee22L,
+ 0xc089c2b8L, 0x43242ef6L, 0xa51e03aaL, 0x9cf2d0a4L,
+ 0x83c061baL, 0x9be96a4dL, 0x8fe51550L, 0xba645bd6L,
+ 0x2826a2f9L, 0xa73a3ae1L, 0x4ba99586L, 0xef5562e9L,
+ 0xc72fefd3L, 0xf752f7daL, 0x3f046f69L, 0x77fa0a59L,
+ 0x80e4a915L, 0x87b08601L, 0x9b09e6adL, 0x3b3ee593L,
+ 0xe990fd5aL, 0x9e34d797L, 0x2cf0b7d9L, 0x022b8b51L,
+ 0x96d5ac3aL, 0x017da67dL, 0xd1cf3ed6L, 0x7c7d2d28L,
+ 0x1f9f25cfL, 0xadf2b89bL, 0x5ad6b472L, 0x5a88f54cL,
+ 0xe029ac71L, 0xe019a5e6L, 0x47b0acfdL, 0xed93fa9bL,
+ 0xe8d3c48dL, 0x283b57ccL, 0xf8d56629L, 0x79132e28L,
+ 0x785f0191L, 0xed756055L, 0xf7960e44L, 0xe3d35e8cL,
+ 0x15056dd4L, 0x88f46dbaL, 0x03a16125L, 0x0564f0bdL,
+ 0xc3eb9e15L, 0x3c9057a2L, 0x97271aecL, 0xa93a072aL,
+ 0x1b3f6d9bL, 0x1e6321f5L, 0xf59c66fbL, 0x26dcf319L,
+ 0x7533d928L, 0xb155fdf5L, 0x03563482L, 0x8aba3cbbL,
+ 0x28517711L, 0xc20ad9f8L, 0xabcc5167L, 0xccad925fL,
+ 0x4de81751L, 0x3830dc8eL, 0x379d5862L, 0x9320f991L,
+ 0xea7a90c2L, 0xfb3e7bceL, 0x5121ce64L, 0x774fbe32L,
+ 0xa8b6e37eL, 0xc3293d46L, 0x48de5369L, 0x6413e680L,
+ 0xa2ae0810L, 0xdd6db224L, 0x69852dfdL, 0x09072166L,
+ 0xb39a460aL, 0x6445c0ddL, 0x586cdecfL, 0x1c20c8aeL,
+ 0x5bbef7ddL, 0x1b588d40L, 0xccd2017fL, 0x6bb4e3bbL,
+ 0xdda26a7eL, 0x3a59ff45L, 0x3e350a44L, 0xbcb4cdd5L,
+ 0x72eacea8L, 0xfa6484bbL, 0x8d6612aeL, 0xbf3c6f47L,
+ 0xd29be463L, 0x542f5d9eL, 0xaec2771bL, 0xf64e6370L,
+ 0x740e0d8dL, 0xe75b1357L, 0xf8721671L, 0xaf537d5dL,
+ 0x4040cb08L, 0x4eb4e2ccL, 0x34d2466aL, 0x0115af84L,
+ 0xe1b00428L, 0x95983a1dL, 0x06b89fb4L, 0xce6ea048L,
+ 0x6f3f3b82L, 0x3520ab82L, 0x011a1d4bL, 0x277227f8L,
+ 0x611560b1L, 0xe7933fdcL, 0xbb3a792bL, 0x344525bdL,
+ 0xa08839e1L, 0x51ce794bL, 0x2f32c9b7L, 0xa01fbac9L,
+ 0xe01cc87eL, 0xbcc7d1f6L, 0xcf0111c3L, 0xa1e8aac7L,
+ 0x1a908749L, 0xd44fbd9aL, 0xd0dadecbL, 0xd50ada38L,
+ 0x0339c32aL, 0xc6913667L, 0x8df9317cL, 0xe0b12b4fL,
+ 0xf79e59b7L, 0x43f5bb3aL, 0xf2d519ffL, 0x27d9459cL,
+ 0xbf97222cL, 0x15e6fc2aL, 0x0f91fc71L, 0x9b941525L,
+ 0xfae59361L, 0xceb69cebL, 0xc2a86459L, 0x12baa8d1L,
+ 0xb6c1075eL, 0xe3056a0cL, 0x10d25065L, 0xcb03a442L,
+ 0xe0ec6e0eL, 0x1698db3bL, 0x4c98a0beL, 0x3278e964L,
+ 0x9f1f9532L, 0xe0d392dfL, 0xd3a0342bL, 0x8971f21eL,
+ 0x1b0a7441L, 0x4ba3348cL, 0xc5be7120L, 0xc37632d8L,
+ 0xdf359f8dL, 0x9b992f2eL, 0xe60b6f47L, 0x0fe3f11dL,
+ 0xe54cda54L, 0x1edad891L, 0xce6279cfL, 0xcd3e7e6fL,
+ 0x1618b166L, 0xfd2c1d05L, 0x848fd2c5L, 0xf6fb2299L,
+ 0xf523f357L, 0xa6327623L, 0x93a83531L, 0x56cccd02L,
+ 0xacf08162L, 0x5a75ebb5L, 0x6e163697L, 0x88d273ccL,
+ 0xde966292L, 0x81b949d0L, 0x4c50901bL, 0x71c65614L,
+ 0xe6c6c7bdL, 0x327a140aL, 0x45e1d006L, 0xc3f27b9aL,
+ 0xc9aa53fdL, 0x62a80f00L, 0xbb25bfe2L, 0x35bdd2f6L,
+ 0x71126905L, 0xb2040222L, 0xb6cbcf7cL, 0xcd769c2bL,
+ 0x53113ec0L, 0x1640e3d3L, 0x38abbd60L, 0x2547adf0L,
+ 0xba38209cL, 0xf746ce76L, 0x77afa1c5L, 0x20756060L,
+ 0x85cbfe4eL, 0x8ae88dd8L, 0x7aaaf9b0L, 0x4cf9aa7eL,
+ 0x1948c25cL, 0x02fb8a8cL, 0x01c36ae4L, 0xd6ebe1f9L,
+ 0x90d4f869L, 0xa65cdea0L, 0x3f09252dL, 0xc208e69fL,
+ 0xb74e6132L, 0xce77e25bL, 0x578fdfe3L, 0x3ac372e6L,
+};
+
--- /dev/null
+++ b/libsec/ccpoly.c
@@ -1,0 +1,90 @@
+#include "os.h"
+#include <libsec.h>
+
+static void
+ccpolyotk(Chachastate *cs, DigestState *ds)
+{+ uchar otk[ChachaBsize];
+
+ memset(ds, 0, sizeof(*ds));
+ memset(otk, 0, 32);
+ chacha_setblock(cs, 0);
+ chacha_encrypt(otk, ChachaBsize, cs);
+ poly1305(nil, 0, otk, 32, nil, ds);
+}
+
+static void
+ccpolypad(uchar *buf, ulong nbuf, DigestState *ds)
+{+ static uchar zeros[16] = {0};+ ulong npad;
+
+ if(nbuf == 0)
+ return;
+ poly1305(buf, nbuf, nil, 0, nil, ds);
+ npad = nbuf % 16;
+ if(npad == 0)
+ return;
+ poly1305(zeros, 16 - npad, nil, 0, nil, ds);
+}
+
+static void
+ccpolylen(ulong n, uchar tag[16], DigestState *ds)
+{+ uchar info[8];
+
+ info[0] = n;
+ info[1] = n>>8;
+ info[2] = n>>16;
+ info[3] = n>>24;
+ info[4] = 0;
+ info[5] = 0;
+ info[6] = 0;
+ info[7] = 0;
+ poly1305(info, 8, nil, 0, tag, ds);
+}
+
+void
+ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs)
+{+ DigestState ds;
+
+ ccpolyotk(cs, &ds);
+ if(cs->ivwords == 2){+ poly1305(aad, naad, nil, 0, nil, &ds);
+ ccpolylen(naad, nil, &ds);
+ chacha_encrypt(dat, ndat, cs);
+ poly1305(dat, ndat, nil, 0, nil, &ds);
+ ccpolylen(ndat, tag, &ds);
+ } else {+ ccpolypad(aad, naad, &ds);
+ chacha_encrypt(dat, ndat, cs);
+ ccpolypad(dat, ndat, &ds);
+ ccpolylen(naad, nil, &ds);
+ ccpolylen(ndat, tag, &ds);
+ }
+}
+
+int
+ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs)
+{+ DigestState ds;
+ uchar tmp[16];
+
+ ccpolyotk(cs, &ds);
+ if(cs->ivwords == 2){+ poly1305(aad, naad, nil, 0, nil, &ds);
+ ccpolylen(naad, nil, &ds);
+ poly1305(dat, ndat, nil, 0, nil, &ds);
+ ccpolylen(ndat, tmp, &ds);
+ } else {+ ccpolypad(aad, naad, &ds);
+ ccpolypad(dat, ndat, &ds);
+ ccpolylen(naad, nil, &ds);
+ ccpolylen(ndat, tmp, &ds);
+ }
+ if(tsmemcmp(tag, tmp, 16) != 0)
+ return -1;
+ chacha_encrypt(dat, ndat, cs);
+ return 0;
+}
--- /dev/null
+++ b/libsec/chacha.c
@@ -1,0 +1,222 @@
+/*
+Adapted from chacha-merged.c version 20080118
+D. J. Bernstein
+Public domain.
+
+modified for use in Plan 9 and Inferno (no algorithmic changes),
+and including the changes to block number and nonce defined in RFC7539
+*/
+
+#include "os.h"
+#include <libsec.h>
+
+/* from chachablock.$O */
+extern void _chachablock(u32int x[16], int rounds);
+
+/* little-endian data order */
+#define GET4(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define PUT4(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+
+#define ENCRYPT(s, x, y, d) {\+ u32int v; \
+ v = GET4(s); \
+ v ^= (x)+(y); \
+ PUT4(d, v); \
+}
+
+static uchar sigma[16] = "expand 32-byte k";
+static uchar tau[16] = "expand 16-byte k";
+
+static void
+load(u32int *d, uchar *s, int nw)
+{+ int i;
+
+ for(i = 0; i < nw; i++, s+=4)
+ d[i] = GET4(s);
+}
+
+void
+setupChachastate(Chachastate *s, uchar *key, ulong keylen, uchar *iv, ulong ivlen, int rounds)
+{+ if(keylen != 256/8 && keylen != 128/8)
+ sysfatal("invalid chacha key length");+ if(ivlen != 64/8 && ivlen != 96/8
+ && ivlen != 128/8 && ivlen != 192/8) /* hchacha, xchacha */
+ sysfatal("invalid chacha iv length");+ if(rounds == 0)
+ rounds = 20;
+ s->rounds = rounds;
+ if(keylen == 256/8) { /* recommended */+ load(&s->input[0], sigma, 4);
+ load(&s->input[4], key, 8);
+ }else{+ load(&s->input[0], tau, 4);
+ load(&s->input[4], key, 4);
+ load(&s->input[8], key, 4);
+ }
+ s->xkey[0] = s->input[4];
+ s->xkey[1] = s->input[5];
+ s->xkey[2] = s->input[6];
+ s->xkey[3] = s->input[7];
+ s->xkey[4] = s->input[8];
+ s->xkey[5] = s->input[9];
+ s->xkey[6] = s->input[10];
+ s->xkey[7] = s->input[11];
+
+ s->ivwords = ivlen/4;
+ s->input[12] = 0;
+ s->input[13] = 0;
+ if(iv == nil){+ s->input[14] = 0;
+ s->input[15] = 0;
+ }else
+ chacha_setiv(s, iv);
+}
+
+static void
+hchachablock(uchar h[32], Chachastate *s)
+{+ u32int x[16];
+
+ x[0] = s->input[0];
+ x[1] = s->input[1];
+ x[2] = s->input[2];
+ x[3] = s->input[3];
+ x[4] = s->input[4];
+ x[5] = s->input[5];
+ x[6] = s->input[6];
+ x[7] = s->input[7];
+ x[8] = s->input[8];
+ x[9] = s->input[9];
+ x[10] = s->input[10];
+ x[11] = s->input[11];
+ x[12] = s->input[12];
+ x[13] = s->input[13];
+ x[14] = s->input[14];
+ x[15] = s->input[15];
+
+ _chachablock(x, s->rounds);
+
+ PUT4(h+0*4, x[0]);
+ PUT4(h+1*4, x[1]);
+ PUT4(h+2*4, x[2]);
+ PUT4(h+3*4, x[3]);
+ PUT4(h+4*4, x[12]);
+ PUT4(h+5*4, x[13]);
+ PUT4(h+6*4, x[14]);
+ PUT4(h+7*4, x[15]);
+}
+
+void
+chacha_setiv(Chachastate *s, uchar *iv)
+{+ if(s->ivwords == 192/32){+ /* xchacha with 192-bit iv */
+ u32int counter[2];
+ uchar h[32];
+
+ s->input[4] = s->xkey[0];
+ s->input[5] = s->xkey[1];
+ s->input[6] = s->xkey[2];
+ s->input[7] = s->xkey[3];
+ s->input[8] = s->xkey[4];
+ s->input[9] = s->xkey[5];
+ s->input[10] = s->xkey[6];
+ s->input[11] = s->xkey[7];
+
+ counter[0] = s->input[12];
+ counter[1] = s->input[13];
+
+ load(&s->input[12], iv, 4);
+
+ hchachablock(h, s);
+ load(&s->input[4], h, 8);
+ memset(h, 0, 32);
+
+ s->input[12] = counter[0];
+ s->input[13] = counter[1];
+
+ load(&s->input[14], iv+16, 2);
+ return;
+ }
+ load(&s->input[16 - s->ivwords], iv, s->ivwords);
+}
+
+void
+chacha_setblock(Chachastate *s, u64int blockno)
+{+ s->input[12] = blockno;
+ if(s->ivwords != 3)
+ s->input[13] = blockno>>32;
+}
+
+static void
+encryptblock(Chachastate *s, uchar *src, uchar *dst)
+{+ u32int x[16];
+ int i;
+
+ x[0] = s->input[0];
+ x[1] = s->input[1];
+ x[2] = s->input[2];
+ x[3] = s->input[3];
+ x[4] = s->input[4];
+ x[5] = s->input[5];
+ x[6] = s->input[6];
+ x[7] = s->input[7];
+ x[8] = s->input[8];
+ x[9] = s->input[9];
+ x[10] = s->input[10];
+ x[11] = s->input[11];
+ x[12] = s->input[12];
+ x[13] = s->input[13];
+ x[14] = s->input[14];
+ x[15] = s->input[15];
+ _chachablock(x, s->rounds);
+
+ for(i=0; i<nelem(x); i+=4){+ ENCRYPT(src, x[i], s->input[i], dst);
+ ENCRYPT(src+4, x[i+1], s->input[i+1], dst+4);
+ ENCRYPT(src+8, x[i+2], s->input[i+2], dst+8);
+ ENCRYPT(src+12, x[i+3], s->input[i+3], dst+12);
+ src += 16;
+ dst += 16;
+ }
+
+ if(++s->input[12] == 0 && s->ivwords != 3)
+ s->input[13]++;
+}
+
+void
+chacha_encrypt2(uchar *src, uchar *dst, ulong bytes, Chachastate *s)
+{+ uchar tmp[ChachaBsize];
+
+ for(; bytes >= ChachaBsize; bytes -= ChachaBsize){+ encryptblock(s, src, dst);
+ src += ChachaBsize;
+ dst += ChachaBsize;
+ }
+ if(bytes > 0){+ memmove(tmp, src, bytes);
+ encryptblock(s, tmp, tmp);
+ memmove(dst, tmp, bytes);
+ }
+}
+
+void
+chacha_encrypt(uchar *buf, ulong bytes, Chachastate *s)
+{+ chacha_encrypt2(buf, buf, bytes, s);
+}
+
+void
+hchacha(uchar h[32], uchar *key, ulong keylen, uchar nonce[16], int rounds)
+{+ Chachastate s[1];
+
+ setupChachastate(s, key, keylen, nonce, 16, rounds);
+ hchachablock(h, s);
+ memset(s, 0, sizeof(s));
+}
--- /dev/null
+++ b/libsec/chachablock.c
@@ -1,0 +1,29 @@
+#include "os.h"
+
+#define ROTATE(v,c) ((u32int)((v) << (c)) | ((v) >> (32 - (c))))
+
+#define QUARTERROUND(ia,ib,ic,id) { \+ u32int a, b, c, d, t; \
+ a = x[ia]; b = x[ib]; c = x[ic]; d = x[id]; \
+ a += b; t = d^a; d = ROTATE(t,16); \
+ c += d; t = b^c; b = ROTATE(t,12); \
+ a += b; t = d^a; d = ROTATE(t, 8); \
+ c += d; t = b^c; b = ROTATE(t, 7); \
+ x[ia] = a; x[ib] = b; x[ic] = c; x[id] = d; \
+}
+
+void
+_chachablock(u32int x[16], int rounds)
+{+ for(; rounds > 0; rounds -= 2) {+ QUARTERROUND(0, 4, 8,12)
+ QUARTERROUND(1, 5, 9,13)
+ QUARTERROUND(2, 6,10,14)
+ QUARTERROUND(3, 7,11,15)
+
+ QUARTERROUND(0, 5,10,15)
+ QUARTERROUND(1, 6,11,12)
+ QUARTERROUND(2, 7, 8,13)
+ QUARTERROUND(3, 4, 9,14)
+ }
+}
--- /dev/null
+++ b/libsec/curve25519.c
@@ -1,0 +1,570 @@
+/* Copyright 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * curve25519: Curve25519 elliptic curve, public key function
+ *
+ * http://code.google.com/p/curve25519-donna/
+ *
+ * Adam Langley <agl@imperialviolet.org>
+ *
+ * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+ *
+ * More information about curve25519 can be found here
+ * http://cr.yp.to/ecdh.html
+ *
+ * djb's sample implementation of curve25519 is written in a special assembly
+ * language called qhasm and uses the floating point registers.
+ *
+ * This is, almost, a clean room reimplementation from the curve25519 paper. It
+ * uses many of the tricks described therein. Only the crecip function is taken
+ * from the sample implementation.
+ */
+#include "os.h"
+#include <libsec.h>
+
+typedef vlong felem;
+
+/* Sum two numbers: output += in */
+static void fsum(felem *output, felem *in) {+ unsigned i;
+ for (i = 0; i < 10; i += 2) {+ output[0+i] = (output[0+i] + in[0+i]);
+ output[1+i] = (output[1+i] + in[1+i]);
+ }
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!)
+ */
+static void fdifference(felem *output, felem *in) {+ unsigned i;
+ for (i = 0; i < 10; ++i) {+ output[i] = (in[i] - output[i]);
+ }
+}
+
+/* Multiply a number my a scalar: output = in * scalar */
+static void fscalar_product(felem *output, felem *in, felem scalar) {+ unsigned i;
+ for (i = 0; i < 10; ++i) {+ output[i] = in[i] * scalar;
+ }
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ */
+static void fproduct(felem *output, felem *in2, felem *in) {+ output[0] = in2[0] * in[0];
+ output[1] = in2[0] * in[1] +
+ in2[1] * in[0];
+ output[2] = 2 * in2[1] * in[1] +
+ in2[0] * in[2] +
+ in2[2] * in[0];
+ output[3] = in2[1] * in[2] +
+ in2[2] * in[1] +
+ in2[0] * in[3] +
+ in2[3] * in[0];
+ output[4] = in2[2] * in[2] +
+ 2 * (in2[1] * in[3] +
+ in2[3] * in[1]) +
+ in2[0] * in[4] +
+ in2[4] * in[0];
+ output[5] = in2[2] * in[3] +
+ in2[3] * in[2] +
+ in2[1] * in[4] +
+ in2[4] * in[1] +
+ in2[0] * in[5] +
+ in2[5] * in[0];
+ output[6] = 2 * (in2[3] * in[3] +
+ in2[1] * in[5] +
+ in2[5] * in[1]) +
+ in2[2] * in[4] +
+ in2[4] * in[2] +
+ in2[0] * in[6] +
+ in2[6] * in[0];
+ output[7] = in2[3] * in[4] +
+ in2[4] * in[3] +
+ in2[2] * in[5] +
+ in2[5] * in[2] +
+ in2[1] * in[6] +
+ in2[6] * in[1] +
+ in2[0] * in[7] +
+ in2[7] * in[0];
+ output[8] = in2[4] * in[4] +
+ 2 * (in2[3] * in[5] +
+ in2[5] * in[3] +
+ in2[1] * in[7] +
+ in2[7] * in[1]) +
+ in2[2] * in[6] +
+ in2[6] * in[2] +
+ in2[0] * in[8] +
+ in2[8] * in[0];
+ output[9] = in2[4] * in[5] +
+ in2[5] * in[4] +
+ in2[3] * in[6] +
+ in2[6] * in[3] +
+ in2[2] * in[7] +
+ in2[7] * in[2] +
+ in2[1] * in[8] +
+ in2[8] * in[1] +
+ in2[0] * in[9] +
+ in2[9] * in[0];
+ output[10] = 2 * (in2[5] * in[5] +
+ in2[3] * in[7] +
+ in2[7] * in[3] +
+ in2[1] * in[9] +
+ in2[9] * in[1]) +
+ in2[4] * in[6] +
+ in2[6] * in[4] +
+ in2[2] * in[8] +
+ in2[8] * in[2];
+ output[11] = in2[5] * in[6] +
+ in2[6] * in[5] +
+ in2[4] * in[7] +
+ in2[7] * in[4] +
+ in2[3] * in[8] +
+ in2[8] * in[3] +
+ in2[2] * in[9] +
+ in2[9] * in[2];
+ output[12] = in2[6] * in[6] +
+ 2 * (in2[5] * in[7] +
+ in2[7] * in[5] +
+ in2[3] * in[9] +
+ in2[9] * in[3]) +
+ in2[4] * in[8] +
+ in2[8] * in[4];
+ output[13] = in2[6] * in[7] +
+ in2[7] * in[6] +
+ in2[5] * in[8] +
+ in2[8] * in[5] +
+ in2[4] * in[9] +
+ in2[9] * in[4];
+ output[14] = 2 * (in2[7] * in[7] +
+ in2[5] * in[9] +
+ in2[9] * in[5]) +
+ in2[6] * in[8] +
+ in2[8] * in[6];
+ output[15] = in2[7] * in[8] +
+ in2[8] * in[7] +
+ in2[6] * in[9] +
+ in2[9] * in[6];
+ output[16] = in2[8] * in[8] +
+ 2 * (in2[7] * in[9] +
+ in2[9] * in[7]);
+ output[17] = in2[8] * in[9] +
+ in2[9] * in[8];
+ output[18] = 2 * in2[9] * in[9];
+}
+
+/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */
+static void freduce_degree(felem *output) {+ output[8] += 19 * output[18];
+ output[7] += 19 * output[17];
+ output[6] += 19 * output[16];
+ output[5] += 19 * output[15];
+ output[4] += 19 * output[14];
+ output[3] += 19 * output[13];
+ output[2] += 19 * output[12];
+ output[1] += 19 * output[11];
+ output[0] += 19 * output[10];
+}
+
+/* Reduce all coefficients of the short form input to be -2**25 <= x <= 2**25
+ */
+static void freduce_coefficients(felem *output) {+ unsigned i;
+ do {+ output[10] = 0;
+
+ for (i = 0; i < 10; i += 2) {+ felem over = output[i] / 0x2000000l;
+ felem over2 = (over + ((over >> 63) * 2) + 1) / 2;
+ output[i+1] += over2;
+ output[i] -= over2 * 0x4000000l;
+
+ over = output[i+1] / 0x2000000;
+ output[i+2] += over;
+ output[i+1] -= over * 0x2000000;
+ }
+ output[0] += 19 * output[10];
+ } while (output[10]);
+}
+
+/* A helpful wrapper around fproduct: output = in * in2.
+ *
+ * output must be distinct to both inputs. The output is reduced degree and
+ * reduced coefficient.
+ */
+static void
+fmult(felem *output, felem *in, felem *in2) {+ felem t[19];
+ fproduct(t, in, in2);
+ freduce_degree(t);
+ freduce_coefficients(t);
+ memcpy(output, t, sizeof(felem) * 10);
+}
+
+static void fsquare_inner(felem *output, felem *in) {+ felem tmp;
+ output[0] = in[0] * in[0];
+ output[1] = 2 * in[0] * in[1];
+ output[2] = 2 * (in[1] * in[1] +
+ in[0] * in[2]);
+ output[3] = 2 * (in[1] * in[2] +
+ in[0] * in[3]);
+ output[4] = in[2] * in[2] +
+ 4 * in[1] * in[3] +
+ 2 * in[0] * in[4];
+ output[5] = 2 * (in[2] * in[3] +
+ in[1] * in[4] +
+ in[0] * in[5]);
+ output[6] = 2 * (in[3] * in[3] +
+ in[2] * in[4] +
+ in[0] * in[6] +
+ 2 * in[1] * in[5]);
+ output[7] = 2 * (in[3] * in[4] +
+ in[2] * in[5] +
+ in[1] * in[6] +
+ in[0] * in[7]);
+ tmp = in[1] * in[7] + in[3] * in[5];
+ output[8] = in[4] * in[4] +
+ 2 * (in[2] * in[6] +
+ in[0] * in[8] +
+ 2 * tmp);
+ output[9] = 2 * (in[4] * in[5] +
+ in[3] * in[6] +
+ in[2] * in[7] +
+ in[1] * in[8] +
+ in[0] * in[9]);
+ tmp = in[3] * in[7] + in[1] * in[9];
+ output[10] = 2 * (in[5] * in[5] +
+ in[4] * in[6] +
+ in[2] * in[8] +
+ 2 * tmp);
+ output[11] = 2 * (in[5] * in[6] +
+ in[4] * in[7] +
+ in[3] * in[8] +
+ in[2] * in[9]);
+ output[12] = in[6] * in[6] +
+ 2 * (in[4] * in[8] +
+ 2 * (in[5] * in[7] +
+ in[3] * in[9]));
+ output[13] = 2 * (in[6] * in[7] +
+ in[5] * in[8] +
+ in[4] * in[9]);
+ output[14] = 2 * (in[7] * in[7] +
+ in[6] * in[8] +
+ 2 * in[5] * in[9]);
+ output[15] = 2 * (in[7] * in[8] +
+ in[6] * in[9]);
+ output[16] = in[8] * in[8] +
+ 4 * in[7] * in[9];
+ output[17] = 2 * in[8] * in[9];
+ output[18] = 2 * in[9] * in[9];
+}
+
+static void
+fsquare(felem *output, felem *in) {+ felem t[19];
+ fsquare_inner(t, in);
+ freduce_degree(t);
+ freduce_coefficients(t);
+ memcpy(output, t, sizeof(felem) * 10);
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void
+fexpand(felem *output, uchar *input) {+#define F(n,start,shift,mask) \
+ output[n] = ((((felem) input[start + 0]) | \
+ ((felem) input[start + 1]) << 8 | \
+ ((felem) input[start + 2]) << 16 | \
+ ((felem) input[start + 3]) << 24) >> shift) & mask;
+ F(0, 0, 0, 0x3ffffff);
+ F(1, 3, 2, 0x1ffffff);
+ F(2, 6, 3, 0x3ffffff);
+ F(3, 9, 5, 0x1ffffff);
+ F(4, 12, 6, 0x3ffffff);
+ F(5, 16, 0, 0x1ffffff);
+ F(6, 19, 1, 0x3ffffff);
+ F(7, 22, 3, 0x1ffffff);
+ F(8, 25, 4, 0x3ffffff);
+ F(9, 28, 6, 0x1ffffff);
+#undef F
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array
+ */
+static void
+fcontract(uchar *output, felem *input) {+ int i;
+
+ do {+ for (i = 0; i < 9; ++i) {+ if ((i & 1) == 1) {+ while (input[i] < 0) {+ input[i] += 0x2000000;
+ input[i + 1]--;
+ }
+ } else {+ while (input[i] < 0) {+ input[i] += 0x4000000;
+ input[i + 1]--;
+ }
+ }
+ }
+ while (input[9] < 0) {+ input[9] += 0x2000000;
+ input[0] -= 19;
+ }
+ } while (input[0] < 0);
+
+ input[1] <<= 2;
+ input[2] <<= 3;
+ input[3] <<= 5;
+ input[4] <<= 6;
+ input[6] <<= 1;
+ input[7] <<= 3;
+ input[8] <<= 4;
+ input[9] <<= 6;
+#define F(i, s) \
+ output[s+0] |= input[i] & 0xff; \
+ output[s+1] = (input[i] >> 8) & 0xff; \
+ output[s+2] = (input[i] >> 16) & 0xff; \
+ output[s+3] = (input[i] >> 24) & 0xff;
+ output[0] = 0;
+ output[16] = 0;
+ F(0,0);
+ F(1,3);
+ F(2,6);
+ F(3,9);
+ F(4,12);
+ F(5,16);
+ F(6,19);
+ F(7,22);
+ F(8,25);
+ F(9,28);
+#undef F
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ * x2 z3: long form
+ * x3 z3: long form
+ * x z: short form, destroyed
+ * xprime zprime: short form, destroyed
+ * qmqp: short form, preserved
+ */
+static void fmonty(felem *x2, felem *z2, /* output 2Q */
+ felem *x3, felem *z3, /* output Q + Q' */
+ felem *x, felem *z, /* input Q */
+ felem *xprime, felem *zprime, /* input Q' */
+ felem *qmqp /* input Q - Q' */) {+ felem origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
+ zzprime[19], zzzprime[19], xxxprime[19];
+
+ memcpy(origx, x, 10 * sizeof(felem));
+ fsum(x, z);
+ fdifference(z, origx); // does x - z
+
+ memcpy(origxprime, xprime, sizeof(felem) * 10);
+ fsum(xprime, zprime);
+ fdifference(zprime, origxprime);
+ fproduct(xxprime, xprime, z);
+ fproduct(zzprime, x, zprime);
+ freduce_degree(xxprime);
+ freduce_coefficients(xxprime);
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ memcpy(origxprime, xxprime, sizeof(felem) * 10);
+ fsum(xxprime, zzprime);
+ fdifference(zzprime, origxprime);
+ fsquare(xxxprime, xxprime);
+ fsquare(zzzprime, zzprime);
+ fproduct(zzprime, zzzprime, qmqp);
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ memcpy(x3, xxxprime, sizeof(felem) * 10);
+ memcpy(z3, zzprime, sizeof(felem) * 10);
+
+ fsquare(xx, x);
+ fsquare(zz, z);
+ fproduct(x2, xx, zz);
+ freduce_degree(x2);
+ freduce_coefficients(x2);
+ fdifference(zz, xx); // does zz = xx - zz
+ memset(zzz + 10, 0, sizeof(felem) * 9);
+ fscalar_product(zzz, zz, 121665);
+ freduce_degree(zzz);
+ freduce_coefficients(zzz);
+ fsum(zzz, xx);
+ fproduct(z2, zz, zzz);
+ freduce_degree(z2);
+ freduce_coefficients(z2);
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * resultx/resultz: the x coordinate of the resulting curve point (short form)
+ * n: a little endian, 32-byte number
+ * q: a point of the curve (short form)
+ */
+static void
+cmult(felem *resultx, felem *resultz, uchar *n, felem *q) {+ felem a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};+ felem *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+ felem e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};+ felem *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+ unsigned i, j;
+
+ memcpy(nqpqx, q, sizeof(felem) * 10);
+
+ for (i = 0; i < 32; ++i) {+ uchar byte = n[31 - i];
+ for (j = 0; j < 8; ++j) {+ if (byte & 0x80) {+ fmonty(nqpqx2, nqpqz2,
+ nqx2, nqz2,
+ nqpqx, nqpqz,
+ nqx, nqz,
+ q);
+ } else {+ fmonty(nqx2, nqz2,
+ nqpqx2, nqpqz2,
+ nqx, nqz,
+ nqpqx, nqpqz,
+ q);
+ }
+
+ t = nqx;
+ nqx = nqx2;
+ nqx2 = t;
+ t = nqz;
+ nqz = nqz2;
+ nqz2 = t;
+ t = nqpqx;
+ nqpqx = nqpqx2;
+ nqpqx2 = t;
+ t = nqpqz;
+ nqpqz = nqpqz2;
+ nqpqz2 = t;
+
+ byte <<= 1;
+ }
+ }
+
+ memcpy(resultx, nqx, sizeof(felem) * 10);
+ memcpy(resultz, nqz, sizeof(felem) * 10);
+}
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code
+// -----------------------------------------------------------------------------
+static void
+crecip(felem *out, felem *z) {+ felem z2[10];
+ felem z9[10];
+ felem z11[10];
+ felem z2_5_0[10];
+ felem z2_10_0[10];
+ felem z2_20_0[10];
+ felem z2_50_0[10];
+ felem z2_100_0[10];
+ felem t0[10];
+ felem t1[10];
+ int i;
+
+ /* 2 */ fsquare(z2,z);
+ /* 4 */ fsquare(t1,z2);
+ /* 8 */ fsquare(t0,t1);
+ /* 9 */ fmult(z9,t0,z);
+ /* 11 */ fmult(z11,z9,z2);
+ /* 22 */ fsquare(t0,z11);
+ /* 2^5 - 2^0 = 31 */ fmult(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
+ /* 2^7 - 2^2 */ fsquare(t1,t0);
+ /* 2^8 - 2^3 */ fsquare(t0,t1);
+ /* 2^9 - 2^4 */ fsquare(t1,t0);
+ /* 2^10 - 2^5 */ fsquare(t0,t1);
+ /* 2^10 - 2^0 */ fmult(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
+ /* 2^12 - 2^2 */ fsquare(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^20 - 2^0 */ fmult(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
+ /* 2^22 - 2^2 */ fsquare(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^40 - 2^0 */ fmult(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ fsquare(t1,t0);
+ /* 2^42 - 2^2 */ fsquare(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }+ /* 2^50 - 2^0 */ fmult(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
+ /* 2^52 - 2^2 */ fsquare(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^100 - 2^0 */ fmult(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
+ /* 2^102 - 2^2 */ fsquare(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }+ /* 2^200 - 2^0 */ fmult(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ fsquare(t0,t1);
+ /* 2^202 - 2^2 */ fsquare(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }+ /* 2^250 - 2^0 */ fmult(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ fsquare(t1,t0);
+ /* 2^252 - 2^2 */ fsquare(t0,t1);
+ /* 2^253 - 2^3 */ fsquare(t1,t0);
+ /* 2^254 - 2^4 */ fsquare(t0,t1);
+ /* 2^255 - 2^5 */ fsquare(t1,t0);
+ /* 2^255 - 21 */ fmult(out,t1,z11);
+}
+
+void
+curve25519(uchar mypublic[32], uchar secret[32], uchar basepoint[32]) {+ felem bp[10], x[10], z[10], zmone[10];
+ fexpand(bp, basepoint);
+ cmult(x, z, secret, bp);
+ crecip(zmone, z);
+ fmult(z, x, zmone);
+ fcontract(mypublic, z);
+}
--- /dev/null
+++ b/libsec/curve25519_dh.c
@@ -1,0 +1,37 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+static uchar nine[32] = {9};+static uchar zero[32] = {0};+
+void
+curve25519_dh_new(uchar x[32], uchar y[32])
+{+ uchar b;
+
+ /* new public/private key pair */
+ genrandom(x, 32);
+ b = x[31];
+ x[0] &= ~7; /* clear bit 0,1,2 */
+ x[31] = 0x40 | (b & 0x7f); /* set bit 254, clear bit 255 */
+ curve25519(y, x, nine);
+
+ /* bit 255 is always 0, so make it random */
+ y[31] |= b & 0x80;
+}
+
+int
+curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32])
+{+ /* remove the random bit */
+ y[31] &= 0x7f;
+
+ /* calculate dhx key */
+ curve25519(z, x, y);
+
+ memset(x, 0, 32);
+ memset(y, 0, 32);
+
+ return tsmemcmp(z, zero, 32) != 0;
+}
--- /dev/null
+++ b/libsec/decodepem.c
@@ -1,0 +1,89 @@
+#include "os.h"
+#include <libsec.h>
+
+#define STRLEN(s) (sizeof(s)-1)
+
+uchar*
+decodePEM(char *s, char *type, int *len, char **new_s)
+{+ uchar *d;
+ char *t, *e, *tt;
+ int n;
+
+ *len = 0;
+
+ /*
+ * find the correct section of the file, stripping garbage at the beginning and end.
+ * the data is delimited by -----BEGIN <type>-----\n and -----END <type>-----\n
+ */
+ n = strlen(type);
+ e = strchr(s, '\0');
+ for(t = s; t != nil && t < e; ){+ tt = t;
+ t = strchr(tt, '\n');
+ if(t != nil)
+ t++;
+ if(strncmp(tt, "-----BEGIN ", STRLEN("-----BEGIN ")) == 0+ && strncmp(&tt[STRLEN("-----BEGIN ")], type, n) == 0+ && strncmp(&tt[STRLEN("-----BEGIN ")+n], "-----", STRLEN("-----")) == 0+ && strchr("\r\n", tt[STRLEN("-----BEGIN ")+n+STRLEN("-----")]) != nil)+ break;
+ }
+ for(tt = t; tt != nil && tt < e; tt++){+ if(strncmp(tt, "-----END ", STRLEN("-----END ")) == 0+ && strncmp(&tt[STRLEN("-----END ")], type, n) == 0+ && strncmp(&tt[STRLEN("-----END ")+n], "-----", STRLEN("-----")) == 0+ && strchr("\r\n", tt[STRLEN("-----END ")+n+STRLEN("-----")]) != nil)+ break;
+ tt = strchr(tt, '\n');
+ if(tt == nil)
+ break;
+ }
+ if(tt == nil || tt == e){+ werrstr("incorrect .pem file format: bad header or trailer");+ return nil;
+ }
+
+ if(new_s)
+ *new_s = tt+1;
+ n = ((tt - t) * 6 + 7) / 8;
+ d = malloc(n);
+ if(d == nil){+ werrstr("out of memory");+ return nil;
+ }
+ n = dec64(d, n, t, tt - t);
+ if(n < 0){+ free(d);
+ werrstr("incorrect .pem file format: bad base64 encoded data");+ return nil;
+ }
+ *len = n;
+ return d;
+}
+
+PEMChain*
+decodepemchain(char *s, char *type)
+{+ PEMChain *first = nil, *last = nil, *chp;
+ uchar *d;
+ char *e;
+ int n;
+
+ e = strchr(s, '\0');
+ while (s < e) {+ d = decodePEM(s, type, &n, &s);
+ if(d == nil)
+ break;
+ chp = malloc(sizeof(PEMChain));
+ chp->next = nil;
+ chp->pem = d;
+ chp->pemlen = n;
+ if (first == nil)
+ first = chp;
+ else
+ last->next = chp;
+ last = chp;
+ }
+ return first;
+}
--- /dev/null
+++ b/libsec/des.c
@@ -1,0 +1,480 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * integrated sbox & p perm
+ */
+static u32int spbox[] = {+
+0x00808200,0x00000000,0x00008000,0x00808202,0x00808002,0x00008202,0x00000002,0x00008000,
+0x00000200,0x00808200,0x00808202,0x00000200,0x00800202,0x00808002,0x00800000,0x00000002,
+0x00000202,0x00800200,0x00800200,0x00008200,0x00008200,0x00808000,0x00808000,0x00800202,
+0x00008002,0x00800002,0x00800002,0x00008002,0x00000000,0x00000202,0x00008202,0x00800000,
+0x00008000,0x00808202,0x00000002,0x00808000,0x00808200,0x00800000,0x00800000,0x00000200,
+0x00808002,0x00008000,0x00008200,0x00800002,0x00000200,0x00000002,0x00800202,0x00008202,
+0x00808202,0x00008002,0x00808000,0x00800202,0x00800002,0x00000202,0x00008202,0x00808200,
+0x00000202,0x00800200,0x00800200,0x00000000,0x00008002,0x00008200,0x00000000,0x00808002,
+
+0x40084010,0x40004000,0x00004000,0x00084010,0x00080000,0x00000010,0x40080010,0x40004010,
+0x40000010,0x40084010,0x40084000,0x40000000,0x40004000,0x00080000,0x00000010,0x40080010,
+0x00084000,0x00080010,0x40004010,0x00000000,0x40000000,0x00004000,0x00084010,0x40080000,
+0x00080010,0x40000010,0x00000000,0x00084000,0x00004010,0x40084000,0x40080000,0x00004010,
+0x00000000,0x00084010,0x40080010,0x00080000,0x40004010,0x40080000,0x40084000,0x00004000,
+0x40080000,0x40004000,0x00000010,0x40084010,0x00084010,0x00000010,0x00004000,0x40000000,
+0x00004010,0x40084000,0x00080000,0x40000010,0x00080010,0x40004010,0x40000010,0x00080010,
+0x00084000,0x00000000,0x40004000,0x00004010,0x40000000,0x40080010,0x40084010,0x00084000,
+
+0x00000104,0x04010100,0x00000000,0x04010004,0x04000100,0x00000000,0x00010104,0x04000100,
+0x00010004,0x04000004,0x04000004,0x00010000,0x04010104,0x00010004,0x04010000,0x00000104,
+0x04000000,0x00000004,0x04010100,0x00000100,0x00010100,0x04010000,0x04010004,0x00010104,
+0x04000104,0x00010100,0x00010000,0x04000104,0x00000004,0x04010104,0x00000100,0x04000000,
+0x04010100,0x04000000,0x00010004,0x00000104,0x00010000,0x04010100,0x04000100,0x00000000,
+0x00000100,0x00010004,0x04010104,0x04000100,0x04000004,0x00000100,0x00000000,0x04010004,
+0x04000104,0x00010000,0x04000000,0x04010104,0x00000004,0x00010104,0x00010100,0x04000004,
+0x04010000,0x04000104,0x00000104,0x04010000,0x00010104,0x00000004,0x04010004,0x00010100,
+
+0x80401000,0x80001040,0x80001040,0x00000040,0x00401040,0x80400040,0x80400000,0x80001000,
+0x00000000,0x00401000,0x00401000,0x80401040,0x80000040,0x00000000,0x00400040,0x80400000,
+0x80000000,0x00001000,0x00400000,0x80401000,0x00000040,0x00400000,0x80001000,0x00001040,
+0x80400040,0x80000000,0x00001040,0x00400040,0x00001000,0x00401040,0x80401040,0x80000040,
+0x00400040,0x80400000,0x00401000,0x80401040,0x80000040,0x00000000,0x00000000,0x00401000,
+0x00001040,0x00400040,0x80400040,0x80000000,0x80401000,0x80001040,0x80001040,0x00000040,
+0x80401040,0x80000040,0x80000000,0x00001000,0x80400000,0x80001000,0x00401040,0x80400040,
+0x80001000,0x00001040,0x00400000,0x80401000,0x00000040,0x00400000,0x00001000,0x00401040,
+
+0x00000080,0x01040080,0x01040000,0x21000080,0x00040000,0x00000080,0x20000000,0x01040000,
+0x20040080,0x00040000,0x01000080,0x20040080,0x21000080,0x21040000,0x00040080,0x20000000,
+0x01000000,0x20040000,0x20040000,0x00000000,0x20000080,0x21040080,0x21040080,0x01000080,
+0x21040000,0x20000080,0x00000000,0x21000000,0x01040080,0x01000000,0x21000000,0x00040080,
+0x00040000,0x21000080,0x00000080,0x01000000,0x20000000,0x01040000,0x21000080,0x20040080,
+0x01000080,0x20000000,0x21040000,0x01040080,0x20040080,0x00000080,0x01000000,0x21040000,
+0x21040080,0x00040080,0x21000000,0x21040080,0x01040000,0x00000000,0x20040000,0x21000000,
+0x00040080,0x01000080,0x20000080,0x00040000,0x00000000,0x20040000,0x01040080,0x20000080,
+
+0x10000008,0x10200000,0x00002000,0x10202008,0x10200000,0x00000008,0x10202008,0x00200000,
+0x10002000,0x00202008,0x00200000,0x10000008,0x00200008,0x10002000,0x10000000,0x00002008,
+0x00000000,0x00200008,0x10002008,0x00002000,0x00202000,0x10002008,0x00000008,0x10200008,
+0x10200008,0x00000000,0x00202008,0x10202000,0x00002008,0x00202000,0x10202000,0x10000000,
+0x10002000,0x00000008,0x10200008,0x00202000,0x10202008,0x00200000,0x00002008,0x10000008,
+0x00200000,0x10002000,0x10000000,0x00002008,0x10000008,0x10202008,0x00202000,0x10200000,
+0x00202008,0x10202000,0x00000000,0x10200008,0x00000008,0x00002000,0x10200000,0x00202008,
+0x00002000,0x00200008,0x10002008,0x00000000,0x10202000,0x10000000,0x00200008,0x10002008,
+
+0x00100000,0x02100001,0x02000401,0x00000000,0x00000400,0x02000401,0x00100401,0x02100400,
+0x02100401,0x00100000,0x00000000,0x02000001,0x00000001,0x02000000,0x02100001,0x00000401,
+0x02000400,0x00100401,0x00100001,0x02000400,0x02000001,0x02100000,0x02100400,0x00100001,
+0x02100000,0x00000400,0x00000401,0x02100401,0x00100400,0x00000001,0x02000000,0x00100400,
+0x02000000,0x00100400,0x00100000,0x02000401,0x02000401,0x02100001,0x02100001,0x00000001,
+0x00100001,0x02000000,0x02000400,0x00100000,0x02100400,0x00000401,0x00100401,0x02100400,
+0x00000401,0x02000001,0x02100401,0x02100000,0x00100400,0x00000000,0x00000001,0x02100401,
+0x00000000,0x00100401,0x02100000,0x00000400,0x02000001,0x02000400,0x00000400,0x00100001,
+
+0x08000820,0x00000800,0x00020000,0x08020820,0x08000000,0x08000820,0x00000020,0x08000000,
+0x00020020,0x08020000,0x08020820,0x00020800,0x08020800,0x00020820,0x00000800,0x00000020,
+0x08020000,0x08000020,0x08000800,0x00000820,0x00020800,0x00020020,0x08020020,0x08020800,
+0x00000820,0x00000000,0x00000000,0x08020020,0x08000020,0x08000800,0x00020820,0x00020000,
+0x00020820,0x00020000,0x08020800,0x00000800,0x00000020,0x08020020,0x00000800,0x00020820,
+0x08000800,0x00000020,0x08000020,0x08020000,0x08020020,0x08000000,0x00020000,0x08000820,
+0x00000000,0x08020820,0x00020020,0x08000020,0x08020000,0x08000800,0x08000820,0x00000000,
+0x08020820,0x00020800,0x00020800,0x00000820,0x00000820,0x00020020,0x08000000,0x08020800,
+};
+
+/*
+ * for manual index calculation
+ * #define fetch(box, i, sh) (*((u32int*)((uchar*)spbox + (box << 8) + ((i >> (sh)) & 0xfc))))
+ */
+#define fetch(box, i, sh) ((spbox+(box << 6))[((i >> (sh + 2)) & 0x3f)])
+
+/*
+ * DES electronic codebook encryption of one block
+ */
+void
+block_cipher(ulong key[32], uchar text[8], int decrypting)
+{+ u32int right, left, v0, v1;
+ int i, keystep;
+
+ /*
+ * initial permutation
+ */
+ v0 = text[0] | ((u32int)text[2]<<8) | ((u32int)text[4]<<16) | ((u32int)text[6]<<24);
+ left = text[1] | ((u32int)text[3]<<8) | ((u32int)text[5]<<16) | ((u32int)text[7]<<24);
+ right = (left & 0xaaaaaaaa) | ((v0 >> 1) & 0x55555555);
+ left = ((left << 1) & 0xaaaaaaaa) | (v0 & 0x55555555);
+ left = ((left << 6) & 0x33003300)
+ | (left & 0xcc33cc33)
+ | ((left >> 6) & 0x00cc00cc);
+ left = ((left << 12) & 0x0f0f0000)
+ | (left & 0xf0f00f0f)
+ | ((left >> 12) & 0x0000f0f0);
+ right = ((right << 6) & 0x33003300)
+ | (right & 0xcc33cc33)
+ | ((right >> 6) & 0x00cc00cc);
+ right = ((right << 12) & 0x0f0f0000)
+ | (right & 0xf0f00f0f)
+ | ((right >> 12) & 0x0000f0f0);
+
+ if (decrypting) {+ keystep = -2;
+ key = key + 32 - 2;
+ } else
+ keystep = 2;
+ for (i = 0; i < 8; i++) {+ v0 = key[0];
+ v0 ^= (right >> 1) | (right << 31);
+ left ^= fetch(0, v0, 24)
+ ^ fetch(2, v0, 16)
+ ^ fetch(4, v0, 8)
+ ^ fetch(6, v0, 0);
+ v1 = key[1];
+ v1 ^= (right << 3) | (right >> 29);
+ left ^= fetch(1, v1, 24)
+ ^ fetch(3, v1, 16)
+ ^ fetch(5, v1, 8)
+ ^ fetch(7, v1, 0);
+ key += keystep;
+
+ v0 = key[0];
+ v0 ^= (left >> 1) | (left << 31);
+ right ^= fetch(0, v0, 24)
+ ^ fetch(2, v0, 16)
+ ^ fetch(4, v0, 8)
+ ^ fetch(6, v0, 0);
+ v1 = key[1];
+ v1 ^= (left << 3) | (left >> 29);
+ right ^= fetch(1, v1, 24)
+ ^ fetch(3, v1, 16)
+ ^ fetch(5, v1, 8)
+ ^ fetch(7, v1, 0);
+ key += keystep;
+ }
+
+ /*
+ * final permutation, inverse initial permutation
+ */
+ v0 = ((left << 1) & 0xaaaaaaaa) | (right & 0x55555555);
+ v1 = (left & 0xaaaaaaaa) | ((right >> 1) & 0x55555555);
+ v1 = ((v1 << 6) & 0x33003300)
+ | (v1 & 0xcc33cc33)
+ | ((v1 >> 6) & 0x00cc00cc);
+ v1 = ((v1 << 12) & 0x0f0f0000)
+ | (v1 & 0xf0f00f0f)
+ | ((v1 >> 12) & 0x0000f0f0);
+ v0 = ((v0 << 6) & 0x33003300)
+ | (v0 & 0xcc33cc33)
+ | ((v0 >> 6) & 0x00cc00cc);
+ v0 = ((v0 << 12) & 0x0f0f0000)
+ | (v0 & 0xf0f00f0f)
+ | ((v0 >> 12) & 0x0000f0f0);
+ text[0] = v0;
+ text[2] = v0 >> 8;
+ text[4] = v0 >> 16;
+ text[6] = v0 >> 24;
+ text[1] = v1;
+ text[3] = v1 >> 8;
+ text[5] = v1 >> 16;
+ text[7] = v1 >> 24;
+}
+
+/*
+ * triple DES electronic codebook encryption of one block
+ */
+void
+triple_block_cipher(ulong expanded_key[3][32], uchar text[8], int ende)
+{+ ulong *key;
+ u32int right, left, v0, v1;
+ int i, j, keystep;
+
+ /*
+ * initial permutation
+ */
+ v0 = text[0] | ((u32int)text[2]<<8) | ((u32int)text[4]<<16) | ((u32int)text[6]<<24);
+ left = text[1] | ((u32int)text[3]<<8) | ((u32int)text[5]<<16) | ((u32int)text[7]<<24);
+ right = (left & 0xaaaaaaaa) | ((v0 >> 1) & 0x55555555);
+ left = ((left << 1) & 0xaaaaaaaa) | (v0 & 0x55555555);
+ left = ((left << 6) & 0x33003300)
+ | (left & 0xcc33cc33)
+ | ((left >> 6) & 0x00cc00cc);
+ left = ((left << 12) & 0x0f0f0000)
+ | (left & 0xf0f00f0f)
+ | ((left >> 12) & 0x0000f0f0);
+ right = ((right << 6) & 0x33003300)
+ | (right & 0xcc33cc33)
+ | ((right >> 6) & 0x00cc00cc);
+ right = ((right << 12) & 0x0f0f0000)
+ | (right & 0xf0f00f0f)
+ | ((right >> 12) & 0x0000f0f0);
+
+ for(j = 0; j < 3; j++){+ if((ende & 1) == DES3D) {+ key = &expanded_key[2-j][32-2];
+ keystep = -2;
+ } else {+ key = &expanded_key[j][0];
+ keystep = 2;
+ }
+ ende >>= 1;
+ for (i = 0; i < 8; i++) {+ v0 = key[0];
+ v0 ^= (right >> 1) | (right << 31);
+ left ^= fetch(0, v0, 24)
+ ^ fetch(2, v0, 16)
+ ^ fetch(4, v0, 8)
+ ^ fetch(6, v0, 0);
+ v1 = key[1];
+ v1 ^= (right << 3) | (right >> 29);
+ left ^= fetch(1, v1, 24)
+ ^ fetch(3, v1, 16)
+ ^ fetch(5, v1, 8)
+ ^ fetch(7, v1, 0);
+ key += keystep;
+
+ v0 = key[0];
+ v0 ^= (left >> 1) | (left << 31);
+ right ^= fetch(0, v0, 24)
+ ^ fetch(2, v0, 16)
+ ^ fetch(4, v0, 8)
+ ^ fetch(6, v0, 0);
+ v1 = key[1];
+ v1 ^= (left << 3) | (left >> 29);
+ right ^= fetch(1, v1, 24)
+ ^ fetch(3, v1, 16)
+ ^ fetch(5, v1, 8)
+ ^ fetch(7, v1, 0);
+ key += keystep;
+ }
+
+ v0 = left;
+ left = right;
+ right = v0;
+ }
+
+ /*
+ * final permutation, inverse initial permutation
+ * left and right are swapped here
+ */
+ v0 = ((right << 1) & 0xaaaaaaaa) | (left & 0x55555555);
+ v1 = (right & 0xaaaaaaaa) | ((left >> 1) & 0x55555555);
+ v1 = ((v1 << 6) & 0x33003300)
+ | (v1 & 0xcc33cc33)
+ | ((v1 >> 6) & 0x00cc00cc);
+ v1 = ((v1 << 12) & 0x0f0f0000)
+ | (v1 & 0xf0f00f0f)
+ | ((v1 >> 12) & 0x0000f0f0);
+ v0 = ((v0 << 6) & 0x33003300)
+ | (v0 & 0xcc33cc33)
+ | ((v0 >> 6) & 0x00cc00cc);
+ v0 = ((v0 << 12) & 0x0f0f0000)
+ | (v0 & 0xf0f00f0f)
+ | ((v0 >> 12) & 0x0000f0f0);
+ text[0] = v0;
+ text[2] = v0 >> 8;
+ text[4] = v0 >> 16;
+ text[6] = v0 >> 24;
+ text[1] = v1;
+ text[3] = v1 >> 8;
+ text[5] = v1 >> 16;
+ text[7] = v1 >> 24;
+}
+
+/*
+ * key compression permutation, 4 bits at a time
+ */
+static u32int comptab[] = {+
+0x000000,0x010000,0x000008,0x010008,0x000080,0x010080,0x000088,0x010088,
+0x000000,0x010000,0x000008,0x010008,0x000080,0x010080,0x000088,0x010088,
+
+0x000000,0x100000,0x000800,0x100800,0x000000,0x100000,0x000800,0x100800,
+0x002000,0x102000,0x002800,0x102800,0x002000,0x102000,0x002800,0x102800,
+
+0x000000,0x000004,0x000400,0x000404,0x000000,0x000004,0x000400,0x000404,
+0x400000,0x400004,0x400400,0x400404,0x400000,0x400004,0x400400,0x400404,
+
+0x000000,0x000020,0x008000,0x008020,0x800000,0x800020,0x808000,0x808020,
+0x000002,0x000022,0x008002,0x008022,0x800002,0x800022,0x808002,0x808022,
+
+0x000000,0x000200,0x200000,0x200200,0x001000,0x001200,0x201000,0x201200,
+0x000000,0x000200,0x200000,0x200200,0x001000,0x001200,0x201000,0x201200,
+
+0x000000,0x000040,0x000010,0x000050,0x004000,0x004040,0x004010,0x004050,
+0x040000,0x040040,0x040010,0x040050,0x044000,0x044040,0x044010,0x044050,
+
+0x000000,0x000100,0x020000,0x020100,0x000001,0x000101,0x020001,0x020101,
+0x080000,0x080100,0x0a0000,0x0a0100,0x080001,0x080101,0x0a0001,0x0a0101,
+
+0x000000,0x000100,0x040000,0x040100,0x000000,0x000100,0x040000,0x040100,
+0x000040,0x000140,0x040040,0x040140,0x000040,0x000140,0x040040,0x040140,
+
+0x000000,0x400000,0x008000,0x408000,0x000008,0x400008,0x008008,0x408008,
+0x000400,0x400400,0x008400,0x408400,0x000408,0x400408,0x008408,0x408408,
+
+0x000000,0x001000,0x080000,0x081000,0x000020,0x001020,0x080020,0x081020,
+0x004000,0x005000,0x084000,0x085000,0x004020,0x005020,0x084020,0x085020,
+
+0x000000,0x000800,0x000000,0x000800,0x000010,0x000810,0x000010,0x000810,
+0x800000,0x800800,0x800000,0x800800,0x800010,0x800810,0x800010,0x800810,
+
+0x000000,0x010000,0x000200,0x010200,0x000000,0x010000,0x000200,0x010200,
+0x100000,0x110000,0x100200,0x110200,0x100000,0x110000,0x100200,0x110200,
+
+0x000000,0x000004,0x000000,0x000004,0x000080,0x000084,0x000080,0x000084,
+0x002000,0x002004,0x002000,0x002004,0x002080,0x002084,0x002080,0x002084,
+
+0x000000,0x000001,0x200000,0x200001,0x020000,0x020001,0x220000,0x220001,
+0x000002,0x000003,0x200002,0x200003,0x020002,0x020003,0x220002,0x220003,
+};
+
+static int keysh[] =
+{+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
+};
+
+static void
+keycompperm(u32int left, u32int right, ulong *ek)
+{+ u32int v0, v1;
+ int i;
+
+ for(i = 0; i < 16; i++){+ left = (left << keysh[i]) | (left >> (28 - keysh[i]));
+ left &= 0xfffffff0;
+ right = (right << keysh[i]) | (right >> (28 - keysh[i]));
+ right &= 0xfffffff0;
+ v0 = comptab[6 * (1 << 4) + ((left >> (32-4)) & 0xf)]
+ | comptab[5 * (1 << 4) + ((left >> (32-8)) & 0xf)]
+ | comptab[4 * (1 << 4) + ((left >> (32-12)) & 0xf)]
+ | comptab[3 * (1 << 4) + ((left >> (32-16)) & 0xf)]
+ | comptab[2 * (1 << 4) + ((left >> (32-20)) & 0xf)]
+ | comptab[1 * (1 << 4) + ((left >> (32-24)) & 0xf)]
+ | comptab[0 * (1 << 4) + ((left >> (32-28)) & 0xf)];
+ v1 = comptab[13 * (1 << 4) + ((right >> (32-4)) & 0xf)]
+ | comptab[12 * (1 << 4) + ((right >> (32-8)) & 0xf)]
+ | comptab[11 * (1 << 4) + ((right >> (32-12)) & 0xf)]
+ | comptab[10 * (1 << 4) + ((right >> (32-16)) & 0xf)]
+ | comptab[9 * (1 << 4) + ((right >> (32-20)) & 0xf)]
+ | comptab[8 * (1 << 4) + ((right >> (32-24)) & 0xf)]
+ | comptab[7 * (1 << 4) + ((right >> (32-28)) & 0xf)];
+ ek[0] = (((v0 >> (24-6)) & 0x3f) << 26)
+ | (((v0 >> (24-18)) & 0x3f) << 18)
+ | (((v1 >> (24-6)) & 0x3f) << 10)
+ | (((v1 >> (24-18)) & 0x3f) << 2);
+ ek[1] = (((v0 >> (24-12)) & 0x3f) << 26)
+ | (((v0 >> (24-24)) & 0x3f) << 18)
+ | (((v1 >> (24-12)) & 0x3f) << 10)
+ | (((v1 >> (24-24)) & 0x3f) << 2);
+ ek += 2;
+ }
+}
+
+void
+des_key_setup(uchar key[8], ulong ek[32])
+{+ u32int left, right, v0, v1;
+
+ v0 = key[0] | ((u32int)key[2] << 8) | ((u32int)key[4] << 16) | ((u32int)key[6] << 24);
+ v1 = key[1] | ((u32int)key[3] << 8) | ((u32int)key[5] << 16) | ((u32int)key[7] << 24);
+ left = ((v0 >> 1) & 0x40404040)
+ | ((v0 >> 2) & 0x10101010)
+ | ((v0 >> 3) & 0x04040404)
+ | ((v0 >> 4) & 0x01010101)
+ | ((v1 >> 0) & 0x80808080)
+ | ((v1 >> 1) & 0x20202020)
+ | ((v1 >> 2) & 0x08080808)
+ | ((v1 >> 3) & 0x02020202);
+ right = ((v0 >> 1) & 0x04040404)
+ | ((v0 << 2) & 0x10101010)
+ | ((v0 << 5) & 0x40404040)
+ | ((v1 << 0) & 0x08080808)
+ | ((v1 << 3) & 0x20202020)
+ | ((v1 << 6) & 0x80808080);
+ left = ((left << 6) & 0x33003300)
+ | (left & 0xcc33cc33)
+ | ((left >> 6) & 0x00cc00cc);
+ v0 = ((left << 12) & 0x0f0f0000)
+ | (left & 0xf0f00f0f)
+ | ((left >> 12) & 0x0000f0f0);
+ right = ((right << 6) & 0x33003300)
+ | (right & 0xcc33cc33)
+ | ((right >> 6) & 0x00cc00cc);
+ v1 = ((right << 12) & 0x0f0f0000)
+ | (right & 0xf0f00f0f)
+ | ((right >> 12) & 0x0000f0f0);
+ left = v0 & 0xfffffff0;
+ right = (v1 & 0xffffff00) | ((v0 << 4) & 0xf0);
+
+ keycompperm(left, right, ek);
+}
+
+static uchar parity[128] =
+{+ 0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e,
+ 0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f,
+ 0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f,
+ 0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e,
+ 0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f,
+ 0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e,
+ 0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e,
+ 0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f,
+ 0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f,
+ 0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e,
+ 0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae,
+ 0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf,
+ 0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce,
+ 0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf,
+ 0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef,
+ 0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe,
+};
+
+/*
+ * convert a 7 byte key to an 8 byte one
+ */
+void
+des56to64(uchar *k56, uchar *k64)
+{+ u32int hi, lo;
+
+ hi = ((u32int)k56[0]<<24)|((u32int)k56[1]<<16)|((u32int)k56[2]<<8)|k56[3];
+ lo = ((u32int)k56[4]<<24)|((u32int)k56[5]<<16)|((u32int)k56[6]<<8);
+
+ k64[0] = parity[(hi>>25)&0x7f];
+ k64[1] = parity[(hi>>18)&0x7f];
+ k64[2] = parity[(hi>>11)&0x7f];
+ k64[3] = parity[(hi>>4)&0x7f];
+ k64[4] = parity[((hi<<3)|(lo>>29))&0x7f];
+ k64[5] = parity[(lo>>22)&0x7f];
+ k64[6] = parity[(lo>>15)&0x7f];
+ k64[7] = parity[(lo>>8)&0x7f];
+}
+
+/*
+ * convert an 8 byte key to a 7 byte one
+ */
+void
+des64to56(uchar *k64, uchar *k56)
+{+ u32int hi, lo;
+
+ hi = (((u32int)k64[0]&0xfe)<<24)|(((u32int)k64[1]&0xfe)<<17)|(((u32int)k64[2]&0xfe)<<10)
+ |((k64[3]&0xfe)<<3)|(k64[4]>>4);
+ lo = (((u32int)k64[4]&0xfe)<<28)|(((u32int)k64[5]&0xfe)<<21)|(((u32int)k64[6]&0xfe)<<14)
+ |(((u32int)k64[7]&0xfe)<<7);
+
+ k56[0] = hi>>24;
+ k56[1] = hi>>16;
+ k56[2] = hi>>8;
+ k56[3] = hi>>0;
+ k56[4] = lo>>24;
+ k56[5] = lo>>16;
+ k56[6] = lo>>8;
+}
+
+void
+key_setup(uchar key[7], ulong ek[32])
+{+ uchar k64[8];
+
+ des56to64(key, k64);
+ des_key_setup(k64, ek);
+}
--- /dev/null
+++ b/libsec/des3CBC.c
@@ -1,0 +1,58 @@
+#include "os.h"
+#include <libsec.h>
+
+// Because of the way that non multiple of 8
+// buffers are handled, the decryptor must
+// be fed buffers of the same size as the
+// encryptor
+
+
+// If the length is not a multiple of 8, I encrypt
+// the overflow to be compatible with lacy's cryptlib
+void
+des3CBCencrypt(uchar *p, int len, DES3state *s)
+{+ uchar *p2, *ip, *eip;
+
+ for(; len >= 8; len -= 8){+ p2 = p;
+ ip = s->ivec;
+ for(eip = ip+8; ip < eip; )
+ *p2++ ^= *ip++;
+ triple_block_cipher(s->expanded, p, DES3EDE);
+ memmove(s->ivec, p, 8);
+ p += 8;
+ }
+
+ if(len > 0){+ ip = s->ivec;
+ triple_block_cipher(s->expanded, ip, DES3EDE);
+ for(eip = ip+len; ip < eip; )
+ *p++ ^= *ip++;
+ }
+}
+
+void
+des3CBCdecrypt(uchar *p, int len, DES3state *s)
+{+ uchar *ip, *eip, *tp;
+ uchar tmp[8];
+
+ for(; len >= 8; len -= 8){+ memmove(tmp, p, 8);
+ triple_block_cipher(s->expanded, p, DES3DED);
+ tp = tmp;
+ ip = s->ivec;
+ for(eip = ip+8; ip < eip; ){+ *p++ ^= *ip;
+ *ip++ = *tp++;
+ }
+ }
+
+ if(len > 0){+ ip = s->ivec;
+ triple_block_cipher(s->expanded, ip, DES3EDE);
+ for(eip = ip+len; ip < eip; )
+ *p++ ^= *ip++;
+ }
+}
--- /dev/null
+++ b/libsec/desmodes.c
@@ -1,0 +1,31 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * these routines use the 64bit format for
+ * DES keys.
+ */
+
+void
+setupDESstate(DESstate *s, uchar key[8], uchar *ivec)
+{+ memset(s, 0, sizeof(*s));
+ memmove(s->key, key, sizeof(s->key));
+ des_key_setup(key, s->expanded);
+ if(ivec)
+ memmove(s->ivec, ivec, 8);
+ s->setup = 0xdeadbeef;
+}
+
+void
+setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec)
+{+ memset(s, 0, sizeof(*s));
+ memmove(s->key, key, sizeof(s->key));
+ des_key_setup(key[0], s->expanded[0]);
+ des_key_setup(key[1], s->expanded[1]);
+ des_key_setup(key[2], s->expanded[2]);
+ if(ivec)
+ memmove(s->ivec, ivec, 8);
+ s->setup = 0xdeadbeef;
+}
--- /dev/null
+++ b/libsec/dh.c
@@ -1,0 +1,74 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+mpint*
+dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g)
+{+ mpint *pm1;
+ int n;
+
+ memset(dh, 0, sizeof(*dh));
+ if(mpcmp(g, mpone) <= 0)
+ return nil;
+
+ n = mpsignif(p);
+ pm1 = mpnew(n);
+ mpsub(p, mpone, pm1);
+ dh->p = mpcopy(p);
+ dh->g = mpcopy(g);
+ dh->q = mpcopy(q != nil ? q : pm1);
+ dh->x = mpnew(mpsignif(dh->q));
+ dh->y = mpnew(n);
+ for(;;){+ mpnrand(dh->q, genrandom, dh->x);
+ mpexp(dh->g, dh->x, dh->p, dh->y);
+ if(mpcmp(dh->y, mpone) > 0 && mpcmp(dh->y, pm1) < 0)
+ break;
+ }
+ mpfree(pm1);
+
+ return dh->y;
+}
+
+mpint*
+dh_finish(DHstate *dh, mpint *y)
+{+ mpint *k = nil;
+
+ if(y == nil || dh->x == nil || dh->p == nil || dh->q == nil)
+ goto Out;
+
+ /* y > 1 */
+ if(mpcmp(y, mpone) <= 0)
+ goto Out;
+
+ k = mpnew(mpsignif(dh->p));
+
+ /* y < p-1 */
+ mpsub(dh->p, mpone, k);
+ if(mpcmp(y, k) >= 0){+Bad:
+ mpfree(k);
+ k = nil;
+ goto Out;
+ }
+
+ /* y**q % p == 1 if q < p-1 */
+ if(mpcmp(dh->q, k) < 0){+ mpexp(y, dh->q, dh->p, k);
+ if(mpcmp(k, mpone) != 0)
+ goto Bad;
+ }
+
+ mpexp(y, dh->x, dh->p, k);
+
+Out:
+ mpfree(dh->p);
+ mpfree(dh->q);
+ mpfree(dh->g);
+ mpfree(dh->x);
+ mpfree(dh->y);
+ memset(dh, 0, sizeof(*dh));
+ return k;
+}
--- /dev/null
+++ b/libsec/ecc.c
@@ -1,0 +1,612 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+#include <ctype.h>
+
+extern void jacobian_affine(mpint *p,
+ mpint *X, mpint *Y, mpint *Z);
+extern void jacobian_dbl(mpint *p, mpint *a,
+ mpint *X1, mpint *Y1, mpint *Z1,
+ mpint *X3, mpint *Y3, mpint *Z3);
+extern void jacobian_add(mpint *p, mpint *a,
+ mpint *X1, mpint *Y1, mpint *Z1,
+ mpint *X2, mpint *Y2, mpint *Z2,
+ mpint *X3, mpint *Y3, mpint *Z3);
+
+void
+ecassign(ECdomain *dom, ECpoint *a, ECpoint *b)
+{+ if((b->inf = a->inf) != 0)
+ return;
+ mpassign(a->x, b->x);
+ mpassign(a->y, b->y);
+ if(b->z != nil){+ mpassign(a->z != nil ? a->z : mpone, b->z);
+ return;
+ }
+ if(a->z != nil){+ b->z = mpcopy(a->z);
+ jacobian_affine(dom->p, b->x, b->y, b->z);
+ mpfree(b->z);
+ b->z = nil;
+ }
+}
+
+void
+ecadd(ECdomain *dom, ECpoint *a, ECpoint *b, ECpoint *s)
+{+ if(a->inf && b->inf){+ s->inf = 1;
+ return;
+ }
+ if(a->inf){+ ecassign(dom, b, s);
+ return;
+ }
+ if(b->inf){+ ecassign(dom, a, s);
+ return;
+ }
+
+ if(s->z == nil){+ s->z = mpcopy(mpone);
+ ecadd(dom, a, b, s);
+ if(!s->inf)
+ jacobian_affine(dom->p, s->x, s->y, s->z);
+ mpfree(s->z);
+ s->z = nil;
+ return;
+ }
+
+ if(a == b)
+ jacobian_dbl(dom->p, dom->a,
+ a->x, a->y, a->z != nil ? a->z : mpone,
+ s->x, s->y, s->z);
+ else
+ jacobian_add(dom->p, dom->a,
+ a->x, a->y, a->z != nil ? a->z : mpone,
+ b->x, b->y, b->z != nil ? b->z : mpone,
+ s->x, s->y, s->z);
+ s->inf = mpcmp(s->z, mpzero) == 0;
+}
+
+void
+ecmul(ECdomain *dom, ECpoint *a, mpint *k, ECpoint *s)
+{+ ECpoint ns, na;
+ mpint *l;
+
+ if(a->inf || mpcmp(k, mpzero) == 0){+ s->inf = 1;
+ return;
+ }
+ ns.inf = 1;
+ ns.x = mpnew(0);
+ ns.y = mpnew(0);
+ ns.z = mpnew(0);
+ na.x = mpnew(0);
+ na.y = mpnew(0);
+ na.z = mpnew(0);
+ ecassign(dom, a, &na);
+ l = mpcopy(k);
+ l->sign = 1;
+ while(mpcmp(l, mpzero) != 0){+ if(l->p[0] & 1)
+ ecadd(dom, &na, &ns, &ns);
+ ecadd(dom, &na, &na, &na);
+ mpright(l, 1, l);
+ }
+ if(k->sign < 0 && !ns.inf){+ ns.y->sign = -1;
+ mpmod(ns.y, dom->p, ns.y);
+ }
+ ecassign(dom, &ns, s);
+ mpfree(ns.x);
+ mpfree(ns.y);
+ mpfree(ns.z);
+ mpfree(na.x);
+ mpfree(na.y);
+ mpfree(na.z);
+ mpfree(l);
+}
+
+int
+ecverify(ECdomain *dom, ECpoint *a)
+{+ mpint *p, *q;
+ int r;
+
+ if(a->inf)
+ return 1;
+
+ assert(a->z == nil); /* need affine coordinates */
+ p = mpnew(0);
+ q = mpnew(0);
+ mpmodmul(a->y, a->y, dom->p, p);
+ mpmodmul(a->x, a->x, dom->p, q);
+ mpmodadd(q, dom->a, dom->p, q);
+ mpmodmul(q, a->x, dom->p, q);
+ mpmodadd(q, dom->b, dom->p, q);
+ r = mpcmp(p, q);
+ mpfree(p);
+ mpfree(q);
+ return r == 0;
+}
+
+int
+ecpubverify(ECdomain *dom, ECpub *a)
+{+ ECpoint p;
+ int r;
+
+ if(a->inf)
+ return 0;
+ if(!ecverify(dom, a))
+ return 0;
+ p.x = mpnew(0);
+ p.y = mpnew(0);
+ p.z = mpnew(0);
+ ecmul(dom, a, dom->n, &p);
+ r = p.inf;
+ mpfree(p.x);
+ mpfree(p.y);
+ mpfree(p.z);
+ return r;
+}
+
+static void
+fixnibble(uchar *a)
+{+ if(*a >= 'a')
+ *a -= 'a'-10;
+ else if(*a >= 'A')
+ *a -= 'A'-10;
+ else
+ *a -= '0';
+}
+
+static int
+octet(char **s)
+{+ uchar c, d;
+
+ c = *(*s)++;
+ if(!isxdigit(c))
+ return -1;
+ d = *(*s)++;
+ if(!isxdigit(d))
+ return -1;
+ fixnibble(&c);
+ fixnibble(&d);
+ return (c << 4) | d;
+}
+
+static mpint*
+halfpt(ECdomain *dom, char *s, char **rptr, mpint *out)
+{+ char *buf, *r;
+ int n;
+ mpint *ret;
+
+ n = ((mpsignif(dom->p)+7)/8)*2;
+ if(strlen(s) < n)
+ return 0;
+ buf = malloc(n+1);
+ buf[n] = 0;
+ memcpy(buf, s, n);
+ ret = strtomp(buf, &r, 16, out);
+ *rptr = s + (r - buf);
+ free(buf);
+ return ret;
+}
+
+static int
+mpleg(mpint *a, mpint *b)
+{+ int r, k;
+ mpint *m, *n, *t;
+
+ r = 1;
+ m = mpcopy(a);
+ n = mpcopy(b);
+ for(;;){+ if(mpcmp(m, n) > 0)
+ mpmod(m, n, m);
+ if(mpcmp(m, mpzero) == 0){+ r = 0;
+ break;
+ }
+ if(mpcmp(m, mpone) == 0)
+ break;
+ k = mplowbits0(m);
+ if(k > 0){+ if(k & 1)
+ switch(n->p[0] & 15){+ case 3: case 5: case 11: case 13:
+ r = -r;
+ }
+ mpright(m, k, m);
+ }
+ if((n->p[0] & 3) == 3 && (m->p[0] & 3) == 3)
+ r = -r;
+ t = m;
+ m = n;
+ n = t;
+ }
+ mpfree(m);
+ mpfree(n);
+ return r;
+}
+
+static int
+mpsqrt(mpint *n, mpint *p, mpint *r)
+{+ mpint *a, *t, *s, *xp, *xq, *yp, *yq, *zp, *zq, *N;
+
+ if(mpleg(n, p) == -1)
+ return 0;
+ a = mpnew(0);
+ t = mpnew(0);
+ s = mpnew(0);
+ N = mpnew(0);
+ xp = mpnew(0);
+ xq = mpnew(0);
+ yp = mpnew(0);
+ yq = mpnew(0);
+ zp = mpnew(0);
+ zq = mpnew(0);
+ for(;;){+ for(;;){+ mpnrand(p, genrandom, a);
+ if(mpcmp(a, mpzero) > 0)
+ break;
+ }
+ mpmul(a, a, t);
+ mpsub(t, n, t);
+ mpmod(t, p, t);
+ if(mpleg(t, p) == -1)
+ break;
+ }
+ mpadd(p, mpone, N);
+ mpright(N, 1, N);
+ mpmul(a, a, t);
+ mpsub(t, n, t);
+ mpassign(a, xp);
+ uitomp(1, xq);
+ uitomp(1, yp);
+ uitomp(0, yq);
+ while(mpcmp(N, mpzero) != 0){+ if(N->p[0] & 1){+ mpmul(xp, yp, zp);
+ mpmul(xq, yq, zq);
+ mpmul(zq, t, zq);
+ mpadd(zp, zq, zp);
+ mpmod(zp, p, zp);
+ mpmul(xp, yq, zq);
+ mpmul(xq, yp, s);
+ mpadd(zq, s, zq);
+ mpmod(zq, p, yq);
+ mpassign(zp, yp);
+ }
+ mpmul(xp, xp, zp);
+ mpmul(xq, xq, zq);
+ mpmul(zq, t, zq);
+ mpadd(zp, zq, zp);
+ mpmod(zp, p, zp);
+ mpmul(xp, xq, zq);
+ mpadd(zq, zq, zq);
+ mpmod(zq, p, xq);
+ mpassign(zp, xp);
+ mpright(N, 1, N);
+ }
+ if(mpcmp(yq, mpzero) != 0)
+ abort();
+ mpassign(yp, r);
+ mpfree(a);
+ mpfree(t);
+ mpfree(s);
+ mpfree(N);
+ mpfree(xp);
+ mpfree(xq);
+ mpfree(yp);
+ mpfree(yq);
+ mpfree(zp);
+ mpfree(zq);
+ return 1;
+}
+
+ECpoint*
+strtoec(ECdomain *dom, char *s, char **rptr, ECpoint *ret)
+{+ int allocd, o;
+ mpint *r;
+
+ allocd = 0;
+ if(ret == nil){+ allocd = 1;
+ ret = mallocz(sizeof(*ret), 1);
+ if(ret == nil)
+ return nil;
+ ret->x = mpnew(0);
+ ret->y = mpnew(0);
+ }
+ ret->inf = 0;
+ o = 0;
+ switch(octet(&s)){+ case 0:
+ ret->inf = 1;
+ break;
+ case 3:
+ o = 1;
+ case 2:
+ if(halfpt(dom, s, &s, ret->x) == nil)
+ goto err;
+ r = mpnew(0);
+ mpmul(ret->x, ret->x, r);
+ mpadd(r, dom->a, r);
+ mpmul(r, ret->x, r);
+ mpadd(r, dom->b, r);
+ if(!mpsqrt(r, dom->p, r)){+ mpfree(r);
+ goto err;
+ }
+ if((r->p[0] & 1) != o)
+ mpsub(dom->p, r, r);
+ mpassign(r, ret->y);
+ mpfree(r);
+ if(!ecverify(dom, ret))
+ goto err;
+ break;
+ case 4:
+ if(halfpt(dom, s, &s, ret->x) == nil)
+ goto err;
+ if(halfpt(dom, s, &s, ret->y) == nil)
+ goto err;
+ if(!ecverify(dom, ret))
+ goto err;
+ break;
+ }
+ if(ret->z != nil && !ret->inf)
+ mpassign(mpone, ret->z);
+ return ret;
+
+err:
+ if(rptr)
+ *rptr = s;
+ if(allocd){+ mpfree(ret->x);
+ mpfree(ret->y);
+ free(ret);
+ }
+ return nil;
+}
+
+ECpriv*
+ecgen(ECdomain *dom, ECpriv *p)
+{+ if(p == nil){+ p = mallocz(sizeof(*p), 1);
+ if(p == nil)
+ return nil;
+ p->a.x = mpnew(0);
+ p->a.y = mpnew(0);
+ p->d = mpnew(0);
+ }
+ for(;;){+ mpnrand(dom->n, genrandom, p->d);
+ if(mpcmp(p->d, mpzero) > 0)
+ break;
+ }
+ ecmul(dom, &dom->G, p->d, &p->a);
+ return p;
+}
+
+void
+ecdsasign(ECdomain *dom, ECpriv *priv, uchar *dig, int len, mpint *r, mpint *s)
+{+ ECpriv tmp;
+ mpint *E, *t;
+
+ tmp.a.x = mpnew(0);
+ tmp.a.y = mpnew(0);
+ tmp.a.z = nil;
+ tmp.d = mpnew(0);
+ E = betomp(dig, len, nil);
+ t = mpnew(0);
+ if(mpsignif(dom->n) < 8*len)
+ mpright(E, 8*len - mpsignif(dom->n), E);
+ for(;;){+ ecgen(dom, &tmp);
+ mpmod(tmp.a.x, dom->n, r);
+ if(mpcmp(r, mpzero) == 0)
+ continue;
+ mpmul(r, priv->d, s);
+ mpadd(E, s, s);
+ mpinvert(tmp.d, dom->n, t);
+ mpmodmul(s, t, dom->n, s);
+ if(mpcmp(s, mpzero) != 0)
+ break;
+ }
+ mpfree(t);
+ mpfree(E);
+ mpfree(tmp.a.x);
+ mpfree(tmp.a.y);
+ mpfree(tmp.d);
+}
+
+int
+ecdsaverify(ECdomain *dom, ECpub *pub, uchar *dig, int len, mpint *r, mpint *s)
+{+ mpint *E, *t, *u1, *u2;
+ ECpoint R, S;
+ int ret;
+
+ if(mpcmp(r, mpone) < 0 || mpcmp(s, mpone) < 0 || mpcmp(r, dom->n) >= 0 || mpcmp(r, dom->n) >= 0)
+ return 0;
+ E = betomp(dig, len, nil);
+ if(mpsignif(dom->n) < 8*len)
+ mpright(E, 8*len - mpsignif(dom->n), E);
+ t = mpnew(0);
+ u1 = mpnew(0);
+ u2 = mpnew(0);
+ R.x = mpnew(0);
+ R.y = mpnew(0);
+ R.z = mpnew(0);
+ S.x = mpnew(0);
+ S.y = mpnew(0);
+ S.z = mpnew(0);
+ mpinvert(s, dom->n, t);
+ mpmodmul(E, t, dom->n, u1);
+ mpmodmul(r, t, dom->n, u2);
+ ecmul(dom, &dom->G, u1, &R);
+ ecmul(dom, pub, u2, &S);
+ ecadd(dom, &R, &S, &R);
+ ret = 0;
+ if(!R.inf){+ jacobian_affine(dom->p, R.x, R.y, R.z);
+ mpmod(R.x, dom->n, t);
+ ret = mpcmp(r, t) == 0;
+ }
+ mpfree(E);
+ mpfree(t);
+ mpfree(u1);
+ mpfree(u2);
+ mpfree(R.x);
+ mpfree(R.y);
+ mpfree(R.z);
+ mpfree(S.x);
+ mpfree(S.y);
+ mpfree(S.z);
+ return ret;
+}
+
+static char *code = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+
+void
+base58enc(uchar *src, char *dst, int len)
+{+ mpint *n, *r, *b;
+ char *sdst, t;
+
+ sdst = dst;
+ n = betomp(src, len, nil);
+ b = uitomp(58, nil);
+ r = mpnew(0);
+ while(mpcmp(n, mpzero) != 0){+ mpdiv(n, b, n, r);
+ *dst++ = code[mptoui(r)];
+ }
+ for(; *src == 0; src++)
+ *dst++ = code[0];
+ dst--;
+ while(dst > sdst){+ t = *sdst;
+ *sdst++ = *dst;
+ *dst-- = t;
+ }
+}
+
+int
+base58dec(char *src, uchar *dst, int len)
+{+ mpint *n, *b, *r;
+ char *t;
+
+ n = mpnew(0);
+ r = mpnew(0);
+ b = uitomp(58, nil);
+ for(; *src; src++){+ t = strchr(code, *src);
+ if(t == nil){+ mpfree(n);
+ mpfree(r);
+ mpfree(b);
+ werrstr("invalid base58 char");+ return -1;
+ }
+ uitomp(t - code, r);
+ mpmul(n, b, n);
+ mpadd(n, r, n);
+ }
+ mptober(n, dst, len);
+ mpfree(n);
+ mpfree(r);
+ mpfree(b);
+ return 0;
+}
+
+void
+ecdominit(ECdomain *dom, void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h))
+{+ memset(dom, 0, sizeof(*dom));
+ dom->p = mpnew(0);
+ dom->a = mpnew(0);
+ dom->b = mpnew(0);
+ dom->G.x = mpnew(0);
+ dom->G.y = mpnew(0);
+ dom->n = mpnew(0);
+ dom->h = mpnew(0);
+ if(init){+ (*init)(dom->p, dom->a, dom->b, dom->G.x, dom->G.y, dom->n, dom->h);
+ dom->p = mpfield(dom->p);
+ }
+}
+
+void
+ecdomfree(ECdomain *dom)
+{+ mpfree(dom->p);
+ mpfree(dom->a);
+ mpfree(dom->b);
+ mpfree(dom->G.x);
+ mpfree(dom->G.y);
+ mpfree(dom->n);
+ mpfree(dom->h);
+ memset(dom, 0, sizeof(*dom));
+}
+
+int
+ecencodepub(ECdomain *dom, ECpub *pub, uchar *data, int len)
+{+ int n;
+
+ n = (mpsignif(dom->p)+7)/8;
+ if(len < 1 + 2*n)
+ return 0;
+ len = 1 + 2*n;
+ data[0] = 0x04;
+ mptober(pub->x, data+1, n);
+ mptober(pub->y, data+1+n, n);
+ return len;
+}
+
+ECpub*
+ecdecodepub(ECdomain *dom, uchar *data, int len)
+{+ ECpub *pub;
+ int n;
+
+ n = (mpsignif(dom->p)+7)/8;
+ if(len != 1 + 2*n || data[0] != 0x04)
+ return nil;
+ pub = mallocz(sizeof(*pub), 1);
+ if(pub == nil)
+ return nil;
+ pub->x = betomp(data+1, n, nil);
+ pub->y = betomp(data+1+n, n, nil);
+ if(!ecpubverify(dom, pub)){+ ecpubfree(pub);
+ pub = nil;
+ }
+ return pub;
+}
+
+void
+ecpubfree(ECpub *p)
+{+ if(p == nil)
+ return;
+ mpfree(p->x);
+ mpfree(p->y);
+ free(p);
+}
--- /dev/null
+++ b/libsec/fastrand.c
@@ -1,0 +1,15 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * use the X917 random number generator to create random
+ * numbers (faster than truerand() but not as random).
+ */
+ulong
+fastrand(void)
+{+ ulong x;
+
+ genrandom((uchar*)&x, sizeof x);
+ return x;
+}
--- /dev/null
+++ b/libsec/genrandom.c
@@ -1,0 +1,44 @@
+#include "os.h"
+#include <libsec.h>
+
+static void
+init(Chachastate *cs)
+{+ ulong seed[11];
+ int i;
+
+ for(i=0; i<nelem(seed); i++)
+ seed[i] = truerand();
+
+ setupChachastate(cs, (uchar*)&seed[0], 32, (uchar*)&seed[8], 12, 20);
+ memset(seed, 0, sizeof(seed));
+}
+
+static void
+fill(Chachastate *cs, uchar *p, int n)
+{+ Chachastate c;
+
+ c = *cs;
+ chacha_encrypt((uchar*)&cs->input[4], 32, &c);
+ if(++cs->input[13] == 0)
+ if(++cs->input[14] == 0)
+ ++cs->input[15];
+
+ chacha_encrypt(p, n, &c);
+ memset(&c, 0, sizeof(c));
+}
+
+void
+genrandom(uchar *p, int n)
+{+ static QLock lk;
+ static Chachastate cs;
+
+ qlock(&lk);
+ if(cs.rounds == 0)
+ init(&cs);
+ cs.input[4] ^= getpid(); /* fork protection */
+ fill(&cs, p, n);
+ qunlock(&lk);
+}
--- /dev/null
+++ b/libsec/hkdf.c
@@ -1,0 +1,36 @@
+#include "os.h"
+#include <libsec.h>
+
+/* 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)
+{+ uchar prk[256], tmp[256], cnt;
+ DigestState *ds;
+
+ assert(xlen <= sizeof(tmp));
+
+ memset(tmp, 0, xlen);
+ if(nsalt == 0){+ salt = tmp;
+ nsalt = xlen;
+ }
+ /* note that salt and key are swapped in this case */
+ (*x)(key, nkey, salt, nsalt, prk, nil);
+ ds = nil;
+ for(cnt=1;; cnt++) {+ if(ninfo > 0)
+ ds = (*x)(info, ninfo, prk, xlen, nil, ds);
+ (*x)(&cnt, 1, prk, xlen, tmp, ds);
+ if(dlen <= xlen){+ memmove(d, tmp, dlen);
+ break;
+ }
+ memmove(d, tmp, xlen);
+ dlen -= xlen;
+ d += xlen;
+ ds = (*x)(tmp, xlen, prk, xlen, nil, nil);
+ }
+}
--- /dev/null
+++ b/libsec/hmac.c
@@ -1,0 +1,46 @@
+#include "os.h"
+#include <libsec.h>
+
+/* rfc2104 */
+DigestState*
+hmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
+ DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen)
+{+ int i;
+ uchar pad[Hmacblksz+1], innerdigest[256];
+
+ if(xlen > sizeof(innerdigest))
+ return nil;
+ if(klen > Hmacblksz){+ if(xlen > Hmacblksz)
+ return nil;
+ (*x)(key, klen, innerdigest, nil);
+ key = innerdigest;
+ klen = xlen;
+ }
+
+ /* first time through */
+ if(s == nil || s->seeded == 0){+ memset(pad, 0x36, Hmacblksz);
+ pad[Hmacblksz] = 0;
+ for(i = 0; i < klen; i++)
+ pad[i] ^= key[i];
+ s = (*x)(pad, Hmacblksz, nil, s);
+ if(s == nil)
+ return nil;
+ }
+
+ s = (*x)(p, len, nil, s);
+ if(digest == nil)
+ return s;
+
+ /* last time through */
+ memset(pad, 0x5c, Hmacblksz);
+ pad[Hmacblksz] = 0;
+ for(i = 0; i < klen; i++)
+ pad[i] ^= key[i];
+ (*x)(nil, 0, innerdigest, s);
+ s = (*x)(pad, Hmacblksz, nil, nil);
+ (*x)(innerdigest, xlen, digest, s);
+ return nil;
+}
--- /dev/null
+++ b/libsec/jacobian.c
@@ -1,0 +1,166 @@
+#include "os.h"
+#include <mp.h>
+void jacobian_new(mpint *x, mpint *y, mpint *z, mpint *X, mpint *Y, mpint *Z){+ mpassign(x, X);
+ mpassign(y, Y);
+ mpassign(z, Z);
+ }
+void jacobian_inf(mpint *X, mpint *Y, mpint *Z){+ jacobian_new(mpzero, mpone, mpzero, X, Y, Z);
+ }
+void jacobian_affine(mpint *p, mpint *X, mpint *Y, mpint *Z){+ mpint *ZZZ = mpnew(0);
+ mpint *ZZ = mpnew(0);
+ if(mpcmp(Z, mpzero) != 0){+ mpmodmul(Z, Z, p, ZZ);
+ mpmodmul(ZZ, Z, p, ZZZ);
+ mpint *tmp1 = mpnew(0);
+ mpinvert(ZZ, p, tmp1);
+ mpmodmul(X, tmp1, p, X);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpinvert(ZZZ, p, tmp1);
+ mpmodmul(Y, tmp1, p, Y);
+ mpfree(tmp1);
+ mpassign(mpone, Z);
+ }
+ mpfree(ZZZ);
+ mpfree(ZZ);
+ }
+void jacobian_dbl(mpint *p, mpint *a, mpint *X1, mpint *Y1, mpint *Z1, mpint *X3, mpint *Y3, mpint *Z3){+ mpint *M = mpnew(0);
+ mpint *S = mpnew(0);
+ mpint *ZZ = mpnew(0);
+ mpint *YYYY = mpnew(0);
+ mpint *YY = mpnew(0);
+ mpint *XX = mpnew(0);
+ if(mpcmp(Y1, mpzero) == 0){+ jacobian_inf(X3, Y3, Z3);
+ }else{+ mpmodmul(X1, X1, p, XX);
+ mpmodmul(Y1, Y1, p, YY);
+ mpmodmul(YY, YY, p, YYYY);
+ mpmodmul(Z1, Z1, p, ZZ);
+ mpint *tmp1 = mpnew(0);
+ mpmodadd(X1, YY, p, tmp1);
+ mpmodmul(tmp1, tmp1, p, tmp1);
+ mpmodsub(tmp1, XX, p, tmp1);
+ mpmodsub(tmp1, YYYY, p, tmp1);
+ mpmodadd(tmp1, tmp1, p, S); // 2*tmp1
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ uitomp(3UL, tmp1);
+ mpmodmul(tmp1, XX, p, M);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpint *tmp2 = mpnew(0);
+ mpmodmul(ZZ, ZZ, p, tmp2);
+ mpmodmul(a, tmp2, p, tmp1);
+ mpfree(tmp2);
+ mpmodadd(M, tmp1, p, M);
+ mpfree(tmp1);
+ mpmodadd(Y1, Z1, p, Z3);
+ mpmodmul(Z3, Z3, p, Z3);
+ mpmodsub(Z3, YY, p, Z3);
+ mpmodsub(Z3, ZZ, p, Z3);
+ mpmodmul(M, M, p, X3);
+ tmp1 = mpnew(0);
+ mpmodadd(S, S, p, tmp1); // 2*S
+ mpmodsub(X3, tmp1, p, X3);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpmodsub(S, X3, p, tmp1);
+ mpmodmul(M, tmp1, p, Y3);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ tmp2 = mpnew(0);
+ uitomp(8UL, tmp2);
+ mpmodmul(tmp2, YYYY, p, tmp1);
+ mpfree(tmp2);
+ mpmodsub(Y3, tmp1, p, Y3);
+ mpfree(tmp1);
+ }
+ mpfree(M);
+ mpfree(S);
+ mpfree(ZZ);
+ mpfree(YYYY);
+ mpfree(YY);
+ mpfree(XX);
+ }
+void jacobian_add(mpint *p, mpint *a, mpint *X1, mpint *Y1, mpint *Z1, mpint *X2, mpint *Y2, mpint *Z2, mpint *X3, mpint *Y3, mpint *Z3){+ mpint *V = mpnew(0);
+ mpint *r = mpnew(0);
+ mpint *J = mpnew(0);
+ mpint *I = mpnew(0);
+ mpint *H = mpnew(0);
+ mpint *S2 = mpnew(0);
+ mpint *S1 = mpnew(0);
+ mpint *U2 = mpnew(0);
+ mpint *U1 = mpnew(0);
+ mpint *Z2Z2 = mpnew(0);
+ mpint *Z1Z1 = mpnew(0);
+ mpmodmul(Z1, Z1, p, Z1Z1);
+ mpmodmul(Z2, Z2, p, Z2Z2);
+ mpmodmul(X1, Z2Z2, p, U1);
+ mpmodmul(X2, Z1Z1, p, U2);
+ mpint *tmp1 = mpnew(0);
+ mpmodmul(Y1, Z2, p, tmp1);
+ mpmodmul(tmp1, Z2Z2, p, S1);
+ mpfree(tmp1);
+ tmp1 = mpnew(0);
+ mpmodmul(Y2, Z1, p, tmp1);
+ mpmodmul(tmp1, Z1Z1, p, S2);
+ mpfree(tmp1);
+ if(mpcmp(U1, U2) == 0){+ if(mpcmp(S1, S2) != 0){+ jacobian_inf(X3, Y3, Z3);
+ }else{+ jacobian_dbl(p, a, X1, Y1, Z1, X3, Y3, Z3);
+ }
+ }else{+ mpmodsub(U2, U1, p, H);
+ mpmodadd(H, H, p, I); // 2*H
+ mpmodmul(I, I, p, I);
+ mpmodmul(H, I, p, J);
+ mpint *tmp2 = mpnew(0);
+ mpmodsub(S2, S1, p, tmp2);
+ mpmodadd(tmp2, tmp2, p, r); // 2*tmp2
+ mpfree(tmp2);
+ mpmodmul(U1, I, p, V);
+ mpmodmul(r, r, p, X3);
+ mpmodsub(X3, J, p, X3);
+ tmp2 = mpnew(0);
+ mpmodadd(V, V, p, tmp2); // 2*V
+ mpmodsub(X3, tmp2, p, X3);
+ mpfree(tmp2);
+ tmp2 = mpnew(0);
+ mpmodsub(V, X3, p, tmp2);
+ mpmodmul(r, tmp2, p, Y3);
+ mpfree(tmp2);
+ tmp2 = mpnew(0);
+ mpint *tmp3 = mpnew(0);
+ mpmodadd(S1, S1, p, tmp3); // 2*S1
+ mpmodmul(tmp3, J, p, tmp2);
+ mpfree(tmp3);
+ mpmodsub(Y3, tmp2, p, Y3);
+ mpfree(tmp2);
+ tmp2 = mpnew(0);
+ mpmodadd(Z1, Z2, p, tmp2);
+ mpmodmul(tmp2, tmp2, p, tmp2);
+ mpmodsub(tmp2, Z1Z1, p, tmp2);
+ mpmodsub(tmp2, Z2Z2, p, tmp2);
+ mpmodmul(tmp2, H, p, Z3);
+ mpfree(tmp2);
+ }
+ mpfree(V);
+ mpfree(r);
+ mpfree(J);
+ mpfree(I);
+ mpfree(H);
+ mpfree(S2);
+ mpfree(S1);
+ mpfree(U2);
+ mpfree(U1);
+ mpfree(Z2Z2);
+ mpfree(Z1Z1);
+ }
--- /dev/null
+++ b/libsec/md5.c
@@ -1,0 +1,154 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * rfc1321 requires that I include this. The code is new. The constants
+ * all come from the rfc (hence the copyright). We trade a table for the
+ * macros in rfc. The total size is a lot less. -- presotto
+ *
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software forany particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+static void encode(uchar*, u32int*, ulong);
+
+extern void _md5block(uchar*, ulong, u32int*);
+
+MD5state*
+md5(uchar *p, ulong len, uchar *digest, MD5state *s)
+{+ u32int x[16];
+ uchar buf[128];
+ int i;
+ uchar *e;
+
+ if(s == nil){+ s = malloc(sizeof(*s));
+ if(s == nil)
+ return nil;
+ memset(s, 0, sizeof(*s));
+ s->malloced = 1;
+ }
+
+ if(s->seeded == 0){+ /* seed the state, these constants would look nicer big-endian */
+ s->state[0] = 0x67452301;
+ s->state[1] = 0xefcdab89;
+ s->state[2] = 0x98badcfe;
+ s->state[3] = 0x10325476;
+ s->seeded = 1;
+ }
+
+ /* fill out the partial 64 byte block from previous calls */
+ if(s->blen){+ i = 64 - s->blen;
+ if(len < i)
+ i = len;
+ memmove(s->buf + s->blen, p, i);
+ len -= i;
+ s->blen += i;
+ p += i;
+ if(s->blen == 64){+ _md5block(s->buf, s->blen, s->state);
+ s->len += s->blen;
+ s->blen = 0;
+ }
+ }
+
+ /* do 64 byte blocks */
+ i = len & ~0x3f;
+ if(i){+ _md5block(p, i, s->state);
+ s->len += i;
+ len -= i;
+ p += i;
+ }
+
+ /* save the left overs if not last call */
+ if(digest == 0){+ if(len){+ memmove(s->buf, p, len);
+ s->blen += len;
+ }
+ return s;
+ }
+
+ /*
+ * this is the last time through, pad what's left with 0x80,
+ * 0's, and the input count to create a multiple of 64 bytes
+ */
+ if(s->blen){+ p = s->buf;
+ len = s->blen;
+ } else {+ memmove(buf, p, len);
+ p = buf;
+ }
+ s->len += len;
+ e = p + len;
+ if(len < 56)
+ i = 56 - len;
+ else
+ i = 120 - len;
+ memset(e, 0, i);
+ *e = 0x80;
+ len += i;
+
+ /* append the count */
+ x[0] = s->len<<3;
+ x[1] = s->len>>29;
+ encode(p+len, x, 8);
+
+ /* digest the last part */
+ _md5block(p, len+8, s->state);
+ s->len += len;
+
+ /* return result and free state */
+ encode(digest, s->state, MD5dlen);
+ if(s->malloced == 1)
+ free(s);
+ return nil;
+}
+
+/*
+ * encodes input (u32int) into output (uchar). Assumes len is
+ * a multiple of 4.
+ */
+static void
+encode(uchar *output, u32int *input, ulong len)
+{+ u32int x;
+ uchar *e;
+
+ for(e = output + len; output < e;) {+ x = *input++;
+ *output++ = x;
+ *output++ = x >> 8;
+ *output++ = x >> 16;
+ *output++ = x >> 24;
+ }
+}
+
+DigestState*
+hmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+ DigestState *s)
+{+ return hmac_x(p, len, key, klen, digest, s, md5, MD5dlen);
+}
--- /dev/null
+++ b/libsec/md5block.c
@@ -1,0 +1,267 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * rfc1321 requires that I include this. The code is new. The constants
+ * all come from the rfc (hence the copyright). We trade a table for the
+ * macros in rfc. The total size is a lot less. -- presotto
+ *
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software forany particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+/*
+ * Rotate ammounts used in the algorithm
+ */
+enum
+{+ S11= 7,
+ S12= 12,
+ S13= 17,
+ S14= 22,
+
+ S21= 5,
+ S22= 9,
+ S23= 14,
+ S24= 20,
+
+ S31= 4,
+ S32= 11,
+ S33= 16,
+ S34= 23,
+
+ S41= 6,
+ S42= 10,
+ S43= 15,
+ S44= 21,
+};
+
+static u32int md5tab[] =
+{+ /* round 1 */
+/*[0]*/ 0xd76aa478,
+ 0xe8c7b756,
+ 0x242070db,
+ 0xc1bdceee,
+ 0xf57c0faf,
+ 0x4787c62a,
+ 0xa8304613,
+ 0xfd469501,
+ 0x698098d8,
+ 0x8b44f7af,
+ 0xffff5bb1,
+ 0x895cd7be,
+ 0x6b901122,
+ 0xfd987193,
+ 0xa679438e,
+ 0x49b40821,
+
+ /* round 2 */
+/*[16]*/0xf61e2562,
+ 0xc040b340,
+ 0x265e5a51,
+ 0xe9b6c7aa,
+ 0xd62f105d,
+ 0x2441453,
+ 0xd8a1e681,
+ 0xe7d3fbc8,
+ 0x21e1cde6,
+ 0xc33707d6,
+ 0xf4d50d87,
+ 0x455a14ed,
+ 0xa9e3e905,
+ 0xfcefa3f8,
+ 0x676f02d9,
+ 0x8d2a4c8a,
+
+ /* round 3 */
+/*[32]*/0xfffa3942,
+ 0x8771f681,
+ 0x6d9d6122,
+ 0xfde5380c,
+ 0xa4beea44,
+ 0x4bdecfa9,
+ 0xf6bb4b60,
+ 0xbebfbc70,
+ 0x289b7ec6,
+ 0xeaa127fa,
+ 0xd4ef3085,
+ 0x4881d05,
+ 0xd9d4d039,
+ 0xe6db99e5,
+ 0x1fa27cf8,
+ 0xc4ac5665,
+
+ /* round 4 */
+/*[48]*/0xf4292244,
+ 0x432aff97,
+ 0xab9423a7,
+ 0xfc93a039,
+ 0x655b59c3,
+ 0x8f0ccc92,
+ 0xffeff47d,
+ 0x85845dd1,
+ 0x6fa87e4f,
+ 0xfe2ce6e0,
+ 0xa3014314,
+ 0x4e0811a1,
+ 0xf7537e82,
+ 0xbd3af235,
+ 0x2ad7d2bb,
+ 0xeb86d391,
+};
+
+static void decode(u32int*, uchar*, ulong);
+extern void _md5block(uchar *p, ulong len, u32int *s);
+
+void
+_md5block(uchar *p, ulong len, u32int *s)
+{+ u32int a, b, c, d, sh;
+ u32int *t;
+ uchar *end;
+ u32int x[16];
+
+ for(end = p+len; p < end; p += 64){+ a = s[0];
+ b = s[1];
+ c = s[2];
+ d = s[3];
+
+ decode(x, p, 64);
+
+ t = md5tab;
+ sh = 0;
+ for(; sh != 16; t += 4){+ a += ((c ^ d) & b) ^ d;
+ a += x[sh] + t[0];
+ a = (a << S11) | (a >> (32 - S11));
+ a += b;
+
+ d += ((b ^ c) & a) ^ c;
+ d += x[sh + 1] + t[1];
+ d = (d << S12) | (d >> (32 - S12));
+ d += a;
+
+ c += ((a ^ b) & d) ^ b;
+ c += x[sh + 2] + t[2];
+ c = (c << S13) | (c >> (32 - S13));
+ c += d;
+
+ b += ((d ^ a) & c) ^ a;
+ b += x[sh + 3] + t[3];
+ b = (b << S14) | (b >> (32 - S14));
+ b += c;
+
+ sh += 4;
+ }
+ sh = 1;
+ for(; sh != 1+20*4; t += 4){+ a += ((b ^ c) & d) ^ c;
+ a += x[sh & 0xf] + t[0];
+ a = (a << S21) | (a >> (32 - S21));
+ a += b;
+
+ d += ((a ^ b) & c) ^ b;
+ d += x[(sh + 5) & 0xf] + t[1];
+ d = (d << S22) | (d >> (32 - S22));
+ d += a;
+
+ c += ((d ^ a) & b) ^ a;
+ c += x[(sh + 10) & 0xf] + t[2];
+ c = (c << S23) | (c >> (32 - S23));
+ c += d;
+
+ b += ((c ^ d) & a) ^ d;
+ b += x[(sh + 15) & 0xf] + t[3];
+ b = (b << S24) | (b >> (32 - S24));
+ b += c;
+
+ sh += 20;
+ }
+ sh = 5;
+ for(; sh != 5+12*4; t += 4){+ a += b ^ c ^ d;
+ a += x[sh & 0xf] + t[0];
+ a = (a << S31) | (a >> (32 - S31));
+ a += b;
+
+ d += a ^ b ^ c;
+ d += x[(sh + 3) & 0xf] + t[1];
+ d = (d << S32) | (d >> (32 - S32));
+ d += a;
+
+ c += d ^ a ^ b;
+ c += x[(sh + 6) & 0xf] + t[2];
+ c = (c << S33) | (c >> (32 - S33));
+ c += d;
+
+ b += c ^ d ^ a;
+ b += x[(sh + 9) & 0xf] + t[3];
+ b = (b << S34) | (b >> (32 - S34));
+ b += c;
+
+ sh += 12;
+ }
+ sh = 0;
+ for(; sh != 28*4; t += 4){+ a += c ^ (b | ~d);
+ a += x[sh & 0xf] + t[0];
+ a = (a << S41) | (a >> (32 - S41));
+ a += b;
+
+ d += b ^ (a | ~c);
+ d += x[(sh + 7) & 0xf] + t[1];
+ d = (d << S42) | (d >> (32 - S42));
+ d += a;
+
+ c += a ^ (d | ~b);
+ c += x[(sh + 14) & 0xf] + t[2];
+ c = (c << S43) | (c >> (32 - S43));
+ c += d;
+
+ b += d ^ (c | ~a);
+ b += x[(sh + 21) & 0xf] + t[3];
+ b = (b << S44) | (b >> (32 - S44));
+ b += c;
+
+ sh += 28;
+ }
+
+ s[0] += a;
+ s[1] += b;
+ s[2] += c;
+ s[3] += d;
+ }
+}
+
+/*
+ * decodes input (uchar) into output (u32int). Assumes len is
+ * a multiple of 4.
+ */
+static void
+decode(u32int *output, uchar *input, ulong len)
+{+ uchar *e;
+
+ for(e = input+len; input < e; input += 4)
+ *output++ = input[0] | (input[1] << 8) |
+ (input[2] << 16) | (input[3] << 24);
+}
--- /dev/null
+++ b/libsec/nfastrand.c
@@ -1,0 +1,22 @@
+#include "os.h"
+#include <libsec.h>
+
+#define Maxrand ((1UL<<31)-1)
+
+ulong
+nfastrand(ulong n)
+{+ ulong m, r;
+
+ /*
+ * set m to the maximum multiple of n <= 2^31-1
+ * so we want a random number < m.
+ */
+ if(n > Maxrand)
+ sysfatal("nfastrand: n too large");+
+ m = Maxrand - Maxrand % n;
+ while((r = fastrand()) >= m)
+ ;
+ return r%n;
+}
--- /dev/null
+++ b/libsec/os.h
@@ -1,0 +1,2 @@
+#include <u.h>
+#include <libc.h>
--- /dev/null
+++ b/libsec/pbkdf2.c
@@ -1,0 +1,32 @@
+#include "os.h"
+#include <libsec.h>
+
+/* 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)
+{+ uchar block[256], tmp[256];
+ ulong i, j, k, n;
+ DigestState *ds;
+
+ assert(xlen <= sizeof(tmp));
+
+ for(i = 1; dlen > 0; i++, d += n, dlen -= n){+ tmp[3] = i;
+ tmp[2] = i >> 8;
+ tmp[1] = i >> 16;
+ tmp[0] = i >> 24;
+ ds = (*x)(s, slen, p, plen, nil, nil);
+ (*x)(tmp, 4, p, plen, block, ds);
+ memmove(tmp, block, xlen);
+ for(j = 1; j < rounds; j++){+ (*x)(tmp, xlen, p, plen, tmp, nil);
+ for(k=0; k<xlen; k++)
+ block[k] ^= tmp[k];
+ }
+ n = dlen > xlen ? xlen : dlen;
+ memmove(d, block, n);
+ }
+}
--- /dev/null
+++ b/libsec/poly1305.c
@@ -1,0 +1,195 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition
+
+ derived from http://github.com/floodberry/poly1305-donna
+*/
+
+#define U8TO32(p) ((u32int)(p)[0] | (u32int)(p)[1]<<8 | (u32int)(p)[2]<<16 | (u32int)(p)[3]<<24)
+#define U32TO8(p, v) (p)[0]=(v), (p)[1]=(v)>>8, (p)[2]=(v)>>16, (p)[3]=(v)>>24
+
+/* (r,s) = (key[0:15],key[16:31]), the one time key */
+DigestState*
+poly1305(uchar *m, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
+{+ u32int r0,r1,r2,r3,r4, s1,s2,s3,s4, h0,h1,h2,h3,h4, g0,g1,g2,g3,g4;
+ u64int d0,d1,d2,d3,d4, f;
+ u32int hibit, mask, c;
+
+ if(s == nil){+ s = malloc(sizeof(*s));
+ if(s == nil)
+ return nil;
+ memset(s, 0, sizeof(*s));
+ s->malloced = 1;
+ }
+
+ if(s->seeded == 0){+ assert(klen == 32);
+
+ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+ s->state[0] = (U8TO32(&key[ 0]) ) & 0x3ffffff;
+ s->state[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
+ s->state[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
+ s->state[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
+ s->state[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
+
+ /* h = 0 */
+ s->state[5] = 0;
+ s->state[6] = 0;
+ s->state[7] = 0;
+ s->state[8] = 0;
+ s->state[9] = 0;
+
+ /* save pad for later */
+ s->state[10] = U8TO32(&key[16]);
+ s->state[11] = U8TO32(&key[20]);
+ s->state[12] = U8TO32(&key[24]);
+ s->state[13] = U8TO32(&key[28]);
+
+ s->seeded = 1;
+ }
+
+ if(s->blen){+ c = 16 - s->blen;
+ if(c > len)
+ c = len;
+ memmove(s->buf + s->blen, m, c);
+ len -= c, m += c;
+ s->blen += c;
+ if(s->blen == 16){+ s->blen = 0;
+ poly1305(s->buf, 16, key, klen, nil, s);
+ } else if(len == 0){+ m = s->buf;
+ len = s->blen;
+ s->blen = 0;
+ }
+ }
+
+ r0 = s->state[0];
+ r1 = s->state[1];
+ r2 = s->state[2];
+ r3 = s->state[3];
+ r4 = s->state[4];
+
+ h0 = s->state[5];
+ h1 = s->state[6];
+ h2 = s->state[7];
+ h3 = s->state[8];
+ h4 = s->state[9];
+
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ hibit = 1<<24; /* 1<<128 */
+
+ while(len >= 16){+Block:
+ /* h += m[i] */
+ h0 += (U8TO32(&m[0]) ) & 0x3ffffff;
+ h1 += (U8TO32(&m[3]) >> 2) & 0x3ffffff;
+ h2 += (U8TO32(&m[6]) >> 4) & 0x3ffffff;
+ h3 += (U8TO32(&m[9]) >> 6) & 0x3ffffff;
+ h4 += (U8TO32(&m[12])>> 8) | hibit;
+
+ /* h *= r */
+ d0 = ((u64int)h0 * r0) + ((u64int)h1 * s4) + ((u64int)h2 * s3) + ((u64int)h3 * s2) + ((u64int)h4 * s1);
+ d1 = ((u64int)h0 * r1) + ((u64int)h1 * r0) + ((u64int)h2 * s4) + ((u64int)h3 * s3) + ((u64int)h4 * s2);
+ d2 = ((u64int)h0 * r2) + ((u64int)h1 * r1) + ((u64int)h2 * r0) + ((u64int)h3 * s4) + ((u64int)h4 * s3);
+ d3 = ((u64int)h0 * r3) + ((u64int)h1 * r2) + ((u64int)h2 * r1) + ((u64int)h3 * r0) + ((u64int)h4 * s4);
+ d4 = ((u64int)h0 * r4) + ((u64int)h1 * r3) + ((u64int)h2 * r2) + ((u64int)h3 * r1) + ((u64int)h4 * r0);
+
+ /* (partial) h %= p */
+ c = (u32int)(d0 >> 26); h0 = (u32int)d0 & 0x3ffffff;
+ d1 += c; c = (u32int)(d1 >> 26); h1 = (u32int)d1 & 0x3ffffff;
+ d2 += c; c = (u32int)(d2 >> 26); h2 = (u32int)d2 & 0x3ffffff;
+ d3 += c; c = (u32int)(d3 >> 26); h3 = (u32int)d3 & 0x3ffffff;
+ d4 += c; c = (u32int)(d4 >> 26); h4 = (u32int)d4 & 0x3ffffff;
+ h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ len -= 16, m += 16;
+ }
+
+ if(len){+ s->blen = len;
+ memmove(s->buf, m, len);
+ }
+
+ if(digest == nil){+ s->state[5] = h0;
+ s->state[6] = h1;
+ s->state[7] = h2;
+ s->state[8] = h3;
+ s->state[9] = h4;
+ return s;
+ }
+
+ if(len){+ m = s->buf;
+ m[len++] = 1;
+ while(len < 16)
+ m[len++] = 0;
+ hibit = 0;
+ goto Block;
+ }
+
+ c = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ /* compute h + -p */
+ g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + c - (1 << 26);
+
+ /* select h if h < p, or h + -p if h >= p */
+ mask = (g4 >> 31) - 1;
+ g0 &= mask;
+ g1 &= mask;
+ g2 &= mask;
+ g3 &= mask;
+ g4 &= mask;
+ mask = ~mask;
+ h0 = (h0 & mask) | g0;
+ h1 = (h1 & mask) | g1;
+ h2 = (h2 & mask) | g2;
+ h3 = (h3 & mask) | g3;
+ h4 = (h4 & mask) | g4;
+
+ /* h = h % (2^128) */
+ h0 = (h0 ) | (h1 << 26);
+ h1 = (h1 >> 6) | (h2 << 20);
+ h2 = (h2 >> 12) | (h3 << 14);
+ h3 = (h3 >> 18) | (h4 << 8);
+
+ /* digest = (h + pad) % (2^128) */
+ f = (u64int)h0 + s->state[10] ; h0 = (u32int)f;
+ f = (u64int)h1 + s->state[11] + (f >> 32); h1 = (u32int)f;
+ f = (u64int)h2 + s->state[12] + (f >> 32); h2 = (u32int)f;
+ f = (u64int)h3 + s->state[13] + (f >> 32); h3 = (u32int)f;
+
+ U32TO8(&digest[0], h0);
+ U32TO8(&digest[4], h1);
+ U32TO8(&digest[8], h2);
+ U32TO8(&digest[12], h3);
+
+ if(s->malloced){+ memset(s, 0, sizeof(*s));
+ free(s);
+ return nil;
+ }
+
+ memset(s, 0, sizeof(*s));
+ return nil;
+}
--- /dev/null
+++ b/libsec/prng.c
@@ -1,0 +1,14 @@
+#include "os.h"
+#include <libsec.h>
+
+//
+// just use the libc prng to fill a buffer
+//
+void
+prng(uchar *p, int n)
+{+ uchar *e;
+
+ for(e = p+n; p < e; p++)
+ *p = rand();
+}
--- /dev/null
+++ b/libsec/rc4.c
@@ -1,0 +1,104 @@
+#include "os.h"
+#include <libsec.h>
+
+void
+setupRC4state(RC4state *key, uchar *start, int n)
+{+ int t;
+ int index2;
+ uchar *state;
+ uchar *p, *e, *sp, *se;
+
+ state = key->state;
+ se = &state[256];
+ for(sp = state; sp < se; sp++)
+ *sp = sp - state;
+
+ key->x = 0;
+ key->y = 0;
+ index2 = 0;
+ e = start + n;
+ p = start;
+ for(sp = state; sp < se; sp++)
+ {+ t = *sp;
+ index2 = (*p + t + index2) & 255;
+ *sp = state[index2];
+ state[index2] = t;
+ if(++p >= e)
+ p = start;
+ }
+}
+
+void
+rc4(RC4state *key, uchar *p, int len)
+{+ int tx, ty;
+ int x, y;
+ uchar *state;
+ uchar *e;
+
+ x = key->x;
+ y = key->y;
+ state = &key->state[0];
+ for(e = p + len; p < e; p++)
+ {+ x = (x+1)&255;
+ tx = state[x];
+ y = (y+tx)&255;
+ ty = state[y];
+ state[x] = ty;
+ state[y] = tx;
+ *p ^= state[(tx+ty)&255];
+ }
+ key->x = x;
+ key->y = y;
+}
+
+void
+rc4skip(RC4state *key, int len)
+{+ int tx, ty;
+ int x, y;
+ uchar *state;
+ int i;
+
+ x = key->x;
+ y = key->y;
+ state = &key->state[0];
+ for(i=0; i<len; i++)
+ {+ x = (x+1)&255;
+ tx = state[x];
+ y = (y+tx)&255;
+ ty = state[y];
+ state[x] = ty;
+ state[y] = tx;
+ }
+ key->x = x;
+ key->y = y;
+}
+
+void
+rc4back(RC4state *key, int len)
+{+ int tx, ty;
+ int x, y;
+ uchar *state;
+ int i;
+
+ x = key->x;
+ y = key->y;
+ state = &key->state[0];
+ for(i=0; i<len; i++)
+ {+ ty = state[x];
+ tx = state[y];
+ state[y] = ty;
+ state[x] = tx;
+ y = (y-tx)&255;
+ x = (x-1)&255;
+ }
+ key->x = x;
+ key->y = y;
+}
--- /dev/null
+++ b/libsec/readcert.c
@@ -1,0 +1,63 @@
+#include "os.h"
+#include <libsec.h>
+
+static char*
+readfile(char *name)
+{+ int fd;
+ char *s;
+ Dir *d;
+
+ fd = open(name, OREAD);
+ if(fd < 0)
+ return nil;
+ if((d = dirfstat(fd)) == nil) {+ close(fd);
+ return nil;
+ }
+ s = malloc(d->length + 1);
+ if(s == nil || readn(fd, s, d->length) != d->length){+ free(s);
+ free(d);
+ close(fd);
+ return nil;
+ }
+ close(fd);
+ s[d->length] = '\0';
+ free(d);
+ return s;
+}
+
+uchar*
+readcert(char *filename, int *pcertlen)
+{+ char *pem;
+ uchar *binary;
+
+ pem = readfile(filename);
+ if(pem == nil){+ werrstr("can't read %s: %r", filename);+ return nil;
+ }
+ binary = decodePEM(pem, "CERTIFICATE", pcertlen, nil);
+ free(pem);
+ if(binary == nil){+ werrstr("can't parse %s", filename);+ return nil;
+ }
+ return binary;
+}
+
+PEMChain *
+readcertchain(char *filename)
+{+ char *chfile;
+
+ chfile = readfile(filename);
+ if (chfile == nil) {+ werrstr("can't read %s: %r", filename);+ return nil;
+ }
+ return decodepemchain(chfile, "CERTIFICATE");
+}
+
--- /dev/null
+++ b/libsec/rsaalloc.c
@@ -1,0 +1,52 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+RSApub*
+rsapuballoc(void)
+{+ RSApub *rsa;
+
+ rsa = mallocz(sizeof(*rsa), 1);
+ if(rsa == nil)
+ sysfatal("rsapuballoc");+ return rsa;
+}
+
+void
+rsapubfree(RSApub *rsa)
+{+ if(rsa == nil)
+ return;
+ mpfree(rsa->ek);
+ mpfree(rsa->n);
+ free(rsa);
+}
+
+
+RSApriv*
+rsaprivalloc(void)
+{+ RSApriv *rsa;
+
+ rsa = mallocz(sizeof(*rsa), 1);
+ if(rsa == nil)
+ sysfatal("rsaprivalloc");+ return rsa;
+}
+
+void
+rsaprivfree(RSApriv *rsa)
+{+ if(rsa == nil)
+ return;
+ mpfree(rsa->pub.ek);
+ mpfree(rsa->pub.n);
+ mpfree(rsa->dk);
+ mpfree(rsa->p);
+ mpfree(rsa->q);
+ mpfree(rsa->kp);
+ mpfree(rsa->kq);
+ mpfree(rsa->c2);
+ free(rsa);
+}
--- /dev/null
+++ b/libsec/rsadecrypt.c
@@ -1,0 +1,37 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+// decrypt rsa using garner's algorithm for the chinese remainder theorem
+// seminumerical algorithms, knuth, pp 253-254
+// applied cryptography, menezes et al, pg 612
+mpint*
+rsadecrypt(RSApriv *rsa, mpint *in, mpint *out)
+{+ mpint *v1, *v2;
+
+ if(out == nil)
+ out = mpnew(0);
+
+ // convert in to modular representation
+ v1 = mpnew(0);
+ mpmod(in, rsa->p, v1);
+ v2 = mpnew(0);
+ mpmod(in, rsa->q, v2);
+
+ // exponentiate the modular rep
+ mpexp(v1, rsa->kp, rsa->p, v1);
+ mpexp(v2, rsa->kq, rsa->q, v2);
+
+ // out = v1 + p*((v2-v1)*c2 mod q)
+ mpsub(v2, v1, v2);
+ mpmul(v2, rsa->c2, v2);
+ mpmod(v2, rsa->q, v2);
+ mpmul(v2, rsa->p, out);
+ mpadd(v1, out, out);
+
+ mpfree(v1);
+ mpfree(v2);
+
+ return out;
+}
--- /dev/null
+++ b/libsec/rsaencrypt.c
@@ -1,0 +1,12 @@
+#include "os.h"
+#include <mp.h>
+#include <libsec.h>
+
+mpint*
+rsaencrypt(RSApub *rsa, mpint *in, mpint *out)
+{+ if(out == nil)
+ out = mpnew(0);
+ mpexp(in, rsa->ek, rsa->n, out);
+ return out;
+}
--- /dev/null
+++ b/libsec/secp256k1.c
@@ -1,0 +1,11 @@
+#include "os.h"
+#include <mp.h>
+void secp256k1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h){+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", nil, 16, p);+ mpassign(mpzero, a);
+ uitomp(7UL, b);
+ strtomp("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", nil, 16, x);+ strtomp("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", nil, 16, y);+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", nil, 16, n);+ mpassign(mpone, h);
+ }
--- /dev/null
+++ b/libsec/secp256r1.c
@@ -1,0 +1,12 @@
+#include "os.h"
+#include <mp.h>
+void secp256r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h){+ strtomp("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", nil, 16, p);+ uitomp(3UL, a);
+ mpsub(p, a, a);
+ strtomp("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", nil, 16, b);+ strtomp("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", nil, 16, x);+ strtomp("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", nil, 16, y);+ strtomp("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", nil, 16, n);+ mpassign(mpone, h);
+ }
--- /dev/null
+++ b/libsec/secp384r1.c
@@ -1,0 +1,12 @@
+#include "os.h"
+#include <mp.h>
+void secp384r1(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h){+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", nil, 16, p);+ uitomp(3UL, a);
+ mpsub(p, a, a);
+ strtomp("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", nil, 16, b);+ strtomp("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", nil, 16, x);+ strtomp("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", nil, 16, y);+ strtomp("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", nil, 16, n);+ mpassign(mpone, h);
+ }
--- /dev/null
+++ b/libsec/sha1.c
@@ -1,0 +1,134 @@
+#include "os.h"
+#include <libsec.h>
+
+static void encode(uchar*, u32int*, ulong);
+
+extern void _sha1block(uchar*, ulong, u32int*);
+
+/*
+ * we require len to be a multiple of 64 for all but
+ * the last call. There must be room in the input buffer
+ * to pad.
+ */
+SHA1state*
+sha1(uchar *p, ulong len, uchar *digest, SHA1state *s)
+{+ uchar buf[128];
+ u32int x[16];
+ int i;
+ uchar *e;
+
+ if(s == nil){+ s = malloc(sizeof(*s));
+ if(s == nil)
+ return nil;
+ memset(s, 0, sizeof(*s));
+ s->malloced = 1;
+ }
+
+ if(s->seeded == 0){+ /* seed the state, these constants would look nicer big-endian */
+ s->state[0] = 0x67452301;
+ s->state[1] = 0xefcdab89;
+ s->state[2] = 0x98badcfe;
+ s->state[3] = 0x10325476;
+ s->state[4] = 0xc3d2e1f0;
+ s->seeded = 1;
+ }
+
+ /* fill out the partial 64 byte block from previous calls */
+ if(s->blen){+ i = 64 - s->blen;
+ if(len < i)
+ i = len;
+ memmove(s->buf + s->blen, p, i);
+ len -= i;
+ s->blen += i;
+ p += i;
+ if(s->blen == 64){+ _sha1block(s->buf, s->blen, s->state);
+ s->len += s->blen;
+ s->blen = 0;
+ }
+ }
+
+ /* do 64 byte blocks */
+ i = len & ~0x3f;
+ if(i){+ _sha1block(p, i, s->state);
+ s->len += i;
+ len -= i;
+ p += i;
+ }
+
+ /* save the left overs if not last call */
+ if(digest == 0){+ if(len){+ memmove(s->buf, p, len);
+ s->blen += len;
+ }
+ return s;
+ }
+
+ /*
+ * this is the last time through, pad what's left with 0x80,
+ * 0's, and the input count to create a multiple of 64 bytes
+ */
+ if(s->blen){+ p = s->buf;
+ len = s->blen;
+ } else {+ memmove(buf, p, len);
+ p = buf;
+ }
+ s->len += len;
+ e = p + len;
+ if(len < 56)
+ i = 56 - len;
+ else
+ i = 120 - len;
+ memset(e, 0, i);
+ *e = 0x80;
+ len += i;
+
+ /* append the count */
+ x[0] = s->len>>29;
+ x[1] = s->len<<3;
+ encode(p+len, x, 8);
+
+ /* digest the last part */
+ _sha1block(p, len+8, s->state);
+ s->len += len+8;
+
+ /* return result and free state */
+ encode(digest, s->state, SHA1dlen);
+ if(s->malloced == 1)
+ free(s);
+ return nil;
+}
+
+/*
+ * encodes input (ulong) into output (uchar). Assumes len is
+ * a multiple of 4.
+ */
+static void
+encode(uchar *output, u32int *input, ulong len)
+{+ u32int x;
+ uchar *e;
+
+ for(e = output + len; output < e;) {+ x = *input++;
+ *output++ = x >> 24;
+ *output++ = x >> 16;
+ *output++ = x >> 8;
+ *output++ = x;
+ }
+}
+
+DigestState*
+hmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+ DigestState *s)
+{+ return hmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen);
+}
--- /dev/null
+++ b/libsec/sha1block.c
@@ -1,0 +1,137 @@
+#include "os.h"
+
+#define ROTL(x,n) (((x)<<n)|((x)>>(32-n)))
+
+#define F0(x,y,z) (0x5a827999 + ((z) ^ ((x) & ((y) ^ (z)))))
+#define F1(x,y,z) (0x6ed9eba1 + ((x) ^ (y) ^ (z)))
+#define F2(x,y,z) (0x8f1bbcdc + (((x) & (y)) | (((x) | (y)) & (z))))
+#define F3(x,y,z) (0xca62c1d6 + ((x) ^ (y) ^ (z)))
+
+void
+_sha1block(uchar *p, ulong len, u32int *s)
+{+ u32int w[16], a, b, c, d, e;
+ uchar *end;
+
+ /* at this point, we have a multiple of 64 bytes */
+ for(end = p+len; p < end;){+ a = s[0];
+ b = s[1];
+ c = s[2];
+ d = s[3];
+ e = s[4];
+
+#define STEP(a,b,c,d,e,f,i) \
+ if(i < 16) {\+ w[i] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3]; \
+ p += 4; \
+ } else { \+ u32int x = w[(i-3)&15] ^ w[(i-8)&15] ^ w[(i-14)&15] ^ w[(i-16)&15]; \
+ w[i&15] = ROTL(x, 1); \
+ } \
+ e += ROTL(a, 5) + w[i&15] + f(b,c,d); \
+ b = ROTL(b, 30);
+
+ STEP(a,b,c,d,e,F0,0);
+ STEP(e,a,b,c,d,F0,1);
+ STEP(d,e,a,b,c,F0,2);
+ STEP(c,d,e,a,b,F0,3);
+ STEP(b,c,d,e,a,F0,4);
+
+ STEP(a,b,c,d,e,F0,5);
+ STEP(e,a,b,c,d,F0,6);
+ STEP(d,e,a,b,c,F0,7);
+ STEP(c,d,e,a,b,F0,8);
+ STEP(b,c,d,e,a,F0,9);
+
+ STEP(a,b,c,d,e,F0,10);
+ STEP(e,a,b,c,d,F0,11);
+ STEP(d,e,a,b,c,F0,12);
+ STEP(c,d,e,a,b,F0,13);
+ STEP(b,c,d,e,a,F0,14);
+
+ STEP(a,b,c,d,e,F0,15);
+ STEP(e,a,b,c,d,F0,16);
+ STEP(d,e,a,b,c,F0,17);
+ STEP(c,d,e,a,b,F0,18);
+ STEP(b,c,d,e,a,F0,19);
+
+ STEP(a,b,c,d,e,F1,20);
+ STEP(e,a,b,c,d,F1,21);
+ STEP(d,e,a,b,c,F1,22);
+ STEP(c,d,e,a,b,F1,23);
+ STEP(b,c,d,e,a,F1,24);
+
+ STEP(a,b,c,d,e,F1,25);
+ STEP(e,a,b,c,d,F1,26);
+ STEP(d,e,a,b,c,F1,27);
+ STEP(c,d,e,a,b,F1,28);
+ STEP(b,c,d,e,a,F1,29);
+
+ STEP(a,b,c,d,e,F1,30);
+ STEP(e,a,b,c,d,F1,31);
+ STEP(d,e,a,b,c,F1,32);
+ STEP(c,d,e,a,b,F1,33);
+ STEP(b,c,d,e,a,F1,34);
+
+ STEP(a,b,c,d,e,F1,35);
+ STEP(e,a,b,c,d,F1,36);
+ STEP(d,e,a,b,c,F1,37);
+ STEP(c,d,e,a,b,F1,38);
+ STEP(b,c,d,e,a,F1,39);
+
+ STEP(a,b,c,d,e,F2,40);
+ STEP(e,a,b,c,d,F2,41);
+ STEP(d,e,a,b,c,F2,42);
+ STEP(c,d,e,a,b,F2,43);
+ STEP(b,c,d,e,a,F2,44);
+
+ STEP(a,b,c,d,e,F2,45);
+ STEP(e,a,b,c,d,F2,46);
+ STEP(d,e,a,b,c,F2,47);
+ STEP(c,d,e,a,b,F2,48);
+ STEP(b,c,d,e,a,F2,49);
+
+ STEP(a,b,c,d,e,F2,50);
+ STEP(e,a,b,c,d,F2,51);
+ STEP(d,e,a,b,c,F2,52);
+ STEP(c,d,e,a,b,F2,53);
+ STEP(b,c,d,e,a,F2,54);
+
+ STEP(a,b,c,d,e,F2,55);
+ STEP(e,a,b,c,d,F2,56);
+ STEP(d,e,a,b,c,F2,57);
+ STEP(c,d,e,a,b,F2,58);
+ STEP(b,c,d,e,a,F2,59);
+
+ STEP(a,b,c,d,e,F3,60);
+ STEP(e,a,b,c,d,F3,61);
+ STEP(d,e,a,b,c,F3,62);
+ STEP(c,d,e,a,b,F3,63);
+ STEP(b,c,d,e,a,F3,64);
+
+ STEP(a,b,c,d,e,F3,65);
+ STEP(e,a,b,c,d,F3,66);
+ STEP(d,e,a,b,c,F3,67);
+ STEP(c,d,e,a,b,F3,68);
+ STEP(b,c,d,e,a,F3,69);
+
+ STEP(a,b,c,d,e,F3,70);
+ STEP(e,a,b,c,d,F3,71);
+ STEP(d,e,a,b,c,F3,72);
+ STEP(c,d,e,a,b,F3,73);
+ STEP(b,c,d,e,a,F3,74);
+
+ STEP(a,b,c,d,e,F3,75);
+ STEP(e,a,b,c,d,F3,76);
+ STEP(d,e,a,b,c,F3,77);
+ STEP(c,d,e,a,b,F3,78);
+ STEP(b,c,d,e,a,F3,79);
+
+ s[0] += a;
+ s[1] += b;
+ s[2] += c;
+ s[3] += d;
+ s[4] += e;
+ }
+}
--- /dev/null
+++ b/libsec/sha2_128.c
@@ -1,0 +1,191 @@
+/*
+ * sha2 128-bit
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+static void encode64(uchar*, u64int*, ulong);
+static DigestState* sha2_128(uchar *, ulong, uchar *, SHA2_256state *, int);
+
+extern void _sha2block128(uchar*, ulong, u64int*);
+
+/*
+ * for sha2_384 and sha2_512, len must be multiple of 128 for all but
+ * the last call. There must be room in the input buffer to pad.
+ *
+ * Note: sha2_384 calls sha2_512block as sha2_384; it just uses a different
+ * initial seed to produce a truncated 384b hash result. otherwise
+ * it's the same as sha2_512.
+ */
+SHA2_384state*
+sha2_384(uchar *p, ulong len, uchar *digest, SHA2_384state *s)
+{+ if(s == nil) {+ s = mallocz(sizeof(*s), 1);
+ if(s == nil)
+ return nil;
+ s->malloced = 1;
+ }
+ if(s->seeded == 0){+ /*
+ * seed the state with the first 64 bits of the fractional
+ * parts of the square roots of the 9th thru 16th primes.
+ */
+ s->bstate[0] = 0xcbbb9d5dc1059ed8LL;
+ s->bstate[1] = 0x629a292a367cd507LL;
+ s->bstate[2] = 0x9159015a3070dd17LL;
+ s->bstate[3] = 0x152fecd8f70e5939LL;
+ s->bstate[4] = 0x67332667ffc00b31LL;
+ s->bstate[5] = 0x8eb44a8768581511LL;
+ s->bstate[6] = 0xdb0c2e0d64f98fa7LL;
+ s->bstate[7] = 0x47b5481dbefa4fa4LL;
+ s->seeded = 1;
+ }
+ return sha2_128(p, len, digest, s, SHA2_384dlen);
+}
+
+SHA2_512state*
+sha2_512(uchar *p, ulong len, uchar *digest, SHA2_512state *s)
+{+
+ if(s == nil) {+ s = mallocz(sizeof(*s), 1);
+ if(s == nil)
+ return nil;
+ s->malloced = 1;
+ }
+ if(s->seeded == 0){+ /*
+ * seed the state with the first 64 bits of the fractional
+ * parts of the square roots of the first 8 primes 2..19).
+ */
+ s->bstate[0] = 0x6a09e667f3bcc908LL;
+ s->bstate[1] = 0xbb67ae8584caa73bLL;
+ s->bstate[2] = 0x3c6ef372fe94f82bLL;
+ s->bstate[3] = 0xa54ff53a5f1d36f1LL;
+ s->bstate[4] = 0x510e527fade682d1LL;
+ s->bstate[5] = 0x9b05688c2b3e6c1fLL;
+ s->bstate[6] = 0x1f83d9abfb41bd6bLL;
+ s->bstate[7] = 0x5be0cd19137e2179LL;
+ s->seeded = 1;
+ }
+ return sha2_128(p, len, digest, s, SHA2_512dlen);
+}
+
+/* common 128 byte block padding and count code for SHA2_384 and SHA2_512 */
+static DigestState*
+sha2_128(uchar *p, ulong len, uchar *digest, SHA2_512state *s, int dlen)
+{+ int i;
+ u64int x[16];
+ uchar buf[256];
+ uchar *e;
+
+ /* fill out the partial 128 byte block from previous calls */
+ if(s->blen){+ i = 128 - s->blen;
+ if(len < i)
+ i = len;
+ memmove(s->buf + s->blen, p, i);
+ len -= i;
+ s->blen += i;
+ p += i;
+ if(s->blen == 128){+ _sha2block128(s->buf, s->blen, s->bstate);
+ s->len += s->blen;
+ s->blen = 0;
+ }
+ }
+
+ /* do 128 byte blocks */
+ i = len & ~(128-1);
+ if(i){+ _sha2block128(p, i, s->bstate);
+ s->len += i;
+ len -= i;
+ p += i;
+ }
+
+ /* save the left overs if not last call */
+ if(digest == 0){+ if(len){+ memmove(s->buf, p, len);
+ s->blen += len;
+ }
+ return s;
+ }
+
+ /*
+ * this is the last time through, pad what's left with 0x80,
+ * 0's, and the input count to create a multiple of 128 bytes.
+ */
+ if(s->blen){+ p = s->buf;
+ len = s->blen;
+ } else {+ memmove(buf, p, len);
+ p = buf;
+ }
+ s->len += len;
+ e = p + len;
+ if(len < 112)
+ i = 112 - len;
+ else
+ i = 240 - len;
+ memset(e, 0, i);
+ *e = 0x80;
+ len += i;
+
+ /* append the count */
+ x[0] = 0; /* assume 32b length, i.e. < 4GB */
+ x[1] = s->len<<3;
+ encode64(p+len, x, 16);
+
+ /* digest the last part */
+ _sha2block128(p, len+16, s->bstate);
+ s->len += len+16;
+
+ /* return result and free state */
+ encode64(digest, s->bstate, dlen);
+ if(s->malloced == 1)
+ free(s);
+ return nil;
+}
+
+/*
+ * Encodes input (ulong long) into output (uchar).
+ * Assumes len is a multiple of 8.
+ */
+static void
+encode64(uchar *output, u64int *input, ulong len)
+{+ u64int x;
+ uchar *e;
+
+ for(e = output + len; output < e;) {+ x = *input++;
+ *output++ = x >> 56;
+ *output++ = x >> 48;
+ *output++ = x >> 40;
+ *output++ = x >> 32;
+ *output++ = x >> 24;
+ *output++ = x >> 16;
+ *output++ = x >> 8;
+ *output++ = x;
+ }
+}
+
+DigestState*
+hmac_sha2_384(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+ DigestState *s)
+{+ return hmac_x(p, len, key, klen, digest, s, sha2_384, SHA2_384dlen);
+}
+
+DigestState*
+hmac_sha2_512(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+ DigestState *s)
+{+ return hmac_x(p, len, key, klen, digest, s, sha2_512, SHA2_512dlen);
+}
--- /dev/null
+++ b/libsec/sha2_64.c
@@ -1,0 +1,187 @@
+/*
+ * sha2 64-bit
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+static void encode32(uchar*, u32int*, ulong);
+static DigestState* sha2_64(uchar *, ulong, uchar *, SHA2_256state *, int);
+
+extern void _sha2block64(uchar*, ulong, u32int*);
+
+/*
+ * for sha2_224 and sha2_256, len must be multiple of 64 for all but
+ * the last call. There must be room in the input buffer to pad.
+ *
+ * Note: sha2_224 calls sha2_256block as sha2_224, just uses different
+ * initial seed and produces a 224b hash result. otherwise it's
+ * the same as sha2_256.
+ */
+
+SHA2_224state*
+sha2_224(uchar *p, ulong len, uchar *digest, SHA2_224state *s)
+{+ if(s == nil) {+ s = mallocz(sizeof(*s), 1);
+ if(s == nil)
+ return nil;
+ s->malloced = 1;
+ }
+ if(s->seeded == 0){+ /*
+ * seed the state with the first 32 bits of the fractional
+ * parts of the square roots of the first 8 primes 2..19).
+ */
+ s->state[0] = 0xc1059ed8;
+ s->state[1] = 0x367cd507;
+ s->state[2] = 0x3070dd17;
+ s->state[3] = 0xf70e5939;
+ s->state[4] = 0xffc00b31;
+ s->state[5] = 0x68581511;
+ s->state[6] = 0x64f98fa7;
+ s->state[7] = 0xbefa4fa4;
+ s->seeded = 1;
+ }
+ return sha2_64(p, len, digest, s, SHA2_224dlen);
+}
+
+SHA2_256state*
+sha2_256(uchar *p, ulong len, uchar *digest, SHA2_256state *s)
+{+ if(s == nil) {+ s = mallocz(sizeof(*s), 1);
+ if(s == nil)
+ return nil;
+ s->malloced = 1;
+ }
+ if(s->seeded == 0){+ /*
+ * seed the state with the first 32 bits of the fractional
+ * parts of the square roots of the first 8 primes 2..19).
+ */
+ s->state[0] = 0x6a09e667;
+ s->state[1] = 0xbb67ae85;
+ s->state[2] = 0x3c6ef372;
+ s->state[3] = 0xa54ff53a;
+ s->state[4] = 0x510e527f;
+ s->state[5] = 0x9b05688c;
+ s->state[6] = 0x1f83d9ab;
+ s->state[7] = 0x5be0cd19;
+ s->seeded = 1;
+ }
+ return sha2_64(p, len, digest, s, SHA2_256dlen);
+}
+
+/* common 64 byte block padding and count code for SHA2_224 and SHA2_256 */
+static DigestState*
+sha2_64(uchar *p, ulong len, uchar *digest, SHA2_256state *s, int dlen)
+{+ int i;
+ u32int x[16];
+ uchar buf[128];
+ uchar *e;
+
+ /* fill out the partial 64 byte block from previous calls */
+ if(s->blen){+ i = 64 - s->blen;
+ if(len < i)
+ i = len;
+ memmove(s->buf + s->blen, p, i);
+ len -= i;
+ s->blen += i;
+ p += i;
+ if(s->blen == 64){+ _sha2block64(s->buf, s->blen, s->state);
+ s->len += s->blen;
+ s->blen = 0;
+ }
+ }
+
+ /* do 64 byte blocks */
+ i = len & ~(64-1);
+ if(i){+ _sha2block64(p, i, s->state);
+ s->len += i;
+ len -= i;
+ p += i;
+ }
+
+ /* save the left overs if not last call */
+ if(digest == 0){+ if(len){+ memmove(s->buf, p, len);
+ s->blen += len;
+ }
+ return s;
+ }
+
+ /*
+ * this is the last time through, pad what's left with 0x80,
+ * 0's, and the input count to create a multiple of 64 bytes.
+ */
+ if(s->blen){+ p = s->buf;
+ len = s->blen;
+ } else {+ memmove(buf, p, len);
+ p = buf;
+ }
+ s->len += len;
+ e = p + len;
+ if(len < 56)
+ i = 56 - len;
+ else
+ i = 120 - len;
+ memset(e, 0, i);
+ *e = 0x80;
+ len += i;
+
+ /* append the count */
+ x[0] = s->len>>29;
+ x[1] = s->len<<3;
+ encode32(p+len, x, 8);
+
+ /* digest the last part */
+ _sha2block64(p, len+8, s->state);
+ s->len += len+8;
+
+ /* return result and free state */
+ encode32(digest, s->state, dlen);
+ if(s->malloced == 1)
+ free(s);
+ return nil;
+}
+
+/*
+ * Encodes input (ulong) into output (uchar).
+ * Assumes len is a multiple of 4.
+ */
+static void
+encode32(uchar *output, u32int *input, ulong len)
+{+ u32int x;
+ uchar *e;
+
+ for(e = output + len; output < e;) {+ x = *input++;
+ *output++ = x >> 24;
+ *output++ = x >> 16;
+ *output++ = x >> 8;
+ *output++ = x;
+ }
+}
+
+DigestState*
+hmac_sha2_224(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+ DigestState *s)
+{+ return hmac_x(p, len, key, klen, digest, s, sha2_224, SHA2_224dlen);
+}
+
+DigestState*
+hmac_sha2_256(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest,
+ DigestState *s)
+{+ return hmac_x(p, len, key, klen, digest, s, sha2_256, SHA2_256dlen);
+}
--- /dev/null
+++ b/libsec/sha2block128.c
@@ -1,0 +1,176 @@
+/*
+ * sha2_512 block cipher - unrolled version
+ *
+ * note: the following upper and lower case macro names are distinct
+ * and reflect the functions defined in FIPS pub. 180-2.
+ */
+
+#include "os.h"
+
+#define ROTR(x,n) (((x) >> (n)) | ((x) << (64-(n))))
+#define sigma0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x) >> 7))
+#define sigma1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x) >> 6))
+#define SIGMA0(x) (ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+#define SIGMA1(x) (ROTR((x),14) ^ ROTR((x),18) ^ ROTR((x),41))
+#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x,y,z) (((x) | (y)) & ((z) | ((x) & (y))))
+
+/*
+ * first 64 bits of the fractional parts of cube roots of
+ * first 80 primes (2..311).
+ */
+static u64int K512[80] = {+ 0x428a2f98d728ae22LL, 0x7137449123ef65cdLL, 0xb5c0fbcfec4d3b2fLL, 0xe9b5dba58189dbbcLL,
+ 0x3956c25bf348b538LL, 0x59f111f1b605d019LL, 0x923f82a4af194f9bLL, 0xab1c5ed5da6d8118LL,
+ 0xd807aa98a3030242LL, 0x12835b0145706fbeLL, 0x243185be4ee4b28cLL, 0x550c7dc3d5ffb4e2LL,
+ 0x72be5d74f27b896fLL, 0x80deb1fe3b1696b1LL, 0x9bdc06a725c71235LL, 0xc19bf174cf692694LL,
+ 0xe49b69c19ef14ad2LL, 0xefbe4786384f25e3LL, 0x0fc19dc68b8cd5b5LL, 0x240ca1cc77ac9c65LL,
+ 0x2de92c6f592b0275LL, 0x4a7484aa6ea6e483LL, 0x5cb0a9dcbd41fbd4LL, 0x76f988da831153b5LL,
+ 0x983e5152ee66dfabLL, 0xa831c66d2db43210LL, 0xb00327c898fb213fLL, 0xbf597fc7beef0ee4LL,
+ 0xc6e00bf33da88fc2LL, 0xd5a79147930aa725LL, 0x06ca6351e003826fLL, 0x142929670a0e6e70LL,
+ 0x27b70a8546d22ffcLL, 0x2e1b21385c26c926LL, 0x4d2c6dfc5ac42aedLL, 0x53380d139d95b3dfLL,
+ 0x650a73548baf63deLL, 0x766a0abb3c77b2a8LL, 0x81c2c92e47edaee6LL, 0x92722c851482353bLL,
+ 0xa2bfe8a14cf10364LL, 0xa81a664bbc423001LL, 0xc24b8b70d0f89791LL, 0xc76c51a30654be30LL,
+ 0xd192e819d6ef5218LL, 0xd69906245565a910LL, 0xf40e35855771202aLL, 0x106aa07032bbd1b8LL,
+ 0x19a4c116b8d2d0c8LL, 0x1e376c085141ab53LL, 0x2748774cdf8eeb99LL, 0x34b0bcb5e19b48a8LL,
+ 0x391c0cb3c5c95a63LL, 0x4ed8aa4ae3418acbLL, 0x5b9cca4f7763e373LL, 0x682e6ff3d6b2b8a3LL,
+ 0x748f82ee5defb2fcLL, 0x78a5636f43172f60LL, 0x84c87814a1f0ab72LL, 0x8cc702081a6439ecLL,
+ 0x90befffa23631e28LL, 0xa4506cebde82bde9LL, 0xbef9a3f7b2c67915LL, 0xc67178f2e372532bLL,
+ 0xca273eceea26619cLL, 0xd186b8c721c0c207LL, 0xeada7dd6cde0eb1eLL, 0xf57d4f7fee6ed178LL,
+ 0x06f067aa72176fbaLL, 0x0a637dc5a2c898a6LL, 0x113f9804bef90daeLL, 0x1b710b35131c471bLL,
+ 0x28db77f523047d84LL, 0x32caab7b40c72493LL, 0x3c9ebe0a15c9bebcLL, 0x431d67c49c100d4cLL,
+ 0x4cc5d4becb3e42b6LL, 0x597f299cfc657e2aLL, 0x5fcb6fab3ad6faecLL, 0x6c44198c4a475817LL
+};
+
+void
+_sha2block128(uchar *p, ulong len, u64int *s)
+{+ u64int w[16], a, b, c, d, e, f, g, h;
+ uchar *end;
+
+ /* at this point, we have a multiple of 64 bytes */
+ for(end = p+len; p < end;){+ a = s[0];
+ b = s[1];
+ c = s[2];
+ d = s[3];
+ e = s[4];
+ f = s[5];
+ g = s[6];
+ h = s[7];
+
+#define STEP(a,b,c,d,e,f,g,h,i) \
+ if(i < 16) { \+ w[i] = (u64int)(p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3])<<32 | \
+ (p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7]); \
+ p += 8; \
+ } else { \+ u64int s0, s1; \
+ s1 = sigma1(w[(i-2)&15]); \
+ s0 = sigma0(w[(i-15)&15]); \
+ w[i&15] += s1 + w[(i-7)&15] + s0; \
+ } \
+ h += SIGMA1(e) + Ch(e,f,g) + K512[i] + w[i&15]; \
+ d += h; \
+ h += SIGMA0(a) + Maj(a,b,c);
+
+ STEP(a,b,c,d,e,f,g,h,0);
+ STEP(h,a,b,c,d,e,f,g,1);
+ STEP(g,h,a,b,c,d,e,f,2);
+ STEP(f,g,h,a,b,c,d,e,3);
+ STEP(e,f,g,h,a,b,c,d,4);
+ STEP(d,e,f,g,h,a,b,c,5);
+ STEP(c,d,e,f,g,h,a,b,6);
+ STEP(b,c,d,e,f,g,h,a,7);
+
+ STEP(a,b,c,d,e,f,g,h,8);
+ STEP(h,a,b,c,d,e,f,g,9);
+ STEP(g,h,a,b,c,d,e,f,10);
+ STEP(f,g,h,a,b,c,d,e,11);
+ STEP(e,f,g,h,a,b,c,d,12);
+ STEP(d,e,f,g,h,a,b,c,13);
+ STEP(c,d,e,f,g,h,a,b,14);
+ STEP(b,c,d,e,f,g,h,a,15);
+
+ STEP(a,b,c,d,e,f,g,h,16);
+ STEP(h,a,b,c,d,e,f,g,17);
+ STEP(g,h,a,b,c,d,e,f,18);
+ STEP(f,g,h,a,b,c,d,e,19);
+ STEP(e,f,g,h,a,b,c,d,20);
+ STEP(d,e,f,g,h,a,b,c,21);
+ STEP(c,d,e,f,g,h,a,b,22);
+ STEP(b,c,d,e,f,g,h,a,23);
+
+ STEP(a,b,c,d,e,f,g,h,24);
+ STEP(h,a,b,c,d,e,f,g,25);
+ STEP(g,h,a,b,c,d,e,f,26);
+ STEP(f,g,h,a,b,c,d,e,27);
+ STEP(e,f,g,h,a,b,c,d,28);
+ STEP(d,e,f,g,h,a,b,c,29);
+ STEP(c,d,e,f,g,h,a,b,30);
+ STEP(b,c,d,e,f,g,h,a,31);
+
+ STEP(a,b,c,d,e,f,g,h,32);
+ STEP(h,a,b,c,d,e,f,g,33);
+ STEP(g,h,a,b,c,d,e,f,34);
+ STEP(f,g,h,a,b,c,d,e,35);
+ STEP(e,f,g,h,a,b,c,d,36);
+ STEP(d,e,f,g,h,a,b,c,37);
+ STEP(c,d,e,f,g,h,a,b,38);
+ STEP(b,c,d,e,f,g,h,a,39);
+
+ STEP(a,b,c,d,e,f,g,h,40);
+ STEP(h,a,b,c,d,e,f,g,41);
+ STEP(g,h,a,b,c,d,e,f,42);
+ STEP(f,g,h,a,b,c,d,e,43);
+ STEP(e,f,g,h,a,b,c,d,44);
+ STEP(d,e,f,g,h,a,b,c,45);
+ STEP(c,d,e,f,g,h,a,b,46);
+ STEP(b,c,d,e,f,g,h,a,47);
+
+ STEP(a,b,c,d,e,f,g,h,48);
+ STEP(h,a,b,c,d,e,f,g,49);
+ STEP(g,h,a,b,c,d,e,f,50);
+ STEP(f,g,h,a,b,c,d,e,51);
+ STEP(e,f,g,h,a,b,c,d,52);
+ STEP(d,e,f,g,h,a,b,c,53);
+ STEP(c,d,e,f,g,h,a,b,54);
+ STEP(b,c,d,e,f,g,h,a,55);
+
+ STEP(a,b,c,d,e,f,g,h,56);
+ STEP(h,a,b,c,d,e,f,g,57);
+ STEP(g,h,a,b,c,d,e,f,58);
+ STEP(f,g,h,a,b,c,d,e,59);
+ STEP(e,f,g,h,a,b,c,d,60);
+ STEP(d,e,f,g,h,a,b,c,61);
+ STEP(c,d,e,f,g,h,a,b,62);
+ STEP(b,c,d,e,f,g,h,a,63);
+
+ STEP(a,b,c,d,e,f,g,h,64);
+ STEP(h,a,b,c,d,e,f,g,65);
+ STEP(g,h,a,b,c,d,e,f,66);
+ STEP(f,g,h,a,b,c,d,e,67);
+ STEP(e,f,g,h,a,b,c,d,68);
+ STEP(d,e,f,g,h,a,b,c,69);
+ STEP(c,d,e,f,g,h,a,b,70);
+ STEP(b,c,d,e,f,g,h,a,71);
+
+ STEP(a,b,c,d,e,f,g,h,72);
+ STEP(h,a,b,c,d,e,f,g,73);
+ STEP(g,h,a,b,c,d,e,f,74);
+ STEP(f,g,h,a,b,c,d,e,75);
+ STEP(e,f,g,h,a,b,c,d,76);
+ STEP(d,e,f,g,h,a,b,c,77);
+ STEP(c,d,e,f,g,h,a,b,78);
+ STEP(b,c,d,e,f,g,h,a,79);
+
+ s[0] += a;
+ s[1] += b;
+ s[2] += c;
+ s[3] += d;
+ s[4] += e;
+ s[5] += f;
+ s[6] += g;
+ s[7] += h;
+ }
+}
--- /dev/null
+++ b/libsec/sha2block64.c
@@ -1,0 +1,150 @@
+/*
+ * sha2_256 block cipher - unrolled version
+ *
+ * note: the following upper and lower case macro names are distinct
+ * and reflect the functions defined in FIPS pub. 180-2.
+ */
+
+#include "os.h"
+
+#define ROTR(x,n) (((x) >> (n)) | ((x) << (32-(n))))
+#define sigma0(x) (ROTR((x),7) ^ ROTR((x),18) ^ ((x) >> 3))
+#define sigma1(x) (ROTR((x),17) ^ ROTR((x),19) ^ ((x) >> 10))
+#define SIGMA0(x) (ROTR((x),2) ^ ROTR((x),13) ^ ROTR((x),22))
+#define SIGMA1(x) (ROTR((x),6) ^ ROTR((x),11) ^ ROTR((x),25))
+#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x,y,z) (((x) | (y)) & ((z) | ((x) & (y))))
+
+/*
+ * first 32 bits of the fractional parts of cube roots of
+ * first 64 primes (2..311).
+ */
+static u32int K256[64] = {+ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,
+ 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
+ 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,
+ 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,
+ 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
+ 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,
+ 0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,
+ 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
+ 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2,
+};
+
+void
+_sha2block64(uchar *p, ulong len, u32int *s)
+{+ u32int w[16], a, b, c, d, e, f, g, h;
+ uchar *end;
+
+ /* at this point, we have a multiple of 64 bytes */
+ for(end = p+len; p < end;){+ a = s[0];
+ b = s[1];
+ c = s[2];
+ d = s[3];
+ e = s[4];
+ f = s[5];
+ g = s[6];
+ h = s[7];
+
+#define STEP(a,b,c,d,e,f,g,h,i) \
+ if(i < 16) {\+ w[i] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3]; \
+ p += 4; \
+ } else { \+ w[i&15] += sigma1(w[(i-2)&15]) + w[(i-7)&15] + sigma0(w[(i-15)&15]); \
+ } \
+ h += SIGMA1(e) + Ch(e,f,g) + K256[i] + w[i&15]; \
+ d += h; \
+ h += SIGMA0(a) + Maj(a,b,c);
+
+ STEP(a,b,c,d,e,f,g,h,0);
+ STEP(h,a,b,c,d,e,f,g,1);
+ STEP(g,h,a,b,c,d,e,f,2);
+ STEP(f,g,h,a,b,c,d,e,3);
+ STEP(e,f,g,h,a,b,c,d,4);
+ STEP(d,e,f,g,h,a,b,c,5);
+ STEP(c,d,e,f,g,h,a,b,6);
+ STEP(b,c,d,e,f,g,h,a,7);
+
+ STEP(a,b,c,d,e,f,g,h,8);
+ STEP(h,a,b,c,d,e,f,g,9);
+ STEP(g,h,a,b,c,d,e,f,10);
+ STEP(f,g,h,a,b,c,d,e,11);
+ STEP(e,f,g,h,a,b,c,d,12);
+ STEP(d,e,f,g,h,a,b,c,13);
+ STEP(c,d,e,f,g,h,a,b,14);
+ STEP(b,c,d,e,f,g,h,a,15);
+
+ STEP(a,b,c,d,e,f,g,h,16);
+ STEP(h,a,b,c,d,e,f,g,17);
+ STEP(g,h,a,b,c,d,e,f,18);
+ STEP(f,g,h,a,b,c,d,e,19);
+ STEP(e,f,g,h,a,b,c,d,20);
+ STEP(d,e,f,g,h,a,b,c,21);
+ STEP(c,d,e,f,g,h,a,b,22);
+ STEP(b,c,d,e,f,g,h,a,23);
+
+ STEP(a,b,c,d,e,f,g,h,24);
+ STEP(h,a,b,c,d,e,f,g,25);
+ STEP(g,h,a,b,c,d,e,f,26);
+ STEP(f,g,h,a,b,c,d,e,27);
+ STEP(e,f,g,h,a,b,c,d,28);
+ STEP(d,e,f,g,h,a,b,c,29);
+ STEP(c,d,e,f,g,h,a,b,30);
+ STEP(b,c,d,e,f,g,h,a,31);
+
+ STEP(a,b,c,d,e,f,g,h,32);
+ STEP(h,a,b,c,d,e,f,g,33);
+ STEP(g,h,a,b,c,d,e,f,34);
+ STEP(f,g,h,a,b,c,d,e,35);
+ STEP(e,f,g,h,a,b,c,d,36);
+ STEP(d,e,f,g,h,a,b,c,37);
+ STEP(c,d,e,f,g,h,a,b,38);
+ STEP(b,c,d,e,f,g,h,a,39);
+
+ STEP(a,b,c,d,e,f,g,h,40);
+ STEP(h,a,b,c,d,e,f,g,41);
+ STEP(g,h,a,b,c,d,e,f,42);
+ STEP(f,g,h,a,b,c,d,e,43);
+ STEP(e,f,g,h,a,b,c,d,44);
+ STEP(d,e,f,g,h,a,b,c,45);
+ STEP(c,d,e,f,g,h,a,b,46);
+ STEP(b,c,d,e,f,g,h,a,47);
+
+ STEP(a,b,c,d,e,f,g,h,48);
+ STEP(h,a,b,c,d,e,f,g,49);
+ STEP(g,h,a,b,c,d,e,f,50);
+ STEP(f,g,h,a,b,c,d,e,51);
+ STEP(e,f,g,h,a,b,c,d,52);
+ STEP(d,e,f,g,h,a,b,c,53);
+ STEP(c,d,e,f,g,h,a,b,54);
+ STEP(b,c,d,e,f,g,h,a,55);
+
+ STEP(a,b,c,d,e,f,g,h,56);
+ STEP(h,a,b,c,d,e,f,g,57);
+ STEP(g,h,a,b,c,d,e,f,58);
+ STEP(f,g,h,a,b,c,d,e,59);
+ STEP(e,f,g,h,a,b,c,d,60);
+ STEP(d,e,f,g,h,a,b,c,61);
+ STEP(c,d,e,f,g,h,a,b,62);
+ STEP(b,c,d,e,f,g,h,a,63);
+
+ s[0] += a;
+ s[1] += b;
+ s[2] += c;
+ s[3] += d;
+ s[4] += e;
+ s[5] += f;
+ s[6] += g;
+ s[7] += h;
+ }
+}
--- /dev/null
+++ b/libsec/thumb.c
@@ -1,0 +1,170 @@
+#include "os.h"
+#include <bio.h>
+#include <libsec.h>
+
+enum{ ThumbTab = 1<<10 };+
+static Thumbprint*
+tablehead(uchar *hash, Thumbprint *table)
+{+ return &table[((hash[0]<<8) + hash[1]) & (ThumbTab-1)];
+}
+
+void
+freeThumbprints(Thumbprint *table)
+{+ Thumbprint *hd, *p, *q;
+
+ if(table == nil)
+ return;
+ for(hd = table; hd < table+ThumbTab; hd++){+ for(p = hd->next; p && p != hd; p = q){+ q = p->next;
+ free(p);
+ }
+ }
+ free(table);
+}
+
+int
+okThumbprint(uchar *hash, int len, Thumbprint *table)
+{+ Thumbprint *hd, *p;
+
+ if(table == nil)
+ return 0;
+ hd = tablehead(hash, table);
+ for(p = hd->next; p; p = p->next){+ if(p->len == len && memcmp(hash, p->hash, len) == 0)
+ return 1;
+ if(p == hd)
+ break;
+ }
+ return 0;
+}
+
+int
+okCertificate(uchar *cert, int len, Thumbprint *table)
+{+ uchar hash[SHA2_256dlen];
+ char thumb[2*SHA2_256dlen+1];
+
+ if(table == nil){+ werrstr("no thumbprints provided");+ return 0;
+ }
+ if(cert == nil || len <= 0){+ werrstr("no certificate provided");+ return 0;
+ }
+
+ sha1(cert, len, hash, nil);
+ if(okThumbprint(hash, SHA1dlen, table))
+ return 1;
+
+ sha2_256(cert, len, hash, nil);
+ if(okThumbprint(hash, SHA2_256dlen, table))
+ return 1;
+
+ if(X509digestSPKI(cert, len, sha2_256, hash) < 0)
+ return 0;
+ if(okThumbprint(hash, SHA2_256dlen, table))
+ return 1;
+
+ len = enc64(thumb, sizeof(thumb), hash, SHA2_256dlen);
+ while(len > 0 && thumb[len-1] == '=')
+ len--;
+ thumb[len] = '\0';
+ werrstr("sha256=%s", thumb);+
+ return 0;
+}
+
+static int
+loadThumbprints(char *file, char *tag, Thumbprint *table, Thumbprint *crltab, int depth)
+{+ Thumbprint *hd, *entry;
+ char *line, *field[50];
+ uchar hash[SHA2_256dlen];
+ Biobuf *bin;
+ int len, n;
+
+ if(depth > 8){+ werrstr("too many includes, last file %s", file);+ return -1;
+ }
+ if(access(file, AEXIST) < 0)
+ return 0; /* not an error */
+ if((bin = Bopen(file, OREAD)) == nil)
+ return -1;
+ for(; (line = Brdstr(bin, '\n', 1)) != nil; free(line)){+ if(tokenize(line, field, nelem(field)) < 2)
+ continue;
+ if(strcmp(field[0], "#include") == 0){+ if(loadThumbprints(field[1], tag, table, crltab, depth+1) < 0)
+ goto err;
+ continue;
+ }
+ if(strcmp(field[0], tag) != 0)
+ continue;
+ if(strncmp(field[1], "sha1=", 5) == 0){+ field[1] += 5;
+ len = SHA1dlen;
+ } else if(strncmp(field[1], "sha256=", 7) == 0){+ field[1] += 7;
+ len = SHA2_256dlen;
+ } else {+ continue;
+ }
+ n = strlen(field[1]);
+ if((n != len*2 || dec16(hash, len, field[1], n) != len)
+ && dec64(hash, len, field[1], n) != len){+ werrstr("malformed %s entry in %s: %s", tag, file, field[1]);+ goto err;
+ }
+ if(crltab && okThumbprint(hash, len, crltab))
+ continue;
+ hd = tablehead(hash, table);
+ if(hd->next == nil)
+ entry = hd;
+ else {+ if((entry = malloc(sizeof(*entry))) == nil)
+ goto err;
+ entry->next = hd->next;
+ }
+ hd->next = entry;
+ entry->len = len;
+ memcpy(entry->hash, hash, len);
+ }
+ Bterm(bin);
+ return 0;
+err:
+ free(line);
+ Bterm(bin);
+ return -1;
+}
+
+Thumbprint *
+initThumbprints(char *ok, char *crl, char *tag)
+{+ Thumbprint *table, *crltab;
+
+ table = crltab = nil;
+ if(crl){+ if((crltab = malloc(ThumbTab * sizeof(*crltab))) == nil)
+ goto err;
+ memset(crltab, 0, ThumbTab * sizeof(*crltab));
+ if(loadThumbprints(crl, tag, crltab, nil, 0) < 0)
+ goto err;
+ }
+ if((table = malloc(ThumbTab * sizeof(*table))) == nil)
+ goto err;
+ memset(table, 0, ThumbTab * sizeof(*table));
+ if(loadThumbprints(ok, tag, table, crltab, 0) < 0){+ freeThumbprints(table);
+ table = nil;
+ }
+err:
+ freeThumbprints(crltab);
+ return table;
+}
--- /dev/null
+++ b/libsec/tlshand.c
@@ -1,0 +1,3024 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+// The main groups of functions are:
+// client/server - main handshake protocol definition
+// message functions - formating handshake messages
+// cipher choices - catalog of digest and encrypt algorithms
+// security functions - PKCS#1, sslHMAC, session keygen
+// general utility functions - malloc, serialization
+// The handshake protocol builds on the TLS/SSL3 record layer protocol,
+// which is implemented in kernel device #a. See also /lib/rfc/rfc2246.
+
+enum {+ TLSFinishedLen = 12,
+ SSL3FinishedLen = MD5dlen+SHA1dlen,
+ MaxKeyData = 160, // amount of secret we may need
+ MAXdlen = SHA2_512dlen,
+ RandomSize = 32,
+ MasterSecretSize = 48,
+ AQueue = 0,
+ AFlush = 1,
+};
+
+typedef struct Bytes{+ int len;
+ uchar data[];
+} Bytes;
+
+typedef struct Ints{+ int len;
+ int data[];
+} Ints;
+
+typedef struct Algs{+ char *enc;
+ char *digest;
+ int nsecret;
+ int tlsid;
+ int ok;
+} Algs;
+
+typedef struct Namedcurve{+ int tlsid;
+ void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+} Namedcurve;
+
+typedef struct Finished{+ uchar verify[SSL3FinishedLen];
+ int n;
+} Finished;
+
+typedef struct HandshakeHash {+ MD5state md5;
+ SHAstate sha1;
+ SHA2_256state sha2_256;
+} HandshakeHash;
+
+typedef struct TlsSec TlsSec;
+struct TlsSec {+ RSApub *rsapub;
+ AuthRpc *rpc; // factotum for rsa private key
+ uchar *psk; // pre-shared key
+ int psklen;
+ int clientVers; // version in ClientHello
+ uchar sec[MasterSecretSize]; // master secret
+ uchar srandom[RandomSize]; // server random
+ uchar crandom[RandomSize]; // client random
+
+ Namedcurve *nc; // selected curve for ECDHE
+ // diffie hellman state
+ DHstate dh;
+ struct {+ ECdomain dom;
+ ECpriv Q;
+ } ec;
+ uchar X[32];
+
+ // byte generation and handshake checksum
+ void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int);
+ void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int);
+ int nfin;
+};
+
+typedef struct TlsConnection{+ TlsSec sec[1]; // security management goo
+ int hand, ctl; // record layer file descriptors
+ int erred; // set when tlsError called
+ int (*trace)(char*fmt, ...); // for debugging
+ int version; // protocol we are speaking
+ Bytes *cert; // server certificate; only last - no chain
+
+ int cipher;
+ int nsecret; // amount of secret data to init keys
+ char *digest; // name of digest algorithm to use
+ char *enc; // name of encryption algorithm to use
+
+ // for finished messages
+ HandshakeHash handhash;
+ Finished finished;
+
+ uchar *sendp;
+ uchar buf[1<<16];
+} TlsConnection;
+
+typedef struct Msg{+ int tag;
+ union {+ struct {+ int version;
+ uchar random[RandomSize];
+ Bytes* sid;
+ Ints* ciphers;
+ Bytes* compressors;
+ Bytes* extensions;
+ } clientHello;
+ struct {+ int version;
+ uchar random[RandomSize];
+ Bytes* sid;
+ int cipher;
+ int compressor;
+ Bytes* extensions;
+ } serverHello;
+ struct {+ int ncert;
+ Bytes **certs;
+ } certificate;
+ struct {+ Bytes *types;
+ Ints *sigalgs;
+ int nca;
+ Bytes **cas;
+ } certificateRequest;
+ struct {+ Bytes *pskid;
+ Bytes *key;
+ } clientKeyExchange;
+ struct {+ Bytes *pskid;
+ Bytes *dh_p;
+ Bytes *dh_g;
+ Bytes *dh_Ys;
+ Bytes *dh_parameters;
+ Bytes *dh_signature;
+ int sigalg;
+ int curve;
+ } serverKeyExchange;
+ struct {+ int sigalg;
+ Bytes *signature;
+ } certificateVerify;
+ Finished finished;
+ } u;
+} Msg;
+
+
+enum {+ SSL3Version = 0x0300,
+ TLS10Version = 0x0301,
+ TLS11Version = 0x0302,
+ TLS12Version = 0x0303,
+ ProtocolVersion = TLS12Version, // maximum version we speak
+ MinProtoVersion = 0x0300, // limits on version we accept
+ MaxProtoVersion = 0x03ff,
+};
+
+// handshake type
+enum {+ HHelloRequest,
+ HClientHello,
+ HServerHello,
+ HSSL2ClientHello = 9, /* local convention; see devtls.c */
+ HCertificate = 11,
+ HServerKeyExchange,
+ HCertificateRequest,
+ HServerHelloDone,
+ HCertificateVerify,
+ HClientKeyExchange,
+ HFinished = 20,
+ HMax
+};
+
+// alerts
+enum {+ ECloseNotify = 0,
+ EUnexpectedMessage = 10,
+ EBadRecordMac = 20,
+ EDecryptionFailed = 21,
+ ERecordOverflow = 22,
+ EDecompressionFailure = 30,
+ EHandshakeFailure = 40,
+ ENoCertificate = 41,
+ EBadCertificate = 42,
+ EUnsupportedCertificate = 43,
+ ECertificateRevoked = 44,
+ ECertificateExpired = 45,
+ ECertificateUnknown = 46,
+ EIllegalParameter = 47,
+ EUnknownCa = 48,
+ EAccessDenied = 49,
+ EDecodeError = 50,
+ EDecryptError = 51,
+ EExportRestriction = 60,
+ EProtocolVersion = 70,
+ EInsufficientSecurity = 71,
+ EInternalError = 80,
+ EInappropriateFallback = 86,
+ EUserCanceled = 90,
+ ENoRenegotiation = 100,
+ EUnknownPSKidentity = 115,
+ EMax = 256
+};
+
+// cipher suites
+enum {+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A,
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016,
+
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033,
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0X0039,
+ TLS_RSA_WITH_AES_128_CBC_SHA256 = 0X003C,
+ TLS_RSA_WITH_AES_256_CBC_SHA256 = 0X003D,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067,
+
+ TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
+
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
+
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
+
+ GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13,
+ GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14,
+ GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15,
+
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9,
+ TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA,
+
+ TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB,
+ TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE,
+ TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
+
+ TLS_FALLBACK_SCSV = 0x5600,
+};
+
+// compression methods
+enum {+ CompressionNull = 0,
+ CompressionMax
+};
+
+
+// curves
+enum {+ X25519 = 0x001d,
+};
+
+// extensions
+enum {+ Extsni = 0x0000,
+ Extec = 0x000a,
+ Extecp = 0x000b,
+ Extsigalgs = 0x000d,
+};
+
+static Algs cipherAlgs[] = {+ // ECDHE-ECDSA
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},+ {"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},+
+ // ECDHE-RSA
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},+ {"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305},+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},+ {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},+
+ // DHE-RSA
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_DHE_RSA_WITH_CHACHA20_POLY1305},+ {"ccpoly64_aead", "clear", 2*32, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305},+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA},+ {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_256_CBC_SHA},+ {"3des_ede_cbc","sha1", 2*(4*8+SHA1dlen), TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA},+
+ // RSA
+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_RSA_WITH_AES_128_GCM_SHA256},+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_RSA_WITH_AES_128_CBC_SHA256},+ {"aes_256_cbc", "sha256", 2*(32+16+SHA2_256dlen), TLS_RSA_WITH_AES_256_CBC_SHA256},+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA},+ {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA},+ {"3des_ede_cbc","sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA},+
+ // PSK
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_PSK_WITH_CHACHA20_POLY1305},+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_PSK_WITH_AES_128_CBC_SHA256},+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_PSK_WITH_AES_128_CBC_SHA},+};
+
+static uchar compressors[] = {+ CompressionNull,
+};
+
+static Namedcurve namedcurves[] = {+ X25519, nil,
+ 0x0017, secp256r1,
+ 0x0018, secp384r1,
+};
+
+static uchar pointformats[] = {+ CompressionNull /* support of uncompressed point format is mandatory */
+};
+
+static struct {+ DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*);
+ int len;
+} hashfun[] = {+/* [0x00] is reserved for MD5+SHA1 for < TLS1.2 */
+ [0x01] {md5, MD5dlen},+ [0x02] {sha1, SHA1dlen},+ [0x03] {sha2_224, SHA2_224dlen},+ [0x04] {sha2_256, SHA2_256dlen},+ [0x05] {sha2_384, SHA2_384dlen},+ [0x06] {sha2_512, SHA2_512dlen},+};
+
+// signature algorithms (only RSA and ECDSA at the moment)
+static int sigalgs[] = {+ 0x0603, /* SHA512 ECDSA */
+ 0x0503, /* SHA384 ECDSA */
+ 0x0403, /* SHA256 ECDSA */
+ 0x0203, /* SHA1 ECDSA */
+
+ 0x0601, /* SHA512 RSA */
+ 0x0501, /* SHA384 RSA */
+ 0x0401, /* SHA256 RSA */
+ 0x0201, /* SHA1 RSA */
+};
+
+static TlsConnection *tlsServer2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ int (*trace)(char*fmt, ...), PEMChain *chain);
+static TlsConnection *tlsClient2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ uchar *ext, int extlen, int (*trace)(char*fmt, ...));
+static void msgClear(Msg *m);
+static char* msgPrint(char *buf, int n, Msg *m);
+static int msgRecv(TlsConnection *c, Msg *m);
+static int msgSend(TlsConnection *c, Msg *m, int act);
+static void tlsError(TlsConnection *c, int err, char *msg, ...);
+static int setVersion(TlsConnection *c, int version);
+static int setSecrets(TlsConnection *c, int isclient);
+static int finishedMatch(TlsConnection *c, Finished *f);
+static void tlsConnectionFree(TlsConnection *c);
+
+static int isDHE(int tlsid);
+static int isECDHE(int tlsid);
+static int isPSK(int tlsid);
+static int isECDSA(int tlsid);
+
+static int setAlgs(TlsConnection *c, int a);
+static int okCipher(Ints *cv, int ispsk, int canec);
+static int okCompression(Bytes *cv);
+static int initCiphers(void);
+static Ints* makeciphers(int ispsk);
+
+static AuthRpc* factotum_rsa_open(RSApub *rsapub);
+static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
+static void factotum_rsa_close(AuthRpc *rpc);
+
+static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom);
+static int tlsSecRSAs(TlsSec *sec, Bytes *epm);
+static Bytes* tlsSecECDHEs1(TlsSec *sec);
+static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc);
+static void tlsSecInitc(TlsSec *sec, int cvers);
+static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert);
+static Bytes* tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys);
+static Bytes* tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys);
+static void tlsSecVers(TlsSec *sec, int v);
+static int tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient);
+static void setMasterSecret(TlsSec *sec, Bytes *pm);
+static int digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg);
+static char* verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg);
+
+static Bytes* pkcs1_encrypt(Bytes* data, RSApub* key);
+static Bytes* pkcs1_decrypt(TlsSec *sec, Bytes *data);
+static Bytes* pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg);
+
+static void* emalloc(int);
+static void* erealloc(void*, int);
+static void put32(uchar *p, u32int);
+static void put24(uchar *p, int);
+static void put16(uchar *p, int);
+static int get24(uchar *p);
+static int get16(uchar *p);
+static Bytes* newbytes(int len);
+static Bytes* makebytes(uchar* buf, int len);
+static Bytes* mptobytes(mpint* big, int len);
+static mpint* bytestomp(Bytes* bytes);
+static void freebytes(Bytes* b);
+static Ints* newints(int len);
+static void freeints(Ints* b);
+static int lookupid(Ints* b, int id);
+
+/* x509.c */
+extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+extern int pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype);
+extern int asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len);
+
+//================= client/server ========================
+
+// push TLS onto fd, returning new (application) file descriptor
+// or -1 if error.
+int
+tlsServer(int fd, TLSconn *conn)
+{+ char buf[8];
+ char dname[64];
+ uchar seed[2*RandomSize];
+ int n, data, ctl, hand;
+ TlsConnection *tls;
+
+ if(conn == nil)
+ return -1;
+ ctl = open("#a/tls/clone", ORDWR|OCEXEC);+ if(ctl < 0)
+ return -1;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0){+ close(ctl);
+ return -1;
+ }
+ buf[n] = 0;
+ snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
+ snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
+ hand = open(dname, ORDWR|OCEXEC);
+ if(hand < 0){+ close(ctl);
+ return -1;
+ }
+ data = -1;
+ fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
+ tls = tlsServer2(ctl, hand,
+ conn->cert, conn->certlen,
+ conn->pskID, conn->psk, conn->psklen,
+ conn->trace, conn->chain);
+ if(tls != nil){+ snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
+ data = open(dname, ORDWR);
+ }
+ close(hand);
+ close(ctl);
+ if(data < 0){+ tlsConnectionFree(tls);
+ return -1;
+ }
+ free(conn->cert);
+ conn->cert = nil; // client certificates are not yet implemented
+ conn->certlen = 0;
+ conn->sessionIDlen = 0;
+ conn->sessionID = nil;
+ if(conn->sessionKey != nil
+ && conn->sessionType != nil
+ && strcmp(conn->sessionType, "ttls") == 0){+ memmove(seed, tls->sec->crandom, RandomSize);
+ memmove(seed+RandomSize, tls->sec->srandom, RandomSize);
+ tls->sec->prf(
+ conn->sessionKey, conn->sessionKeylen,
+ tls->sec->sec, MasterSecretSize,
+ conn->sessionConst,
+ seed, sizeof(seed));
+ }
+ tlsConnectionFree(tls);
+ close(fd);
+ return data;
+}
+
+static uchar*
+tlsClientExtensions(TLSconn *conn, int *plen)
+{+ uchar *b, *p;
+ int i, n, m;
+
+ p = b = nil;
+
+ // RFC6066 - Server Name Identification
+ if(conn->serverName != nil && (n = strlen(conn->serverName)) > 0){+ m = p - b;
+ b = erealloc(b, m + 2+2+2+1+2+n);
+ p = b + m;
+
+ put16(p, Extsni), p += 2; /* Type: server_name */
+ put16(p, 2+1+2+n), p += 2; /* Length */
+ put16(p, 1+2+n), p += 2; /* Server Name list length */
+ *p++ = 0; /* Server Name Type: host_name */
+ put16(p, n), p += 2; /* Server Name length */
+ memmove(p, conn->serverName, n);
+ p += n;
+ }
+
+ // Elliptic Curves (also called Supported Groups)
+ if(ProtocolVersion >= TLS10Version){+ m = p - b;
+ b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats));
+ p = b + m;
+
+ n = nelem(namedcurves);
+ put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */
+ put16(p, (n+1)*2), p += 2; /* Length */
+ put16(p, n*2), p += 2; /* Elliptic Curves Length */
+ for(i=0; i < n; i++){ /* Elliptic Curves */+ put16(p, namedcurves[i].tlsid);
+ p += 2;
+ }
+
+ n = nelem(pointformats);
+ put16(p, Extecp), p += 2; /* Type: ec_point_formats */
+ put16(p, n+1), p += 2; /* Length */
+ *p++ = n; /* EC point formats Length */
+ for(i=0; i < n; i++) /* EC point formats */
+ *p++ = pointformats[i];
+ }
+
+ // signature algorithms
+ if(ProtocolVersion >= TLS12Version){+ n = nelem(sigalgs);
+
+ m = p - b;
+ b = erealloc(b, m + 2+2+2+n*2);
+ p = b + m;
+
+ put16(p, Extsigalgs), p += 2;
+ put16(p, n*2 + 2), p += 2;
+ put16(p, n*2), p += 2;
+ for(i=0; i < n; i++){+ put16(p, sigalgs[i]);
+ p += 2;
+ }
+ }
+
+ *plen = p - b;
+ return b;
+}
+
+// push TLS onto fd, returning new (application) file descriptor
+// or -1 if error.
+int
+tlsClient(int fd, TLSconn *conn)
+{+ char buf[8];
+ char dname[64];
+ uchar seed[2*RandomSize];
+ int n, data, ctl, hand;
+ TlsConnection *tls;
+ uchar *ext;
+
+ if(conn == nil)
+ return -1;
+ ctl = open("#a/tls/clone", ORDWR|OCEXEC);+ if(ctl < 0)
+ return -1;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0){+ close(ctl);
+ return -1;
+ }
+ buf[n] = 0;
+ snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
+ snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
+ hand = open(dname, ORDWR|OCEXEC);
+ if(hand < 0){+ close(ctl);
+ return -1;
+ }
+ snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
+ data = open(dname, ORDWR);
+ if(data < 0){+ close(hand);
+ close(ctl);
+ return -1;
+ }
+ fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
+ ext = tlsClientExtensions(conn, &n);
+ tls = tlsClient2(ctl, hand,
+ conn->cert, conn->certlen,
+ conn->pskID, conn->psk, conn->psklen,
+ ext, n, conn->trace);
+ free(ext);
+ close(hand);
+ close(ctl);
+ if(tls == nil){+ close(data);
+ return -1;
+ }
+ free(conn->cert);
+ if(tls->cert != nil){+ conn->certlen = tls->cert->len;
+ conn->cert = emalloc(conn->certlen);
+ memcpy(conn->cert, tls->cert->data, conn->certlen);
+ } else {+ conn->certlen = 0;
+ conn->cert = nil;
+ }
+ conn->sessionIDlen = 0;
+ conn->sessionID = nil;
+ if(conn->sessionKey != nil
+ && conn->sessionType != nil
+ && strcmp(conn->sessionType, "ttls") == 0){+ memmove(seed, tls->sec->crandom, RandomSize);
+ memmove(seed+RandomSize, tls->sec->srandom, RandomSize);
+ tls->sec->prf(
+ conn->sessionKey, conn->sessionKeylen,
+ tls->sec->sec, MasterSecretSize,
+ conn->sessionConst,
+ seed, sizeof(seed));
+ }
+ tlsConnectionFree(tls);
+ close(fd);
+ return data;
+}
+
+static int
+countchain(PEMChain *p)
+{+ int i = 0;
+
+ while (p) {+ i++;
+ p = p->next;
+ }
+ return i;
+}
+
+static int
+checkClientExtensions(TlsConnection *c, Bytes *ext)
+{+ uchar *p, *e;
+ int i, j, n;
+
+ if(ext == nil)
+ return 0;
+
+ for(p = ext->data, e = p+ext->len; p < e; p += n){+ if(e-p < 4)
+ goto Short;
+ p += 4;
+ if(e-p < (n = get16(p-2)))
+ goto Short;
+ switch(get16(p-4)){+ case Extec:
+ if(n < 4 || n % 2 || get16(p) != (n -= 2))
+ goto Short;
+ p += 2;
+ for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++)
+ for(j = 0; j < n; j += 2)
+ if(namedcurves[i].tlsid == get16(p+j)){+ c->sec->nc = &namedcurves[i];
+ break;
+ }
+ break;
+ }
+ }
+
+ return 0;
+Short:
+ tlsError(c, EDecodeError, "clienthello extensions has invalid length");
+ return -1;
+}
+
+static TlsConnection *
+tlsServer2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ int (*trace)(char*fmt, ...), PEMChain *chp)
+{+ int cipher, compressor, numcerts, i;
+ TlsConnection *c;
+ Msg m;
+
+ if(trace)
+ trace("tlsServer2\n");+ if(!initCiphers())
+ return nil;
+
+ c = emalloc(sizeof(TlsConnection));
+ c->ctl = ctl;
+ c->hand = hand;
+ c->trace = trace;
+ c->version = ProtocolVersion;
+ c->sendp = c->buf;
+
+ memset(&m, 0, sizeof(m));
+ if(!msgRecv(c, &m)){+ if(trace)
+ trace("initial msgRecv failed\n");+ goto Err;
+ }
+ if(m.tag != HClientHello) {+ tlsError(c, EUnexpectedMessage, "expected a client hello");
+ goto Err;
+ }
+ if(trace)
+ trace("ClientHello version %x\n", m.u.clientHello.version);+ if(setVersion(c, m.u.clientHello.version) < 0) {+ tlsError(c, EIllegalParameter, "incompatible version");
+ goto Err;
+ }
+ if(c->version < ProtocolVersion
+ && lookupid(m.u.clientHello.ciphers, TLS_FALLBACK_SCSV) >= 0){+ tlsError(c, EInappropriateFallback, "inappropriate fallback");
+ goto Err;
+ }
+ tlsSecInits(c->sec, m.u.clientHello.version, m.u.clientHello.random);
+ tlsSecVers(c->sec, c->version);
+ if(psklen > 0){+ c->sec->psk = psk;
+ c->sec->psklen = psklen;
+ }
+ if(certlen > 0){+ /* server certificate */
+ c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+ if(c->sec->rsapub == nil){+ tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate");
+ goto Err;
+ }
+ c->sec->rpc = factotum_rsa_open(c->sec->rsapub);
+ if(c->sec->rpc == nil){+ tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
+ goto Err;
+ }
+ }
+ if(checkClientExtensions(c, m.u.clientHello.extensions) < 0)
+ goto Err;
+ cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil);
+ if(cipher < 0 || !setAlgs(c, cipher)) {+ tlsError(c, EHandshakeFailure, "no matching cipher suite");
+ goto Err;
+ }
+ compressor = okCompression(m.u.clientHello.compressors);
+ if(compressor < 0) {+ tlsError(c, EHandshakeFailure, "no matching compressor");
+ goto Err;
+ }
+ if(trace)
+ trace(" cipher %x, compressor %x\n", cipher, compressor);+ msgClear(&m);
+
+ m.tag = HServerHello;
+ m.u.serverHello.version = c->version;
+ memmove(m.u.serverHello.random, c->sec->srandom, RandomSize);
+ m.u.serverHello.cipher = cipher;
+ m.u.serverHello.compressor = compressor;
+ m.u.serverHello.sid = makebytes(nil, 0);
+ if(!msgSend(c, &m, AQueue))
+ goto Err;
+
+ if(certlen > 0){+ m.tag = HCertificate;
+ numcerts = countchain(chp);
+ m.u.certificate.ncert = 1 + numcerts;
+ m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
+ m.u.certificate.certs[0] = makebytes(cert, certlen);
+ for (i = 0; i < numcerts && chp; i++, chp = chp->next)
+ m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen);
+ if(!msgSend(c, &m, AQueue))
+ goto Err;
+ }
+
+ if(isECDHE(cipher)){+ m.tag = HServerKeyExchange;
+ m.u.serverKeyExchange.curve = c->sec->nc->tlsid;
+ m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec);
+ if(m.u.serverKeyExchange.dh_parameters == nil){+ tlsError(c, EInternalError, "can't set DH parameters");
+ goto Err;
+ }
+
+ /* sign the DH parameters */
+ if(certlen > 0){+ uchar digest[MAXdlen];
+ int digestlen;
+
+ if(c->version >= TLS12Version)
+ m.u.serverKeyExchange.sigalg = 0x0401; /* RSA SHA256 */
+ digestlen = digestDHparams(c->sec, m.u.serverKeyExchange.dh_parameters,
+ digest, m.u.serverKeyExchange.sigalg);
+ if((m.u.serverKeyExchange.dh_signature = pkcs1_sign(c->sec, digest, digestlen,
+ m.u.serverKeyExchange.sigalg)) == nil){+ tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
+ goto Err;
+ }
+ }
+ if(!msgSend(c, &m, AQueue))
+ goto Err;
+ }
+
+ m.tag = HServerHelloDone;
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag != HClientKeyExchange) {+ tlsError(c, EUnexpectedMessage, "expected a client key exchange");
+ goto Err;
+ }
+ if(pskid != nil){+ if(m.u.clientKeyExchange.pskid == nil
+ || m.u.clientKeyExchange.pskid->len != strlen(pskid)
+ || memcmp(pskid, m.u.clientKeyExchange.pskid->data, m.u.clientKeyExchange.pskid->len) != 0){+ tlsError(c, EUnknownPSKidentity, "unknown or missing pskid");
+ goto Err;
+ }
+ }
+ if(isECDHE(cipher)){+ if(tlsSecECDHEs2(c->sec, m.u.clientKeyExchange.key) < 0){+ tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
+ goto Err;
+ }
+ } else if(certlen > 0){+ if(tlsSecRSAs(c->sec, m.u.clientKeyExchange.key) < 0){+ tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
+ goto Err;
+ }
+ } else if(psklen > 0){+ setMasterSecret(c->sec, newbytes(psklen));
+ } else {+ tlsError(c, EInternalError, "no psk or certificate");
+ goto Err;
+ }
+
+ if(trace)
+ trace("tls secrets\n");+ if(setSecrets(c, 0) < 0){+ tlsError(c, EHandshakeFailure, "can't set secrets: %r");
+ goto Err;
+ }
+
+ /* no CertificateVerify; skip to Finished */
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){+ tlsError(c, EInternalError, "can't set finished: %r");
+ goto Err;
+ }
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag != HFinished) {+ tlsError(c, EUnexpectedMessage, "expected a finished");
+ goto Err;
+ }
+ if(!finishedMatch(c, &m.u.finished)) {+ tlsError(c, EHandshakeFailure, "finished verification failed");
+ goto Err;
+ }
+ msgClear(&m);
+
+ /* change cipher spec */
+ if(fprint(c->ctl, "changecipher") < 0){+ tlsError(c, EInternalError, "can't enable cipher: %r");
+ goto Err;
+ }
+
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){+ tlsError(c, EInternalError, "can't set finished: %r");
+ goto Err;
+ }
+ m.tag = HFinished;
+ m.u.finished = c->finished;
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+ if(trace)
+ trace("tls finished\n");+
+ if(fprint(c->ctl, "opened") < 0)
+ goto Err;
+ return c;
+
+Err:
+ msgClear(&m);
+ tlsConnectionFree(c);
+ return nil;
+}
+
+static Bytes*
+tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys)
+{+ DHstate *dh = &sec->dh;
+ mpint *G, *P, *Y, *K;
+ Bytes *Yc;
+ int n;
+
+ if(p == nil || g == nil || Ys == nil)
+ return nil;
+ // reject dh primes that is susceptible to logjam
+ if(p->len <= 1024/8)
+ return nil;
+ Yc = nil;
+ P = bytestomp(p);
+ G = bytestomp(g);
+ Y = bytestomp(Ys);
+ K = nil;
+
+ if(dh_new(dh, P, nil, G) == nil)
+ goto Out;
+ n = (mpsignif(P)+7)/8;
+ Yc = mptobytes(dh->y, n);
+ K = dh_finish(dh, Y); /* zeros dh */
+ if(K == nil){+ freebytes(Yc);
+ Yc = nil;
+ goto Out;
+ }
+ setMasterSecret(sec, mptobytes(K, n));
+
+Out:
+ mpfree(K);
+ mpfree(Y);
+ mpfree(G);
+ mpfree(P);
+
+ return Yc;
+}
+
+static Bytes*
+tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys)
+{+ ECdomain *dom = &sec->ec.dom;
+ ECpriv *Q = &sec->ec.Q;
+ ECpub *pub;
+ ECpoint K;
+ Namedcurve *nc;
+ Bytes *Yc;
+ Bytes *Z;
+ int n;
+
+ if(Ys == nil)
+ return nil;
+
+ if(curve == X25519){+ if(Ys->len != 32)
+ return nil;
+ Yc = newbytes(32);
+ curve25519_dh_new(sec->X, Yc->data);
+ Z = newbytes(32);
+ if(!curve25519_dh_finish(sec->X, Ys->data, Z->data)){+ freebytes(Yc);
+ freebytes(Z);
+ return nil;
+ }
+ setMasterSecret(sec, Z);
+ }else{+ for(nc = namedcurves; nc->tlsid != curve; nc++)
+ if(nc == &namedcurves[nelem(namedcurves)])
+ return nil;
+ ecdominit(dom, nc->init);
+ pub = ecdecodepub(dom, Ys->data, Ys->len);
+ if(pub == nil)
+ return nil;
+
+ memset(Q, 0, sizeof(*Q));
+ Q->a.x = mpnew(0);
+ Q->a.y = mpnew(0);
+ Q->d = mpnew(0);
+
+ memset(&K, 0, sizeof(K));
+ K.x = mpnew(0);
+ K.y = mpnew(0);
+
+ ecgen(dom, Q);
+ ecmul(dom, pub, Q->d, &K);
+
+ n = (mpsignif(dom->p)+7)/8;
+ setMasterSecret(sec, mptobytes(K.x, n));
+ Yc = newbytes(1 + 2*n);
+ Yc->len = ecencodepub(dom, &Q->a, Yc->data, Yc->len);
+
+ mpfree(K.x);
+ mpfree(K.y);
+
+ ecpubfree(pub);
+ }
+ return Yc;
+}
+
+static TlsConnection *
+tlsClient2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ uchar *ext, int extlen,
+ int (*trace)(char*fmt, ...))
+{+ int creq, dhx, cipher;
+ TlsConnection *c;
+ Bytes *epm;
+ Msg m;
+
+ if(!initCiphers())
+ return nil;
+
+ epm = nil;
+ memset(&m, 0, sizeof(m));
+ c = emalloc(sizeof(TlsConnection));
+
+ c->ctl = ctl;
+ c->hand = hand;
+ c->trace = trace;
+ c->cert = nil;
+ c->sendp = c->buf;
+
+ c->version = ProtocolVersion;
+ tlsSecInitc(c->sec, c->version);
+ if(psklen > 0){+ c->sec->psk = psk;
+ c->sec->psklen = psklen;
+ }
+ if(certlen > 0){+ /* client certificate */
+ c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+ if(c->sec->rsapub == nil){+ tlsError(c, EInternalError, "invalid X509/rsa certificate");
+ goto Err;
+ }
+ c->sec->rpc = factotum_rsa_open(c->sec->rsapub);
+ if(c->sec->rpc == nil){+ tlsError(c, EInternalError, "factotum_rsa_open: %r");
+ goto Err;
+ }
+ }
+
+ /* client hello */
+ m.tag = HClientHello;
+ m.u.clientHello.version = c->version;
+ memmove(m.u.clientHello.random, c->sec->crandom, RandomSize);
+ m.u.clientHello.sid = makebytes(nil, 0);
+ m.u.clientHello.ciphers = makeciphers(psklen > 0);
+ m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors));
+ m.u.clientHello.extensions = makebytes(ext, extlen);
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+
+ /* server hello */
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag != HServerHello) {+ tlsError(c, EUnexpectedMessage, "expected a server hello");
+ goto Err;
+ }
+ if(setVersion(c, m.u.serverHello.version) < 0) {+ tlsError(c, EIllegalParameter, "incompatible version: %r");
+ goto Err;
+ }
+ tlsSecVers(c->sec, c->version);
+ memmove(c->sec->srandom, m.u.serverHello.random, RandomSize);
+
+ cipher = m.u.serverHello.cipher;
+ if((psklen > 0) != isPSK(cipher) || !setAlgs(c, cipher)) {+ tlsError(c, EIllegalParameter, "invalid cipher suite");
+ goto Err;
+ }
+ if(m.u.serverHello.compressor != CompressionNull) {+ tlsError(c, EIllegalParameter, "invalid compression");
+ goto Err;
+ }
+ dhx = isDHE(cipher) || isECDHE(cipher);
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag == HCertificate){+ if(m.u.certificate.ncert < 1) {+ tlsError(c, EIllegalParameter, "runt certificate");
+ goto Err;
+ }
+ c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len);
+ if(!msgRecv(c, &m))
+ goto Err;
+ } else if(psklen == 0) {+ tlsError(c, EUnexpectedMessage, "expected a certificate");
+ goto Err;
+ }
+ if(m.tag == HServerKeyExchange) {+ if(dhx){+ char *err = verifyDHparams(c->sec,
+ m.u.serverKeyExchange.dh_parameters,
+ c->cert,
+ m.u.serverKeyExchange.dh_signature,
+ c->version<TLS12Version ? 0x01 : m.u.serverKeyExchange.sigalg);
+ if(err != nil){+ tlsError(c, EBadCertificate, "can't verify DH parameters: %s", err);
+ goto Err;
+ }
+ if(isECDHE(cipher))
+ epm = tlsSecECDHEc(c->sec,
+ m.u.serverKeyExchange.curve,
+ m.u.serverKeyExchange.dh_Ys);
+ else
+ epm = tlsSecDHEc(c->sec,
+ m.u.serverKeyExchange.dh_p,
+ m.u.serverKeyExchange.dh_g,
+ m.u.serverKeyExchange.dh_Ys);
+ if(epm == nil){+ tlsError(c, EHandshakeFailure, "bad DH parameters");
+ goto Err;
+ }
+ } else if(psklen == 0){+ tlsError(c, EUnexpectedMessage, "got an server key exchange");
+ goto Err;
+ }
+ if(!msgRecv(c, &m))
+ goto Err;
+ } else if(dhx){+ tlsError(c, EUnexpectedMessage, "expected server key exchange");
+ goto Err;
+ }
+
+ /* certificate request (optional) */
+ creq = 0;
+ if(m.tag == HCertificateRequest) {+ creq = 1;
+ if(!msgRecv(c, &m))
+ goto Err;
+ }
+
+ if(m.tag != HServerHelloDone) {+ tlsError(c, EUnexpectedMessage, "expected a server hello done");
+ goto Err;
+ }
+ msgClear(&m);
+
+ if(!dhx){+ if(c->cert != nil){+ epm = tlsSecRSAc(c->sec, c->cert->data, c->cert->len);
+ if(epm == nil){+ tlsError(c, EBadCertificate, "bad certificate: %r");
+ goto Err;
+ }
+ } else if(psklen > 0){+ setMasterSecret(c->sec, newbytes(psklen));
+ } else {+ tlsError(c, EInternalError, "no psk or certificate");
+ goto Err;
+ }
+ }
+
+ if(trace)
+ trace("tls secrets\n");+ if(setSecrets(c, 1) < 0){+ tlsError(c, EHandshakeFailure, "can't set secrets: %r");
+ goto Err;
+ }
+
+ if(creq) {+ m.tag = HCertificate;
+ if(certlen > 0){+ m.u.certificate.ncert = 1;
+ m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
+ m.u.certificate.certs[0] = makebytes(cert, certlen);
+ }
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+ }
+
+ /* client key exchange */
+ m.tag = HClientKeyExchange;
+ if(psklen > 0){+ if(pskid == nil)
+ pskid = "";
+ m.u.clientKeyExchange.pskid = makebytes((uchar*)pskid, strlen(pskid));
+ }
+ m.u.clientKeyExchange.key = epm;
+ epm = nil;
+
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+
+ /* certificate verify */
+ if(creq && certlen > 0) {+ HandshakeHash hsave;
+ uchar digest[MAXdlen];
+ int digestlen;
+
+ /* save the state for the Finish message */
+ hsave = c->handhash;
+ if(c->version < TLS12Version){+ md5(nil, 0, digest, &c->handhash.md5);
+ sha1(nil, 0, digest+MD5dlen, &c->handhash.sha1);
+ digestlen = MD5dlen+SHA1dlen;
+ } else {+ m.u.certificateVerify.sigalg = 0x0401; /* RSA SHA256 */
+ sha2_256(nil, 0, digest, &c->handhash.sha2_256);
+ digestlen = SHA2_256dlen;
+ }
+ c->handhash = hsave;
+
+ if((m.u.certificateVerify.signature = pkcs1_sign(c->sec, digest, digestlen,
+ m.u.certificateVerify.sigalg)) == nil){+ tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
+ goto Err;
+ }
+
+ m.tag = HCertificateVerify;
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+ }
+
+ /* change cipher spec */
+ if(fprint(c->ctl, "changecipher") < 0){+ tlsError(c, EInternalError, "can't enable cipher: %r");
+ goto Err;
+ }
+
+ // Cipherchange must occur immediately before Finished to avoid
+ // potential hole; see section 4.3 of Wagner Schneier 1996.
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){+ tlsError(c, EInternalError, "can't set finished 1: %r");
+ goto Err;
+ }
+ m.tag = HFinished;
+ m.u.finished = c->finished;
+ if(!msgSend(c, &m, AFlush)) {+ tlsError(c, EInternalError, "can't flush after client Finished: %r");
+ goto Err;
+ }
+
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){+ tlsError(c, EInternalError, "can't set finished 0: %r");
+ goto Err;
+ }
+ if(!msgRecv(c, &m)) {+ tlsError(c, EInternalError, "can't read server Finished: %r");
+ goto Err;
+ }
+ if(m.tag != HFinished) {+ tlsError(c, EUnexpectedMessage, "expected a Finished msg from server");
+ goto Err;
+ }
+
+ if(!finishedMatch(c, &m.u.finished)) {+ tlsError(c, EHandshakeFailure, "finished verification failed");
+ goto Err;
+ }
+ msgClear(&m);
+
+ if(fprint(c->ctl, "opened") < 0){+ if(trace)
+ trace("unable to do final open: %r\n");+ goto Err;
+ }
+ return c;
+
+Err:
+ free(epm);
+ msgClear(&m);
+ tlsConnectionFree(c);
+ return nil;
+}
+
+
+//================= message functions ========================
+
+static void
+msgHash(TlsConnection *c, uchar *p, int n)
+{+ md5(p, n, 0, &c->handhash.md5);
+ sha1(p, n, 0, &c->handhash.sha1);
+ if(c->version >= TLS12Version)
+ sha2_256(p, n, 0, &c->handhash.sha2_256);
+}
+
+static int
+msgSend(TlsConnection *c, Msg *m, int act)
+{+ uchar *p, *e; // sendp = start of new message; p = write pointer; e = end pointer
+ int n, i;
+
+ p = c->sendp;
+ e = &c->buf[sizeof(c->buf)];
+ if(c->trace)
+ c->trace("send %s", msgPrint((char*)p, e - p, m));+
+ p[0] = m->tag; // header - fill in size later
+ p += 4;
+
+ switch(m->tag) {+ default:
+ tlsError(c, EInternalError, "can't encode a %d", m->tag);
+ goto Err;
+ case HClientHello:
+ if(p+2+RandomSize > e)
+ goto Overflow;
+ put16(p, m->u.clientHello.version), p += 2;
+ memmove(p, m->u.clientHello.random, RandomSize);
+ p += RandomSize;
+
+ if(p+1+(n = m->u.clientHello.sid->len) > e)
+ goto Overflow;
+ *p++ = n;
+ memmove(p, m->u.clientHello.sid->data, n);
+ p += n;
+
+ if(p+2+(n = m->u.clientHello.ciphers->len) > e)
+ goto Overflow;
+ put16(p, n*2), p += 2;
+ for(i=0; i<n; i++)
+ put16(p, m->u.clientHello.ciphers->data[i]), p += 2;
+
+ if(p+1+(n = m->u.clientHello.compressors->len) > e)
+ goto Overflow;
+ *p++ = n;
+ memmove(p, m->u.clientHello.compressors->data, n);
+ p += n;
+
+ if(m->u.clientHello.extensions == nil
+ || (n = m->u.clientHello.extensions->len) == 0)
+ break;
+ if(p+2+n > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.clientHello.extensions->data, n);
+ p += n;
+ break;
+ case HServerHello:
+ if(p+2+RandomSize > e)
+ goto Overflow;
+ put16(p, m->u.serverHello.version), p += 2;
+ memmove(p, m->u.serverHello.random, RandomSize);
+ p += RandomSize;
+
+ if(p+1+(n = m->u.serverHello.sid->len) > e)
+ goto Overflow;
+ *p++ = n;
+ memmove(p, m->u.serverHello.sid->data, n);
+ p += n;
+
+ if(p+2+1 > e)
+ goto Overflow;
+ put16(p, m->u.serverHello.cipher), p += 2;
+ *p++ = m->u.serverHello.compressor;
+
+ if(m->u.serverHello.extensions == nil
+ || (n = m->u.serverHello.extensions->len) == 0)
+ break;
+ if(p+2+n > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.serverHello.extensions->data, n);
+ p += n;
+ break;
+ case HServerHelloDone:
+ break;
+ case HCertificate:
+ n = 0;
+ for(i = 0; i < m->u.certificate.ncert; i++)
+ n += 3 + m->u.certificate.certs[i]->len;
+ if(p+3+n > e)
+ goto Overflow;
+ put24(p, n), p += 3;
+ for(i = 0; i < m->u.certificate.ncert; i++){+ n = m->u.certificate.certs[i]->len;
+ put24(p, n), p += 3;
+ memmove(p, m->u.certificate.certs[i]->data, n);
+ p += n;
+ }
+ break;
+ case HCertificateVerify:
+ if(p+2+2+(n = m->u.certificateVerify.signature->len) > e)
+ goto Overflow;
+ if(m->u.certificateVerify.sigalg != 0)
+ put16(p, m->u.certificateVerify.sigalg), p += 2;
+ put16(p, n), p += 2;
+ memmove(p, m->u.certificateVerify.signature->data, n);
+ p += n;
+ break;
+ case HServerKeyExchange:
+ if(m->u.serverKeyExchange.pskid != nil){+ if(p+2+(n = m->u.serverKeyExchange.pskid->len) > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.serverKeyExchange.pskid->data, n);
+ p += n;
+ }
+ if(m->u.serverKeyExchange.dh_parameters == nil)
+ break;
+ if(p+(n = m->u.serverKeyExchange.dh_parameters->len) > e)
+ goto Overflow;
+ memmove(p, m->u.serverKeyExchange.dh_parameters->data, n);
+ p += n;
+ if(m->u.serverKeyExchange.dh_signature == nil)
+ break;
+ if(p+2+2+(n = m->u.serverKeyExchange.dh_signature->len) > e)
+ goto Overflow;
+ if(c->version >= TLS12Version)
+ put16(p, m->u.serverKeyExchange.sigalg), p += 2;
+ put16(p, n), p += 2;
+ memmove(p, m->u.serverKeyExchange.dh_signature->data, n);
+ p += n;
+ break;
+ case HClientKeyExchange:
+ if(m->u.clientKeyExchange.pskid != nil){+ if(p+2+(n = m->u.clientKeyExchange.pskid->len) > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.clientKeyExchange.pskid->data, n);
+ p += n;
+ }
+ if(m->u.clientKeyExchange.key == nil)
+ break;
+ if(p+2+(n = m->u.clientKeyExchange.key->len) > e)
+ goto Overflow;
+ if(isECDHE(c->cipher))
+ *p++ = n;
+ else if(isDHE(c->cipher) || c->version != SSL3Version)
+ put16(p, n), p += 2;
+ memmove(p, m->u.clientKeyExchange.key->data, n);
+ p += n;
+ break;
+ case HFinished:
+ if(p+m->u.finished.n > e)
+ goto Overflow;
+ memmove(p, m->u.finished.verify, m->u.finished.n);
+ p += m->u.finished.n;
+ break;
+ }
+
+ // go back and fill in size
+ n = p - c->sendp;
+ put24(c->sendp+1, n-4);
+
+ // remember hash of Handshake messages
+ if(m->tag != HHelloRequest)
+ msgHash(c, c->sendp, n);
+
+ c->sendp = p;
+ if(act == AFlush){+ c->sendp = c->buf;
+ if(write(c->hand, c->buf, p - c->buf) < 0){+ fprint(2, "write error: %r\n");
+ goto Err;
+ }
+ }
+ msgClear(m);
+ return 1;
+Overflow:
+ tlsError(c, EInternalError, "not enougth send buffer for message (%d)", m->tag);
+Err:
+ msgClear(m);
+ return 0;
+}
+
+static uchar*
+tlsReadN(TlsConnection *c, int n)
+{+ uchar *p, *w, *e;
+
+ e = &c->buf[sizeof(c->buf)];
+ p = e - n;
+ if(n > sizeof(c->buf) || p < c->sendp){+ tlsError(c, EDecodeError, "handshake message too long %d", n);
+ return nil;
+ }
+ for(w = p; w < e; w += n)
+ if((n = read(c->hand, w, e - w)) <= 0)
+ return nil;
+ return p;
+}
+
+static int
+msgRecv(TlsConnection *c, Msg *m)
+{+ uchar *p, *s;
+ int type, n, nn, i;
+
+ msgClear(m);
+ for(;;) {+ p = tlsReadN(c, 4);
+ if(p == nil)
+ return 0;
+ type = p[0];
+ n = get24(p+1);
+
+ if(type != HHelloRequest)
+ break;
+ if(n != 0) {+ tlsError(c, EDecodeError, "invalid hello request during handshake");
+ return 0;
+ }
+ }
+
+ if(type == HSSL2ClientHello){+ /* Cope with an SSL3 ClientHello expressed in SSL2 record format.
+ This is sent by some clients that we must interoperate
+ with, such as Java's JSSE and Microsoft's Internet Explorer. */
+ int nsid, nrandom, nciph;
+
+ p = tlsReadN(c, n);
+ if(p == nil)
+ return 0;
+ msgHash(c, p, n);
+ m->tag = HClientHello;
+ if(n < 22)
+ goto Short;
+ m->u.clientHello.version = get16(p+1);
+ p += 3;
+ n -= 3;
+ nn = get16(p); /* cipher_spec_len */
+ nsid = get16(p + 2);
+ nrandom = get16(p + 4);
+ p += 6;
+ n -= 6;
+ if(nsid != 0 /* no sid's, since shouldn't restart using ssl2 header */
+ || nrandom < 16 || nn % 3 || n - nrandom < nn)
+ goto Err;
+ /* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */+ nciph = 0;
+ for(i = 0; i < nn; i += 3)
+ if(p[i] == 0)
+ nciph++;
+ m->u.clientHello.ciphers = newints(nciph);
+ nciph = 0;
+ for(i = 0; i < nn; i += 3)
+ if(p[i] == 0)
+ m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]);
+ p += nn;
+ m->u.clientHello.sid = makebytes(nil, 0);
+ if(nrandom > RandomSize)
+ nrandom = RandomSize;
+ memset(m->u.clientHello.random, 0, RandomSize - nrandom);
+ memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom);
+ m->u.clientHello.compressors = newbytes(1);
+ m->u.clientHello.compressors->data[0] = CompressionNull;
+ goto Ok;
+ }
+ msgHash(c, p, 4);
+
+ p = tlsReadN(c, n);
+ if(p == nil)
+ return 0;
+
+ msgHash(c, p, n);
+
+ m->tag = type;
+
+ switch(type) {+ default:
+ tlsError(c, EUnexpectedMessage, "can't decode a %d", type);
+ goto Err;
+ case HClientHello:
+ if(n < 2)
+ goto Short;
+ m->u.clientHello.version = get16(p);
+ p += 2, n -= 2;
+
+ if(n < RandomSize)
+ goto Short;
+ memmove(m->u.clientHello.random, p, RandomSize);
+ p += RandomSize, n -= RandomSize;
+ if(n < 1 || n < p[0]+1)
+ goto Short;
+ m->u.clientHello.sid = makebytes(p+1, p[0]);
+ p += m->u.clientHello.sid->len+1;
+ n -= m->u.clientHello.sid->len+1;
+
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+
+ if(nn % 2 || n < nn || nn < 2)
+ goto Short;
+ m->u.clientHello.ciphers = newints(nn >> 1);
+ for(i = 0; i < nn; i += 2)
+ m->u.clientHello.ciphers->data[i >> 1] = get16(&p[i]);
+ p += nn, n -= nn;
+
+ if(n < 1 || n < p[0]+1 || p[0] == 0)
+ goto Short;
+ nn = p[0];
+ m->u.clientHello.compressors = makebytes(p+1, nn);
+ p += nn + 1, n -= nn + 1;
+
+ if(n < 2)
+ break;
+ nn = get16(p);
+ if(nn > n-2)
+ goto Short;
+ m->u.clientHello.extensions = makebytes(p+2, nn);
+ n -= nn + 2;
+ break;
+ case HServerHello:
+ if(n < 2)
+ goto Short;
+ m->u.serverHello.version = get16(p);
+ p += 2, n -= 2;
+
+ if(n < RandomSize)
+ goto Short;
+ memmove(m->u.serverHello.random, p, RandomSize);
+ p += RandomSize, n -= RandomSize;
+
+ if(n < 1 || n < p[0]+1)
+ goto Short;
+ m->u.serverHello.sid = makebytes(p+1, p[0]);
+ p += m->u.serverHello.sid->len+1;
+ n -= m->u.serverHello.sid->len+1;
+
+ if(n < 3)
+ goto Short;
+ m->u.serverHello.cipher = get16(p);
+ m->u.serverHello.compressor = p[2];
+ p += 3, n -= 3;
+
+ if(n < 2)
+ break;
+ nn = get16(p);
+ if(nn > n-2)
+ goto Short;
+ m->u.serverHello.extensions = makebytes(p+2, nn);
+ n -= nn + 2;
+ break;
+ case HCertificate:
+ if(n < 3)
+ goto Short;
+ nn = get24(p);
+ p += 3, n -= 3;
+ if(nn == 0 && n > 0)
+ goto Short;
+ /* certs */
+ i = 0;
+ while(n > 0) {+ if(n < 3)
+ goto Short;
+ nn = get24(p);
+ p += 3, n -= 3;
+ if(nn > n)
+ goto Short;
+ m->u.certificate.ncert = i+1;
+ m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes*));
+ m->u.certificate.certs[i] = makebytes(p, nn);
+ p += nn, n -= nn;
+ i++;
+ }
+ break;
+ case HCertificateRequest:
+ if(n < 1)
+ goto Short;
+ nn = p[0];
+ p++, n--;
+ if(nn > n)
+ goto Short;
+ m->u.certificateRequest.types = makebytes(p, nn);
+ p += nn, n -= nn;
+ if(c->version >= TLS12Version){+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn % 2)
+ goto Short;
+ m->u.certificateRequest.sigalgs = newints(nn>>1);
+ for(i = 0; i < nn; i += 2)
+ m->u.certificateRequest.sigalgs->data[i >> 1] = get16(&p[i]);
+ p += nn, n -= nn;
+
+ }
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ /* nn == 0 can happen; yahoo's servers do it */
+ if(nn != n)
+ goto Short;
+ /* cas */
+ i = 0;
+ while(n > 0) {+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.certificateRequest.nca = i+1;
+ m->u.certificateRequest.cas = erealloc(
+ m->u.certificateRequest.cas, (i+1)*sizeof(Bytes*));
+ m->u.certificateRequest.cas[i] = makebytes(p, nn);
+ p += nn, n -= nn;
+ i++;
+ }
+ break;
+ case HServerHelloDone:
+ break;
+ case HServerKeyExchange:
+ if(isPSK(c->cipher)){+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn > n)
+ goto Short;
+ m->u.serverKeyExchange.pskid = makebytes(p, nn);
+ p += nn, n -= nn;
+ if(n == 0)
+ break;
+ }
+ if(n < 2)
+ goto Short;
+ s = p;
+ if(isECDHE(c->cipher)){+ nn = *p;
+ p++, n--;
+ if(nn != 3 || nn > n) /* not a named curve */
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ m->u.serverKeyExchange.curve = nn;
+
+ nn = *p++, n--;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_Ys = makebytes(p, nn);
+ p += nn, n -= nn;
+ }else if(isDHE(c->cipher)){+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_p = makebytes(p, nn);
+ p += nn, n -= nn;
+
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_g = makebytes(p, nn);
+ p += nn, n -= nn;
+
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_Ys = makebytes(p, nn);
+ p += nn, n -= nn;
+ } else {+ /* should not happen */
+ goto Short;
+ }
+ m->u.serverKeyExchange.dh_parameters = makebytes(s, p - s);
+ if(n >= 2){+ m->u.serverKeyExchange.sigalg = 0;
+ if(c->version >= TLS12Version){+ m->u.serverKeyExchange.sigalg = get16(p);
+ p += 2, n -= 2;
+ if(n < 2)
+ goto Short;
+ }
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn > 0 && nn <= n){+ m->u.serverKeyExchange.dh_signature = makebytes(p, nn);
+ n -= nn;
+ }
+ }
+ break;
+ case HClientKeyExchange:
+ if(isPSK(c->cipher)){+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn > n)
+ goto Short;
+ m->u.clientKeyExchange.pskid = makebytes(p, nn);
+ p += nn, n -= nn;
+ if(n == 0)
+ break;
+ }
+ if(n < 2)
+ goto Short;
+ if(isECDHE(c->cipher))
+ nn = *p++, n--;
+ else if(isDHE(c->cipher) || c->version != SSL3Version)
+ nn = get16(p), p += 2, n -= 2;
+ else
+ nn = n;
+ if(n < nn)
+ goto Short;
+ m->u.clientKeyExchange.key = makebytes(p, nn);
+ n -= nn;
+ break;
+ case HFinished:
+ m->u.finished.n = c->finished.n;
+ if(n < m->u.finished.n)
+ goto Short;
+ memmove(m->u.finished.verify, p, m->u.finished.n);
+ n -= m->u.finished.n;
+ break;
+ }
+
+ if(n != 0 && type != HClientHello && type != HServerHello)
+ goto Short;
+Ok:
+ if(c->trace)
+ c->trace("recv %s", msgPrint((char*)c->sendp, &c->buf[sizeof(c->buf)] - c->sendp, m));+ return 1;
+Short:
+ tlsError(c, EDecodeError, "handshake message (%d) has invalid length", type);
+Err:
+ msgClear(m);
+ return 0;
+}
+
+static void
+msgClear(Msg *m)
+{+ int i;
+
+ switch(m->tag) {+ case HHelloRequest:
+ break;
+ case HClientHello:
+ freebytes(m->u.clientHello.sid);
+ freeints(m->u.clientHello.ciphers);
+ freebytes(m->u.clientHello.compressors);
+ freebytes(m->u.clientHello.extensions);
+ break;
+ case HServerHello:
+ freebytes(m->u.serverHello.sid);
+ freebytes(m->u.serverHello.extensions);
+ break;
+ case HCertificate:
+ for(i=0; i<m->u.certificate.ncert; i++)
+ freebytes(m->u.certificate.certs[i]);
+ free(m->u.certificate.certs);
+ break;
+ case HCertificateRequest:
+ freebytes(m->u.certificateRequest.types);
+ freeints(m->u.certificateRequest.sigalgs);
+ for(i=0; i<m->u.certificateRequest.nca; i++)
+ freebytes(m->u.certificateRequest.cas[i]);
+ free(m->u.certificateRequest.cas);
+ break;
+ case HCertificateVerify:
+ freebytes(m->u.certificateVerify.signature);
+ break;
+ case HServerHelloDone:
+ break;
+ case HServerKeyExchange:
+ freebytes(m->u.serverKeyExchange.pskid);
+ freebytes(m->u.serverKeyExchange.dh_p);
+ freebytes(m->u.serverKeyExchange.dh_g);
+ freebytes(m->u.serverKeyExchange.dh_Ys);
+ freebytes(m->u.serverKeyExchange.dh_parameters);
+ freebytes(m->u.serverKeyExchange.dh_signature);
+ break;
+ case HClientKeyExchange:
+ freebytes(m->u.clientKeyExchange.pskid);
+ freebytes(m->u.clientKeyExchange.key);
+ break;
+ case HFinished:
+ break;
+ }
+ memset(m, 0, sizeof(Msg));
+}
+
+static char *
+bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1)
+{+ int i;
+
+ if(s0)
+ bs = seprint(bs, be, "%s", s0);
+ if(b == nil)
+ bs = seprint(bs, be, "nil");
+ else {+ bs = seprint(bs, be, "<%d> [ ", b->len);
+ for(i=0; i<b->len; i++)
+ bs = seprint(bs, be, "%.2x ", b->data[i]);
+ bs = seprint(bs, be, "]");
+ }
+ if(s1)
+ bs = seprint(bs, be, "%s", s1);
+ return bs;
+}
+
+static char *
+intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1)
+{+ int i;
+
+ if(s0)
+ bs = seprint(bs, be, "%s", s0);
+ if(b == nil)
+ bs = seprint(bs, be, "nil");
+ else {+ bs = seprint(bs, be, "[ ");
+ for(i=0; i<b->len; i++)
+ bs = seprint(bs, be, "%x ", b->data[i]);
+ bs = seprint(bs, be, "]");
+ }
+ if(s1)
+ bs = seprint(bs, be, "%s", s1);
+ return bs;
+}
+
+static char*
+msgPrint(char *buf, int n, Msg *m)
+{+ int i;
+ char *bs = buf, *be = buf+n;
+
+ switch(m->tag) {+ default:
+ bs = seprint(bs, be, "unknown %d\n", m->tag);
+ break;
+ case HClientHello:
+ bs = seprint(bs, be, "ClientHello\n");
+ bs = seprint(bs, be, "\tversion: %.4x\n", m->u.clientHello.version);
+ bs = seprint(bs, be, "\trandom: ");
+ for(i=0; i<RandomSize; i++)
+ bs = seprint(bs, be, "%.2x", m->u.clientHello.random[i]);
+ bs = seprint(bs, be, "\n");
+ bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n");
+ bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n");
+ bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n");
+ if(m->u.clientHello.extensions != nil)
+ bs = bytesPrint(bs, be, "\textensions: ", m->u.clientHello.extensions, "\n");
+ break;
+ case HServerHello:
+ bs = seprint(bs, be, "ServerHello\n");
+ bs = seprint(bs, be, "\tversion: %.4x\n", m->u.serverHello.version);
+ bs = seprint(bs, be, "\trandom: ");
+ for(i=0; i<RandomSize; i++)
+ bs = seprint(bs, be, "%.2x", m->u.serverHello.random[i]);
+ bs = seprint(bs, be, "\n");
+ bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n");
+ bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher);
+ bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor);
+ if(m->u.serverHello.extensions != nil)
+ bs = bytesPrint(bs, be, "\textensions: ", m->u.serverHello.extensions, "\n");
+ break;
+ case HCertificate:
+ bs = seprint(bs, be, "Certificate\n");
+ for(i=0; i<m->u.certificate.ncert; i++)
+ bs = bytesPrint(bs, be, "\t", m->u.certificate.certs[i], "\n");
+ break;
+ case HCertificateRequest:
+ bs = seprint(bs, be, "CertificateRequest\n");
+ bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n");
+ if(m->u.certificateRequest.sigalgs != nil)
+ bs = intsPrint(bs, be, "\tsigalgs: ", m->u.certificateRequest.sigalgs, "\n");
+ bs = seprint(bs, be, "\tcertificateauthorities\n");
+ for(i=0; i<m->u.certificateRequest.nca; i++)
+ bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n");
+ break;
+ case HCertificateVerify:
+ bs = seprint(bs, be, "HCertificateVerify\n");
+ if(m->u.certificateVerify.sigalg != 0)
+ bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.certificateVerify.sigalg);
+ bs = bytesPrint(bs, be, "\tsignature: ", m->u.certificateVerify.signature,"\n");
+ break;
+ case HServerHelloDone:
+ bs = seprint(bs, be, "ServerHelloDone\n");
+ break;
+ case HServerKeyExchange:
+ bs = seprint(bs, be, "HServerKeyExchange\n");
+ if(m->u.serverKeyExchange.pskid != nil)
+ bs = bytesPrint(bs, be, "\tpskid: ", m->u.serverKeyExchange.pskid, "\n");
+ if(m->u.serverKeyExchange.dh_parameters == nil)
+ break;
+ if(m->u.serverKeyExchange.curve != 0){+ bs = seprint(bs, be, "\tcurve: %.4x\n", m->u.serverKeyExchange.curve);
+ } else {+ bs = bytesPrint(bs, be, "\tdh_p: ", m->u.serverKeyExchange.dh_p, "\n");
+ bs = bytesPrint(bs, be, "\tdh_g: ", m->u.serverKeyExchange.dh_g, "\n");
+ }
+ bs = bytesPrint(bs, be, "\tdh_Ys: ", m->u.serverKeyExchange.dh_Ys, "\n");
+ if(m->u.serverKeyExchange.sigalg != 0)
+ bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.serverKeyExchange.sigalg);
+ bs = bytesPrint(bs, be, "\tdh_parameters: ", m->u.serverKeyExchange.dh_parameters, "\n");
+ bs = bytesPrint(bs, be, "\tdh_signature: ", m->u.serverKeyExchange.dh_signature, "\n");
+ break;
+ case HClientKeyExchange:
+ bs = seprint(bs, be, "HClientKeyExchange\n");
+ if(m->u.clientKeyExchange.pskid != nil)
+ bs = bytesPrint(bs, be, "\tpskid: ", m->u.clientKeyExchange.pskid, "\n");
+ if(m->u.clientKeyExchange.key != nil)
+ bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n");
+ break;
+ case HFinished:
+ bs = seprint(bs, be, "HFinished\n");
+ for(i=0; i<m->u.finished.n; i++)
+ bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]);
+ bs = seprint(bs, be, "\n");
+ break;
+ }
+ USED(bs);
+ return buf;
+}
+
+static void
+tlsError(TlsConnection *c, int err, char *fmt, ...)
+{+ char msg[512];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(msg, msg+sizeof(msg), fmt, arg);
+ va_end(arg);
+ if(c->trace)
+ c->trace("tlsError: %s\n", msg);+ if(c->erred)
+ fprint(2, "double error: %r, %s", msg);
+ else
+ errstr(msg, sizeof(msg));
+ c->erred = 1;
+ fprint(c->ctl, "alert %d", err);
+}
+
+// commit to specific version number
+static int
+setVersion(TlsConnection *c, int version)
+{+ if(version > MaxProtoVersion || version < MinProtoVersion)
+ return -1;
+ if(version > c->version)
+ version = c->version;
+ if(version == SSL3Version) {+ c->version = version;
+ c->finished.n = SSL3FinishedLen;
+ }else {+ c->version = version;
+ c->finished.n = TLSFinishedLen;
+ }
+ return fprint(c->ctl, "version 0x%x", version);
+}
+
+// confirm that received Finished message matches the expected value
+static int
+finishedMatch(TlsConnection *c, Finished *f)
+{+ return tsmemcmp(f->verify, c->finished.verify, f->n) == 0;
+}
+
+// free memory associated with TlsConnection struct
+// (but don't close the TLS channel itself)
+static void
+tlsConnectionFree(TlsConnection *c)
+{+ if(c == nil)
+ return;
+
+ dh_finish(&c->sec->dh, nil);
+
+ mpfree(c->sec->ec.Q.a.x);
+ mpfree(c->sec->ec.Q.a.y);
+ mpfree(c->sec->ec.Q.d);
+ ecdomfree(&c->sec->ec.dom);
+
+ factotum_rsa_close(c->sec->rpc);
+ rsapubfree(c->sec->rsapub);
+ freebytes(c->cert);
+
+ memset(c, 0, sizeof(*c));
+ free(c);
+}
+
+
+//================= cipher choices ========================
+
+static int
+isDHE(int tlsid)
+{+ switch(tlsid){+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305:
+ case GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isECDHE(int tlsid)
+{+ switch(tlsid){+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+ case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isPSK(int tlsid)
+{+ switch(tlsid){+ case TLS_PSK_WITH_CHACHA20_POLY1305:
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case TLS_PSK_WITH_AES_128_CBC_SHA:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isECDSA(int tlsid)
+{+ switch(tlsid){+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+setAlgs(TlsConnection *c, int a)
+{+ int i;
+
+ for(i = 0; i < nelem(cipherAlgs); i++){+ if(cipherAlgs[i].tlsid == a){+ c->cipher = a;
+ c->enc = cipherAlgs[i].enc;
+ c->digest = cipherAlgs[i].digest;
+ c->nsecret = cipherAlgs[i].nsecret;
+ if(c->nsecret > MaxKeyData)
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+okCipher(Ints *cv, int ispsk, int canec)
+{+ int i, c;
+
+ for(i = 0; i < nelem(cipherAlgs); i++) {+ c = cipherAlgs[i].tlsid;
+ if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c))
+ continue;
+ if(isPSK(c) != ispsk)
+ continue;
+ if(isECDHE(c) && !canec)
+ continue;
+ if(lookupid(cv, c) >= 0)
+ return c;
+ }
+ return -1;
+}
+
+static int
+okCompression(Bytes *cv)
+{+ int i, c;
+
+ for(i = 0; i < nelem(compressors); i++) {+ c = compressors[i];
+ if(memchr(cv->data, c, cv->len) != nil)
+ return c;
+ }
+ return -1;
+}
+
+static Lock ciphLock;
+static int nciphers;
+
+static int
+initCiphers(void)
+{+ enum {MaxAlgF = 1024, MaxAlgs = 10};+ char s[MaxAlgF], *flds[MaxAlgs];
+ int i, j, n, ok;
+
+ lock(&ciphLock);
+ if(nciphers){+ unlock(&ciphLock);
+ return nciphers;
+ }
+ j = open("#a/tls/encalgs", OREAD|OCEXEC);+ if(j < 0){+ werrstr("can't open #a/tls/encalgs: %r");+ goto out;
+ }
+ n = read(j, s, MaxAlgF-1);
+ close(j);
+ if(n <= 0){+ werrstr("nothing in #a/tls/encalgs: %r");+ goto out;
+ }
+ s[n] = 0;
+ n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
+ for(i = 0; i < nelem(cipherAlgs); i++){+ ok = 0;
+ for(j = 0; j < n; j++){+ if(strcmp(cipherAlgs[i].enc, flds[j]) == 0){+ ok = 1;
+ break;
+ }
+ }
+ cipherAlgs[i].ok = ok;
+ }
+
+ j = open("#a/tls/hashalgs", OREAD|OCEXEC);+ if(j < 0){+ werrstr("can't open #a/tls/hashalgs: %r");+ goto out;
+ }
+ n = read(j, s, MaxAlgF-1);
+ close(j);
+ if(n <= 0){+ werrstr("nothing in #a/tls/hashalgs: %r");+ goto out;
+ }
+ s[n] = 0;
+ n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
+ for(i = 0; i < nelem(cipherAlgs); i++){+ ok = 0;
+ for(j = 0; j < n; j++){+ if(strcmp(cipherAlgs[i].digest, flds[j]) == 0){+ ok = 1;
+ break;
+ }
+ }
+ cipherAlgs[i].ok &= ok;
+ if(cipherAlgs[i].ok)
+ nciphers++;
+ }
+out:
+ unlock(&ciphLock);
+ return nciphers;
+}
+
+static Ints*
+makeciphers(int ispsk)
+{+ Ints *is;
+ int i, j;
+
+ is = newints(nciphers);
+ j = 0;
+ for(i = 0; i < nelem(cipherAlgs); i++)
+ if(cipherAlgs[i].ok && isPSK(cipherAlgs[i].tlsid) == ispsk)
+ is->data[j++] = cipherAlgs[i].tlsid;
+ is->len = j;
+ return is;
+}
+
+
+//================= security functions ========================
+
+// given a public key, set up connection to factotum
+// for using corresponding private key
+static AuthRpc*
+factotum_rsa_open(RSApub *rsapub)
+{+ int afd;
+ char *s;
+ mpint *n;
+ AuthRpc *rpc;
+
+ // start talking to factotum
+ if((afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC)) < 0)+ return nil;
+ if((rpc = auth_allocrpc(afd)) == nil){+ close(afd);
+ return nil;
+ }
+ s = "proto=rsa service=tls role=client";
+ if(auth_rpc(rpc, "start", s, strlen(s)) == ARok){+ // roll factotum keyring around to match public key
+ n = mpnew(0);
+ while(auth_rpc(rpc, "read", nil, 0) == ARok){+ if(strtomp(rpc->arg, nil, 16, n) != nil
+ && mpcmp(n, rsapub->n) == 0){+ mpfree(n);
+ return rpc;
+ }
+ }
+ mpfree(n);
+ }
+ factotum_rsa_close(rpc);
+ return nil;
+}
+
+static mpint*
+factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher)
+{+ char *p;
+ int rv;
+
+ if(cipher == nil)
+ return nil;
+ p = mptoa(cipher, 16, nil, 0);
+ mpfree(cipher);
+ if(p == nil)
+ return nil;
+ rv = auth_rpc(rpc, "write", p, strlen(p));
+ free(p);
+ if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok)
+ return nil;
+ return strtomp(rpc->arg, nil, 16, nil);
+}
+
+static void
+factotum_rsa_close(AuthRpc *rpc)
+{+ if(rpc == nil)
+ return;
+ close(rpc->afd);
+ auth_freerpc(rpc);
+}
+
+// buf ^= prf
+static void
+tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed,
+ DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen)
+{+ uchar ai[SHA2_256dlen], tmp[SHA2_256dlen];
+ DigestState *s;
+ int n, i;
+
+ assert(xlen <= sizeof(ai) && xlen <= sizeof(tmp));
+ // generate a1
+ s = x(label, nlabel, key, nkey, nil, nil);
+ x(seed, nseed, key, nkey, ai, s);
+
+ while(nbuf > 0) {+ s = x(ai, xlen, key, nkey, nil, nil);
+ s = x(label, nlabel, key, nkey, nil, s);
+ x(seed, nseed, key, nkey, tmp, s);
+ n = xlen;
+ if(n > nbuf)
+ n = nbuf;
+ for(i = 0; i < n; i++)
+ buf[i] ^= tmp[i];
+ buf += n;
+ nbuf -= n;
+ x(ai, xlen, key, nkey, tmp, nil);
+ memmove(ai, tmp, xlen);
+ }
+}
+
+
+// fill buf with md5(args)^sha1(args)
+static void
+tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed)
+{+ int nlabel = strlen(label);
+ int n = (nkey + 1) >> 1;
+
+ memset(buf, 0, nbuf);
+ tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed,
+ hmac_md5, MD5dlen);
+ tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed,
+ hmac_sha1, SHA1dlen);
+}
+
+static void
+tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed)
+{+ memset(buf, 0, nbuf);
+ tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed,
+ hmac_sha2_256, SHA2_256dlen);
+}
+
+static void
+sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed)
+{+ uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26];
+ DigestState *s;
+ int i, n, len;
+
+ USED(label);
+ len = 1;
+ while(nbuf > 0){+ if(len > 26)
+ return;
+ for(i = 0; i < len; i++)
+ tmp[i] = 'A' - 1 + len;
+ s = sha1(tmp, len, nil, nil);
+ s = sha1(key, nkey, nil, s);
+ sha1(seed, nseed, sha1dig, s);
+ s = md5(key, nkey, nil, nil);
+ md5(sha1dig, SHA1dlen, md5dig, s);
+ n = MD5dlen;
+ if(n > nbuf)
+ n = nbuf;
+ memmove(buf, md5dig, n);
+ buf += n;
+ nbuf -= n;
+ len++;
+ }
+}
+
+static void
+sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
+{+ DigestState *s;
+ uchar h0[MD5dlen], h1[SHA1dlen], pad[48];
+ char *label;
+
+ if(isclient)
+ label = "CLNT";
+ else
+ label = "SRVR";
+
+ md5((uchar*)label, 4, nil, &hsh.md5);
+ md5(sec->sec, MasterSecretSize, nil, &hsh.md5);
+ memset(pad, 0x36, 48);
+ md5(pad, 48, nil, &hsh.md5);
+ md5(nil, 0, h0, &hsh.md5);
+ memset(pad, 0x5C, 48);
+ s = md5(sec->sec, MasterSecretSize, nil, nil);
+ s = md5(pad, 48, nil, s);
+ md5(h0, MD5dlen, finished, s);
+
+ sha1((uchar*)label, 4, nil, &hsh.sha1);
+ sha1(sec->sec, MasterSecretSize, nil, &hsh.sha1);
+ memset(pad, 0x36, 40);
+ sha1(pad, 40, nil, &hsh.sha1);
+ sha1(nil, 0, h1, &hsh.sha1);
+ memset(pad, 0x5C, 40);
+ s = sha1(sec->sec, MasterSecretSize, nil, nil);
+ s = sha1(pad, 40, nil, s);
+ sha1(h1, SHA1dlen, finished + MD5dlen, s);
+}
+
+// fill "finished" arg with md5(args)^sha1(args)
+static void
+tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
+{+ uchar h[MD5dlen+SHA1dlen];
+ char *label;
+
+ // get current hash value, but allow further messages to be hashed in
+ md5(nil, 0, h, &hsh.md5);
+ sha1(nil, 0, h+MD5dlen, &hsh.sha1);
+
+ if(isclient)
+ label = "client finished";
+ else
+ label = "server finished";
+ tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h));
+}
+
+static void
+tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
+{+ uchar seed[SHA2_256dlen];
+ char *label;
+
+ // get current hash value, but allow further messages to be hashed in
+ sha2_256(nil, 0, seed, &hsh.sha2_256);
+
+ if(isclient)
+ label = "client finished";
+ else
+ label = "server finished";
+ tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen);
+}
+
+static void
+tlsSecInits(TlsSec *sec, int cvers, uchar *crandom)
+{+ memset(sec, 0, sizeof(*sec));
+ sec->clientVers = cvers;
+ memmove(sec->crandom, crandom, RandomSize);
+
+ // putting time()'s output to the first 4 bytes is no
+ // longer recommended and is not useful
+ genrandom(sec->srandom, RandomSize);
+}
+
+static int
+tlsSecRSAs(TlsSec *sec, Bytes *epm)
+{+ Bytes *pm;
+
+ if(epm == nil){+ werrstr("no encrypted premaster secret");+ return -1;
+ }
+ // if the client messed up, just continue as if everything is ok,
+ // to prevent attacks to check for correctly formatted messages.
+ pm = pkcs1_decrypt(sec, epm);
+ if(pm == nil || pm->len != MasterSecretSize || get16(pm->data) != sec->clientVers){+ freebytes(pm);
+ pm = newbytes(MasterSecretSize);
+ genrandom(pm->data, pm->len);
+ }
+ setMasterSecret(sec, pm);
+ return 0;
+}
+
+static Bytes*
+tlsSecECDHEs1(TlsSec *sec)
+{+ ECdomain *dom = &sec->ec.dom;
+ ECpriv *Q = &sec->ec.Q;
+ Bytes *par;
+ int n;
+
+ if(sec->nc == nil)
+ return nil;
+ if(sec->nc->tlsid == X25519){+ par = newbytes(1+2+1+32);
+ par->data[0] = 3;
+ put16(par->data+1, X25519);
+ par->data[3] = 32;
+ curve25519_dh_new(sec->X, par->data+4);
+ }else{+ ecdominit(dom, sec->nc->init);
+ memset(Q, 0, sizeof(*Q));
+ Q->a.x = mpnew(0);
+ Q->a.y = mpnew(0);
+ Q->d = mpnew(0);
+ ecgen(dom, Q);
+ n = 1 + 2*((mpsignif(dom->p)+7)/8);
+ par = newbytes(1+2+1+n);
+ par->data[0] = 3;
+ put16(par->data+1, sec->nc->tlsid);
+ n = ecencodepub(dom, &Q->a, par->data+4, par->len-4);
+ par->data[3] = n;
+ par->len = 1+2+1+n;
+ }
+ return par;
+}
+
+static int
+tlsSecECDHEs2(TlsSec *sec, Bytes *Yc)
+{+ ECdomain *dom = &sec->ec.dom;
+ ECpriv *Q = &sec->ec.Q;
+ ECpoint K;
+ ECpub *Y;
+ Bytes *Z;
+
+ if(Yc == nil){+ werrstr("no public key");+ return -1;
+ }
+
+ if(sec->nc->tlsid == X25519){+ if(Yc->len != 32){+ werrstr("bad public key");+ return -1;
+ }
+ Z = newbytes(32);
+ if(!curve25519_dh_finish(sec->X, Yc->data, Z->data)){+ werrstr("unlucky shared key");+ freebytes(Z);
+ return -1;
+ }
+ setMasterSecret(sec, Z);
+ }else{+ if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){+ werrstr("bad public key");+ return -1;
+ }
+
+ memset(&K, 0, sizeof(K));
+ K.x = mpnew(0);
+ K.y = mpnew(0);
+
+ ecmul(dom, Y, Q->d, &K);
+
+ setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8));
+
+ mpfree(K.x);
+ mpfree(K.y);
+
+ ecpubfree(Y);
+ }
+ return 0;
+}
+
+static void
+tlsSecInitc(TlsSec *sec, int cvers)
+{+ memset(sec, 0, sizeof(*sec));
+ sec->clientVers = cvers;
+ // see the comment on tlsSecInits
+ genrandom(sec->crandom, RandomSize);
+}
+
+static Bytes*
+tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert)
+{+ RSApub *pub;
+ Bytes *pm, *epm;
+
+ pub = X509toRSApub(cert, ncert, nil, 0);
+ if(pub == nil){+ werrstr("invalid x509/rsa certificate");+ return nil;
+ }
+ pm = newbytes(MasterSecretSize);
+ put16(pm->data, sec->clientVers);
+ genrandom(pm->data+2, MasterSecretSize - 2);
+ epm = pkcs1_encrypt(pm, pub);
+ setMasterSecret(sec, pm);
+ rsapubfree(pub);
+ return epm;
+}
+
+static int
+tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient)
+{+ if(sec->nfin != nfin){+ werrstr("invalid finished exchange");+ return -1;
+ }
+ hsh.md5.malloced = 0;
+ hsh.sha1.malloced = 0;
+ hsh.sha2_256.malloced = 0;
+ (*sec->setFinished)(sec, hsh, fin, isclient);
+ return 0;
+}
+
+static void
+tlsSecVers(TlsSec *sec, int v)
+{+ if(v == SSL3Version){+ sec->setFinished = sslSetFinished;
+ sec->nfin = SSL3FinishedLen;
+ sec->prf = sslPRF;
+ }else if(v < TLS12Version) {+ sec->setFinished = tls10SetFinished;
+ sec->nfin = TLSFinishedLen;
+ sec->prf = tls10PRF;
+ }else {+ sec->setFinished = tls12SetFinished;
+ sec->nfin = TLSFinishedLen;
+ sec->prf = tls12PRF;
+ }
+}
+
+static int
+setSecrets(TlsConnection *c, int isclient)
+{+ uchar kd[MaxKeyData], seed[2*RandomSize];
+ char *secrets;
+ int rv;
+
+ assert(c->nsecret <= sizeof(kd));
+ secrets = emalloc(2*c->nsecret);
+
+ memmove(seed, c->sec->srandom, RandomSize);
+ memmove(seed+RandomSize, c->sec->crandom, RandomSize);
+ /*
+ * generate secret keys from the master secret.
+ *
+ * different cipher selections will require different amounts
+ * of key expansion and use of key expansion data,
+ * but it's all generated using the same function.
+ */
+ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion",
+ seed, sizeof(seed));
+
+ enc64(secrets, 2*c->nsecret, kd, c->nsecret);
+ memset(kd, 0, c->nsecret);
+
+ rv = fprint(c->ctl, "secret %s %s %d %s", c->digest, c->enc, isclient, secrets);
+ memset(secrets, 0, 2*c->nsecret);
+ free(secrets);
+
+ return rv;
+}
+
+/*
+ * set the master secret from the pre-master secret,
+ * destroys premaster.
+ */
+static void
+setMasterSecret(TlsSec *sec, Bytes *pm)
+{+ uchar seed[2*RandomSize];
+
+ if(sec->psklen > 0){+ Bytes *opm = pm;
+ uchar *p;
+
+ /* concatenate psk to pre-master secret */
+ pm = newbytes(4 + opm->len + sec->psklen);
+ p = pm->data;
+ put16(p, opm->len), p += 2;
+ memmove(p, opm->data, opm->len), p += opm->len;
+ put16(p, sec->psklen), p += 2;
+ memmove(p, sec->psk, sec->psklen);
+
+ memset(opm->data, 0, opm->len);
+ freebytes(opm);
+ }
+
+ memmove(seed, sec->crandom, RandomSize);
+ memmove(seed+RandomSize, sec->srandom, RandomSize);
+ (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret",
+ seed, sizeof(seed));
+
+ memset(pm->data, 0, pm->len);
+ freebytes(pm);
+}
+
+static int
+digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg)
+{+ int hashalg = (sigalg>>8) & 0xFF;
+ int digestlen;
+ Bytes *blob;
+
+ blob = newbytes(2*RandomSize + par->len);
+ memmove(blob->data+0*RandomSize, sec->crandom, RandomSize);
+ memmove(blob->data+1*RandomSize, sec->srandom, RandomSize);
+ memmove(blob->data+2*RandomSize, par->data, par->len);
+ if(hashalg == 0){+ digestlen = MD5dlen+SHA1dlen;
+ md5(blob->data, blob->len, digest, nil);
+ sha1(blob->data, blob->len, digest+MD5dlen, nil);
+ } else {+ digestlen = -1;
+ if(hashalg < nelem(hashfun) && hashfun[hashalg].fun != nil){+ digestlen = hashfun[hashalg].len;
+ (*hashfun[hashalg].fun)(blob->data, blob->len, digest, nil);
+ }
+ }
+ freebytes(blob);
+ return digestlen;
+}
+
+static char*
+verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg)
+{+ uchar digest[MAXdlen];
+ int digestlen;
+ ECdomain dom;
+ ECpub *ecpk;
+ RSApub *rsapk;
+ char *err;
+
+ if(par == nil || par->len <= 0)
+ return "no DH parameters";
+
+ if(sig == nil || sig->len <= 0){+ if(sec->psklen > 0)
+ return nil;
+ return "no signature";
+ }
+
+ if(cert == nil)
+ return "no certificate";
+
+ digestlen = digestDHparams(sec, par, digest, sigalg);
+ if(digestlen <= 0)
+ return "unknown signature digest algorithm";
+
+ switch(sigalg & 0xFF){+ case 0x01:
+ rsapk = X509toRSApub(cert->data, cert->len, nil, 0);
+ if(rsapk == nil)
+ return "bad certificate";
+ err = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, rsapk);
+ rsapubfree(rsapk);
+ break;
+ case 0x03:
+ ecpk = X509toECpub(cert->data, cert->len, nil, 0, &dom);
+ if(ecpk == nil)
+ return "bad certificate";
+ err = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, &dom, ecpk);
+ ecdomfree(&dom);
+ ecpubfree(ecpk);
+ break;
+ default:
+ err = "signaure algorithm not RSA or ECDSA";
+ }
+
+ return err;
+}
+
+// encrypt data according to PKCS#1, /lib/rfc/rfc2437 9.1.2.1
+static Bytes*
+pkcs1_encrypt(Bytes* data, RSApub* key)
+{+ mpint *x, *y;
+
+ x = pkcs1padbuf(data->data, data->len, key->n, 2);
+ if(x == nil)
+ return nil;
+ y = rsaencrypt(key, x, nil);
+ mpfree(x);
+ data = newbytes((mpsignif(key->n)+7)/8);
+ mptober(y, data->data, data->len);
+ mpfree(y);
+ return data;
+}
+
+// decrypt data according to PKCS#1, with given key.
+static Bytes*
+pkcs1_decrypt(TlsSec *sec, Bytes *data)
+{+ mpint *y;
+
+ if(data->len != (mpsignif(sec->rsapub->n)+7)/8)
+ return nil;
+ y = factotum_rsa_decrypt(sec->rpc, bytestomp(data));
+ if(y == nil)
+ return nil;
+ data = mptobytes(y, (mpsignif(y)+7)/8);
+ mpfree(y);
+ if((data->len = pkcs1unpadbuf(data->data, data->len, sec->rsapub->n, 2)) < 0){+ freebytes(data);
+ return nil;
+ }
+ return data;
+}
+
+static Bytes*
+pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg)
+{+ int hashalg = (sigalg>>8)&0xFF;
+ mpint *signedMP;
+ Bytes *signature;
+ uchar buf[128];
+
+ if(hashalg > 0 && hashalg < nelem(hashfun) && hashfun[hashalg].len == digestlen)
+ digestlen = asn1encodedigest(hashfun[hashalg].fun, digest, buf, sizeof(buf));
+ else if(digestlen == MD5dlen+SHA1dlen)
+ memmove(buf, digest, digestlen);
+ else
+ digestlen = -1;
+ if(digestlen <= 0){+ werrstr("bad digest algorithm");+ return nil;
+ }
+
+ signedMP = factotum_rsa_decrypt(sec->rpc, pkcs1padbuf(buf, digestlen, sec->rsapub->n, 1));
+ if(signedMP == nil)
+ return nil;
+ signature = mptobytes(signedMP, (mpsignif(sec->rsapub->n)+7)/8);
+ mpfree(signedMP);
+ return signature;
+}
+
+
+//================= general utility functions ========================
+
+static void *
+emalloc(int n)
+{+ void *p;
+ if(n==0)
+ n=1;
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("out of memory");+ memset(p, 0, n);
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+static void *
+erealloc(void *ReallocP, int ReallocN)
+{+ if(ReallocN == 0)
+ ReallocN = 1;
+ if(ReallocP == nil)
+ ReallocP = emalloc(ReallocN);
+ else if((ReallocP = realloc(ReallocP, ReallocN)) == nil)
+ sysfatal("out of memory");+ setrealloctag(ReallocP, getcallerpc(&ReallocP));
+ return(ReallocP);
+}
+
+static void
+put32(uchar *p, u32int x)
+{+ p[0] = x>>24;
+ p[1] = x>>16;
+ p[2] = x>>8;
+ p[3] = x;
+}
+
+static void
+put24(uchar *p, int x)
+{+ p[0] = x>>16;
+ p[1] = x>>8;
+ p[2] = x;
+}
+
+static void
+put16(uchar *p, int x)
+{+ p[0] = x>>8;
+ p[1] = x;
+}
+
+static int
+get24(uchar *p)
+{+ return (p[0]<<16)|(p[1]<<8)|p[2];
+}
+
+static int
+get16(uchar *p)
+{+ return (p[0]<<8)|p[1];
+}
+
+static Bytes*
+newbytes(int len)
+{+ Bytes* ans;
+
+ if(len < 0)
+ abort();
+ ans = emalloc(sizeof(Bytes) + len);
+ ans->len = len;
+ return ans;
+}
+
+/*
+ * newbytes(len), with data initialized from buf
+ */
+static Bytes*
+makebytes(uchar* buf, int len)
+{+ Bytes* ans;
+
+ ans = newbytes(len);
+ memmove(ans->data, buf, len);
+ return ans;
+}
+
+static void
+freebytes(Bytes* b)
+{+ free(b);
+}
+
+static mpint*
+bytestomp(Bytes* bytes)
+{+ return betomp(bytes->data, bytes->len, nil);
+}
+
+/*
+ * Convert mpint* to Bytes, putting high order byte first.
+ */
+static Bytes*
+mptobytes(mpint *big, int len)
+{+ Bytes* ans;
+
+ if(len == 0) len++;
+ ans = newbytes(len);
+ mptober(big, ans->data, ans->len);
+ return ans;
+}
+
+/* len is number of ints */
+static Ints*
+newints(int len)
+{+ Ints* ans;
+
+ if(len < 0 || len > ((uint)-1>>1)/sizeof(int))
+ abort();
+ ans = emalloc(sizeof(Ints) + len*sizeof(int));
+ ans->len = len;
+ return ans;
+}
+
+static void
+freeints(Ints* b)
+{+ free(b);
+}
+
+static int
+lookupid(Ints* b, int id)
+{+ int i;
+
+ for(i=0; i<b->len; i++)
+ if(b->data[i] == id)
+ return i;
+ return -1;
+}
--- /dev/null
+++ b/libsec/tsmemcmp.c
@@ -1,0 +1,25 @@
+#include "os.h"
+#include <libsec.h>
+
+/*
+ * timing safe memcmp()
+ */
+int
+tsmemcmp(void *a1, void *a2, ulong n)
+{+ int lt, gt, c1, c2, r, m;
+ uchar *s1, *s2;
+
+ r = m = 0;
+ s1 = a1;
+ s2 = a2;
+ while(n--){+ c1 = *s1++;
+ c2 = *s2++;
+ lt = (c1 - c2) >> 8;
+ gt = (c2 - c1) >> 8;
+ r |= (lt - gt) & ~m;
+ m |= lt | gt;
+ }
+ return r;
+}
--- /dev/null
+++ b/libsec/x509.c
@@ -1,0 +1,3033 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+/*=============================================================*/
+/* general ASN1 declarations and parsing
+ *
+ * For now, this is used only for extracting the key from an
+ * X509 certificate, so the entire collection is hidden. But
+ * someday we should probably make the functions visible and
+ * give them their own man page.
+ */
+typedef struct Elem Elem;
+typedef struct Tag Tag;
+typedef struct Value Value;
+typedef struct Bytes Bytes;
+typedef struct Ints Ints;
+typedef struct Bits Bits;
+typedef struct Elist Elist;
+
+/* tag classes */
+#define Universal 0
+#define Context 0x80
+
+/* universal tags */
+#define BOOLEAN 1
+#define INTEGER 2
+#define BIT_STRING 3
+#define OCTET_STRING 4
+#define NULLTAG 5
+#define OBJECT_ID 6
+#define ObjectDescriptor 7
+#define EXTERNAL 8
+#define REAL 9
+#define ENUMERATED 10
+#define EMBEDDED_PDV 11
+#define UTF8String 12
+#define SEQUENCE 16 /* also SEQUENCE OF */
+#define SETOF 17 /* also SETOF OF */
+#define NumericString 18
+#define PrintableString 19
+#define TeletexString 20
+#define VideotexString 21
+#define IA5String 22
+#define UTCTime 23
+#define GeneralizedTime 24
+#define GraphicString 25
+#define VisibleString 26
+#define GeneralString 27
+#define UniversalString 28
+#define BMPString 30
+
+struct Bytes {+ int len;
+ uchar data[];
+};
+
+struct Ints {+ int len;
+ int data[];
+};
+
+struct Bits {+ int len; /* number of bytes */
+ int unusedbits; /* unused bits in last byte */
+ uchar data[]; /* most-significant bit first */
+};
+
+struct Tag {+ int class;
+ int num;
+};
+
+enum { VBool, VInt, VOctets, VBigInt, VReal, VOther,+ VBitString, VNull, VEOC, VObjId, VString, VSeq, VSet };
+struct Value {+ int tag; /* VBool, etc. */
+ union {+ int boolval;
+ int intval;
+ Bytes* octetsval;
+ Bytes* bigintval;
+ Bytes* realval; /* undecoded; hardly ever used */
+ Bytes* otherval;
+ Bits* bitstringval;
+ Ints* objidval;
+ char* stringval;
+ Elist* seqval;
+ Elist* setval;
+ } u; /* (Don't use anonymous unions, for ease of porting) */
+};
+
+struct Elem {+ Tag tag;
+ Value val;
+};
+
+struct Elist {+ Elist* tl;
+ Elem hd;
+};
+
+/* decoding errors */
+enum { ASN_OK, ASN_ESHORT, ASN_ETOOBIG, ASN_EVALLEN,+ ASN_ECONSTR, ASN_EPRIM, ASN_EINVAL, ASN_EUNIMPL };
+
+
+/* here are the functions to consider making extern someday */
+static Bytes* newbytes(int len);
+static Bytes* makebytes(uchar* buf, int len);
+static void freebytes(Bytes* b);
+static Bytes* catbytes(Bytes* b1, Bytes* b2);
+static Ints* newints(int len);
+static Ints* makeints(int* buf, int len);
+static void freeints(Ints* b);
+static Bits* newbits(int len);
+static Bits* makebits(uchar* buf, int len, int unusedbits);
+static void freebits(Bits* b);
+static Elist* mkel(Elem e, Elist* tail);
+static void freeelist(Elist* el);
+static int elistlen(Elist* el);
+static int is_seq(Elem* pe, Elist** pseq);
+static int is_set(Elem* pe, Elist** pset);
+static int is_int(Elem* pe, int* pint);
+static int is_bigint(Elem* pe, Bytes** pbigint);
+static int is_bitstring(Elem* pe, Bits** pbits);
+static int is_octetstring(Elem* pe, Bytes** poctets);
+static int is_oid(Elem* pe, Ints** poid);
+static int is_string(Elem* pe, char** pstring);
+static int is_time(Elem* pe, char** ptime);
+static int decode(uchar* a, int alen, Elem* pelem);
+static int encode(Elem e, Bytes** pbytes);
+static int oid_lookup(Ints* o, Ints** tab);
+static void freevalfields(Value* v);
+static mpint *asn1mpint(Elem *e);
+static void edump(Elem);
+
+#define TAG_MASK 0x1F
+#define CONSTR_MASK 0x20
+#define CLASS_MASK 0xC0
+#define MAXOBJIDLEN 20
+
+static int ber_decode(uchar** pp, uchar* pend, Elem* pelem);
+static int tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr);
+static int length_decode(uchar** pp, uchar* pend, int* plength);
+static int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval);
+static int int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint);
+static int uint7_decode(uchar** pp, uchar* pend, int* pint);
+static int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes);
+static int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist);
+static int enc(uchar** pp, Elem e, int lenonly);
+static int val_enc(uchar** pp, Elem e, int *pconstr, int lenonly);
+static void uint7_enc(uchar** pp, int num, int lenonly);
+static void int_enc(uchar** pp, int num, int unsgned, int lenonly);
+
+static void *
+emalloc(int n)
+{+ void *p;
+ if(n==0)
+ n=1;
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("out of memory");+ memset(p, 0, n);
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+static char*
+estrdup(char *s)
+{+ char *d;
+ int n;
+
+ n = strlen(s)+1;
+ d = emalloc(n);
+ memmove(d, s, n);
+ return d;
+}
+
+
+/*
+ * Decode a[0..len] as a BER encoding of an ASN1 type.
+ * The return value is one of ASN_OK, etc.
+ * Depending on the error, the returned elem may or may not
+ * be nil.
+ */
+static int
+decode(uchar* a, int alen, Elem* pelem)
+{+ uchar* p = a;
+ int err;
+
+ err = ber_decode(&p, &a[alen], pelem);
+ if(err == ASN_OK && p != &a[alen])
+ err = ASN_EVALLEN;
+ return err;
+}
+
+/*
+ * All of the following decoding routines take arguments:
+ * uchar **pp;
+ * uchar *pend;
+ * Where parsing is supposed to start at **pp, and when parsing
+ * is done, *pp is updated to point at next char to be parsed.
+ * The pend pointer is just past end of string; an error should
+ * be returned parsing hasn't finished by then.
+ *
+ * The returned int is ASN_OK if all went fine, else ASN_ESHORT, etc.
+ * The remaining argument(s) are pointers to where parsed entity goes.
+ */
+
+/* Decode an ASN1 'Elem' (tag, length, value) */
+static int
+ber_decode(uchar** pp, uchar* pend, Elem* pelem)
+{+ int err;
+ int isconstr;
+ int length;
+ Tag tag;
+ Value val;
+
+ memset(pelem, 0, sizeof(*pelem));
+ err = tag_decode(pp, pend, &tag, &isconstr);
+ if(err == ASN_OK) {+ err = length_decode(pp, pend, &length);
+ if(err == ASN_OK) {+ if(tag.class == Universal)
+ err = value_decode(pp, pend, length, tag.num, isconstr, &val);
+ else
+ err = value_decode(pp, pend, length, OCTET_STRING, 0, &val);
+ if(err == ASN_OK) {+ pelem->tag = tag;
+ pelem->val = val;
+ }
+ }
+ }
+ return err;
+}
+
+/* Decode a tag field */
+static int
+tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr)
+{+ int err;
+ int v;
+ uchar* p;
+
+ err = ASN_OK;
+ p = *pp;
+ if(pend-p >= 2) {+ v = *p++;
+ ptag->class = v&CLASS_MASK;
+ if(v&CONSTR_MASK)
+ *pisconstr = 1;
+ else
+ *pisconstr = 0;
+ v &= TAG_MASK;
+ if(v == TAG_MASK)
+ err = uint7_decode(&p, pend, &v);
+ ptag->num = v;
+ }
+ else
+ err = ASN_ESHORT;
+ *pp = p;
+ return err;
+}
+
+/* Decode a length field */
+static int
+length_decode(uchar** pp, uchar* pend, int* plength)
+{+ int err;
+ int num;
+ int v;
+ uchar* p;
+
+ err = ASN_OK;
+ num = 0;
+ p = *pp;
+ if(p < pend) {+ v = *p++;
+ if(v&0x80)
+ err = int_decode(&p, pend, v&0x7F, 1, &num);
+ else
+ num = v;
+ }
+ else
+ err = ASN_ESHORT;
+ *pp = p;
+ *plength = num;
+ return err;
+}
+
+/* Decode a value field */
+static int
+value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval)
+{+ int err;
+ Bytes* va;
+ int num;
+ int bitsunused;
+ int subids[MAXOBJIDLEN];
+ int isubid;
+ Elist* vl;
+ uchar* p;
+ uchar* pe;
+
+ err = ASN_OK;
+ p = *pp;
+ if(length == -1) { /* "indefinite" length spec */+ if(!isconstr)
+ err = ASN_EINVAL;
+ }
+ else if(p + length > pend)
+ err = ASN_EVALLEN;
+ if(err != ASN_OK)
+ return err;
+
+ switch(kind) {+ case 0:
+ /* marker for end of indefinite constructions */
+ if(length == 0)
+ pval->tag = VNull;
+ else
+ err = ASN_EINVAL;
+ break;
+
+ case BOOLEAN:
+ if(isconstr)
+ err = ASN_ECONSTR;
+ else if(length != 1)
+ err = ASN_EVALLEN;
+ else {+ pval->tag = VBool;
+ pval->u.boolval = (*p++ != 0);
+ }
+ break;
+
+ case INTEGER:
+ case ENUMERATED:
+ if(isconstr)
+ err = ASN_ECONSTR;
+ else if(length <= 4) {+ err = int_decode(&p, pend, length, 0, &num);
+ if(err == ASN_OK) {+ pval->tag = VInt;
+ pval->u.intval = num;
+ }
+ }
+ else {+ pval->tag = VBigInt;
+ pval->u.bigintval = makebytes(p, length);
+ p += length;
+ }
+ break;
+
+ case BIT_STRING:
+ pval->tag = VBitString;
+ if(isconstr) {+ if(length == -1 && p + 2 <= pend && *p == 0 && *(p+1) ==0) {+ pval->u.bitstringval = makebits(0, 0, 0);
+ p += 2;
+ }
+ else /* TODO: recurse and concat results */
+ err = ASN_EUNIMPL;
+ }
+ else {+ if(length < 2) {+ if(length == 1 && *p == 0) {+ pval->u.bitstringval = makebits(0, 0, 0);
+ p++;
+ }
+ else
+ err = ASN_EINVAL;
+ }
+ else {+ bitsunused = *p;
+ if(bitsunused > 7)
+ err = ASN_EINVAL;
+ else if(length > 0x0FFFFFFF)
+ err = ASN_ETOOBIG;
+ else {+ pval->u.bitstringval = makebits(p+1, length-1, bitsunused);
+ p += length;
+ }
+ }
+ }
+ break;
+
+ case OCTET_STRING:
+ case ObjectDescriptor:
+ err = octet_decode(&p, pend, length, isconstr, &va);
+ if(err == ASN_OK) {+ pval->tag = VOctets;
+ pval->u.octetsval = va;
+ }
+ break;
+
+ case NULLTAG:
+ if(isconstr)
+ err = ASN_ECONSTR;
+ else if(length != 0)
+ err = ASN_EVALLEN;
+ else
+ pval->tag = VNull;
+ break;
+
+ case OBJECT_ID:
+ if(isconstr)
+ err = ASN_ECONSTR;
+ else if(length == 0)
+ err = ASN_EVALLEN;
+ else {+ isubid = 0;
+ pe = p+length;
+ while(p < pe && isubid < MAXOBJIDLEN) {+ err = uint7_decode(&p, pend, &num);
+ if(err != ASN_OK)
+ break;
+ if(isubid == 0) {+ subids[isubid++] = num / 40;
+ subids[isubid++] = num % 40;
+ }
+ else
+ subids[isubid++] = num;
+ }
+ if(err == ASN_OK) {+ if(p != pe)
+ err = ASN_EVALLEN;
+ else {+ pval->tag = VObjId;
+ pval->u.objidval = makeints(subids, isubid);
+ }
+ }
+ }
+ break;
+
+ case EXTERNAL:
+ case EMBEDDED_PDV:
+ /* TODO: parse this internally */
+ if(p+length > pend)
+ err = ASN_EVALLEN;
+ else {+ pval->tag = VOther;
+ pval->u.otherval = makebytes(p, length);
+ p += length;
+ }
+ break;
+
+ case REAL:
+ /* Let the application decode */
+ if(isconstr)
+ err = ASN_ECONSTR;
+ else if(p+length > pend)
+ err = ASN_EVALLEN;
+ else {+ pval->tag = VReal;
+ pval->u.realval = makebytes(p, length);
+ p += length;
+ }
+ break;
+
+ case SEQUENCE:
+ err = seq_decode(&p, pend, length, isconstr, &vl);
+ if(err == ASN_OK) {+ pval->tag = VSeq ;
+ pval->u.seqval = vl;
+ }
+ break;
+
+ case SETOF:
+ err = seq_decode(&p, pend, length, isconstr, &vl);
+ if(err == ASN_OK) {+ pval->tag = VSet;
+ pval->u.setval = vl;
+ }
+ break;
+
+ case UTF8String:
+ case NumericString:
+ case PrintableString:
+ case TeletexString:
+ case VideotexString:
+ case IA5String:
+ case UTCTime:
+ case GeneralizedTime:
+ case GraphicString:
+ case VisibleString:
+ case GeneralString:
+ case UniversalString:
+ case BMPString:
+ err = octet_decode(&p, pend, length, isconstr, &va);
+ if(err == ASN_OK) {+ uchar *s;
+ char *d;
+ Rune r;
+ int n;
+
+ switch(kind){+ case UniversalString:
+ n = va->len / 4;
+ d = emalloc(n*UTFmax+1);
+ pval->u.stringval = d;
+ s = va->data;
+ while(n > 0){+ r = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
+ if(r == 0)
+ break;
+ n--;
+ s += 4;
+ d += runetochar(d, &r);
+ }
+ *d = 0;
+ break;
+ case BMPString:
+ n = va->len / 2;
+ d = emalloc(n*UTFmax+1);
+ pval->u.stringval = d;
+ s = va->data;
+ while(n > 0){+ r = s[0]<<8 | s[1];
+ if(r == 0)
+ break;
+ n--;
+ s += 2;
+ d += runetochar(d, &r);
+ }
+ *d = 0;
+ break;
+ default:
+ n = va->len;
+ d = emalloc(n+1);
+ pval->u.stringval = d;
+ s = va->data;
+ while(n > 0){+ if((*d = *s) == 0)
+ break;
+ n--;
+ s++;
+ d++;
+ }
+ *d = 0;
+ break;
+ }
+ if(n != 0){+ err = ASN_EINVAL;
+ free(pval->u.stringval);
+ } else
+ pval->tag = VString;
+ free(va);
+ }
+ break;
+
+ default:
+ if(p+length > pend)
+ err = ASN_EVALLEN;
+ else {+ pval->tag = VOther;
+ pval->u.otherval = makebytes(p, length);
+ p += length;
+ }
+ break;
+ }
+ *pp = p;
+ return err;
+}
+
+/*
+ * Decode an int in format where count bytes are
+ * concatenated to form value.
+ * Although ASN1 allows any size integer, we return
+ * an error if the result doesn't fit in a 32-bit int.
+ * If unsgned is not set, make sure to propagate sign bit.
+ */
+static int
+int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint)
+{+ int err;
+ int num;
+ uchar* p;
+
+ p = *pp;
+ err = ASN_OK;
+ num = 0;
+ if(p+count <= pend) {+ if((count > 4) || (unsgned && count == 4 && (*p&0x80)))
+ err = ASN_ETOOBIG;
+ else {+ if(!unsgned && count > 0 && count < 4 && (*p&0x80))
+ num = -1; /* set all bits, initially */
+ while(count--)
+ num = (num << 8)|(*p++);
+ }
+ }
+ else
+ err = ASN_ESHORT;
+ *pint = num;
+ *pp = p;
+ return err;
+}
+
+/*
+ * Decode an unsigned int in format where each
+ * byte except last has high bit set, and remaining
+ * seven bits of each byte are concatenated to form value.
+ * Although ASN1 allows any size integer, we return
+ * an error if the result doesn't fit in a 32 bit int.
+ */
+static int
+uint7_decode(uchar** pp, uchar* pend, int* pint)
+{+ int err;
+ int num;
+ int more;
+ int v;
+ uchar* p;
+
+ p = *pp;
+ err = ASN_OK;
+ num = 0;
+ more = 1;
+ while(more && p < pend) {+ v = *p++;
+ if(num&0x7F000000) {+ err = ASN_ETOOBIG;
+ break;
+ }
+ num <<= 7;
+ more = v&0x80;
+ num |= (v&0x7F);
+ }
+ if(p == pend)
+ err = ASN_ESHORT;
+ *pint = num;
+ *pp = p;
+ return err;
+}
+
+/*
+ * Decode an octet string, recursively if isconstr.
+ * We've already checked that length==-1 implies isconstr==1,
+ * and otherwise that specified length fits within (*pp..pend)
+ */
+static int
+octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes)
+{+ int err;
+ uchar* p;
+ Bytes* ans;
+ Bytes* newans;
+ uchar* pstart;
+ uchar* pold;
+ Elem elem;
+
+ err = ASN_OK;
+ p = *pp;
+ ans = nil;
+ if(length >= 0 && !isconstr) {+ ans = makebytes(p, length);
+ p += length;
+ }
+ else {+ /* constructed, either definite or indefinite length */
+ pstart = p;
+ for(;;) {+ if(length >= 0 && p >= pstart + length) {+ if(p != pstart + length)
+ err = ASN_EVALLEN;
+ break;
+ }
+ pold = p;
+ err = ber_decode(&p, pend, &elem);
+ if(err != ASN_OK)
+ break;
+ switch(elem.val.tag) {+ case VOctets:
+ newans = catbytes(ans, elem.val.u.octetsval);
+ freevalfields(&elem.val);
+ freebytes(ans);
+ ans = newans;
+ break;
+
+ case VEOC:
+ if(length == -1)
+ goto cloop_done;
+ /* no break */
+ default:
+ freevalfields(&elem.val);
+ p = pold;
+ err = ASN_EINVAL;
+ goto cloop_done;
+ }
+ }
+cloop_done:
+ if(err != ASN_OK){+ freebytes(ans);
+ ans = nil;
+ }
+ }
+ *pp = p;
+ *pbytes = ans;
+ return err;
+}
+
+/*
+ * Decode a sequence or set.
+ * We've already checked that length==-1 implies isconstr==1,
+ * and otherwise that specified length fits within (*p..pend)
+ */
+static int
+seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist)
+{+ int err;
+ uchar* p;
+ uchar* pstart;
+ uchar* pold;
+ Elist* ans;
+ Elem elem;
+ Elist* lve;
+ Elist* lveold;
+
+ err = ASN_OK;
+ ans = nil;
+ p = *pp;
+ if(!isconstr)
+ err = ASN_EPRIM;
+ else {+ /* constructed, either definite or indefinite length */
+ lve = nil;
+ pstart = p;
+ for(;;) {+ if(length >= 0 && p >= pstart + length) {+ if(p != pstart + length)
+ err = ASN_EVALLEN;
+ break;
+ }
+ pold = p;
+ err = ber_decode(&p, pend, &elem);
+ if(err != ASN_OK)
+ break;
+ if(elem.val.tag == VEOC) {+ if(length != -1) {+ p = pold;
+ err = ASN_EINVAL;
+ }
+ break;
+ }
+ else
+ lve = mkel(elem, lve);
+ }
+ if(err != ASN_OK)
+ freeelist(lve);
+ else {+ /* reverse back to original order */
+ while(lve != nil) {+ lveold = lve;
+ lve = lve->tl;
+ lveold->tl = ans;
+ ans = lveold;
+ }
+ }
+ }
+ *pp = p;
+ *pelist = ans;
+ return err;
+}
+
+/*
+ * Encode e by BER rules, putting answer in *pbytes.
+ * This is done by first calling enc with lenonly==1
+ * to get the length of the needed buffer,
+ * then allocating the buffer and using enc again to fill it up.
+ */
+static int
+encode(Elem e, Bytes** pbytes)
+{+ uchar* p;
+ Bytes* ans;
+ int err;
+ uchar uc;
+
+ p = &uc;
+ err = enc(&p, e, 1);
+ if(err == ASN_OK) {+ ans = newbytes(p-&uc);
+ p = ans->data;
+ err = enc(&p, e, 0);
+ *pbytes = ans;
+ }
+ return err;
+}
+
+/*
+ * The various enc functions take a pointer to a pointer
+ * into a buffer, and encode their entity starting there,
+ * updating the pointer afterwards.
+ * If lenonly is 1, only the pointer update is done,
+ * allowing enc to be called first to calculate the needed
+ * buffer length.
+ * If lenonly is 0, it is assumed that the answer will fit.
+ */
+
+static int
+enc(uchar** pp, Elem e, int lenonly)
+{+ int err;
+ int vlen;
+ int constr;
+ Tag tag;
+ int v;
+ int ilen;
+ uchar* p;
+ uchar* psave;
+
+ p = *pp;
+ err = val_enc(&p, e, &constr, 1);
+ if(err != ASN_OK)
+ return err;
+ vlen = p - *pp;
+ p = *pp;
+ tag = e.tag;
+ v = tag.class|constr;
+ if(tag.num < 31) {+ if(!lenonly)
+ *p = (v|tag.num);
+ p++;
+ }
+ else {+ if(!lenonly)
+ *p = (v|31);
+ p++;
+ if(tag.num < 0)
+ return ASN_EINVAL;
+ uint7_enc(&p, tag.num, lenonly);
+ }
+ if(vlen < 0x80) {+ if(!lenonly)
+ *p = vlen;
+ p++;
+ }
+ else {+ psave = p;
+ int_enc(&p, vlen, 1, 1);
+ ilen = p-psave;
+ p = psave;
+ if(!lenonly) {+ *p++ = (0x80 | ilen);
+ int_enc(&p, vlen, 1, 0);
+ }
+ else
+ p += 1 + ilen;
+ }
+ if(!lenonly)
+ val_enc(&p, e, &constr, 0);
+ else
+ p += vlen;
+ *pp = p;
+ return err;
+}
+
+static int
+val_enc(uchar** pp, Elem e, int *pconstr, int lenonly)
+{+ int err;
+ uchar* p;
+ int kind;
+ int cl;
+ int v;
+ Bytes* bb = nil;
+ Bits* bits;
+ Ints* oid;
+ int k;
+ Elist* el;
+ char* s;
+
+ p = *pp;
+ err = ASN_OK;
+ kind = e.tag.num;
+ cl = e.tag.class;
+ *pconstr = 0;
+ if(cl != Universal) {+ switch(e.val.tag) {+ case VBool:
+ kind = BOOLEAN;
+ break;
+ case VInt:
+ kind = INTEGER;
+ break;
+ case VBigInt:
+ kind = INTEGER;
+ break;
+ case VOctets:
+ kind = OCTET_STRING;
+ break;
+ case VReal:
+ kind = REAL;
+ break;
+ case VOther:
+ kind = OCTET_STRING;
+ break;
+ case VBitString:
+ kind = BIT_STRING;
+ break;
+ case VNull:
+ kind = NULLTAG;
+ break;
+ case VObjId:
+ kind = OBJECT_ID;
+ break;
+ case VString:
+ kind = UniversalString;
+ break;
+ case VSeq:
+ kind = SEQUENCE;
+ break;
+ case VSet:
+ kind = SETOF;
+ break;
+ }
+ }
+ switch(kind) {+ case BOOLEAN:
+ if(is_int(&e, &v)) {+ if(v != 0)
+ v = 255;
+ int_enc(&p, v, 1, lenonly);
+ }
+ else
+ err = ASN_EINVAL;
+ break;
+
+ case INTEGER:
+ case ENUMERATED:
+ if(is_int(&e, &v))
+ int_enc(&p, v, 0, lenonly);
+ else {+ if(is_bigint(&e, &bb)) {+ if(!lenonly)
+ memmove(p, bb->data, bb->len);
+ p += bb->len;
+ }
+ else
+ err = ASN_EINVAL;
+ }
+ break;
+
+ case BIT_STRING:
+ if(is_bitstring(&e, &bits)) {+ if(bits->len == 0) {+ if(!lenonly)
+ *p = 0;
+ p++;
+ }
+ else {+ v = bits->unusedbits;
+ if(v < 0 || v > 7)
+ err = ASN_EINVAL;
+ else {+ if(!lenonly) {+ *p = v;
+ memmove(p+1, bits->data, bits->len);
+ }
+ p += 1 + bits->len;
+ }
+ }
+ }
+ else
+ err = ASN_EINVAL;
+ break;
+
+ case OCTET_STRING:
+ case ObjectDescriptor:
+ case EXTERNAL:
+ case REAL:
+ case EMBEDDED_PDV:
+ bb = nil;
+ switch(e.val.tag) {+ case VOctets:
+ bb = e.val.u.octetsval;
+ break;
+ case VReal:
+ bb = e.val.u.realval;
+ break;
+ case VOther:
+ bb = e.val.u.otherval;
+ break;
+ }
+ if(bb != nil) {+ if(!lenonly)
+ memmove(p, bb->data, bb->len);
+ p += bb->len;
+ }
+ else
+ err = ASN_EINVAL;
+ break;
+
+ case NULLTAG:
+ break;
+
+ case OBJECT_ID:
+ if(is_oid(&e, &oid)) {+ for(k = 0; k < oid->len; k++) {+ v = oid->data[k];
+ if(k == 0) {+ v *= 40;
+ if(oid->len > 1)
+ v += oid->data[++k];
+ }
+ uint7_enc(&p, v, lenonly);
+ }
+ }
+ else
+ err = ASN_EINVAL;
+ break;
+
+ case SEQUENCE:
+ case SETOF:
+ el = nil;
+ if(e.val.tag == VSeq)
+ el = e.val.u.seqval;
+ else if(e.val.tag == VSet)
+ el = e.val.u.setval;
+ else
+ err = ASN_EINVAL;
+ if(el != nil) {+ *pconstr = CONSTR_MASK;
+ for(; el != nil; el = el->tl) {+ err = enc(&p, el->hd, lenonly);
+ if(err != ASN_OK)
+ break;
+ }
+ }
+ break;
+
+ case UTF8String:
+ case NumericString:
+ case PrintableString:
+ case TeletexString:
+ case VideotexString:
+ case IA5String:
+ case UTCTime:
+ case GeneralizedTime:
+ case GraphicString:
+ case VisibleString:
+ case GeneralString:
+ case UniversalString:
+ case BMPString:
+ if(e.val.tag == VString) {+ s = e.val.u.stringval;
+ if(s != nil) {+ v = strlen(s);
+ if(!lenonly)
+ memmove(p, s, v);
+ p += v;
+ }
+ }
+ else
+ err = ASN_EINVAL;
+ break;
+
+ default:
+ err = ASN_EINVAL;
+ }
+ *pp = p;
+ return err;
+}
+
+/*
+ * Encode num as unsigned 7 bit values with top bit 1 on all bytes
+ * except last, only putting in bytes if !lenonly.
+ */
+static void
+uint7_enc(uchar** pp, int num, int lenonly)
+{+ int n;
+ int v;
+ int k;
+ uchar* p;
+
+ p = *pp;
+ n = 1;
+ v = num >> 7;
+ while(v > 0) {+ v >>= 7;
+ n++;
+ }
+ if(lenonly)
+ p += n;
+ else {+ for(k = (n - 1)*7; k > 0; k -= 7)
+ *p++= ((num >> k)|0x80);
+ *p++ = (num&0x7F);
+ }
+ *pp = p;
+}
+
+/*
+ * Encode num as unsigned or signed integer,
+ * only putting in bytes if !lenonly.
+ * Encoding is length followed by bytes to concatenate.
+ */
+static void
+int_enc(uchar** pp, int num, int unsgned, int lenonly)
+{+ int v;
+ int n;
+ int prevv;
+ int k;
+ uchar* p;
+
+ p = *pp;
+ v = num;
+ if(v < 0)
+ v = -(v + 1);
+ n = 1;
+ prevv = v;
+ v >>= 8;
+ while(v > 0) {+ prevv = v;
+ v >>= 8;
+ n++;
+ }
+ if(!unsgned && (prevv&0x80))
+ n++;
+ if(lenonly)
+ p += n;
+ else {+ for(k = (n - 1)*8; k >= 0; k -= 8)
+ *p++ = (num >> k);
+ }
+ *pp = p;
+}
+
+static int
+ints_eq(Ints* a, Ints* b)
+{+ int alen;
+ int i;
+
+ alen = a->len;
+ if(alen != b->len)
+ return 0;
+ for(i = 0; i < alen; i++)
+ if(a->data[i] != b->data[i])
+ return 0;
+ return 1;
+}
+
+/*
+ * Look up o in tab (which must have nil entry to terminate).
+ * Return index of matching entry, or -1 if none.
+ */
+static int
+oid_lookup(Ints* o, Ints** tab)
+{+ int i;
+
+ for(i = 0; tab[i] != nil; i++)
+ if(ints_eq(o, tab[i]))
+ return i;
+ return -1;
+}
+
+/*
+ * Return true if *pe is a SEQUENCE, and set *pseq to
+ * the value of the sequence if so.
+ */
+static int
+is_seq(Elem* pe, Elist** pseq)
+{+ if(pe->tag.class == Universal && pe->tag.num == SEQUENCE && pe->val.tag == VSeq) {+ *pseq = pe->val.u.seqval;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+is_set(Elem* pe, Elist** pset)
+{+ if(pe->tag.class == Universal && pe->tag.num == SETOF && pe->val.tag == VSet) {+ *pset = pe->val.u.setval;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+is_int(Elem* pe, int* pint)
+{+ if(pe->tag.class == Universal) {+ if(pe->tag.num == INTEGER && pe->val.tag == VInt) {+ *pint = pe->val.u.intval;
+ return 1;
+ }
+ else if(pe->tag.num == BOOLEAN && pe->val.tag == VBool) {+ *pint = pe->val.u.boolval;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * for convience, all VInt's are readable via this routine,
+ * as well as all VBigInt's
+ */
+static int
+is_bigint(Elem* pe, Bytes** pbigint)
+{+ if(pe->tag.class == Universal && pe->tag.num == INTEGER && pe->val.tag == VBigInt) {+ *pbigint = pe->val.u.bigintval;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+is_bitstring(Elem* pe, Bits** pbits)
+{+ if(pe->tag.class == Universal && pe->tag.num == BIT_STRING && pe->val.tag == VBitString) {+ *pbits = pe->val.u.bitstringval;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+is_octetstring(Elem* pe, Bytes** poctets)
+{+ if(pe->tag.class == Universal && pe->tag.num == OCTET_STRING && pe->val.tag == VOctets) {+ *poctets = pe->val.u.octetsval;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+is_oid(Elem* pe, Ints** poid)
+{+ if(pe->tag.class == Universal && pe->tag.num == OBJECT_ID && pe->val.tag == VObjId) {+ *poid = pe->val.u.objidval;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+is_string(Elem* pe, char** pstring)
+{+ if(pe->tag.class == Universal) {+ switch(pe->tag.num) {+ case UTF8String:
+ case NumericString:
+ case PrintableString:
+ case TeletexString:
+ case VideotexString:
+ case IA5String:
+ case GraphicString:
+ case VisibleString:
+ case GeneralString:
+ case UniversalString:
+ case BMPString:
+ if(pe->val.tag == VString) {+ *pstring = pe->val.u.stringval;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+is_time(Elem* pe, char** ptime)
+{+ if(pe->tag.class == Universal
+ && (pe->tag.num == UTCTime || pe->tag.num == GeneralizedTime)
+ && pe->val.tag == VString) {+ *ptime = pe->val.u.stringval;
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * malloc and return a new Bytes structure capable of
+ * holding len bytes. (len >= 0)
+ */
+static Bytes*
+newbytes(int len)
+{+ Bytes* ans;
+
+ if(len < 0)
+ abort();
+ ans = emalloc(sizeof(Bytes) + len);
+ ans->len = len;
+ return ans;
+}
+
+/*
+ * newbytes(len), with data initialized from buf
+ */
+static Bytes*
+makebytes(uchar* buf, int len)
+{+ Bytes* ans;
+
+ ans = newbytes(len);
+ memmove(ans->data, buf, len);
+ return ans;
+}
+
+static void
+freebytes(Bytes* b)
+{+ free(b);
+}
+
+/*
+ * Make a new Bytes, containing bytes of b1 followed by those of b2.
+ * Either b1 or b2 or both can be nil.
+ */
+static Bytes*
+catbytes(Bytes* b1, Bytes* b2)
+{+ Bytes* ans;
+ int n;
+
+ if(b1 == nil) {+ if(b2 == nil)
+ ans = newbytes(0);
+ else
+ ans = makebytes(b2->data, b2->len);
+ }
+ else if(b2 == nil) {+ ans = makebytes(b1->data, b1->len);
+ }
+ else {+ n = b1->len + b2->len;
+ ans = newbytes(n);
+ ans->len = n;
+ memmove(ans->data, b1->data, b1->len);
+ memmove(ans->data+b1->len, b2->data, b2->len);
+ }
+ return ans;
+}
+
+/* len is number of ints */
+static Ints*
+newints(int len)
+{+ Ints* ans;
+
+ if(len < 0 || len > ((uint)-1>>1)/sizeof(int))
+ abort();
+ ans = emalloc(sizeof(Ints) + len*sizeof(int));
+ ans->len = len;
+ return ans;
+}
+
+static Ints*
+makeints(int* buf, int len)
+{+ Ints* ans;
+
+ ans = newints(len);
+ memmove(ans->data, buf, len*sizeof(int));
+ return ans;
+}
+
+static void
+freeints(Ints* b)
+{+ free(b);
+}
+
+/* len is number of bytes */
+static Bits*
+newbits(int len)
+{+ Bits* ans;
+
+ if(len < 0)
+ abort();
+ ans = emalloc(sizeof(Bits) + len);
+ ans->len = len;
+ ans->unusedbits = 0;
+ return ans;
+}
+
+static Bits*
+makebits(uchar* buf, int len, int unusedbits)
+{+ Bits* ans;
+
+ ans = newbits(len);
+ memmove(ans->data, buf, len);
+ ans->unusedbits = unusedbits;
+ return ans;
+}
+
+static void
+freebits(Bits* b)
+{+ free(b);
+}
+
+static Elist*
+mkel(Elem e, Elist* tail)
+{+ Elist* el;
+
+ el = (Elist*)emalloc(sizeof(Elist));
+ setmalloctag(el, getcallerpc(&e));
+ el->hd = e;
+ el->tl = tail;
+ return el;
+}
+
+static int
+elistlen(Elist* el)
+{+ int ans = 0;
+ while(el != nil) {+ ans++;
+ el = el->tl;
+ }
+ return ans;
+}
+
+/* Frees elist, but not fields inside values of constituent elems */
+static void
+freeelist(Elist* el)
+{+ Elist* next;
+
+ while(el != nil) {+ next = el->tl;
+ free(el);
+ el = next;
+ }
+}
+
+/* free any allocated structures inside v (recursively freeing Elists) */
+static void
+freevalfields(Value* v)
+{+ Elist* el;
+ Elist* l;
+ if(v == nil)
+ return;
+ switch(v->tag) {+ case VOctets:
+ freebytes(v->u.octetsval);
+ break;
+ case VBigInt:
+ freebytes(v->u.bigintval);
+ break;
+ case VReal:
+ freebytes(v->u.realval);
+ break;
+ case VOther:
+ freebytes(v->u.otherval);
+ break;
+ case VBitString:
+ freebits(v->u.bitstringval);
+ break;
+ case VObjId:
+ freeints(v->u.objidval);
+ break;
+ case VString:
+ free(v->u.stringval);
+ break;
+ case VSeq:
+ el = v->u.seqval;
+ for(l = el; l != nil; l = l->tl)
+ freevalfields(&l->hd.val);
+ freeelist(el);
+ break;
+ case VSet:
+ el = v->u.setval;
+ for(l = el; l != nil; l = l->tl)
+ freevalfields(&l->hd.val);
+ freeelist(el);
+ break;
+ }
+ memset(v, 0, sizeof(*v));
+}
+
+static mpint*
+asn1mpint(Elem *e)
+{+ Bytes *b;
+ int v;
+
+ if(is_int(e, &v))
+ return itomp(v, nil);
+ if(is_bigint(e, &b))
+ return betomp(b->data, b->len, nil);
+ return nil;
+}
+
+/* end of general ASN1 functions */
+
+
+
+
+
+/*=============================================================*/
+/*
+ * Decode and parse an X.509 Certificate, defined by this ASN1:
+ * Certificate ::= SEQUENCE {+ * certificateInfo CertificateInfo,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING }
+ *
+ * CertificateInfo ::= SEQUENCE {+ * version [0] INTEGER DEFAULT v1 (0),
+ * serialNumber INTEGER,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo }
+ * (version v2 has two more fields, optional unique identifiers for
+ * issuer and subject; since we ignore these anyway, we won't parse them)
+ *
+ * Validity ::= SEQUENCE {+ * notBefore UTCTime,
+ * notAfter UTCTime }
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {+ * algorithm OBJECT IDENTIFER,
+ * parameters ANY DEFINED BY ALGORITHM OPTIONAL }
+ *
+ * Name ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::= SETOF SIZE(1..MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {+ * type OBJECT IDENTIFER,
+ * value DirectoryString }
+ * (selected attributes have these Object Ids:
+ * commonName {2 5 4 3}+ * countryName {2 5 4 6}+ * localityName {2 5 4 7}+ * stateOrProvinceName {2 5 4 8}+ * organizationName {2 5 4 10}+ * organizationalUnitName {2 5 4 11}+ * )
+ *
+ * DirectoryString ::= CHOICE {+ * teletexString TeletexString,
+ * printableString PrintableString,
+ * universalString UniversalString }
+ *
+ * See rfc1423, rfc2437 for AlgorithmIdentifier, subjectPublicKeyInfo, signature.
+ *
+ * Not yet implemented:
+ * CertificateRevocationList ::= SIGNED SEQUENCE{+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * lastUpdate UTCTime,
+ * nextUpdate UTCTime,
+ * revokedCertificates
+ * SEQUENCE OF CRLEntry OPTIONAL}
+ * CRLEntry ::= SEQUENCE{+ * userCertificate SerialNumber,
+ * revocationDate UTCTime}
+ */
+
+typedef struct CertX509 {+ int serial;
+ char* issuer;
+ char* validity_start;
+ char* validity_end;
+ char* subject;
+ int publickey_alg;
+ Bits* publickey;
+ int signature_alg;
+ Bits* signature;
+ int curve;
+} CertX509;
+
+/* Algorithm object-ids */
+enum {+ ALG_rsaEncryption,
+ ALG_md2WithRSAEncryption,
+ ALG_md4WithRSAEncryption,
+ ALG_md5WithRSAEncryption,
+
+ ALG_sha1WithRSAEncryption,
+ ALG_sha1WithRSAEncryptionOiw,
+
+ ALG_sha256WithRSAEncryption,
+ ALG_sha384WithRSAEncryption,
+ ALG_sha512WithRSAEncryption,
+ ALG_sha224WithRSAEncryption,
+
+ ALG_ecPublicKey,
+ ALG_sha1WithECDSA,
+ ALG_sha256WithECDSA,
+ ALG_sha384WithECDSA,
+ ALG_sha512WithECDSA,
+
+ ALG_md5,
+ ALG_sha1,
+ ALG_sha256,
+ ALG_sha384,
+ ALG_sha512,
+ ALG_sha224,
+
+ NUMALGS
+};
+
+typedef struct Ints15 {+ int len;
+ int data[15];
+} Ints15;
+
+typedef struct DigestAlg {+ int alg;
+ DigestState* (*fun)(uchar*,ulong,uchar*,DigestState*);
+ int len;
+} DigestAlg;
+
+static DigestAlg alg_md5 = { ALG_md5, md5, MD5dlen};+static DigestAlg alg_sha1 = { ALG_sha1, sha1, SHA1dlen };+static DigestAlg alg_sha256 = { ALG_sha256, sha2_256, SHA2_256dlen };+static DigestAlg alg_sha384 = { ALG_sha384, sha2_384, SHA2_384dlen };+static DigestAlg alg_sha512 = { ALG_sha512, sha2_512, SHA2_512dlen };+static DigestAlg alg_sha224 = { ALG_sha224, sha2_224, SHA2_224dlen };+
+/* maximum length of digest output of the digest algs above */
+enum {+ MAXdlen = SHA2_512dlen,
+};
+
+static Ints15 oid_rsaEncryption = {7, 1, 2, 840, 113549, 1, 1, 1 };+
+static Ints15 oid_md2WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 2 };+static Ints15 oid_md4WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 3 };+static Ints15 oid_md5WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 4 };+static Ints15 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 };+static Ints15 oid_sha1WithRSAEncryptionOiw ={6, 1, 3, 14, 3, 2, 29 };+static Ints15 oid_sha256WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 11 };+static Ints15 oid_sha384WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 12 };+static Ints15 oid_sha512WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 13 };+static Ints15 oid_sha224WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 14 };+
+static Ints15 oid_ecPublicKey = {6, 1, 2, 840, 10045, 2, 1 };+static Ints15 oid_sha1WithECDSA = {6, 1, 2, 840, 10045, 4, 1 };+static Ints15 oid_sha256WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 2 };+static Ints15 oid_sha384WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 3 };+static Ints15 oid_sha512WithECDSA = {7, 1, 2, 840, 10045, 4, 3, 4 };+
+static Ints15 oid_md5 = {6, 1, 2, 840, 113549, 2, 5 };+static Ints15 oid_sha1 = {6, 1, 3, 14, 3, 2, 26 };+static Ints15 oid_sha256= {9, 2, 16, 840, 1, 101, 3, 4, 2, 1 };+static Ints15 oid_sha384= {9, 2, 16, 840, 1, 101, 3, 4, 2, 2 };+static Ints15 oid_sha512= {9, 2, 16, 840, 1, 101, 3, 4, 2, 3 };+static Ints15 oid_sha224= {9, 2, 16, 840, 1, 101, 3, 4, 2, 4 };+
+static Ints *alg_oid_tab[NUMALGS+1] = {+ (Ints*)&oid_rsaEncryption,
+ (Ints*)&oid_md2WithRSAEncryption,
+ (Ints*)&oid_md4WithRSAEncryption,
+ (Ints*)&oid_md5WithRSAEncryption,
+
+ (Ints*)&oid_sha1WithRSAEncryption,
+ (Ints*)&oid_sha1WithRSAEncryptionOiw,
+
+ (Ints*)&oid_sha256WithRSAEncryption,
+ (Ints*)&oid_sha384WithRSAEncryption,
+ (Ints*)&oid_sha512WithRSAEncryption,
+ (Ints*)&oid_sha224WithRSAEncryption,
+
+ (Ints*)&oid_ecPublicKey,
+ (Ints*)&oid_sha1WithECDSA,
+ (Ints*)&oid_sha256WithECDSA,
+ (Ints*)&oid_sha384WithECDSA,
+ (Ints*)&oid_sha512WithECDSA,
+
+ (Ints*)&oid_md5,
+ (Ints*)&oid_sha1,
+ (Ints*)&oid_sha256,
+ (Ints*)&oid_sha384,
+ (Ints*)&oid_sha512,
+ (Ints*)&oid_sha224,
+ nil
+};
+
+static DigestAlg *digestalg[NUMALGS+1] = {+ &alg_md5, &alg_md5, &alg_md5, &alg_md5,
+ &alg_sha1, &alg_sha1,
+ &alg_sha256, &alg_sha384, &alg_sha512, &alg_sha224,
+ &alg_sha256, &alg_sha1, &alg_sha256, &alg_sha384, &alg_sha512,
+ &alg_md5, &alg_sha1, &alg_sha256, &alg_sha384, &alg_sha512, &alg_sha224,
+ nil
+};
+
+static Bytes* encode_digest(DigestAlg *da, uchar *digest);
+
+static Ints15 oid_secp256r1 = {7, 1, 2, 840, 10045, 3, 1, 7};+static Ints15 oid_secp384r1 = {5, 1, 3, 132, 0, 34};+
+static Ints *namedcurves_oid_tab[] = {+ (Ints*)&oid_secp256r1,
+ (Ints*)&oid_secp384r1,
+ nil,
+};
+static void (*namedcurves[])(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h) = {+ secp256r1,
+ secp384r1,
+ nil,
+};
+
+static void
+freecert(CertX509* c)
+{+ if(c == nil)
+ return;
+ free(c->issuer);
+ free(c->validity_start);
+ free(c->validity_end);
+ free(c->subject);
+ freebits(c->publickey);
+ freebits(c->signature);
+ free(c);
+}
+
+/*
+ * Parse the Name ASN1 type.
+ * The sequence of RelativeDistinguishedName's gives a sort of pathname,
+ * from most general to most specific. Each element of the path can be
+ * one or more (but usually just one) attribute-value pair, such as
+ * countryName="US".
+ * We'll just form a "postal-style" address string by concatenating the elements
+ * from most specific to least specific, separated by commas.
+ * Return name-as-string (which must be freed by caller).
+ */
+static char*
+parse_name(Elem* e)
+{+ Elist* el;
+ Elem* es;
+ Elist* esetl;
+ Elem* eat;
+ Elist* eatl;
+ char* s;
+ enum { MAXPARTS = 100 };+ char* parts[MAXPARTS];
+ int i;
+ int plen;
+ char* ans = nil;
+
+ if(!is_seq(e, &el))
+ goto errret;
+ i = 0;
+ plen = 0;
+ while(el != nil) {+ es = &el->hd;
+ if(!is_set(es, &esetl))
+ goto errret;
+ while(esetl != nil) {+ eat = &esetl->hd;
+ if(!is_seq(eat, &eatl) || elistlen(eatl) != 2)
+ goto errret;
+ if(!is_string(&eatl->tl->hd, &s) || i>=MAXPARTS)
+ goto errret;
+ parts[i++] = s;
+ plen += strlen(s) + 2; /* room for ", " after */
+ esetl = esetl->tl;
+ }
+ el = el->tl;
+ }
+ if(i > 0) {+ ans = (char*)emalloc(plen);
+ *ans = '\0';
+ while(--i >= 0) {+ s = parts[i];
+ strcat(ans, s);
+ if(i > 0)
+ strcat(ans, ", ");
+ }
+ }
+
+errret:
+ return ans;
+}
+
+/*
+ * Parse an AlgorithmIdentifer ASN1 type.
+ * Look up the oid in oid_tab and return one of OID_rsaEncryption, etc..,
+ * or -1 if not found.
+ * For now, ignore parameters, since none of our algorithms need them.
+ */
+static int
+parse_alg(Elem* e)
+{+ Elist* el;
+ Ints* oid;
+
+ if(!is_seq(e, &el) || el == nil || !is_oid(&el->hd, &oid))
+ return -1;
+ return oid_lookup(oid, alg_oid_tab);
+}
+
+static int
+parse_curve(Elem* e)
+{+ Elist* el;
+ Ints* oid;
+
+ if(!is_seq(e, &el) || elistlen(el)<2 || !is_oid(&el->tl->hd, &oid))
+ return -1;
+ return oid_lookup(oid, namedcurves_oid_tab);
+}
+
+static CertX509*
+decode_cert(uchar *buf, int len)
+{+ int ok = 0;
+ int n;
+ Elem ecert;
+ Elem* ecertinfo;
+ Elem* esigalg;
+ Elem* esig;
+ Elem* eserial;
+ Elem* eissuer;
+ Elem* evalidity;
+ Elem* esubj;
+ Elem* epubkey;
+ Elist* el;
+ Elist* elcert = nil;
+ Elist* elcertinfo = nil;
+ Elist* elvalidity = nil;
+ Elist* elpubkey = nil;
+ Bits* bits = nil;
+ Bytes* b;
+ Elem* e;
+ CertX509* c = nil;
+
+ if(decode(buf, len, &ecert) != ASN_OK)
+ goto errret;
+
+ c = (CertX509*)emalloc(sizeof(CertX509));
+ c->serial = -1;
+ c->issuer = nil;
+ c->validity_start = nil;
+ c->validity_end = nil;
+ c->subject = nil;
+ c->publickey_alg = -1;
+ c->publickey = nil;
+ c->signature_alg = -1;
+ c->signature = nil;
+
+ /* Certificate */
+ if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3)
+ goto errret;
+ ecertinfo = &elcert->hd;
+ el = elcert->tl;
+ esigalg = &el->hd;
+ c->signature_alg = parse_alg(esigalg);
+ el = el->tl;
+ esig = &el->hd;
+
+ /* Certificate Info */
+ if(!is_seq(ecertinfo, &elcertinfo))
+ goto errret;
+ n = elistlen(elcertinfo);
+ if(n < 6)
+ goto errret;
+ eserial =&elcertinfo->hd;
+ el = elcertinfo->tl;
+ /* check for optional version, marked by explicit context tag 0 */
+ if(eserial->tag.class == Context && eserial->tag.num == 0) {+ eserial = &el->hd;
+ if(n < 7)
+ goto errret;
+ el = el->tl;
+ }
+
+ if(parse_alg(&el->hd) != c->signature_alg)
+ goto errret;
+ el = el->tl;
+ eissuer = &el->hd;
+ el = el->tl;
+ evalidity = &el->hd;
+ el = el->tl;
+ esubj = &el->hd;
+ el = el->tl;
+ epubkey = &el->hd;
+ if(!is_int(eserial, &c->serial)) {+ if(!is_bigint(eserial, &b))
+ goto errret;
+ c->serial = -1; /* else we have to change cert struct */
+ }
+ c->issuer = parse_name(eissuer);
+ if(c->issuer == nil)
+ goto errret;
+ /* Validity */
+ if(!is_seq(evalidity, &elvalidity))
+ goto errret;
+ if(elistlen(elvalidity) != 2)
+ goto errret;
+ e = &elvalidity->hd;
+ if(!is_time(e, &c->validity_start))
+ goto errret;
+ e->val.u.stringval = nil; /* string ownership transfer */
+ e = &elvalidity->tl->hd;
+ if(!is_time(e, &c->validity_end))
+ goto errret;
+ e->val.u.stringval = nil; /* string ownership transfer */
+
+ /* resume CertificateInfo */
+ c->subject = parse_name(esubj);
+ if(c->subject == nil)
+ goto errret;
+
+ /* SubjectPublicKeyInfo */
+ if(!is_seq(epubkey, &elpubkey))
+ goto errret;
+ if(elistlen(elpubkey) != 2)
+ goto errret;
+
+ c->publickey_alg = parse_alg(&elpubkey->hd);
+ if(c->publickey_alg < 0)
+ goto errret;
+ c->curve = -1;
+ if(c->publickey_alg == ALG_ecPublicKey){+ c->curve = parse_curve(&elpubkey->hd);
+ if(c->curve < 0)
+ goto errret;
+ }
+ elpubkey = elpubkey->tl;
+ if(!is_bitstring(&elpubkey->hd, &bits))
+ goto errret;
+ elpubkey->hd.val.u.bitstringval = nil; /* transfer ownership */
+ c->publickey = bits;
+
+ /*resume Certificate */
+ if(c->signature_alg < 0)
+ goto errret;
+ if(!is_bitstring(esig, &bits))
+ goto errret;
+ esig->val.u.bitstringval = nil; /* transfer ownership */
+ c->signature = bits;
+ ok = 1;
+
+errret:
+ freevalfields(&ecert.val); /* recurses through lists, too */
+ if(!ok){+ freecert(c);
+ c = nil;
+ }
+ return c;
+}
+
+/*
+ * RSAPublickKey ::= SEQUENCE {+ * modulus INTEGER,
+ * publicExponent INTEGER
+ * }
+ */
+RSApub*
+asn1toRSApub(uchar *buf, int len)
+{+ Elem e;
+ Elist *el;
+ RSApub* key;
+
+ key = nil;
+ if(decode(buf, len, &e) != ASN_OK)
+ goto errret;
+ if(!is_seq(&e, &el) || elistlen(el) != 2)
+ goto errret;
+
+ key = rsapuballoc();
+ if((key->n = asn1mpint(&el->hd)) == nil)
+ goto errret;
+ el = el->tl;
+ if((key->ek = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ freevalfields(&e.val);
+ return key;
+errret:
+ freevalfields(&e.val);
+ rsapubfree(key);
+ return nil;
+
+}
+
+/*
+ * RSAPrivateKey ::= SEQUENCE {+ * version Version,
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * privateExponent INTEGER, -- d
+ * prime1 INTEGER, -- p
+ * prime2 INTEGER, -- q
+ * exponent1 INTEGER, -- d mod (p-1)
+ * exponent2 INTEGER, -- d mod (q-1)
+ * coefficient INTEGER -- (inverse of q) mod p }
+ */
+RSApriv*
+asn1toRSApriv(uchar *buf, int len)
+{+ int version;
+ Elem e;
+ Elist *el;
+ Bytes *b;
+ RSApriv* key = nil;
+
+ if(decode(buf, len, &e) != ASN_OK)
+ goto errret;
+ if(!is_seq(&e, &el))
+ goto errret;
+
+ if(!is_int(&el->hd, &version) || version != 0)
+ goto errret;
+
+ if(elistlen(el) != 9){+ if(elistlen(el) == 3
+ && parse_alg(&el->tl->hd) == ALG_rsaEncryption
+ && is_octetstring(&el->tl->tl->hd, &b)){+ key = asn1toRSApriv(b->data, b->len);
+ if(key != nil)
+ goto done;
+ }
+ goto errret;
+ }
+
+ key = rsaprivalloc();
+ el = el->tl;
+ if((key->pub.n = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->pub.ek = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->dk = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->q = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->p = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->kq = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->kp = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+ el = el->tl;
+ if((key->c2 = asn1mpint(&el->hd)) == nil)
+ goto errret;
+
+done:
+ freevalfields(&e.val);
+ return key;
+errret:
+ freevalfields(&e.val);
+ rsaprivfree(key);
+ return nil;
+}
+
+/*
+ * digest(CertificateInfo)
+ * Our ASN.1 library doesn't return pointers into the original
+ * data array, so we need to do a little hand decoding.
+ */
+static int
+digest_certinfo(uchar *cert, int ncert, DigestAlg *da, uchar *digest)
+{+ uchar *info, *p, *pend;
+ int isconstr, length;
+ Tag tag;
+ Elem elem;
+
+ p = cert;
+ pend = cert + ncert;
+ if(tag_decode(&p, pend, &tag, &isconstr) != ASN_OK ||
+ tag.class != Universal || tag.num != SEQUENCE ||
+ length_decode(&p, pend, &length) != ASN_OK ||
+ p+length > pend ||
+ p+length < p)
+ return -1;
+ info = p;
+ if(ber_decode(&p, pend, &elem) != ASN_OK)
+ return -1;
+ freevalfields(&elem.val);
+ if(elem.tag.num != SEQUENCE)
+ return -1;
+ (*da->fun)(info, p - info, digest, nil);
+ return da->len;
+}
+
+mpint*
+pkcs1padbuf(uchar *buf, int len, mpint *modulus, int blocktype)
+{+ int i, n = (mpsignif(modulus)-1)/8;
+ int pad = n - 2 - len;
+ uchar *p;
+ mpint *mp;
+
+ if(pad < 8){+ werrstr("rsa modulus too small");+ return nil;
+ }
+ if((p = malloc(n)) == nil)
+ return nil;
+ p[0] = blocktype;
+ switch(blocktype){+ default:
+ case 1:
+ memset(p+1, 0xFF, pad);
+ break;
+ case 2:
+ for(i=1; i <= pad; i++)
+ p[i] = 1 + nfastrand(255);
+ break;
+ }
+ p[1+pad] = 0;
+ memmove(p+2+pad, buf, len);
+ mp = betomp(p, n, nil);
+ free(p);
+ return mp;
+}
+
+int
+pkcs1unpadbuf(uchar *buf, int len, mpint *modulus, int blocktype)
+{+ uchar *p = buf + 1, *e = buf + len;
+
+ if(len < 1 || len != (mpsignif(modulus)-1)/8 || buf[0] != blocktype)
+ return -1;
+ switch(blocktype){+ default:
+ case 1:
+ while(p < e && *p == 0xFF)
+ p++;
+ break;
+ case 2:
+ while(p < e && *p != 0x00)
+ p++;
+ break;
+ }
+ if(p - buf <= 8 || p >= e || *p++ != 0x00)
+ return -1;
+ memmove(buf, p, len = e - p);
+ return len;
+}
+
+static char Ebadsig[] = "bad signature";
+
+char*
+X509rsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, RSApub *pk)
+{+ mpint *x, *y;
+ DigestAlg **dp;
+ Bytes *digest;
+ uchar *buf;
+ int len;
+ char *err;
+
+ x = betomp(sig, siglen, nil);
+ y = rsaencrypt(pk, x, nil);
+ mpfree(x);
+ len = mptobe(y, nil, 0, &buf);
+ mpfree(y);
+
+ err = Ebadsig;
+ len = pkcs1unpadbuf(buf, len, pk->n, 1);
+ if(len == edigestlen && tsmemcmp(buf, edigest, edigestlen) == 0)
+ err = nil;
+ for(dp = digestalg; err != nil && *dp != nil; dp++){+ if((*dp)->len != edigestlen)
+ continue;
+ digest = encode_digest(*dp, edigest);
+ if(digest->len == len && tsmemcmp(digest->data, buf, len) == 0)
+ err = nil;
+ freebytes(digest);
+ }
+ free(buf);
+ return err;
+}
+
+char*
+X509ecdsaverifydigest(uchar *sig, int siglen, uchar *edigest, int edigestlen, ECdomain *dom, ECpub *pub)
+{+ Elem e;
+ Elist *el;
+ mpint *r, *s;
+ char *err;
+
+ r = s = nil;
+ err = Ebadsig;
+ if(decode(sig, siglen, &e) != ASN_OK)
+ goto end;
+ if(!is_seq(&e, &el) || elistlen(el) != 2)
+ goto end;
+ r = asn1mpint(&el->hd);
+ if(r == nil)
+ goto end;
+ el = el->tl;
+ s = asn1mpint(&el->hd);
+ if(s == nil)
+ goto end;
+ if(ecdsaverify(dom, pub, edigest, edigestlen, r, s))
+ err = nil;
+end:
+ freevalfields(&e.val);
+ mpfree(s);
+ mpfree(r);
+ return err;
+}
+
+static void
+copysubject(char *name, int nname, char *subject)
+{+ char *e;
+
+ if(name == nil)
+ return;
+ memset(name, 0, nname);
+ if(subject == nil)
+ return;
+ strncpy(name, subject, nname-1);
+ e = strchr(name, ',');
+ if(e != nil)
+ *e = 0; /* take just CN part of Distinguished Name */
+}
+
+ECpub*
+X509toECpub(uchar *cert, int ncert, char *name, int nname, ECdomain *dom)
+{+ CertX509 *c;
+ ECpub *pub;
+
+ c = decode_cert(cert, ncert);
+ if(c == nil)
+ return nil;
+ copysubject(name, nname, c->subject);
+ pub = nil;
+ if(c->publickey_alg == ALG_ecPublicKey){+ ecdominit(dom, namedcurves[c->curve]);
+ pub = ecdecodepub(dom, c->publickey->data, c->publickey->len);
+ if(pub == nil)
+ ecdomfree(dom);
+ }
+ freecert(c);
+ return pub;
+}
+
+char*
+X509ecdsaverify(uchar *cert, int ncert, ECdomain *dom, ECpub *pk)
+{+ char *e;
+ CertX509 *c;
+ int digestlen;
+ uchar digest[MAXdlen];
+
+ c = decode_cert(cert, ncert);
+ if(c == nil)
+ return "cannot decode cert";
+ digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
+ if(digestlen <= 0){+ freecert(c);
+ return "cannot decode certinfo";
+ }
+ e = X509ecdsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, dom, pk);
+ freecert(c);
+ return e;
+}
+
+RSApub*
+X509toRSApub(uchar *cert, int ncert, char *name, int nname)
+{+ CertX509 *c;
+ RSApub *pub;
+
+ c = decode_cert(cert, ncert);
+ if(c == nil)
+ return nil;
+ copysubject(name, nname, c->subject);
+ pub = nil;
+ if(c->publickey_alg == ALG_rsaEncryption)
+ pub = asn1toRSApub(c->publickey->data, c->publickey->len);
+ freecert(c);
+ return pub;
+}
+
+char*
+X509rsaverify(uchar *cert, int ncert, RSApub *pk)
+{+ char *e;
+ CertX509 *c;
+ int digestlen;
+ uchar digest[MAXdlen];
+
+ c = decode_cert(cert, ncert);
+ if(c == nil)
+ return "cannot decode cert";
+ digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
+ if(digestlen <= 0){+ freecert(c);
+ return "cannot decode certinfo";
+ }
+ e = X509rsaverifydigest(c->signature->data, c->signature->len, digest, digestlen, pk);
+ freecert(c);
+ return e;
+}
+
+/* ------- Elem constructors ---------- */
+static Elem
+Null(void)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = NULLTAG;
+ e.val.tag = VNull;
+ return e;
+}
+
+static Elem
+mkint(int j)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = INTEGER;
+ e.val.tag = VInt;
+ e.val.u.intval = j;
+ return e;
+}
+
+static Elem
+mkbigint(mpint *p)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = INTEGER;
+ e.val.tag = VBigInt;
+ e.val.u.bigintval = newbytes((mpsignif(p)+8)/8);
+ if(p->sign < 0){+ mpint *s = mpnew(e.val.u.bigintval->len*8+1);
+ mpleft(mpone, e.val.u.bigintval->len*8, s);
+ mpadd(p, s, s);
+ mptober(s, e.val.u.bigintval->data, e.val.u.bigintval->len);
+ mpfree(s);
+ } else {+ mptober(p, e.val.u.bigintval->data, e.val.u.bigintval->len);
+ }
+ return e;
+}
+
+static int
+printable(char *s)
+{+ int c;
+
+ while((c = (uchar)*s++) != 0){+ if((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || strchr("'=()+,-./:? ", c) != nil)+ continue;
+ return 0;
+ }
+ return 1;
+}
+
+#define DirectoryString 0
+
+static Elem
+mkstring(char *s, int t)
+{+ Elem e;
+
+ if(t == DirectoryString)
+ t = printable(s) ? PrintableString : UTF8String;
+ e.tag.class = Universal;
+ e.tag.num = t;
+ e.val.tag = VString;
+ e.val.u.stringval = estrdup(s);
+ return e;
+}
+
+static Elem
+mkoctet(uchar *buf, int buflen)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = OCTET_STRING;
+ e.val.tag = VOctets;
+ e.val.u.octetsval = makebytes(buf, buflen);
+ return e;
+}
+
+static Elem
+mkbits(uchar *buf, int buflen)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = BIT_STRING;
+ e.val.tag = VBitString;
+ e.val.u.bitstringval = makebits(buf, buflen, 0);
+ return e;
+}
+
+static Elem
+mkutc(long t)
+{+ Elem e;
+ char utc[50];
+ Tm *tm = gmtime(t);
+
+ e.tag.class = Universal;
+ e.tag.num = UTCTime;
+ e.val.tag = VString;
+ snprint(utc, sizeof(utc), "%.2d%.2d%.2d%.2d%.2d%.2dZ",
+ tm->year % 100, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);
+ e.val.u.stringval = estrdup(utc);
+ return e;
+}
+
+static Elem
+mkoid(Ints *oid)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = OBJECT_ID;
+ e.val.tag = VObjId;
+ e.val.u.objidval = makeints(oid->data, oid->len);
+ return e;
+}
+
+static Elem
+mkseq(Elist *el)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = SEQUENCE;
+ e.val.tag = VSeq;
+ e.val.u.seqval = el;
+ return e;
+}
+
+static Elem
+mkset(Elist *el)
+{+ Elem e;
+
+ e.tag.class = Universal;
+ e.tag.num = SETOF;
+ e.val.tag = VSet;
+ e.val.u.setval = el;
+ return e;
+}
+
+static Elem
+mkalg(int alg)
+{+ return mkseq(mkel(mkoid(alg_oid_tab[alg]), mkel(Null(), nil)));
+}
+
+typedef struct Ints7pref {+ int len;
+ int data[7];
+ char prefix[4];
+ int stype;
+} Ints7pref;
+Ints7pref DN_oid[] = {+ {4, 2, 5, 4, 6, 0, 0, 0, "C=", PrintableString},+ {4, 2, 5, 4, 8, 0, 0, 0, "ST=",DirectoryString},+ {4, 2, 5, 4, 7, 0, 0, 0, "L=", DirectoryString},+ {4, 2, 5, 4, 10, 0, 0, 0, "O=", DirectoryString},+ {4, 2, 5, 4, 11, 0, 0, 0, "OU=",DirectoryString},+ {4, 2, 5, 4, 3, 0, 0, 0, "CN=",DirectoryString},+ {7, 1,2,840,113549,1,9,1, "E=", IA5String},+ {7, 0,9,2342,19200300,100,1,25, "DC=",IA5String},+};
+
+static Elem
+mkname(Ints7pref *oid, char *subj)
+{+ return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj, oid->stype), nil))), nil));
+}
+
+static Elem
+mkDN(char *dn)
+{+ int i, j, nf;
+ char *f[20], *prefix, *d2 = estrdup(dn);
+ Elist* el = nil;
+
+ nf = tokenize(d2, f, nelem(f));
+ for(i=nf-1; i>=0; i--){+ for(j=0; j<nelem(DN_oid); j++){+ prefix = DN_oid[j].prefix;
+ if(strncmp(f[i],prefix,strlen(prefix))==0){+ el = mkel(mkname(&DN_oid[j],f[i]+strlen(prefix)), el);
+ break;
+ }
+ }
+ }
+ free(d2);
+ return mkseq(el);
+}
+
+/*
+ * DigestInfo ::= SEQUENCE {+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING }
+ */
+static Bytes*
+encode_digest(DigestAlg *da, uchar *digest)
+{+ Bytes *b = nil;
+ Elem e = mkseq(
+ mkel(mkalg(da->alg),
+ mkel(mkoctet(digest, da->len),
+ nil)));
+ encode(e, &b);
+ freevalfields(&e.val);
+ return b;
+}
+
+int
+asn1encodedigest(DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest, uchar *buf, int len)
+{+ Bytes *bytes;
+ DigestAlg **dp;
+
+ for(dp = digestalg; *dp != nil; dp++){+ if((*dp)->fun != fun)
+ continue;
+ bytes = encode_digest(*dp, digest);
+ if(bytes == nil)
+ break;
+ if(bytes->len > len){+ freebytes(bytes);
+ break;
+ }
+ len = bytes->len;
+ memmove(buf, bytes->data, len);
+ freebytes(bytes);
+ return len;
+ }
+ return -1;
+}
+
+static Elem
+mkcont(Elem e, int num)
+{+ e = mkseq(mkel(e, nil));
+ e.tag.class = Context;
+ e.tag.num = num;
+ return e;
+}
+
+static Elem
+mkaltname(char *s)
+{+ Elem e;
+ int i;
+
+ for(i=0; i<nelem(DN_oid); i++){+ if(strstr(s, DN_oid[i].prefix) != nil)
+ return mkcont(mkDN(s), 4); /* DN */
+ }
+ e = mkstring(s, IA5String);
+ e.tag.class = Context;
+ e.tag.num = strchr(s, '@') != nil ? 1 : 2; /* email : DNS */
+ return e;
+}
+
+static Elist*
+mkaltnames(char *alts)
+{+ Elist *el;
+ char *s, *p;
+
+ if(alts == nil)
+ return nil;
+
+ el = nil;
+ alts = estrdup(alts);
+ for(s = alts; s != nil; s = p){+ while(*s == ' ')
+ s++;
+ if(*s == '\0')
+ break;
+ if((p = strchr(s, ',')) != nil)
+ *p++ = 0;
+ el = mkel(mkaltname(s), el);
+ }
+ free(alts);
+ return el;
+}
+
+static Elist*
+mkextel(Elem e, Ints *oid, Elist *el)
+{+ Bytes *b = nil;
+
+ if(encode(e, &b) == ASN_OK){+ el = mkel(mkseq(
+ mkel(mkoid(oid),
+ mkel(mkoctet(b->data, b->len),
+ nil))), el);
+ freebytes(b);
+ }
+ freevalfields(&e.val);
+ return el;
+}
+
+static Ints15 oid_subjectAltName = {4, 2, 5, 29, 17 };+static Ints15 oid_extensionRequest = { 7, 1, 2, 840, 113549, 1, 9, 14};+
+static Elist*
+mkextensions(char *alts, int req)
+{+ Elist *sl, *xl;
+
+ xl = nil;
+ if((sl = mkaltnames(alts)) != nil)
+ xl = mkextel(mkseq(sl), (Ints*)&oid_subjectAltName, xl);
+ if(xl != nil){+ if(req) return mkel(mkcont(mkseq(
+ mkel(mkoid((Ints*)&oid_extensionRequest),
+ mkel(mkset(mkel(mkseq(xl), nil)), nil))), 0), nil);
+ return mkel(mkcont(mkseq(xl), 3), nil);
+ }
+ return nil;
+}
+
+static char*
+splitalts(char *s)
+{+ int q;
+
+ for(q = 0; *s != '\0'; s++){+ if(*s == '\'')
+ q ^= 1;
+ else if(q == 0 && *s == ','){+ *s++ = 0;
+ return s;
+ }
+ }
+ return nil;
+}
+
+static Bytes*
+encode_rsapubkey(RSApub *pk)
+{+ Bytes *b = nil;
+ Elem e = mkseq(
+ mkel(mkbigint(pk->n),
+ mkel(mpsignif(pk->ek)<32 ? mkint(mptoi(pk->ek)) : mkbigint(pk->ek),
+ nil)));
+ encode(e, &b);
+ freevalfields(&e.val);
+ return b;
+}
+
+int
+asn1encodeRSApub(RSApub *pk, uchar *buf, int len)
+{+ Bytes *b = encode_rsapubkey(pk);
+ if(b == nil)
+ return -1;
+ if(b->len > len){+ freebytes(b);
+ werrstr("buffer too small");+ return -1;
+ }
+ memmove(buf, b->data, len = b->len);
+ freebytes(b);
+ return len;
+}
+
+uchar*
+X509rsagen(RSApriv *priv, char *subj, ulong valid[2], int *certlen)
+{+ int serial = 0, sigalg = ALG_sha256WithRSAEncryption;
+ uchar *cert = nil;
+ Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
+ Elem e, certinfo;
+ DigestAlg *da;
+ uchar digest[MAXdlen], *buf;
+ int buflen;
+ mpint *pkcs1;
+ char *alts;
+
+ if((pkbytes = encode_rsapubkey(&priv->pub)) == nil)
+ return nil;
+
+ subj = estrdup(subj);
+ alts = splitalts(subj);
+
+ e = mkseq(
+ mkel(mkcont(mkint(2), 0),
+ mkel(mkint(serial),
+ mkel(mkalg(sigalg),
+ mkel(mkDN(subj),
+ mkel(mkseq(
+ mkel(mkutc(valid[0]),
+ mkel(mkutc(valid[1]),
+ nil))),
+ mkel(mkDN(subj),
+ mkel(mkseq(
+ mkel(mkalg(ALG_rsaEncryption),
+ mkel(mkbits(pkbytes->data, pkbytes->len),
+ nil))),
+ mkextensions(alts, 0)))))))));
+ freebytes(pkbytes);
+ if(encode(e, &certinfobytes) != ASN_OK)
+ goto errret;
+
+ da = digestalg[sigalg];
+ (*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
+ freebytes(certinfobytes);
+ certinfo = e;
+
+ sigbytes = encode_digest(da, digest);
+ if(sigbytes == nil)
+ goto errret;
+ pkcs1 = pkcs1padbuf(sigbytes->data, sigbytes->len, priv->pub.n, 1);
+ freebytes(sigbytes);
+ if(pkcs1 == nil)
+ goto errret;
+
+ rsadecrypt(priv, pkcs1, pkcs1);
+ buflen = mptobe(pkcs1, nil, 0, &buf);
+ mpfree(pkcs1);
+ e = mkseq(
+ mkel(certinfo,
+ mkel(mkalg(sigalg),
+ mkel(mkbits(buf, buflen),
+ nil))));
+ free(buf);
+ if(encode(e, &certbytes) != ASN_OK)
+ goto errret;
+ if(certlen != nil)
+ *certlen = certbytes->len;
+ cert = (uchar*)certbytes;
+ memmove(cert, certbytes->data, certbytes->len);
+errret:
+ freevalfields(&e.val);
+ free(subj);
+ return cert;
+}
+
+uchar*
+X509rsareq(RSApriv *priv, char *subj, int *certlen)
+{+ /* RFC 2314, PKCS #10 Certification Request Syntax */
+ int version = 0, sigalg = ALG_sha256WithRSAEncryption;
+ uchar *cert = nil;
+ Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes;
+ Elem e, certinfo;
+ DigestAlg *da;
+ uchar digest[MAXdlen], *buf;
+ int buflen;
+ mpint *pkcs1;
+ char *alts;
+
+ if((pkbytes = encode_rsapubkey(&priv->pub)) == nil)
+ return nil;
+
+ subj = estrdup(subj);
+ alts = splitalts(subj);
+
+ e = mkseq(
+ mkel(mkint(version),
+ mkel(mkDN(subj),
+ mkel(mkseq(
+ mkel(mkalg(ALG_rsaEncryption),
+ mkel(mkbits(pkbytes->data, pkbytes->len),
+ nil))),
+ mkextensions(alts, 1)))));
+ freebytes(pkbytes);
+ if(encode(e, &certinfobytes) != ASN_OK)
+ goto errret;
+ da = digestalg[sigalg];
+ (*da->fun)(certinfobytes->data, certinfobytes->len, digest, 0);
+ freebytes(certinfobytes);
+ certinfo = e;
+
+ sigbytes = encode_digest(da, digest);
+ if(sigbytes == nil)
+ goto errret;
+ pkcs1 = pkcs1padbuf(sigbytes->data, sigbytes->len, priv->pub.n, 1);
+ freebytes(sigbytes);
+ if(pkcs1 == nil)
+ goto errret;
+
+ rsadecrypt(priv, pkcs1, pkcs1);
+ buflen = mptobe(pkcs1, nil, 0, &buf);
+ mpfree(pkcs1);
+ e = mkseq(
+ mkel(certinfo,
+ mkel(mkalg(sigalg),
+ mkel(mkbits(buf, buflen),
+ nil))));
+ free(buf);
+ if(encode(e, &certbytes) != ASN_OK)
+ goto errret;
+ if(certlen != nil)
+ *certlen = certbytes->len;
+ cert = (uchar*)certbytes;
+ memmove(cert, certbytes->data, certbytes->len);
+errret:
+ freevalfields(&e.val);
+ free(subj);
+ return cert;
+}
+
+static void
+digestSPKI(int alg, uchar *pubkey, int npubkey, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
+{+ Bytes *b = nil;
+ Elem e = mkseq(mkel(mkalg(alg), mkel(mkbits(pubkey, npubkey), nil)));
+ encode(e, &b);
+ freevalfields(&e.val);
+ (*fun)(b->data, b->len, digest, nil);
+ freebytes(b);
+}
+
+int
+X509digestSPKI(uchar *cert, int ncert, DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*), uchar *digest)
+{+ CertX509 *c;
+
+ c = decode_cert(cert, ncert);
+ if(c == nil){+ werrstr("cannot decode cert");+ return -1;
+ }
+ digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, fun, digest);
+ freecert(c);
+ return 0;
+}
+
+static char*
+tagdump(Tag tag)
+{+ static char buf[32];
+
+ if(tag.class != Universal){+ snprint(buf, sizeof(buf), "class%d,num%d", tag.class, tag.num);
+ return buf;
+ }
+ switch(tag.num){+ case BOOLEAN: return "BOOLEAN";
+ case INTEGER: return "INTEGER";
+ case BIT_STRING: return "BIT STRING";
+ case OCTET_STRING: return "OCTET STRING";
+ case NULLTAG: return "NULLTAG";
+ case OBJECT_ID: return "OID";
+ case ObjectDescriptor: return "OBJECT_DES";
+ case EXTERNAL: return "EXTERNAL";
+ case REAL: return "REAL";
+ case ENUMERATED: return "ENUMERATED";
+ case EMBEDDED_PDV: return "EMBEDDED PDV";
+ case SEQUENCE: return "SEQUENCE";
+ case SETOF: return "SETOF";
+ case UTF8String: return "UTF8String";
+ case NumericString: return "NumericString";
+ case PrintableString: return "PrintableString";
+ case TeletexString: return "TeletexString";
+ case VideotexString: return "VideotexString";
+ case IA5String: return "IA5String";
+ case UTCTime: return "UTCTime";
+ case GeneralizedTime: return "GeneralizedTime";
+ case GraphicString: return "GraphicString";
+ case VisibleString: return "VisibleString";
+ case GeneralString: return "GeneralString";
+ case UniversalString: return "UniversalString";
+ case BMPString: return "BMPString";
+ default:
+ snprint(buf, sizeof(buf), "Universal,num%d", tag.num);
+ return buf;
+ }
+}
+
+static void
+edump(Elem e)
+{+ Value v;
+ Elist *el;
+ int i;
+
+ print("%s{", tagdump(e.tag));+ v = e.val;
+ switch(v.tag){+ case VBool: print("Bool %d",v.u.boolval); break;+ case VInt: print("Int %d",v.u.intval); break;+ case VOctets: print("Octets[%d] %.2x%.2x...",v.u.octetsval->len,v.u.octetsval->data[0],v.u.octetsval->data[1]); break;+ case VBigInt: print("BigInt[%d] %.2x%.2x...",v.u.bigintval->len,v.u.bigintval->data[0],v.u.bigintval->data[1]); break;+ case VReal: print("Real..."); break;+ case VOther: print("Other..."); break;+ case VBitString: print("BitString[%d]...", v.u.bitstringval->len*8 - v.u.bitstringval->unusedbits); break;+ case VNull: print("Null"); break;+ case VEOC: print("EOC..."); break;+ case VObjId: print("ObjId");+ for(i = 0; i<v.u.objidval->len; i++)
+ print(" %d", v.u.objidval->data[i]);+ break;
+ case VString: print("String \"%s\"",v.u.stringval); break;+ case VSeq: print("Seq\n");+ for(el = v.u.seqval; el!=nil; el = el->tl)
+ edump(el->hd);
+ break;
+ case VSet: print("Set\n");+ for(el = v.u.setval; el!=nil; el = el->tl)
+ edump(el->hd);
+ break;
+ }
+ print("}\n");+}
+
+void
+asn1dump(uchar *der, int len)
+{+ Elem e;
+
+ if(decode(der, len, &e) != ASN_OK){+ print("didn't parse\n");+ exits("didn't parse");+ }
+ edump(e);
+}
+
+void
+X509dump(uchar *cert, int ncert)
+{+ char *e;
+ CertX509 *c;
+ RSApub *rsapub;
+ ECpub *ecpub;
+ ECdomain ecdom;
+ int digestlen;
+ uchar digest[MAXdlen];
+
+ print("begin X509dump\n");+ c = decode_cert(cert, ncert);
+ if(c == nil){+ print("cannot decode cert\n");+ return;
+ }
+
+ digestlen = digest_certinfo(cert, ncert, digestalg[c->signature_alg], digest);
+ if(digestlen <= 0){+ freecert(c);
+ print("cannot decode certinfo\n");+ return;
+ }
+
+ print("serial %d\n", c->serial);+ print("issuer %s\n", c->issuer);+ print("validity %s %s\n", c->validity_start, c->validity_end);+ print("subject %s\n", c->subject);+ print("sigalg=%d digest=%.*H\n", c->signature_alg, digestlen, digest);+ print("publickey_alg=%d pubkey[%d] %.*H\n", c->publickey_alg, c->publickey->len,+ c->publickey->len, c->publickey->data);
+
+ switch(c->publickey_alg){+ case ALG_rsaEncryption:
+ rsapub = asn1toRSApub(c->publickey->data, c->publickey->len);
+ if(rsapub != nil){+ print("rsa pubkey e=%B n(%d)=%B\n", rsapub->ek, mpsignif(rsapub->n), rsapub->n);+ e = X509rsaverifydigest(c->signature->data, c->signature->len,
+ digest, digestlen, rsapub);
+ if(e==nil)
+ e = "nil (meaning ok)";
+ print("self-signed X509rsaverifydigest returns: %s\n", e);+ rsapubfree(rsapub);
+ }
+ break;
+ case ALG_ecPublicKey:
+ ecdominit(&ecdom, namedcurves[c->curve]);
+ ecpub = ecdecodepub(&ecdom, c->publickey->data, c->publickey->len);
+ if(ecpub != nil){+ e = X509ecdsaverifydigest(c->signature->data, c->signature->len,
+ digest, digestlen, &ecdom, ecpub);
+ if(e==nil)
+ e = "nil (meaning ok)";
+ print("self-signed X509ecdsaverifydigest returns: %s\n", e);+ ecpubfree(ecpub);
+ }
+ ecdomfree(&ecdom);
+ break;
+ }
+
+ digestSPKI(c->publickey_alg, c->publickey->data, c->publickey->len, sha2_256, digest);
+ print("publickey_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);+
+ sha2_256(cert, ncert, digest, nil);
+ print("cert_thumbprint sha256=%.*[\n", SHA2_256dlen, digest);+
+ sha1(cert, ncert, digest, nil);
+ print("cert_thumbprint sha1=%.*H\n", SHA1dlen, digest);+
+ freecert(c);
+ print("end X509dump\n");+}
--- a/main.c
+++ b/main.c
@@ -79,8 +79,8 @@
{extern ulong kerndate;
char *path, *file;
- int debug = 0;
+ debug = 0;
path = nil;
kerndate = seconds();
eve = getuser();
@@ -115,15 +115,15 @@
panic("bind #I: %r"); if(bind("#U", "/root", MREPL|MCREATE) < 0) panic("bind #U: %r");+ //if(bind("#P", "/proc", MBEFORE) < 0)+ // panic("bind #P: %r");+ bind("#A", "/dev", MAFTER);+ bind("#N", "/dev", MAFTER); bind("#C", "/", MAFTER);- //bind("#P", "/proc", MBEFORE); if(bind("/root", "/", MAFTER) < 0) panic("bind /root: %r");- //if(bind("/dev/fd", "/fd", MAFTER) < 0)- // panic("bind /fd: %r");-
if(path != nil)
bind(path, "/bin", MAFTER);
@@ -137,10 +137,17 @@
free(file);
}
+ if(open("/dev/cons", OREAD) != 0)+ panic("open0: %r");+ if(open("/dev/cons", OWRITE) != 1)+ panic("open1: %r");+ if(open("/dev/cons", OWRITE) != 2)+ panic("open2: %r");+
notify(notehandler);
for(;;) {- if(debug)
- dump();
+// if(debug)
+// dump();
step();
while((up->notein - up->noteout) % NNOTE) {donote(up->notes[up->noteout % NNOTE], 0);
--
⑨