update tlshand to v1.2. mostly from cinap, but forward and backward compatability was added to avoid having new binaries incompatable with newer kernels, and vice versa. Reference: /n/atom/patch/applied/tlshand1.2 Date: Tue Sep 22 04:26:38 CES 2015 Signed-off-by: quanstro@quanstro.net --- /sys/src/libsec/port/tlshand.c Tue Sep 22 04:25:56 2015 +++ /sys/src/libsec/port/tlshand.c Tue Sep 22 04:25:58 2015 @@ -16,8 +16,8 @@ enum { TLSFinishedLen = 12, SSL3FinishedLen = MD5dlen+SHA1dlen, - MaxKeyData = 136, // amount of secret we may need - MaxChunk = 1<<14, + MaxKeyData = 160, // amount of secret we may need + MaxChunk = 1<<15, RandomSize = 32, SidSize = 32, MasterSecretSize = 48, @@ -45,11 +45,29 @@ int ok; } Algs; +typedef struct Namedcurve{ + int tlsid; + char *name; + + char *p; + char *a; + char *b; + char *G; + char *n; + char *h; +} Namedcurve; + typedef struct Finished{ uchar verify[SSL3FinishedLen]; int n; } Finished; +typedef struct HandshakeHash { + MD5state md5; + SHAstate sha1; + SHA2_256state sha2_256; +} HandshakeHash; + typedef struct TlsConnection{ TlsSec *sec; // security management goo int hand, ctl; // record layer file descriptors @@ -76,13 +94,13 @@ uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random int clientVersion; // version in ClientHello + int cipher; char *digest; // name of digest algorithm to use char *enc; // name of encryption algorithm to use int nsecret; // amount of secret data to init keys // for finished messages - MD5state hsmd5; // handshake hash - SHAstate hssha1; // handshake hash + HandshakeHash handhash; Finished finished; } TlsConnection; @@ -95,13 +113,15 @@ Bytes* sid; Ints* ciphers; Bytes* compressors; + Bytes* extensions; } clientHello; struct { int version; - uchar random[RandomSize]; + uchar random[RandomSize]; Bytes* sid; int cipher; int compressor; + Bytes* extensions; } serverHello; struct { int ncert; @@ -116,6 +136,14 @@ Bytes *key; } clientKeyExchange; struct { + Bytes *dh_p; + Bytes *dh_g; + Bytes *dh_Ys; + Bytes *dh_signature; + int sigalg; + int curve; + } serverKeyExchange; + struct { Bytes *signature; } certificateVerify; Finished finished; @@ -134,16 +162,18 @@ int vers; // final version // byte generation and handshake checksum void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); - void (*setFinished)(TlsSec*, MD5state, SHAstate, uchar*, int); + void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); int nfin; } TlsSec; enum { - TLSVersion = 0x0301, - SSL3Version = 0x0300, - ProtocolVersion = 0x0301, // maximum version we speak - MinProtoVersion = 0x0300, // limits on version we accept + SSL3Version = 0x0300, + TLS10Version = 0x0301, + TLS11Version = 0x0302, + TLS12Version = 0x0303, + ProtocolVersion = TLS12Version, // maximum version we speak + MinProtoVersion = SSL3Version, // limits on version we accept MaxProtoVersion = 0x03ff, }; @@ -235,6 +265,14 @@ TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0X0038, TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0X0039, TLS_DH_anon_WITH_AES_256_CBC_SHA = 0X003A, + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0X003C, + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0X003D, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0XC009, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0XC00A, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0XC013, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0XC014, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0XC023, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, CipherMax }; @@ -245,20 +283,56 @@ }; static Algs cipherAlgs[] = { - {"rc4_128", "md5", 2*(16+MD5dlen), TLS_RSA_WITH_RC4_128_MD5}, - {"rc4_128", "sha1", 2*(16+SHA1dlen), TLS_RSA_WITH_RC4_128_SHA}, - {"3des_ede_cbc", "sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, + {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, + {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, + {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, + {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, + {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, + {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_RSA_WITH_AES_128_CBC_SHA256}, + {"aes_256_cbc", "sha256", 2*(32+16+SHA2_256dlen), TLS_RSA_WITH_AES_256_CBC_SHA256}, {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA}, {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA}, + {"3des_ede_cbc","sha1", 2*(4*8+SHA1dlen), TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA}, + {"3des_ede_cbc","sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + {"rc4_128", "sha1", 2*(16+SHA1dlen), TLS_RSA_WITH_RC4_128_SHA}, + {"rc4_128", "md5", 2*(16+MD5dlen), TLS_RSA_WITH_RC4_128_MD5}, }; static uchar compressors[] = { CompressionNull, }; -static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chain); -static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, int (*trace)(char*fmt, ...)); +static Namedcurve namedcurves[] = { +{0x0017, "secp256r1", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", + "1"} +}; + +static uchar pointformats[] = { + CompressionNull /* support of uncompressed point format is mandatory */ +}; + +// signature algorithms +static int sigalgs[] = { + 0x0601, /* SHA512 RSA */ + 0x0501, /* SHA384 RSA */ + 0x0401, /* SHA256 RSA */ + 0x0201, /* SHA1 RSA */ + 0x0603, /* SHA512 ECDSA */ + 0x0503, /* SHA384 ECDSA */ + 0x0403, /* SHA256 ECDSA */ + 0x0203, /* SHA1 ECDSA */ +}; +static TlsConnection *tlsServer2(int ctl, int ver, int hand, uchar *cert, int certlen, int (*trace)(char*fmt, ...), PEMChain *chain); +static TlsConnection *tlsClient2(int ctl, int ver, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, uchar *ext, int extlen, int (*trace)(char*fmt, ...)); static void msgClear(Msg *m); static char* msgPrint(char *buf, int n, Msg *m); static int msgRecv(TlsConnection *c, Msg *m); @@ -276,21 +350,24 @@ static Ints* makeciphers(void); static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom); -static int tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd); +static int tlsSecRSAs(TlsSec *sec, int vers, Bytes *epm); static TlsSec* tlsSecInitc(int cvers, uchar *crandom); -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); -static int tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient); +static Bytes* tlsSecRSAc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers); +static Bytes* tlsSecDHEc(TlsSec *sec, uchar *srandom, int vers, Bytes *p, Bytes *g, Bytes *Ys); +static Bytes* tlsSecECDHEc(TlsSec *sec, uchar *srandom, int vers, int curve, Bytes *Ys); +static int tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient); static void tlsSecOk(TlsSec *sec); static void tlsSecKill(TlsSec *sec); static void tlsSecClose(TlsSec *sec); static void setMasterSecret(TlsSec *sec, Bytes *pm); -static void serverMasterSecret(TlsSec *sec, uchar *epm, int nepm); +static void serverMasterSecret(TlsSec *sec, Bytes *epm); static void setSecrets(TlsSec *sec, uchar *kd, int nkd); -static int clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm); +static Bytes* clientMasterSecret(TlsSec *sec, RSApub *pub); static Bytes *pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype); -static Bytes *pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm); -static void tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); -static void sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); +static Bytes *pkcs1_decrypt(TlsSec *sec, Bytes *cipher); +static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient); +static void tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient); +static void sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient); static void sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1); static int setVers(TlsSec *sec, int version); @@ -310,9 +387,9 @@ static Bytes* newbytes(int len); static Bytes* makebytes(uchar* buf, int len); static Bytes* mptobytes(mpint* big); +static mpint* bytestomp(Bytes* bytes); static void freebytes(Bytes* b); static Ints* newints(int len); -static Ints* makeints(int* buf, int len); static void freeints(Ints* b); /* x509.c */ @@ -320,6 +397,25 @@ //================= client/server ======================== +int +setfd(int ctl, int fd, int version) +{ + int i; + + /* botch: detect bug in older kernels */ + if(fprint(ctl, "fd %d 0x%x", fd, MaxProtoVersion) > 0){ + fprint(ctl, "fd %d 0x%x", fd, TLS10Version); + // fprint(2, "old kernel detected because of bug\n"); + return TLS10Version; + } + for(i = version; i >= MinProtoVersion; i--) + if(fprint(ctl, "fd %d 0x%x", fd, i) > 0){ + // fprint(2, "newer kernel detected %#.3ux\n", i); + return i; + } + return -1; +} + // push TLS onto fd, returning new (application) file descriptor // or -1 if error. int @@ -327,7 +423,7 @@ { char buf[8]; char dname[64]; - int n, data, ctl, hand; + int n, ver, data, ctl, hand; TlsConnection *tls; if(conn == nil) @@ -344,12 +440,11 @@ snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf); snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf); hand = open(dname, ORDWR); - if(hand < 0){ + if(hand < 0 || (ver = setfd(ctl, fd, ProtocolVersion)) == -1){ close(ctl); return -1; } - fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); - tls = tlsServer2(ctl, hand, conn->cert, conn->certlen, conn->trace, conn->chain); + tls = tlsServer2(ctl, ver, hand, conn->cert, conn->certlen, conn->trace, conn->chain); snprint(dname, sizeof(dname), "#a/tls/%s/data", buf); data = open(dname, ORDWR); close(hand); @@ -372,6 +467,75 @@ return data; } +static uchar* +tlsClientExtensions(TLSconn *conn, int ver, int *plen) +{ + uchar *b, *p; + int i, n, m; + + p = b = nil; + + // RFC6066 - Server Name Identification + if(conn->serverName != nil){ + n = strlen(conn->serverName); + + m = p - b; + b = erealloc(b, m + 2+2+2+1+2+n); + p = b + m; + + put16(p, 0), p += 2; /* Type: server_name */ + put16(p, 2+1+2+n), p += 2; /* Length */ + put16(p, 1+2+n), p += 2; /* Server Name list length */ + *p++ = 0; /* Server Name Type: host_name */ + put16(p, n), p += 2; /* Server Name length */ + memmove(p, conn->serverName, n); + p += n; + } + + // ECDHE + if(1){ + m = p - b; + b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); + p = b + m; + + n = nelem(namedcurves); + put16(p, 0x000a), p += 2; /* Type: elliptic_curves */ + put16(p, (n+1)*2), p += 2; /* Length */ + put16(p, n*2), p += 2; /* Elliptic Curves Length */ + for(i=0; i < n; i++){ /* Elliptic curves */ + put16(p, namedcurves[i].tlsid); + p += 2; + } + + n = nelem(pointformats); + put16(p, 0x000b), p += 2; /* Type: ec_point_formats */ + put16(p, n+1), p += 2; /* Length */ + *p++ = n; /* EC point formats Length */ + for(i=0; i < n; i++) /* Elliptic curves point formats */ + *p++ = pointformats[i]; + } + + // signature algorithms + if(ver >= TLS12Version){ + n = nelem(sigalgs); + + m = p - b; + b = erealloc(b, m + 2+2+2+n*2); + p = b + m; + + put16(p, 0x000d), p += 2; + put16(p, n*2 + 2), p += 2; + put16(p, n*2), p += 2; + for(i=0; i < n; i++){ + put16(p, sigalgs[i]); + p += 2; + } + } + + *plen = p - b; + return b; +} + // push TLS onto fd, returning new (application) file descriptor // or -1 if error. int @@ -379,10 +543,11 @@ { char buf[8]; char dname[64]; - int n, data, ctl, hand; + int n, ver, data, ctl, hand; TlsConnection *tls; + uchar *ext; - if(!conn) + if(conn == nil) return -1; ctl = open("#a/tls/clone", ORDWR); if(ctl < 0) @@ -402,14 +567,15 @@ } snprint(dname, sizeof(dname), "#a/tls/%s/data", buf); data = open(dname, ORDWR); - if(data < 0){ + if(data < 0 || (ver = setfd(ctl, fd, ProtocolVersion)) == -1){ close(hand); close(ctl); return -1; } - fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); - tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->cert, conn->certlen, conn->trace); - close(fd); + ext = tlsClientExtensions(conn, ver, &n); + tls = tlsClient2(ctl, ver, hand, conn->sessionID, conn->sessionIDlen, conn->cert, conn->certlen, + ext, n, conn->trace); + free(ext); close(hand); close(ctl); if(tls == nil){ @@ -425,6 +591,7 @@ if(conn->sessionKey != nil && conn->sessionType != nil && strcmp(conn->sessionType, "ttls") == 0) tls->sec->prf(conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, tls->sec->crandom, RandomSize, tls->sec->srandom, RandomSize); tlsConnectionFree(tls); + close(fd); return data; } @@ -441,7 +608,7 @@ } static TlsConnection * -tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chp) +tlsServer2(int ctl, int ver, int hand, uchar *cert, int certlen, int (*trace)(char*fmt, ...), PEMChain *chp) { TlsConnection *c; Msg m; @@ -458,7 +625,7 @@ c->ctl = ctl; c->hand = hand; c->trace = trace; - c->version = ProtocolVersion; + c->version = ver; memset(&m, 0, sizeof(m)); if(!msgRecv(c, &m)){ @@ -473,7 +640,7 @@ c->clientVersion = m.u.clientHello.version; if(trace) trace("ClientHello version %x\n", c->clientVersion); - if(setVersion(c, m.u.clientHello.version) < 0) { + if(setVersion(c, c->clientVersion) < 0) { tlsError(c, EIllegalParameter, "incompatible version"); goto Err; } @@ -506,12 +673,16 @@ tlsError(c, EHandshakeFailure, "can't initialize security: %r"); goto Err; } - c->sec->rpc = factotum_rsa_open(cert, ncert); + c->sec->rpc = factotum_rsa_open(cert, certlen); if(c->sec->rpc == nil){ tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r"); goto Err; } - c->sec->rsapub = X509toRSApub(cert, ncert, nil, 0); + c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0); + if(c->sec->rsapub == nil){ + tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate"); + goto Err; + } msgClear(&m); m.tag = HServerHello; @@ -528,8 +699,8 @@ m.tag = HCertificate; numcerts = countchain(chp); m.u.certificate.ncert = 1 + numcerts; - m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes)); - m.u.certificate.certs[0] = makebytes(cert, ncert); + m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*)); + m.u.certificate.certs[0] = makebytes(cert, certlen); for (i = 0; i < numcerts && chp; i++, chp = chp->next) m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen); if(!msgSend(c, &m, AQueue)) @@ -547,10 +718,11 @@ tlsError(c, EUnexpectedMessage, "expected a client key exchange"); goto Err; } - if(tlsSecSecrets(c->sec, c->version, m.u.clientKeyExchange.key->data, m.u.clientKeyExchange.key->len, kd, c->nsecret) < 0){ + if(tlsSecRSAs(c->sec, c->version, m.u.clientKeyExchange.key) < 0){ tlsError(c, EHandshakeFailure, "couldn't set secrets: %r"); goto Err; } + setSecrets(c->sec, kd, c->nsecret); if(trace) trace("tls secrets\n"); secrets = (char*)emalloc(2*c->nsecret); @@ -566,7 +738,7 @@ msgClear(&m); /* no CertificateVerify; skip to Finished */ - if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ + if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){ tlsError(c, EInternalError, "can't set finished: %r"); goto Err; } @@ -588,7 +760,7 @@ goto Err; } - if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ + if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){ tlsError(c, EInternalError, "can't set finished: %r"); goto Err; } @@ -611,20 +783,236 @@ return 0; } +static int +isDHE(int tlsid) +{ + switch(tlsid){ + case TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: + case TLS_DHE_DSS_WITH_DES_CBC_SHA: + case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + return 1; + } + return 0; +} + +static int +isECDHE(int tlsid) +{ + switch(tlsid){ + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return 1; + } + return 0; +} + +static Bytes* +tlsSecDHEc(TlsSec *sec, uchar *srandom, int vers, + Bytes *p, Bytes *g, Bytes *Ys) +{ + mpint *G, *P, *Y, *K; + Bytes *epm; + DHstate dh; + + if(p == nil || g == nil || Ys == nil) + return nil; + + memmove(sec->srandom, srandom, RandomSize); + if(setVers(sec, vers) < 0) + return nil; + + epm = nil; + P = bytestomp(p); + G = bytestomp(g); + Y = bytestomp(Ys); + K = nil; + + if(P == nil || G == nil || Y == nil || dh_new(&dh, P, nil, G) == nil) + goto Out; + epm = mptobytes(dh.y); + K = dh_finish(&dh, Y); + if(K == nil){ + freebytes(epm); + epm = nil; + goto Out; + } + setMasterSecret(sec, mptobytes(K)); + +Out: + mpfree(K); + mpfree(Y); + mpfree(G); + mpfree(P); + + return epm; +} + +static ECpoint* +bytestoec(ECdomain *dom, Bytes *bp, ECpoint *ret) +{ + char *hex = "0123456789ABCDEF"; + char *s; + int i; + + s = emalloc(2*bp->len + 1); + for(i=0; i < bp->len; i++){ + s[2*i] = hex[bp->data[i]>>4 & 15]; + s[2*i+1] = hex[bp->data[i] & 15]; + } + s[2*bp->len] = '\0'; + ret = strtoec(dom, s, nil, ret); + free(s); + return ret; +} + +static Bytes* +ectobytes(int type, ECpoint *p) +{ + Bytes *bx, *by, *bp; + + bx = mptobytes(p->x); + by = mptobytes(p->y); + bp = newbytes(bx->len + by->len + 1); + bp->data[0] = type; + memmove(bp->data+1, bx->data, bx->len); + memmove(bp->data+1+bx->len, by->data, by->len); + freebytes(bx); + freebytes(by); + return bp; +} + +static Bytes* +tlsSecECDHEc(TlsSec *sec, uchar *srandom, int vers, int curve, Bytes *Ys) +{ + Namedcurve *nc, *enc; + Bytes *epm; + ECdomain dom; + ECpoint G, K, Y; + ECpriv Q; + + if(Ys == nil) + return nil; + + enc = &namedcurves[nelem(namedcurves)]; + for(nc = namedcurves; nc != enc; nc++) + if(nc->tlsid == curve) + break; + + if(nc == enc) + return nil; + + memmove(sec->srandom, srandom, RandomSize); + if(setVers(sec, vers) < 0) + return nil; + + epm = nil; + + memset(&dom, 0, sizeof(dom)); + dom.p = strtomp(nc->p, nil, 16, nil); + dom.a = strtomp(nc->a, nil, 16, nil); + dom.b = strtomp(nc->b, nil, 16, nil); + dom.n = strtomp(nc->n, nil, 16, nil); + dom.h = strtomp(nc->h, nil, 16, nil); + + memset(&G, 0, sizeof(G)); + G.x = mpnew(0); + G.y = mpnew(0); + + memset(&Q, 0, sizeof(Q)); + Q.x = mpnew(0); + Q.y = mpnew(0); + Q.d = mpnew(0); + + memset(&K, 0, sizeof(K)); + K.x = mpnew(0); + K.y = mpnew(0); + + memset(&Y, 0, sizeof(Y)); + Y.x = mpnew(0); + Y.y = mpnew(0); + + if(dom.p == nil || dom.a == nil || dom.b == nil || dom.n == nil || dom.h == nil) + goto Out; + if(Q.x == nil || Q.y == nil || Q.d == nil) + goto Out; + if(G.x == nil || G.y == nil) + goto Out; + if(K.x == nil || K.y == nil) + goto Out; + if(Y.x == nil || Y.y == nil) + goto Out; + + dom.G = strtoec(&dom, nc->G, nil, &G); + if(dom.G == nil) + goto Out; + + if(bytestoec(&dom, Ys, &Y) == nil) + goto Out; + + if(ecgen(&dom, &Q) == nil) + goto Out; + + ecmul(&dom, &Y, Q.d, &K); + setMasterSecret(sec, mptobytes(K.x)); + + /* 0x04 = uncompressed public key */ + epm = ectobytes(0x04, &Q); + +Out: + mpfree(Y.x); + mpfree(Y.y); + + mpfree(K.x); + mpfree(K.y); + + mpfree(Q.x); + mpfree(Q.y); + mpfree(Q.d); + + mpfree(G.x); + mpfree(G.y); + + mpfree(dom.p); + mpfree(dom.a); + mpfree(dom.b); + mpfree(dom.n); + mpfree(dom.h); + + return epm; +} + static TlsConnection * -tlsClient2(int ctl, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, int (*trace)(char*fmt, ...)) +tlsClient2(int ctl, int ver, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, uchar *ext, int extlen, int (*trace)(char*fmt, ...)) { TlsConnection *c; Msg m; - uchar kd[MaxKeyData], *epm; + uchar kd[MaxKeyData]; char *secrets; - int creq, nepm, rv; + int creq, dhx, rv, cipher; + mpint *signedMP, *paddedHashes; + Bytes *epm; if(!initCiphers()) return nil; epm = nil; c = emalloc(sizeof(TlsConnection)); - c->version = ProtocolVersion; + c->version = ver; + + // client certificate signature not implemented for TLS1.2 + if(cert != nil && certlen > 0 && c->version >= TLS12Version) + c->version = TLS11Version; + c->ctl = ctl; c->hand = hand; c->trace = trace; @@ -634,7 +1022,6 @@ c->sec = tlsSecInitc(c->clientVersion, c->crandom); if(c->sec == nil) goto Err; - /* client hello */ memset(&m, 0, sizeof(m)); m.tag = HClientHello; @@ -643,6 +1030,7 @@ m.u.clientHello.sid = makebytes(csid, ncsid); m.u.clientHello.ciphers = makeciphers(); m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors)); + m.u.clientHello.extensions = makebytes(ext, extlen); if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); @@ -664,7 +1052,8 @@ tlsError(c, EIllegalParameter, "invalid server session identifier"); goto Err; } - if(!setAlgs(c, m.u.serverHello.cipher)) { + cipher = m.u.serverHello.cipher; + if(!setAlgs(c, cipher)) { tlsError(c, EIllegalParameter, "invalid cipher suite"); goto Err; } @@ -686,14 +1075,32 @@ c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len); msgClear(&m); - /* server key exchange (optional) */ + /* server key exchange */ + dhx = isDHE(cipher) || isECDHE(cipher); if(!msgRecv(c, &m)) goto Err; if(m.tag == HServerKeyExchange) { - tlsError(c, EUnexpectedMessage, "got an server key exchange"); + if(!dhx){ + tlsError(c, EUnexpectedMessage, "got an server key exchange"); + goto Err; + } + if(isECDHE(cipher)) + epm = tlsSecECDHEc(c->sec, c->srandom, c->version, + m.u.serverKeyExchange.curve, + m.u.serverKeyExchange.dh_Ys); + else + epm = tlsSecDHEc(c->sec, c->srandom, c->version, + m.u.serverKeyExchange.dh_p, + m.u.serverKeyExchange.dh_g, + m.u.serverKeyExchange.dh_Ys); + if(epm == nil) + goto Badcert; + msgClear(&m); + if(!msgRecv(c, &m)) + goto Err; + } else if(dhx){ + tlsError(c, EUnexpectedMessage, "expected server key exchange"); goto Err; - // If implementing this later, watch out for rollback attack - // described in Wagner Schneier 1996, section 4.4. } /* certificate request (optional) */ @@ -711,12 +1118,17 @@ } msgClear(&m); - if(tlsSecSecretc(c->sec, c->sid->data, c->sid->len, c->srandom, - c->cert->data, c->cert->len, c->version, &epm, &nepm, - kd, c->nsecret) < 0){ + if(!dhx) + epm = tlsSecRSAc(c->sec, c->sid->data, c->sid->len, c->srandom, + c->cert->data, c->cert->len, c->version); + + if(epm == nil){ + Badcert: tlsError(c, EBadCertificate, "bad certificate: %r"); goto Err; } + + setSecrets(c->sec, kd, c->nsecret); secrets = (char*)emalloc(2*c->nsecret); enc64(secrets, 2*c->nsecret, kd, c->nsecret); rv = fprint(c->ctl, "secret %s %s 1 %s", c->digest, c->enc, secrets); @@ -731,7 +1143,7 @@ if(creq) { if(cert != nil && certlen > 0){ m.u.certificate.ncert = 1; - m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes)); + m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*)); m.u.certificate.certs[0] = makebytes(cert, certlen); } m.tag = HCertificate; @@ -742,39 +1154,27 @@ /* client key exchange */ m.tag = HClientKeyExchange; - m.u.clientKeyExchange.key = makebytes(epm, nepm); - free(epm); + m.u.clientKeyExchange.key = epm; epm = nil; if(m.u.clientKeyExchange.key == nil) { tlsError(c, EHandshakeFailure, "can't set secret: %r"); goto Err; } + if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); - /* CertificateVerify */ - /* - * XXX I should only send this when it is not DH right? - * Also we need to know which TLS key - * we have to use in case there are more than one - */ - if(cert){ + /* certificate verify */ + if(creq && cert != nil && certlen > 0) { uchar hshashes[MD5dlen+SHA1dlen]; /* content of signature */ - MD5state hsmd5_save; - SHAstate hssha1_save; - mpint *signedMP, *paddedHashes; - - /* save the state for the Finish message */ + HandshakeHash hsave; - m.tag = HCertificateVerify; - hsmd5_save = c->hsmd5; - hssha1_save = c->hssha1; - md5(nil, 0, hshashes, &c->hsmd5); - sha1(nil, 0, hshashes+MD5dlen, &c->hssha1); - - c->hsmd5 = hsmd5_save; - c->hssha1 = hssha1_save; + /* save the state for the Finish message */ + hsave = c->handhash; + md5(nil, 0, hshashes, &c->handhash.md5); + sha1(nil, 0, hshashes+MD5dlen, &c->handhash.sha1); + c->handhash = hsave; c->sec->rpc = factotum_rsa_open(cert, certlen); if(c->sec->rpc == nil){ @@ -782,21 +1182,23 @@ goto Err; } c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0); - - paddedHashes = pkcs1padbuf(hshashes, 36, c->sec->rsapub->n); - signedMP = factotum_rsa_decrypt(c->sec->rpc, paddedHashes); - m.u.certificateVerify.signature = mptobytes(signedMP); - free(signedMP); - - if(m.u.certificateVerify.signature == nil){ - msgClear(&m); + if(c->sec->rsapub == nil){ + tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate"); goto Err; } - if(!msgSend(c, &m, AFlush)){ - msgClear(&m); + paddedHashes = pkcs1padbuf(hshashes, MD5dlen+SHA1dlen, c->sec->rsapub->n); + signedMP = factotum_rsa_decrypt(c->sec->rpc, paddedHashes); + if(signedMP == nil){ + tlsError(c, EHandshakeFailure, "factotum_rsa_decrypt: %r"); goto Err; } + m.u.certificateVerify.signature = mptobytes(signedMP); + mpfree(signedMP); + + m.tag = HCertificateVerify; + if(!msgSend(c, &m, AFlush)) + goto Err; msgClear(&m); } @@ -808,32 +1210,27 @@ // Cipherchange must occur immediately before Finished to avoid // potential hole; see section 4.3 of Wagner Schneier 1996. - if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ + if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){ tlsError(c, EInternalError, "can't set finished 1: %r"); goto Err; } m.tag = HFinished; m.u.finished = c->finished; - if(!msgSend(c, &m, AFlush)) { - fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EInternalError, "can't flush after client Finished: %r"); goto Err; } msgClear(&m); - if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ - fprint(2, "tlsClient nepm=%d\n", nepm); + if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){ tlsError(c, EInternalError, "can't set finished 0: %r"); goto Err; } if(!msgRecv(c, &m)) { - fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EInternalError, "can't read server Finished: %r"); goto Err; } if(m.tag != HFinished) { - fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EUnexpectedMessage, "expected a Finished msg from server"); goto Err; } @@ -862,6 +1259,15 @@ //================= message functions ======================== +static void +msgHash(TlsConnection *c, uchar *p, int n) +{ + md5(p, n, 0, &c->handhash.md5); + sha1(p, n, 0, &c->handhash.sha1); + if(c->version >= TLS12Version) + sha2_256(p, n, 0, &c->handhash.sha2_256); +} + static int msgSend(TlsConnection *c, Msg *m, int act) { @@ -911,6 +1317,15 @@ p[0] = n; memmove(p+1, m->u.clientHello.compressors->data, n); p += n+1; + + if(m->u.clientHello.extensions == nil) + break; + n = m->u.clientHello.extensions->len; + if(n == 0) + break; + put16(p, n); + memmove(p+2, m->u.clientHello.extensions->data, n); + p += n+2; break; case HServerHello: put16(p, m->u.serverHello.version); @@ -931,6 +1346,15 @@ p += 2; p[0] = m->u.serverHello.compressor; p += 1; + + if(m->u.serverHello.extensions == nil) + break; + n = m->u.serverHello.extensions->len; + if(n == 0) + break; + put16(p, n); + memmove(p+2, m->u.serverHello.extensions->data, n); + p += n+2; break; case HServerHelloDone: break; @@ -960,8 +1384,12 @@ case HClientKeyExchange: n = m->u.clientKeyExchange.key->len; if(c->version != SSL3Version){ - put16(p, n); - p += 2; + if(isECDHE(c->cipher)) + *p++ = n; + else{ + put16(p, n); + p += 2; + } } memmove(p, m->u.clientKeyExchange.key->data, n); p += n; @@ -978,10 +1406,8 @@ put24(c->sendp+1, n-4); // remember hash of Handshake messages - if(m->tag != HHelloRequest) { - md5(c->sendp, n, 0, &c->hsmd5); - sha1(c->sendp, n, 0, &c->hssha1); - } + if(m->tag != HHelloRequest) + msgHash(c, c->sendp, n); c->sendp = p; if(act == AFlush){ @@ -1056,8 +1482,7 @@ p = tlsReadN(c, n); if(p == nil) return 0; - md5(p, n, 0, &c->hsmd5); - sha1(p, n, 0, &c->hssha1); + msgHash(c, p, n); m->tag = HClientHello; if(n < 22) goto Short; @@ -1095,15 +1520,13 @@ goto Ok; } - md5(p, 4, 0, &c->hsmd5); - sha1(p, 4, 0, &c->hssha1); + msgHash(c, p, 4); p = tlsReadN(c, n); if(p == nil) return 0; - md5(p, n, 0, &c->hsmd5); - sha1(p, n, 0, &c->hssha1); + msgHash(c, p, n); m->tag = type; @@ -1146,9 +1569,17 @@ if(n < 1 || n < p[0]+1 || p[0] == 0) goto Short; nn = p[0]; - m->u.clientHello.compressors = newbytes(nn); - memmove(m->u.clientHello.compressors->data, p+1, nn); + m->u.clientHello.compressors = makebytes(p+1, nn); + p += nn + 1; n -= nn + 1; + + if(n < 2) + break; + nn = get16(p); + if(nn > n-2) + goto Short; + m->u.clientHello.extensions = makebytes(p+2, nn); + n -= nn + 2; break; case HServerHello: if(n < 2) @@ -1173,7 +1604,16 @@ goto Short; m->u.serverHello.cipher = get16(p); m->u.serverHello.compressor = p[2]; + p += 3; n -= 3; + + if(n < 2) + break; + nn = get16(p); + if(nn > n-2) + goto Short; + m->u.serverHello.extensions = makebytes(p+2, nn); + n -= nn + 2; break; case HCertificate: if(n < 3) @@ -1194,7 +1634,7 @@ if(nn > n) goto Short; m->u.certificate.ncert = i+1; - m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes)); + m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes*)); m->u.certificate.certs[i] = makebytes(p, nn); p += nn; n -= nn; @@ -1212,6 +1652,19 @@ m->u.certificateRequest.types = makebytes(p, nn); p += nn; n -= nn; + if(c->version >= TLS12Version){ + /* skip supported_signature_algorithms */ + if(n < 2) + goto Short; + nn = get16(p); + p += 2; + n -= 2; + if(nn > n) + goto Short; + p += nn; + n -= nn; + + } if(n < 2) goto Short; nn = get16(p); @@ -1232,7 +1685,7 @@ goto Short; m->u.certificateRequest.nca = i+1; m->u.certificateRequest.cas = erealloc( - m->u.certificateRequest.cas, (i+1)*sizeof(Bytes)); + m->u.certificateRequest.cas, (i+1)*sizeof(Bytes*)); m->u.certificateRequest.cas[i] = makebytes(p, nn); p += nn; n -= nn; @@ -1241,6 +1694,68 @@ break; case HServerHelloDone: break; + case HServerKeyExchange: + if(n < 2) + goto Short; + if(isECDHE(c->cipher)){ + nn = *p; + p++, n--; + if(nn != 3 || nn > n) /* not a named curve */ + goto Short; + nn = get16(p); + p += 2, n -= 2; + m->u.serverKeyExchange.curve = nn; + + nn = *p++, n--; + if(nn < 1 || nn > n) + goto Short; + m->u.serverKeyExchange.dh_Ys = makebytes(p, nn); + p += nn, n -= nn; + }else if(isDHE(c->cipher)){ + nn = get16(p); + p += 2, n -= 2; + if(nn < 1 || nn > n) + goto Short; + m->u.serverKeyExchange.dh_p = makebytes(p, nn); + p += nn, n -= nn; + + if(n < 2) + goto Short; + nn = get16(p); + p += 2, n -= 2; + if(nn < 1 || nn > n) + goto Short; + m->u.serverKeyExchange.dh_g = makebytes(p, nn); + p += nn, n -= nn; + + if(n < 2) + goto Short; + nn = get16(p); + p += 2, n -= 2; + if(nn < 1 || nn > n) + goto Short; + m->u.serverKeyExchange.dh_Ys = makebytes(p, nn); + p += nn, n -= nn; + } else { + /* should not happen */ + break; + } + if(n >= 2){ + m->u.serverKeyExchange.sigalg = 0; + if(c->version >= TLS12Version){ + m->u.serverKeyExchange.sigalg = get16(p); + p += 2, n -= 2; + if(n < 2) + goto Short; + } + nn = get16(p); + p += 2, n -= 2; + if(nn > 0 && nn <= n){ + m->u.serverKeyExchange.dh_signature = makebytes(p, nn); + n -= nn; + } + } + break; case HClientKeyExchange: /* * this message depends upon the encryption selected @@ -1269,7 +1784,7 @@ break; } - if(type != HClientHello && n != 0) + if(type != HClientHello && type != HServerHello && n != 0) goto Short; Ok: if(c->trace){ @@ -1280,7 +1795,7 @@ } return 1; Short: - tlsError(c, EDecodeError, "handshake message has invalid length"); + tlsError(c, EDecodeError, "handshake message (%d) has invalid length", type); Err: msgClear(m); return 0; @@ -1300,9 +1815,11 @@ freebytes(m->u.clientHello.sid); freeints(m->u.clientHello.ciphers); freebytes(m->u.clientHello.compressors); + freebytes(m->u.clientHello.extensions); break; case HServerHello: - freebytes(m->u.clientHello.sid); + freebytes(m->u.serverHello.sid); + freebytes(m->u.serverHello.extensions); break; case HCertificate: for(i=0; iu.certificate.ncert; i++) @@ -1320,6 +1837,12 @@ break; case HServerHelloDone: break; + case HServerKeyExchange: + freebytes(m->u.serverKeyExchange.dh_p); + freebytes(m->u.serverKeyExchange.dh_g); + freebytes(m->u.serverKeyExchange.dh_Ys); + freebytes(m->u.serverKeyExchange.dh_signature); + break; case HClientKeyExchange: freebytes(m->u.clientKeyExchange.key); break; @@ -1336,12 +1859,13 @@ if(s0) bs = seprint(bs, be, "%s", s0); - bs = seprint(bs, be, "["); if(b == nil) bs = seprint(bs, be, "nil"); - else + else { + bs = seprint(bs, be, "<%d> [", b->len); for(i=0; ilen; i++) bs = seprint(bs, be, "%.2x ", b->data[i]); + } bs = seprint(bs, be, "]"); if(s1) bs = seprint(bs, be, "%s", s1); @@ -1387,6 +1911,8 @@ bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n"); bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n"); bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n"); + if(m->u.clientHello.extensions != nil) + bs = bytesPrint(bs, be, "\textensions: ", m->u.clientHello.extensions, "\n"); break; case HServerHello: bs = seprint(bs, be, "ServerHello\n"); @@ -1398,6 +1924,8 @@ bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n"); bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher); bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor); + if(m->u.serverHello.extensions != nil) + bs = bytesPrint(bs, be, "\textensions: ", m->u.serverHello.extensions, "\n"); break; case HCertificate: bs = seprint(bs, be, "Certificate\n"); @@ -1418,6 +1946,19 @@ case HServerHelloDone: bs = seprint(bs, be, "ServerHelloDone\n"); break; + case HServerKeyExchange: + bs = seprint(bs, be, "HServerKeyExchange\n"); + if(m->u.serverKeyExchange.curve != 0){ + bs = seprint(bs, be, "\tcurve: %.4x\n", m->u.serverKeyExchange.curve); + } else { + bs = bytesPrint(bs, be, "\tdh_p: ", m->u.serverKeyExchange.dh_p, "\n"); + bs = bytesPrint(bs, be, "\tdh_g: ", m->u.serverKeyExchange.dh_g, "\n"); + } + bs = bytesPrint(bs, be, "\tdh_Ys: ", m->u.serverKeyExchange.dh_Ys, "\n"); + if(m->u.serverKeyExchange.sigalg != 0) + bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.serverKeyExchange.sigalg); + bs = bytesPrint(bs, be, "\tdh_signature: ", m->u.serverKeyExchange.dh_signature, "\n"); + break; case HClientKeyExchange: bs = seprint(bs, be, "HClientKeyExchange\n"); bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n"); @@ -1463,11 +2004,10 @@ if(version == SSL3Version) { c->version = version; c->finished.n = SSL3FinishedLen; - }else if(version == TLSVersion){ + }else { c->version = version; c->finished.n = TLSFinishedLen; - }else - return -1; + } c->verset = 1; return fprint(c->ctl, "version 0x%x", version); } @@ -1496,34 +2036,34 @@ static int weakCipher[CipherMax] = { - 1, /* TLS_NULL_WITH_NULL_NULL */ - 1, /* TLS_RSA_WITH_NULL_MD5 */ - 1, /* TLS_RSA_WITH_NULL_SHA */ - 1, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */ - 0, /* TLS_RSA_WITH_RC4_128_MD5 */ - 0, /* TLS_RSA_WITH_RC4_128_SHA */ - 1, /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */ - 0, /* TLS_RSA_WITH_IDEA_CBC_SHA */ - 1, /* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */ - 0, /* TLS_RSA_WITH_DES_CBC_SHA */ - 0, /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */ - 1, /* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */ - 0, /* TLS_DH_DSS_WITH_DES_CBC_SHA */ - 0, /* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */ - 1, /* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */ - 0, /* TLS_DH_RSA_WITH_DES_CBC_SHA */ - 0, /* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */ - 1, /* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */ - 0, /* TLS_DHE_DSS_WITH_DES_CBC_SHA */ - 0, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */ - 1, /* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */ - 0, /* TLS_DHE_RSA_WITH_DES_CBC_SHA */ - 0, /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */ - 1, /* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */ - 1, /* TLS_DH_anon_WITH_RC4_128_MD5 */ - 1, /* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */ - 1, /* TLS_DH_anon_WITH_DES_CBC_SHA */ - 1, /* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */ +[TLS_NULL_WITH_NULL_NULL] 1, +[TLS_RSA_WITH_NULL_MD5] 1, +[TLS_RSA_WITH_NULL_SHA] 1, +[TLS_RSA_EXPORT_WITH_RC4_40_MD5] 1, +[TLS_RSA_WITH_RC4_128_MD5] 0, +[TLS_RSA_WITH_RC4_128_SHA] 0, +[TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5] 1, +[TLS_RSA_WITH_IDEA_CBC_SHA] 0, +[TLS_RSA_EXPORT_WITH_DES40_CBC_SHA] 1, +[TLS_RSA_WITH_DES_CBC_SHA] 0, +[TLS_RSA_WITH_3DES_EDE_CBC_SHA] 0, +[TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA] 1, +[TLS_DH_DSS_WITH_DES_CBC_SHA] 0, +[TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA] 0, +[TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA] 1, +[TLS_DH_RSA_WITH_DES_CBC_SHA] 0, +[TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA] 0, +[TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA] 1, +[TLS_DHE_DSS_WITH_DES_CBC_SHA] 0, +[TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA] 0, +[TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA] 1, +[TLS_DHE_RSA_WITH_DES_CBC_SHA] 0, +[TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA] 0, +[TLS_DH_anon_EXPORT_WITH_RC4_40_MD5] 1, +[TLS_DH_anon_WITH_RC4_128_MD5] 1, +[TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA] 1, +[TLS_DH_anon_WITH_DES_CBC_SHA] 1, +[TLS_DH_anon_WITH_3DES_EDE_CBC_SHA] 1, }; static int @@ -1533,6 +2073,7 @@ for(i = 0; i < nelem(cipherAlgs); i++){ if(cipherAlgs[i].tlsid == a){ + c->cipher = a; c->enc = cipherAlgs[i].enc; c->digest = cipherAlgs[i].digest; c->nsecret = cipherAlgs[i].nsecret; @@ -1552,10 +2093,12 @@ weak = 1; for(i = 0; i < cv->len; i++) { c = cv->data[i]; - if(c >= CipherMax) + if(c >= nelem(weakCipher)) weak = 0; else weak &= weakCipher[c]; + if(isDHE(c) || isECDHE(c)) + continue; /* TODO: not implemented for server */ for(j = 0; j < nelem(cipherAlgs); j++) if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c) return c; @@ -1716,13 +2259,14 @@ char *p; int rv; - if((p = mptoa(cipher, 16, nil, 0)) == nil) + p = mptoa(cipher, 16, nil, 0); + mpfree(cipher); + if(p == nil) return nil; rv = auth_rpc(rpc, "write", p, strlen(p)); free(p); if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok) return nil; - mpfree(cipher); return strtomp(rpc->arg, nil, 16, nil); } @@ -1793,20 +2337,55 @@ } } +static void +p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed) +{ + uchar ai[SHA2_256dlen], tmp[SHA2_256dlen]; + SHAstate *s; + int n; + + // generate a1 + s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil); + hmac_sha2_256(seed, nseed, key, nkey, ai, s); + + while(nbuf > 0) { + s = hmac_sha2_256(ai, SHA2_256dlen, key, nkey, nil, nil); + s = hmac_sha2_256(label, nlabel, key, nkey, nil, s); + hmac_sha2_256(seed, nseed, key, nkey, tmp, s); + n = SHA2_256dlen; + if(n > nbuf) + n = nbuf; + memmove(buf, tmp, n); + buf += n; + nbuf -= n; + hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil); + memmove(ai, tmp, SHA2_256dlen); + } +} + // fill buf with md5(args)^sha1(args) static void -tlsPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) { - int i; int nlabel = strlen(label); int n = (nkey + 1) >> 1; - for(i = 0; i < nbuf; i++) - buf[i] = 0; + memset(buf, 0, nbuf); tlsPmd5(buf, nbuf, key, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); tlsPsha1(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); } +static void +tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +{ + uchar seed[2*RandomSize]; + + assert(nseed0+nseed1 <= sizeof(seed)); + memmove(seed, seed0, nseed0); + memmove(seed+nseed0, seed1, nseed1); + p_sha256(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed0+nseed1); +} + /* * for setting server session id's */ @@ -1843,17 +2422,16 @@ } static int -tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd) +tlsSecRSAs(TlsSec *sec, int vers, Bytes *epm) { if(epm != nil){ if(setVers(sec, vers) < 0) goto Err; - serverMasterSecret(sec, epm, nepm); + serverMasterSecret(sec, epm); }else if(sec->vers != vers){ werrstr("mismatched session versions"); goto Err; } - setSecrets(sec, kd, nkd); return 0; Err: sec->ok = -1; @@ -1871,50 +2449,44 @@ return sec; } -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) +static Bytes* +tlsSecRSAc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers) { RSApub *pub; - - pub = nil; + Bytes *epm; USED(sid); USED(nsid); memmove(sec->srandom, srandom, RandomSize); - if(setVers(sec, vers) < 0) goto Err; - pub = X509toRSApub(cert, ncert, nil, 0); if(pub == nil){ werrstr("invalid x509/rsa certificate"); goto Err; } - if(clientMasterSecret(sec, pub, epm, nepm) < 0) - goto Err; + epm = clientMasterSecret(sec, pub); rsapubfree(pub); - setSecrets(sec, kd, nkd); - return 0; - + if(epm != nil) + return epm; Err: - if(pub != nil) - rsapubfree(pub); sec->ok = -1; - return -1; + return nil; } static int -tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient) +tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient) { if(sec->nfin != nfin){ sec->ok = -1; werrstr("invalid finished exchange"); return -1; } - md5.malloced = 0; - sha1.malloced = 0; - (*sec->setFinished)(sec, md5, sha1, fin, isclient); + hsh.md5.malloced = 0; + hsh.sha1.malloced = 0; + hsh.sha2_256.malloced = 0; + (*sec->setFinished)(sec, hsh, fin, isclient); return 1; } @@ -1951,12 +2523,16 @@ sec->setFinished = sslSetFinished; sec->nfin = SSL3FinishedLen; sec->prf = sslPRF; - }else if(v == TLSVersion){ - sec->setFinished = tlsSetFinished; + }else if(v < TLS12Version) { + sec->setFinished = tls10SetFinished; sec->nfin = TLSFinishedLen; - sec->prf = tlsPRF; + sec->prf = tls10PRF; + }else if(v == TLS12Version) { + sec->setFinished = tls12SetFinished; + sec->nfin = TLSFinishedLen; + sec->prf = tls12PRF; }else{ - werrstr("invalid version"); + werrstr("tlsClient: invalid version %.3ux", v); return -1; } sec->vers = v; @@ -1978,74 +2554,55 @@ } /* - * set the master secret from the pre-master secret. + * set the master secret from the pre-master secret, + * destroys premaster. */ static void setMasterSecret(TlsSec *sec, Bytes *pm) { - (*sec->prf)(sec->sec, MasterSecretSize, pm->data, MasterSecretSize, "master secret", + (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret", sec->crandom, RandomSize, sec->srandom, RandomSize); + memset(pm->data, 0, pm->len); + freebytes(pm); } static void -serverMasterSecret(TlsSec *sec, uchar *epm, int nepm) +serverMasterSecret(TlsSec *sec, Bytes *epm) { Bytes *pm; - pm = pkcs1_decrypt(sec, epm, nepm); + pm = pkcs1_decrypt(sec, epm); // if the client messed up, just continue as if everything is ok, // to prevent attacks to check for correctly formatted messages. // Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client. if(sec->ok < 0 || pm == nil || get16(pm->data) != sec->clientVers){ fprint(2, "serverMasterSecret failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n", - sec->ok, pm, pm ? get16(pm->data) : -1, sec->clientVers, nepm); + sec->ok, pm, pm != nil ? get16(pm->data) : -1, sec->clientVers, epm->len); sec->ok = -1; - if(pm != nil) - freebytes(pm); + freebytes(pm); pm = newbytes(MasterSecretSize); genrandom(pm->data, MasterSecretSize); } + assert(pm->len == MasterSecretSize); setMasterSecret(sec, pm); - memset(pm->data, 0, pm->len); - freebytes(pm); } -static int -clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm) +static Bytes* +clientMasterSecret(TlsSec *sec, RSApub *pub) { - Bytes *pm, *key; + Bytes *pm, *epm; pm = newbytes(MasterSecretSize); put16(pm->data, sec->clientVers); genrandom(pm->data+2, MasterSecretSize - 2); - + epm = pkcs1_encrypt(pm, pub, 2); setMasterSecret(sec, pm); - - key = pkcs1_encrypt(pm, pub, 2); - memset(pm->data, 0, pm->len); - freebytes(pm); - if(key == nil){ - werrstr("tls pkcs1_encrypt failed"); - return -1; - } - - *nepm = key->len; - *epm = malloc(*nepm); - if(*epm == nil){ - freebytes(key); - werrstr("out of memory"); - return -1; - } - memmove(*epm, key->data, *nepm); - - freebytes(key); - - return 1; + return epm; } static void -sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) +sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient) { DigestState *s; uchar h0[MD5dlen], h1[SHA1dlen], pad[48]; @@ -2056,21 +2613,21 @@ else label = "SRVR"; - md5((uchar*)label, 4, nil, &hsmd5); - md5(sec->sec, MasterSecretSize, nil, &hsmd5); + md5((uchar*)label, 4, nil, &hsh.md5); + md5(sec->sec, MasterSecretSize, nil, &hsh.md5); memset(pad, 0x36, 48); - md5(pad, 48, nil, &hsmd5); - md5(nil, 0, h0, &hsmd5); + md5(pad, 48, nil, &hsh.md5); + md5(nil, 0, h0, &hsh.md5); memset(pad, 0x5C, 48); s = md5(sec->sec, MasterSecretSize, nil, nil); s = md5(pad, 48, nil, s); md5(h0, MD5dlen, finished, s); - sha1((uchar*)label, 4, nil, &hssha1); - sha1(sec->sec, MasterSecretSize, nil, &hssha1); + sha1((uchar*)label, 4, nil, &hsh.sha1); + sha1(sec->sec, MasterSecretSize, nil, &hsh.sha1); memset(pad, 0x36, 40); - sha1(pad, 40, nil, &hssha1); - sha1(nil, 0, h1, &hssha1); + sha1(pad, 40, nil, &hsh.sha1); + sha1(nil, 0, h1, &hsh.sha1); memset(pad, 0x5C, 40); s = sha1(sec->sec, MasterSecretSize, nil, nil); s = sha1(pad, 40, nil, s); @@ -2079,27 +2636,43 @@ // fill "finished" arg with md5(args)^sha1(args) static void -tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) +tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient) { uchar h0[MD5dlen], h1[SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in - md5(nil, 0, h0, &hsmd5); - sha1(nil, 0, h1, &hssha1); + md5(nil, 0, h0, &hsh.md5); + sha1(nil, 0, h1, &hsh.sha1); + + if(isClient) + label = "client finished"; + else + label = "server finished"; + tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); +} + +static void +tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isClient) +{ + uchar seed[SHA2_256dlen]; + char *label; + + // get current hash value, but allow further messages to be hashed in + sha2_256(nil, 0, seed, &hsh.sha2_256); if(isClient) label = "client finished"; else label = "server finished"; - tlsPRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); + p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen); } static void sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) { - DigestState *s; uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; + DigestState *s; int i, n, len; USED(label); @@ -2128,10 +2701,7 @@ static mpint* bytestomp(Bytes* bytes) { - mpint* ans; - - ans = betomp(bytes->data, bytes->len, nil); - return ans; + return betomp(bytes->data, bytes->len, nil); } /* @@ -2140,16 +2710,14 @@ static Bytes* mptobytes(mpint* big) { - int n, m; - uchar *a; Bytes* ans; + int n; - a = nil; n = (mpsignif(big)+7)/8; - m = mptobe(big, nil, n, &a); - ans = makebytes(a, m); - if(a != nil) - free(a); + if(n == 0) + n = 1; + ans = newbytes(n); + ans->len = mptobe(big, ans->data, n, nil); return ans; } @@ -2167,6 +2735,7 @@ mpfree(x); ybytes = mptobytes(y); ylen = ybytes->len; + mpfree(y); if(ylen < modlen) { a = newbytes(modlen); @@ -2182,7 +2751,6 @@ freebytes(ybytes); ybytes = a; } - mpfree(y); return ybytes; } @@ -2223,20 +2791,21 @@ // decrypt data according to PKCS#1, with given key. // expect a block type of 2. static Bytes* -pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm) +pkcs1_decrypt(TlsSec *sec, Bytes *cipher) { Bytes *eb, *ans = nil; int i, modlen; mpint *x, *y; modlen = (mpsignif(sec->rsapub->n)+7)/8; - if(nepm != modlen) + if(cipher->len != modlen) return nil; - x = betomp(epm, nepm, nil); + x = bytestomp(cipher); y = factotum_rsa_decrypt(sec->rpc, x); if(y == nil) return nil; eb = mptobytes(y); + mpfree(y); if(eb->len < modlen){ // pad on left with zeros ans = newbytes(modlen); memset(ans->data, 0, modlen-eb->len); @@ -2265,9 +2834,8 @@ if(n==0) n=1; p = malloc(n); - if(p == nil){ - exits("out of memory"); - } + if(p == nil) + sysfatal("out of memory"); memset(p, 0, n); setmalloctag(p, getcallerpc(&n)); return p; @@ -2278,11 +2846,10 @@ { if(ReallocN == 0) ReallocN = 1; - if(!ReallocP) + if(ReallocP == nil) ReallocP = emalloc(ReallocN); - else if(!(ReallocP = realloc(ReallocP, ReallocN))){ - exits("out of memory"); - } + else if((ReallocP = realloc(ReallocP, ReallocN)) == nil) + sysfatal("out of memory"); setrealloctag(ReallocP, getcallerpc(&ReallocP)); return(ReallocP); } @@ -2331,17 +2898,12 @@ #define OFFSET(x, s) offsetof(s, x) -/* - * malloc and return a new Bytes structure capable of - * holding len bytes. (len >= 0) - * Used to use crypt_malloc, which aborts if malloc fails. - */ static Bytes* newbytes(int len) { Bytes* ans; - ans = (Bytes*)malloc(OFFSET(data[0], Bytes) + len); + ans = (Bytes*)emalloc(OFFSET(data[0], Bytes) + len); ans->len = len; return ans; } @@ -2362,8 +2924,7 @@ static void freebytes(Bytes* b) { - if(b != nil) - free(b); + free(b); } /* len is number of ints */ @@ -2372,25 +2933,13 @@ { Ints* ans; - ans = (Ints*)malloc(OFFSET(data[0], Ints) + len*sizeof(int)); + ans = (Ints*)emalloc(OFFSET(data[0], Ints) + len*sizeof(int)); ans->len = len; return ans; } -static Ints* -makeints(int* buf, int len) -{ - Ints* ans; - - ans = newints(len); - if(len > 0) - memmove(ans->data, buf, len*sizeof(int)); - return ans; -} - static void freeints(Ints* b) { - if(b != nil) - free(b); + free(b); }