* [9front] libsec: various tls changes @ 2021-06-11 15:06 kemal 2021-06-12 13:15 ` cinap_lenrek 0 siblings, 1 reply; 12+ messages in thread From: kemal @ 2021-06-11 15:06 UTC (permalink / raw) To: 9front [-- Attachment #1: Type: text/plain, Size: 51 bytes --] hello, the changes are explained in the patchset. [-- Attachment #2: diff.txt --] [-- Type: text/plain, Size: 23686 bytes --] From 5dbc574bb9451ddbccc1fe6ca214d0eec3930d0e From: kemal <kemalinanc8@gmail.com> Date: Fri, 11 Jun 2021 14:22:36 +0000 Subject: [PATCH] libsec: various changes to tls 1. add the curve x25519 to tls, both client and server. it's more faster, immune to timing attacks by design, does not require verifying if the public key is valid, etc etc. server-side has to check if the client supports the curve, so a new function has been introduced to parse the client's extensions. 2. reject weak dhe primes that can be easily cracked with the number field sieve algorithm. this avoids attacks like logjam. 3. stop putting unix time to the first 4 bytes of client/ server random. it can allow fingerprinting, tls 1.3 doesn't recommend it any more and there was a draft to deprecate this behaviour earlier.[1] 4. simply prf code, remove useless cipher enums. [1] https://datatracker.ietf.org/doc/html/draft-mathewson-no-gmtunixtime-00 --- diff 465199fc65151f513fc82d22919cfc67db9e0d98 5dbc574bb9451ddbccc1fe6ca214d0eec3930d0e --- a/sys/src/libsec/port/tlshand.c Tue Jun 8 23:13:57 2021 +++ b/sys/src/libsec/port/tlshand.c Fri Jun 11 17:22:36 2021 @@ -66,18 +66,20 @@ int psklen; int clientVers; // version in ClientHello uchar sec[MasterSecretSize]; // master secret - uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random + uchar crandom[RandomSize]; // client random + Namedcurve *nc; // selected curve for ECDHE // diffie hellman state DHstate dh; struct { ECdomain dom; ECpriv Q; } ec; + uchar X[32]; // byte generation and handshake checksum - void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); + void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int); void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); int nfin; }; @@ -107,7 +109,7 @@ int tag; union { struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; Ints* ciphers; @@ -115,7 +117,7 @@ Bytes* extensions; } clientHello; struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; int cipher; @@ -214,79 +216,36 @@ // cipher suites enum { - TLS_NULL_WITH_NULL_NULL = 0x0000, - TLS_RSA_WITH_NULL_MD5 = 0x0001, - TLS_RSA_WITH_NULL_SHA = 0x0002, - TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, - TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, - TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, - TLS_RSA_WITH_DES_CBC_SHA = 0X0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, - TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, - TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, - TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, - TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, - TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, - TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, - TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, - TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, - TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, // aes, aka rijndael with 128 bit blocks - TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, - TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, + + TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, - TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, - TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, - TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, - 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_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067, TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, - TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, - TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB, TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, @@ -300,6 +259,20 @@ CompressionMax }; + +// curves +enum { + X25519 = 0x001d, +}; + +// extensions +enum { + Extsni = 0x0000, + Extec = 0x000a, + Extecp = 0x000b, + Extsigalgs = 0x000d, +}; + static Algs cipherAlgs[] = { // ECDHE-ECDSA {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}, @@ -343,6 +316,7 @@ }; static Namedcurve namedcurves[] = { + X25519, nil, 0x0017, secp256r1, 0x0018, secp384r1, }; @@ -413,7 +387,7 @@ static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom); static int tlsSecRSAs(TlsSec *sec, Bytes *epm); -static Bytes* tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc); +static Bytes* tlsSecECDHEs1(TlsSec *sec); static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc); static void tlsSecInitc(TlsSec *sec, int cvers); static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert); @@ -454,6 +428,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; @@ -498,13 +473,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -526,7 +503,7 @@ b = erealloc(b, m + 2+2+2+1+2+n); p = b + m; - put16(p, 0), p += 2; /* Type: server_name */ + put16(p, Extsni), p += 2; /* Type: server_name */ put16(p, 2+1+2+n), p += 2; /* Length */ put16(p, 1+2+n), p += 2; /* Server Name list length */ *p++ = 0; /* Server Name Type: host_name */ @@ -535,26 +512,26 @@ p += n; } - // ECDHE + // Elliptic Curves (also called Supported Groups) if(ProtocolVersion >= TLS10Version){ m = p - b; b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); p = b + m; n = nelem(namedcurves); - put16(p, 0x000a), p += 2; /* Type: elliptic_curves */ + put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ put16(p, (n+1)*2), p += 2; /* Length */ put16(p, n*2), p += 2; /* Elliptic Curves Length */ - for(i=0; i < n; i++){ /* Elliptic curves */ + 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, Extecp), p += 2; /* Type: ec_point_formats */ put16(p, n+1), p += 2; /* Length */ *p++ = n; /* EC point formats Length */ - for(i=0; i < n; i++) /* Elliptic curves point formats */ + for(i=0; i < n; i++) /* EC point formats */ *p++ = pointformats[i]; } @@ -566,7 +543,7 @@ b = erealloc(b, m + 2+2+2+n*2); p = b + m; - put16(p, 0x000d), p += 2; + put16(p, Extsigalgs), p += 2; put16(p, n*2 + 2), p += 2; put16(p, n*2), p += 2; for(i=0; i < n; i++){ @@ -586,6 +563,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; uchar *ext; @@ -641,13 +619,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -665,6 +645,53 @@ return i; } +static Bytes * +tlsServerExtensions(TlsConnection *c, Bytes *ext) +{ + uchar *p, *e; + int i, j, n; + + p = ext->data; + e = p+ext->len; + while(p < e){ + if(e-p < 2) + goto Short; + switch(get16(p)){ + case Extec: + p += 2; + n = get16(p); + if(e-p < n || n < 2) + goto Short; + p += 2; + n = get16(p); + p += 2; + if(e-p < n || n & 1 || n == 0) + goto Short; + for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++) + for(j = 0; j < n; j += 2) + if(namedcurves[i].tlsid == get16(p+j)){ + c->sec->nc = &namedcurves[i]; + break; + } + p += n; + break; + default: + p += 2; + n = get16(p); + p += 2; + if(e-p < n) + goto Short; + p += n; + break; + } + } + // we do not send any extensions yet + return nil; +Short: + tlsError(c, EDecodeError, "clienthello extensions has invalid length"); + return nil; +} + static TlsConnection * tlsServer2(int ctl, int hand, uchar *cert, int certlen, @@ -674,6 +701,7 @@ int cipher, compressor, numcerts, i; TlsConnection *c; Msg m; + Bytes *ext; if(trace) trace("tlsServer2\n"); @@ -740,6 +768,9 @@ goto Err; } } + ext = tlsServerExtensions(c, m.u.clientHello.extensions); + if(c->erred) + goto Err; msgClear(&m); m.tag = HServerHello; @@ -748,6 +779,7 @@ m.u.serverHello.cipher = cipher; m.u.serverHello.compressor = compressor; m.u.serverHello.sid = makebytes(nil, 0); + m.u.serverHello.extensions = ext; if(!msgSend(c, &m, AQueue)) goto Err; @@ -764,11 +796,9 @@ } if(isECDHE(cipher)){ - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ - m.tag = HServerKeyExchange; - m.u.serverKeyExchange.curve = nc->tlsid; - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); if(m.u.serverKeyExchange.dh_parameters == nil){ tlsError(c, EInternalError, "can't set DH parameters"); goto Err; @@ -889,7 +919,9 @@ if(p == nil || g == nil || Ys == nil) return nil; - + // reject dh primes that is susceptible to logjam + if(p->len <= 1024/8) + return nil; Yc = nil; P = bytestomp(p); G = bytestomp(g); @@ -922,47 +954,55 @@ { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; - Namedcurve *nc; ECpub *pub; ECpoint K; + Namedcurve *nc; Bytes *Yc; + Bytes *Z; int n; if(Ys == nil) return nil; - for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++) - if(nc->tlsid == curve) - goto Found; - return nil; -Found: - ecdominit(dom, nc->init); - pub = ecdecodepub(dom, Ys->data, Ys->len); - if(pub == nil) - return nil; - - 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); - - ecgen(dom, Q); - ecmul(dom, pub, Q->d, &K); - - n = (mpsignif(dom->p)+7)/8; - setMasterSecret(sec, mptobytes(K.x, n)); - Yc = newbytes(1 + 2*n); - Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); + if(curve == X25519){ + if(Ys->len != 32) + return nil; + Yc = newbytes(32); + curve25519_dh_new(sec->X, Yc->data); + Z = newbytes(32); + curve25519_dh_finish(sec->X, Ys->data, Z->data); + setMasterSecret(sec, Z); + }else{ + for(nc = namedcurves; nc->tlsid != curve; nc++) + if(nc == &namedcurves[nelem(namedcurves)]) + return nil; + ecdominit(dom, nc->init); + pub = ecdecodepub(dom, Ys->data, Ys->len); + if(pub == nil) + return nil; - mpfree(K.x); - mpfree(K.y); + 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); + + ecgen(dom, Q); + ecmul(dom, pub, Q->d, &K); + + n = (mpsignif(dom->p)+7)/8; + setMasterSecret(sec, mptobytes(K.x, n)); + Yc = newbytes(1 + 2*n); + Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); - ecpubfree(pub); + mpfree(K.x); + mpfree(K.y); + ecpubfree(pub); + } return Yc; } @@ -1045,7 +1085,6 @@ tlsError(c, EIllegalParameter, "invalid compression"); goto Err; } - dhx = isDHE(cipher) || isECDHE(cipher); if(!msgRecv(c, &m)) goto Err; @@ -2313,114 +2352,55 @@ } static void -tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[MD5dlen], tmp[MD5dlen]; - int i, n; - MD5state *s; - - // generate a1 - s = hmac_md5(label, nlabel, key, nkey, nil, nil); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); - s = hmac_md5(label, nlabel, key, nkey, nil, s); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, tmp, s); - n = MD5dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); - memmove(ai, tmp, MD5dlen); - } -} - -static void -tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[SHA1dlen], tmp[SHA1dlen]; - int i, n; - SHAstate *s; - - // generate a1 - s = hmac_sha1(label, nlabel, key, nkey, nil, nil); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); - s = hmac_sha1(label, nlabel, key, nkey, nil, s); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, tmp, s); - n = SHA1dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA1dlen); - } -} - -static void -p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed) +tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed, + DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen) { uchar ai[SHA2_256dlen], tmp[SHA2_256dlen]; - SHAstate *s; + DigestState *s; int n; + assert(sizeof(ai) <= xlen && sizeof(tmp) <= xlen); // generate a1 - s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil); - hmac_sha2_256(seed, nseed, key, nkey, ai, s); + s = x(label, nlabel, key, nkey, nil, nil); + x(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; + s = x(ai, xlen, key, nkey, nil, nil); + s = x(label, nlabel, key, nkey, nil, s); + x(seed, nseed, key, nkey, tmp, s); + n = xlen; if(n > nbuf) n = nbuf; memmove(buf, tmp, n); buf += n; nbuf -= n; - hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA2_256dlen); + x(ai, xlen, key, nkey, tmp, nil); + memmove(ai, tmp, xlen); } } // fill buf with md5(args)^sha1(args) static void -tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { int nlabel = strlen(label); int n = (nkey + 1) >> 1; - memset(buf, 0, nbuf); - 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); + tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed, + hmac_md5, MD5dlen); + tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed, + hmac_sha1, SHA1dlen); } static void -tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { - 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); + tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed, + hmac_sha2_256, SHA2_256dlen); } static void -sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; DigestState *s; @@ -2435,8 +2415,7 @@ tmp[i] = 'A' - 1 + len; s = sha1(tmp, len, nil, nil); s = sha1(key, nkey, nil, s); - s = sha1(seed0, nseed0, nil, s); - sha1(seed1, nseed1, sha1dig, s); + sha1(seed, nseed, sha1dig, s); s = md5(key, nkey, nil, nil); md5(sha1dig, SHA1dlen, md5dig, s); n = MD5dlen; @@ -2486,18 +2465,18 @@ static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) { - uchar h0[MD5dlen], h1[SHA1dlen]; + uchar h[MD5dlen+SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in - md5(nil, 0, h0, &hsh.md5); - sha1(nil, 0, h1, &hsh.sha1); + md5(nil, 0, h, &hsh.md5); + sha1(nil, 0, h+MD5dlen, &hsh.sha1); if(isclient) label = "client finished"; else label = "server finished"; - tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); + tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); } static void @@ -2513,7 +2492,7 @@ label = "client finished"; else label = "server finished"; - p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen); + tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); } static void @@ -2523,8 +2502,9 @@ sec->clientVers = cvers; memmove(sec->crandom, crandom, RandomSize); - put32(sec->srandom, time(nil)); - genrandom(sec->srandom+4, RandomSize-4); + // putting time()'s output to the first 4 bytes is no + // longer recommended and is not useful + genrandom(sec->srandom, RandomSize); } static int @@ -2549,27 +2529,36 @@ } static Bytes* -tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc) +tlsSecECDHEs1(TlsSec *sec) { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; Bytes *par; int n; - ecdominit(dom, nc->init); - memset(Q, 0, sizeof(*Q)); - Q->x = mpnew(0); - Q->y = mpnew(0); - Q->d = mpnew(0); - ecgen(dom, Q); - n = 1 + 2*((mpsignif(dom->p)+7)/8); - par = newbytes(1+2+1+n); - par->data[0] = 3; - put16(par->data+1, nc->tlsid); - n = ecencodepub(dom, Q, par->data+4, par->len-4); - par->data[3] = n; - par->len = 1+2+1+n; - + if(sec->nc == nil) + return nil; + if(sec->nc->tlsid == X25519){ + par = newbytes(1+2+1+32); + par->data[0] = 3; + put16(par->data+1, X25519); + par->data[3] = 32; + curve25519_dh_new(sec->X, par->data+4); + }else{ + ecdominit(dom, sec->nc->init); + memset(Q, 0, sizeof(*Q)); + Q->x = mpnew(0); + Q->y = mpnew(0); + Q->d = mpnew(0); + ecgen(dom, Q); + n = 1 + 2*((mpsignif(dom->p)+7)/8); + par = newbytes(1+2+1+n); + par->data[0] = 3; + put16(par->data+1, sec->nc->tlsid); + n = ecencodepub(dom, Q, par->data+4, par->len-4); + par->data[3] = n; + par->len = 1+2+1+n; + } return par; } @@ -2580,30 +2569,40 @@ ECpriv *Q = &sec->ec.Q; ECpoint K; ECpub *Y; + Bytes *Z; if(Yc == nil){ werrstr("no public key"); return -1; } - if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ - werrstr("bad public key"); - return -1; - } - - memset(&K, 0, sizeof(K)); - K.x = mpnew(0); - K.y = mpnew(0); + if(sec->nc->tlsid == X25519){ + if(Yc->len != 32){ + werrstr("bad public key"); + return -1; + } + Z = newbytes(32); + curve25519_dh_finish(sec->X, Yc->data, Z->data); + setMasterSecret(sec, Z); + }else{ + if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ + werrstr("bad public key"); + return -1; + } - ecmul(dom, Y, Q->d, &K); + memset(&K, 0, sizeof(K)); + K.x = mpnew(0); + K.y = mpnew(0); - setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); + ecmul(dom, Y, Q->d, &K); - mpfree(K.x); - mpfree(K.y); + setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); - ecpubfree(Y); + mpfree(K.x); + mpfree(K.y); + ecpubfree(Y); + } return 0; } @@ -2612,8 +2611,8 @@ { memset(sec, 0, sizeof(*sec)); sec->clientVers = cvers; - put32(sec->crandom, time(nil)); - genrandom(sec->crandom+4, RandomSize-4); + // see the comment on tlsSecInits + genrandom(sec->crandom, RandomSize); } static Bytes* @@ -2671,13 +2670,15 @@ static int setSecrets(TlsConnection *c, int isclient) { - uchar kd[MaxKeyData]; + uchar kd[MaxKeyData], seed[2*RandomSize]; char *secrets; int rv; assert(c->nsecret <= sizeof(kd)); secrets = emalloc(2*c->nsecret); + memmove(seed, c->sec->srandom, RandomSize); + memmove(seed+RandomSize, c->sec->crandom, RandomSize); /* * generate secret keys from the master secret. * @@ -2686,7 +2687,7 @@ * but it's all generated using the same function. */ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion", - c->sec->srandom, RandomSize, c->sec->crandom, RandomSize); + seed, sizeof(seed)); enc64(secrets, 2*c->nsecret, kd, c->nsecret); memset(kd, 0, c->nsecret); @@ -2705,6 +2706,8 @@ static void setMasterSecret(TlsSec *sec, Bytes *pm) { + uchar seed[2*RandomSize]; + if(sec->psklen > 0){ Bytes *opm = pm; uchar *p; @@ -2721,8 +2724,10 @@ freebytes(opm); } + memmove(seed, sec->crandom, RandomSize); + memmove(seed+RandomSize, sec->srandom, RandomSize); (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret", - sec->crandom, RandomSize, sec->srandom, RandomSize); + seed, sizeof(seed)); memset(pm->data, 0, pm->len); freebytes(pm); ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-11 15:06 [9front] libsec: various tls changes kemal @ 2021-06-12 13:15 ` cinap_lenrek 2021-06-12 19:54 ` kemal 0 siblings, 1 reply; 12+ messages in thread From: cinap_lenrek @ 2021-06-12 13:15 UTC (permalink / raw) To: 9front i think theres a problem with the curve negotiation. the new tlsServerExtensions() function can leave c->sec->nc unset. and later, tlsServer2() has this change: - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ - m.tag = HServerKeyExchange; - m.u.serverKeyExchange.curve = nc->tlsid; - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); maybe we can make the code more explicit about this? it is hard to see that it is supposed to modify. otherwise the changes seem reasonable. it is just alot of changes all at once. maybe you can submit the prf changes and the unixtime changes separately? -- cinap ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-12 13:15 ` cinap_lenrek @ 2021-06-12 19:54 ` kemal 2021-06-14 18:26 ` cinap_lenrek 0 siblings, 1 reply; 12+ messages in thread From: kemal @ 2021-06-12 19:54 UTC (permalink / raw) To: 9front [-- Attachment #1: Type: text/plain, Size: 788 bytes --] > i think theres a problem with the curve negotiation. > > the new tlsServerExtensions() function can leave c->sec->nc unset. > > and later, tlsServer2() has this change: > > - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ > - > m.tag = HServerKeyExchange; > - m.u.serverKeyExchange.curve = nc->tlsid; > - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); > + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; > + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); > > maybe we can make the code more explicit about this? it is hard to > see that it is supposed to modify. ok, tlsServer2 now will fallback to secp256r1 if for some reason tlsServerExtensions left it unset. in addition now comments mention that curve is chosen by tlsServerExtensions. [-- Attachment #2: patchset.txt --] [-- Type: text/plain, Size: 23819 bytes --] From 4a0f44660843a9c3f8911b159581b2ad43f21b67 From: kemal <kemalinanc8@gmail.com> Date: Sat, 12 Jun 2021 19:42:16 +0000 Subject: [PATCH] libsec: various changes to tls 1. add the curve x25519 to tls, both client and server. it's more faster, immune to timing attacks by design, does not require verifying if the public key is valid, etc etc. server-side has to check if the client supports the curve, so a new function has been introduced to parse the client's extensions. 2. reject weak dhe primes that can be easily cracked with the number field sieve algorithm. this avoids attacks like logjam. 3. stop putting unix time to the first 4 bytes of client/ server random. it can allow fingerprinting, tls 1.3 doesn't recommend it any more and there was a draft to deprecate this behaviour earlier.[1] 4. simply prf code, remove useless cipher enums. [1] https://datatracker.ietf.org/doc/html/draft-mathewson-no-gmtunixtime-00 --- diff 465199fc65151f513fc82d22919cfc67db9e0d98 4a0f44660843a9c3f8911b159581b2ad43f21b67 --- a/sys/src/libsec/port/tlshand.c Tue Jun 8 23:13:57 2021 +++ b/sys/src/libsec/port/tlshand.c Sat Jun 12 22:42:16 2021 @@ -66,18 +66,20 @@ int psklen; int clientVers; // version in ClientHello uchar sec[MasterSecretSize]; // master secret - uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random + uchar crandom[RandomSize]; // client random + Namedcurve *nc; // selected curve for ECDHE // diffie hellman state DHstate dh; struct { ECdomain dom; ECpriv Q; } ec; + uchar X[32]; // byte generation and handshake checksum - void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); + void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int); void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); int nfin; }; @@ -107,7 +109,7 @@ int tag; union { struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; Ints* ciphers; @@ -115,7 +117,7 @@ Bytes* extensions; } clientHello; struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; int cipher; @@ -214,79 +216,36 @@ // cipher suites enum { - TLS_NULL_WITH_NULL_NULL = 0x0000, - TLS_RSA_WITH_NULL_MD5 = 0x0001, - TLS_RSA_WITH_NULL_SHA = 0x0002, - TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, - TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, - TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, - TLS_RSA_WITH_DES_CBC_SHA = 0X0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, - TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, - TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, - TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, - TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, - TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, - TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, - TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, - TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, - TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, // aes, aka rijndael with 128 bit blocks - TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, - TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, + + TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, - TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, - TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, - TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, - 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_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067, TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, - TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, - TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB, TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, @@ -300,6 +259,20 @@ CompressionMax }; + +// curves +enum { + X25519 = 0x001d, +}; + +// extensions +enum { + Extsni = 0x0000, + Extec = 0x000a, + Extecp = 0x000b, + Extsigalgs = 0x000d, +}; + static Algs cipherAlgs[] = { // ECDHE-ECDSA {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}, @@ -343,6 +316,7 @@ }; static Namedcurve namedcurves[] = { + X25519, nil, 0x0017, secp256r1, 0x0018, secp384r1, }; @@ -413,7 +387,7 @@ static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom); static int tlsSecRSAs(TlsSec *sec, Bytes *epm); -static Bytes* tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc); +static Bytes* tlsSecECDHEs1(TlsSec *sec); static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc); static void tlsSecInitc(TlsSec *sec, int cvers); static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert); @@ -454,6 +428,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; @@ -498,13 +473,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -526,7 +503,7 @@ b = erealloc(b, m + 2+2+2+1+2+n); p = b + m; - put16(p, 0), p += 2; /* Type: server_name */ + put16(p, Extsni), p += 2; /* Type: server_name */ put16(p, 2+1+2+n), p += 2; /* Length */ put16(p, 1+2+n), p += 2; /* Server Name list length */ *p++ = 0; /* Server Name Type: host_name */ @@ -535,26 +512,26 @@ p += n; } - // ECDHE + // Elliptic Curves (also called Supported Groups) if(ProtocolVersion >= TLS10Version){ m = p - b; b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); p = b + m; n = nelem(namedcurves); - put16(p, 0x000a), p += 2; /* Type: elliptic_curves */ + put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ put16(p, (n+1)*2), p += 2; /* Length */ put16(p, n*2), p += 2; /* Elliptic Curves Length */ - for(i=0; i < n; i++){ /* Elliptic curves */ + 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, Extecp), p += 2; /* Type: ec_point_formats */ put16(p, n+1), p += 2; /* Length */ *p++ = n; /* EC point formats Length */ - for(i=0; i < n; i++) /* Elliptic curves point formats */ + for(i=0; i < n; i++) /* EC point formats */ *p++ = pointformats[i]; } @@ -566,7 +543,7 @@ b = erealloc(b, m + 2+2+2+n*2); p = b + m; - put16(p, 0x000d), p += 2; + put16(p, Extsigalgs), p += 2; put16(p, n*2 + 2), p += 2; put16(p, n*2), p += 2; for(i=0; i < n; i++){ @@ -586,6 +563,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; uchar *ext; @@ -641,13 +619,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -665,6 +645,53 @@ return i; } +static Bytes * +tlsServerExtensions(TlsConnection *c, Bytes *ext) +{ + uchar *p, *e; + int i, j, n; + + p = ext->data; + e = p+ext->len; + while(p < e){ + if(e-p < 2) + goto Short; + switch(get16(p)){ + case Extec: + p += 2; + n = get16(p); + if(e-p < n || n < 2) + goto Short; + p += 2; + n = get16(p); + p += 2; + if(e-p < n || n & 1 || n == 0) + goto Short; + for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++) + for(j = 0; j < n; j += 2) + if(namedcurves[i].tlsid == get16(p+j)){ + c->sec->nc = &namedcurves[i]; + break; + } + p += n; + break; + default: + p += 2; + n = get16(p); + p += 2; + if(e-p < n) + goto Short; + p += n; + break; + } + } + // we do not send any extensions yet + return nil; +Short: + tlsError(c, EDecodeError, "clienthello extensions has invalid length"); + return nil; +} + static TlsConnection * tlsServer2(int ctl, int hand, uchar *cert, int certlen, @@ -674,6 +701,7 @@ int cipher, compressor, numcerts, i; TlsConnection *c; Msg m; + Bytes *ext; if(trace) trace("tlsServer2\n"); @@ -740,6 +768,9 @@ goto Err; } } + ext = tlsServerExtensions(c, m.u.clientHello.extensions); + if(c->erred) + goto Err; msgClear(&m); m.tag = HServerHello; @@ -748,6 +779,7 @@ m.u.serverHello.cipher = cipher; m.u.serverHello.compressor = compressor; m.u.serverHello.sid = makebytes(nil, 0); + m.u.serverHello.extensions = ext; if(!msgSend(c, &m, AQueue)) goto Err; @@ -764,11 +796,14 @@ } if(isECDHE(cipher)){ - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ + // curve is chosen by tlsServerExtensions(). + // if it failed to choose a curve, fallback to secp256r1. + if(c->sec->nc == nil) + c->sec->nc = &namedcurves[1]; m.tag = HServerKeyExchange; - m.u.serverKeyExchange.curve = nc->tlsid; - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); if(m.u.serverKeyExchange.dh_parameters == nil){ tlsError(c, EInternalError, "can't set DH parameters"); goto Err; @@ -889,7 +924,9 @@ if(p == nil || g == nil || Ys == nil) return nil; - + // reject dh primes that is susceptible to logjam + if(p->len <= 1024/8) + return nil; Yc = nil; P = bytestomp(p); G = bytestomp(g); @@ -922,47 +959,55 @@ { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; - Namedcurve *nc; ECpub *pub; ECpoint K; + Namedcurve *nc; Bytes *Yc; + Bytes *Z; int n; if(Ys == nil) return nil; - for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++) - if(nc->tlsid == curve) - goto Found; - return nil; -Found: - ecdominit(dom, nc->init); - pub = ecdecodepub(dom, Ys->data, Ys->len); - if(pub == nil) - return nil; - - 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); - - ecgen(dom, Q); - ecmul(dom, pub, Q->d, &K); - - n = (mpsignif(dom->p)+7)/8; - setMasterSecret(sec, mptobytes(K.x, n)); - Yc = newbytes(1 + 2*n); - Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); + if(curve == X25519){ + if(Ys->len != 32) + return nil; + Yc = newbytes(32); + curve25519_dh_new(sec->X, Yc->data); + Z = newbytes(32); + curve25519_dh_finish(sec->X, Ys->data, Z->data); + setMasterSecret(sec, Z); + }else{ + for(nc = namedcurves; nc->tlsid != curve; nc++) + if(nc == &namedcurves[nelem(namedcurves)]) + return nil; + ecdominit(dom, nc->init); + pub = ecdecodepub(dom, Ys->data, Ys->len); + if(pub == nil) + return nil; - mpfree(K.x); - mpfree(K.y); + 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); + + ecgen(dom, Q); + ecmul(dom, pub, Q->d, &K); + + n = (mpsignif(dom->p)+7)/8; + setMasterSecret(sec, mptobytes(K.x, n)); + Yc = newbytes(1 + 2*n); + Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); - ecpubfree(pub); + mpfree(K.x); + mpfree(K.y); + ecpubfree(pub); + } return Yc; } @@ -1045,7 +1090,6 @@ tlsError(c, EIllegalParameter, "invalid compression"); goto Err; } - dhx = isDHE(cipher) || isECDHE(cipher); if(!msgRecv(c, &m)) goto Err; @@ -2313,114 +2357,55 @@ } static void -tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[MD5dlen], tmp[MD5dlen]; - int i, n; - MD5state *s; - - // generate a1 - s = hmac_md5(label, nlabel, key, nkey, nil, nil); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); - s = hmac_md5(label, nlabel, key, nkey, nil, s); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, tmp, s); - n = MD5dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); - memmove(ai, tmp, MD5dlen); - } -} - -static void -tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[SHA1dlen], tmp[SHA1dlen]; - int i, n; - SHAstate *s; - - // generate a1 - s = hmac_sha1(label, nlabel, key, nkey, nil, nil); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); - s = hmac_sha1(label, nlabel, key, nkey, nil, s); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, tmp, s); - n = SHA1dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA1dlen); - } -} - -static void -p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed) +tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed, + DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen) { uchar ai[SHA2_256dlen], tmp[SHA2_256dlen]; - SHAstate *s; + DigestState *s; int n; + assert(sizeof(ai) <= xlen && sizeof(tmp) <= xlen); // generate a1 - s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil); - hmac_sha2_256(seed, nseed, key, nkey, ai, s); + s = x(label, nlabel, key, nkey, nil, nil); + x(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; + s = x(ai, xlen, key, nkey, nil, nil); + s = x(label, nlabel, key, nkey, nil, s); + x(seed, nseed, key, nkey, tmp, s); + n = xlen; if(n > nbuf) n = nbuf; memmove(buf, tmp, n); buf += n; nbuf -= n; - hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA2_256dlen); + x(ai, xlen, key, nkey, tmp, nil); + memmove(ai, tmp, xlen); } } // fill buf with md5(args)^sha1(args) static void -tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { int nlabel = strlen(label); int n = (nkey + 1) >> 1; - memset(buf, 0, nbuf); - 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); + tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed, + hmac_md5, MD5dlen); + tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed, + hmac_sha1, SHA1dlen); } static void -tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { - 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); + tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed, + hmac_sha2_256, SHA2_256dlen); } static void -sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; DigestState *s; @@ -2435,8 +2420,7 @@ tmp[i] = 'A' - 1 + len; s = sha1(tmp, len, nil, nil); s = sha1(key, nkey, nil, s); - s = sha1(seed0, nseed0, nil, s); - sha1(seed1, nseed1, sha1dig, s); + sha1(seed, nseed, sha1dig, s); s = md5(key, nkey, nil, nil); md5(sha1dig, SHA1dlen, md5dig, s); n = MD5dlen; @@ -2486,18 +2470,18 @@ static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) { - uchar h0[MD5dlen], h1[SHA1dlen]; + uchar h[MD5dlen+SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in - md5(nil, 0, h0, &hsh.md5); - sha1(nil, 0, h1, &hsh.sha1); + md5(nil, 0, h, &hsh.md5); + sha1(nil, 0, h+MD5dlen, &hsh.sha1); if(isclient) label = "client finished"; else label = "server finished"; - tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); + tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); } static void @@ -2513,7 +2497,7 @@ label = "client finished"; else label = "server finished"; - p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen); + tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); } static void @@ -2523,8 +2507,9 @@ sec->clientVers = cvers; memmove(sec->crandom, crandom, RandomSize); - put32(sec->srandom, time(nil)); - genrandom(sec->srandom+4, RandomSize-4); + // putting time()'s output to the first 4 bytes is no + // longer recommended and is not useful + genrandom(sec->srandom, RandomSize); } static int @@ -2549,27 +2534,34 @@ } static Bytes* -tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc) +tlsSecECDHEs1(TlsSec *sec) { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; Bytes *par; int n; - ecdominit(dom, nc->init); - memset(Q, 0, sizeof(*Q)); - Q->x = mpnew(0); - Q->y = mpnew(0); - Q->d = mpnew(0); - ecgen(dom, Q); - n = 1 + 2*((mpsignif(dom->p)+7)/8); - par = newbytes(1+2+1+n); - par->data[0] = 3; - put16(par->data+1, nc->tlsid); - n = ecencodepub(dom, Q, par->data+4, par->len-4); - par->data[3] = n; - par->len = 1+2+1+n; - + if(sec->nc->tlsid == X25519){ + par = newbytes(1+2+1+32); + par->data[0] = 3; + put16(par->data+1, X25519); + par->data[3] = 32; + curve25519_dh_new(sec->X, par->data+4); + }else{ + ecdominit(dom, sec->nc->init); + memset(Q, 0, sizeof(*Q)); + Q->x = mpnew(0); + Q->y = mpnew(0); + Q->d = mpnew(0); + ecgen(dom, Q); + n = 1 + 2*((mpsignif(dom->p)+7)/8); + par = newbytes(1+2+1+n); + par->data[0] = 3; + put16(par->data+1, sec->nc->tlsid); + n = ecencodepub(dom, Q, par->data+4, par->len-4); + par->data[3] = n; + par->len = 1+2+1+n; + } return par; } @@ -2580,30 +2572,40 @@ ECpriv *Q = &sec->ec.Q; ECpoint K; ECpub *Y; + Bytes *Z; if(Yc == nil){ werrstr("no public key"); return -1; } - if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ - werrstr("bad public key"); - return -1; - } - - memset(&K, 0, sizeof(K)); - K.x = mpnew(0); - K.y = mpnew(0); + if(sec->nc->tlsid == X25519){ + if(Yc->len != 32){ + werrstr("bad public key"); + return -1; + } + Z = newbytes(32); + curve25519_dh_finish(sec->X, Yc->data, Z->data); + setMasterSecret(sec, Z); + }else{ + if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ + werrstr("bad public key"); + return -1; + } - ecmul(dom, Y, Q->d, &K); + memset(&K, 0, sizeof(K)); + K.x = mpnew(0); + K.y = mpnew(0); - setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); + ecmul(dom, Y, Q->d, &K); - mpfree(K.x); - mpfree(K.y); + setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); - ecpubfree(Y); + mpfree(K.x); + mpfree(K.y); + ecpubfree(Y); + } return 0; } @@ -2612,8 +2614,8 @@ { memset(sec, 0, sizeof(*sec)); sec->clientVers = cvers; - put32(sec->crandom, time(nil)); - genrandom(sec->crandom+4, RandomSize-4); + // see the comment on tlsSecInits + genrandom(sec->crandom, RandomSize); } static Bytes* @@ -2671,13 +2673,15 @@ static int setSecrets(TlsConnection *c, int isclient) { - uchar kd[MaxKeyData]; + uchar kd[MaxKeyData], seed[2*RandomSize]; char *secrets; int rv; assert(c->nsecret <= sizeof(kd)); secrets = emalloc(2*c->nsecret); + memmove(seed, c->sec->srandom, RandomSize); + memmove(seed+RandomSize, c->sec->crandom, RandomSize); /* * generate secret keys from the master secret. * @@ -2686,7 +2690,7 @@ * but it's all generated using the same function. */ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion", - c->sec->srandom, RandomSize, c->sec->crandom, RandomSize); + seed, sizeof(seed)); enc64(secrets, 2*c->nsecret, kd, c->nsecret); memset(kd, 0, c->nsecret); @@ -2705,6 +2709,8 @@ static void setMasterSecret(TlsSec *sec, Bytes *pm) { + uchar seed[2*RandomSize]; + if(sec->psklen > 0){ Bytes *opm = pm; uchar *p; @@ -2721,8 +2727,10 @@ freebytes(opm); } + memmove(seed, sec->crandom, RandomSize); + memmove(seed+RandomSize, sec->srandom, RandomSize); (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret", - sec->crandom, RandomSize, sec->srandom, RandomSize); + seed, sizeof(seed)); memset(pm->data, 0, pm->len); freebytes(pm); ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-12 19:54 ` kemal @ 2021-06-14 18:26 ` cinap_lenrek 2021-06-14 20:27 ` kemal 0 siblings, 1 reply; 12+ messages in thread From: cinap_lenrek @ 2021-06-14 18:26 UTC (permalink / raw) To: 9front ok, that looks good. before, we would always just pick secp256r1, no negotiation whatsoever. i just wonder what the rfc sais, now that we have negotiation for it... it might be better to just error and close the connection instead? but otherwise it looks fine now... -- cinap ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-14 18:26 ` cinap_lenrek @ 2021-06-14 20:27 ` kemal 2021-06-16 9:48 ` cinap_lenrek 0 siblings, 1 reply; 12+ messages in thread From: kemal @ 2021-06-14 20:27 UTC (permalink / raw) To: 9front [-- Attachment #1: Type: text/plain, Size: 985 bytes --] > ok, that looks good. before, we would always just pick secp256r1, > no negotiation whatsoever. i just wonder what the rfc sais, now > that we have negotiation for it... it might be better to just > error and close the connection instead? according to rfc 8422 section 5.1 we should not negotiate ecdh* ciphers if we can't find a suitable curve, if i understood correctly. If a server does not understand the Supported Elliptic Curves Extension, does not understand the Supported Point Formats Extension, or is unable to complete the ECC handshake while restricting itself to the enumerated curves and point formats, it MUST NOT negotiate the use of an ECC cipher suite. Depending on what other cipher suites are proposed by the client and supported by the server, this may result in a fatal handshake failure alert due to the lack of common cipher suites. tlsServer2 now tells okCipher to not pass over ECDHE ciphers if we couldn't find a suitable curve. [-- Attachment #2: patchset.txt --] [-- Type: text/plain, Size: 25530 bytes --] From 93c6b636680a2069c4a60ac98ca983418fd54922 From: kemal <kemalinanc8@gmail.com> Date: Mon, 14 Jun 2021 20:21:53 +0000 Subject: [PATCH] libsec: various changes to tls 1. add the curve x25519 to tls, both client and server. it's more faster, immune to timing attacks by design, does not require verifying if the public key is valid, etc etc. server-side has to check if the client supports the curve, so a new function has been introduced to parse the client's extensions. 2. reject weak dhe primes that can be easily cracked with the number field sieve algorithm. this avoids attacks like logjam. 3. stop putting unix time to the first 4 bytes of client/ server random. it can allow fingerprinting, tls 1.3 doesn't recommend it any more and there was a draft to deprecate this behaviour earlier.[1] 4. simply prf code, remove useless cipher enums. [1] https://datatracker.ietf.org/doc/html/draft-mathewson-no-gmtunixtime-00 --- diff a73a964e51247ed169d322c725a3a18859f109a3 93c6b636680a2069c4a60ac98ca983418fd54922 --- a/sys/src/libsec/port/tlshand.c Mon Jun 14 03:00:37 2021 +++ b/sys/src/libsec/port/tlshand.c Mon Jun 14 23:21:53 2021 @@ -66,18 +66,20 @@ int psklen; int clientVers; // version in ClientHello uchar sec[MasterSecretSize]; // master secret - uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random + uchar crandom[RandomSize]; // client random + Namedcurve *nc; // selected curve for ECDHE // diffie hellman state DHstate dh; struct { ECdomain dom; ECpriv Q; } ec; + uchar X[32]; // byte generation and handshake checksum - void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); + void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int); void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); int nfin; }; @@ -107,7 +109,7 @@ int tag; union { struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; Ints* ciphers; @@ -115,7 +117,7 @@ Bytes* extensions; } clientHello; struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; int cipher; @@ -214,79 +216,36 @@ // cipher suites enum { - TLS_NULL_WITH_NULL_NULL = 0x0000, - TLS_RSA_WITH_NULL_MD5 = 0x0001, - TLS_RSA_WITH_NULL_SHA = 0x0002, - TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, - TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, - TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, - TLS_RSA_WITH_DES_CBC_SHA = 0X0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, - TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, - TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, - TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, - TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, - TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, - TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, - TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, - TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, - TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, // aes, aka rijndael with 128 bit blocks - TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, - TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, + + TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, - TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, - TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, - TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, - 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_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067, TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, - TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, - TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB, TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, @@ -300,6 +259,20 @@ CompressionMax }; + +// curves +enum { + X25519 = 0x001d, +}; + +// extensions +enum { + Extsni = 0x0000, + Extec = 0x000a, + Extecp = 0x000b, + Extsigalgs = 0x000d, +}; + static Algs cipherAlgs[] = { // ECDHE-ECDSA {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}, @@ -343,6 +316,7 @@ }; static Namedcurve namedcurves[] = { + X25519, nil, 0x0017, secp256r1, 0x0018, secp384r1, }; @@ -402,7 +376,7 @@ static int isECDSA(int tlsid); static int setAlgs(TlsConnection *c, int a); -static int okCipher(Ints *cv, int ispsk); +static int okCipher(Ints *cv, int ispsk, int canec); static int okCompression(Bytes *cv); static int initCiphers(void); static Ints* makeciphers(int ispsk); @@ -413,7 +387,7 @@ static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom); static int tlsSecRSAs(TlsSec *sec, Bytes *epm); -static Bytes* tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc); +static Bytes* tlsSecECDHEs1(TlsSec *sec); static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc); static void tlsSecInitc(TlsSec *sec, int cvers); static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert); @@ -454,6 +428,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; @@ -498,13 +473,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -526,7 +503,7 @@ b = erealloc(b, m + 2+2+2+1+2+n); p = b + m; - put16(p, 0), p += 2; /* Type: server_name */ + put16(p, Extsni), p += 2; /* Type: server_name */ put16(p, 2+1+2+n), p += 2; /* Length */ put16(p, 1+2+n), p += 2; /* Server Name list length */ *p++ = 0; /* Server Name Type: host_name */ @@ -535,26 +512,26 @@ p += n; } - // ECDHE + // Elliptic Curves (also called Supported Groups) if(ProtocolVersion >= TLS10Version){ m = p - b; b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); p = b + m; n = nelem(namedcurves); - put16(p, 0x000a), p += 2; /* Type: elliptic_curves */ + put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ put16(p, (n+1)*2), p += 2; /* Length */ put16(p, n*2), p += 2; /* Elliptic Curves Length */ - for(i=0; i < n; i++){ /* Elliptic curves */ + 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, Extecp), p += 2; /* Type: ec_point_formats */ put16(p, n+1), p += 2; /* Length */ *p++ = n; /* EC point formats Length */ - for(i=0; i < n; i++) /* Elliptic curves point formats */ + for(i=0; i < n; i++) /* EC point formats */ *p++ = pointformats[i]; } @@ -566,7 +543,7 @@ b = erealloc(b, m + 2+2+2+n*2); p = b + m; - put16(p, 0x000d), p += 2; + put16(p, Extsigalgs), p += 2; put16(p, n*2 + 2), p += 2; put16(p, n*2), p += 2; for(i=0; i < n; i++){ @@ -586,6 +563,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; uchar *ext; @@ -641,13 +619,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -665,6 +645,53 @@ return i; } +static Bytes * +tlsServerExtensions(TlsConnection *c, Bytes *ext) +{ + uchar *p, *e; + int i, j, n; + + p = ext->data; + e = p+ext->len; + while(p < e){ + if(e-p < 2) + goto Short; + switch(get16(p)){ + case Extec: + p += 2; + n = get16(p); + if(e-p < n || n < 2) + goto Short; + p += 2; + n = get16(p); + p += 2; + if(e-p < n || n & 1 || n == 0) + goto Short; + for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++) + for(j = 0; j < n; j += 2) + if(namedcurves[i].tlsid == get16(p+j)){ + c->sec->nc = &namedcurves[i]; + break; + } + p += n; + break; + default: + p += 2; + n = get16(p); + p += 2; + if(e-p < n) + goto Short; + p += n; + break; + } + } + // we do not send any extensions yet + return nil; +Short: + tlsError(c, EDecodeError, "clienthello extensions has invalid length"); + return nil; +} + static TlsConnection * tlsServer2(int ctl, int hand, uchar *cert, int certlen, @@ -674,6 +701,7 @@ int cipher, compressor, numcerts, i; TlsConnection *c; Msg m; + Bytes *ext; if(trace) trace("tlsServer2\n"); @@ -708,19 +736,6 @@ tlsError(c, EInappropriateFallback, "inappropriate fallback"); goto Err; } - cipher = okCipher(m.u.clientHello.ciphers, psklen > 0); - if(cipher < 0 || !setAlgs(c, cipher)) { - tlsError(c, EHandshakeFailure, "no matching cipher suite"); - goto Err; - } - compressor = okCompression(m.u.clientHello.compressors); - if(compressor < 0) { - tlsError(c, EHandshakeFailure, "no matching compressor"); - goto Err; - } - if(trace) - trace(" cipher %x, compressor %x\n", cipher, compressor); - tlsSecInits(c->sec, m.u.clientHello.version, m.u.clientHello.random); tlsSecVers(c->sec, c->version); if(psklen > 0){ @@ -740,6 +755,21 @@ goto Err; } } + ext = tlsServerExtensions(c, m.u.clientHello.extensions); + if(c->erred) + goto Err; + cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil); + if(cipher < 0 || !setAlgs(c, cipher)) { + tlsError(c, EHandshakeFailure, "no matching cipher suite"); + goto Err; + } + compressor = okCompression(m.u.clientHello.compressors); + if(compressor < 0) { + tlsError(c, EHandshakeFailure, "no matching compressor"); + goto Err; + } + if(trace) + trace(" cipher %x, compressor %x\n", cipher, compressor); msgClear(&m); m.tag = HServerHello; @@ -748,6 +778,7 @@ m.u.serverHello.cipher = cipher; m.u.serverHello.compressor = compressor; m.u.serverHello.sid = makebytes(nil, 0); + m.u.serverHello.extensions = ext; if(!msgSend(c, &m, AQueue)) goto Err; @@ -764,11 +795,9 @@ } if(isECDHE(cipher)){ - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ - m.tag = HServerKeyExchange; - m.u.serverKeyExchange.curve = nc->tlsid; - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); if(m.u.serverKeyExchange.dh_parameters == nil){ tlsError(c, EInternalError, "can't set DH parameters"); goto Err; @@ -889,7 +918,9 @@ if(p == nil || g == nil || Ys == nil) return nil; - + // reject dh primes that is susceptible to logjam + if(p->len <= 1024/8) + return nil; Yc = nil; P = bytestomp(p); G = bytestomp(g); @@ -922,47 +953,55 @@ { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; - Namedcurve *nc; ECpub *pub; ECpoint K; + Namedcurve *nc; Bytes *Yc; + Bytes *Z; int n; if(Ys == nil) return nil; - for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++) - if(nc->tlsid == curve) - goto Found; - return nil; - -Found: - ecdominit(dom, nc->init); - pub = ecdecodepub(dom, Ys->data, Ys->len); - if(pub == nil) - return nil; - 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); - - ecgen(dom, Q); - ecmul(dom, pub, Q->d, &K); - - n = (mpsignif(dom->p)+7)/8; - setMasterSecret(sec, mptobytes(K.x, n)); - Yc = newbytes(1 + 2*n); - Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); + if(curve == X25519){ + if(Ys->len != 32) + return nil; + Yc = newbytes(32); + curve25519_dh_new(sec->X, Yc->data); + Z = newbytes(32); + curve25519_dh_finish(sec->X, Ys->data, Z->data); + setMasterSecret(sec, Z); + }else{ + for(nc = namedcurves; nc->tlsid != curve; nc++) + if(nc == &namedcurves[nelem(namedcurves)]) + return nil; + ecdominit(dom, nc->init); + pub = ecdecodepub(dom, Ys->data, Ys->len); + if(pub == nil) + return nil; - mpfree(K.x); - mpfree(K.y); + 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); + + ecgen(dom, Q); + ecmul(dom, pub, Q->d, &K); + + n = (mpsignif(dom->p)+7)/8; + setMasterSecret(sec, mptobytes(K.x, n)); + Yc = newbytes(1 + 2*n); + Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); - ecpubfree(pub); + mpfree(K.x); + mpfree(K.y); + ecpubfree(pub); + } return Yc; } @@ -1045,7 +1084,6 @@ tlsError(c, EIllegalParameter, "invalid compression"); goto Err; } - dhx = isDHE(cipher) || isECDHE(cipher); if(!msgRecv(c, &m)) goto Err; @@ -2136,13 +2174,17 @@ } static int -okCipher(Ints *cv, int ispsk) +okCipher(Ints *cv, int ispsk, int canec) { int i, c; for(i = 0; i < nelem(cipherAlgs); i++) { c = cipherAlgs[i].tlsid; - if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c) || isPSK(c) != ispsk) + if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c)) + continue; + if(isPSK(c) != ispsk) + continue; + if(isECDHE(c) && !canec) continue; if(lookupid(cv, c) >= 0) return c; @@ -2313,114 +2355,55 @@ } static void -tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[MD5dlen], tmp[MD5dlen]; - int i, n; - MD5state *s; - - // generate a1 - s = hmac_md5(label, nlabel, key, nkey, nil, nil); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); - s = hmac_md5(label, nlabel, key, nkey, nil, s); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, tmp, s); - n = MD5dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); - memmove(ai, tmp, MD5dlen); - } -} - -static void -tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[SHA1dlen], tmp[SHA1dlen]; - int i, n; - SHAstate *s; - - // generate a1 - s = hmac_sha1(label, nlabel, key, nkey, nil, nil); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); - s = hmac_sha1(label, nlabel, key, nkey, nil, s); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, tmp, s); - n = SHA1dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA1dlen); - } -} - -static void -p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed) +tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed, + DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen) { uchar ai[SHA2_256dlen], tmp[SHA2_256dlen]; - SHAstate *s; + DigestState *s; int n; + assert(sizeof(ai) <= xlen && sizeof(tmp) <= xlen); // generate a1 - s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil); - hmac_sha2_256(seed, nseed, key, nkey, ai, s); + s = x(label, nlabel, key, nkey, nil, nil); + x(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; + s = x(ai, xlen, key, nkey, nil, nil); + s = x(label, nlabel, key, nkey, nil, s); + x(seed, nseed, key, nkey, tmp, s); + n = xlen; if(n > nbuf) n = nbuf; memmove(buf, tmp, n); buf += n; nbuf -= n; - hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA2_256dlen); + x(ai, xlen, key, nkey, tmp, nil); + memmove(ai, tmp, xlen); } } // fill buf with md5(args)^sha1(args) static void -tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { int nlabel = strlen(label); int n = (nkey + 1) >> 1; - memset(buf, 0, nbuf); - 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); + tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed, + hmac_md5, MD5dlen); + tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed, + hmac_sha1, SHA1dlen); } static void -tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { - 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); + tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed, + hmac_sha2_256, SHA2_256dlen); } static void -sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; DigestState *s; @@ -2435,8 +2418,7 @@ tmp[i] = 'A' - 1 + len; s = sha1(tmp, len, nil, nil); s = sha1(key, nkey, nil, s); - s = sha1(seed0, nseed0, nil, s); - sha1(seed1, nseed1, sha1dig, s); + sha1(seed, nseed, sha1dig, s); s = md5(key, nkey, nil, nil); md5(sha1dig, SHA1dlen, md5dig, s); n = MD5dlen; @@ -2486,18 +2468,18 @@ static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) { - uchar h0[MD5dlen], h1[SHA1dlen]; + uchar h[MD5dlen+SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in - md5(nil, 0, h0, &hsh.md5); - sha1(nil, 0, h1, &hsh.sha1); + md5(nil, 0, h, &hsh.md5); + sha1(nil, 0, h+MD5dlen, &hsh.sha1); if(isclient) label = "client finished"; else label = "server finished"; - tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); + tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); } static void @@ -2513,7 +2495,7 @@ label = "client finished"; else label = "server finished"; - p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen); + tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); } static void @@ -2523,8 +2505,9 @@ sec->clientVers = cvers; memmove(sec->crandom, crandom, RandomSize); - put32(sec->srandom, time(nil)); - genrandom(sec->srandom+4, RandomSize-4); + // putting time()'s output to the first 4 bytes is no + // longer recommended and is not useful + genrandom(sec->srandom, RandomSize); } static int @@ -2549,27 +2532,36 @@ } static Bytes* -tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc) +tlsSecECDHEs1(TlsSec *sec) { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; Bytes *par; int n; - ecdominit(dom, nc->init); - memset(Q, 0, sizeof(*Q)); - Q->x = mpnew(0); - Q->y = mpnew(0); - Q->d = mpnew(0); - ecgen(dom, Q); - n = 1 + 2*((mpsignif(dom->p)+7)/8); - par = newbytes(1+2+1+n); - par->data[0] = 3; - put16(par->data+1, nc->tlsid); - n = ecencodepub(dom, Q, par->data+4, par->len-4); - par->data[3] = n; - par->len = 1+2+1+n; - + if(sec->nc == nil) + return nil; + if(sec->nc->tlsid == X25519){ + par = newbytes(1+2+1+32); + par->data[0] = 3; + put16(par->data+1, X25519); + par->data[3] = 32; + curve25519_dh_new(sec->X, par->data+4); + }else{ + ecdominit(dom, sec->nc->init); + memset(Q, 0, sizeof(*Q)); + Q->x = mpnew(0); + Q->y = mpnew(0); + Q->d = mpnew(0); + ecgen(dom, Q); + n = 1 + 2*((mpsignif(dom->p)+7)/8); + par = newbytes(1+2+1+n); + par->data[0] = 3; + put16(par->data+1, sec->nc->tlsid); + n = ecencodepub(dom, Q, par->data+4, par->len-4); + par->data[3] = n; + par->len = 1+2+1+n; + } return par; } @@ -2580,30 +2572,40 @@ ECpriv *Q = &sec->ec.Q; ECpoint K; ECpub *Y; + Bytes *Z; if(Yc == nil){ werrstr("no public key"); return -1; } - if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ - werrstr("bad public key"); - return -1; - } - - memset(&K, 0, sizeof(K)); - K.x = mpnew(0); - K.y = mpnew(0); + if(sec->nc->tlsid == X25519){ + if(Yc->len != 32){ + werrstr("bad public key"); + return -1; + } + Z = newbytes(32); + curve25519_dh_finish(sec->X, Yc->data, Z->data); + setMasterSecret(sec, Z); + }else{ + if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ + werrstr("bad public key"); + return -1; + } - ecmul(dom, Y, Q->d, &K); + memset(&K, 0, sizeof(K)); + K.x = mpnew(0); + K.y = mpnew(0); - setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); + ecmul(dom, Y, Q->d, &K); - mpfree(K.x); - mpfree(K.y); + setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); - ecpubfree(Y); + mpfree(K.x); + mpfree(K.y); + ecpubfree(Y); + } return 0; } @@ -2612,8 +2614,8 @@ { memset(sec, 0, sizeof(*sec)); sec->clientVers = cvers; - put32(sec->crandom, time(nil)); - genrandom(sec->crandom+4, RandomSize-4); + // see the comment on tlsSecInits + genrandom(sec->crandom, RandomSize); } static Bytes* @@ -2671,13 +2673,15 @@ static int setSecrets(TlsConnection *c, int isclient) { - uchar kd[MaxKeyData]; + uchar kd[MaxKeyData], seed[2*RandomSize]; char *secrets; int rv; assert(c->nsecret <= sizeof(kd)); secrets = emalloc(2*c->nsecret); + memmove(seed, c->sec->srandom, RandomSize); + memmove(seed+RandomSize, c->sec->crandom, RandomSize); /* * generate secret keys from the master secret. * @@ -2686,7 +2690,7 @@ * but it's all generated using the same function. */ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion", - c->sec->srandom, RandomSize, c->sec->crandom, RandomSize); + seed, sizeof(seed)); enc64(secrets, 2*c->nsecret, kd, c->nsecret); memset(kd, 0, c->nsecret); @@ -2705,6 +2709,8 @@ static void setMasterSecret(TlsSec *sec, Bytes *pm) { + uchar seed[2*RandomSize]; + if(sec->psklen > 0){ Bytes *opm = pm; uchar *p; @@ -2721,8 +2727,10 @@ freebytes(opm); } + memmove(seed, sec->crandom, RandomSize); + memmove(seed+RandomSize, sec->srandom, RandomSize); (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret", - sec->crandom, RandomSize, sec->srandom, RandomSize); + seed, sizeof(seed)); memset(pm->data, 0, pm->len); freebytes(pm); ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-14 20:27 ` kemal @ 2021-06-16 9:48 ` cinap_lenrek 2021-06-16 11:59 ` kemal 0 siblings, 1 reply; 12+ messages in thread From: cinap_lenrek @ 2021-06-16 9:48 UTC (permalink / raw) To: 9front seems to be a memory leak of the Bytes *ext in tlsServer2() in case of an error? -- cinap ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-16 9:48 ` cinap_lenrek @ 2021-06-16 11:59 ` kemal 2021-06-16 14:02 ` cinap_lenrek 0 siblings, 1 reply; 12+ messages in thread From: kemal @ 2021-06-16 11:59 UTC (permalink / raw) To: 9front > seems to be a memory leak of the Bytes *ext in tlsServer2() in case > of an error? wdym? tlsServerExtensions always returns nil, (well, for now as we don't send any extensions.) so, we don't need to free ext. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-16 11:59 ` kemal @ 2021-06-16 14:02 ` cinap_lenrek 2021-06-18 18:51 ` kemal 0 siblings, 1 reply; 12+ messages in thread From: cinap_lenrek @ 2021-06-16 14:02 UTC (permalink / raw) To: 9front > wdym? tlsServerExtensions always returns nil, > (well, for now as we don't send any extensions.) > so, we don't need to free ext. hm... that sounds like a trap waiting to happen. if we never produce any extension response, maybe it would be better to just get rid of it for now and return a error status instead? also, the naming would need to change. as this function actually never *produces* server extensions and instead *processes* the client extensions... what about something like: if(!okClientExtensions(c, m.u.clientHello.extensions)) goto Err; once we need to produce server extensions we can add the function back... and we can place it somewhere near/before the sending the server hello so error handling is less complicated. -- cinap ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-16 14:02 ` cinap_lenrek @ 2021-06-18 18:51 ` kemal 2021-06-18 19:13 ` kemal 2021-06-20 14:56 ` cinap_lenrek 0 siblings, 2 replies; 12+ messages in thread From: kemal @ 2021-06-18 18:51 UTC (permalink / raw) To: 9front [-- Attachment #1: Type: text/plain, Size: 758 bytes --] > if we never produce any extension response, maybe > it would be better to just get rid of it for now > and return a error status instead? > > also, the naming would need to change. as this > function actually never *produces* server > extensions and instead *processes* the client > extensions... what about something like: > > if(!okClientExtensions(c, m.u.clientHello.extensions)) > goto Err; ok. also, after a bit of diving in the rfcs i found out that the x25519 rfc specifies an optional step that if the shared secret turned out to be all zeroes, abort. rfc 8422 (tls ecc rfc) says that this MUST be done. i was unsure if i should have added that check to curve25519_finish, as it would break source compatibility, so i added it to tlsSecECDHE*. [-- Attachment #2: diff --] [-- Type: application/octet-stream, Size: 27438 bytes --] From e92ab3b04ad05b4f5f22797c29ae88d62fbe309d From: kemal <kemalinanc8@gmail.com> Date: Fri, 18 Jun 2021 18:36:38 +0000 Subject: [PATCH] libsec: various changes to tls 1. add the curve x25519 to tls, both client and server. it's more faster, immune to timing attacks by design, does not require verifying if the public key is valid, etc etc. server-side has to check if the client supports the curve, so a new function has been introduced to parse the client's extensions. 2. reject weak dhe primes that can be easily cracked with the number field sieve algorithm. this avoids attacks like logjam. 3. stop putting unix time to the first 4 bytes of client/ server random. it can allow fingerprinting, tls 1.3 doesn't recommend it any more and there was a draft to deprecate this behaviour earlier.[1] 4. simply prf code, remove useless cipher enums. [1] https://datatracker.ietf.org/doc/html/draft-mathewson-no-gmtunixtime-00 --- diff 7f697e822b2efd5c757141720469d105a5605ca4 e92ab3b04ad05b4f5f22797c29ae88d62fbe309d --- a/sys/man/1/date Wed Jun 16 03:28:01 2021 +++ b/sys/man/1/date Fri Jun 18 21:36:38 2021 @@ -6,6 +6,9 @@ [ .I option ] [ +.B -f +.I format +] [ .I seconds ] .br @@ -16,6 +19,8 @@ .B Tue Aug 16 17:03:52 CDT 1977 .PP +Or the format specified by the format option. + The options are .TP .B -u @@ -33,6 +38,13 @@ .TP .B -m Report the date as an email compatible (RFC2822) time stamp. +.TP +.B -f +Print using the format specified by the +.I format +string rather than the default. Format strings are in the format used by +.IR tmdate (2) + .PP The conversion from Greenwich Mean Time to local time depends on the .B $timezone @@ -65,3 +77,5 @@ .B /sys/src/cmd/date.c .br .B /sys/src/cmd/clock.c +.SH "SEE ALSO" +.IR tmdate (2) --- a/sys/src/cmd/git/branch Wed Jun 16 03:28:01 2021 +++ b/sys/src/cmd/git/branch Fri Jun 18 21:36:38 2021 @@ -37,6 +37,15 @@ if not base=`{git/query HEAD} +if(~ $#newbr 0){ + if(! ~ $#baseref 0) + die update would clobber $branch with $baseref + baseref=`$nl{echo -n $new | sed s@refs/heads/@refs/remotes/origin/@} + echo $baseref + if(! test -e .git/$new) + if(! base=`{git/query $baseref}) + die could not find branch $branch +} modified=`$nl{git/query -c HEAD $base | grep '^[^-]' | subst '^..'} deleted=`$nl{git/query -c HEAD $base | grep '^-' | subst '^..'} @@ -48,14 +57,6 @@ rm -f .git/$new echo 'deleted branch' $new exit -} -if(~ $#newbr 0){ - if(! ~ $#baseref 0) - die update would clobber $branch with $baseref - baseref=`$nl{echo -n $new | sed s@refs/heads/@refs/remotes/origin/@} - if(! test -e .git/$new) - if(! base=`{git/query $baseref}) - die could not find branch $branch } commit=`{git/query $base} || die 'branch does not exist:' $base if(~ $new */*) --- a/sys/src/libsec/port/tlshand.c Wed Jun 16 03:28:01 2021 +++ b/sys/src/libsec/port/tlshand.c Fri Jun 18 21:36:38 2021 @@ -66,18 +66,20 @@ int psklen; int clientVers; // version in ClientHello uchar sec[MasterSecretSize]; // master secret - uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random + uchar crandom[RandomSize]; // client random + Namedcurve *nc; // selected curve for ECDHE // diffie hellman state DHstate dh; struct { ECdomain dom; ECpriv Q; } ec; + uchar X[32]; // byte generation and handshake checksum - void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); + void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int); void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); int nfin; }; @@ -107,7 +109,7 @@ int tag; union { struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; Ints* ciphers; @@ -115,7 +117,7 @@ Bytes* extensions; } clientHello; struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; int cipher; @@ -214,79 +216,36 @@ // cipher suites enum { - TLS_NULL_WITH_NULL_NULL = 0x0000, - TLS_RSA_WITH_NULL_MD5 = 0x0001, - TLS_RSA_WITH_NULL_SHA = 0x0002, - TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, - TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, - TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, - TLS_RSA_WITH_DES_CBC_SHA = 0X0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, - TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, - TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, - TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, - TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, - TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, - TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, - TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, - TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, - TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, // aes, aka rijndael with 128 bit blocks - TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, - TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, + + TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, - TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, - TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, - TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, - 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_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067, TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, - TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, - TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB, TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, @@ -300,6 +259,20 @@ CompressionMax }; + +// curves +enum { + X25519 = 0x001d, +}; + +// extensions +enum { + Extsni = 0x0000, + Extec = 0x000a, + Extecp = 0x000b, + Extsigalgs = 0x000d, +}; + static Algs cipherAlgs[] = { // ECDHE-ECDSA {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}, @@ -343,6 +316,7 @@ }; static Namedcurve namedcurves[] = { + X25519, nil, 0x0017, secp256r1, 0x0018, secp384r1, }; @@ -402,7 +376,7 @@ static int isECDSA(int tlsid); static int setAlgs(TlsConnection *c, int a); -static int okCipher(Ints *cv, int ispsk); +static int okCipher(Ints *cv, int ispsk, int canec); static int okCompression(Bytes *cv); static int initCiphers(void); static Ints* makeciphers(int ispsk); @@ -413,7 +387,7 @@ static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom); static int tlsSecRSAs(TlsSec *sec, Bytes *epm); -static Bytes* tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc); +static Bytes* tlsSecECDHEs1(TlsSec *sec); static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc); static void tlsSecInitc(TlsSec *sec, int cvers); static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert); @@ -454,6 +428,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; @@ -498,13 +473,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -526,7 +503,7 @@ b = erealloc(b, m + 2+2+2+1+2+n); p = b + m; - put16(p, 0), p += 2; /* Type: server_name */ + put16(p, Extsni), p += 2; /* Type: server_name */ put16(p, 2+1+2+n), p += 2; /* Length */ put16(p, 1+2+n), p += 2; /* Server Name list length */ *p++ = 0; /* Server Name Type: host_name */ @@ -535,26 +512,26 @@ p += n; } - // ECDHE + // Elliptic Curves (also called Supported Groups) if(ProtocolVersion >= TLS10Version){ m = p - b; b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); p = b + m; n = nelem(namedcurves); - put16(p, 0x000a), p += 2; /* Type: elliptic_curves */ + put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ put16(p, (n+1)*2), p += 2; /* Length */ put16(p, n*2), p += 2; /* Elliptic Curves Length */ - for(i=0; i < n; i++){ /* Elliptic curves */ + 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, Extecp), p += 2; /* Type: ec_point_formats */ put16(p, n+1), p += 2; /* Length */ *p++ = n; /* EC point formats Length */ - for(i=0; i < n; i++) /* Elliptic curves point formats */ + for(i=0; i < n; i++) /* EC point formats */ *p++ = pointformats[i]; } @@ -566,7 +543,7 @@ b = erealloc(b, m + 2+2+2+n*2); p = b + m; - put16(p, 0x000d), p += 2; + put16(p, Extsigalgs), p += 2; put16(p, n*2 + 2), p += 2; put16(p, n*2), p += 2; for(i=0; i < n; i++){ @@ -586,6 +563,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; uchar *ext; @@ -641,13 +619,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -665,6 +645,53 @@ return i; } +static int +checkClientExtensions(TlsConnection *c, Bytes *ext) +{ + uchar *p, *e; + int i, j, n; + + p = ext->data; + e = p+ext->len; + while(p < e){ + if(e-p < 2) + goto Short; + switch(get16(p)){ + case Extec: + p += 2; + n = get16(p); + if(e-p < n || n < 2) + goto Short; + p += 2; + n = get16(p); + p += 2; + if(e-p < n || n & 1 || n == 0) + goto Short; + for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++) + for(j = 0; j < n; j += 2) + if(namedcurves[i].tlsid == get16(p+j)){ + c->sec->nc = &namedcurves[i]; + break; + } + p += n; + break; + default: + p += 2; + n = get16(p); + p += 2; + if(e-p < n) + goto Short; + p += n; + break; + } + } + + return 0; +Short: + tlsError(c, EDecodeError, "clienthello extensions has invalid length"); + return -1; +} + static TlsConnection * tlsServer2(int ctl, int hand, uchar *cert, int certlen, @@ -708,19 +735,6 @@ tlsError(c, EInappropriateFallback, "inappropriate fallback"); goto Err; } - cipher = okCipher(m.u.clientHello.ciphers, psklen > 0); - if(cipher < 0 || !setAlgs(c, cipher)) { - tlsError(c, EHandshakeFailure, "no matching cipher suite"); - goto Err; - } - compressor = okCompression(m.u.clientHello.compressors); - if(compressor < 0) { - tlsError(c, EHandshakeFailure, "no matching compressor"); - goto Err; - } - if(trace) - trace(" cipher %x, compressor %x\n", cipher, compressor); - tlsSecInits(c->sec, m.u.clientHello.version, m.u.clientHello.random); tlsSecVers(c->sec, c->version); if(psklen > 0){ @@ -740,6 +754,20 @@ goto Err; } } + if(checkClientExtensions(c, m.u.clientHello.extensions) < 0) + goto Err; + cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil); + if(cipher < 0 || !setAlgs(c, cipher)) { + tlsError(c, EHandshakeFailure, "no matching cipher suite"); + goto Err; + } + compressor = okCompression(m.u.clientHello.compressors); + if(compressor < 0) { + tlsError(c, EHandshakeFailure, "no matching compressor"); + goto Err; + } + if(trace) + trace(" cipher %x, compressor %x\n", cipher, compressor); msgClear(&m); m.tag = HServerHello; @@ -764,11 +792,9 @@ } if(isECDHE(cipher)){ - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ - m.tag = HServerKeyExchange; - m.u.serverKeyExchange.curve = nc->tlsid; - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); if(m.u.serverKeyExchange.dh_parameters == nil){ tlsError(c, EInternalError, "can't set DH parameters"); goto Err; @@ -889,7 +915,9 @@ if(p == nil || g == nil || Ys == nil) return nil; - + // reject dh primes that is susceptible to logjam + if(p->len <= 1024/8) + return nil; Yc = nil; P = bytestomp(p); G = bytestomp(g); @@ -920,49 +948,65 @@ static Bytes* tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys) { + static char zero[32] = {0}; ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; - Namedcurve *nc; ECpub *pub; ECpoint K; + Namedcurve *nc; Bytes *Yc; + Bytes *Z; int n; if(Ys == nil) return nil; - for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++) - if(nc->tlsid == curve) - goto Found; - return nil; - -Found: - ecdominit(dom, nc->init); - pub = ecdecodepub(dom, Ys->data, Ys->len); - if(pub == nil) - return nil; - 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); - - ecgen(dom, Q); - ecmul(dom, pub, Q->d, &K); - - n = (mpsignif(dom->p)+7)/8; - setMasterSecret(sec, mptobytes(K.x, n)); - Yc = newbytes(1 + 2*n); - Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); + if(curve == X25519){ + if(Ys->len != 32) + return nil; + Yc = newbytes(32); + curve25519_dh_new(sec->X, Yc->data); + Z = newbytes(32); + curve25519_dh_finish(sec->X, Ys->data, Z->data); + // rfc wants us to terminate the connection if + // shared secret == all zeroes. + if(tsmemcmp(Z->data, zero, Z->len) == 0){ + freebytes(Yc); + freebytes(Z); + return nil; + } + setMasterSecret(sec, Z); + }else{ + for(nc = namedcurves; nc->tlsid != curve; nc++) + if(nc == &namedcurves[nelem(namedcurves)]) + return nil; + ecdominit(dom, nc->init); + pub = ecdecodepub(dom, Ys->data, Ys->len); + if(pub == nil) + return nil; - mpfree(K.x); - mpfree(K.y); + 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); + + ecgen(dom, Q); + ecmul(dom, pub, Q->d, &K); + + n = (mpsignif(dom->p)+7)/8; + setMasterSecret(sec, mptobytes(K.x, n)); + Yc = newbytes(1 + 2*n); + Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); - ecpubfree(pub); + mpfree(K.x); + mpfree(K.y); + ecpubfree(pub); + } return Yc; } @@ -1045,7 +1089,6 @@ tlsError(c, EIllegalParameter, "invalid compression"); goto Err; } - dhx = isDHE(cipher) || isECDHE(cipher); if(!msgRecv(c, &m)) goto Err; @@ -2136,13 +2179,17 @@ } static int -okCipher(Ints *cv, int ispsk) +okCipher(Ints *cv, int ispsk, int canec) { int i, c; for(i = 0; i < nelem(cipherAlgs); i++) { c = cipherAlgs[i].tlsid; - if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c) || isPSK(c) != ispsk) + if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c)) + continue; + if(isPSK(c) != ispsk) + continue; + if(isECDHE(c) && !canec) continue; if(lookupid(cv, c) >= 0) return c; @@ -2313,114 +2360,55 @@ } static void -tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[MD5dlen], tmp[MD5dlen]; - int i, n; - MD5state *s; - - // generate a1 - s = hmac_md5(label, nlabel, key, nkey, nil, nil); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); - s = hmac_md5(label, nlabel, key, nkey, nil, s); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, tmp, s); - n = MD5dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); - memmove(ai, tmp, MD5dlen); - } -} - -static void -tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[SHA1dlen], tmp[SHA1dlen]; - int i, n; - SHAstate *s; - - // generate a1 - s = hmac_sha1(label, nlabel, key, nkey, nil, nil); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); - s = hmac_sha1(label, nlabel, key, nkey, nil, s); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, tmp, s); - n = SHA1dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA1dlen); - } -} - -static void -p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed) +tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed, + DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen) { uchar ai[SHA2_256dlen], tmp[SHA2_256dlen]; - SHAstate *s; + DigestState *s; int n; + assert(sizeof(ai) <= xlen && sizeof(tmp) <= xlen); // generate a1 - s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil); - hmac_sha2_256(seed, nseed, key, nkey, ai, s); + s = x(label, nlabel, key, nkey, nil, nil); + x(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; + s = x(ai, xlen, key, nkey, nil, nil); + s = x(label, nlabel, key, nkey, nil, s); + x(seed, nseed, key, nkey, tmp, s); + n = xlen; if(n > nbuf) n = nbuf; memmove(buf, tmp, n); buf += n; nbuf -= n; - hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA2_256dlen); + x(ai, xlen, key, nkey, tmp, nil); + memmove(ai, tmp, xlen); } } // fill buf with md5(args)^sha1(args) static void -tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { int nlabel = strlen(label); int n = (nkey + 1) >> 1; - memset(buf, 0, nbuf); - 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); + tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed, + hmac_md5, MD5dlen); + tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed, + hmac_sha1, SHA1dlen); } static void -tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { - 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); + tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed, + hmac_sha2_256, SHA2_256dlen); } static void -sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; DigestState *s; @@ -2435,8 +2423,7 @@ tmp[i] = 'A' - 1 + len; s = sha1(tmp, len, nil, nil); s = sha1(key, nkey, nil, s); - s = sha1(seed0, nseed0, nil, s); - sha1(seed1, nseed1, sha1dig, s); + sha1(seed, nseed, sha1dig, s); s = md5(key, nkey, nil, nil); md5(sha1dig, SHA1dlen, md5dig, s); n = MD5dlen; @@ -2486,18 +2473,18 @@ static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) { - uchar h0[MD5dlen], h1[SHA1dlen]; + uchar h[MD5dlen+SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in - md5(nil, 0, h0, &hsh.md5); - sha1(nil, 0, h1, &hsh.sha1); + md5(nil, 0, h, &hsh.md5); + sha1(nil, 0, h+MD5dlen, &hsh.sha1); if(isclient) label = "client finished"; else label = "server finished"; - tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); + tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); } static void @@ -2513,7 +2500,7 @@ label = "client finished"; else label = "server finished"; - p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen); + tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); } static void @@ -2523,8 +2510,9 @@ sec->clientVers = cvers; memmove(sec->crandom, crandom, RandomSize); - put32(sec->srandom, time(nil)); - genrandom(sec->srandom+4, RandomSize-4); + // putting time()'s output to the first 4 bytes is no + // longer recommended and is not useful + genrandom(sec->srandom, RandomSize); } static int @@ -2549,61 +2537,88 @@ } static Bytes* -tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc) +tlsSecECDHEs1(TlsSec *sec) { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; Bytes *par; int n; - ecdominit(dom, nc->init); - memset(Q, 0, sizeof(*Q)); - Q->x = mpnew(0); - Q->y = mpnew(0); - Q->d = mpnew(0); - ecgen(dom, Q); - n = 1 + 2*((mpsignif(dom->p)+7)/8); - par = newbytes(1+2+1+n); - par->data[0] = 3; - put16(par->data+1, nc->tlsid); - n = ecencodepub(dom, Q, par->data+4, par->len-4); - par->data[3] = n; - par->len = 1+2+1+n; - + if(sec->nc == nil) + return nil; + if(sec->nc->tlsid == X25519){ + par = newbytes(1+2+1+32); + par->data[0] = 3; + put16(par->data+1, X25519); + par->data[3] = 32; + curve25519_dh_new(sec->X, par->data+4); + }else{ + ecdominit(dom, sec->nc->init); + memset(Q, 0, sizeof(*Q)); + Q->x = mpnew(0); + Q->y = mpnew(0); + Q->d = mpnew(0); + ecgen(dom, Q); + n = 1 + 2*((mpsignif(dom->p)+7)/8); + par = newbytes(1+2+1+n); + par->data[0] = 3; + put16(par->data+1, sec->nc->tlsid); + n = ecencodepub(dom, Q, par->data+4, par->len-4); + par->data[3] = n; + par->len = 1+2+1+n; + } return par; } static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc) { + static char zero[32] = {0}; ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; ECpoint K; ECpub *Y; + Bytes *Z; if(Yc == nil){ werrstr("no public key"); return -1; } - if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ - werrstr("bad public key"); - return -1; - } - - memset(&K, 0, sizeof(K)); - K.x = mpnew(0); - K.y = mpnew(0); + if(sec->nc->tlsid == X25519){ + if(Yc->len != 32){ + werrstr("bad public key"); + return -1; + } + Z = newbytes(32); + curve25519_dh_finish(sec->X, Yc->data, Z->data); + // rfc wants us to terminate the connection if + // shared secret == all zeroes. + if(tsmemcmp(Z->data, zero, Z->len) == 0){ + werrstr("unlucky shared key"); + freebytes(Z); + return -1; + } + setMasterSecret(sec, Z); + }else{ + if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ + werrstr("bad public key"); + return -1; + } + + memset(&K, 0, sizeof(K)); + K.x = mpnew(0); + K.y = mpnew(0); - ecmul(dom, Y, Q->d, &K); + ecmul(dom, Y, Q->d, &K); - setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); + setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); - mpfree(K.x); - mpfree(K.y); - - ecpubfree(Y); + mpfree(K.x); + mpfree(K.y); + ecpubfree(Y); + } return 0; } @@ -2612,8 +2627,8 @@ { memset(sec, 0, sizeof(*sec)); sec->clientVers = cvers; - put32(sec->crandom, time(nil)); - genrandom(sec->crandom+4, RandomSize-4); + // see the comment on tlsSecInits + genrandom(sec->crandom, RandomSize); } static Bytes* @@ -2671,13 +2686,15 @@ static int setSecrets(TlsConnection *c, int isclient) { - uchar kd[MaxKeyData]; + uchar kd[MaxKeyData], seed[2*RandomSize]; char *secrets; int rv; assert(c->nsecret <= sizeof(kd)); secrets = emalloc(2*c->nsecret); + memmove(seed, c->sec->srandom, RandomSize); + memmove(seed+RandomSize, c->sec->crandom, RandomSize); /* * generate secret keys from the master secret. * @@ -2686,7 +2703,7 @@ * but it's all generated using the same function. */ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion", - c->sec->srandom, RandomSize, c->sec->crandom, RandomSize); + seed, sizeof(seed)); enc64(secrets, 2*c->nsecret, kd, c->nsecret); memset(kd, 0, c->nsecret); @@ -2705,6 +2722,8 @@ static void setMasterSecret(TlsSec *sec, Bytes *pm) { + uchar seed[2*RandomSize]; + if(sec->psklen > 0){ Bytes *opm = pm; uchar *p; @@ -2721,8 +2740,10 @@ freebytes(opm); } + memmove(seed, sec->crandom, RandomSize); + memmove(seed+RandomSize, sec->srandom, RandomSize); (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret", - sec->crandom, RandomSize, sec->srandom, RandomSize); + seed, sizeof(seed)); memset(pm->data, 0, pm->len); freebytes(pm); ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-18 18:51 ` kemal @ 2021-06-18 19:13 ` kemal 2021-06-19 14:20 ` cinap_lenrek 2021-06-20 14:56 ` cinap_lenrek 1 sibling, 1 reply; 12+ messages in thread From: kemal @ 2021-06-18 19:13 UTC (permalink / raw) To: 9front [-- Attachment #1: Type: text/plain, Size: 82 bytes --] fuck, somehow last 2 commits got into my git/export? here's an unfucked patchset: [-- Attachment #2: patchset.txt --] [-- Type: text/plain, Size: 25691 bytes --] From dfc20b135ecaac3d0af1a369197264cb680692e9 From: kemal <kemalinanc8@gmail.com> Date: Fri, 18 Jun 2021 19:12:44 +0000 Subject: [PATCH] libsec: various changes to tls 1. add the curve x25519 to tls, both client and server. it's more faster, immune to timing attacks by design, does not require verifying if the public key is valid, etc etc. server-side has to check if the client supports the curve, so a new function has been introduced to parse the client's extensions. 2. reject weak dhe primes that can be easily cracked with the number field sieve algorithm. this avoids attacks like logjam. 3. stop putting unix time to the first 4 bytes of client/ server random. it can allow fingerprinting, tls 1.3 doesn't recommend it any more and there was a draft to deprecate this behaviour earlier.[1] 4. simply prf code, remove useless cipher enums. [1] https://datatracker.ietf.org/doc/html/draft-mathewson-no-gmtunixtime-00 --- diff b3215c807556cdaaa0f949f1fb3f97c484ffd1da dfc20b135ecaac3d0af1a369197264cb680692e9 --- a/sys/src/libsec/port/tlshand.c Fri Jun 18 00:12:26 2021 +++ b/sys/src/libsec/port/tlshand.c Fri Jun 18 22:12:44 2021 @@ -66,18 +66,20 @@ int psklen; int clientVers; // version in ClientHello uchar sec[MasterSecretSize]; // master secret - uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random + uchar crandom[RandomSize]; // client random + Namedcurve *nc; // selected curve for ECDHE // diffie hellman state DHstate dh; struct { ECdomain dom; ECpriv Q; } ec; + uchar X[32]; // byte generation and handshake checksum - void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); + void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int); void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); int nfin; }; @@ -107,7 +109,7 @@ int tag; union { struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; Ints* ciphers; @@ -115,7 +117,7 @@ Bytes* extensions; } clientHello; struct { - int version; + int version; uchar random[RandomSize]; Bytes* sid; int cipher; @@ -214,79 +216,36 @@ // cipher suites enum { - TLS_NULL_WITH_NULL_NULL = 0x0000, - TLS_RSA_WITH_NULL_MD5 = 0x0001, - TLS_RSA_WITH_NULL_SHA = 0x0002, - TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, - TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, - TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, - TLS_RSA_WITH_DES_CBC_SHA = 0X0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, - TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, - TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, - TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, - TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, - TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, - TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, - TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, - TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, - TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, // aes, aka rijndael with 128 bit blocks - TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, - TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, + + TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F, TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, - TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, - TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, - TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, - 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_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067, TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, - TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, - TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA, + TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB, TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, @@ -300,6 +259,20 @@ CompressionMax }; + +// curves +enum { + X25519 = 0x001d, +}; + +// extensions +enum { + Extsni = 0x0000, + Extec = 0x000a, + Extecp = 0x000b, + Extsigalgs = 0x000d, +}; + static Algs cipherAlgs[] = { // ECDHE-ECDSA {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}, @@ -343,6 +316,7 @@ }; static Namedcurve namedcurves[] = { + X25519, nil, 0x0017, secp256r1, 0x0018, secp384r1, }; @@ -402,7 +376,7 @@ static int isECDSA(int tlsid); static int setAlgs(TlsConnection *c, int a); -static int okCipher(Ints *cv, int ispsk); +static int okCipher(Ints *cv, int ispsk, int canec); static int okCompression(Bytes *cv); static int initCiphers(void); static Ints* makeciphers(int ispsk); @@ -413,7 +387,7 @@ static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom); static int tlsSecRSAs(TlsSec *sec, Bytes *epm); -static Bytes* tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc); +static Bytes* tlsSecECDHEs1(TlsSec *sec); static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc); static void tlsSecInitc(TlsSec *sec, int cvers); static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert); @@ -454,6 +428,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; @@ -498,13 +473,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -526,7 +503,7 @@ b = erealloc(b, m + 2+2+2+1+2+n); p = b + m; - put16(p, 0), p += 2; /* Type: server_name */ + put16(p, Extsni), p += 2; /* Type: server_name */ put16(p, 2+1+2+n), p += 2; /* Length */ put16(p, 1+2+n), p += 2; /* Server Name list length */ *p++ = 0; /* Server Name Type: host_name */ @@ -535,26 +512,26 @@ p += n; } - // ECDHE + // Elliptic Curves (also called Supported Groups) if(ProtocolVersion >= TLS10Version){ m = p - b; b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); p = b + m; n = nelem(namedcurves); - put16(p, 0x000a), p += 2; /* Type: elliptic_curves */ + put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ put16(p, (n+1)*2), p += 2; /* Length */ put16(p, n*2), p += 2; /* Elliptic Curves Length */ - for(i=0; i < n; i++){ /* Elliptic curves */ + 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, Extecp), p += 2; /* Type: ec_point_formats */ put16(p, n+1), p += 2; /* Length */ *p++ = n; /* EC point formats Length */ - for(i=0; i < n; i++) /* Elliptic curves point formats */ + for(i=0; i < n; i++) /* EC point formats */ *p++ = pointformats[i]; } @@ -566,7 +543,7 @@ b = erealloc(b, m + 2+2+2+n*2); p = b + m; - put16(p, 0x000d), p += 2; + put16(p, Extsigalgs), p += 2; put16(p, n*2 + 2), p += 2; put16(p, n*2), p += 2; for(i=0; i < n; i++){ @@ -586,6 +563,7 @@ { char buf[8]; char dname[64]; + uchar seed[2*RandomSize]; int n, data, ctl, hand; TlsConnection *tls; uchar *ext; @@ -641,13 +619,15 @@ conn->sessionID = nil; if(conn->sessionKey != nil && conn->sessionType != nil - && strcmp(conn->sessionType, "ttls") == 0) + && strcmp(conn->sessionType, "ttls") == 0){ + memmove(seed, tls->sec->crandom, RandomSize); + memmove(seed+RandomSize, tls->sec->srandom, RandomSize); tls->sec->prf( conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, - tls->sec->crandom, RandomSize, - tls->sec->srandom, RandomSize); + seed, sizeof(seed)); + } tlsConnectionFree(tls); close(fd); return data; @@ -665,6 +645,53 @@ return i; } +static int +checkClientExtensions(TlsConnection *c, Bytes *ext) +{ + uchar *p, *e; + int i, j, n; + + p = ext->data; + e = p+ext->len; + while(p < e){ + if(e-p < 2) + goto Short; + switch(get16(p)){ + case Extec: + p += 2; + n = get16(p); + if(e-p < n || n < 2) + goto Short; + p += 2; + n = get16(p); + p += 2; + if(e-p < n || n & 1 || n == 0) + goto Short; + for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++) + for(j = 0; j < n; j += 2) + if(namedcurves[i].tlsid == get16(p+j)){ + c->sec->nc = &namedcurves[i]; + break; + } + p += n; + break; + default: + p += 2; + n = get16(p); + p += 2; + if(e-p < n) + goto Short; + p += n; + break; + } + } + + return 0; +Short: + tlsError(c, EDecodeError, "clienthello extensions has invalid length"); + return -1; +} + static TlsConnection * tlsServer2(int ctl, int hand, uchar *cert, int certlen, @@ -708,19 +735,6 @@ tlsError(c, EInappropriateFallback, "inappropriate fallback"); goto Err; } - cipher = okCipher(m.u.clientHello.ciphers, psklen > 0); - if(cipher < 0 || !setAlgs(c, cipher)) { - tlsError(c, EHandshakeFailure, "no matching cipher suite"); - goto Err; - } - compressor = okCompression(m.u.clientHello.compressors); - if(compressor < 0) { - tlsError(c, EHandshakeFailure, "no matching compressor"); - goto Err; - } - if(trace) - trace(" cipher %x, compressor %x\n", cipher, compressor); - tlsSecInits(c->sec, m.u.clientHello.version, m.u.clientHello.random); tlsSecVers(c->sec, c->version); if(psklen > 0){ @@ -740,6 +754,20 @@ goto Err; } } + if(checkClientExtensions(c, m.u.clientHello.extensions) < 0) + goto Err; + cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil); + if(cipher < 0 || !setAlgs(c, cipher)) { + tlsError(c, EHandshakeFailure, "no matching cipher suite"); + goto Err; + } + compressor = okCompression(m.u.clientHello.compressors); + if(compressor < 0) { + tlsError(c, EHandshakeFailure, "no matching compressor"); + goto Err; + } + if(trace) + trace(" cipher %x, compressor %x\n", cipher, compressor); msgClear(&m); m.tag = HServerHello; @@ -764,11 +792,9 @@ } if(isECDHE(cipher)){ - Namedcurve *nc = &namedcurves[0]; /* secp256r1 */ - m.tag = HServerKeyExchange; - m.u.serverKeyExchange.curve = nc->tlsid; - m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec, nc); + m.u.serverKeyExchange.curve = c->sec->nc->tlsid; + m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec); if(m.u.serverKeyExchange.dh_parameters == nil){ tlsError(c, EInternalError, "can't set DH parameters"); goto Err; @@ -889,7 +915,9 @@ if(p == nil || g == nil || Ys == nil) return nil; - + // reject dh primes that is susceptible to logjam + if(p->len <= 1024/8) + return nil; Yc = nil; P = bytestomp(p); G = bytestomp(g); @@ -920,49 +948,65 @@ static Bytes* tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys) { + static char zero[32] = {0}; ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; - Namedcurve *nc; ECpub *pub; ECpoint K; + Namedcurve *nc; Bytes *Yc; + Bytes *Z; int n; if(Ys == nil) return nil; - for(nc = namedcurves; nc != &namedcurves[nelem(namedcurves)]; nc++) - if(nc->tlsid == curve) - goto Found; - return nil; - -Found: - ecdominit(dom, nc->init); - pub = ecdecodepub(dom, Ys->data, Ys->len); - if(pub == nil) - return nil; - 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); - - ecgen(dom, Q); - ecmul(dom, pub, Q->d, &K); - - n = (mpsignif(dom->p)+7)/8; - setMasterSecret(sec, mptobytes(K.x, n)); - Yc = newbytes(1 + 2*n); - Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); + if(curve == X25519){ + if(Ys->len != 32) + return nil; + Yc = newbytes(32); + curve25519_dh_new(sec->X, Yc->data); + Z = newbytes(32); + curve25519_dh_finish(sec->X, Ys->data, Z->data); + // rfc wants us to terminate the connection if + // shared secret == all zeroes. + if(tsmemcmp(Z->data, zero, Z->len) == 0){ + freebytes(Yc); + freebytes(Z); + return nil; + } + setMasterSecret(sec, Z); + }else{ + for(nc = namedcurves; nc->tlsid != curve; nc++) + if(nc == &namedcurves[nelem(namedcurves)]) + return nil; + ecdominit(dom, nc->init); + pub = ecdecodepub(dom, Ys->data, Ys->len); + if(pub == nil) + return nil; - mpfree(K.x); - mpfree(K.y); + 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); + + ecgen(dom, Q); + ecmul(dom, pub, Q->d, &K); + + n = (mpsignif(dom->p)+7)/8; + setMasterSecret(sec, mptobytes(K.x, n)); + Yc = newbytes(1 + 2*n); + Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len); - ecpubfree(pub); + mpfree(K.x); + mpfree(K.y); + ecpubfree(pub); + } return Yc; } @@ -1045,7 +1089,6 @@ tlsError(c, EIllegalParameter, "invalid compression"); goto Err; } - dhx = isDHE(cipher) || isECDHE(cipher); if(!msgRecv(c, &m)) goto Err; @@ -2136,13 +2179,17 @@ } static int -okCipher(Ints *cv, int ispsk) +okCipher(Ints *cv, int ispsk, int canec) { int i, c; for(i = 0; i < nelem(cipherAlgs); i++) { c = cipherAlgs[i].tlsid; - if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c) || isPSK(c) != ispsk) + if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c)) + continue; + if(isPSK(c) != ispsk) + continue; + if(isECDHE(c) && !canec) continue; if(lookupid(cv, c) >= 0) return c; @@ -2313,114 +2360,55 @@ } static void -tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[MD5dlen], tmp[MD5dlen]; - int i, n; - MD5state *s; - - // generate a1 - s = hmac_md5(label, nlabel, key, nkey, nil, nil); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); - s = hmac_md5(label, nlabel, key, nkey, nil, s); - s = hmac_md5(seed0, nseed0, key, nkey, nil, s); - hmac_md5(seed1, nseed1, key, nkey, tmp, s); - n = MD5dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); - memmove(ai, tmp, MD5dlen); - } -} - -static void -tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) -{ - uchar ai[SHA1dlen], tmp[SHA1dlen]; - int i, n; - SHAstate *s; - - // generate a1 - s = hmac_sha1(label, nlabel, key, nkey, nil, nil); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, ai, s); - - while(nbuf > 0) { - s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); - s = hmac_sha1(label, nlabel, key, nkey, nil, s); - s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); - hmac_sha1(seed1, nseed1, key, nkey, tmp, s); - n = SHA1dlen; - if(n > nbuf) - n = nbuf; - for(i = 0; i < n; i++) - buf[i] ^= tmp[i]; - buf += n; - nbuf -= n; - hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA1dlen); - } -} - -static void -p_sha256(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed) +tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed, + DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen) { uchar ai[SHA2_256dlen], tmp[SHA2_256dlen]; - SHAstate *s; + DigestState *s; int n; + assert(sizeof(ai) <= xlen && sizeof(tmp) <= xlen); // generate a1 - s = hmac_sha2_256(label, nlabel, key, nkey, nil, nil); - hmac_sha2_256(seed, nseed, key, nkey, ai, s); + s = x(label, nlabel, key, nkey, nil, nil); + x(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; + s = x(ai, xlen, key, nkey, nil, nil); + s = x(label, nlabel, key, nkey, nil, s); + x(seed, nseed, key, nkey, tmp, s); + n = xlen; if(n > nbuf) n = nbuf; memmove(buf, tmp, n); buf += n; nbuf -= n; - hmac_sha2_256(ai, SHA2_256dlen, key, nkey, tmp, nil); - memmove(ai, tmp, SHA2_256dlen); + x(ai, xlen, key, nkey, tmp, nil); + memmove(ai, tmp, xlen); } } // fill buf with md5(args)^sha1(args) static void -tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { int nlabel = strlen(label); int n = (nkey + 1) >> 1; - memset(buf, 0, nbuf); - 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); + tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed, + hmac_md5, MD5dlen); + tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed, + hmac_sha1, SHA1dlen); } static void -tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { - 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); + tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed, + hmac_sha2_256, SHA2_256dlen); } static void -sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) { uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; DigestState *s; @@ -2435,8 +2423,7 @@ tmp[i] = 'A' - 1 + len; s = sha1(tmp, len, nil, nil); s = sha1(key, nkey, nil, s); - s = sha1(seed0, nseed0, nil, s); - sha1(seed1, nseed1, sha1dig, s); + sha1(seed, nseed, sha1dig, s); s = md5(key, nkey, nil, nil); md5(sha1dig, SHA1dlen, md5dig, s); n = MD5dlen; @@ -2486,18 +2473,18 @@ static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) { - uchar h0[MD5dlen], h1[SHA1dlen]; + uchar h[MD5dlen+SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in - md5(nil, 0, h0, &hsh.md5); - sha1(nil, 0, h1, &hsh.sha1); + md5(nil, 0, h, &hsh.md5); + sha1(nil, 0, h+MD5dlen, &hsh.sha1); if(isclient) label = "client finished"; else label = "server finished"; - tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); + tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); } static void @@ -2513,7 +2500,7 @@ label = "client finished"; else label = "server finished"; - p_sha256(finished, TLSFinishedLen, sec->sec, MasterSecretSize, (uchar*)label, strlen(label), seed, SHA2_256dlen); + tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); } static void @@ -2523,8 +2510,9 @@ sec->clientVers = cvers; memmove(sec->crandom, crandom, RandomSize); - put32(sec->srandom, time(nil)); - genrandom(sec->srandom+4, RandomSize-4); + // putting time()'s output to the first 4 bytes is no + // longer recommended and is not useful + genrandom(sec->srandom, RandomSize); } static int @@ -2549,61 +2537,88 @@ } static Bytes* -tlsSecECDHEs1(TlsSec *sec, Namedcurve *nc) +tlsSecECDHEs1(TlsSec *sec) { ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; Bytes *par; int n; - ecdominit(dom, nc->init); - memset(Q, 0, sizeof(*Q)); - Q->x = mpnew(0); - Q->y = mpnew(0); - Q->d = mpnew(0); - ecgen(dom, Q); - n = 1 + 2*((mpsignif(dom->p)+7)/8); - par = newbytes(1+2+1+n); - par->data[0] = 3; - put16(par->data+1, nc->tlsid); - n = ecencodepub(dom, Q, par->data+4, par->len-4); - par->data[3] = n; - par->len = 1+2+1+n; - + if(sec->nc == nil) + return nil; + if(sec->nc->tlsid == X25519){ + par = newbytes(1+2+1+32); + par->data[0] = 3; + put16(par->data+1, X25519); + par->data[3] = 32; + curve25519_dh_new(sec->X, par->data+4); + }else{ + ecdominit(dom, sec->nc->init); + memset(Q, 0, sizeof(*Q)); + Q->x = mpnew(0); + Q->y = mpnew(0); + Q->d = mpnew(0); + ecgen(dom, Q); + n = 1 + 2*((mpsignif(dom->p)+7)/8); + par = newbytes(1+2+1+n); + par->data[0] = 3; + put16(par->data+1, sec->nc->tlsid); + n = ecencodepub(dom, Q, par->data+4, par->len-4); + par->data[3] = n; + par->len = 1+2+1+n; + } return par; } static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc) { + static char zero[32] = {0}; ECdomain *dom = &sec->ec.dom; ECpriv *Q = &sec->ec.Q; ECpoint K; ECpub *Y; + Bytes *Z; if(Yc == nil){ werrstr("no public key"); return -1; } - if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ - werrstr("bad public key"); - return -1; - } - - memset(&K, 0, sizeof(K)); - K.x = mpnew(0); - K.y = mpnew(0); + if(sec->nc->tlsid == X25519){ + if(Yc->len != 32){ + werrstr("bad public key"); + return -1; + } + Z = newbytes(32); + curve25519_dh_finish(sec->X, Yc->data, Z->data); + // rfc wants us to terminate the connection if + // shared secret == all zeroes. + if(tsmemcmp(Z->data, zero, Z->len) == 0){ + werrstr("unlucky shared key"); + freebytes(Z); + return -1; + } + setMasterSecret(sec, Z); + }else{ + if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){ + werrstr("bad public key"); + return -1; + } + + memset(&K, 0, sizeof(K)); + K.x = mpnew(0); + K.y = mpnew(0); - ecmul(dom, Y, Q->d, &K); + ecmul(dom, Y, Q->d, &K); - setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); + setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8)); - mpfree(K.x); - mpfree(K.y); - - ecpubfree(Y); + mpfree(K.x); + mpfree(K.y); + ecpubfree(Y); + } return 0; } @@ -2612,8 +2627,8 @@ { memset(sec, 0, sizeof(*sec)); sec->clientVers = cvers; - put32(sec->crandom, time(nil)); - genrandom(sec->crandom+4, RandomSize-4); + // see the comment on tlsSecInits + genrandom(sec->crandom, RandomSize); } static Bytes* @@ -2671,13 +2686,15 @@ static int setSecrets(TlsConnection *c, int isclient) { - uchar kd[MaxKeyData]; + uchar kd[MaxKeyData], seed[2*RandomSize]; char *secrets; int rv; assert(c->nsecret <= sizeof(kd)); secrets = emalloc(2*c->nsecret); + memmove(seed, c->sec->srandom, RandomSize); + memmove(seed+RandomSize, c->sec->crandom, RandomSize); /* * generate secret keys from the master secret. * @@ -2686,7 +2703,7 @@ * but it's all generated using the same function. */ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion", - c->sec->srandom, RandomSize, c->sec->crandom, RandomSize); + seed, sizeof(seed)); enc64(secrets, 2*c->nsecret, kd, c->nsecret); memset(kd, 0, c->nsecret); @@ -2705,6 +2722,8 @@ static void setMasterSecret(TlsSec *sec, Bytes *pm) { + uchar seed[2*RandomSize]; + if(sec->psklen > 0){ Bytes *opm = pm; uchar *p; @@ -2721,8 +2740,10 @@ freebytes(opm); } + memmove(seed, sec->crandom, RandomSize); + memmove(seed+RandomSize, sec->srandom, RandomSize); (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret", - sec->crandom, RandomSize, sec->srandom, RandomSize); + seed, sizeof(seed)); memset(pm->data, 0, pm->len); freebytes(pm); ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-18 19:13 ` kemal @ 2021-06-19 14:20 ` cinap_lenrek 0 siblings, 0 replies; 12+ messages in thread From: cinap_lenrek @ 2021-06-19 14:20 UTC (permalink / raw) To: 9front applied! thanks! -- cinap ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [9front] libsec: various tls changes 2021-06-18 18:51 ` kemal 2021-06-18 19:13 ` kemal @ 2021-06-20 14:56 ` cinap_lenrek 1 sibling, 0 replies; 12+ messages in thread From: cinap_lenrek @ 2021-06-20 14:56 UTC (permalink / raw) To: 9front > also, after a bit of diving in the rfcs i found out that > the x25519 rfc specifies an optional step that if the > shared secret turned out to be all zeroes, abort. > rfc 8422 (tls ecc rfc) says that this MUST be done. yeah, good catch. this is also what boringssl does. > i was unsure if i should have added that check to > curve25519_finish, as it would break source compatibility, > so i added it to tlsSecECDHE*. yeah, but this function is used rarely (only ssh and tlshand right now) so it is not a big deal to change the return value from void to int and fix the callers. i think it is better todo it internally, especially as the caller might forget todo the cehck in a timing-safe way. pushed a fix for both plan9front and drawterm. -- cinap ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2021-06-20 22:48 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-06-11 15:06 [9front] libsec: various tls changes kemal 2021-06-12 13:15 ` cinap_lenrek 2021-06-12 19:54 ` kemal 2021-06-14 18:26 ` cinap_lenrek 2021-06-14 20:27 ` kemal 2021-06-16 9:48 ` cinap_lenrek 2021-06-16 11:59 ` kemal 2021-06-16 14:02 ` cinap_lenrek 2021-06-18 18:51 ` kemal 2021-06-18 19:13 ` kemal 2021-06-19 14:20 ` cinap_lenrek 2021-06-20 14:56 ` cinap_lenrek
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).