xref: /plan9-contrib/sys/src/libsec/port/tlshand.c (revision e7c1227a38335dd6dd7ddd89d0dacb1a4d6356b8)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <auth.h>
59a747e4fSDavid du Colombier #include <mp.h>
69a747e4fSDavid du Colombier #include <libsec.h>
79a747e4fSDavid du Colombier 
89a747e4fSDavid du Colombier // The main groups of functions are:
99a747e4fSDavid du Colombier //		client/server - main handshake protocol definition
109a747e4fSDavid du Colombier //		message functions - formating handshake messages
119a747e4fSDavid du Colombier //		cipher choices - catalog of digest and encrypt algorithms
129a747e4fSDavid du Colombier //		security functions - PKCS#1, sslHMAC, session keygen
139a747e4fSDavid du Colombier //		general utility functions - malloc, serialization
149a747e4fSDavid du Colombier // The handshake protocol builds on the TLS/SSL3 record layer protocol,
159a747e4fSDavid du Colombier // which is implemented in kernel device #a.  See also /lib/rfc/rfc2246.
169a747e4fSDavid du Colombier 
179a747e4fSDavid du Colombier enum {
189a747e4fSDavid du Colombier 	TLSFinishedLen = 12,
199a747e4fSDavid du Colombier 	SSL3FinishedLen = MD5dlen+SHA1dlen,
20ad6ca847SDavid du Colombier 	MaxKeyData = 136,	// amount of secret we may need
219a747e4fSDavid du Colombier 	MaxChunk = 1<<14,
229a747e4fSDavid du Colombier 	RandomSize = 32,
239a747e4fSDavid du Colombier 	SidSize = 32,
249a747e4fSDavid du Colombier 	MasterSecretSize = 48,
259a747e4fSDavid du Colombier 	AQueue = 0,
269a747e4fSDavid du Colombier 	AFlush = 1,
279a747e4fSDavid du Colombier };
289a747e4fSDavid du Colombier 
299a747e4fSDavid du Colombier typedef struct TlsSec TlsSec;
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier typedef struct Bytes{
329a747e4fSDavid du Colombier 	int len;
339a747e4fSDavid du Colombier 	uchar data[1];  // [len]
349a747e4fSDavid du Colombier } Bytes;
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier typedef struct Ints{
379a747e4fSDavid du Colombier 	int len;
389a747e4fSDavid du Colombier 	int data[1];  // [len]
399a747e4fSDavid du Colombier } Ints;
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier typedef struct Algs{
429a747e4fSDavid du Colombier 	char *enc;
439a747e4fSDavid du Colombier 	char *digest;
449a747e4fSDavid du Colombier 	int nsecret;
459a747e4fSDavid du Colombier 	int tlsid;
469a747e4fSDavid du Colombier 	int ok;
479a747e4fSDavid du Colombier } Algs;
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier typedef struct Finished{
509a747e4fSDavid du Colombier 	uchar verify[SSL3FinishedLen];
519a747e4fSDavid du Colombier 	int n;
529a747e4fSDavid du Colombier } Finished;
539a747e4fSDavid du Colombier 
54e94c8628SDavid du Colombier typedef struct HandHash{
55e94c8628SDavid du Colombier 	MD5state	md5;
56e94c8628SDavid du Colombier 	SHAstate	sha1;
57e94c8628SDavid du Colombier 	SHA2_256state	sha2_256;
58e94c8628SDavid du Colombier } HandHash;
59e94c8628SDavid du Colombier 
609a747e4fSDavid du Colombier typedef struct TlsConnection{
619a747e4fSDavid du Colombier 	TlsSec *sec;	// security management goo
629a747e4fSDavid du Colombier 	int hand, ctl;	// record layer file descriptors
639a747e4fSDavid du Colombier 	int erred;		// set when tlsError called
649a747e4fSDavid du Colombier 	int (*trace)(char*fmt, ...); // for debugging
659a747e4fSDavid du Colombier 	int version;	// protocol we are speaking
669a747e4fSDavid du Colombier 	int verset;		// version has been set
679a747e4fSDavid du Colombier 	int ver2hi;		// server got a version 2 hello
689a747e4fSDavid du Colombier 	int isClient;	// is this the client or server?
699a747e4fSDavid du Colombier 	Bytes *sid;		// SessionID
709a747e4fSDavid du Colombier 	Bytes *cert;	// only last - no chain
719a747e4fSDavid du Colombier 
729a747e4fSDavid du Colombier 	Lock statelk;
739a747e4fSDavid du Colombier 	int state;		// must be set using setstate
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier 	// input buffer for handshake messages
76fc8e446fSDavid du Colombier 	uchar buf[MaxChunk+8*1024];
779a747e4fSDavid du Colombier 	uchar *rp, *ep;
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier 	uchar crandom[RandomSize];	// client random
809a747e4fSDavid du Colombier 	uchar srandom[RandomSize];	// server random
819a747e4fSDavid du Colombier 	int clientVersion;	// version in ClientHello
829a747e4fSDavid du Colombier 	char *digest;	// name of digest algorithm to use
839a747e4fSDavid du Colombier 	char *enc;		// name of encryption algorithm to use
849a747e4fSDavid du Colombier 	int nsecret;	// amount of secret data to init keys
859a747e4fSDavid du Colombier 
869a747e4fSDavid du Colombier 	// for finished messages
87e94c8628SDavid du Colombier 	HandHash	hs;	// handshake hash
889a747e4fSDavid du Colombier 	Finished	finished;
899a747e4fSDavid du Colombier } TlsConnection;
909a747e4fSDavid du Colombier 
919a747e4fSDavid du Colombier typedef struct Msg{
929a747e4fSDavid du Colombier 	int tag;
939a747e4fSDavid du Colombier 	union {
949a747e4fSDavid du Colombier 		struct {
959a747e4fSDavid du Colombier 			int version;
969a747e4fSDavid du Colombier 			uchar 	random[RandomSize];
979a747e4fSDavid du Colombier 			Bytes*	sid;
989a747e4fSDavid du Colombier 			Ints*	ciphers;
999a747e4fSDavid du Colombier 			Bytes*	compressors;
100cac18bb6SDavid du Colombier 			Ints*	sigAlgs;
1019a747e4fSDavid du Colombier 		} clientHello;
1029a747e4fSDavid du Colombier 		struct {
1039a747e4fSDavid du Colombier 			int version;
1049a747e4fSDavid du Colombier 			uchar 	random[RandomSize];
1059a747e4fSDavid du Colombier 			Bytes*	sid;
1069a747e4fSDavid du Colombier 			int cipher;
1079a747e4fSDavid du Colombier 			int compressor;
1089a747e4fSDavid du Colombier 		} serverHello;
1099a747e4fSDavid du Colombier 		struct {
1109a747e4fSDavid du Colombier 			int ncert;
1119a747e4fSDavid du Colombier 			Bytes **certs;
1129a747e4fSDavid du Colombier 		} certificate;
1139a747e4fSDavid du Colombier 		struct {
1149a747e4fSDavid du Colombier 			Bytes *types;
1159a747e4fSDavid du Colombier 			int nca;
1169a747e4fSDavid du Colombier 			Bytes **cas;
1179a747e4fSDavid du Colombier 		} certificateRequest;
1189a747e4fSDavid du Colombier 		struct {
1199a747e4fSDavid du Colombier 			Bytes *key;
1209a747e4fSDavid du Colombier 		} clientKeyExchange;
1219a747e4fSDavid du Colombier 		Finished finished;
1229a747e4fSDavid du Colombier 	} u;
1239a747e4fSDavid du Colombier } Msg;
1249a747e4fSDavid du Colombier 
1259a747e4fSDavid du Colombier typedef struct TlsSec{
1269a747e4fSDavid du Colombier 	char *server;	// name of remote; nil for server
1279a747e4fSDavid du Colombier 	int ok;	// <0 killed; == 0 in progress; >0 reusable
1289a747e4fSDavid du Colombier 	RSApub *rsapub;
1299a747e4fSDavid du Colombier 	AuthRpc *rpc;	// factotum for rsa private key
1309a747e4fSDavid du Colombier 	uchar sec[MasterSecretSize];	// master secret
1319a747e4fSDavid du Colombier 	uchar crandom[RandomSize];	// client random
1329a747e4fSDavid du Colombier 	uchar srandom[RandomSize];	// server random
1339a747e4fSDavid du Colombier 	int clientVers;		// version in ClientHello
1349a747e4fSDavid du Colombier 	int vers;			// final version
1359a747e4fSDavid du Colombier 	// byte generation and handshake checksum
1369a747e4fSDavid du Colombier 	void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int);
137e94c8628SDavid du Colombier 	void (*setFinished)(TlsSec*, HandHash, uchar*, int);
1389a747e4fSDavid du Colombier 	int nfin;
1399a747e4fSDavid du Colombier } TlsSec;
1409a747e4fSDavid du Colombier 
1419a747e4fSDavid du Colombier 
1429a747e4fSDavid du Colombier enum {
1439a747e4fSDavid du Colombier 	SSL3Version  = 0x0300,
144e94c8628SDavid du Colombier 	TLS10Version = 0x0301,
145e94c8628SDavid du Colombier 	TLS11Version = 0x0302,
146e94c8628SDavid du Colombier 	TLS12Version = 0x0303,
147e94c8628SDavid du Colombier 	ProtocolVersion = TLS12Version,	// maximum version we speak
1489a747e4fSDavid du Colombier 	MinProtoVersion = 0x0300,	// limits on version we accept
1499a747e4fSDavid du Colombier 	MaxProtoVersion	= 0x03ff,
1509a747e4fSDavid du Colombier };
1519a747e4fSDavid du Colombier 
1529a747e4fSDavid du Colombier // handshake type
1539a747e4fSDavid du Colombier enum {
1549a747e4fSDavid du Colombier 	HHelloRequest,
1559a747e4fSDavid du Colombier 	HClientHello,
1569a747e4fSDavid du Colombier 	HServerHello,
1579a747e4fSDavid du Colombier 	HSSL2ClientHello = 9,  /* local convention;  see devtls.c */
1589a747e4fSDavid du Colombier 	HCertificate = 11,
1599a747e4fSDavid du Colombier 	HServerKeyExchange,
1609a747e4fSDavid du Colombier 	HCertificateRequest,
1619a747e4fSDavid du Colombier 	HServerHelloDone,
1629a747e4fSDavid du Colombier 	HCertificateVerify,
1639a747e4fSDavid du Colombier 	HClientKeyExchange,
1649a747e4fSDavid du Colombier 	HFinished = 20,
1659a747e4fSDavid du Colombier 	HMax
1669a747e4fSDavid du Colombier };
1679a747e4fSDavid du Colombier 
1689a747e4fSDavid du Colombier // alerts
1699a747e4fSDavid du Colombier enum {
1709a747e4fSDavid du Colombier 	ECloseNotify = 0,
1719a747e4fSDavid du Colombier 	EUnexpectedMessage = 10,
1729a747e4fSDavid du Colombier 	EBadRecordMac = 20,
1739a747e4fSDavid du Colombier 	EDecryptionFailed = 21,
1749a747e4fSDavid du Colombier 	ERecordOverflow = 22,
1759a747e4fSDavid du Colombier 	EDecompressionFailure = 30,
1769a747e4fSDavid du Colombier 	EHandshakeFailure = 40,
1779a747e4fSDavid du Colombier 	ENoCertificate = 41,
1789a747e4fSDavid du Colombier 	EBadCertificate = 42,
1799a747e4fSDavid du Colombier 	EUnsupportedCertificate = 43,
1809a747e4fSDavid du Colombier 	ECertificateRevoked = 44,
1819a747e4fSDavid du Colombier 	ECertificateExpired = 45,
1829a747e4fSDavid du Colombier 	ECertificateUnknown = 46,
1839a747e4fSDavid du Colombier 	EIllegalParameter = 47,
1849a747e4fSDavid du Colombier 	EUnknownCa = 48,
1859a747e4fSDavid du Colombier 	EAccessDenied = 49,
1869a747e4fSDavid du Colombier 	EDecodeError = 50,
1879a747e4fSDavid du Colombier 	EDecryptError = 51,
1889a747e4fSDavid du Colombier 	EExportRestriction = 60,
1899a747e4fSDavid du Colombier 	EProtocolVersion = 70,
1909a747e4fSDavid du Colombier 	EInsufficientSecurity = 71,
1919a747e4fSDavid du Colombier 	EInternalError = 80,
1929a747e4fSDavid du Colombier 	EUserCanceled = 90,
1939a747e4fSDavid du Colombier 	ENoRenegotiation = 100,
1949a747e4fSDavid du Colombier 	EMax = 256
1959a747e4fSDavid du Colombier };
1969a747e4fSDavid du Colombier 
1979a747e4fSDavid du Colombier // cipher suites
1989a747e4fSDavid du Colombier enum {
1999a747e4fSDavid du Colombier 	TLS_NULL_WITH_NULL_NULL	 		= 0x0000,
2009a747e4fSDavid du Colombier 	TLS_RSA_WITH_NULL_MD5 			= 0x0001,
2019a747e4fSDavid du Colombier 	TLS_RSA_WITH_NULL_SHA 			= 0x0002,
2029a747e4fSDavid du Colombier 	TLS_RSA_EXPORT_WITH_RC4_40_MD5 		= 0x0003,
2039a747e4fSDavid du Colombier 	TLS_RSA_WITH_RC4_128_MD5 		= 0x0004,
2049a747e4fSDavid du Colombier 	TLS_RSA_WITH_RC4_128_SHA 		= 0x0005,
2059a747e4fSDavid du Colombier 	TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5	= 0X0006,
2069a747e4fSDavid du Colombier 	TLS_RSA_WITH_IDEA_CBC_SHA 		= 0X0007,
2079a747e4fSDavid du Colombier 	TLS_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X0008,
2089a747e4fSDavid du Colombier 	TLS_RSA_WITH_DES_CBC_SHA		= 0X0009,
2099a747e4fSDavid du Colombier 	TLS_RSA_WITH_3DES_EDE_CBC_SHA		= 0X000A,
2109a747e4fSDavid du Colombier 	TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA	= 0X000B,
2119a747e4fSDavid du Colombier 	TLS_DH_DSS_WITH_DES_CBC_SHA		= 0X000C,
2129a747e4fSDavid du Colombier 	TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA	= 0X000D,
2139a747e4fSDavid du Colombier 	TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X000E,
2149a747e4fSDavid du Colombier 	TLS_DH_RSA_WITH_DES_CBC_SHA		= 0X000F,
2159a747e4fSDavid du Colombier 	TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA	= 0X0010,
2169a747e4fSDavid du Colombier 	TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA	= 0X0011,
2179a747e4fSDavid du Colombier 	TLS_DHE_DSS_WITH_DES_CBC_SHA		= 0X0012,
2189a747e4fSDavid du Colombier 	TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA	= 0X0013,	// ZZZ must be implemented for tls1.0 compliance
2199a747e4fSDavid du Colombier 	TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X0014,
2209a747e4fSDavid du Colombier 	TLS_DHE_RSA_WITH_DES_CBC_SHA		= 0X0015,
2219a747e4fSDavid du Colombier 	TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA	= 0X0016,
2229a747e4fSDavid du Colombier 	TLS_DH_anon_EXPORT_WITH_RC4_40_MD5	= 0x0017,
2239a747e4fSDavid du Colombier 	TLS_DH_anon_WITH_RC4_128_MD5 		= 0x0018,
2249a747e4fSDavid du Colombier 	TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA	= 0X0019,
2259a747e4fSDavid du Colombier 	TLS_DH_anon_WITH_DES_CBC_SHA		= 0X001A,
2269a747e4fSDavid du Colombier 	TLS_DH_anon_WITH_3DES_EDE_CBC_SHA	= 0X001B,
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier 	TLS_RSA_WITH_AES_128_CBC_SHA		= 0X002f,	// aes, aka rijndael with 128 bit blocks
2299a747e4fSDavid du Colombier 	TLS_DH_DSS_WITH_AES_128_CBC_SHA		= 0X0030,
2309a747e4fSDavid du Colombier 	TLS_DH_RSA_WITH_AES_128_CBC_SHA		= 0X0031,
2319a747e4fSDavid du Colombier 	TLS_DHE_DSS_WITH_AES_128_CBC_SHA	= 0X0032,
2329a747e4fSDavid du Colombier 	TLS_DHE_RSA_WITH_AES_128_CBC_SHA	= 0X0033,
2339a747e4fSDavid du Colombier 	TLS_DH_anon_WITH_AES_128_CBC_SHA	= 0X0034,
2349a747e4fSDavid du Colombier 	TLS_RSA_WITH_AES_256_CBC_SHA		= 0X0035,
2359a747e4fSDavid du Colombier 	TLS_DH_DSS_WITH_AES_256_CBC_SHA		= 0X0036,
2369a747e4fSDavid du Colombier 	TLS_DH_RSA_WITH_AES_256_CBC_SHA		= 0X0037,
2379a747e4fSDavid du Colombier 	TLS_DHE_DSS_WITH_AES_256_CBC_SHA	= 0X0038,
2389a747e4fSDavid du Colombier 	TLS_DHE_RSA_WITH_AES_256_CBC_SHA	= 0X0039,
2399a747e4fSDavid du Colombier 	TLS_DH_anon_WITH_AES_256_CBC_SHA	= 0X003A,
2409a747e4fSDavid du Colombier 	CipherMax
2419a747e4fSDavid du Colombier };
2429a747e4fSDavid du Colombier 
2439a747e4fSDavid du Colombier // compression methods
2449a747e4fSDavid du Colombier enum {
2459a747e4fSDavid du Colombier 	CompressionNull = 0,
2469a747e4fSDavid du Colombier 	CompressionMax
2479a747e4fSDavid du Colombier };
2489a747e4fSDavid du Colombier 
249cac18bb6SDavid du Colombier // extensions
250cac18bb6SDavid du Colombier enum {
251cac18bb6SDavid du Colombier 	ExtSigalgs = 0xd,
252cac18bb6SDavid du Colombier };
253cac18bb6SDavid du Colombier 
254cac18bb6SDavid du Colombier // signature algorithms
255cac18bb6SDavid du Colombier enum {
256cac18bb6SDavid du Colombier 	RSA_PKCS1_SHA1   = 0x0201,
257cac18bb6SDavid du Colombier 	RSA_PKCS1_SHA256 = 0x0401,
258cac18bb6SDavid du Colombier 	RSA_PKCS1_SHA384 = 0x0501,
259cac18bb6SDavid du Colombier 	RSA_PKCS1_SHA512 = 0x0601
260cac18bb6SDavid du Colombier };
261cac18bb6SDavid du Colombier 
2629a747e4fSDavid du Colombier static Algs cipherAlgs[] = {
2639a747e4fSDavid du Colombier 	{"rc4_128", "md5", 2*(16+MD5dlen), TLS_RSA_WITH_RC4_128_MD5},
2649a747e4fSDavid du Colombier 	{"rc4_128", "sha1", 2*(16+SHA1dlen), TLS_RSA_WITH_RC4_128_SHA},
2659a747e4fSDavid du Colombier 	{"3des_ede_cbc", "sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA},
266ad6ca847SDavid du Colombier 	{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA},
267ad6ca847SDavid du Colombier 	{"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA}
2689a747e4fSDavid du Colombier };
2699a747e4fSDavid du Colombier 
2709a747e4fSDavid du Colombier static uchar compressors[] = {
2719a747e4fSDavid du Colombier 	CompressionNull,
2729a747e4fSDavid du Colombier };
2739a747e4fSDavid du Colombier 
274cac18bb6SDavid du Colombier static int sigAlgs[] = {
275cac18bb6SDavid du Colombier 	RSA_PKCS1_SHA256,
276cac18bb6SDavid du Colombier 	RSA_PKCS1_SHA1,
277cac18bb6SDavid du Colombier };
278cac18bb6SDavid du Colombier 
27951711cb6SDavid du Colombier static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chain);
2809a747e4fSDavid du Colombier static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...));
2819a747e4fSDavid du Colombier 
2829a747e4fSDavid du Colombier static void	msgClear(Msg *m);
2839a747e4fSDavid du Colombier static char* msgPrint(char *buf, int n, Msg *m);
2849a747e4fSDavid du Colombier static int	msgRecv(TlsConnection *c, Msg *m);
2859a747e4fSDavid du Colombier static int	msgSend(TlsConnection *c, Msg *m, int act);
2869a747e4fSDavid du Colombier static void	tlsError(TlsConnection *c, int err, char *msg, ...);
2879a747e4fSDavid du Colombier #pragma	varargck argpos	tlsError 3
2889a747e4fSDavid du Colombier static int setVersion(TlsConnection *c, int version);
2899a747e4fSDavid du Colombier static int finishedMatch(TlsConnection *c, Finished *f);
2909a747e4fSDavid du Colombier static void tlsConnectionFree(TlsConnection *c);
2919a747e4fSDavid du Colombier 
2929a747e4fSDavid du Colombier static int setAlgs(TlsConnection *c, int a);
2939a747e4fSDavid du Colombier static int okCipher(Ints *cv);
2949a747e4fSDavid du Colombier static int okCompression(Bytes *cv);
2959a747e4fSDavid du Colombier static int initCiphers(void);
2969a747e4fSDavid du Colombier static Ints* makeciphers(void);
2979a747e4fSDavid du Colombier 
2989a747e4fSDavid du Colombier static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom);
2999a747e4fSDavid du Colombier static int	tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd);
3009a747e4fSDavid du Colombier static TlsSec*	tlsSecInitc(int cvers, uchar *crandom);
3019a747e4fSDavid du Colombier static int	tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd);
302e94c8628SDavid du Colombier static int	tlsSecFinished(TlsSec *sec, HandHash hs, uchar *fin, int nfin, int isclient);
3039a747e4fSDavid du Colombier static void	tlsSecOk(TlsSec *sec);
3049a747e4fSDavid du Colombier static void	tlsSecKill(TlsSec *sec);
3059a747e4fSDavid du Colombier static void	tlsSecClose(TlsSec *sec);
3069a747e4fSDavid du Colombier static void	setMasterSecret(TlsSec *sec, Bytes *pm);
3079a747e4fSDavid du Colombier static void	serverMasterSecret(TlsSec *sec, uchar *epm, int nepm);
3089a747e4fSDavid du Colombier static void	setSecrets(TlsSec *sec, uchar *kd, int nkd);
3099a747e4fSDavid du Colombier static int	clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm);
3109a747e4fSDavid du Colombier static Bytes *pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype);
3119a747e4fSDavid du Colombier static Bytes *pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm);
312e94c8628SDavid du Colombier static void	tlsSetFinished(TlsSec *sec, HandHash hs, uchar *finished, int isClient);
313e94c8628SDavid du Colombier static void	tls12SetFinished(TlsSec *sec, HandHash hs, uchar *finished, int isClient);
314e94c8628SDavid du Colombier static void	sslSetFinished(TlsSec *sec, HandHash hs, uchar *finished, int isClient);
3159a747e4fSDavid du Colombier static void	sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label,
3169a747e4fSDavid du Colombier 			uchar *seed0, int nseed0, uchar *seed1, int nseed1);
3179a747e4fSDavid du Colombier static int setVers(TlsSec *sec, int version);
3189a747e4fSDavid du Colombier 
3199a747e4fSDavid du Colombier static AuthRpc* factotum_rsa_open(uchar *cert, int certlen);
3209a747e4fSDavid du Colombier static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
3219a747e4fSDavid du Colombier static void factotum_rsa_close(AuthRpc*rpc);
3229a747e4fSDavid du Colombier 
3239a747e4fSDavid du Colombier static void* emalloc(int);
3249a747e4fSDavid du Colombier static void* erealloc(void*, int);
3259a747e4fSDavid du Colombier static void put32(uchar *p, u32int);
3269a747e4fSDavid du Colombier static void put24(uchar *p, int);
3279a747e4fSDavid du Colombier static void put16(uchar *p, int);
3289a747e4fSDavid du Colombier static u32int get32(uchar *p);
3299a747e4fSDavid du Colombier static int get24(uchar *p);
3309a747e4fSDavid du Colombier static int get16(uchar *p);
3319a747e4fSDavid du Colombier static Bytes* newbytes(int len);
3329a747e4fSDavid du Colombier static Bytes* makebytes(uchar* buf, int len);
3339a747e4fSDavid du Colombier static void freebytes(Bytes* b);
3349a747e4fSDavid du Colombier static Ints* newints(int len);
3359a747e4fSDavid du Colombier static Ints* makeints(int* buf, int len);
3369a747e4fSDavid du Colombier static void freeints(Ints* b);
3379a747e4fSDavid du Colombier 
3389a747e4fSDavid du Colombier //================= client/server ========================
3399a747e4fSDavid du Colombier 
3409a747e4fSDavid du Colombier //	push TLS onto fd, returning new (application) file descriptor
3419a747e4fSDavid du Colombier //		or -1 if error.
3429a747e4fSDavid du Colombier int
tlsServer(int fd,TLSconn * conn)3439a747e4fSDavid du Colombier tlsServer(int fd, TLSconn *conn)
3449a747e4fSDavid du Colombier {
3459a747e4fSDavid du Colombier 	char buf[8];
3469a747e4fSDavid du Colombier 	char dname[64];
3479a747e4fSDavid du Colombier 	int n, data, ctl, hand;
3489a747e4fSDavid du Colombier 	TlsConnection *tls;
3499a747e4fSDavid du Colombier 
3509a747e4fSDavid du Colombier 	if(conn == nil)
3519a747e4fSDavid du Colombier 		return -1;
3529a747e4fSDavid du Colombier 	ctl = open("#a/tls/clone", ORDWR);
3539a747e4fSDavid du Colombier 	if(ctl < 0)
3549a747e4fSDavid du Colombier 		return -1;
3559a747e4fSDavid du Colombier 	n = read(ctl, buf, sizeof(buf)-1);
3569a747e4fSDavid du Colombier 	if(n < 0){
3579a747e4fSDavid du Colombier 		close(ctl);
3589a747e4fSDavid du Colombier 		return -1;
3599a747e4fSDavid du Colombier 	}
3609a747e4fSDavid du Colombier 	buf[n] = 0;
361*e7c1227aSDavid du Colombier 	snprint(conn->dir, sizeof conn->dir, "#a/tls/%s", buf);
362*e7c1227aSDavid du Colombier 	snprint(dname, sizeof dname, "#a/tls/%s/hand", buf);
3639a747e4fSDavid du Colombier 	hand = open(dname, ORDWR);
3649a747e4fSDavid du Colombier 	if(hand < 0){
3659a747e4fSDavid du Colombier 		close(ctl);
3669a747e4fSDavid du Colombier 		return -1;
3679a747e4fSDavid du Colombier 	}
3689a747e4fSDavid du Colombier 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
36951711cb6SDavid du Colombier 	tls = tlsServer2(ctl, hand, conn->cert, conn->certlen, conn->trace, conn->chain);
370*e7c1227aSDavid du Colombier 	snprint(dname, sizeof dname, "#a/tls/%s/data", buf);
3719a747e4fSDavid du Colombier 	data = open(dname, ORDWR);
3729a747e4fSDavid du Colombier 	close(fd);
3739a747e4fSDavid du Colombier 	close(hand);
3749a747e4fSDavid du Colombier 	close(ctl);
3759a747e4fSDavid du Colombier 	if(data < 0){
3769a747e4fSDavid du Colombier 		return -1;
3779a747e4fSDavid du Colombier 	}
3789a747e4fSDavid du Colombier 	if(tls == nil){
3799a747e4fSDavid du Colombier 		close(data);
3809a747e4fSDavid du Colombier 		return -1;
3819a747e4fSDavid du Colombier 	}
3829a747e4fSDavid du Colombier 	if(conn->cert)
3839a747e4fSDavid du Colombier 		free(conn->cert);
3849a747e4fSDavid du Colombier 	conn->cert = 0;  // client certificates are not yet implemented
3859a747e4fSDavid du Colombier 	conn->certlen = 0;
3869a747e4fSDavid du Colombier 	conn->sessionIDlen = tls->sid->len;
3879a747e4fSDavid du Colombier 	conn->sessionID = emalloc(conn->sessionIDlen);
3889a747e4fSDavid du Colombier 	memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen);
3890d862726SDavid du Colombier 	if(conn->sessionKey != nil && conn->sessionType != nil && strcmp(conn->sessionType, "ttls") == 0)
3900d862726SDavid du Colombier 		tls->sec->prf(conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst,  tls->sec->crandom, RandomSize, tls->sec->srandom, RandomSize);
3919a747e4fSDavid du Colombier 	tlsConnectionFree(tls);
3929a747e4fSDavid du Colombier 	return data;
3939a747e4fSDavid du Colombier }
3949a747e4fSDavid du Colombier 
3959a747e4fSDavid du Colombier //	push TLS onto fd, returning new (application) file descriptor
3969a747e4fSDavid du Colombier //		or -1 if error.
3979a747e4fSDavid du Colombier int
tlsClient(int fd,TLSconn * conn)3989a747e4fSDavid du Colombier tlsClient(int fd, TLSconn *conn)
3999a747e4fSDavid du Colombier {
4009a747e4fSDavid du Colombier 	char buf[8];
4019a747e4fSDavid du Colombier 	char dname[64];
4029a747e4fSDavid du Colombier 	int n, data, ctl, hand;
4039a747e4fSDavid du Colombier 	TlsConnection *tls;
4049a747e4fSDavid du Colombier 
4059a747e4fSDavid du Colombier 	if(!conn)
4069a747e4fSDavid du Colombier 		return -1;
4079a747e4fSDavid du Colombier 	ctl = open("#a/tls/clone", ORDWR);
4089a747e4fSDavid du Colombier 	if(ctl < 0)
4099a747e4fSDavid du Colombier 		return -1;
4109a747e4fSDavid du Colombier 	n = read(ctl, buf, sizeof(buf)-1);
4119a747e4fSDavid du Colombier 	if(n < 0){
4129a747e4fSDavid du Colombier 		close(ctl);
4139a747e4fSDavid du Colombier 		return -1;
4149a747e4fSDavid du Colombier 	}
4159a747e4fSDavid du Colombier 	buf[n] = 0;
416*e7c1227aSDavid du Colombier 	snprint(conn->dir, sizeof conn->dir, "#a/tls/%s", buf);
417*e7c1227aSDavid du Colombier 	snprint(dname, sizeof dname, "#a/tls/%s/hand", buf);
4189a747e4fSDavid du Colombier 	hand = open(dname, ORDWR);
4199a747e4fSDavid du Colombier 	if(hand < 0){
4209a747e4fSDavid du Colombier 		close(ctl);
4219a747e4fSDavid du Colombier 		return -1;
4229a747e4fSDavid du Colombier 	}
423*e7c1227aSDavid du Colombier 	snprint(dname, sizeof dname, "#a/tls/%s/data", buf);
4249a747e4fSDavid du Colombier 	data = open(dname, ORDWR);
425fc8e446fSDavid du Colombier 	if(data < 0) {
426fc8e446fSDavid du Colombier 		close(hand);
427fc8e446fSDavid du Colombier 		close(ctl);
4289a747e4fSDavid du Colombier 		return -1;
429fc8e446fSDavid du Colombier 	}
4309a747e4fSDavid du Colombier 	fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
4319a747e4fSDavid du Colombier 	tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->trace);
4329a747e4fSDavid du Colombier 	close(fd);
4339a747e4fSDavid du Colombier 	close(hand);
4349a747e4fSDavid du Colombier 	close(ctl);
4359a747e4fSDavid du Colombier 	if(tls == nil){
4369a747e4fSDavid du Colombier 		close(data);
4379a747e4fSDavid du Colombier 		return -1;
4389a747e4fSDavid du Colombier 	}
4399a747e4fSDavid du Colombier 	conn->certlen = tls->cert->len;
4409a747e4fSDavid du Colombier 	conn->cert = emalloc(conn->certlen);
4419a747e4fSDavid du Colombier 	memcpy(conn->cert, tls->cert->data, conn->certlen);
4429a747e4fSDavid du Colombier 	conn->sessionIDlen = tls->sid->len;
4439a747e4fSDavid du Colombier 	conn->sessionID = emalloc(conn->sessionIDlen);
4449a747e4fSDavid du Colombier 	memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen);
4450d862726SDavid du Colombier 	if(conn->sessionKey != nil && conn->sessionType != nil && strcmp(conn->sessionType, "ttls") == 0)
4460d862726SDavid du Colombier 		tls->sec->prf(conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst,  tls->sec->crandom, RandomSize, tls->sec->srandom, RandomSize);
4479a747e4fSDavid du Colombier 	tlsConnectionFree(tls);
4489a747e4fSDavid du Colombier 	return data;
4499a747e4fSDavid du Colombier }
4509a747e4fSDavid du Colombier 
45151711cb6SDavid du Colombier static int
countchain(PEMChain * p)45251711cb6SDavid du Colombier countchain(PEMChain *p)
45351711cb6SDavid du Colombier {
45451711cb6SDavid du Colombier 	int i = 0;
45551711cb6SDavid du Colombier 
45651711cb6SDavid du Colombier 	while (p) {
45751711cb6SDavid du Colombier 		i++;
45851711cb6SDavid du Colombier 		p = p->next;
45951711cb6SDavid du Colombier 	}
46051711cb6SDavid du Colombier 	return i;
46151711cb6SDavid du Colombier }
46251711cb6SDavid du Colombier 
4639a747e4fSDavid du Colombier static TlsConnection *
tlsServer2(int ctl,int hand,uchar * cert,int ncert,int (* trace)(char * fmt,...),PEMChain * chp)46451711cb6SDavid du Colombier tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chp)
4659a747e4fSDavid du Colombier {
4669a747e4fSDavid du Colombier 	TlsConnection *c;
4679a747e4fSDavid du Colombier 	Msg m;
4689a747e4fSDavid du Colombier 	Bytes *csid;
4699a747e4fSDavid du Colombier 	uchar sid[SidSize], kd[MaxKeyData];
4709a747e4fSDavid du Colombier 	char *secrets;
47151711cb6SDavid du Colombier 	int cipher, compressor, nsid, rv, numcerts, i;
4729a747e4fSDavid du Colombier 
4739a747e4fSDavid du Colombier 	if(trace)
4749a747e4fSDavid du Colombier 		trace("tlsServer2\n");
4759a747e4fSDavid du Colombier 	if(!initCiphers())
4769a747e4fSDavid du Colombier 		return nil;
4779a747e4fSDavid du Colombier 	c = emalloc(sizeof(TlsConnection));
4789a747e4fSDavid du Colombier 	c->ctl = ctl;
4799a747e4fSDavid du Colombier 	c->hand = hand;
4809a747e4fSDavid du Colombier 	c->trace = trace;
4819a747e4fSDavid du Colombier 	c->version = ProtocolVersion;
4829a747e4fSDavid du Colombier 
4839a747e4fSDavid du Colombier 	memset(&m, 0, sizeof(m));
4849a747e4fSDavid du Colombier 	if(!msgRecv(c, &m)){
4859a747e4fSDavid du Colombier 		if(trace)
4869a747e4fSDavid du Colombier 			trace("initial msgRecv failed\n");
4879a747e4fSDavid du Colombier 		goto Err;
4889a747e4fSDavid du Colombier 	}
4899a747e4fSDavid du Colombier 	if(m.tag != HClientHello) {
4909a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a client hello");
4919a747e4fSDavid du Colombier 		goto Err;
4929a747e4fSDavid du Colombier 	}
4939a747e4fSDavid du Colombier 	c->clientVersion = m.u.clientHello.version;
4949a747e4fSDavid du Colombier 	if(trace)
4959a747e4fSDavid du Colombier 		trace("ClientHello version %x\n", c->clientVersion);
4969a747e4fSDavid du Colombier 	if(setVersion(c, m.u.clientHello.version) < 0) {
4979a747e4fSDavid du Colombier 		tlsError(c, EIllegalParameter, "incompatible version");
4989a747e4fSDavid du Colombier 		goto Err;
4999a747e4fSDavid du Colombier 	}
5009a747e4fSDavid du Colombier 
5019a747e4fSDavid du Colombier 	memmove(c->crandom, m.u.clientHello.random, RandomSize);
5029a747e4fSDavid du Colombier 	cipher = okCipher(m.u.clientHello.ciphers);
5039a747e4fSDavid du Colombier 	if(cipher < 0) {
5049a747e4fSDavid du Colombier 		// reply with EInsufficientSecurity if we know that's the case
5059a747e4fSDavid du Colombier 		if(cipher == -2)
5069a747e4fSDavid du Colombier 			tlsError(c, EInsufficientSecurity, "cipher suites too weak");
5079a747e4fSDavid du Colombier 		else
5089a747e4fSDavid du Colombier 			tlsError(c, EHandshakeFailure, "no matching cipher suite");
5099a747e4fSDavid du Colombier 		goto Err;
5109a747e4fSDavid du Colombier 	}
5119a747e4fSDavid du Colombier 	if(!setAlgs(c, cipher)){
5129a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "no matching cipher suite");
5139a747e4fSDavid du Colombier 		goto Err;
5149a747e4fSDavid du Colombier 	}
5159a747e4fSDavid du Colombier 	compressor = okCompression(m.u.clientHello.compressors);
5169a747e4fSDavid du Colombier 	if(compressor < 0) {
5179a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "no matching compressor");
5189a747e4fSDavid du Colombier 		goto Err;
5199a747e4fSDavid du Colombier 	}
5209a747e4fSDavid du Colombier 
5219a747e4fSDavid du Colombier 	csid = m.u.clientHello.sid;
5229a747e4fSDavid du Colombier 	if(trace)
5239a747e4fSDavid du Colombier 		trace("  cipher %d, compressor %d, csidlen %d\n", cipher, compressor, csid->len);
5249a747e4fSDavid du Colombier 	c->sec = tlsSecInits(c->clientVersion, csid->data, csid->len, c->crandom, sid, &nsid, c->srandom);
5259a747e4fSDavid du Colombier 	if(c->sec == nil){
5269a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "can't initialize security: %r");
5279a747e4fSDavid du Colombier 		goto Err;
5289a747e4fSDavid du Colombier 	}
5299a747e4fSDavid du Colombier 	c->sec->rpc = factotum_rsa_open(cert, ncert);
5309a747e4fSDavid du Colombier 	if(c->sec->rpc == nil){
5319a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
5329a747e4fSDavid du Colombier 		goto Err;
5339a747e4fSDavid du Colombier 	}
5349a747e4fSDavid du Colombier 	c->sec->rsapub = X509toRSApub(cert, ncert, nil, 0);
5359a747e4fSDavid du Colombier 	msgClear(&m);
5369a747e4fSDavid du Colombier 
5379a747e4fSDavid du Colombier 	m.tag = HServerHello;
5389a747e4fSDavid du Colombier 	m.u.serverHello.version = c->version;
5399a747e4fSDavid du Colombier 	memmove(m.u.serverHello.random, c->srandom, RandomSize);
5409a747e4fSDavid du Colombier 	m.u.serverHello.cipher = cipher;
5419a747e4fSDavid du Colombier 	m.u.serverHello.compressor = compressor;
5429a747e4fSDavid du Colombier 	c->sid = makebytes(sid, nsid);
5439a747e4fSDavid du Colombier 	m.u.serverHello.sid = makebytes(c->sid->data, c->sid->len);
5449a747e4fSDavid du Colombier 	if(!msgSend(c, &m, AQueue))
5459a747e4fSDavid du Colombier 		goto Err;
5469a747e4fSDavid du Colombier 	msgClear(&m);
5479a747e4fSDavid du Colombier 
5489a747e4fSDavid du Colombier 	m.tag = HCertificate;
54951711cb6SDavid du Colombier 	numcerts = countchain(chp);
55051711cb6SDavid du Colombier 	m.u.certificate.ncert = 1 + numcerts;
5519a747e4fSDavid du Colombier 	m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes));
5529a747e4fSDavid du Colombier 	m.u.certificate.certs[0] = makebytes(cert, ncert);
55351711cb6SDavid du Colombier 	for (i = 0; i < numcerts && chp; i++, chp = chp->next)
55451711cb6SDavid du Colombier 		m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen);
5559a747e4fSDavid du Colombier 	if(!msgSend(c, &m, AQueue))
5569a747e4fSDavid du Colombier 		goto Err;
5579a747e4fSDavid du Colombier 	msgClear(&m);
5589a747e4fSDavid du Colombier 
5599a747e4fSDavid du Colombier 	m.tag = HServerHelloDone;
5609a747e4fSDavid du Colombier 	if(!msgSend(c, &m, AFlush))
5619a747e4fSDavid du Colombier 		goto Err;
5629a747e4fSDavid du Colombier 	msgClear(&m);
5639a747e4fSDavid du Colombier 
5649a747e4fSDavid du Colombier 	if(!msgRecv(c, &m))
5659a747e4fSDavid du Colombier 		goto Err;
5669a747e4fSDavid du Colombier 	if(m.tag != HClientKeyExchange) {
5679a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a client key exchange");
5689a747e4fSDavid du Colombier 		goto Err;
5699a747e4fSDavid du Colombier 	}
5709a747e4fSDavid du Colombier 	if(tlsSecSecrets(c->sec, c->version, m.u.clientKeyExchange.key->data, m.u.clientKeyExchange.key->len, kd, c->nsecret) < 0){
5719a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "couldn't set secrets: %r");
5729a747e4fSDavid du Colombier 		goto Err;
5739a747e4fSDavid du Colombier 	}
5749a747e4fSDavid du Colombier 	if(trace)
5759a747e4fSDavid du Colombier 		trace("tls secrets\n");
5769a747e4fSDavid du Colombier 	secrets = (char*)emalloc(2*c->nsecret);
5779a747e4fSDavid du Colombier 	enc64(secrets, 2*c->nsecret, kd, c->nsecret);
5789a747e4fSDavid du Colombier 	rv = fprint(c->ctl, "secret %s %s 0 %s", c->digest, c->enc, secrets);
5799a747e4fSDavid du Colombier 	memset(secrets, 0, 2*c->nsecret);
5809a747e4fSDavid du Colombier 	free(secrets);
5819a747e4fSDavid du Colombier 	memset(kd, 0, c->nsecret);
5829a747e4fSDavid du Colombier 	if(rv < 0){
5839a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "can't set keys: %r");
5849a747e4fSDavid du Colombier 		goto Err;
5859a747e4fSDavid du Colombier 	}
5869a747e4fSDavid du Colombier 	msgClear(&m);
5879a747e4fSDavid du Colombier 
5889a747e4fSDavid du Colombier 	/* no CertificateVerify; skip to Finished */
589e94c8628SDavid du Colombier 	if(tlsSecFinished(c->sec, c->hs, c->finished.verify, c->finished.n, 1) < 0){
5909a747e4fSDavid du Colombier 		tlsError(c, EInternalError, "can't set finished: %r");
5919a747e4fSDavid du Colombier 		goto Err;
5929a747e4fSDavid du Colombier 	}
5939a747e4fSDavid du Colombier 	if(!msgRecv(c, &m))
5949a747e4fSDavid du Colombier 		goto Err;
5959a747e4fSDavid du Colombier 	if(m.tag != HFinished) {
5969a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a finished");
5979a747e4fSDavid du Colombier 		goto Err;
5989a747e4fSDavid du Colombier 	}
5999a747e4fSDavid du Colombier 	if(!finishedMatch(c, &m.u.finished)) {
6009a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "finished verification failed");
6019a747e4fSDavid du Colombier 		goto Err;
6029a747e4fSDavid du Colombier 	}
6039a747e4fSDavid du Colombier 	msgClear(&m);
6049a747e4fSDavid du Colombier 
6059a747e4fSDavid du Colombier 	/* change cipher spec */
6069a747e4fSDavid du Colombier 	if(fprint(c->ctl, "changecipher") < 0){
6079a747e4fSDavid du Colombier 		tlsError(c, EInternalError, "can't enable cipher: %r");
6089a747e4fSDavid du Colombier 		goto Err;
6099a747e4fSDavid du Colombier 	}
6109a747e4fSDavid du Colombier 
611e94c8628SDavid du Colombier 	if(tlsSecFinished(c->sec, c->hs, c->finished.verify, c->finished.n, 0) < 0){
6129a747e4fSDavid du Colombier 		tlsError(c, EInternalError, "can't set finished: %r");
6139a747e4fSDavid du Colombier 		goto Err;
6149a747e4fSDavid du Colombier 	}
6159a747e4fSDavid du Colombier 	m.tag = HFinished;
6169a747e4fSDavid du Colombier 	m.u.finished = c->finished;
6179a747e4fSDavid du Colombier 	if(!msgSend(c, &m, AFlush))
6189a747e4fSDavid du Colombier 		goto Err;
6199a747e4fSDavid du Colombier 	msgClear(&m);
6209a747e4fSDavid du Colombier 	if(trace)
6219a747e4fSDavid du Colombier 		trace("tls finished\n");
6229a747e4fSDavid du Colombier 
6239a747e4fSDavid du Colombier 	if(fprint(c->ctl, "opened") < 0)
6249a747e4fSDavid du Colombier 		goto Err;
6259a747e4fSDavid du Colombier 	tlsSecOk(c->sec);
6269a747e4fSDavid du Colombier 	return c;
6279a747e4fSDavid du Colombier 
6289a747e4fSDavid du Colombier Err:
6299a747e4fSDavid du Colombier 	msgClear(&m);
6309a747e4fSDavid du Colombier 	tlsConnectionFree(c);
6319a747e4fSDavid du Colombier 	return 0;
6329a747e4fSDavid du Colombier }
6339a747e4fSDavid du Colombier 
6349a747e4fSDavid du Colombier static TlsConnection *
tlsClient2(int ctl,int hand,uchar * csid,int ncsid,int (* trace)(char * fmt,...))6359a747e4fSDavid du Colombier tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...))
6369a747e4fSDavid du Colombier {
6379a747e4fSDavid du Colombier 	TlsConnection *c;
6389a747e4fSDavid du Colombier 	Msg m;
6399a747e4fSDavid du Colombier 	uchar kd[MaxKeyData], *epm;
6409a747e4fSDavid du Colombier 	char *secrets;
6419a747e4fSDavid du Colombier 	int creq, nepm, rv;
6429a747e4fSDavid du Colombier 
6439a747e4fSDavid du Colombier 	if(!initCiphers())
6449a747e4fSDavid du Colombier 		return nil;
6459a747e4fSDavid du Colombier 	epm = nil;
6469a747e4fSDavid du Colombier 	c = emalloc(sizeof(TlsConnection));
6479a747e4fSDavid du Colombier 	c->version = ProtocolVersion;
6489a747e4fSDavid du Colombier 	c->ctl = ctl;
6499a747e4fSDavid du Colombier 	c->hand = hand;
6509a747e4fSDavid du Colombier 	c->trace = trace;
6519a747e4fSDavid du Colombier 	c->isClient = 1;
6529a747e4fSDavid du Colombier 	c->clientVersion = c->version;
6539a747e4fSDavid du Colombier 
6549a747e4fSDavid du Colombier 	c->sec = tlsSecInitc(c->clientVersion, c->crandom);
6559a747e4fSDavid du Colombier 	if(c->sec == nil)
6569a747e4fSDavid du Colombier 		goto Err;
6579a747e4fSDavid du Colombier 
6589a747e4fSDavid du Colombier 	/* client hello */
6599a747e4fSDavid du Colombier 	memset(&m, 0, sizeof(m));
6609a747e4fSDavid du Colombier 	m.tag = HClientHello;
6619a747e4fSDavid du Colombier 	m.u.clientHello.version = c->clientVersion;
6629a747e4fSDavid du Colombier 	memmove(m.u.clientHello.random, c->crandom, RandomSize);
6639a747e4fSDavid du Colombier 	m.u.clientHello.sid = makebytes(csid, ncsid);
6649a747e4fSDavid du Colombier 	m.u.clientHello.ciphers = makeciphers();
6659a747e4fSDavid du Colombier 	m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors));
666cac18bb6SDavid du Colombier 	if(c->clientVersion >= TLS12Version)
667cac18bb6SDavid du Colombier 		m.u.clientHello.sigAlgs = makeints(sigAlgs, nelem(sigAlgs));
6689a747e4fSDavid du Colombier 	if(!msgSend(c, &m, AFlush))
6699a747e4fSDavid du Colombier 		goto Err;
6709a747e4fSDavid du Colombier 	msgClear(&m);
6719a747e4fSDavid du Colombier 
6729a747e4fSDavid du Colombier 	/* server hello */
6739a747e4fSDavid du Colombier 	if(!msgRecv(c, &m))
6749a747e4fSDavid du Colombier 		goto Err;
6759a747e4fSDavid du Colombier 	if(m.tag != HServerHello) {
6769a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a server hello");
6779a747e4fSDavid du Colombier 		goto Err;
6789a747e4fSDavid du Colombier 	}
6799a747e4fSDavid du Colombier 	if(setVersion(c, m.u.serverHello.version) < 0) {
6809a747e4fSDavid du Colombier 		tlsError(c, EIllegalParameter, "incompatible version %r");
6819a747e4fSDavid du Colombier 		goto Err;
6829a747e4fSDavid du Colombier 	}
6839a747e4fSDavid du Colombier 	memmove(c->srandom, m.u.serverHello.random, RandomSize);
6849a747e4fSDavid du Colombier 	c->sid = makebytes(m.u.serverHello.sid->data, m.u.serverHello.sid->len);
6859a747e4fSDavid du Colombier 	if(c->sid->len != 0 && c->sid->len != SidSize) {
6869a747e4fSDavid du Colombier 		tlsError(c, EIllegalParameter, "invalid server session identifier");
6879a747e4fSDavid du Colombier 		goto Err;
6889a747e4fSDavid du Colombier 	}
6899a747e4fSDavid du Colombier 	if(!setAlgs(c, m.u.serverHello.cipher)) {
6909a747e4fSDavid du Colombier 		tlsError(c, EIllegalParameter, "invalid cipher suite");
6919a747e4fSDavid du Colombier 		goto Err;
6929a747e4fSDavid du Colombier 	}
6939a747e4fSDavid du Colombier 	if(m.u.serverHello.compressor != CompressionNull) {
6949a747e4fSDavid du Colombier 		tlsError(c, EIllegalParameter, "invalid compression");
6959a747e4fSDavid du Colombier 		goto Err;
6969a747e4fSDavid du Colombier 	}
6979a747e4fSDavid du Colombier 	msgClear(&m);
6989a747e4fSDavid du Colombier 
6999a747e4fSDavid du Colombier 	/* certificate */
7009a747e4fSDavid du Colombier 	if(!msgRecv(c, &m) || m.tag != HCertificate) {
7019a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a certificate");
7029a747e4fSDavid du Colombier 		goto Err;
7039a747e4fSDavid du Colombier 	}
7049a747e4fSDavid du Colombier 	if(m.u.certificate.ncert < 1) {
7059a747e4fSDavid du Colombier 		tlsError(c, EIllegalParameter, "runt certificate");
7069a747e4fSDavid du Colombier 		goto Err;
7079a747e4fSDavid du Colombier 	}
7089a747e4fSDavid du Colombier 	c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len);
7099a747e4fSDavid du Colombier 	msgClear(&m);
7109a747e4fSDavid du Colombier 
7119a747e4fSDavid du Colombier 	/* server key exchange (optional) */
7129a747e4fSDavid du Colombier 	if(!msgRecv(c, &m))
7139a747e4fSDavid du Colombier 		goto Err;
7149a747e4fSDavid du Colombier 	if(m.tag == HServerKeyExchange) {
7159a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "got an server key exchange");
7169a747e4fSDavid du Colombier 		goto Err;
7179a747e4fSDavid du Colombier 		// If implementing this later, watch out for rollback attack
7189a747e4fSDavid du Colombier 		// described in Wagner Schneier 1996, section 4.4.
7199a747e4fSDavid du Colombier 	}
7209a747e4fSDavid du Colombier 
7219a747e4fSDavid du Colombier 	/* certificate request (optional) */
7229a747e4fSDavid du Colombier 	creq = 0;
7239a747e4fSDavid du Colombier 	if(m.tag == HCertificateRequest) {
7249a747e4fSDavid du Colombier 		creq = 1;
7259a747e4fSDavid du Colombier 		msgClear(&m);
7269a747e4fSDavid du Colombier 		if(!msgRecv(c, &m))
7279a747e4fSDavid du Colombier 			goto Err;
7289a747e4fSDavid du Colombier 	}
7299a747e4fSDavid du Colombier 
7309a747e4fSDavid du Colombier 	if(m.tag != HServerHelloDone) {
7319a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a server hello done");
7329a747e4fSDavid du Colombier 		goto Err;
7339a747e4fSDavid du Colombier 	}
7349a747e4fSDavid du Colombier 	msgClear(&m);
7359a747e4fSDavid du Colombier 
7369a747e4fSDavid du Colombier 	if(tlsSecSecretc(c->sec, c->sid->data, c->sid->len, c->srandom,
7379a747e4fSDavid du Colombier 			c->cert->data, c->cert->len, c->version, &epm, &nepm,
7389a747e4fSDavid du Colombier 			kd, c->nsecret) < 0){
7399a747e4fSDavid du Colombier 		tlsError(c, EBadCertificate, "invalid x509/rsa certificate");
7409a747e4fSDavid du Colombier 		goto Err;
7419a747e4fSDavid du Colombier 	}
7429a747e4fSDavid du Colombier 	secrets = (char*)emalloc(2*c->nsecret);
7439a747e4fSDavid du Colombier 	enc64(secrets, 2*c->nsecret, kd, c->nsecret);
7449a747e4fSDavid du Colombier 	rv = fprint(c->ctl, "secret %s %s 1 %s", c->digest, c->enc, secrets);
7459a747e4fSDavid du Colombier 	memset(secrets, 0, 2*c->nsecret);
7469a747e4fSDavid du Colombier 	free(secrets);
7479a747e4fSDavid du Colombier 	memset(kd, 0, c->nsecret);
7489a747e4fSDavid du Colombier 	if(rv < 0){
7499a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "can't set keys: %r");
7509a747e4fSDavid du Colombier 		goto Err;
7519a747e4fSDavid du Colombier 	}
7529a747e4fSDavid du Colombier 
7539a747e4fSDavid du Colombier 	if(creq) {
7549a747e4fSDavid du Colombier 		/* send a zero length certificate */
7559a747e4fSDavid du Colombier 		m.tag = HCertificate;
7569a747e4fSDavid du Colombier 		if(!msgSend(c, &m, AFlush))
7579a747e4fSDavid du Colombier 			goto Err;
7589a747e4fSDavid du Colombier 		msgClear(&m);
7599a747e4fSDavid du Colombier 	}
7609a747e4fSDavid du Colombier 
7619a747e4fSDavid du Colombier 	/* client key exchange */
7629a747e4fSDavid du Colombier 	m.tag = HClientKeyExchange;
7639a747e4fSDavid du Colombier 	m.u.clientKeyExchange.key = makebytes(epm, nepm);
7649a747e4fSDavid du Colombier 	free(epm);
7659a747e4fSDavid du Colombier 	epm = nil;
7669a747e4fSDavid du Colombier 	if(m.u.clientKeyExchange.key == nil) {
7679a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "can't set secret: %r");
7689a747e4fSDavid du Colombier 		goto Err;
7699a747e4fSDavid du Colombier 	}
7709a747e4fSDavid du Colombier 	if(!msgSend(c, &m, AFlush))
7719a747e4fSDavid du Colombier 		goto Err;
7729a747e4fSDavid du Colombier 	msgClear(&m);
7739a747e4fSDavid du Colombier 
7749a747e4fSDavid du Colombier 	/* change cipher spec */
7759a747e4fSDavid du Colombier 	if(fprint(c->ctl, "changecipher") < 0){
7769a747e4fSDavid du Colombier 		tlsError(c, EInternalError, "can't enable cipher: %r");
7779a747e4fSDavid du Colombier 		goto Err;
7789a747e4fSDavid du Colombier 	}
7799a747e4fSDavid du Colombier 
7809a747e4fSDavid du Colombier 	// Cipherchange must occur immediately before Finished to avoid
7819a747e4fSDavid du Colombier 	// potential hole;  see section 4.3 of Wagner Schneier 1996.
782e94c8628SDavid du Colombier 	if(tlsSecFinished(c->sec, c->hs, c->finished.verify, c->finished.n, 1) < 0){
78365fa3f8bSDavid du Colombier 		tlsError(c, EInternalError, "can't set finished 1: %r");
7849a747e4fSDavid du Colombier 		goto Err;
7859a747e4fSDavid du Colombier 	}
7869a747e4fSDavid du Colombier 	m.tag = HFinished;
7879a747e4fSDavid du Colombier 	m.u.finished = c->finished;
7889a747e4fSDavid du Colombier 
78965fa3f8bSDavid du Colombier 	if(!msgSend(c, &m, AFlush)) {
79065fa3f8bSDavid du Colombier 		fprint(2, "tlsClient nepm=%d\n", nepm);
79165fa3f8bSDavid du Colombier 		tlsError(c, EInternalError, "can't flush after client Finished: %r");
7929a747e4fSDavid du Colombier 		goto Err;
79365fa3f8bSDavid du Colombier 	}
7949a747e4fSDavid du Colombier 	msgClear(&m);
7959a747e4fSDavid du Colombier 
796e94c8628SDavid du Colombier 	if(tlsSecFinished(c->sec, c->hs, c->finished.verify, c->finished.n, 0) < 0){
79765fa3f8bSDavid du Colombier 		fprint(2, "tlsClient nepm=%d\n", nepm);
79865fa3f8bSDavid du Colombier 		tlsError(c, EInternalError, "can't set finished 0: %r");
7999a747e4fSDavid du Colombier 		goto Err;
8009a747e4fSDavid du Colombier 	}
80165fa3f8bSDavid du Colombier 	if(!msgRecv(c, &m)) {
80265fa3f8bSDavid du Colombier 		fprint(2, "tlsClient nepm=%d\n", nepm);
80365fa3f8bSDavid du Colombier 		tlsError(c, EInternalError, "can't read server Finished: %r");
8049a747e4fSDavid du Colombier 		goto Err;
80565fa3f8bSDavid du Colombier 	}
8069a747e4fSDavid du Colombier 	if(m.tag != HFinished) {
80765fa3f8bSDavid du Colombier 		fprint(2, "tlsClient nepm=%d\n", nepm);
80865fa3f8bSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "expected a Finished msg from server");
8099a747e4fSDavid du Colombier 		goto Err;
8109a747e4fSDavid du Colombier 	}
8119a747e4fSDavid du Colombier 
8129a747e4fSDavid du Colombier 	if(!finishedMatch(c, &m.u.finished)) {
8139a747e4fSDavid du Colombier 		tlsError(c, EHandshakeFailure, "finished verification failed");
8149a747e4fSDavid du Colombier 		goto Err;
8159a747e4fSDavid du Colombier 	}
8169a747e4fSDavid du Colombier 	msgClear(&m);
8179a747e4fSDavid du Colombier 
8189a747e4fSDavid du Colombier 	if(fprint(c->ctl, "opened") < 0){
8199a747e4fSDavid du Colombier 		if(trace)
8209a747e4fSDavid du Colombier 			trace("unable to do final open: %r\n");
8219a747e4fSDavid du Colombier 		goto Err;
8229a747e4fSDavid du Colombier 	}
8239a747e4fSDavid du Colombier 	tlsSecOk(c->sec);
8249a747e4fSDavid du Colombier 	return c;
8259a747e4fSDavid du Colombier 
8269a747e4fSDavid du Colombier Err:
8279a747e4fSDavid du Colombier 	free(epm);
8289a747e4fSDavid du Colombier 	msgClear(&m);
8299a747e4fSDavid du Colombier 	tlsConnectionFree(c);
8309a747e4fSDavid du Colombier 	return 0;
8319a747e4fSDavid du Colombier }
8329a747e4fSDavid du Colombier 
8339a747e4fSDavid du Colombier 
8349a747e4fSDavid du Colombier //================= message functions ========================
8359a747e4fSDavid du Colombier 
8369a747e4fSDavid du Colombier static uchar sendbuf[9000], *sendp;
8379a747e4fSDavid du Colombier 
838e94c8628SDavid du Colombier static void
msgHash(TlsConnection * c,uchar * p,int n)839e94c8628SDavid du Colombier msgHash(TlsConnection *c, uchar *p, int n)
840e94c8628SDavid du Colombier {
841e94c8628SDavid du Colombier 	md5(p, n, 0, &c->hs.md5);
842e94c8628SDavid du Colombier 	sha1(p, n, 0, &c->hs.sha1);
843e94c8628SDavid du Colombier 	if(c->version >= TLS12Version)
844e94c8628SDavid du Colombier 		sha2_256(p, n, 0, &c->hs.sha2_256);
845e94c8628SDavid du Colombier 	else
846e94c8628SDavid du Colombier 		memset(&c->hs.sha2_256, 0, sizeof c->hs.sha2_256);
847e94c8628SDavid du Colombier }
848e94c8628SDavid du Colombier 
8499a747e4fSDavid du Colombier static int
msgSend(TlsConnection * c,Msg * m,int act)8509a747e4fSDavid du Colombier msgSend(TlsConnection *c, Msg *m, int act)
8519a747e4fSDavid du Colombier {
8529a747e4fSDavid du Colombier 	uchar *p; // sendp = start of new message;  p = write pointer
8539a747e4fSDavid du Colombier 	int nn, n, i;
8549a747e4fSDavid du Colombier 
8559a747e4fSDavid du Colombier 	if(sendp == nil)
8569a747e4fSDavid du Colombier 		sendp = sendbuf;
8579a747e4fSDavid du Colombier 	p = sendp;
8589a747e4fSDavid du Colombier 	if(c->trace)
8599a747e4fSDavid du Colombier 		c->trace("send %s", msgPrint((char*)p, (sizeof sendbuf) - (p-sendbuf), m));
8609a747e4fSDavid du Colombier 
8619a747e4fSDavid du Colombier 	p[0] = m->tag;	// header - fill in size later
8629a747e4fSDavid du Colombier 	p += 4;
8639a747e4fSDavid du Colombier 
8649a747e4fSDavid du Colombier 	switch(m->tag) {
8659a747e4fSDavid du Colombier 	default:
8669a747e4fSDavid du Colombier 		tlsError(c, EInternalError, "can't encode a %d", m->tag);
8679a747e4fSDavid du Colombier 		goto Err;
8689a747e4fSDavid du Colombier 	case HClientHello:
8699a747e4fSDavid du Colombier 		// version
8709a747e4fSDavid du Colombier 		put16(p, m->u.clientHello.version);
8719a747e4fSDavid du Colombier 		p += 2;
8729a747e4fSDavid du Colombier 
8739a747e4fSDavid du Colombier 		// random
8749a747e4fSDavid du Colombier 		memmove(p, m->u.clientHello.random, RandomSize);
8759a747e4fSDavid du Colombier 		p += RandomSize;
8769a747e4fSDavid du Colombier 
8779a747e4fSDavid du Colombier 		// sid
8789a747e4fSDavid du Colombier 		n = m->u.clientHello.sid->len;
8799a747e4fSDavid du Colombier 		assert(n < 256);
8809a747e4fSDavid du Colombier 		p[0] = n;
8819a747e4fSDavid du Colombier 		memmove(p+1, m->u.clientHello.sid->data, n);
8829a747e4fSDavid du Colombier 		p += n+1;
8839a747e4fSDavid du Colombier 
8849a747e4fSDavid du Colombier 		n = m->u.clientHello.ciphers->len;
8859a747e4fSDavid du Colombier 		assert(n > 0 && n < 200);
8869a747e4fSDavid du Colombier 		put16(p, n*2);
8879a747e4fSDavid du Colombier 		p += 2;
8889a747e4fSDavid du Colombier 		for(i=0; i<n; i++) {
8899a747e4fSDavid du Colombier 			put16(p, m->u.clientHello.ciphers->data[i]);
8909a747e4fSDavid du Colombier 			p += 2;
8919a747e4fSDavid du Colombier 		}
8929a747e4fSDavid du Colombier 
8939a747e4fSDavid du Colombier 		n = m->u.clientHello.compressors->len;
8949a747e4fSDavid du Colombier 		assert(n > 0);
8959a747e4fSDavid du Colombier 		p[0] = n;
8969a747e4fSDavid du Colombier 		memmove(p+1, m->u.clientHello.compressors->data, n);
8979a747e4fSDavid du Colombier 		p += n+1;
898cac18bb6SDavid du Colombier 
899cac18bb6SDavid du Colombier 		if(m->u.clientHello.sigAlgs != nil) {
900cac18bb6SDavid du Colombier 			n = m->u.clientHello.sigAlgs->len;
901cac18bb6SDavid du Colombier 			put16(p, 6 + 2*n);   /* length of extensions */
902cac18bb6SDavid du Colombier 			put16(p+2, ExtSigalgs);
903cac18bb6SDavid du Colombier 			put16(p+4, 2 + 2*n); /* length of extension content */
904cac18bb6SDavid du Colombier 			put16(p+6, 2*n);     /* length of algorithm list */
905cac18bb6SDavid du Colombier 			p += 8;
906cac18bb6SDavid du Colombier 			for(i = 0; i < n; i++) {
907cac18bb6SDavid du Colombier 				put16(p, m->u.clientHello.sigAlgs->data[i]);
908cac18bb6SDavid du Colombier 				p += 2;
909cac18bb6SDavid du Colombier 			}
910cac18bb6SDavid du Colombier 		}
9119a747e4fSDavid du Colombier 		break;
9129a747e4fSDavid du Colombier 	case HServerHello:
9139a747e4fSDavid du Colombier 		put16(p, m->u.serverHello.version);
9149a747e4fSDavid du Colombier 		p += 2;
9159a747e4fSDavid du Colombier 
9169a747e4fSDavid du Colombier 		// random
9179a747e4fSDavid du Colombier 		memmove(p, m->u.serverHello.random, RandomSize);
9189a747e4fSDavid du Colombier 		p += RandomSize;
9199a747e4fSDavid du Colombier 
9209a747e4fSDavid du Colombier 		// sid
9219a747e4fSDavid du Colombier 		n = m->u.serverHello.sid->len;
9229a747e4fSDavid du Colombier 		assert(n < 256);
9239a747e4fSDavid du Colombier 		p[0] = n;
9249a747e4fSDavid du Colombier 		memmove(p+1, m->u.serverHello.sid->data, n);
9259a747e4fSDavid du Colombier 		p += n+1;
9269a747e4fSDavid du Colombier 
9279a747e4fSDavid du Colombier 		put16(p, m->u.serverHello.cipher);
9289a747e4fSDavid du Colombier 		p += 2;
9299a747e4fSDavid du Colombier 		p[0] = m->u.serverHello.compressor;
9309a747e4fSDavid du Colombier 		p += 1;
9319a747e4fSDavid du Colombier 		break;
9329a747e4fSDavid du Colombier 	case HServerHelloDone:
9339a747e4fSDavid du Colombier 		break;
9349a747e4fSDavid du Colombier 	case HCertificate:
9359a747e4fSDavid du Colombier 		nn = 0;
9369a747e4fSDavid du Colombier 		for(i = 0; i < m->u.certificate.ncert; i++)
9379a747e4fSDavid du Colombier 			nn += 3 + m->u.certificate.certs[i]->len;
9389a747e4fSDavid du Colombier 		if(p + 3 + nn - sendbuf > sizeof(sendbuf)) {
9399a747e4fSDavid du Colombier 			tlsError(c, EInternalError, "output buffer too small for certificate");
9409a747e4fSDavid du Colombier 			goto Err;
9419a747e4fSDavid du Colombier 		}
9429a747e4fSDavid du Colombier 		put24(p, nn);
9439a747e4fSDavid du Colombier 		p += 3;
9449a747e4fSDavid du Colombier 		for(i = 0; i < m->u.certificate.ncert; i++){
9459a747e4fSDavid du Colombier 			put24(p, m->u.certificate.certs[i]->len);
9469a747e4fSDavid du Colombier 			p += 3;
9479a747e4fSDavid du Colombier 			memmove(p, m->u.certificate.certs[i]->data, m->u.certificate.certs[i]->len);
9489a747e4fSDavid du Colombier 			p += m->u.certificate.certs[i]->len;
9499a747e4fSDavid du Colombier 		}
9509a747e4fSDavid du Colombier 		break;
9519a747e4fSDavid du Colombier 	case HClientKeyExchange:
9529a747e4fSDavid du Colombier 		n = m->u.clientKeyExchange.key->len;
9539a747e4fSDavid du Colombier 		if(c->version != SSL3Version){
9549a747e4fSDavid du Colombier 			put16(p, n);
9559a747e4fSDavid du Colombier 			p += 2;
9569a747e4fSDavid du Colombier 		}
9579a747e4fSDavid du Colombier 		memmove(p, m->u.clientKeyExchange.key->data, n);
9589a747e4fSDavid du Colombier 		p += n;
9599a747e4fSDavid du Colombier 		break;
9609a747e4fSDavid du Colombier 	case HFinished:
9619a747e4fSDavid du Colombier 		memmove(p, m->u.finished.verify, m->u.finished.n);
9629a747e4fSDavid du Colombier 		p += m->u.finished.n;
9639a747e4fSDavid du Colombier 		break;
9649a747e4fSDavid du Colombier 	}
9659a747e4fSDavid du Colombier 
9669a747e4fSDavid du Colombier 	// go back and fill in size
9679a747e4fSDavid du Colombier 	n = p-sendp;
9689a747e4fSDavid du Colombier 	assert(p <= sendbuf+sizeof(sendbuf));
9699a747e4fSDavid du Colombier 	put24(sendp+1, n-4);
9709a747e4fSDavid du Colombier 
9719a747e4fSDavid du Colombier 	// remember hash of Handshake messages
9729a747e4fSDavid du Colombier 	if(m->tag != HHelloRequest) {
973e94c8628SDavid du Colombier 		msgHash(c, sendp, n);
9749a747e4fSDavid du Colombier 	}
9759a747e4fSDavid du Colombier 
9769a747e4fSDavid du Colombier 	sendp = p;
9779a747e4fSDavid du Colombier 	if(act == AFlush){
9789a747e4fSDavid du Colombier 		sendp = sendbuf;
9799a747e4fSDavid du Colombier 		if(write(c->hand, sendbuf, p-sendbuf) < 0){
9809a747e4fSDavid du Colombier 			fprint(2, "write error: %r\n");
9819a747e4fSDavid du Colombier 			goto Err;
9829a747e4fSDavid du Colombier 		}
9839a747e4fSDavid du Colombier 	}
9849a747e4fSDavid du Colombier 	msgClear(m);
9859a747e4fSDavid du Colombier 	return 1;
9869a747e4fSDavid du Colombier Err:
9879a747e4fSDavid du Colombier 	msgClear(m);
9889a747e4fSDavid du Colombier 	return 0;
9899a747e4fSDavid du Colombier }
9909a747e4fSDavid du Colombier 
9919a747e4fSDavid du Colombier static uchar*
tlsReadN(TlsConnection * c,int n)9929a747e4fSDavid du Colombier tlsReadN(TlsConnection *c, int n)
9939a747e4fSDavid du Colombier {
9949a747e4fSDavid du Colombier 	uchar *p;
9959a747e4fSDavid du Colombier 	int nn, nr;
9969a747e4fSDavid du Colombier 
9979a747e4fSDavid du Colombier 	nn = c->ep - c->rp;
9989a747e4fSDavid du Colombier 	if(nn < n){
9999a747e4fSDavid du Colombier 		if(c->rp != c->buf){
10009a747e4fSDavid du Colombier 			memmove(c->buf, c->rp, nn);
10019a747e4fSDavid du Colombier 			c->rp = c->buf;
10029a747e4fSDavid du Colombier 			c->ep = &c->buf[nn];
10039a747e4fSDavid du Colombier 		}
10049a747e4fSDavid du Colombier 		for(; nn < n; nn += nr) {
10059a747e4fSDavid du Colombier 			nr = read(c->hand, &c->rp[nn], n - nn);
10069a747e4fSDavid du Colombier 			if(nr <= 0)
10079a747e4fSDavid du Colombier 				return nil;
10089a747e4fSDavid du Colombier 			c->ep += nr;
10099a747e4fSDavid du Colombier 		}
10109a747e4fSDavid du Colombier 	}
10119a747e4fSDavid du Colombier 	p = c->rp;
10129a747e4fSDavid du Colombier 	c->rp += n;
10139a747e4fSDavid du Colombier 	return p;
10149a747e4fSDavid du Colombier }
10159a747e4fSDavid du Colombier 
10169a747e4fSDavid du Colombier static int
msgRecv(TlsConnection * c,Msg * m)10179a747e4fSDavid du Colombier msgRecv(TlsConnection *c, Msg *m)
10189a747e4fSDavid du Colombier {
10199a747e4fSDavid du Colombier 	uchar *p;
1020cac18bb6SDavid du Colombier 	int type, n, nn, nx, i, nsid, nrandom, nciph;
10219a747e4fSDavid du Colombier 
10229a747e4fSDavid du Colombier 	for(;;) {
10239a747e4fSDavid du Colombier 		p = tlsReadN(c, 4);
10249a747e4fSDavid du Colombier 		if(p == nil)
10259a747e4fSDavid du Colombier 			return 0;
10269a747e4fSDavid du Colombier 		type = p[0];
10279a747e4fSDavid du Colombier 		n = get24(p+1);
10289a747e4fSDavid du Colombier 
10299a747e4fSDavid du Colombier 		if(type != HHelloRequest)
10309a747e4fSDavid du Colombier 			break;
10319a747e4fSDavid du Colombier 		if(n != 0) {
10329a747e4fSDavid du Colombier 			tlsError(c, EDecodeError, "invalid hello request during handshake");
10339a747e4fSDavid du Colombier 			return 0;
10349a747e4fSDavid du Colombier 		}
10359a747e4fSDavid du Colombier 	}
10369a747e4fSDavid du Colombier 
10379a747e4fSDavid du Colombier 	if(n > sizeof(c->buf)) {
10389a747e4fSDavid du Colombier 		tlsError(c, EDecodeError, "handshake message too long %d %d", n, sizeof(c->buf));
10399a747e4fSDavid du Colombier 		return 0;
10409a747e4fSDavid du Colombier 	}
10419a747e4fSDavid du Colombier 
10429a747e4fSDavid du Colombier 	if(type == HSSL2ClientHello){
10439a747e4fSDavid du Colombier 		/* Cope with an SSL3 ClientHello expressed in SSL2 record format.
10449a747e4fSDavid du Colombier 			This is sent by some clients that we must interoperate
10459a747e4fSDavid du Colombier 			with, such as Java's JSSE and Microsoft's Internet Explorer. */
10469a747e4fSDavid du Colombier 		p = tlsReadN(c, n);
10479a747e4fSDavid du Colombier 		if(p == nil)
10489a747e4fSDavid du Colombier 			return 0;
1049e94c8628SDavid du Colombier 		msgHash(c, p, n);
10509a747e4fSDavid du Colombier 		m->tag = HClientHello;
10519a747e4fSDavid du Colombier 		if(n < 22)
10529a747e4fSDavid du Colombier 			goto Short;
10539a747e4fSDavid du Colombier 		m->u.clientHello.version = get16(p+1);
10549a747e4fSDavid du Colombier 		p += 3;
10559a747e4fSDavid du Colombier 		n -= 3;
10569a747e4fSDavid du Colombier 		nn = get16(p); /* cipher_spec_len */
10579a747e4fSDavid du Colombier 		nsid = get16(p + 2);
10589a747e4fSDavid du Colombier 		nrandom = get16(p + 4);
10599a747e4fSDavid du Colombier 		p += 6;
10609a747e4fSDavid du Colombier 		n -= 6;
10619a747e4fSDavid du Colombier 		if(nsid != 0 	/* no sid's, since shouldn't restart using ssl2 header */
10629a747e4fSDavid du Colombier 				|| nrandom < 16 || nn % 3)
10639a747e4fSDavid du Colombier 			goto Err;
10649a747e4fSDavid du Colombier 		if(c->trace && (n - nrandom != nn))
10659a747e4fSDavid du Colombier 			c->trace("n-nrandom!=nn: n=%d nrandom=%d nn=%d\n", n, nrandom, nn);
10669a747e4fSDavid du Colombier 		/* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */
10679a747e4fSDavid du Colombier 		nciph = 0;
10689a747e4fSDavid du Colombier 		for(i = 0; i < nn; i += 3)
10699a747e4fSDavid du Colombier 			if(p[i] == 0)
10709a747e4fSDavid du Colombier 				nciph++;
10719a747e4fSDavid du Colombier 		m->u.clientHello.ciphers = newints(nciph);
10729a747e4fSDavid du Colombier 		nciph = 0;
10739a747e4fSDavid du Colombier 		for(i = 0; i < nn; i += 3)
10749a747e4fSDavid du Colombier 			if(p[i] == 0)
10759a747e4fSDavid du Colombier 				m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]);
10769a747e4fSDavid du Colombier 		p += nn;
10779a747e4fSDavid du Colombier 		m->u.clientHello.sid = makebytes(nil, 0);
10789a747e4fSDavid du Colombier 		if(nrandom > RandomSize)
10799a747e4fSDavid du Colombier 			nrandom = RandomSize;
10809a747e4fSDavid du Colombier 		memset(m->u.clientHello.random, 0, RandomSize - nrandom);
10819a747e4fSDavid du Colombier 		memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom);
10829a747e4fSDavid du Colombier 		m->u.clientHello.compressors = newbytes(1);
10839a747e4fSDavid du Colombier 		m->u.clientHello.compressors->data[0] = CompressionNull;
10849a747e4fSDavid du Colombier 		goto Ok;
10859a747e4fSDavid du Colombier 	}
10869a747e4fSDavid du Colombier 
1087e94c8628SDavid du Colombier 	msgHash(c, p, 4);
10889a747e4fSDavid du Colombier 
10899a747e4fSDavid du Colombier 	p = tlsReadN(c, n);
10909a747e4fSDavid du Colombier 	if(p == nil)
10919a747e4fSDavid du Colombier 		return 0;
10929a747e4fSDavid du Colombier 
1093e94c8628SDavid du Colombier 	msgHash(c, p, n);
10949a747e4fSDavid du Colombier 
10959a747e4fSDavid du Colombier 	m->tag = type;
10969a747e4fSDavid du Colombier 
10979a747e4fSDavid du Colombier 	switch(type) {
10989a747e4fSDavid du Colombier 	default:
10999a747e4fSDavid du Colombier 		tlsError(c, EUnexpectedMessage, "can't decode a %d", type);
11009a747e4fSDavid du Colombier 		goto Err;
11019a747e4fSDavid du Colombier 	case HClientHello:
11029a747e4fSDavid du Colombier 		if(n < 2)
11039a747e4fSDavid du Colombier 			goto Short;
11049a747e4fSDavid du Colombier 		m->u.clientHello.version = get16(p);
11059a747e4fSDavid du Colombier 		p += 2;
11069a747e4fSDavid du Colombier 		n -= 2;
11079a747e4fSDavid du Colombier 
11089a747e4fSDavid du Colombier 		if(n < RandomSize)
11099a747e4fSDavid du Colombier 			goto Short;
11109a747e4fSDavid du Colombier 		memmove(m->u.clientHello.random, p, RandomSize);
11119a747e4fSDavid du Colombier 		p += RandomSize;
11129a747e4fSDavid du Colombier 		n -= RandomSize;
11139a747e4fSDavid du Colombier 		if(n < 1 || n < p[0]+1)
11149a747e4fSDavid du Colombier 			goto Short;
11159a747e4fSDavid du Colombier 		m->u.clientHello.sid = makebytes(p+1, p[0]);
11169a747e4fSDavid du Colombier 		p += m->u.clientHello.sid->len+1;
11179a747e4fSDavid du Colombier 		n -= m->u.clientHello.sid->len+1;
11189a747e4fSDavid du Colombier 
11199a747e4fSDavid du Colombier 		if(n < 2)
11209a747e4fSDavid du Colombier 			goto Short;
11219a747e4fSDavid du Colombier 		nn = get16(p);
11229a747e4fSDavid du Colombier 		p += 2;
11239a747e4fSDavid du Colombier 		n -= 2;
11249a747e4fSDavid du Colombier 
11259a747e4fSDavid du Colombier 		if((nn & 1) || n < nn || nn < 2)
11269a747e4fSDavid du Colombier 			goto Short;
11279a747e4fSDavid du Colombier 		m->u.clientHello.ciphers = newints(nn >> 1);
11289a747e4fSDavid du Colombier 		for(i = 0; i < nn; i += 2)
11299a747e4fSDavid du Colombier 			m->u.clientHello.ciphers->data[i >> 1] = get16(&p[i]);
11309a747e4fSDavid du Colombier 		p += nn;
11319a747e4fSDavid du Colombier 		n -= nn;
11329a747e4fSDavid du Colombier 
11339a747e4fSDavid du Colombier 		if(n < 1 || n < p[0]+1 || p[0] == 0)
11349a747e4fSDavid du Colombier 			goto Short;
11359a747e4fSDavid du Colombier 		nn = p[0];
11369a747e4fSDavid du Colombier 		m->u.clientHello.compressors = newbytes(nn);
11379a747e4fSDavid du Colombier 		memmove(m->u.clientHello.compressors->data, p+1, nn);
1138cac18bb6SDavid du Colombier 		p += nn + 1;
11399a747e4fSDavid du Colombier 		n -= nn + 1;
1140cac18bb6SDavid du Colombier 
1141cac18bb6SDavid du Colombier 		/* extensions */
1142cac18bb6SDavid du Colombier 		if(n == 0)
1143cac18bb6SDavid du Colombier 			break;
1144cac18bb6SDavid du Colombier 		if(n < 2)
1145cac18bb6SDavid du Colombier 			goto Short;
1146cac18bb6SDavid du Colombier 		nx = get16(p);
1147cac18bb6SDavid du Colombier 		p += 2;
1148cac18bb6SDavid du Colombier 		n -= 2;
1149cac18bb6SDavid du Colombier 		while(nx > 0){
1150cac18bb6SDavid du Colombier 			if(n < nx || nx < 4)
1151cac18bb6SDavid du Colombier 				goto Short;
1152cac18bb6SDavid du Colombier 			i = get16(p);
1153cac18bb6SDavid du Colombier 			nn = get16(p+2);
1154cac18bb6SDavid du Colombier 			if(nx < nn+4)
1155cac18bb6SDavid du Colombier 				goto Short;
1156cac18bb6SDavid du Colombier 			nx -= nn+4;
1157cac18bb6SDavid du Colombier 			p += 4;
1158cac18bb6SDavid du Colombier 			n -= 4;
1159cac18bb6SDavid du Colombier 			if(i == ExtSigalgs){
1160cac18bb6SDavid du Colombier 				if(get16(p) != nn-2)
1161cac18bb6SDavid du Colombier 					goto Short;
1162cac18bb6SDavid du Colombier 				p += 2;
1163cac18bb6SDavid du Colombier 				n -= 2;
1164cac18bb6SDavid du Colombier 				nn -= 2;
1165cac18bb6SDavid du Colombier 				m->u.clientHello.sigAlgs = newints(nn/2);
1166cac18bb6SDavid du Colombier 				for(i = 0; i < nn; i += 2)
1167cac18bb6SDavid du Colombier 					m->u.clientHello.sigAlgs->data[i >> 1] = get16(&p[i]);
1168cac18bb6SDavid du Colombier 			}
1169cac18bb6SDavid du Colombier 			p += nn;
1170cac18bb6SDavid du Colombier 			n -= nn;
1171cac18bb6SDavid du Colombier 		}
11729a747e4fSDavid du Colombier 		break;
11739a747e4fSDavid du Colombier 	case HServerHello:
11749a747e4fSDavid du Colombier 		if(n < 2)
11759a747e4fSDavid du Colombier 			goto Short;
11769a747e4fSDavid du Colombier 		m->u.serverHello.version = get16(p);
11779a747e4fSDavid du Colombier 		p += 2;
11789a747e4fSDavid du Colombier 		n -= 2;
11799a747e4fSDavid du Colombier 
11809a747e4fSDavid du Colombier 		if(n < RandomSize)
11819a747e4fSDavid du Colombier 			goto Short;
11829a747e4fSDavid du Colombier 		memmove(m->u.serverHello.random, p, RandomSize);
11839a747e4fSDavid du Colombier 		p += RandomSize;
11849a747e4fSDavid du Colombier 		n -= RandomSize;
11859a747e4fSDavid du Colombier 
11869a747e4fSDavid du Colombier 		if(n < 1 || n < p[0]+1)
11879a747e4fSDavid du Colombier 			goto Short;
11889a747e4fSDavid du Colombier 		m->u.serverHello.sid = makebytes(p+1, p[0]);
11899a747e4fSDavid du Colombier 		p += m->u.serverHello.sid->len+1;
11909a747e4fSDavid du Colombier 		n -= m->u.serverHello.sid->len+1;
11919a747e4fSDavid du Colombier 
11929a747e4fSDavid du Colombier 		if(n < 3)
11939a747e4fSDavid du Colombier 			goto Short;
11949a747e4fSDavid du Colombier 		m->u.serverHello.cipher = get16(p);
11959a747e4fSDavid du Colombier 		m->u.serverHello.compressor = p[2];
11969a747e4fSDavid du Colombier 		n -= 3;
11979a747e4fSDavid du Colombier 		break;
11989a747e4fSDavid du Colombier 	case HCertificate:
11999a747e4fSDavid du Colombier 		if(n < 3)
12009a747e4fSDavid du Colombier 			goto Short;
12019a747e4fSDavid du Colombier 		nn = get24(p);
12029a747e4fSDavid du Colombier 		p += 3;
12039a747e4fSDavid du Colombier 		n -= 3;
12049a747e4fSDavid du Colombier 		if(n != nn)
12059a747e4fSDavid du Colombier 			goto Short;
12069a747e4fSDavid du Colombier 		/* certs */
12079a747e4fSDavid du Colombier 		i = 0;
12089a747e4fSDavid du Colombier 		while(n > 0) {
12099a747e4fSDavid du Colombier 			if(n < 3)
12109a747e4fSDavid du Colombier 				goto Short;
12119a747e4fSDavid du Colombier 			nn = get24(p);
12129a747e4fSDavid du Colombier 			p += 3;
12139a747e4fSDavid du Colombier 			n -= 3;
12149a747e4fSDavid du Colombier 			if(nn > n)
12159a747e4fSDavid du Colombier 				goto Short;
12169a747e4fSDavid du Colombier 			m->u.certificate.ncert = i+1;
12179a747e4fSDavid du Colombier 			m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes));
12189a747e4fSDavid du Colombier 			m->u.certificate.certs[i] = makebytes(p, nn);
12199a747e4fSDavid du Colombier 			p += nn;
12209a747e4fSDavid du Colombier 			n -= nn;
12219a747e4fSDavid du Colombier 			i++;
12229a747e4fSDavid du Colombier 		}
12239a747e4fSDavid du Colombier 		break;
12249a747e4fSDavid du Colombier 	case HCertificateRequest:
122590630c3aSDavid du Colombier 		if(n < 1)
122690630c3aSDavid du Colombier 			goto Short;
122790630c3aSDavid du Colombier 		nn = p[0];
122890630c3aSDavid du Colombier 		p += 1;
122990630c3aSDavid du Colombier 		n -= 1;
123090630c3aSDavid du Colombier 		if(nn < 1 || nn > n)
123190630c3aSDavid du Colombier 			goto Short;
123290630c3aSDavid du Colombier 		m->u.certificateRequest.types = makebytes(p, nn);
123390630c3aSDavid du Colombier 		p += nn;
123490630c3aSDavid du Colombier 		n -= nn;
12359a747e4fSDavid du Colombier 		if(n < 2)
12369a747e4fSDavid du Colombier 			goto Short;
12379a747e4fSDavid du Colombier 		nn = get16(p);
12389a747e4fSDavid du Colombier 		p += 2;
12399a747e4fSDavid du Colombier 		n -= 2;
124025fc6993SDavid du Colombier 		/* nn == 0 can happen; yahoo's servers do it */
124125fc6993SDavid du Colombier 		if(nn != n)
12429a747e4fSDavid du Colombier 			goto Short;
12439a747e4fSDavid du Colombier 		/* cas */
12449a747e4fSDavid du Colombier 		i = 0;
12459a747e4fSDavid du Colombier 		while(n > 0) {
12469a747e4fSDavid du Colombier 			if(n < 2)
12479a747e4fSDavid du Colombier 				goto Short;
12489a747e4fSDavid du Colombier 			nn = get16(p);
12499a747e4fSDavid du Colombier 			p += 2;
12509a747e4fSDavid du Colombier 			n -= 2;
12519a747e4fSDavid du Colombier 			if(nn < 1 || nn > n)
12529a747e4fSDavid du Colombier 				goto Short;
12539a747e4fSDavid du Colombier 			m->u.certificateRequest.nca = i+1;
125425fc6993SDavid du Colombier 			m->u.certificateRequest.cas = erealloc(
125525fc6993SDavid du Colombier 				m->u.certificateRequest.cas, (i+1)*sizeof(Bytes));
12569a747e4fSDavid du Colombier 			m->u.certificateRequest.cas[i] = makebytes(p, nn);
12579a747e4fSDavid du Colombier 			p += nn;
12589a747e4fSDavid du Colombier 			n -= nn;
12599a747e4fSDavid du Colombier 			i++;
12609a747e4fSDavid du Colombier 		}
12619a747e4fSDavid du Colombier 		break;
12629a747e4fSDavid du Colombier 	case HServerHelloDone:
12639a747e4fSDavid du Colombier 		break;
12649a747e4fSDavid du Colombier 	case HClientKeyExchange:
12659a747e4fSDavid du Colombier 		/*
12669a747e4fSDavid du Colombier 		 * this message depends upon the encryption selected
12679a747e4fSDavid du Colombier 		 * assume rsa.
12689a747e4fSDavid du Colombier 		 */
12699a747e4fSDavid du Colombier 		if(c->version == SSL3Version)
12709a747e4fSDavid du Colombier 			nn = n;
12719a747e4fSDavid du Colombier 		else{
12729a747e4fSDavid du Colombier 			if(n < 2)
12739a747e4fSDavid du Colombier 				goto Short;
12749a747e4fSDavid du Colombier 			nn = get16(p);
12759a747e4fSDavid du Colombier 			p += 2;
12769a747e4fSDavid du Colombier 			n -= 2;
12779a747e4fSDavid du Colombier 		}
12789a747e4fSDavid du Colombier 		if(n < nn)
12799a747e4fSDavid du Colombier 			goto Short;
12809a747e4fSDavid du Colombier 		m->u.clientKeyExchange.key = makebytes(p, nn);
12819a747e4fSDavid du Colombier 		n -= nn;
12829a747e4fSDavid du Colombier 		break;
12839a747e4fSDavid du Colombier 	case HFinished:
12849a747e4fSDavid du Colombier 		m->u.finished.n = c->finished.n;
12859a747e4fSDavid du Colombier 		if(n < m->u.finished.n)
12869a747e4fSDavid du Colombier 			goto Short;
12879a747e4fSDavid du Colombier 		memmove(m->u.finished.verify, p, m->u.finished.n);
12889a747e4fSDavid du Colombier 		n -= m->u.finished.n;
12899a747e4fSDavid du Colombier 		break;
12909a747e4fSDavid du Colombier 	}
12919a747e4fSDavid du Colombier 
12929a747e4fSDavid du Colombier 	if(type != HClientHello && n != 0)
12939a747e4fSDavid du Colombier 		goto Short;
12949a747e4fSDavid du Colombier Ok:
12959a747e4fSDavid du Colombier 	if(c->trace){
1296ed50b0e8SDavid du Colombier 		char *buf;
1297ed50b0e8SDavid du Colombier 		buf = emalloc(8000);
1298ed50b0e8SDavid du Colombier 		c->trace("recv %s", msgPrint(buf, 8000, m));
1299ed50b0e8SDavid du Colombier 		free(buf);
13009a747e4fSDavid du Colombier 	}
13019a747e4fSDavid du Colombier 	return 1;
13029a747e4fSDavid du Colombier Short:
13039a747e4fSDavid du Colombier 	tlsError(c, EDecodeError, "handshake message has invalid length");
13049a747e4fSDavid du Colombier Err:
13059a747e4fSDavid du Colombier 	msgClear(m);
13069a747e4fSDavid du Colombier 	return 0;
13079a747e4fSDavid du Colombier }
13089a747e4fSDavid du Colombier 
13099a747e4fSDavid du Colombier static void
msgClear(Msg * m)13109a747e4fSDavid du Colombier msgClear(Msg *m)
13119a747e4fSDavid du Colombier {
13129a747e4fSDavid du Colombier 	int i;
13139a747e4fSDavid du Colombier 
13149a747e4fSDavid du Colombier 	switch(m->tag) {
13159a747e4fSDavid du Colombier 	default:
131614cc0f53SDavid du Colombier 		sysfatal("msgClear: unknown message type: %d", m->tag);
13179a747e4fSDavid du Colombier 	case HHelloRequest:
13189a747e4fSDavid du Colombier 		break;
13199a747e4fSDavid du Colombier 	case HClientHello:
13209a747e4fSDavid du Colombier 		freebytes(m->u.clientHello.sid);
13219a747e4fSDavid du Colombier 		freeints(m->u.clientHello.ciphers);
1322fc8e446fSDavid du Colombier 		m->u.clientHello.ciphers = nil;
13239a747e4fSDavid du Colombier 		freebytes(m->u.clientHello.compressors);
1324cac18bb6SDavid du Colombier 		freeints(m->u.clientHello.sigAlgs);
13259a747e4fSDavid du Colombier 		break;
13269a747e4fSDavid du Colombier 	case HServerHello:
13279a747e4fSDavid du Colombier 		freebytes(m->u.clientHello.sid);
13289a747e4fSDavid du Colombier 		break;
13299a747e4fSDavid du Colombier 	case HCertificate:
13309a747e4fSDavid du Colombier 		for(i=0; i<m->u.certificate.ncert; i++)
13319a747e4fSDavid du Colombier 			freebytes(m->u.certificate.certs[i]);
13329a747e4fSDavid du Colombier 		free(m->u.certificate.certs);
13339a747e4fSDavid du Colombier 		break;
13349a747e4fSDavid du Colombier 	case HCertificateRequest:
13359a747e4fSDavid du Colombier 		freebytes(m->u.certificateRequest.types);
13369a747e4fSDavid du Colombier 		for(i=0; i<m->u.certificateRequest.nca; i++)
13379a747e4fSDavid du Colombier 			freebytes(m->u.certificateRequest.cas[i]);
13389a747e4fSDavid du Colombier 		free(m->u.certificateRequest.cas);
13399a747e4fSDavid du Colombier 		break;
13409a747e4fSDavid du Colombier 	case HServerHelloDone:
13419a747e4fSDavid du Colombier 		break;
13429a747e4fSDavid du Colombier 	case HClientKeyExchange:
13439a747e4fSDavid du Colombier 		freebytes(m->u.clientKeyExchange.key);
13449a747e4fSDavid du Colombier 		break;
13459a747e4fSDavid du Colombier 	case HFinished:
13469a747e4fSDavid du Colombier 		break;
13479a747e4fSDavid du Colombier 	}
13489a747e4fSDavid du Colombier 	memset(m, 0, sizeof(Msg));
13499a747e4fSDavid du Colombier }
13509a747e4fSDavid du Colombier 
13519a747e4fSDavid du Colombier static char *
bytesPrint(char * bs,char * be,char * s0,Bytes * b,char * s1)13529a747e4fSDavid du Colombier bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1)
13539a747e4fSDavid du Colombier {
13549a747e4fSDavid du Colombier 	int i;
13559a747e4fSDavid du Colombier 
13569a747e4fSDavid du Colombier 	if(s0)
13579a747e4fSDavid du Colombier 		bs = seprint(bs, be, "%s", s0);
13589a747e4fSDavid du Colombier 	bs = seprint(bs, be, "[");
13599a747e4fSDavid du Colombier 	if(b == nil)
13609a747e4fSDavid du Colombier 		bs = seprint(bs, be, "nil");
13619a747e4fSDavid du Colombier 	else
13629a747e4fSDavid du Colombier 		for(i=0; i<b->len; i++)
13639a747e4fSDavid du Colombier 			bs = seprint(bs, be, "%.2x ", b->data[i]);
13649a747e4fSDavid du Colombier 	bs = seprint(bs, be, "]");
13659a747e4fSDavid du Colombier 	if(s1)
13669a747e4fSDavid du Colombier 		bs = seprint(bs, be, "%s", s1);
13679a747e4fSDavid du Colombier 	return bs;
13689a747e4fSDavid du Colombier }
13699a747e4fSDavid du Colombier 
13709a747e4fSDavid du Colombier static char *
intsPrint(char * bs,char * be,char * s0,Ints * b,char * s1)13719a747e4fSDavid du Colombier intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1)
13729a747e4fSDavid du Colombier {
13739a747e4fSDavid du Colombier 	int i;
13749a747e4fSDavid du Colombier 
13759a747e4fSDavid du Colombier 	if(s0)
13769a747e4fSDavid du Colombier 		bs = seprint(bs, be, "%s", s0);
13779a747e4fSDavid du Colombier 	bs = seprint(bs, be, "[");
13789a747e4fSDavid du Colombier 	if(b == nil)
13799a747e4fSDavid du Colombier 		bs = seprint(bs, be, "nil");
13809a747e4fSDavid du Colombier 	else
13819a747e4fSDavid du Colombier 		for(i=0; i<b->len; i++)
13829a747e4fSDavid du Colombier 			bs = seprint(bs, be, "%x ", b->data[i]);
13839a747e4fSDavid du Colombier 	bs = seprint(bs, be, "]");
13849a747e4fSDavid du Colombier 	if(s1)
13859a747e4fSDavid du Colombier 		bs = seprint(bs, be, "%s", s1);
13869a747e4fSDavid du Colombier 	return bs;
13879a747e4fSDavid du Colombier }
13889a747e4fSDavid du Colombier 
13899a747e4fSDavid du Colombier static char*
msgPrint(char * buf,int n,Msg * m)13909a747e4fSDavid du Colombier msgPrint(char *buf, int n, Msg *m)
13919a747e4fSDavid du Colombier {
13929a747e4fSDavid du Colombier 	int i;
13939a747e4fSDavid du Colombier 	char *bs = buf, *be = buf+n;
13949a747e4fSDavid du Colombier 
13959a747e4fSDavid du Colombier 	switch(m->tag) {
13969a747e4fSDavid du Colombier 	default:
13979a747e4fSDavid du Colombier 		bs = seprint(bs, be, "unknown %d\n", m->tag);
13989a747e4fSDavid du Colombier 		break;
13999a747e4fSDavid du Colombier 	case HClientHello:
14009a747e4fSDavid du Colombier 		bs = seprint(bs, be, "ClientHello\n");
14019a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\tversion: %.4x\n", m->u.clientHello.version);
14029a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\trandom: ");
14039a747e4fSDavid du Colombier 		for(i=0; i<RandomSize; i++)
14049a747e4fSDavid du Colombier 			bs = seprint(bs, be, "%.2x", m->u.clientHello.random[i]);
14059a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\n");
14069a747e4fSDavid du Colombier 		bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n");
14079a747e4fSDavid du Colombier 		bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n");
14089a747e4fSDavid du Colombier 		bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n");
1409cac18bb6SDavid du Colombier 		if(m->u.clientHello.sigAlgs != nil)
1410cac18bb6SDavid du Colombier 			bs = intsPrint(bs, be, "\tsigAlgs: ", m->u.clientHello.sigAlgs, "\n");
14119a747e4fSDavid du Colombier 		break;
14129a747e4fSDavid du Colombier 	case HServerHello:
14139a747e4fSDavid du Colombier 		bs = seprint(bs, be, "ServerHello\n");
14149a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\tversion: %.4x\n", m->u.serverHello.version);
14159a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\trandom: ");
14169a747e4fSDavid du Colombier 		for(i=0; i<RandomSize; i++)
14179a747e4fSDavid du Colombier 			bs = seprint(bs, be, "%.2x", m->u.serverHello.random[i]);
14189a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\n");
14199a747e4fSDavid du Colombier 		bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n");
14209a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher);
14219a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor);
14229a747e4fSDavid du Colombier 		break;
14239a747e4fSDavid du Colombier 	case HCertificate:
14249a747e4fSDavid du Colombier 		bs = seprint(bs, be, "Certificate\n");
14259a747e4fSDavid du Colombier 		for(i=0; i<m->u.certificate.ncert; i++)
14269a747e4fSDavid du Colombier 			bs = bytesPrint(bs, be, "\t", m->u.certificate.certs[i], "\n");
14279a747e4fSDavid du Colombier 		break;
14289a747e4fSDavid du Colombier 	case HCertificateRequest:
14299a747e4fSDavid du Colombier 		bs = seprint(bs, be, "CertificateRequest\n");
14309a747e4fSDavid du Colombier 		bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n");
14319a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\tcertificateauthorities\n");
14329a747e4fSDavid du Colombier 		for(i=0; i<m->u.certificateRequest.nca; i++)
14339a747e4fSDavid du Colombier 			bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n");
14349a747e4fSDavid du Colombier 		break;
14359a747e4fSDavid du Colombier 	case HServerHelloDone:
14369a747e4fSDavid du Colombier 		bs = seprint(bs, be, "ServerHelloDone\n");
14379a747e4fSDavid du Colombier 		break;
14389a747e4fSDavid du Colombier 	case HClientKeyExchange:
14399a747e4fSDavid du Colombier 		bs = seprint(bs, be, "HClientKeyExchange\n");
14409a747e4fSDavid du Colombier 		bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n");
14419a747e4fSDavid du Colombier 		break;
14429a747e4fSDavid du Colombier 	case HFinished:
14439a747e4fSDavid du Colombier 		bs = seprint(bs, be, "HFinished\n");
14449a747e4fSDavid du Colombier 		for(i=0; i<m->u.finished.n; i++)
14459a747e4fSDavid du Colombier 			bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]);
14469a747e4fSDavid du Colombier 		bs = seprint(bs, be, "\n");
14479a747e4fSDavid du Colombier 		break;
14489a747e4fSDavid du Colombier 	}
14499a747e4fSDavid du Colombier 	USED(bs);
14509a747e4fSDavid du Colombier 	return buf;
14519a747e4fSDavid du Colombier }
14529a747e4fSDavid du Colombier 
14539a747e4fSDavid du Colombier static void
tlsError(TlsConnection * c,int err,char * fmt,...)14549a747e4fSDavid du Colombier tlsError(TlsConnection *c, int err, char *fmt, ...)
14559a747e4fSDavid du Colombier {
14569a747e4fSDavid du Colombier 	char msg[512];
14579a747e4fSDavid du Colombier 	va_list arg;
14589a747e4fSDavid du Colombier 
14599a747e4fSDavid du Colombier 	va_start(arg, fmt);
14609a747e4fSDavid du Colombier 	vseprint(msg, msg+sizeof(msg), fmt, arg);
14619a747e4fSDavid du Colombier 	va_end(arg);
14629a747e4fSDavid du Colombier 	if(c->trace)
14639a747e4fSDavid du Colombier 		c->trace("tlsError: %s\n", msg);
14649a747e4fSDavid du Colombier 	else if(c->erred)
14659a747e4fSDavid du Colombier 		fprint(2, "double error: %r, %s", msg);
14669a747e4fSDavid du Colombier 	else
14679a747e4fSDavid du Colombier 		werrstr("tls: local %s", msg);
14689a747e4fSDavid du Colombier 	c->erred = 1;
14699a747e4fSDavid du Colombier 	fprint(c->ctl, "alert %d", err);
14709a747e4fSDavid du Colombier }
14719a747e4fSDavid du Colombier 
14729a747e4fSDavid du Colombier // commit to specific version number
14739a747e4fSDavid du Colombier static int
setVersion(TlsConnection * c,int version)14749a747e4fSDavid du Colombier setVersion(TlsConnection *c, int version)
14759a747e4fSDavid du Colombier {
14769a747e4fSDavid du Colombier 	if(c->verset || version > MaxProtoVersion || version < MinProtoVersion)
14779a747e4fSDavid du Colombier 		return -1;
14789a747e4fSDavid du Colombier 	if(version > c->version)
14799a747e4fSDavid du Colombier 		version = c->version;
1480e94c8628SDavid du Colombier 	switch(version) {
1481e94c8628SDavid du Colombier 	case SSL3Version:
14829a747e4fSDavid du Colombier 		c->finished.n = SSL3FinishedLen;
1483daa4c21dSDavid du Colombier 		return -1;
1484e94c8628SDavid du Colombier 	case TLS10Version:
1485e94c8628SDavid du Colombier 	case TLS11Version:
1486e94c8628SDavid du Colombier 	case TLS12Version:
14879a747e4fSDavid du Colombier 		c->finished.n = TLSFinishedLen;
1488e94c8628SDavid du Colombier 		break;
1489e94c8628SDavid du Colombier 	default:
14909a747e4fSDavid du Colombier 		return -1;
1491e94c8628SDavid du Colombier 	}
1492e94c8628SDavid du Colombier 	c->version = version;
14939a747e4fSDavid du Colombier 	c->verset = 1;
14949a747e4fSDavid du Colombier 	return fprint(c->ctl, "version 0x%x", version);
14959a747e4fSDavid du Colombier }
14969a747e4fSDavid du Colombier 
14979a747e4fSDavid du Colombier // confirm that received Finished message matches the expected value
14989a747e4fSDavid du Colombier static int
finishedMatch(TlsConnection * c,Finished * f)14999a747e4fSDavid du Colombier finishedMatch(TlsConnection *c, Finished *f)
15009a747e4fSDavid du Colombier {
15019a747e4fSDavid du Colombier 	return memcmp(f->verify, c->finished.verify, f->n) == 0;
15029a747e4fSDavid du Colombier }
15039a747e4fSDavid du Colombier 
15049a747e4fSDavid du Colombier // free memory associated with TlsConnection struct
15059a747e4fSDavid du Colombier //		(but don't close the TLS channel itself)
15069a747e4fSDavid du Colombier static void
tlsConnectionFree(TlsConnection * c)15079a747e4fSDavid du Colombier tlsConnectionFree(TlsConnection *c)
15089a747e4fSDavid du Colombier {
15099a747e4fSDavid du Colombier 	tlsSecClose(c->sec);
15109a747e4fSDavid du Colombier 	freebytes(c->sid);
15119a747e4fSDavid du Colombier 	freebytes(c->cert);
1512fc8e446fSDavid du Colombier 	memset(c, 0, sizeof *c);
15139a747e4fSDavid du Colombier 	free(c);
15149a747e4fSDavid du Colombier }
15159a747e4fSDavid du Colombier 
15169a747e4fSDavid du Colombier 
15179a747e4fSDavid du Colombier //================= cipher choices ========================
15189a747e4fSDavid du Colombier 
15199a747e4fSDavid du Colombier static int weakCipher[CipherMax] =
15209a747e4fSDavid du Colombier {
15219a747e4fSDavid du Colombier 	1,	/* TLS_NULL_WITH_NULL_NULL */
15229a747e4fSDavid du Colombier 	1,	/* TLS_RSA_WITH_NULL_MD5 */
15239a747e4fSDavid du Colombier 	1,	/* TLS_RSA_WITH_NULL_SHA */
15249a747e4fSDavid du Colombier 	1,	/* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */
1525651baa21SDavid du Colombier 	1,	/* TLS_RSA_WITH_RC4_128_MD5 */
1526651baa21SDavid du Colombier 	1,	/* TLS_RSA_WITH_RC4_128_SHA */
15279a747e4fSDavid du Colombier 	1,	/* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */
15289a747e4fSDavid du Colombier 	0,	/* TLS_RSA_WITH_IDEA_CBC_SHA */
15299a747e4fSDavid du Colombier 	1,	/* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */
15309a747e4fSDavid du Colombier 	0,	/* TLS_RSA_WITH_DES_CBC_SHA */
15319a747e4fSDavid du Colombier 	0,	/* TLS_RSA_WITH_3DES_EDE_CBC_SHA */
15329a747e4fSDavid du Colombier 	1,	/* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */
15339a747e4fSDavid du Colombier 	0,	/* TLS_DH_DSS_WITH_DES_CBC_SHA */
15349a747e4fSDavid du Colombier 	0,	/* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */
15359a747e4fSDavid du Colombier 	1,	/* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */
15369a747e4fSDavid du Colombier 	0,	/* TLS_DH_RSA_WITH_DES_CBC_SHA */
15379a747e4fSDavid du Colombier 	0,	/* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */
15389a747e4fSDavid du Colombier 	1,	/* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */
15399a747e4fSDavid du Colombier 	0,	/* TLS_DHE_DSS_WITH_DES_CBC_SHA */
15409a747e4fSDavid du Colombier 	0,	/* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */
15419a747e4fSDavid du Colombier 	1,	/* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */
15429a747e4fSDavid du Colombier 	0,	/* TLS_DHE_RSA_WITH_DES_CBC_SHA */
15439a747e4fSDavid du Colombier 	0,	/* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */
15449a747e4fSDavid du Colombier 	1,	/* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */
15459a747e4fSDavid du Colombier 	1,	/* TLS_DH_anon_WITH_RC4_128_MD5 */
15469a747e4fSDavid du Colombier 	1,	/* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */
15479a747e4fSDavid du Colombier 	1,	/* TLS_DH_anon_WITH_DES_CBC_SHA */
15489a747e4fSDavid du Colombier 	1,	/* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */
15499a747e4fSDavid du Colombier };
15509a747e4fSDavid du Colombier 
15519a747e4fSDavid du Colombier static int
setAlgs(TlsConnection * c,int a)15529a747e4fSDavid du Colombier setAlgs(TlsConnection *c, int a)
15539a747e4fSDavid du Colombier {
15549a747e4fSDavid du Colombier 	int i;
15559a747e4fSDavid du Colombier 
15569a747e4fSDavid du Colombier 	for(i = 0; i < nelem(cipherAlgs); i++){
15579a747e4fSDavid du Colombier 		if(cipherAlgs[i].tlsid == a){
15589a747e4fSDavid du Colombier 			c->enc = cipherAlgs[i].enc;
15599a747e4fSDavid du Colombier 			c->digest = cipherAlgs[i].digest;
15609a747e4fSDavid du Colombier 			c->nsecret = cipherAlgs[i].nsecret;
15619a747e4fSDavid du Colombier 			if(c->nsecret > MaxKeyData)
15629a747e4fSDavid du Colombier 				return 0;
15639a747e4fSDavid du Colombier 			return 1;
15649a747e4fSDavid du Colombier 		}
15659a747e4fSDavid du Colombier 	}
15669a747e4fSDavid du Colombier 	return 0;
15679a747e4fSDavid du Colombier }
15689a747e4fSDavid du Colombier 
15699a747e4fSDavid du Colombier static int
okCipher(Ints * cv)15709a747e4fSDavid du Colombier okCipher(Ints *cv)
15719a747e4fSDavid du Colombier {
15729a747e4fSDavid du Colombier 	int weak, i, j, c;
15739a747e4fSDavid du Colombier 
15749a747e4fSDavid du Colombier 	weak = 1;
15759a747e4fSDavid du Colombier 	for(i = 0; i < cv->len; i++) {
15769a747e4fSDavid du Colombier 		c = cv->data[i];
15779a747e4fSDavid du Colombier 		if(c >= CipherMax)
15789a747e4fSDavid du Colombier 			weak = 0;
15799a747e4fSDavid du Colombier 		else
15809a747e4fSDavid du Colombier 			weak &= weakCipher[c];
15819a747e4fSDavid du Colombier 		for(j = 0; j < nelem(cipherAlgs); j++)
15829a747e4fSDavid du Colombier 			if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c)
15839a747e4fSDavid du Colombier 				return c;
15849a747e4fSDavid du Colombier 	}
15859a747e4fSDavid du Colombier 	if(weak)
15869a747e4fSDavid du Colombier 		return -2;
15879a747e4fSDavid du Colombier 	return -1;
15889a747e4fSDavid du Colombier }
15899a747e4fSDavid du Colombier 
15909a747e4fSDavid du Colombier static int
okCompression(Bytes * cv)15919a747e4fSDavid du Colombier okCompression(Bytes *cv)
15929a747e4fSDavid du Colombier {
15939a747e4fSDavid du Colombier 	int i, j, c;
15949a747e4fSDavid du Colombier 
15959a747e4fSDavid du Colombier 	for(i = 0; i < cv->len; i++) {
15969a747e4fSDavid du Colombier 		c = cv->data[i];
15979a747e4fSDavid du Colombier 		for(j = 0; j < nelem(compressors); j++) {
15989a747e4fSDavid du Colombier 			if(compressors[j] == c)
15999a747e4fSDavid du Colombier 				return c;
16009a747e4fSDavid du Colombier 		}
16019a747e4fSDavid du Colombier 	}
16029a747e4fSDavid du Colombier 	return -1;
16039a747e4fSDavid du Colombier }
16049a747e4fSDavid du Colombier 
16059a747e4fSDavid du Colombier static Lock	ciphLock;
16069a747e4fSDavid du Colombier static int	nciphers;
16079a747e4fSDavid du Colombier 
16089a747e4fSDavid du Colombier static int
initCiphers(void)16099a747e4fSDavid du Colombier initCiphers(void)
16109a747e4fSDavid du Colombier {
16119a747e4fSDavid du Colombier 	enum {MaxAlgF = 1024, MaxAlgs = 10};
16129a747e4fSDavid du Colombier 	char s[MaxAlgF], *flds[MaxAlgs];
16139a747e4fSDavid du Colombier 	int i, j, n, ok;
16149a747e4fSDavid du Colombier 
16159a747e4fSDavid du Colombier 	lock(&ciphLock);
16169a747e4fSDavid du Colombier 	if(nciphers){
16179a747e4fSDavid du Colombier 		unlock(&ciphLock);
16189a747e4fSDavid du Colombier 		return nciphers;
16199a747e4fSDavid du Colombier 	}
16209a747e4fSDavid du Colombier 	j = open("#a/tls/encalgs", OREAD);
16219a747e4fSDavid du Colombier 	if(j < 0){
16229a747e4fSDavid du Colombier 		werrstr("can't open #a/tls/encalgs: %r");
1623fc8e446fSDavid du Colombier 		unlock(&ciphLock);
16249a747e4fSDavid du Colombier 		return 0;
16259a747e4fSDavid du Colombier 	}
16269a747e4fSDavid du Colombier 	n = read(j, s, MaxAlgF-1);
16279a747e4fSDavid du Colombier 	close(j);
16289a747e4fSDavid du Colombier 	if(n <= 0){
16299a747e4fSDavid du Colombier 		werrstr("nothing in #a/tls/encalgs: %r");
1630fc8e446fSDavid du Colombier 		unlock(&ciphLock);
16319a747e4fSDavid du Colombier 		return 0;
16329a747e4fSDavid du Colombier 	}
16339a747e4fSDavid du Colombier 	s[n] = 0;
16349a747e4fSDavid du Colombier 	n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
16359a747e4fSDavid du Colombier 	for(i = 0; i < nelem(cipherAlgs); i++){
16369a747e4fSDavid du Colombier 		ok = 0;
16379a747e4fSDavid du Colombier 		for(j = 0; j < n; j++){
16389a747e4fSDavid du Colombier 			if(strcmp(cipherAlgs[i].enc, flds[j]) == 0){
16399a747e4fSDavid du Colombier 				ok = 1;
16409a747e4fSDavid du Colombier 				break;
16419a747e4fSDavid du Colombier 			}
16429a747e4fSDavid du Colombier 		}
16439a747e4fSDavid du Colombier 		cipherAlgs[i].ok = ok;
16449a747e4fSDavid du Colombier 	}
16459a747e4fSDavid du Colombier 
16469a747e4fSDavid du Colombier 	j = open("#a/tls/hashalgs", OREAD);
16479a747e4fSDavid du Colombier 	if(j < 0){
16489a747e4fSDavid du Colombier 		werrstr("can't open #a/tls/hashalgs: %r");
1649fc8e446fSDavid du Colombier 		unlock(&ciphLock);
16509a747e4fSDavid du Colombier 		return 0;
16519a747e4fSDavid du Colombier 	}
16529a747e4fSDavid du Colombier 	n = read(j, s, MaxAlgF-1);
16539a747e4fSDavid du Colombier 	close(j);
16549a747e4fSDavid du Colombier 	if(n <= 0){
16559a747e4fSDavid du Colombier 		werrstr("nothing in #a/tls/hashalgs: %r");
1656fc8e446fSDavid du Colombier 		unlock(&ciphLock);
16579a747e4fSDavid du Colombier 		return 0;
16589a747e4fSDavid du Colombier 	}
16599a747e4fSDavid du Colombier 	s[n] = 0;
16609a747e4fSDavid du Colombier 	n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
16619a747e4fSDavid du Colombier 	for(i = 0; i < nelem(cipherAlgs); i++){
16629a747e4fSDavid du Colombier 		ok = 0;
16639a747e4fSDavid du Colombier 		for(j = 0; j < n; j++){
16649a747e4fSDavid du Colombier 			if(strcmp(cipherAlgs[i].digest, flds[j]) == 0){
16659a747e4fSDavid du Colombier 				ok = 1;
16669a747e4fSDavid du Colombier 				break;
16679a747e4fSDavid du Colombier 			}
16689a747e4fSDavid du Colombier 		}
16699a747e4fSDavid du Colombier 		cipherAlgs[i].ok &= ok;
16709a747e4fSDavid du Colombier 		if(cipherAlgs[i].ok)
16719a747e4fSDavid du Colombier 			nciphers++;
16729a747e4fSDavid du Colombier 	}
16739a747e4fSDavid du Colombier 	unlock(&ciphLock);
16749a747e4fSDavid du Colombier 	return nciphers;
16759a747e4fSDavid du Colombier }
16769a747e4fSDavid du Colombier 
16779a747e4fSDavid du Colombier static Ints*
makeciphers(void)16789a747e4fSDavid du Colombier makeciphers(void)
16799a747e4fSDavid du Colombier {
16809a747e4fSDavid du Colombier 	Ints *is;
16819a747e4fSDavid du Colombier 	int i, j;
16829a747e4fSDavid du Colombier 
16839a747e4fSDavid du Colombier 	is = newints(nciphers);
16849a747e4fSDavid du Colombier 	j = 0;
16859a747e4fSDavid du Colombier 	for(i = 0; i < nelem(cipherAlgs); i++){
16869a747e4fSDavid du Colombier 		if(cipherAlgs[i].ok)
16879a747e4fSDavid du Colombier 			is->data[j++] = cipherAlgs[i].tlsid;
16889a747e4fSDavid du Colombier 	}
16899a747e4fSDavid du Colombier 	return is;
16909a747e4fSDavid du Colombier }
16919a747e4fSDavid du Colombier 
16929a747e4fSDavid du Colombier 
16939a747e4fSDavid du Colombier 
16949a747e4fSDavid du Colombier //================= security functions ========================
16959a747e4fSDavid du Colombier 
16969a747e4fSDavid du Colombier // given X.509 certificate, set up connection to factotum
16979a747e4fSDavid du Colombier //	for using corresponding private key
16989a747e4fSDavid du Colombier static AuthRpc*
factotum_rsa_open(uchar * cert,int certlen)16999a747e4fSDavid du Colombier factotum_rsa_open(uchar *cert, int certlen)
17009a747e4fSDavid du Colombier {
17019a747e4fSDavid du Colombier 	int afd;
17029a747e4fSDavid du Colombier 	char *s;
17039a747e4fSDavid du Colombier 	mpint *pub = nil;
17049a747e4fSDavid du Colombier 	RSApub *rsapub;
17059a747e4fSDavid du Colombier 	AuthRpc *rpc;
17069a747e4fSDavid du Colombier 
17079a747e4fSDavid du Colombier 	// start talking to factotum
17089a747e4fSDavid du Colombier 	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0)
17099a747e4fSDavid du Colombier 		return nil;
17109a747e4fSDavid du Colombier 	if((rpc = auth_allocrpc(afd)) == nil){
17119a747e4fSDavid du Colombier 		close(afd);
17129a747e4fSDavid du Colombier 		return nil;
17139a747e4fSDavid du Colombier 	}
171465fa3f8bSDavid du Colombier 	s = "proto=rsa service=tls role=client";
17159a747e4fSDavid du Colombier 	if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){
17169a747e4fSDavid du Colombier 		factotum_rsa_close(rpc);
17179a747e4fSDavid du Colombier 		return nil;
17189a747e4fSDavid du Colombier 	}
17199a747e4fSDavid du Colombier 
17209a747e4fSDavid du Colombier 	// roll factotum keyring around to match certificate
17219a747e4fSDavid du Colombier 	rsapub = X509toRSApub(cert, certlen, nil, 0);
17229a747e4fSDavid du Colombier 	while(1){
17239a747e4fSDavid du Colombier 		if(auth_rpc(rpc, "read", nil, 0) != ARok){
17249a747e4fSDavid du Colombier 			factotum_rsa_close(rpc);
17259a747e4fSDavid du Colombier 			rpc = nil;
17269a747e4fSDavid du Colombier 			goto done;
17279a747e4fSDavid du Colombier 		}
17289a747e4fSDavid du Colombier 		pub = strtomp(rpc->arg, nil, 16, nil);
17299a747e4fSDavid du Colombier 		assert(pub != nil);
17309a747e4fSDavid du Colombier 		if(mpcmp(pub,rsapub->n) == 0)
17319a747e4fSDavid du Colombier 			break;
17329a747e4fSDavid du Colombier 	}
17339a747e4fSDavid du Colombier done:
17349a747e4fSDavid du Colombier 	mpfree(pub);
17359a747e4fSDavid du Colombier 	rsapubfree(rsapub);
17369a747e4fSDavid du Colombier 	return rpc;
17379a747e4fSDavid du Colombier }
17389a747e4fSDavid du Colombier 
17399a747e4fSDavid du Colombier static mpint*
factotum_rsa_decrypt(AuthRpc * rpc,mpint * cipher)17409a747e4fSDavid du Colombier factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher)
17419a747e4fSDavid du Colombier {
17429a747e4fSDavid du Colombier 	char *p;
17439a747e4fSDavid du Colombier 	int rv;
17449a747e4fSDavid du Colombier 
17459a747e4fSDavid du Colombier 	if((p = mptoa(cipher, 16, nil, 0)) == nil)
17469a747e4fSDavid du Colombier 		return nil;
17479a747e4fSDavid du Colombier 	rv = auth_rpc(rpc, "write", p, strlen(p));
17489a747e4fSDavid du Colombier 	free(p);
17499a747e4fSDavid du Colombier 	if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok)
17509a747e4fSDavid du Colombier 		return nil;
17519a747e4fSDavid du Colombier 	mpfree(cipher);
17529a747e4fSDavid du Colombier 	return strtomp(rpc->arg, nil, 16, nil);
17539a747e4fSDavid du Colombier }
17549a747e4fSDavid du Colombier 
17559a747e4fSDavid du Colombier static void
factotum_rsa_close(AuthRpc * rpc)17569a747e4fSDavid du Colombier factotum_rsa_close(AuthRpc*rpc)
17579a747e4fSDavid du Colombier {
17589a747e4fSDavid du Colombier 	if(!rpc)
17599a747e4fSDavid du Colombier 		return;
17609a747e4fSDavid du Colombier 	close(rpc->afd);
17619a747e4fSDavid du Colombier 	auth_freerpc(rpc);
17629a747e4fSDavid du Colombier }
17639a747e4fSDavid du Colombier 
17649a747e4fSDavid du Colombier static void
tlsPmd5(uchar * buf,int nbuf,uchar * key,int nkey,uchar * label,int nlabel,uchar * seed0,int nseed0,uchar * seed1,int nseed1)17659a747e4fSDavid du Colombier tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
17669a747e4fSDavid du Colombier {
17679a747e4fSDavid du Colombier 	uchar ai[MD5dlen], tmp[MD5dlen];
17689a747e4fSDavid du Colombier 	int i, n;
17699a747e4fSDavid du Colombier 	MD5state *s;
17709a747e4fSDavid du Colombier 
17719a747e4fSDavid du Colombier 	// generate a1
17729a747e4fSDavid du Colombier 	s = hmac_md5(label, nlabel, key, nkey, nil, nil);
17739a747e4fSDavid du Colombier 	s = hmac_md5(seed0, nseed0, key, nkey, nil, s);
17749a747e4fSDavid du Colombier 	hmac_md5(seed1, nseed1, key, nkey, ai, s);
17759a747e4fSDavid du Colombier 
17769a747e4fSDavid du Colombier 	while(nbuf > 0) {
17779a747e4fSDavid du Colombier 		s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil);
17789a747e4fSDavid du Colombier 		s = hmac_md5(label, nlabel, key, nkey, nil, s);
17799a747e4fSDavid du Colombier 		s = hmac_md5(seed0, nseed0, key, nkey, nil, s);
17809a747e4fSDavid du Colombier 		hmac_md5(seed1, nseed1, key, nkey, tmp, s);
17819a747e4fSDavid du Colombier 		n = MD5dlen;
17829a747e4fSDavid du Colombier 		if(n > nbuf)
17839a747e4fSDavid du Colombier 			n = nbuf;
17849a747e4fSDavid du Colombier 		for(i = 0; i < n; i++)
17859a747e4fSDavid du Colombier 			buf[i] ^= tmp[i];
17869a747e4fSDavid du Colombier 		buf += n;
17879a747e4fSDavid du Colombier 		nbuf -= n;
17889a747e4fSDavid du Colombier 		hmac_md5(ai, MD5dlen, key, nkey, tmp, nil);
17899a747e4fSDavid du Colombier 		memmove(ai, tmp, MD5dlen);
17909a747e4fSDavid du Colombier 	}
17919a747e4fSDavid du Colombier }
17929a747e4fSDavid du Colombier 
17939a747e4fSDavid du Colombier static void
tlsPsha1(uchar * buf,int nbuf,uchar * key,int nkey,uchar * label,int nlabel,uchar * seed0,int nseed0,uchar * seed1,int nseed1)17949a747e4fSDavid du Colombier tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
17959a747e4fSDavid du Colombier {
17969a747e4fSDavid du Colombier 	uchar ai[SHA1dlen], tmp[SHA1dlen];
17979a747e4fSDavid du Colombier 	int i, n;
17989a747e4fSDavid du Colombier 	SHAstate *s;
17999a747e4fSDavid du Colombier 
18009a747e4fSDavid du Colombier 	// generate a1
18019a747e4fSDavid du Colombier 	s = hmac_sha1(label, nlabel, key, nkey, nil, nil);
18029a747e4fSDavid du Colombier 	s = hmac_sha1(seed0, nseed0, key, nkey, nil, s);
18039a747e4fSDavid du Colombier 	hmac_sha1(seed1, nseed1, key, nkey, ai, s);
18049a747e4fSDavid du Colombier 
18059a747e4fSDavid du Colombier 	while(nbuf > 0) {
18069a747e4fSDavid du Colombier 		s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil);
18079a747e4fSDavid du Colombier 		s = hmac_sha1(label, nlabel, key, nkey, nil, s);
18089a747e4fSDavid du Colombier 		s = hmac_sha1(seed0, nseed0, key, nkey, nil, s);
18099a747e4fSDavid du Colombier 		hmac_sha1(seed1, nseed1, key, nkey, tmp, s);
18109a747e4fSDavid du Colombier 		n = SHA1dlen;
18119a747e4fSDavid du Colombier 		if(n > nbuf)
18129a747e4fSDavid du Colombier 			n = nbuf;
18139a747e4fSDavid du Colombier 		for(i = 0; i < n; i++)
18149a747e4fSDavid du Colombier 			buf[i] ^= tmp[i];
18159a747e4fSDavid du Colombier 		buf += n;
18169a747e4fSDavid du Colombier 		nbuf -= n;
18179a747e4fSDavid du Colombier 		hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil);
18189a747e4fSDavid du Colombier 		memmove(ai, tmp, SHA1dlen);
18199a747e4fSDavid du Colombier 	}
18209a747e4fSDavid du Colombier }
18219a747e4fSDavid du Colombier 
1822e94c8628SDavid du Colombier static void
tlsPsha2_256(uchar * buf,int nbuf,uchar * key,int nkey,uchar * label,int nlabel,uchar * seed,int nseed)1823e94c8628SDavid du Colombier tlsPsha2_256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed)
1824e94c8628SDavid du Colombier {
1825e94c8628SDavid du Colombier 	uchar ai[SHA2_256dlen], tmp[SHA2_256dlen];
1826e94c8628SDavid du Colombier 	int n;
1827e94c8628SDavid du Colombier 	SHAstate *s;
1828e94c8628SDavid du Colombier 
1829e94c8628SDavid du Colombier 	// generate a1
1830e94c8628SDavid du Colombier 	s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil);
1831e94c8628SDavid du Colombier 	hmac_sha2_256(seed, nseed, key, nkey, ai, s);
1832e94c8628SDavid du Colombier 
1833e94c8628SDavid du Colombier 	while(nbuf > 0) {
1834e94c8628SDavid du Colombier 		s = hmac_sha2_256(ai, SHA2_256dlen, key, nkey, nil, nil);
1835e94c8628SDavid du Colombier 		s = hmac_sha2_256(label, nlabel, key, nkey, nil, s);
1836e94c8628SDavid du Colombier 		hmac_sha2_256(seed, nseed, key, nkey, tmp, s);
1837e94c8628SDavid du Colombier 		n = SHA2_256dlen;
1838e94c8628SDavid du Colombier 		if(n > nbuf)
1839e94c8628SDavid du Colombier 			n = nbuf;
1840e94c8628SDavid du Colombier 		memmove(buf, tmp, n);
1841e94c8628SDavid du Colombier 		buf += n;
1842e94c8628SDavid du Colombier 		nbuf -= n;
1843e94c8628SDavid du Colombier 		hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil);
1844e94c8628SDavid du Colombier 		memmove(ai, tmp, SHA2_256dlen);
1845e94c8628SDavid du Colombier 	}
1846e94c8628SDavid du Colombier }
1847e94c8628SDavid du Colombier 
18489a747e4fSDavid du Colombier // fill buf with md5(args)^sha1(args)
18499a747e4fSDavid du Colombier static void
tlsPRF(uchar * buf,int nbuf,uchar * key,int nkey,char * label,uchar * seed0,int nseed0,uchar * seed1,int nseed1)18509a747e4fSDavid du Colombier tlsPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
18519a747e4fSDavid du Colombier {
18529a747e4fSDavid du Colombier 	int i;
18539a747e4fSDavid du Colombier 	int nlabel = strlen(label);
18549a747e4fSDavid du Colombier 	int n = (nkey + 1) >> 1;
18559a747e4fSDavid du Colombier 
18569a747e4fSDavid du Colombier 	for(i = 0; i < nbuf; i++)
18579a747e4fSDavid du Colombier 		buf[i] = 0;
18589a747e4fSDavid du Colombier 	tlsPmd5(buf, nbuf, key, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1);
18599a747e4fSDavid du Colombier 	tlsPsha1(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1);
18609a747e4fSDavid du Colombier }
18619a747e4fSDavid du Colombier 
1862e94c8628SDavid du Colombier void
tls12PRF(uchar * buf,int nbuf,uchar * key,int nkey,char * label,uchar * seed0,int nseed0,uchar * seed1,int nseed1)1863e94c8628SDavid du Colombier tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
1864e94c8628SDavid du Colombier {
1865e94c8628SDavid du Colombier 	uchar seed[2*RandomSize];
1866e94c8628SDavid du Colombier 	int nlabel = strlen(label);
1867e94c8628SDavid du Colombier 
1868e94c8628SDavid du Colombier 	memmove(seed, seed0, nseed0);
1869e94c8628SDavid du Colombier 	memmove(seed+nseed0, seed1, nseed1);
1870e94c8628SDavid du Colombier 	tlsPsha2_256(buf, nbuf, key, nkey, (uchar*)label, nlabel, seed, nseed0+nseed1);
1871e94c8628SDavid du Colombier }
1872e94c8628SDavid du Colombier 
18739a747e4fSDavid du Colombier /*
18749a747e4fSDavid du Colombier  * for setting server session id's
18759a747e4fSDavid du Colombier  */
18769a747e4fSDavid du Colombier static Lock	sidLock;
18779a747e4fSDavid du Colombier static long	maxSid = 1;
18789a747e4fSDavid du Colombier 
18799a747e4fSDavid du Colombier /* the keys are verified to have the same public components
18809a747e4fSDavid du Colombier  * and to function correctly with pkcs 1 encryption and decryption. */
18819a747e4fSDavid du Colombier static TlsSec*
tlsSecInits(int cvers,uchar * csid,int ncsid,uchar * crandom,uchar * ssid,int * nssid,uchar * srandom)18829a747e4fSDavid du Colombier tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom)
18839a747e4fSDavid du Colombier {
18849a747e4fSDavid du Colombier 	TlsSec *sec = emalloc(sizeof(*sec));
18859a747e4fSDavid du Colombier 
18869a747e4fSDavid du Colombier 	USED(csid); USED(ncsid);  // ignore csid for now
18879a747e4fSDavid du Colombier 
18889a747e4fSDavid du Colombier 	memmove(sec->crandom, crandom, RandomSize);
18899a747e4fSDavid du Colombier 	sec->clientVers = cvers;
18909a747e4fSDavid du Colombier 
18919a747e4fSDavid du Colombier 	put32(sec->srandom, time(0));
18929a747e4fSDavid du Colombier 	genrandom(sec->srandom+4, RandomSize-4);
18939a747e4fSDavid du Colombier 	memmove(srandom, sec->srandom, RandomSize);
18949a747e4fSDavid du Colombier 
18959a747e4fSDavid du Colombier 	/*
18969a747e4fSDavid du Colombier 	 * make up a unique sid: use our pid, and and incrementing id
18979a747e4fSDavid du Colombier 	 * can signal no sid by setting nssid to 0.
18989a747e4fSDavid du Colombier 	 */
18999a747e4fSDavid du Colombier 	memset(ssid, 0, SidSize);
19009a747e4fSDavid du Colombier 	put32(ssid, getpid());
19019a747e4fSDavid du Colombier 	lock(&sidLock);
19029a747e4fSDavid du Colombier 	put32(ssid+4, maxSid++);
19039a747e4fSDavid du Colombier 	unlock(&sidLock);
19049a747e4fSDavid du Colombier 	*nssid = SidSize;
19059a747e4fSDavid du Colombier 	return sec;
19069a747e4fSDavid du Colombier }
19079a747e4fSDavid du Colombier 
19089a747e4fSDavid du Colombier static int
tlsSecSecrets(TlsSec * sec,int vers,uchar * epm,int nepm,uchar * kd,int nkd)19099a747e4fSDavid du Colombier tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd)
19109a747e4fSDavid du Colombier {
19119a747e4fSDavid du Colombier 	if(epm != nil){
19129a747e4fSDavid du Colombier 		if(setVers(sec, vers) < 0)
19139a747e4fSDavid du Colombier 			goto Err;
19149a747e4fSDavid du Colombier 		serverMasterSecret(sec, epm, nepm);
19159a747e4fSDavid du Colombier 	}else if(sec->vers != vers){
19169a747e4fSDavid du Colombier 		werrstr("mismatched session versions");
19179a747e4fSDavid du Colombier 		goto Err;
19189a747e4fSDavid du Colombier 	}
19199a747e4fSDavid du Colombier 	setSecrets(sec, kd, nkd);
19209a747e4fSDavid du Colombier 	return 0;
19219a747e4fSDavid du Colombier Err:
19229a747e4fSDavid du Colombier 	sec->ok = -1;
19239a747e4fSDavid du Colombier 	return -1;
19249a747e4fSDavid du Colombier }
19259a747e4fSDavid du Colombier 
19269a747e4fSDavid du Colombier static TlsSec*
tlsSecInitc(int cvers,uchar * crandom)19279a747e4fSDavid du Colombier tlsSecInitc(int cvers, uchar *crandom)
19289a747e4fSDavid du Colombier {
19299a747e4fSDavid du Colombier 	TlsSec *sec = emalloc(sizeof(*sec));
19309a747e4fSDavid du Colombier 	sec->clientVers = cvers;
19319a747e4fSDavid du Colombier 	put32(sec->crandom, time(0));
19329a747e4fSDavid du Colombier 	genrandom(sec->crandom+4, RandomSize-4);
19339a747e4fSDavid du Colombier 	memmove(crandom, sec->crandom, RandomSize);
19349a747e4fSDavid du Colombier 	return sec;
19359a747e4fSDavid du Colombier }
19369a747e4fSDavid du Colombier 
19379a747e4fSDavid du Colombier static int
tlsSecSecretc(TlsSec * sec,uchar * sid,int nsid,uchar * srandom,uchar * cert,int ncert,int vers,uchar ** epm,int * nepm,uchar * kd,int nkd)19389a747e4fSDavid du Colombier tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd)
19399a747e4fSDavid du Colombier {
19409a747e4fSDavid du Colombier 	RSApub *pub;
19419a747e4fSDavid du Colombier 
19429a747e4fSDavid du Colombier 	pub = nil;
19439a747e4fSDavid du Colombier 
19449a747e4fSDavid du Colombier 	USED(sid);
19459a747e4fSDavid du Colombier 	USED(nsid);
19469a747e4fSDavid du Colombier 
19479a747e4fSDavid du Colombier 	memmove(sec->srandom, srandom, RandomSize);
19489a747e4fSDavid du Colombier 
19499a747e4fSDavid du Colombier 	if(setVers(sec, vers) < 0)
19509a747e4fSDavid du Colombier 		goto Err;
19519a747e4fSDavid du Colombier 
19529a747e4fSDavid du Colombier 	pub = X509toRSApub(cert, ncert, nil, 0);
19539a747e4fSDavid du Colombier 	if(pub == nil){
19549a747e4fSDavid du Colombier 		werrstr("invalid x509/rsa certificate");
19559a747e4fSDavid du Colombier 		goto Err;
19569a747e4fSDavid du Colombier 	}
19579a747e4fSDavid du Colombier 	if(clientMasterSecret(sec, pub, epm, nepm) < 0)
19589a747e4fSDavid du Colombier 		goto Err;
19599a747e4fSDavid du Colombier 	rsapubfree(pub);
19609a747e4fSDavid du Colombier 	setSecrets(sec, kd, nkd);
19619a747e4fSDavid du Colombier 	return 0;
19629a747e4fSDavid du Colombier 
19639a747e4fSDavid du Colombier Err:
19649a747e4fSDavid du Colombier 	if(pub != nil)
19659a747e4fSDavid du Colombier 		rsapubfree(pub);
19669a747e4fSDavid du Colombier 	sec->ok = -1;
19679a747e4fSDavid du Colombier 	return -1;
19689a747e4fSDavid du Colombier }
19699a747e4fSDavid du Colombier 
19709a747e4fSDavid du Colombier static int
tlsSecFinished(TlsSec * sec,HandHash hs,uchar * fin,int nfin,int isclient)1971e94c8628SDavid du Colombier tlsSecFinished(TlsSec *sec, HandHash hs, uchar *fin, int nfin, int isclient)
19729a747e4fSDavid du Colombier {
19739a747e4fSDavid du Colombier 	if(sec->nfin != nfin){
19749a747e4fSDavid du Colombier 		sec->ok = -1;
19759a747e4fSDavid du Colombier 		werrstr("invalid finished exchange");
19769a747e4fSDavid du Colombier 		return -1;
19779a747e4fSDavid du Colombier 	}
1978e94c8628SDavid du Colombier 	hs.md5.malloced = 0;
1979e94c8628SDavid du Colombier 	hs.sha1.malloced = 0;
1980fc8e446fSDavid du Colombier 	if(sec->setFinished == nil ){
1981fc8e446fSDavid du Colombier 		sec->ok = -1;
1982fc8e446fSDavid du Colombier 		werrstr("nil sec->setFinished in tlsSecFinished");
1983fc8e446fSDavid du Colombier 		return -1;
1984fc8e446fSDavid du Colombier 	}
1985e94c8628SDavid du Colombier 	(*sec->setFinished)(sec, hs, fin, isclient);
19869a747e4fSDavid du Colombier 	return 1;
19879a747e4fSDavid du Colombier }
19889a747e4fSDavid du Colombier 
19899a747e4fSDavid du Colombier static void
tlsSecOk(TlsSec * sec)19909a747e4fSDavid du Colombier tlsSecOk(TlsSec *sec)
19919a747e4fSDavid du Colombier {
19929a747e4fSDavid du Colombier 	if(sec->ok == 0)
19939a747e4fSDavid du Colombier 		sec->ok = 1;
19949a747e4fSDavid du Colombier }
19959a747e4fSDavid du Colombier 
19969a747e4fSDavid du Colombier static void
tlsSecKill(TlsSec * sec)19979a747e4fSDavid du Colombier tlsSecKill(TlsSec *sec)
19989a747e4fSDavid du Colombier {
19999a747e4fSDavid du Colombier 	if(!sec)
20009a747e4fSDavid du Colombier 		return;
20019a747e4fSDavid du Colombier 	factotum_rsa_close(sec->rpc);
20029a747e4fSDavid du Colombier 	sec->ok = -1;
20039a747e4fSDavid du Colombier }
20049a747e4fSDavid du Colombier 
20059a747e4fSDavid du Colombier static void
tlsSecClose(TlsSec * sec)20069a747e4fSDavid du Colombier tlsSecClose(TlsSec *sec)
20079a747e4fSDavid du Colombier {
20089a747e4fSDavid du Colombier 	if(!sec)
20099a747e4fSDavid du Colombier 		return;
20109a747e4fSDavid du Colombier 	factotum_rsa_close(sec->rpc);
20119a747e4fSDavid du Colombier 	free(sec->server);
20129a747e4fSDavid du Colombier 	free(sec);
20139a747e4fSDavid du Colombier }
20149a747e4fSDavid du Colombier 
20159a747e4fSDavid du Colombier static int
setVers(TlsSec * sec,int v)20169a747e4fSDavid du Colombier setVers(TlsSec *sec, int v)
20179a747e4fSDavid du Colombier {
2018e94c8628SDavid du Colombier 	switch(v){
2019e94c8628SDavid du Colombier 	case SSL3Version:
20209a747e4fSDavid du Colombier 		sec->setFinished = sslSetFinished;
20219a747e4fSDavid du Colombier 		sec->nfin = SSL3FinishedLen;
20229a747e4fSDavid du Colombier 		sec->prf = sslPRF;
2023e94c8628SDavid du Colombier 		break;
2024e94c8628SDavid du Colombier 	case TLS10Version:
2025e94c8628SDavid du Colombier 	case TLS11Version:
20269a747e4fSDavid du Colombier 		sec->setFinished = tlsSetFinished;
20279a747e4fSDavid du Colombier 		sec->nfin = TLSFinishedLen;
20289a747e4fSDavid du Colombier 		sec->prf = tlsPRF;
2029e94c8628SDavid du Colombier 		break;
2030e94c8628SDavid du Colombier 	case TLS12Version:
2031e94c8628SDavid du Colombier 		sec->setFinished = tls12SetFinished;
2032e94c8628SDavid du Colombier 		sec->nfin = TLSFinishedLen;
2033e94c8628SDavid du Colombier 		sec->prf = tls12PRF;
2034e94c8628SDavid du Colombier 		break;
2035e94c8628SDavid du Colombier 	default:
20369a747e4fSDavid du Colombier 		werrstr("invalid version");
2037fc8e446fSDavid du Colombier 		sec->setFinished = nil;
2038fc8e446fSDavid du Colombier 		sec->prf = nil;
20399a747e4fSDavid du Colombier 		return -1;
20409a747e4fSDavid du Colombier 	}
20419a747e4fSDavid du Colombier 	sec->vers = v;
20429a747e4fSDavid du Colombier 	return 0;
20439a747e4fSDavid du Colombier }
20449a747e4fSDavid du Colombier 
20459a747e4fSDavid du Colombier /*
20469a747e4fSDavid du Colombier  * generate secret keys from the master secret.
20479a747e4fSDavid du Colombier  *
20489a747e4fSDavid du Colombier  * different crypto selections will require different amounts
20499a747e4fSDavid du Colombier  * of key expansion and use of key expansion data,
20509a747e4fSDavid du Colombier  * but it's all generated using the same function.
20519a747e4fSDavid du Colombier  */
20529a747e4fSDavid du Colombier static void
setSecrets(TlsSec * sec,uchar * kd,int nkd)20539a747e4fSDavid du Colombier setSecrets(TlsSec *sec, uchar *kd, int nkd)
20549a747e4fSDavid du Colombier {
2055fc8e446fSDavid du Colombier 	if (sec->prf == nil) {
2056fc8e446fSDavid du Colombier 		werrstr("nil sec->prf in setSecrets");
2057fc8e446fSDavid du Colombier 		return;
2058fc8e446fSDavid du Colombier 	}
20599a747e4fSDavid du Colombier 	(*sec->prf)(kd, nkd, sec->sec, MasterSecretSize, "key expansion",
20609a747e4fSDavid du Colombier 			sec->srandom, RandomSize, sec->crandom, RandomSize);
20619a747e4fSDavid du Colombier }
20629a747e4fSDavid du Colombier 
20639a747e4fSDavid du Colombier /*
20649a747e4fSDavid du Colombier  * set the master secret from the pre-master secret.
20659a747e4fSDavid du Colombier  */
20669a747e4fSDavid du Colombier static void
setMasterSecret(TlsSec * sec,Bytes * pm)20679a747e4fSDavid du Colombier setMasterSecret(TlsSec *sec, Bytes *pm)
20689a747e4fSDavid du Colombier {
20699a747e4fSDavid du Colombier 	(*sec->prf)(sec->sec, MasterSecretSize, pm->data, MasterSecretSize, "master secret",
20709a747e4fSDavid du Colombier 			sec->crandom, RandomSize, sec->srandom, RandomSize);
20719a747e4fSDavid du Colombier }
20729a747e4fSDavid du Colombier 
20739a747e4fSDavid du Colombier static void
serverMasterSecret(TlsSec * sec,uchar * epm,int nepm)20749a747e4fSDavid du Colombier serverMasterSecret(TlsSec *sec, uchar *epm, int nepm)
20759a747e4fSDavid du Colombier {
20769a747e4fSDavid du Colombier 	Bytes *pm;
20779a747e4fSDavid du Colombier 
20789a747e4fSDavid du Colombier 	pm = pkcs1_decrypt(sec, epm, nepm);
20799a747e4fSDavid du Colombier 
20809a747e4fSDavid du Colombier 	// if the client messed up, just continue as if everything is ok,
20819a747e4fSDavid du Colombier 	// to prevent attacks to check for correctly formatted messages.
208265fa3f8bSDavid du Colombier 	// Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client.
20839a747e4fSDavid du Colombier 	if(sec->ok < 0 || pm == nil || get16(pm->data) != sec->clientVers){
208465fa3f8bSDavid du Colombier 		fprint(2, "serverMasterSecret failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n",
208565fa3f8bSDavid du Colombier 			sec->ok, pm, pm ? get16(pm->data) : -1, sec->clientVers, nepm);
20869a747e4fSDavid du Colombier 		sec->ok = -1;
20879a747e4fSDavid du Colombier 		if(pm != nil)
20889a747e4fSDavid du Colombier 			freebytes(pm);
20899a747e4fSDavid du Colombier 		pm = newbytes(MasterSecretSize);
20909a747e4fSDavid du Colombier 		genrandom(pm->data, MasterSecretSize);
20919a747e4fSDavid du Colombier 	}
20929a747e4fSDavid du Colombier 	setMasterSecret(sec, pm);
20939a747e4fSDavid du Colombier 	memset(pm->data, 0, pm->len);
20949a747e4fSDavid du Colombier 	freebytes(pm);
20959a747e4fSDavid du Colombier }
20969a747e4fSDavid du Colombier 
20979a747e4fSDavid du Colombier static int
clientMasterSecret(TlsSec * sec,RSApub * pub,uchar ** epm,int * nepm)20989a747e4fSDavid du Colombier clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm)
20999a747e4fSDavid du Colombier {
21009a747e4fSDavid du Colombier 	Bytes *pm, *key;
21019a747e4fSDavid du Colombier 
21029a747e4fSDavid du Colombier 	pm = newbytes(MasterSecretSize);
21039a747e4fSDavid du Colombier 	put16(pm->data, sec->clientVers);
21049a747e4fSDavid du Colombier 	genrandom(pm->data+2, MasterSecretSize - 2);
21059a747e4fSDavid du Colombier 
21069a747e4fSDavid du Colombier 	setMasterSecret(sec, pm);
21079a747e4fSDavid du Colombier 
21089a747e4fSDavid du Colombier 	key = pkcs1_encrypt(pm, pub, 2);
21099a747e4fSDavid du Colombier 	memset(pm->data, 0, pm->len);
21109a747e4fSDavid du Colombier 	freebytes(pm);
21119a747e4fSDavid du Colombier 	if(key == nil){
21129a747e4fSDavid du Colombier 		werrstr("tls pkcs1_encrypt failed");
21139a747e4fSDavid du Colombier 		return -1;
21149a747e4fSDavid du Colombier 	}
21159a747e4fSDavid du Colombier 
21169a747e4fSDavid du Colombier 	*nepm = key->len;
21179a747e4fSDavid du Colombier 	*epm = malloc(*nepm);
21189a747e4fSDavid du Colombier 	if(*epm == nil){
21199a747e4fSDavid du Colombier 		freebytes(key);
21209a747e4fSDavid du Colombier 		werrstr("out of memory");
21219a747e4fSDavid du Colombier 		return -1;
21229a747e4fSDavid du Colombier 	}
21239a747e4fSDavid du Colombier 	memmove(*epm, key->data, *nepm);
21249a747e4fSDavid du Colombier 
21259a747e4fSDavid du Colombier 	freebytes(key);
21269a747e4fSDavid du Colombier 
21279a747e4fSDavid du Colombier 	return 1;
21289a747e4fSDavid du Colombier }
21299a747e4fSDavid du Colombier 
21309a747e4fSDavid du Colombier static void
sslSetFinished(TlsSec * sec,HandHash hs,uchar * finished,int isClient)2131e94c8628SDavid du Colombier sslSetFinished(TlsSec *sec, HandHash hs, uchar *finished, int isClient)
21329a747e4fSDavid du Colombier {
21339a747e4fSDavid du Colombier 	DigestState *s;
21349a747e4fSDavid du Colombier 	uchar h0[MD5dlen], h1[SHA1dlen], pad[48];
21359a747e4fSDavid du Colombier 	char *label;
21369a747e4fSDavid du Colombier 
21379a747e4fSDavid du Colombier 	if(isClient)
21389a747e4fSDavid du Colombier 		label = "CLNT";
21399a747e4fSDavid du Colombier 	else
21409a747e4fSDavid du Colombier 		label = "SRVR";
21419a747e4fSDavid du Colombier 
2142e94c8628SDavid du Colombier 	md5((uchar*)label, 4, nil, &hs.md5);
2143e94c8628SDavid du Colombier 	md5(sec->sec, MasterSecretSize, nil, &hs.md5);
21449a747e4fSDavid du Colombier 	memset(pad, 0x36, 48);
2145e94c8628SDavid du Colombier 	md5(pad, 48, nil, &hs.md5);
2146e94c8628SDavid du Colombier 	md5(nil, 0, h0, &hs.md5);
21479a747e4fSDavid du Colombier 	memset(pad, 0x5C, 48);
21489a747e4fSDavid du Colombier 	s = md5(sec->sec, MasterSecretSize, nil, nil);
21499a747e4fSDavid du Colombier 	s = md5(pad, 48, nil, s);
21509a747e4fSDavid du Colombier 	md5(h0, MD5dlen, finished, s);
21519a747e4fSDavid du Colombier 
2152e94c8628SDavid du Colombier 	sha1((uchar*)label, 4, nil, &hs.sha1);
2153e94c8628SDavid du Colombier 	sha1(sec->sec, MasterSecretSize, nil, &hs.sha1);
21549a747e4fSDavid du Colombier 	memset(pad, 0x36, 40);
2155e94c8628SDavid du Colombier 	sha1(pad, 40, nil, &hs.sha1);
2156e94c8628SDavid du Colombier 	sha1(nil, 0, h1, &hs.sha1);
21579a747e4fSDavid du Colombier 	memset(pad, 0x5C, 40);
21589a747e4fSDavid du Colombier 	s = sha1(sec->sec, MasterSecretSize, nil, nil);
21599a747e4fSDavid du Colombier 	s = sha1(pad, 40, nil, s);
21609a747e4fSDavid du Colombier 	sha1(h1, SHA1dlen, finished + MD5dlen, s);
21619a747e4fSDavid du Colombier }
21629a747e4fSDavid du Colombier 
21639a747e4fSDavid du Colombier // fill "finished" arg with md5(args)^sha1(args)
21649a747e4fSDavid du Colombier static void
tlsSetFinished(TlsSec * sec,HandHash hs,uchar * finished,int isClient)2165e94c8628SDavid du Colombier tlsSetFinished(TlsSec *sec, HandHash hs, uchar *finished, int isClient)
21669a747e4fSDavid du Colombier {
21679a747e4fSDavid du Colombier 	uchar h0[MD5dlen], h1[SHA1dlen];
21689a747e4fSDavid du Colombier 	char *label;
21699a747e4fSDavid du Colombier 
21709a747e4fSDavid du Colombier 	// get current hash value, but allow further messages to be hashed in
2171e94c8628SDavid du Colombier 	md5(nil, 0, h0, &hs.md5);
2172e94c8628SDavid du Colombier 	sha1(nil, 0, h1, &hs.sha1);
21739a747e4fSDavid du Colombier 
21749a747e4fSDavid du Colombier 	if(isClient)
21759a747e4fSDavid du Colombier 		label = "client finished";
21769a747e4fSDavid du Colombier 	else
21779a747e4fSDavid du Colombier 		label = "server finished";
2178fc8e446fSDavid du Colombier 	if (sec->prf == nil) {
2179fc8e446fSDavid du Colombier 		werrstr("nil sec->prf in tlsSetFinished");
2180fc8e446fSDavid du Colombier 		return;
2181fc8e446fSDavid du Colombier 	}
2182e94c8628SDavid du Colombier 	(*sec->prf)(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen);
2183e94c8628SDavid du Colombier }
2184e94c8628SDavid du Colombier 
2185e94c8628SDavid du Colombier // fill "finished" arg with sha256(args)
2186e94c8628SDavid du Colombier static void
tls12SetFinished(TlsSec * sec,HandHash hs,uchar * finished,int isClient)2187e94c8628SDavid du Colombier tls12SetFinished(TlsSec *sec, HandHash hs, uchar *finished, int isClient)
2188e94c8628SDavid du Colombier {
2189e94c8628SDavid du Colombier 	uchar h[SHA2_256dlen];
2190e94c8628SDavid du Colombier 	char *label;
2191e94c8628SDavid du Colombier 
2192e94c8628SDavid du Colombier 	// get current hash value, but allow further messages to be hashed in
2193e94c8628SDavid du Colombier 	sha2_256(nil, 0, h, &hs.sha2_256);
2194e94c8628SDavid du Colombier 
2195e94c8628SDavid du Colombier 	if(isClient)
2196e94c8628SDavid du Colombier 		label = "client finished";
2197e94c8628SDavid du Colombier 	else
2198e94c8628SDavid du Colombier 		label = "server finished";
2199e94c8628SDavid du Colombier 	tlsPsha2_256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), h, SHA2_256dlen);
22009a747e4fSDavid du Colombier }
22019a747e4fSDavid du Colombier 
22029a747e4fSDavid du Colombier static void
sslPRF(uchar * buf,int nbuf,uchar * key,int nkey,char * label,uchar * seed0,int nseed0,uchar * seed1,int nseed1)22039a747e4fSDavid du Colombier sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1)
22049a747e4fSDavid du Colombier {
22059a747e4fSDavid du Colombier 	DigestState *s;
22069a747e4fSDavid du Colombier 	uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26];
22079a747e4fSDavid du Colombier 	int i, n, len;
22089a747e4fSDavid du Colombier 
22099a747e4fSDavid du Colombier 	USED(label);
22109a747e4fSDavid du Colombier 	len = 1;
22119a747e4fSDavid du Colombier 	while(nbuf > 0){
22129a747e4fSDavid du Colombier 		if(len > 26)
22139a747e4fSDavid du Colombier 			return;
22149a747e4fSDavid du Colombier 		for(i = 0; i < len; i++)
22159a747e4fSDavid du Colombier 			tmp[i] = 'A' - 1 + len;
22169a747e4fSDavid du Colombier 		s = sha1(tmp, len, nil, nil);
22179a747e4fSDavid du Colombier 		s = sha1(key, nkey, nil, s);
22189a747e4fSDavid du Colombier 		s = sha1(seed0, nseed0, nil, s);
22199a747e4fSDavid du Colombier 		sha1(seed1, nseed1, sha1dig, s);
22209a747e4fSDavid du Colombier 		s = md5(key, nkey, nil, nil);
22219a747e4fSDavid du Colombier 		md5(sha1dig, SHA1dlen, md5dig, s);
22229a747e4fSDavid du Colombier 		n = MD5dlen;
22239a747e4fSDavid du Colombier 		if(n > nbuf)
22249a747e4fSDavid du Colombier 			n = nbuf;
22259a747e4fSDavid du Colombier 		memmove(buf, md5dig, n);
22269a747e4fSDavid du Colombier 		buf += n;
22279a747e4fSDavid du Colombier 		nbuf -= n;
22289a747e4fSDavid du Colombier 		len++;
22299a747e4fSDavid du Colombier 	}
22309a747e4fSDavid du Colombier }
22319a747e4fSDavid du Colombier 
22329a747e4fSDavid du Colombier static mpint*
bytestomp(Bytes * bytes)22339a747e4fSDavid du Colombier bytestomp(Bytes* bytes)
22349a747e4fSDavid du Colombier {
22359a747e4fSDavid du Colombier 	mpint* ans;
22369a747e4fSDavid du Colombier 
22379a747e4fSDavid du Colombier 	ans = betomp(bytes->data, bytes->len, nil);
22389a747e4fSDavid du Colombier 	return ans;
22399a747e4fSDavid du Colombier }
22409a747e4fSDavid du Colombier 
22419a747e4fSDavid du Colombier /*
22429a747e4fSDavid du Colombier  * Convert mpint* to Bytes, putting high order byte first.
22439a747e4fSDavid du Colombier  */
22449a747e4fSDavid du Colombier static Bytes*
mptobytes(mpint * big)22459a747e4fSDavid du Colombier mptobytes(mpint* big)
22469a747e4fSDavid du Colombier {
22479a747e4fSDavid du Colombier 	int n, m;
22489a747e4fSDavid du Colombier 	uchar *a;
22499a747e4fSDavid du Colombier 	Bytes* ans;
22509a747e4fSDavid du Colombier 
225192fd5f07SDavid du Colombier 	a = nil;
22529a747e4fSDavid du Colombier 	n = (mpsignif(big)+7)/8;
22539a747e4fSDavid du Colombier 	m = mptobe(big, nil, n, &a);
22549a747e4fSDavid du Colombier 	ans = makebytes(a, m);
225592fd5f07SDavid du Colombier 	if(a != nil)
225692fd5f07SDavid du Colombier 		free(a);
22579a747e4fSDavid du Colombier 	return ans;
22589a747e4fSDavid du Colombier }
22599a747e4fSDavid du Colombier 
22609a747e4fSDavid du Colombier // Do RSA computation on block according to key, and pad
22619a747e4fSDavid du Colombier // result on left with zeros to make it modlen long.
22629a747e4fSDavid du Colombier static Bytes*
rsacomp(Bytes * block,RSApub * key,int modlen)22639a747e4fSDavid du Colombier rsacomp(Bytes* block, RSApub* key, int modlen)
22649a747e4fSDavid du Colombier {
22659a747e4fSDavid du Colombier 	mpint *x, *y;
22669a747e4fSDavid du Colombier 	Bytes *a, *ybytes;
22679a747e4fSDavid du Colombier 	int ylen;
22689a747e4fSDavid du Colombier 
22699a747e4fSDavid du Colombier 	x = bytestomp(block);
22709a747e4fSDavid du Colombier 	y = rsaencrypt(key, x, nil);
22719a747e4fSDavid du Colombier 	mpfree(x);
22729a747e4fSDavid du Colombier 	ybytes = mptobytes(y);
22739a747e4fSDavid du Colombier 	ylen = ybytes->len;
22749a747e4fSDavid du Colombier 
22759a747e4fSDavid du Colombier 	if(ylen < modlen) {
22769a747e4fSDavid du Colombier 		a = newbytes(modlen);
22779a747e4fSDavid du Colombier 		memset(a->data, 0, modlen-ylen);
22789a747e4fSDavid du Colombier 		memmove(a->data+modlen-ylen, ybytes->data, ylen);
22799a747e4fSDavid du Colombier 		freebytes(ybytes);
22809a747e4fSDavid du Colombier 		ybytes = a;
22819a747e4fSDavid du Colombier 	}
22829a747e4fSDavid du Colombier 	else if(ylen > modlen) {
22839a747e4fSDavid du Colombier 		// assume it has leading zeros (mod should make it so)
22849a747e4fSDavid du Colombier 		a = newbytes(modlen);
22859a747e4fSDavid du Colombier 		memmove(a->data, ybytes->data, modlen);
22869a747e4fSDavid du Colombier 		freebytes(ybytes);
22879a747e4fSDavid du Colombier 		ybytes = a;
22889a747e4fSDavid du Colombier 	}
22899a747e4fSDavid du Colombier 	mpfree(y);
22909a747e4fSDavid du Colombier 	return ybytes;
22919a747e4fSDavid du Colombier }
22929a747e4fSDavid du Colombier 
22933ff48bf5SDavid du Colombier // encrypt data according to PKCS#1, /lib/rfc/rfc2437 9.1.2.1
22949a747e4fSDavid du Colombier static Bytes*
pkcs1_encrypt(Bytes * data,RSApub * key,int blocktype)22959a747e4fSDavid du Colombier pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype)
22969a747e4fSDavid du Colombier {
22979a747e4fSDavid du Colombier 	Bytes *pad, *eb, *ans;
22989a747e4fSDavid du Colombier 	int i, dlen, padlen, modlen;
22999a747e4fSDavid du Colombier 
23009a747e4fSDavid du Colombier 	modlen = (mpsignif(key->n)+7)/8;
23019a747e4fSDavid du Colombier 	dlen = data->len;
23029a747e4fSDavid du Colombier 	if(modlen < 12 || dlen > modlen - 11)
23039a747e4fSDavid du Colombier 		return nil;
23049a747e4fSDavid du Colombier 	padlen = modlen - 3 - dlen;
23059a747e4fSDavid du Colombier 	pad = newbytes(padlen);
23069a747e4fSDavid du Colombier 	genrandom(pad->data, padlen);
23079a747e4fSDavid du Colombier 	for(i = 0; i < padlen; i++) {
23089a747e4fSDavid du Colombier 		if(blocktype == 0)
23099a747e4fSDavid du Colombier 			pad->data[i] = 0;
23109a747e4fSDavid du Colombier 		else if(blocktype == 1)
23119a747e4fSDavid du Colombier 			pad->data[i] = 255;
23129a747e4fSDavid du Colombier 		else if(pad->data[i] == 0)
23139a747e4fSDavid du Colombier 			pad->data[i] = 1;
23149a747e4fSDavid du Colombier 	}
23159a747e4fSDavid du Colombier 	eb = newbytes(modlen);
23169a747e4fSDavid du Colombier 	eb->data[0] = 0;
23179a747e4fSDavid du Colombier 	eb->data[1] = blocktype;
23189a747e4fSDavid du Colombier 	memmove(eb->data+2, pad->data, padlen);
23199a747e4fSDavid du Colombier 	eb->data[padlen+2] = 0;
23209a747e4fSDavid du Colombier 	memmove(eb->data+padlen+3, data->data, dlen);
23219a747e4fSDavid du Colombier 	ans = rsacomp(eb, key, modlen);
23229a747e4fSDavid du Colombier 	freebytes(eb);
23239a747e4fSDavid du Colombier 	freebytes(pad);
23249a747e4fSDavid du Colombier 	return ans;
23259a747e4fSDavid du Colombier }
23269a747e4fSDavid du Colombier 
23279a747e4fSDavid du Colombier // decrypt data according to PKCS#1, with given key.
23289a747e4fSDavid du Colombier // expect a block type of 2.
23299a747e4fSDavid du Colombier static Bytes*
pkcs1_decrypt(TlsSec * sec,uchar * epm,int nepm)23309a747e4fSDavid du Colombier pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm)
23319a747e4fSDavid du Colombier {
23329a747e4fSDavid du Colombier 	Bytes *eb, *ans = nil;
23339a747e4fSDavid du Colombier 	int i, modlen;
23349a747e4fSDavid du Colombier 	mpint *x, *y;
23359a747e4fSDavid du Colombier 
23369a747e4fSDavid du Colombier 	modlen = (mpsignif(sec->rsapub->n)+7)/8;
23379a747e4fSDavid du Colombier 	if(nepm != modlen)
23389a747e4fSDavid du Colombier 		return nil;
23399a747e4fSDavid du Colombier 	x = betomp(epm, nepm, nil);
23409a747e4fSDavid du Colombier 	y = factotum_rsa_decrypt(sec->rpc, x);
23419a747e4fSDavid du Colombier 	if(y == nil)
23429a747e4fSDavid du Colombier 		return nil;
23439a747e4fSDavid du Colombier 	eb = mptobytes(y);
23449a747e4fSDavid du Colombier 	if(eb->len < modlen){ // pad on left with zeros
23459a747e4fSDavid du Colombier 		ans = newbytes(modlen);
23469a747e4fSDavid du Colombier 		memset(ans->data, 0, modlen-eb->len);
23479a747e4fSDavid du Colombier 		memmove(ans->data+modlen-eb->len, eb->data, eb->len);
23489a747e4fSDavid du Colombier 		freebytes(eb);
23499a747e4fSDavid du Colombier 		eb = ans;
23509a747e4fSDavid du Colombier 	}
23519a747e4fSDavid du Colombier 	if(eb->data[0] == 0 && eb->data[1] == 2) {
23529a747e4fSDavid du Colombier 		for(i = 2; i < modlen; i++)
23539a747e4fSDavid du Colombier 			if(eb->data[i] == 0)
23549a747e4fSDavid du Colombier 				break;
23559a747e4fSDavid du Colombier 		if(i < modlen - 1)
23569a747e4fSDavid du Colombier 			ans = makebytes(eb->data+i+1, modlen-(i+1));
23579a747e4fSDavid du Colombier 	}
2358fc8e446fSDavid du Colombier 	if (eb != ans)			/* not freed above? */
23599a747e4fSDavid du Colombier 		freebytes(eb);
23609a747e4fSDavid du Colombier 	return ans;
23619a747e4fSDavid du Colombier }
23629a747e4fSDavid du Colombier 
23639a747e4fSDavid du Colombier 
23649a747e4fSDavid du Colombier //================= general utility functions ========================
23659a747e4fSDavid du Colombier 
23669a747e4fSDavid du Colombier static void *
emalloc(int n)23679a747e4fSDavid du Colombier emalloc(int n)
23689a747e4fSDavid du Colombier {
23699a747e4fSDavid du Colombier 	void *p;
23709a747e4fSDavid du Colombier 	if(n==0)
23719a747e4fSDavid du Colombier 		n=1;
23729a747e4fSDavid du Colombier 	p = malloc(n);
23739a747e4fSDavid du Colombier 	if(p == nil){
23749a747e4fSDavid du Colombier 		exits("out of memory");
23759a747e4fSDavid du Colombier 	}
23769a747e4fSDavid du Colombier 	memset(p, 0, n);
23779a747e4fSDavid du Colombier 	return p;
23789a747e4fSDavid du Colombier }
23799a747e4fSDavid du Colombier 
23809a747e4fSDavid du Colombier static void *
erealloc(void * ReallocP,int ReallocN)23819a747e4fSDavid du Colombier erealloc(void *ReallocP, int ReallocN)
23829a747e4fSDavid du Colombier {
23839a747e4fSDavid du Colombier 	if(ReallocN == 0)
23849a747e4fSDavid du Colombier 		ReallocN = 1;
23859a747e4fSDavid du Colombier 	if(!ReallocP)
23869a747e4fSDavid du Colombier 		ReallocP = emalloc(ReallocN);
23879a747e4fSDavid du Colombier 	else if(!(ReallocP = realloc(ReallocP, ReallocN))){
23889a747e4fSDavid du Colombier 		exits("out of memory");
23899a747e4fSDavid du Colombier 	}
23909a747e4fSDavid du Colombier 	return(ReallocP);
23919a747e4fSDavid du Colombier }
23929a747e4fSDavid du Colombier 
23939a747e4fSDavid du Colombier static void
put32(uchar * p,u32int x)23949a747e4fSDavid du Colombier put32(uchar *p, u32int x)
23959a747e4fSDavid du Colombier {
23969a747e4fSDavid du Colombier 	p[0] = x>>24;
23979a747e4fSDavid du Colombier 	p[1] = x>>16;
23989a747e4fSDavid du Colombier 	p[2] = x>>8;
23999a747e4fSDavid du Colombier 	p[3] = x;
24009a747e4fSDavid du Colombier }
24019a747e4fSDavid du Colombier 
24029a747e4fSDavid du Colombier static void
put24(uchar * p,int x)24039a747e4fSDavid du Colombier put24(uchar *p, int x)
24049a747e4fSDavid du Colombier {
24059a747e4fSDavid du Colombier 	p[0] = x>>16;
24069a747e4fSDavid du Colombier 	p[1] = x>>8;
24079a747e4fSDavid du Colombier 	p[2] = x;
24089a747e4fSDavid du Colombier }
24099a747e4fSDavid du Colombier 
24109a747e4fSDavid du Colombier static void
put16(uchar * p,int x)24119a747e4fSDavid du Colombier put16(uchar *p, int x)
24129a747e4fSDavid du Colombier {
24139a747e4fSDavid du Colombier 	p[0] = x>>8;
24149a747e4fSDavid du Colombier 	p[1] = x;
24159a747e4fSDavid du Colombier }
24169a747e4fSDavid du Colombier 
24179a747e4fSDavid du Colombier static u32int
get32(uchar * p)24189a747e4fSDavid du Colombier get32(uchar *p)
24199a747e4fSDavid du Colombier {
24209a747e4fSDavid du Colombier 	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
24219a747e4fSDavid du Colombier }
24229a747e4fSDavid du Colombier 
24239a747e4fSDavid du Colombier static int
get24(uchar * p)24249a747e4fSDavid du Colombier get24(uchar *p)
24259a747e4fSDavid du Colombier {
24269a747e4fSDavid du Colombier 	return (p[0]<<16)|(p[1]<<8)|p[2];
24279a747e4fSDavid du Colombier }
24289a747e4fSDavid du Colombier 
24299a747e4fSDavid du Colombier static int
get16(uchar * p)24309a747e4fSDavid du Colombier get16(uchar *p)
24319a747e4fSDavid du Colombier {
24329a747e4fSDavid du Colombier 	return (p[0]<<8)|p[1];
24339a747e4fSDavid du Colombier }
24349a747e4fSDavid du Colombier 
24352d8b52e8SDavid du Colombier #define OFFSET(x, s) offsetof(s, x)
24369a747e4fSDavid du Colombier 
24379a747e4fSDavid du Colombier /*
24389a747e4fSDavid du Colombier  * malloc and return a new Bytes structure capable of
24399a747e4fSDavid du Colombier  * holding len bytes. (len >= 0)
24409a747e4fSDavid du Colombier  * Used to use crypt_malloc, which aborts if malloc fails.
24419a747e4fSDavid du Colombier  */
24429a747e4fSDavid du Colombier static Bytes*
newbytes(int len)24439a747e4fSDavid du Colombier newbytes(int len)
24449a747e4fSDavid du Colombier {
24459a747e4fSDavid du Colombier 	Bytes* ans;
24469a747e4fSDavid du Colombier 
24479a747e4fSDavid du Colombier 	ans = (Bytes*)malloc(OFFSET(data[0], Bytes) + len);
24489a747e4fSDavid du Colombier 	ans->len = len;
24499a747e4fSDavid du Colombier 	return ans;
24509a747e4fSDavid du Colombier }
24519a747e4fSDavid du Colombier 
24529a747e4fSDavid du Colombier /*
24539a747e4fSDavid du Colombier  * newbytes(len), with data initialized from buf
24549a747e4fSDavid du Colombier  */
24559a747e4fSDavid du Colombier static Bytes*
makebytes(uchar * buf,int len)24569a747e4fSDavid du Colombier makebytes(uchar* buf, int len)
24579a747e4fSDavid du Colombier {
24589a747e4fSDavid du Colombier 	Bytes* ans;
24599a747e4fSDavid du Colombier 
24609a747e4fSDavid du Colombier 	ans = newbytes(len);
24619a747e4fSDavid du Colombier 	memmove(ans->data, buf, len);
24629a747e4fSDavid du Colombier 	return ans;
24639a747e4fSDavid du Colombier }
24649a747e4fSDavid du Colombier 
24659a747e4fSDavid du Colombier static void
freebytes(Bytes * b)24669a747e4fSDavid du Colombier freebytes(Bytes* b)
24679a747e4fSDavid du Colombier {
24689a747e4fSDavid du Colombier 	if(b != nil)
24699a747e4fSDavid du Colombier 		free(b);
24709a747e4fSDavid du Colombier }
24719a747e4fSDavid du Colombier 
24729a747e4fSDavid du Colombier /* len is number of ints */
24739a747e4fSDavid du Colombier static Ints*
newints(int len)24749a747e4fSDavid du Colombier newints(int len)
24759a747e4fSDavid du Colombier {
24769a747e4fSDavid du Colombier 	Ints* ans;
24779a747e4fSDavid du Colombier 
24789a747e4fSDavid du Colombier 	ans = (Ints*)malloc(OFFSET(data[0], Ints) + len*sizeof(int));
24799a747e4fSDavid du Colombier 	ans->len = len;
24809a747e4fSDavid du Colombier 	return ans;
24819a747e4fSDavid du Colombier }
24829a747e4fSDavid du Colombier 
24839a747e4fSDavid du Colombier static Ints*
makeints(int * buf,int len)24849a747e4fSDavid du Colombier makeints(int* buf, int len)
24859a747e4fSDavid du Colombier {
24869a747e4fSDavid du Colombier 	Ints* ans;
24879a747e4fSDavid du Colombier 
24889a747e4fSDavid du Colombier 	ans = newints(len);
24899a747e4fSDavid du Colombier 	if(len > 0)
24909a747e4fSDavid du Colombier 		memmove(ans->data, buf, len*sizeof(int));
24919a747e4fSDavid du Colombier 	return ans;
24929a747e4fSDavid du Colombier }
24939a747e4fSDavid du Colombier 
24949a747e4fSDavid du Colombier static void
freeints(Ints * b)24959a747e4fSDavid du Colombier freeints(Ints* b)
24969a747e4fSDavid du Colombier {
24979a747e4fSDavid du Colombier 	if(b != nil)
24989a747e4fSDavid du Colombier 		free(b);
24999a747e4fSDavid du Colombier }
2500