diff e54b6c6cbd4d82d70ddb4932aeafb0b028cd71f5 uncommitted --- a//sys/man/2/pushtls +++ b//sys/man/2/pushtls @@ -1,6 +1,6 @@ .TH PUSHTLS 2 .SH NAME -pushtls, tlsClient, tlsServer, initThumbprints, freeThumbprints, okThumbprint, okCertificate, readcert, readcertchain \- attach TLS1 or SSL3 encryption to a communication channel +pushtls, tlsClient, tlsServer, initThumbprints, freeThumbprints, okThumbprint, okCertificate, readcert, readcertchain \- attach TLS encryption to a communication channel .SH SYNOPSIS .B #include .br @@ -45,8 +45,6 @@ and a handshake protocol, doing initial authentication and secret creation at user level and then starting a data channel in the record protocol. -TLS is nearly the same as SSL 3.0, and the software should interoperate -with implementations of either standard. .PP To use just the record layer, as described in .IR tls (3), --- a//sys/man/3/tls +++ b//sys/man/3/tls @@ -1,6 +1,6 @@ .TH TLS 3 .SH NAME -tls \- TLS and SSL3 record layer +tls \- TLS record layer .SH SYNOPSIS .nf .B bind -a #a /net @@ -16,10 +16,9 @@ .BI /net/tls/ n /status .fi .SH DESCRIPTION -The TLS device implements the record layer protocols -of Transport Layer Security version 1.0-1.2 and Secure Sockets Layer version 3.0. -It does not implement the handshake protocols, which are responsible for -mutual authentication and key exchange. +The TLS device implements the record layer protocols of Transport Layer Security +version 1.0-1.2. It does not implement the handshake protocols, which are responsible +for mutual authentication and key exchange. The .I tls device can be thought of as filters providing optional encryption and anti-tampering. @@ -53,13 +52,11 @@ .I vers format records, but incoming messages of either version are accepted. Valid versions are -.B 0x300 -for SSLv3.0 and .BR 0x301 , .B 0x302 and .B 0x303 -for TLSv1.0 (which could be known as SSLv3.01), TLSv1.1 and TLSv1.2. +for TLSv1.0, TLSv1.1 and TLSv1.2. This command must be issued before any other command and before reading or writing any messages; it may only be executed once. @@ -120,10 +117,9 @@ .TP .BI alert \ alertno Send an alert message. -.I Alertno -may be a valid alert code for either SSLv3.0 or TLS, -and is mapped to an appropriate code for the protocol in use. -If it is a fatal alert, the filter is set into an error state. +If +.I alertno +is a fatal alert, the filter is set into an error state. .PP Application messages and handshake messages are communicated using .I data --- a//sys/src/libsec/port/tlshand.c +++ b//sys/src/libsec/port/tlshand.c @@ -8,14 +8,13 @@ // client/server - main handshake protocol definition // message functions - formating handshake messages // cipher choices - catalog of digest and encrypt algorithms -// security functions - PKCS#1, sslHMAC, session keygen +// security functions - PKCS#1, session keygen // general utility functions - malloc, serialization -// The handshake protocol builds on the TLS/SSL3 record layer protocol, -// which is implemented in kernel device #a. See also /lib/rfc/rfc2246. +// The handshake protocol builds on the TLS record layer protocol, +// which is implemented in kernel device #a. See also /lib/rfc/rfc5246. enum { - TLSFinishedLen = 12, - SSL3FinishedLen = MD5dlen+SHA1dlen, + FinishedLen = 12, MaxKeyData = 160, // amount of secret we may need MAXdlen = SHA2_512dlen, RandomSize = 32, @@ -47,11 +46,6 @@ void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h); } Namedcurve; -typedef struct Finished{ - uchar verify[SSL3FinishedLen]; - int n; -} Finished; - typedef struct HandshakeHash { MD5state md5; SHAstate sha1; @@ -81,7 +75,6 @@ // byte generation and handshake checksum void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int); void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int); - int nfin; }; typedef struct TlsConnection{ @@ -99,7 +92,7 @@ // for finished messages HandshakeHash handhash; - Finished finished; + uchar finished[FinishedLen]; uchar *sendp; uchar buf[1<<16]; @@ -152,18 +145,17 @@ int sigalg; Bytes *signature; } certificateVerify; - Finished finished; + uchar finished[FinishedLen]; } u; } Msg; enum { - SSL3Version = 0x0300, TLS10Version = 0x0301, TLS11Version = 0x0302, TLS12Version = 0x0303, ProtocolVersion = TLS12Version, // maximum version we speak - MinProtoVersion = 0x0300, // limits on version we accept + MinProtoVersion = 0x0301, // limits on version we accept MaxProtoVersion = 0x03ff, }; @@ -172,7 +164,6 @@ HHelloRequest, HClientHello, HServerHello, - HSSL2ClientHello = 9, /* local convention; see devtls.c */ HCertificate = 11, HServerKeyExchange, HCertificateRequest, @@ -192,7 +183,6 @@ ERecordOverflow = 22, EDecompressionFailure = 30, EHandshakeFailure = 40, - ENoCertificate = 41, EBadCertificate = 42, EUnsupportedCertificate = 43, ECertificateRevoked = 44, @@ -367,7 +357,6 @@ #pragma varargck argpos tlsError 3 static int setVersion(TlsConnection *c, int version); static int setSecrets(TlsConnection *c, int isclient); -static int finishedMatch(TlsConnection *c, Finished *f); static void tlsConnectionFree(TlsConnection *c); static int isDHE(int tlsid); @@ -394,7 +383,6 @@ static Bytes* tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys); static Bytes* tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys); static void tlsSecVers(TlsSec *sec, int v); -static int tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient); static void setMasterSecret(TlsSec *sec, Bytes *pm); static int digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg); static char* verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg); @@ -511,28 +499,26 @@ } // 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; + m = p - b; + b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats)); + p = b + m; - n = nelem(namedcurves); - put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ - put16(p, (n+1)*2), p += 2; /* Length */ - put16(p, n*2), p += 2; /* Elliptic Curves Length */ - for(i=0; i < n; i++){ /* Elliptic Curves */ - put16(p, namedcurves[i].tlsid); - p += 2; - } - - n = nelem(pointformats); - put16(p, Extecp), p += 2; /* Type: ec_point_formats */ - put16(p, n+1), p += 2; /* Length */ - *p++ = n; /* EC point formats Length */ - for(i=0; i < n; i++) /* EC point formats */ - *p++ = pointformats[i]; + n = nelem(namedcurves); + put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */ + put16(p, (n+1)*2), p += 2; /* Length */ + put16(p, n*2), p += 2; /* Elliptic Curves Length */ + for(i=0; i < n; i++){ /* Elliptic Curves */ + put16(p, namedcurves[i].tlsid); + p += 2; } + n = nelem(pointformats); + put16(p, Extecp), p += 2; /* Type: ec_point_formats */ + put16(p, n+1), p += 2; /* Length */ + *p++ = n; /* EC point formats Length */ + for(i=0; i < n; i++) /* EC point formats */ + *p++ = pointformats[i]; + // signature algorithms if(ProtocolVersion >= TLS12Version){ n = nelem(sigalgs); @@ -849,10 +835,7 @@ } /* no CertificateVerify; skip to Finished */ - if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){ - tlsError(c, EInternalError, "can't set finished: %r"); - goto Err; - } + (*c->sec->setFinished)(c->sec, c->handhash, c->finished, 1); if(!msgRecv(c, &m)) goto Err; if(m.tag != HFinished) { @@ -859,7 +842,7 @@ tlsError(c, EUnexpectedMessage, "expected a finished"); goto Err; } - if(!finishedMatch(c, &m.u.finished)) { + if(tsmemcmp(c->finished, m.u.finished, FinishedLen) != 0) { tlsError(c, EHandshakeFailure, "finished verification failed"); goto Err; } @@ -871,12 +854,9 @@ goto Err; } - if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){ - tlsError(c, EInternalError, "can't set finished: %r"); - goto Err; - } + (*c->sec->setFinished)(c->sec, c->handhash, c->finished, 0); m.tag = HFinished; - m.u.finished = c->finished; + memmove(m.u.finished, c->finished, FinishedLen); if(!msgSend(c, &m, AFlush)) goto Err; if(trace) @@ -1220,21 +1200,15 @@ // Cipherchange must occur immediately before Finished to avoid // potential hole; see section 4.3 of Wagner Schneier 1996. - if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){ - tlsError(c, EInternalError, "can't set finished 1: %r"); - goto Err; - } + (*c->sec->setFinished)(c->sec, c->handhash, c->finished, 1); m.tag = HFinished; - m.u.finished = c->finished; + memmove(m.u.finished, c->finished, FinishedLen); if(!msgSend(c, &m, AFlush)) { tlsError(c, EInternalError, "can't flush after client Finished: %r"); goto Err; } - if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){ - tlsError(c, EInternalError, "can't set finished 0: %r"); - goto Err; - } + (*c->sec->setFinished)(c->sec, c->handhash, c->finished, 0); if(!msgRecv(c, &m)) { tlsError(c, EInternalError, "can't read server Finished: %r"); goto Err; @@ -1244,7 +1218,7 @@ goto Err; } - if(!finishedMatch(c, &m.u.finished)) { + if(tsmemcmp(c->finished, m.u.finished, FinishedLen) != 0) { tlsError(c, EHandshakeFailure, "finished verification failed"); goto Err; } @@ -1418,16 +1392,16 @@ goto Overflow; if(isECDHE(c->cipher)) *p++ = n; - else if(isDHE(c->cipher) || c->version != SSL3Version) + else if(isDHE(c->cipher)) put16(p, n), p += 2; memmove(p, m->u.clientKeyExchange.key->data, n); p += n; break; case HFinished: - if(p+m->u.finished.n > e) + if(p+FinishedLen > e) goto Overflow; - memmove(p, m->u.finished.verify, m->u.finished.n); - p += m->u.finished.n; + memmove(p, m->u.finished, FinishedLen); + p += FinishedLen; break; } @@ -1495,50 +1469,6 @@ } } - if(type == HSSL2ClientHello){ - /* Cope with an SSL3 ClientHello expressed in SSL2 record format. - This is sent by some clients that we must interoperate - with, such as Java's JSSE and Microsoft's Internet Explorer. */ - int nsid, nrandom, nciph; - - p = tlsReadN(c, n); - if(p == nil) - return 0; - msgHash(c, p, n); - m->tag = HClientHello; - if(n < 22) - goto Short; - m->u.clientHello.version = get16(p+1); - p += 3; - n -= 3; - nn = get16(p); /* cipher_spec_len */ - nsid = get16(p + 2); - nrandom = get16(p + 4); - p += 6; - n -= 6; - if(nsid != 0 /* no sid's, since shouldn't restart using ssl2 header */ - || nrandom < 16 || nn % 3 || n - nrandom < nn) - goto Err; - /* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */ - nciph = 0; - for(i = 0; i < nn; i += 3) - if(p[i] == 0) - nciph++; - m->u.clientHello.ciphers = newints(nciph); - nciph = 0; - for(i = 0; i < nn; i += 3) - if(p[i] == 0) - m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]); - p += nn; - m->u.clientHello.sid = makebytes(nil, 0); - if(nrandom > RandomSize) - nrandom = RandomSize; - memset(m->u.clientHello.random, 0, RandomSize - nrandom); - memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom); - m->u.clientHello.compressors = newbytes(1); - m->u.clientHello.compressors->data[0] = CompressionNull; - goto Ok; - } msgHash(c, p, 4); p = tlsReadN(c, n); @@ -1790,7 +1720,7 @@ goto Short; if(isECDHE(c->cipher)) nn = *p++, n--; - else if(isDHE(c->cipher) || c->version != SSL3Version) + else if(isDHE(c->cipher)) nn = get16(p), p += 2, n -= 2; else nn = n; @@ -1800,17 +1730,16 @@ n -= nn; break; case HFinished: - m->u.finished.n = c->finished.n; - if(n < m->u.finished.n) + if(n < FinishedLen) goto Short; - memmove(m->u.finished.verify, p, m->u.finished.n); - n -= m->u.finished.n; + memmove(m->u.finished, p, FinishedLen); + n -= FinishedLen; break; } if(n != 0 && type != HClientHello && type != HServerHello) goto Short; -Ok: + if(c->trace) c->trace("recv %s", msgPrint((char*)c->sendp, &c->buf[sizeof(c->buf)] - c->sendp, m)); return 1; @@ -2000,8 +1929,8 @@ break; case HFinished: bs = seprint(bs, be, "HFinished\n"); - for(i=0; iu.finished.n; i++) - bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]); + for(i=0; iu.finished[i]); bs = seprint(bs, be, "\n"); break; } @@ -2036,23 +1965,11 @@ return -1; if(version > c->version) version = c->version; - if(version == SSL3Version) { + else c->version = version; - c->finished.n = SSL3FinishedLen; - }else { - c->version = version; - c->finished.n = TLSFinishedLen; - } return fprint(c->ctl, "version 0x%x", version); } -// confirm that received Finished message matches the expected value -static int -finishedMatch(TlsConnection *c, Finished *f) -{ - return tsmemcmp(f->verify, c->finished.verify, f->n) == 0; -} - // free memory associated with TlsConnection struct // (but don't close the TLS channel itself) static void @@ -2390,68 +2307,6 @@ hmac_sha2_256, SHA2_256dlen); } -static void -sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed) -{ - uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; - DigestState *s; - int i, n, len; - - USED(label); - len = 1; - while(nbuf > 0){ - if(len > 26) - return; - for(i = 0; i < len; i++) - tmp[i] = 'A' - 1 + len; - s = sha1(tmp, len, nil, nil); - s = sha1(key, nkey, nil, s); - sha1(seed, nseed, sha1dig, s); - s = md5(key, nkey, nil, nil); - md5(sha1dig, SHA1dlen, md5dig, s); - n = MD5dlen; - if(n > nbuf) - n = nbuf; - memmove(buf, md5dig, n); - buf += n; - nbuf -= n; - len++; - } -} - -static void -sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) -{ - DigestState *s; - uchar h0[MD5dlen], h1[SHA1dlen], pad[48]; - char *label; - - if(isclient) - label = "CLNT"; - else - label = "SRVR"; - - md5((uchar*)label, 4, nil, &hsh.md5); - md5(sec->sec, MasterSecretSize, nil, &hsh.md5); - memset(pad, 0x36, 48); - md5(pad, 48, nil, &hsh.md5); - md5(nil, 0, h0, &hsh.md5); - memset(pad, 0x5C, 48); - s = md5(sec->sec, MasterSecretSize, nil, nil); - s = md5(pad, 48, nil, s); - md5(h0, MD5dlen, finished, s); - - sha1((uchar*)label, 4, nil, &hsh.sha1); - sha1(sec->sec, MasterSecretSize, nil, &hsh.sha1); - memset(pad, 0x36, 40); - sha1(pad, 40, nil, &hsh.sha1); - sha1(nil, 0, h1, &hsh.sha1); - memset(pad, 0x5C, 40); - s = sha1(sec->sec, MasterSecretSize, nil, nil); - s = sha1(pad, 40, nil, s); - sha1(h1, SHA1dlen, finished + MD5dlen, s); -} - // fill "finished" arg with md5(args)^sha1(args) static void tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient) @@ -2460,6 +2315,8 @@ char *label; // get current hash value, but allow further messages to be hashed in + hsh.md5.malloced = 0; + hsh.sha1.malloced = 0; md5(nil, 0, h, &hsh.md5); sha1(nil, 0, h+MD5dlen, &hsh.sha1); @@ -2467,7 +2324,7 @@ label = "client finished"; else label = "server finished"; - tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); + tls10PRF(finished, FinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h)); } static void @@ -2477,6 +2334,7 @@ char *label; // get current hash value, but allow further messages to be hashed in + hsh.sha2_256.malloced = 0; sha2_256(nil, 0, seed, &hsh.sha2_256); if(isclient) @@ -2483,7 +2341,7 @@ label = "client finished"; else label = "server finished"; - tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); + tls12PRF(finished, FinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen); } static void @@ -2630,34 +2488,14 @@ return epm; } -static int -tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient) -{ - if(sec->nfin != nfin){ - werrstr("invalid finished exchange"); - return -1; - } - hsh.md5.malloced = 0; - hsh.sha1.malloced = 0; - hsh.sha2_256.malloced = 0; - (*sec->setFinished)(sec, hsh, fin, isclient); - return 0; -} - static void tlsSecVers(TlsSec *sec, int v) { - if(v == SSL3Version){ - sec->setFinished = sslSetFinished; - sec->nfin = SSL3FinishedLen; - sec->prf = sslPRF; - }else if(v < TLS12Version) { + if(v < TLS12Version){ sec->setFinished = tls10SetFinished; - sec->nfin = TLSFinishedLen; sec->prf = tls10PRF; - }else { + }else{ sec->setFinished = tls12SetFinished; - sec->nfin = TLSFinishedLen; sec->prf = tls12PRF; } }