summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwtc%google.com <devnull@localhost>2010-07-31 04:33:52 +0000
committerwtc%google.com <devnull@localhost>2010-07-31 04:33:52 +0000
commitd6d306a5e73b4b4925de21f76179c68b03434936 (patch)
tree39da3db5bbf67e10e8fb28e3dcdeb5c3add5b130
parent25c8423db8df53cce7467b085999902d7e3c7f42 (diff)
downloadnss-hg-d6d306a5e73b4b4925de21f76179c68b03434936.tar.gz
Bug 525092: Support TLS false start. The patch is contributed by AdamNSS_3_12_8_BETA1
Langley of Google <agl@chromium.org>. r=wtc. Modified Files: Tag: NSS_3_12_BRANCH cmd/strsclnt/strsclnt.c cmd/tstclnt/tstclnt.c lib/ssl/ssl.h lib/ssl/ssl3con.c lib/ssl/ssl3gthr.c lib/ssl/sslimpl.h lib/ssl/sslsecur.c lib/ssl/sslsock.c tests/ssl/sslstress.txt
-rw-r--r--security/nss/cmd/strsclnt/strsclnt.c14
-rw-r--r--security/nss/cmd/tstclnt/tstclnt.c15
-rw-r--r--security/nss/lib/ssl/ssl.h11
-rw-r--r--security/nss/lib/ssl/ssl3con.c22
-rw-r--r--security/nss/lib/ssl/ssl3gthr.c18
-rw-r--r--security/nss/lib/ssl/sslimpl.h3
-rw-r--r--security/nss/lib/ssl/sslsecur.c11
-rw-r--r--security/nss/lib/ssl/sslsock.c11
-rw-r--r--security/nss/tests/ssl/sslstress.txt4
9 files changed, 100 insertions, 9 deletions
diff --git a/security/nss/cmd/strsclnt/strsclnt.c b/security/nss/cmd/strsclnt/strsclnt.c
index ae4feb9d1..786b6bcf7 100644
--- a/security/nss/cmd/strsclnt/strsclnt.c
+++ b/security/nss/cmd/strsclnt/strsclnt.c
@@ -162,6 +162,7 @@ static PRBool disableLocking = PR_FALSE;
static PRBool ignoreErrors = PR_FALSE;
static PRBool enableSessionTickets = PR_FALSE;
static PRBool enableCompression = PR_FALSE;
+static PRBool enableFalseStart = PR_FALSE;
PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT;
@@ -197,7 +198,8 @@ Usage(const char *progName)
" -U means enable throttling up threads\n"
" -B bypasses the PKCS11 layer for SSL encryption and MACing\n"
" -u enable TLS Session Ticket extension\n"
- " -z enable compression\n",
+ " -z enable compression\n"
+ " -g enable false start\n",
progName);
exit(1);
}
@@ -1244,6 +1246,12 @@ client_main(
errExit("SSL_OptionSet SSL_ENABLE_DEFLATE");
}
+ if (enableFalseStart) {
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_FALSE_START, PR_TRUE);
+ if (rv != SECSuccess)
+ errExit("SSL_OptionSet SSL_ENABLE_FALSE_START");
+ }
+
SSL_SetURL(model_sock, hostName);
SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate,
@@ -1354,7 +1362,7 @@ main(int argc, char **argv)
optstate = PL_CreateOptState(argc, argv,
- "23BC:DNP:TUW:a:c:d:f:in:op:qst:uvw:z");
+ "23BC:DNP:TUW:a:c:d:f:gin:op:qst:uvw:z");
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch(optstate->option) {
@@ -1384,6 +1392,8 @@ main(int argc, char **argv)
case 'f': fileName = optstate->value; break;
+ case 'g': enableFalseStart = PR_TRUE; break;
+
case 'i': ignoreErrors = PR_TRUE; break;
case 'n': nickName = PL_strdup(optstate->value); break;
diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c
index c15a0ad47..55684e685 100644
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -225,6 +225,7 @@ static void Usage(const char *progName)
fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
fprintf(stderr, "%-20s Enable compression.\n", "-z");
+ fprintf(stderr, "%-20s Enable false start.\n", "-g");
fprintf(stderr, "%-20s Letter(s) chosen from the following list\n",
"-c ciphers");
fprintf(stderr,
@@ -521,6 +522,7 @@ int main(int argc, char **argv)
int useExportPolicy = 0;
int enableSessionTickets = 0;
int enableCompression = 0;
+ int enableFalseStart = 0;
PRSocketOptionData opt;
PRNetAddr addr;
PRPollDesc pollset[2];
@@ -551,7 +553,7 @@ int main(int argc, char **argv)
}
optstate = PL_CreateOptState(argc, argv,
- "23BSTW:a:c:d:fh:m:n:op:qr:suvw:xz");
+ "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case '?':
@@ -578,6 +580,8 @@ int main(int argc, char **argv)
case 'c': cipherString = PORT_Strdup(optstate->value); break;
+ case 'g': enableFalseStart = 1; break;
+
case 'd': certDir = PORT_Strdup(optstate->value); break;
case 'f': clientSpeaksFirst = PR_TRUE; break;
@@ -863,7 +867,14 @@ int main(int argc, char **argv)
SECU_PrintError(progName, "error enabling compression");
return 1;
}
-
+
+ /* enable false start. */
+ rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error enabling false start");
+ return 1;
+ }
+
SSL_SetPKCS11PinArg(s, &pwdata);
SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h
index 70c53f541..e294a67cf 100644
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -128,6 +128,17 @@ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd);
/* Renegotiation Info (RI) */
/* extension in ALL handshakes. */
/* default: off */
+#define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */
+ /* default, applies only to */
+ /* clients). False start is a */
+/* mode where an SSL client will start sending application data before */
+/* verifying the server's Finished message. This means that we could end up */
+/* sending data to an imposter. However, the data will be encrypted and */
+/* only the true server can derive the session key. Thus, so long as the */
+/* cipher isn't broken this is safe. Because of this, False Start will only */
+/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */
+/* bits. The advantage of False Start is that it saves a round trip for */
+/* client-speaks-first protocols when performing a full handshake. */
#ifdef SSL_DEPRECATED_FUNCTION
/* Old deprecated function names */
diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c
index 7a7bc1043..754ebadda 100644
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -5665,7 +5665,17 @@ ssl3_RestartHandshakeAfterCertReq(sslSocket * ss,
return rv;
}
-
+PRBool
+ssl3_CanFalseStart(sslSocket *ss) {
+ return ss->opt.enableFalseStart &&
+ !ss->sec.isServer &&
+ !ss->ssl3.hs.isResuming &&
+ ss->ssl3.cwSpec &&
+ ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
+ (ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
+ ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh ||
+ ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
+}
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
* ssl3 Server Hello Done message.
@@ -5737,6 +5747,12 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
ss->ssl3.hs.ws = wait_new_session_ticket;
else
ss->ssl3.hs.ws = wait_change_cipher;
+
+ /* Do the handshake callback for sslv3 here, if we can false start. */
+ if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
+ (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+ }
+
return SECSuccess;
loser:
@@ -8476,8 +8492,8 @@ xmit_loser:
}
ss->ssl3.hs.ws = idle_handshake;
- /* Do the handshake callback for sslv3 here. */
- if (ss->handshakeCallback != NULL) {
+ /* Do the handshake callback for sslv3 here, if we cannot false start. */
+ if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
}
diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c
index 4465c4359..437a9293e 100644
--- a/security/nss/lib/ssl/ssl3gthr.c
+++ b/security/nss/lib/ssl/ssl3gthr.c
@@ -188,6 +188,7 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
{
SSL3Ciphertext cText;
int rv;
+ PRBool canFalseStart = PR_FALSE;
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
do {
@@ -199,6 +200,8 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
/* decipher it, and handle it if it's a handshake.
* If it's application data, ss->gs.buf will not be empty upon return.
+ * If it's a change cipher spec, alert, or handshake message,
+ * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
*/
cText.type = (SSL3ContentType)ss->gs.hdr[0];
cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
@@ -207,7 +210,20 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
if (rv < 0) {
return ss->recvdCloseNotify ? 0 : rv;
}
- } while (ss->ssl3.hs.ws != idle_handshake && ss->gs.buf.len == 0);
+
+ /* If we kicked off a false start in ssl3_HandleServerHelloDone, break
+ * out of this loop early without finishing the handshake.
+ */
+ if (ss->opt.enableFalseStart) {
+ ssl_GetSSL3HandshakeLock(ss);
+ canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
+ ss->ssl3.hs.ws == wait_new_session_ticket) &&
+ ssl3_CanFalseStart(ss);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ }
+ } while (ss->ssl3.hs.ws != idle_handshake &&
+ !canFalseStart &&
+ ss->gs.buf.len == 0);
ss->gs.readOffset = 0;
ss->gs.writeOffset = ss->gs.buf.len;
diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h
index e21420a5c..bc0102b40 100644
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -333,6 +333,7 @@ typedef struct sslOptionsStr {
unsigned int enableDeflate : 1; /* 19 */
unsigned int enableRenegotiation : 2; /* 20-21 */
unsigned int requireSafeNegotiation : 1; /* 22 */
+ unsigned int enableFalseStart : 1; /* 23 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -1250,6 +1251,8 @@ extern void ssl_SetAlwaysBlock(sslSocket *ss);
extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
+extern PRBool ssl3_CanFalseStart(sslSocket *ss);
+
#define SSL_LOCK_READER(ss) if (ss->recvLock) PZ_Lock(ss->recvLock)
#define SSL_UNLOCK_READER(ss) if (ss->recvLock) PZ_Unlock(ss->recvLock)
#define SSL_LOCK_WRITER(ss) if (ss->sendLock) PZ_Lock(ss->sendLock)
diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c
index fc2c73dd9..3740873b0 100644
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -1199,8 +1199,17 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
ss->writerThread = PR_GetCurrentThread();
/* If any of these is non-zero, the initial handshake is not done. */
if (!ss->firstHsDone) {
+ PRBool canFalseStart = PR_FALSE;
ssl_Get1stHandshakeLock(ss);
- if (ss->handshake || ss->nextHandshake || ss->securityHandshake) {
+ if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
+ (ss->ssl3.hs.ws == wait_change_cipher ||
+ ss->ssl3.hs.ws == wait_finished ||
+ ss->ssl3.hs.ws == wait_new_session_ticket) &&
+ ssl3_CanFalseStart(ss)) {
+ canFalseStart = PR_TRUE;
+ }
+ if (!canFalseStart &&
+ (ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
rv = ssl_Do1stHandshake(ss);
}
ssl_Release1stHandshakeLock(ss);
diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
index cfb5ba455..8b04e508b 100644
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -183,6 +183,7 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* enableDeflate */
2, /* enableRenegotiation (default: requires extension) */
PR_FALSE, /* requireSafeNegotiation */
+ PR_FALSE, /* enableFalseStart */
};
sslSessionIDLookupFunc ssl_sid_lookup;
@@ -728,6 +729,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
ss->opt.requireSafeNegotiation = on;
break;
+ case SSL_ENABLE_FALSE_START:
+ ss->opt.enableFalseStart = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@@ -791,6 +796,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
on = ss->opt.enableRenegotiation; break;
case SSL_REQUIRE_SAFE_NEGOTIATION:
on = ss->opt.requireSafeNegotiation; break;
+ case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -841,6 +847,7 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
case SSL_REQUIRE_SAFE_NEGOTIATION:
on = ssl_defaults.requireSafeNegotiation;
break;
+ case SSL_ENABLE_FALSE_START: on = ssl_defaults.enableFalseStart; break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -984,6 +991,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
ssl_defaults.requireSafeNegotiation = on;
break;
+ case SSL_ENABLE_FALSE_START:
+ ssl_defaults.enableFalseStart = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
diff --git a/security/nss/tests/ssl/sslstress.txt b/security/nss/tests/ssl/sslstress.txt
index 9a3aae849..64f6b76e3 100644
--- a/security/nss/tests/ssl/sslstress.txt
+++ b/security/nss/tests/ssl/sslstress.txt
@@ -42,9 +42,11 @@
noECC 0 _ -c_1000_-C_A Stress SSL2 RC4 128 with MD5
noECC 0 _ -c_1000_-C_c_-T Stress SSL3 RC4 128 with MD5
noECC 0 _ -c_1000_-C_c Stress TLS RC4 128 with MD5
+ noECC 0 _ -c_1000_-C_c_-g Stress TLS RC4 128 with MD5 (false start)
noECC 0 -u -2_-c_1000_-C_c_-u Stress TLS RC4 128 with MD5 (session ticket)
noECC 0 -z -2_-c_1000_-C_c_-z Stress TLS RC4 128 with MD5 (compression)
noECC 0 -u_-z -2_-c_1000_-C_c_-u_-z Stress TLS RC4 128 with MD5 (session ticket, compression)
+ noECC 0 -u_-z -2_-c_1000_-C_c_-u_-z_-g Stress TLS RC4 128 with MD5 (session ticket, compression, false start)
SNI 0 -u_-a_Host-sni.Dom -2_-3_-c_1000_-C_c_-u Stress TLS RC4 128 with MD5 (session ticket, SNI)
#
@@ -55,7 +57,9 @@
noECC 0 -r_-r -c_100_-C_c_-N_-n_TestUser Stress TLS RC4 128 with MD5 (no reuse, client auth)
noECC 0 -r_-r_-u -2_-c_100_-C_c_-n_TestUser_-u Stress TLS RC4 128 with MD5 (session ticket, client auth)
noECC 0 -r_-r_-z -2_-c_100_-C_c_-n_TestUser_-z Stress TLS RC4 128 with MD5 (compression, client auth)
+ noECC 0 -r_-r_-z -2_-c_100_-C_c_-n_TestUser_-z_-g Stress TLS RC4 128 with MD5 (compression, client auth, false start)
noECC 0 -r_-r_-u_-z -2_-c_100_-C_c_-n_TestUser_-u_-z Stress TLS RC4 128 with MD5 (session ticket, compression, client auth)
+ noECC 0 -r_-r_-u_-z -2_-c_100_-C_c_-n_TestUser_-u_-z_-g Stress TLS RC4 128 with MD5 (session ticket, compression, client auth, false start)
SNI 0 -r_-r_-u_-a_Host-sni.Dom -2_-3_-c_1000_-C_c_-u Stress TLS RC4 128 with MD5 (session ticket, SNI, client auth, default virt host)
SNI 0 -r_-r_-u_-a_Host-sni.Dom_-k_Host-sni.Dom -2_-3_-c_1000_-C_c_-u_-a_Host-sni.Dom Stress TLS RC4 128 with MD5 (session ticket, SNI, client auth, change virt host)