summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwtc%google.com <devnull@localhost>2007-12-22 00:20:14 +0000
committerwtc%google.com <devnull@localhost>2007-12-22 00:20:14 +0000
commit89274cc7dee1e331405609324dfd81c1660085bb (patch)
tree99d112648b5d76c8455518c4eff4106f735e0a74
parentf942fe90de6faf916c4651e6e2f0b955a1585874 (diff)
downloadnss-hg-89274cc7dee1e331405609324dfd81c1660085bb.tar.gz
Bug 403563: implement the TLS session ticket extension as specified in
draft-salowey-tls-rfc4507bis-01.txt. Contributed by Nagendra Modadugu of Google. Modified Files: Tag: NSS_RFC4507BIS_BRANCH cmd/selfserv/selfserv.c cmd/strsclnt/strsclnt.c cmd/tstclnt/tstclnt.c lib/ssl/manifest.mn lib/ssl/ssl.h lib/ssl/ssl3con.c lib/ssl/ssl3ecc.c lib/ssl/ssl3prot.h lib/ssl/sslerr.h lib/ssl/sslimpl.h lib/ssl/sslnonce.c lib/ssl/sslsock.c lib/ssl/sslt.h tests/ssl/sslstress.txt Added Files: Tag: NSS_RFC4507BIS_BRANCH lib/ssl/ssl3ext.c
-rw-r--r--security/nss/cmd/selfserv/selfserv.c35
-rw-r--r--security/nss/cmd/strsclnt/strsclnt.c49
-rw-r--r--security/nss/cmd/tstclnt/tstclnt.c32
-rw-r--r--security/nss/lib/ssl/manifest.mn1
-rw-r--r--security/nss/lib/ssl/ssl.h2
-rw-r--r--security/nss/lib/ssl/ssl3con.c262
-rw-r--r--security/nss/lib/ssl/ssl3ecc.c207
-rw-r--r--security/nss/lib/ssl/ssl3ext.c1214
-rw-r--r--security/nss/lib/ssl/ssl3prot.h48
-rw-r--r--security/nss/lib/ssl/sslerr.h3
-rw-r--r--security/nss/lib/ssl/sslimpl.h120
-rw-r--r--security/nss/lib/ssl/sslnonce.c52
-rw-r--r--security/nss/lib/ssl/sslsock.c17
-rw-r--r--security/nss/lib/ssl/sslt.h5
-rw-r--r--security/nss/tests/ssl/sslstress.txt10
15 files changed, 1771 insertions, 286 deletions
diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c
index b8a3a2147..d70743c65 100644
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -199,6 +199,7 @@ Usage(const char *progName)
" 3 -r's mean request, not require, cert on second handshake.\n"
" 4 -r's mean request and require, cert on second handshake.\n"
"-s means disable SSL socket locking for performance\n"
+"-u means enable Session Ticket extension for TLS1.\n"
"-v means verbose output\n"
"-x means use export policy.\n"
"-L seconds means log statistics every 'seconds' seconds (default=30).\n"
@@ -347,19 +348,28 @@ mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
return rv;
}
+void
+printSSLStatistics()
+{
+ SSL3Statistics * ssl3stats = SSL_GetStatistics();
+
+ printf(
+ "selfserv: %ld cache hits; %ld cache misses, %ld cache not reusable\n"
+ " %ld stateless resumes\n",
+ ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
+ ssl3stats->hch_sid_cache_not_ok, ssl3stats->hch_sid_stateless_resumes);
+}
+
void
printSecurityInfo(PRFileDesc *fd)
{
CERTCertificate * cert = NULL;
- SSL3Statistics * ssl3stats = SSL_GetStatistics();
SECStatus result;
SSLChannelInfo channel;
SSLCipherSuiteInfo suite;
- PRINTF(
- "selfserv: %ld cache hits; %ld cache misses, %ld cache not reusable\n",
- ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
- ssl3stats->hch_sid_cache_not_ok);
+ if (verbose)
+ printSSLStatistics();
result = SSL_GetChannelInfo(fd, &channel, sizeof channel);
if (result == SECSuccess &&
@@ -657,6 +667,7 @@ PRBool disableStepDown = PR_FALSE;
PRBool bypassPKCS11 = PR_FALSE;
PRBool disableLocking = PR_FALSE;
PRBool testbypass = PR_FALSE;
+PRBool enableSessionTicketExtension = PR_FALSE;
static const char stopCmd[] = { "GET /stop " };
static const char getCmd[] = { "GET " };
@@ -1439,6 +1450,13 @@ server_main(
errExit("error disabling SSL socket locking ");
}
}
+ if (enableSessionTicketExtension) {
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKET_EXTENSION,
+ PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error enabling Session Ticket extension ");
+ }
+ }
for (kea = kt_rsa; kea < kt_kea_size; kea++) {
if (cert[kea] != NULL) {
@@ -1697,7 +1715,7 @@ main(int argc, char **argv)
** numbers, then capital letters, then lower case, alphabetical.
*/
optstate = PL_CreateOptState(argc, argv,
- "2:3BC:DEL:M:NP:RSTbc:d:e:f:hi:lmn:op:qrst:vw:xy");
+ "2:3BC:DEL:M:NP:RSTbc:d:e:f:hi:lmn:op:qrst:uvw:xy");
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
++optionsFound;
switch(optstate->option) {
@@ -1776,6 +1794,8 @@ main(int argc, char **argv)
if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS;
break;
+ case 'u': enableSessionTicketExtension = PR_TRUE; break;
+
case 'v': verbose++; break;
case 'w': passwd = PORT_Strdup(optstate->value); break;
@@ -2085,6 +2105,8 @@ main(int argc, char **argv)
VLOG(("selfserv: server_thread: exiting"));
cleanup:
+ printSSLStatistics();
+
{
int i;
for (i=0; i<kt_kea_size; i++) {
@@ -2131,4 +2153,3 @@ cleanup:
printf("selfserv: normal termination\n");
return 0;
}
-
diff --git a/security/nss/cmd/strsclnt/strsclnt.c b/security/nss/cmd/strsclnt/strsclnt.c
index e56ca4733..540b8bafc 100644
--- a/security/nss/cmd/strsclnt/strsclnt.c
+++ b/security/nss/cmd/strsclnt/strsclnt.c
@@ -160,6 +160,7 @@ static PRBool disableTLS = PR_FALSE;
static PRBool bypassPKCS11 = PR_FALSE;
static PRBool disableLocking = PR_FALSE;
static PRBool ignoreErrors = PR_FALSE;
+static PRBool enableSessionTicketExtension = PR_FALSE;
PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT;
@@ -203,7 +204,8 @@ Usage(const char *progName)
" -3 means disable SSL3\n"
" -T means disable TLS\n"
" -U means enable throttling up threads\n"
- " -B bypasses the PKCS11 layer for SSL encryption and MACing\n",
+ " -B bypasses the PKCS11 layer for SSL encryption and MACing\n"
+ " -u enable TLS Session Ticket extension\n",
progName);
exit(1);
}
@@ -347,10 +349,12 @@ printSecurityInfo(PRFileDesc *fd)
cert = NULL;
}
fprintf(stderr,
- "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n",
+ "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n"
+ " %ld stateless resumes\n",
ssl3stats->hsh_sid_cache_hits,
ssl3stats->hsh_sid_cache_misses,
- ssl3stats->hsh_sid_cache_not_ok);
+ ssl3stats->hsh_sid_cache_not_ok,
+ ssl3stats->hsh_sid_stateless_resumes);
}
@@ -1232,6 +1236,13 @@ client_main(
}
}
+ if (enableSessionTicketExtension) {
+ rv = SSL_OptionSet(model_sock,
+ SSL_ENABLE_SESSION_TICKET_EXTENSION, PR_TRUE);
+ if (rv != SECSuccess)
+ errExit("SSL_OptionSet SSL_ENABLE_SESSION_TICKET_EXTENSION");
+ }
+
SSL_SetURL(model_sock, hostName);
SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate,
@@ -1337,7 +1348,7 @@ main(int argc, char **argv)
progName = progName ? progName + 1 : tmp;
- optstate = PL_CreateOptState(argc, argv, "23BC:DNP:TUc:d:f:in:op:qst:vw:");
+ optstate = PL_CreateOptState(argc, argv, "23BC:DNP:TUc:d:f:in:op:qst:uvw:");
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch(optstate->option) {
@@ -1383,7 +1394,9 @@ main(int argc, char **argv)
max_threads = active_threads = tmpInt;
break;
- case 'v': verbose++; break;
+ case 'u': enableSessionTicketExtension = PR_TRUE; break;
+
+ case 'v': verbose++; break;
case 'w': passwd = PL_strdup(optstate->value); break;
@@ -1480,29 +1493,40 @@ main(int argc, char **argv)
PL_strfree(hostName);
/* some final stats. */
- if (ssl3stats->hsh_sid_cache_hits + ssl3stats->hsh_sid_cache_misses +
- ssl3stats->hsh_sid_cache_not_ok == 0) {
+ if (ssl3stats->hsh_sid_cache_hits +
+ ssl3stats->hsh_sid_cache_misses +
+ ssl3stats->hsh_sid_cache_not_ok +
+ ssl3stats->hsh_sid_stateless_resumes == 0) {
/* presumably we were testing SSL2. */
printf("strsclnt: SSL2 - %d server certificates tested.\n",
certsTested);
} else {
printf(
- "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n",
+ "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n"
+ " %ld stateless resumes\n",
ssl3stats->hsh_sid_cache_hits,
ssl3stats->hsh_sid_cache_misses,
- ssl3stats->hsh_sid_cache_not_ok);
+ ssl3stats->hsh_sid_cache_not_ok,
+ ssl3stats->hsh_sid_stateless_resumes);
}
if (!NoReuse)
- exitVal = (ssl3stats->hsh_sid_cache_misses > 1) ||
+ exitVal = (enableSessionTicketExtension &&
+ (connections - ssl3stats->hsh_sid_stateless_resumes > 1)) ||
+ (!enableSessionTicketExtension &&
+ ((ssl3stats->hsh_sid_cache_misses > 1) ||
+ (ssl3stats->hsh_sid_stateless_resumes != 0))) ||
(ssl3stats->hsh_sid_cache_not_ok != 0) ||
(certsTested > 1);
else {
printf("strsclnt: NoReuse - %d server certificates tested.\n",
certsTested);
- if (ssl3stats->hsh_sid_cache_hits + ssl3stats->hsh_sid_cache_misses +
- ssl3stats->hsh_sid_cache_not_ok > 0) {
+ if (ssl3stats->hsh_sid_cache_hits +
+ ssl3stats->hsh_sid_cache_misses +
+ ssl3stats->hsh_sid_cache_not_ok +
+ ssl3stats->hsh_sid_stateless_resumes > 0) {
exitVal = (ssl3stats->hsh_sid_cache_misses != connections) ||
+ (ssl3stats->hsh_sid_stateless_resumes != 0) ||
(certsTested != connections);
} else { /* ssl2 connections */
exitVal = (certsTested != connections);
@@ -1519,4 +1543,3 @@ main(int argc, char **argv)
PR_Cleanup();
return exitVal;
}
-
diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c
index 18fdb503b..5a4172d37 100644
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -122,6 +122,7 @@ int ssl3CipherSuites[] = {
unsigned long __cmp_umuls;
PRBool verbose;
+int renegotiate = 0;
static char *progName;
@@ -181,23 +182,26 @@ void printSecurityInfo(PRFileDesc *fd)
CERT_DestroyCertificate(cert);
cert = NULL;
}
- fprintf(stderr,
- "%ld cache hits; %ld cache misses, %ld cache not reusable\n",
- ssl3stats->hsh_sid_cache_hits, ssl3stats->hsh_sid_cache_misses,
- ssl3stats->hsh_sid_cache_not_ok);
-
+ fprintf(stderr, "%ld cache hits; %ld stateless resumes; %ld cache misses, "
+ "%ld cache not reusable\n", ssl3stats->hsh_sid_cache_hits,
+ ssl3stats->hsh_sid_stateless_resumes,
+ ssl3stats->hsh_sid_cache_misses, ssl3stats->hsh_sid_cache_not_ok);
}
void
handshakeCallback(PRFileDesc *fd, void *client_data)
{
printSecurityInfo(fd);
+ if (renegotiate > 0) {
+ renegotiate--;
+ SSL_ReHandshake(fd, PR_FALSE);
+ }
}
static void Usage(const char *progName)
{
fprintf(stderr,
-"Usage: %s -h host [-p port] [-d certdir] [-n nickname] [-23BTfosvx] \n"
+"Usage: %s -h host [-p port] [-d certdir] [-n nickname] [-23BTfosvxr] \n"
" [-c ciphers] [-w passwd] [-q]\n", progName);
fprintf(stderr, "%-20s Hostname to connect with\n", "-h host");
fprintf(stderr, "%-20s Port number for SSL server\n", "-p port");
@@ -218,6 +222,8 @@ static void Usage(const char *progName)
fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v");
fprintf(stderr, "%-20s Use export policy.\n", "-x");
fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
+ fprintf(stderr, "%-20s Renegotiate with session resumption.\n", "-r");
+ fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
fprintf(stderr, "%-20s Letter(s) chosen from the following list\n",
"-c ciphers");
fprintf(stderr,
@@ -513,6 +519,7 @@ int main(int argc, char **argv)
int bypassPKCS11 = 0;
int disableLocking = 0;
int useExportPolicy = 0;
+ int enableSessionTicketExtension = 0;
PRSocketOptionData opt;
PRNetAddr addr;
PRPollDesc pollset[2];
@@ -541,7 +548,7 @@ int main(int argc, char **argv)
}
}
- optstate = PL_CreateOptState(argc, argv, "23BTSfc:h:p:d:m:n:oqsvw:x");
+ optstate = PL_CreateOptState(argc, argv, "23BTSfc:h:p:d:m:n:oqr:suvw:x");
while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case '?':
@@ -581,8 +588,12 @@ int main(int argc, char **argv)
case 's': disableLocking = 1; break;
+ case 'u': enableSessionTicketExtension = PR_TRUE; break;
+
case 'v': verbose++; break;
+ case 'r': renegotiate = atoi(optstate->value); break;
+
case 'w':
password = PORT_Strdup(optstate->value);
useCommandLinePassword = PR_TRUE;
@@ -825,6 +836,13 @@ int main(int argc, char **argv)
return 1;
}
+ /* enable Session Ticket extension. */
+ rv = SSL_OptionSet(s, SSL_ENABLE_SESSION_TICKET_EXTENSION,
+ enableSessionTicketExtension);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error enabling Session Ticket extension");
+ return 1;
+ }
if (useCommandLinePassword) {
SSL_SetPKCS11PinArg(s, password);
diff --git a/security/nss/lib/ssl/manifest.mn b/security/nss/lib/ssl/manifest.mn
index 7a63a0f5f..dfcd43031 100644
--- a/security/nss/lib/ssl/manifest.mn
+++ b/security/nss/lib/ssl/manifest.mn
@@ -64,6 +64,7 @@ CSRCS = \
ssldef.c \
sslenum.c \
sslerr.c \
+ ssl3ext.c \
sslgathr.c \
sslmutex.c \
sslnonce.c \
diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h
index 56f1691e1..06179409d 100644
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -112,6 +112,8 @@ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd);
/* step-down keys if needed. */
#define SSL_BYPASS_PKCS11 16 /* use PKCS#11 for pub key only */
#define SSL_NO_LOCKS 17 /* Don't use locks for protection */
+#define SSL_ENABLE_SESSION_TICKET_EXTENSION 18 /* Enable TLS1 SessionTicket *
+ * extension (off by default) */
#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 7f981a85a..887dab716 100644
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -78,7 +78,6 @@ static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
static SECStatus ssl3_DeriveConnectionKeysPKCS11(sslSocket *ss);
static SECStatus ssl3_HandshakeFailure( sslSocket *ss);
static SECStatus ssl3_InitState( sslSocket *ss);
-static sslSessionID *ssl3_NewSessionID( sslSocket *ss, PRBool is_server);
static SECStatus ssl3_SendCertificate( sslSocket *ss);
static SECStatus ssl3_SendEmptyCertificate( sslSocket *ss);
static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
@@ -2522,6 +2521,10 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
SSL_TRC(3, ("%d: SSL3[%d]: handle change_cipher_spec record",
SSL_GETPID(), ss->fd));
+ /* When doing a stateless resume, OpenSSL sends the SessionTicket
+ * extension but does not send a NewSessionTicket message so we
+ * work around the bug here.
+ */
if (ws != wait_change_cipher) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
@@ -3501,6 +3504,11 @@ ssl3_SendClientHello(sslSocket *ss)
return rv; /* ssl3_InitState has set the error code. */
}
+ /* We might be starting a session renegotiation in which case we should
+ * clear previous state.
+ */
+ PORT_Memset(&ss->ssl3.extension_data, 0, sizeof(TLS1ExtensionData));
+
SSL_TRC(30,("%d: SSL3[%d]: reset handshake hashes",
SSL_GETPID(), ss->fd ));
rv = ssl3_RestartHandshakeHashes(ss);
@@ -3566,6 +3574,11 @@ ssl3_SendClientHello(sslSocket *ss)
if (sid) {
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
+ /* Are we attempting a stateless session resume? */
+ if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
+ sid->u.ssl3.session_ticket.ticket.data)
+ SSL_AtomicIncrementLong(&ssl3stats.sch_sid_stateless_resumes);
+
rv = ssl3_NegotiateVersion(ss, sid->version);
if (rv != SECSuccess)
return rv; /* error code was set */
@@ -4641,8 +4654,15 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
ss->ssl3.hs.compression = (SSL3CompressionMethod)temp;
-#ifdef DISALLOW_SERVER_HELLO_EXTENSIONS
- if (length != 0) { /* malformed */
+#ifndef DISALLOW_SERVER_HELLO_EXTENSIONS
+ if (isTLS && length > 0) {
+ SECItem extensions;
+ rv = ssl3_ConsumeHandshakeVariable(ss, &extensions, 2, &b, &length);
+ if (rv != SECSuccess || length != 0)
+ goto alert_loser;
+ rv = ssl3_HandleHelloExtensions(ss, &extensions.data, &extensions.len);
+ if (rv != SECSuccess) goto alert_loser;
+ } else if (length > 0) {
goto alert_loser;
}
#endif
@@ -4753,9 +4773,19 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
/* Got a Match */
SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_hits );
- ss->ssl3.hs.ws = wait_change_cipher;
+
+ /* If we sent a session ticket, then this is a stateless resume. */
+ if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
+ sid->u.ssl3.session_ticket.ticket.data != NULL)
+ SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes );
+
ss->ssl3.hs.isResuming = PR_TRUE;
+ if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn))
+ ss->ssl3.hs.ws = wait_new_session_ticket;
+ else
+ ss->ssl3.hs.ws = wait_change_cipher;
+
/* copy the peer cert from the SID */
if (sid->peerCert != NULL) {
ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
@@ -5348,7 +5378,10 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
ssl_ReleaseXmitBufLock(ss); /*******************************/
- ss->ssl3.hs.ws = wait_change_cipher;
+ if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn))
+ ss->ssl3.hs.ws = wait_new_session_ticket;
+ else
+ ss->ssl3.hs.ws = wait_change_cipher;
return SECSuccess;
loser:
@@ -5389,7 +5422,7 @@ ssl3_SendHelloRequest(sslSocket *ss)
* ssl3_HandleClientHello()
* ssl3_HandleV2ClientHello()
*/
-static sslSessionID *
+sslSessionID *
ssl3_NewSessionID(sslSocket *ss, PRBool is_server)
{
sslSessionID *sid;
@@ -5574,21 +5607,6 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
goto loser; /* malformed */
}
- if (sidBytes.len > 0 && !ss->opt.noCache) {
- SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x",
- SSL_GETPID(), ss->fd, ss->sec.ci.peer.pr_s6_addr32[0],
- ss->sec.ci.peer.pr_s6_addr32[1],
- ss->sec.ci.peer.pr_s6_addr32[2],
- ss->sec.ci.peer.pr_s6_addr32[3]));
- if (ssl_sid_lookup) {
- sid = (*ssl_sid_lookup)(&ss->sec.ci.peer, sidBytes.data,
- sidBytes.len, ss->dbHandle);
- } else {
- errCode = SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED;
- goto loser;
- }
- }
-
/* grab the list of cipher suites. */
rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
if (rv != SECSuccess) {
@@ -5603,6 +5621,91 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
desc = handshake_failure;
+ /* Handle TLS hello extensions, for SSL3 & TLS, We don not know if
+ * we are restarting a previous session until extensions have been
+ * parsed, since we might have received a SessionTicket extension.
+ */
+
+ /* We might be starting a session renegotiation in which case we should
+ * clear previous state.
+ */
+ PORT_Memset(&ss->ssl3.extension_data, 0, sizeof(TLS1ExtensionData));
+ ss->ssl3.stateless_resume = PR_FALSE;
+
+ /* OpenSSL 0.9.8g sends TLS extensions even when negotiating SSL3,
+ * so we simply ignore any trailing bytes if the negotiated
+ * version is not TLS. */
+ if (length && ss->version > SSL_LIBRARY_VERSION_3_0) {
+ /* Get length of hello extensions */
+ PRInt32 extension_length;
+ extension_length = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
+ if (extension_length < 0) {
+ goto loser; /* alert already sent */
+ }
+ if (extension_length != length) {
+ ssl3_DecodeError(ss); /* send alert */
+ goto loser;
+ }
+ rv = ssl3_HandleHelloExtensions(ss, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* malformed */
+ }
+ }
+
+ /* We do stateful resumes only if either of the following
+ * conditions are satisfied: (1) the client does not support the
+ * session ticket extension, or (2) the client support the session
+ * ticket extension, but sent an empty ticket.
+ */
+ if (!ssl3_ExtensionNegotiated(ss, session_ticket_xtn) ||
+ ss->ssl3.extension_data.empty_session_ticket) {
+ if (sidBytes.len > 0 && !ss->opt.noCache) {
+ SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x",
+ SSL_GETPID(), ss->fd, ss->sec.ci.peer.pr_s6_addr32[0],
+ ss->sec.ci.peer.pr_s6_addr32[1],
+ ss->sec.ci.peer.pr_s6_addr32[2],
+ ss->sec.ci.peer.pr_s6_addr32[3]));
+ if (ssl_sid_lookup) {
+ sid = (*ssl_sid_lookup)(&ss->sec.ci.peer, sidBytes.data,
+ sidBytes.len, ss->dbHandle);
+ } else {
+ errCode = SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED;
+ goto loser;
+ }
+ }
+ } else if (ss->ssl3.stateless_resume) {
+ /* Fill in the client's session ID if doing a stateless resume.
+ * (When doing stateless resumes, server echos client's SessionID.)
+ */
+ sid = ss->sec.ci.sid;
+ PORT_Assert(sid != NULL); /* Should have already been filled in.*/
+
+ if (sidBytes.len > 0 && sidBytes.len <= SSL3_SESSIONID_BYTES) {
+ sid->u.ssl3.sessionIDLength = sidBytes.len;
+ PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes.data,
+ sidBytes.len);
+ sid->u.ssl3.sessionIDLength = sidBytes.len;
+ } else {
+ sid->u.ssl3.sessionIDLength = 0;
+ }
+ ss->sec.ci.sid = NULL;
+ }
+
+ /* We only send a session ticket extension if the client supports
+ * the extension and we are unable to do either a stateful or
+ * stateless resume.
+ *
+ * TODO: send a session ticket if performing a stateful
+ * resumption. (As per RFC4507, a server may issue a session
+ * ticket while doing a (stateless or stateful) session resume,
+ * but OpenSSL-0.9.8g does not accept session tickets while
+ * resuming.)
+ */
+ if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn) && sid == NULL) {
+ ssl3_RegisterServerHelloExtensionSender(ss,
+ session_ticket_xtn, ssl3_SendSessionTicketExt);
+ }
+
if (sid != NULL) {
/* We've found a session cache entry for this client.
* Now, if we're going to require a client-auth cert,
@@ -5678,26 +5781,6 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
/* START A NEW SESSION */
- /* Handle TLS hello extensions, for SSL3 & TLS,
- * only if we're not restarting a previous session.
- */
- if (length) {
- /* Get length of hello extensions */
- PRInt32 extension_length;
- extension_length = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
- if (extension_length < 0) {
- goto loser; /* alert already sent */
- }
- if (extension_length != length) {
- ssl3_DecodeError(ss); /* send alert */
- goto loser;
- }
- rv = ssl3_HandleClientHelloExtensions(ss, &b, &length);
- if (rv != SECSuccess) {
- goto loser; /* malformed */
- }
- }
-
#ifndef PARANOID
/* Look for a matching cipher suite. */
j = ssl3_config_match_init(ss);
@@ -5775,6 +5858,7 @@ compression_found:
ssl_GetSpecWriteLock(ss); haveSpecWriteLock = PR_TRUE;
pwSpec = ss->ssl3.pwSpec;
+
if (sid->u.ssl3.keys.msIsWrapped) {
PK11SymKey * wrapKey; /* wrapping key */
CK_FLAGS keyFlags = 0;
@@ -5842,6 +5926,9 @@ compression_found:
* XXX make sure compression still matches
*/
SSL_AtomicIncrementLong(& ssl3stats.hch_sid_cache_hits );
+ if (ss->ssl3.stateless_resume)
+ SSL_AtomicIncrementLong(&ssl3stats.hch_sid_stateless_resumes);
+
ss->ssl3.hs.isResuming = PR_TRUE;
ss->sec.authAlgorithm = sid->authAlgorithm;
@@ -5877,11 +5964,13 @@ compression_found:
goto loser;
}
+
rv = ssl3_SendChangeCipherSpecs(ss);
if (rv != SECSuccess) {
errCode = PORT_GetError();
goto loser;
}
+
rv = ssl3_SendFinished(ss, 0);
ss->ssl3.hs.ws = wait_change_cipher;
if (rv != SECSuccess) {
@@ -6146,12 +6235,12 @@ ssl3_SendServerHello(sslSocket *ss)
sid = ss->sec.ci.sid;
extensions_len = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes,
- &ss->serverExtensionSenders[0]);
+ &ss->ssl3.extension_data.serverExtensionSenders[0]);
if (extensions_len > 0)
extensions_len += 2; /* Add sizeof total extension length */
length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH + 1 +
- ((sid == NULL) ? 0: SSL3_SESSIONID_BYTES) +
+ ((sid == NULL) ? 0: sid->u.ssl3.sessionIDLength) +
sizeof(ssl3CipherSuite) + 1 + extensions_len;
rv = ssl3_AppendHandshakeHeader(ss, server_hello, length);
if (rv != SECSuccess) {
@@ -6173,11 +6262,11 @@ ssl3_SendServerHello(sslSocket *ss)
return rv; /* err set by AppendHandshake. */
}
- if (sid)
+ if (sid && sid->u.ssl3.sessionIDLength > 0)
rv = ssl3_AppendHandshakeVariable(
ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
else
- rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
+ rv = ssl3_AppendHandshakeNumber(ss, 0, 1);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
}
@@ -6198,7 +6287,7 @@ ssl3_SendServerHello(sslSocket *ss)
if (rv != SECSuccess)
return rv; /* err set by ssl3_SetupPendingCipherSpec */
sent_len = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, extensions_len,
- &ss->serverExtensionSenders[0]);
+ &ss->ssl3.extension_data.serverExtensionSenders[0]);
PORT_Assert(sent_len == extensions_len);
if (sent_len != extensions_len) {
if (sent_len >= 0)
@@ -6788,6 +6877,51 @@ ssl3_SendEmptyCertificate(sslSocket *ss)
return rv; /* error, if any, set by functions called above. */
}
+SECStatus
+ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+{
+ SECStatus rv;
+ NewSessionTicket session_ticket;
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle session_ticket handshake",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+
+ if (ss->ssl3.hs.ws != wait_new_session_ticket) {
+ SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+
+ session_ticket.received_timestamp = ssl_Time();
+ if (length < 4) {
+ (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+ session_ticket.ticket_lifetime_hint =
+ (PRUint32)ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
+
+ rv = ssl3_ConsumeHandshakeVariable(ss, &session_ticket.ticket, 2,
+ &b, &length);
+ if (length != 0 || rv != SECSuccess) {
+ (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+ return SECFailure; /* malformed */
+ }
+
+ rv = ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &session_ticket);
+ if (rv != SECSuccess) {
+ (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
+ PORT_SetError(SSL_ERROR_INTERNAL_ERROR_ALERT);
+ return SECFailure;
+ }
+ ss->ssl3.hs.ws = wait_change_cipher;
+ return SECSuccess;
+}
+
#ifdef NISCC_TEST
static PRInt32 connNum = 0;
@@ -7374,6 +7508,7 @@ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec,
inData.data = (unsigned char *)hashes->md5;
inData.len = sizeof hashes[0];
+
outData.data = tlsFinished->verify_data;
outData.len = sizeof tlsFinished->verify_data;
rv = TLS_PRF(&spec->msItem, label, &inData, &outData, isFIPS);
@@ -7443,17 +7578,17 @@ fail:
/* wrap the master secret, and put it into the SID.
* Caller holds the Spec read lock.
*/
-static SECStatus
-ssl3_CacheWrappedMasterSecret(sslSocket *ss, SSL3KEAType effectiveExchKeyType)
+SECStatus
+ssl3_CacheWrappedMasterSecret(sslSocket *ss, sslSessionID *sid,
+ ssl3CipherSpec *spec, SSL3KEAType effectiveExchKeyType)
{
- sslSessionID * sid = ss->sec.ci.sid;
PK11SymKey * wrappingKey = NULL;
PK11SlotInfo * symKeySlot;
void * pwArg = ss->pkcs11PinArg;
SECStatus rv = SECFailure;
PRBool isServer = ss->sec.isServer;
CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
- symKeySlot = PK11_GetSlotFromKey(ss->ssl3.crSpec->master_secret);
+ symKeySlot = PK11_GetSlotFromKey(spec->master_secret);
if (!isServer) {
int wrapKeyIndex;
int incarnation;
@@ -7589,6 +7724,20 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
(!isServer && ss->ssl3.hs.isResuming)) {
PRInt32 flags = 0;
+ /* Send a NewSessionTicket message if the client sent us
+ * either an empty session ticket, or one that did not verify.
+ * (Note that if either of these conditions was met, then the
+ * server has sent a SessionTicket extension in the
+ * ServerHello message.)
+ */
+ if (isServer && !ss->ssl3.hs.isResuming &&
+ ssl3_ExtensionNegotiated(ss, session_ticket_xtn)) {
+ rv = ssl3_SendNewSessionTicket(ss);
+ if (rv != SECSuccess) {
+ goto xmit_loser;
+ }
+ }
+
rv = ssl3_SendChangeCipherSpecs(ss);
if (rv != SECSuccess) {
goto xmit_loser; /* err is set. */
@@ -7663,7 +7812,8 @@ xmit_loser:
sid->u.ssl3.keys.msIsWrapped = PR_FALSE;
rv = SECSuccess;
} else {
- rv = ssl3_CacheWrappedMasterSecret(ss, effectiveExchKeyType);
+ rv = ssl3_CacheWrappedMasterSecret(ss, ss->sec.ci.sid,
+ ss->ssl3.crSpec, effectiveExchKeyType);
sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
}
ssl_ReleaseSpecReadLock(ss); /*************************************/
@@ -7826,6 +7976,14 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
rv = ssl3_HandleClientKeyExchange(ss, b, length);
break;
+ case new_session_ticket:
+ if (ss->sec.isServer) {
+ (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+ rv = ssl3_HandleNewSessionTicket(ss, b, length);
+ break;
case finished:
rv = ssl3_HandleFinished(ss, b, length, &hashes);
break;
@@ -8255,6 +8413,8 @@ ssl3_InitState(sslSocket *ss)
#endif
ssl_ReleaseSpecWriteLock(ss);
+ PORT_Memset(&ss->ssl3.extension_data, 0, sizeof(TLS1ExtensionData));
+
rv = ssl3_NewHandshakeHashes(ss);
if (rv == SECSuccess) {
ss->ssl3.initialized = PR_TRUE;
diff --git a/security/nss/lib/ssl/ssl3ecc.c b/security/nss/lib/ssl/ssl3ecc.c
index 14c12e28d..970112503 100644
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -1048,7 +1048,7 @@ static const PRUint8 ECPtFmt[6] = {
* which says that we support all TLS-defined named curves.
*/
PRInt32
-ssl3_SendSupportedEllipticCurvesExtension(
+ssl3_SendSupportedCurvesExt(
sslSocket * ss,
PRBool append,
PRUint32 maxBytes)
@@ -1057,6 +1057,14 @@ ssl3_SendSupportedEllipticCurvesExtension(
return 0;
if (append && maxBytes >= (sizeof EClist)) {
SECStatus rv = ssl3_AppendHandshake(ss, EClist, (sizeof EClist));
+ if (rv != SECSuccess)
+ return -1;
+ if (!ss->sec.isServer) {
+ TLS1ExtensionData *ex_data = &ss->ssl3.extension_data;
+ ex_data->advertisedClientExtensions[
+ ex_data->numAdvertisedClientExtensions++] =
+ elliptic_curves_xtn;
+ }
}
return (sizeof EClist);
}
@@ -1065,7 +1073,7 @@ ssl3_SendSupportedEllipticCurvesExtension(
* which says that we only support uncompressed points.
*/
PRInt32
-ssl3_SendSupportedPointFormatsExtension(
+ssl3_SendSupportedPointExt(
sslSocket * ss,
PRBool append,
PRUint32 maxBytes)
@@ -1074,6 +1082,14 @@ ssl3_SendSupportedPointFormatsExtension(
return 0;
if (append && maxBytes >= (sizeof ECPtFmt)) {
SECStatus rv = ssl3_AppendHandshake(ss, ECPtFmt, (sizeof ECPtFmt));
+ if (rv != SECSuccess)
+ return -1;
+ if (!ss->sec.isServer) {
+ TLS1ExtensionData *ex_data = &ss->ssl3.extension_data;
+ ex_data->advertisedClientExtensions[
+ ex_data->numAdvertisedClientExtensions++] =
+ elliptic_point_formats_xtn;
+ }
}
return (sizeof ECPtFmt);
}
@@ -1081,9 +1097,9 @@ ssl3_SendSupportedPointFormatsExtension(
/* Just make sure that the remote client supports uncompressed points,
* Since that is all we support. Disable ECC cipher suites if it doesn't.
*/
-static SECStatus
-ssl3_HandleSupportedPointFormatsExtension(sslSocket * ss, PRUint16 ex_type,
- SECItem *data)
+SECStatus
+ssl3_HandleSupportedPointExt(sslSocket * ss, PRUint16 ex_type,
+ SECItem *data)
{
int i;
@@ -1097,7 +1113,7 @@ ssl3_HandleSupportedPointFormatsExtension(sslSocket * ss, PRUint16 ex_type,
/* indicate that we should send a reply */
SECStatus rv;
rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type,
- &ssl3_SendSupportedPointFormatsExtension);
+ &ssl3_SendSupportedPointExt);
return rv;
}
}
@@ -1128,9 +1144,9 @@ ECName ssl3_GetSvrCertCurveName(sslSocket *ss)
/* Ensure that the curve in our server cert is one of the ones suppored
* by the remote client, and disable all ECC cipher suites if not.
*/
-static SECStatus
-ssl3_HandleSupportedEllipticCurvesExtension(sslSocket * ss, PRUint16 ex_type,
- SECItem *data)
+SECStatus
+ssl3_HandleSupportedCurvesExt(sslSocket * ss, PRUint16 ex_type,
+ SECItem *data)
{
PRInt32 list_len;
PRUint32 peerCurves = 0;
@@ -1181,176 +1197,3 @@ loser:
}
#endif /* NSS_ENABLE_ECC */
-
-/* Format an SNI extension, using the name from the socket's URL,
- * unless that name is a dotted decimal string.
- */
-PRInt32
-ssl3_SendServerNameIndicationExtension(
- sslSocket * ss,
- PRBool append,
- PRUint32 maxBytes)
-{
- PRUint32 len, span;
- /* must have a hostname */
- if (!ss || !ss->url || !ss->url[0])
- return 0;
- /* must have at lest one character other than [0-9\.] */
- len = PORT_Strlen(ss->url);
- span = strspn(ss->url, "0123456789.");
- if (len == span) {
- /* is a dotted decimal IP address */
- return 0;
- }
- if (append && maxBytes >= len + 9) {
- SECStatus rv;
- /* extension_type */
- rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
- if (rv != SECSuccess) return 0;
- /* length of extension_data */
- rv = ssl3_AppendHandshakeNumber(ss, len + 5, 2);
- if (rv != SECSuccess) return 0;
- /* length of server_name_list */
- rv = ssl3_AppendHandshakeNumber(ss, len + 3, 2);
- if (rv != SECSuccess) return 0;
- /* Name Type (host_name) */
- rv = ssl3_AppendHandshake(ss, "\0", 1);
- if (rv != SECSuccess) return 0;
- /* HostName (length and value) */
- rv = ssl3_AppendHandshakeVariable(ss, ss->url, len, 2);
- if (rv != SECSuccess) return 0;
- }
- return len + 9;
-}
-
-/* handle an incoming SNI extension, by ignoring it. */
-SECStatus
-ssl3_HandleServerNameIndicationExtension(sslSocket * ss, PRUint16 ex_type,
- SECItem *data)
-{
- /* For now, we ignore this, as if we didn't understand it. :-) */
- return SECSuccess;
-}
-
-/* Table of handlers for received TLS hello extensions, one per extension.
- * In the second generation, this table will be dynamic, and functions
- * will be registered here.
- */
-static const ssl3HelloExtensionHandler handlers[] = {
- { 0, &ssl3_HandleServerNameIndicationExtension },
-#ifdef NSS_ENABLE_ECC
- { 10, &ssl3_HandleSupportedEllipticCurvesExtension },
- { 11, &ssl3_HandleSupportedPointFormatsExtension },
-#endif
- { -1, NULL }
-};
-
-/* Table of functions to format TLS hello extensions, one per extension.
- * This static table is for the formatting of client hello extensions.
- * The server's table of hello senders is dynamic, in the socket struct,
- * and sender functions are registered there.
- */
-static const
-ssl3HelloExtensionSender clientHelloSenders[MAX_EXTENSION_SENDERS] = {
- { 0, &ssl3_SendServerNameIndicationExtension },
-#ifdef NSS_ENABLE_ECC
- { 10, &ssl3_SendSupportedEllipticCurvesExtension },
- { 11, &ssl3_SendSupportedPointFormatsExtension },
-#else
- { -1, NULL }
-#endif
-};
-
-/* go through hello extensions in buffer "b".
- * For each one, find the extension handler in the table above, and
- * if present, invoke that handler.
- * ignore any extensions with unknown extension types.
- */
-SECStatus
-ssl3_HandleClientHelloExtensions(sslSocket *ss,
- SSL3Opaque **b,
- PRUint32 *length)
-{
- while (*length) {
- const ssl3HelloExtensionHandler * handler;
- SECStatus rv;
- PRInt32 extension_type;
- SECItem extension_data;
-
- /* Get the extension's type field */
- extension_type = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
- if (extension_type < 0) /* failure to decode extension_type */
- return SECFailure; /* alert already sent */
-
- /* get the data for this extension, so we can pass it or skip it. */
- rv = ssl3_ConsumeHandshakeVariable(ss, &extension_data, 2, b, length);
- if (rv != SECSuccess)
- return rv;
-
- /* find extension_type in table of Client Hello Extension Handlers */
- for (handler = handlers; handler->ex_type >= 0; handler++) {
- if (handler->ex_type == extension_type)
- break;
- }
-
- /* if found, Call this handler */
- if (handler->ex_type == extension_type) {
- rv = (*handler->ex_handler)(ss, (PRUint16)extension_type,
- &extension_data);
- /* Ignore this result */
- /* Essentially, treat all bad extensions as unrecognized types. */
- }
- }
- return SECSuccess;
-}
-
-/* Add a callback function to the table of senders of server hello extensions.
- */
-SECStatus
-ssl3_RegisterServerHelloExtensionSender(sslSocket *ss, PRUint16 ex_type,
- ssl3HelloExtensionSenderFunc cb)
-{
- int i;
- ssl3HelloExtensionSender *sender = &ss->serverExtensionSenders[0];
-
- for (i = 0; i < MAX_EXTENSION_SENDERS; ++i, ++sender) {
- if (!sender->ex_sender) {
- sender->ex_type = ex_type;
- sender->ex_sender = cb;
- return SECSuccess;
- }
- /* detect duplicate senders */
- PORT_Assert(sender->ex_type != ex_type);
- if (sender->ex_type == ex_type) {
- /* duplicate */
- break;
- }
- }
- PORT_Assert(i < MAX_EXTENSION_SENDERS); /* table needs to grow */
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
-}
-
-/* call each of the extension senders and return the accumulated length */
-PRInt32
-ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes,
- const ssl3HelloExtensionSender *sender)
-{
- PRInt32 total_exten_len = 0;
- int i;
-
- if (!sender)
- sender = &clientHelloSenders[0];
-
- for (i = 0; i < MAX_EXTENSION_SENDERS; ++i, ++sender) {
- if (sender->ex_sender) {
- PRInt32 extLen = (*sender->ex_sender)(ss, append, maxBytes);
- if (extLen < 0)
- return -1;
- maxBytes -= extLen;
- total_exten_len += extLen;
- }
- }
- return total_exten_len;
-}
-
diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c
new file mode 100644
index 000000000..c202c4daa
--- /dev/null
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -0,0 +1,1214 @@
+/*
+ * SSL3 Protocol
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com> and
+ * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
+ * Nagendra Modadugu <ngm@google.com>, Google Inc.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* TLS extension code moved here from ssl3con.c */
+/* $Id$ */
+
+#include "nssrenam.h"
+#include "nss.h"
+#include "ssl.h"
+#include "sslimpl.h"
+#include "pk11pub.h"
+#include "blapi.h"
+
+static PK11SymKey *session_ticket_enc_key_pkcs11 = NULL;
+static PK11SymKey *session_ticket_mac_key_pkcs11 = NULL;
+static PRCallOnceType generate_session_keys_once_pkcs11;
+
+static unsigned char session_ticket_enc_key[32];
+static unsigned char session_ticket_mac_key[SHA256_LENGTH];
+static PRCallOnceType generate_session_keys_once;
+
+static PRInt32 ssl3_SendServerNameExt(sslSocket * ss,
+ PRBool append, PRUint32 maxBytes);
+static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss,
+ SECItem *data, EncryptedSessionTicket *enc_session_ticket);
+static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf,
+ PRUint32 bytes);
+static SECStatus ssl3_AppendNumberToItem(SECItem *item, PRUint32 num,
+ PRInt32 lenSize);
+
+
+/*
+ * Write bytes. Using this function means the SECItem structure
+ * cannot be freed. The caller is expected to call this function
+ * on a shallow copy of the structure.
+ */
+static SECStatus
+ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
+{
+ if (bytes > item->len)
+ return SECFailure;
+
+ PORT_Memcpy(item->data, buf, bytes);
+ item->data += bytes;
+ item->len -= bytes;
+ return SECSuccess;
+}
+
+/*
+ * Write a number in network byte order. Using this function means the
+ * SECItem structure cannot be freed. The caller is expected to call
+ * this function on a shallow copy of the structure.
+ */
+static SECStatus
+ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize)
+{
+ SECStatus rv;
+ uint8 b[4];
+ uint8 * p = b;
+
+ switch (lenSize) {
+ case 4:
+ *p++ = (num >> 24) & 0xff;
+ case 3:
+ *p++ = (num >> 16) & 0xff;
+ case 2:
+ *p++ = (num >> 8) & 0xff;
+ case 1:
+ *p = num & 0xff;
+ }
+ rv = ssl3_AppendToItem(item, &b[0], lenSize);
+ return rv;
+}
+
+static SECStatus ssl3_SessionTicketShutdown(void* appData, void* nssData)
+{
+ if (session_ticket_enc_key_pkcs11) {
+ PK11_FreeSymKey(session_ticket_enc_key_pkcs11);
+ session_ticket_enc_key_pkcs11 = NULL;
+ }
+ if (session_ticket_mac_key_pkcs11) {
+ PK11_FreeSymKey(session_ticket_mac_key_pkcs11);
+ session_ticket_mac_key_pkcs11 = NULL;
+ }
+ memset(&generate_session_keys_once_pkcs11, 0, sizeof(PRCallOnceType));
+ return SECSuccess;
+}
+
+static PRStatus
+ssl3_GenerateSessionTicketKeysPKCS11(void)
+{
+ PK11SlotInfo *slot;
+ SECStatus rv;
+
+ slot = PK11_GetBestSlot(CKM_AES_CBC, NULL);
+ /* no parameter, 128-bit key size */
+ session_ticket_enc_key_pkcs11 =
+ PK11_KeyGen(slot, CKM_AES_KEY_GEN, NULL, 16, NULL);
+ PK11_FreeSlot(slot);
+ if (!session_ticket_enc_key_pkcs11)
+ return PR_FAILURE;
+ slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL);
+ /* no parameter, 256-bit key size */
+ session_ticket_mac_key_pkcs11 =
+ PK11_KeyGen(slot, CKM_GENERIC_SECRET_KEY_GEN, NULL, 32, NULL);
+ PK11_FreeSlot(slot);
+ if (!session_ticket_mac_key_pkcs11)
+ goto loser;
+ rv = NSS_RegisterShutdown(ssl3_SessionTicketShutdown, NULL);
+ if (rv != SECSuccess)
+ goto loser;
+ return PR_SUCCESS;
+
+loser:
+ ssl3_SessionTicketShutdown(NULL, NULL);
+ return PR_FAILURE;
+}
+
+SECStatus
+ssl3_GetSessionTicketKeysPKCS11(PK11SymKey **aes_key, PK11SymKey **mac_key)
+{
+ PRStatus rv;
+ rv = PR_CallOnce(&generate_session_keys_once_pkcs11,
+ ssl3_GenerateSessionTicketKeysPKCS11);
+ if (rv != PR_SUCCESS)
+ return SECFailure;
+
+ *aes_key = session_ticket_enc_key_pkcs11;
+ *mac_key = session_ticket_mac_key_pkcs11;
+
+ return SECSuccess;
+}
+
+static PRStatus
+ssl3_GenerateSessionTicketKeys(void)
+{
+ SECStatus rv = PK11_GenerateRandom(session_ticket_enc_key,
+ sizeof(session_ticket_enc_key));
+ if (rv != SECSuccess) return PR_FAILURE;
+ rv = PK11_GenerateRandom(session_ticket_mac_key,
+ sizeof(session_ticket_mac_key));
+ return rv == SECSuccess ? PR_SUCCESS : PR_FAILURE;
+}
+
+SECStatus
+ssl3_GetSessionTicketKeys(const unsigned char **aes_key,
+ PRUint32 *aes_key_length, const unsigned char **mac_key,
+ PRUint32 *mac_key_length)
+{
+ PRStatus rv;
+ rv = PR_CallOnce(&generate_session_keys_once,
+ ssl3_GenerateSessionTicketKeys);
+ if (rv != PR_SUCCESS)
+ return SECFailure;
+
+ *aes_key = session_ticket_enc_key;
+ *aes_key_length = sizeof(session_ticket_enc_key);
+ *mac_key = session_ticket_mac_key;
+ *mac_key_length = sizeof(session_ticket_mac_key);
+
+ return SECSuccess;
+}
+
+/* Table of handlers for received TLS hello extensions, one
+ * per extension.
+ * In the second generation, this table will be dynamic, and functions
+ * will be registered here.
+ */
+static const ssl3HelloExtensionHandler client_handlers[] = {
+ { server_name_xtn, &ssl3_HandleServerNameExt },
+ { session_ticket_xtn, &ssl3_ClientHandleSessionTicketExt },
+ { -1, NULL }
+};
+
+static const ssl3HelloExtensionHandler server_handlers[] = {
+ { server_name_xtn, &ssl3_HandleServerNameExt },
+#ifdef NSS_ENABLE_ECC
+ { elliptic_curves_xtn, &ssl3_HandleSupportedCurvesExt },
+ { elliptic_point_formats_xtn,
+ &ssl3_HandleSupportedPointExt },
+#endif
+ { session_ticket_xtn, &ssl3_ServerHandleSessionTicketExt },
+ { -1, NULL }
+};
+
+/* Table of functions to format TLS hello extensions, one per extension.
+ * This static table is for the formatting of client hello extensions.
+ * The server's table of hello senders is dynamic, in the socket struct,
+ * and sender functions are registered there.
+ */
+static const
+ssl3HelloExtensionSender clientHelloSenders[MAX_EXTENSION_SENDERS] = {
+ { server_name_xtn, &ssl3_SendServerNameExt },
+#ifdef NSS_ENABLE_ECC
+ { elliptic_curves_xtn, &ssl3_SendSupportedCurvesExt },
+ { elliptic_point_formats_xtn,
+ &ssl3_SendSupportedPointExt },
+#endif
+ { session_ticket_xtn, ssl3_SendSessionTicketExt },
+ { -1, NULL }
+};
+
+
+static PRBool
+arrayContainsExtension(PRUint16 *array, PRUint32 array_len, PRUint16 ex_type)
+{
+ int i;
+ for (i = 0; i < array_len; i++) {
+ if (ex_type == array[i])
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRBool
+ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type) {
+ TLS1ExtensionData *extension_data = &ss->ssl3.extension_data;
+ return arrayContainsExtension(extension_data->negotiatedExtensions,
+ extension_data->numNegotiatedExtensions, ex_type);
+}
+
+PRBool
+ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type) {
+ TLS1ExtensionData *extension_data = &ss->ssl3.extension_data;
+ return arrayContainsExtension(extension_data->advertisedClientExtensions,
+ extension_data->numAdvertisedClientExtensions, ex_type);
+}
+
+/* Format an SNI extension, using the name from the socket's URL,
+ * unless that name is a dotted decimal string.
+ */
+static PRInt32
+ssl3_SendServerNameExt(
+ sslSocket * ss,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ PRUint32 len, span;
+ /* must have a hostname */
+ if (!ss || !ss->url || !ss->url[0])
+ return 0;
+ /* must have at lest one character other than [0-9\.] */
+ len = PORT_Strlen(ss->url);
+ span = strspn(ss->url, "0123456789.");
+ if (len == span) {
+ /* is a dotted decimal IP address */
+ return 0;
+ }
+ if (append && maxBytes >= len + 9) {
+ SECStatus rv;
+ /* extension_type */
+ rv = ssl3_AppendHandshakeNumber(ss, server_name_xtn, 2);
+ if (rv != SECSuccess) return -1;
+ /* length of extension_data */
+ rv = ssl3_AppendHandshakeNumber(ss, len + 5, 2);
+ if (rv != SECSuccess) return -1;
+ /* length of server_name_list */
+ rv = ssl3_AppendHandshakeNumber(ss, len + 3, 2);
+ if (rv != SECSuccess) return -1;
+ /* Name Type (host_name) */
+ rv = ssl3_AppendHandshake(ss, "\0", 1);
+ if (rv != SECSuccess) return -1;
+ /* HostName (length and value) */
+ rv = ssl3_AppendHandshakeVariable(ss, (unsigned char *)ss->url, len, 2);
+ if (rv != SECSuccess) return -1;
+ if (!ss->sec.isServer) {
+ TLS1ExtensionData *ex_data = &ss->ssl3.extension_data;
+ ex_data->advertisedClientExtensions[
+ ex_data->numAdvertisedClientExtensions++] =
+ server_name_xtn;
+ }
+ }
+ return len + 9;
+}
+
+/* handle an incoming SNI extension, by ignoring it. */
+SECStatus
+ssl3_HandleServerNameExt(sslSocket * ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ /* For now, we ignore this, as if we didn't understand it. :-) */
+ return SECSuccess;
+}
+
+/* Called by both clients and servers.
+ * Clients sends a filled in session ticket if one is available, and otherwise
+ * sends an empty ticket. Servers always send empty tickets.
+ */
+PRInt32
+ssl3_SendSessionTicketExt(
+ sslSocket * ss,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ sslSessionID *sid;
+ NewSessionTicket *session_ticket = NULL;
+
+ /* Ignore the SessionTicket extension if processing is disabled. */
+ if (!ss->opt.enableSessionTicketExtension)
+ return 0;
+
+ /* Empty extension length = extension_type (2-bytes) +
+ * length(extension_data) (2-bytes)
+ */
+ PRInt32 extension_length = 4;
+
+ /* If we are a client then send a session ticket if one is availble.
+ * Servers that support the extension and are willing to negotiate the
+ * the extension always respond with an empty extension.
+ */
+ if (!ss->sec.isServer) {
+ sid = ss->sec.ci.sid;
+ session_ticket = &sid->u.ssl3.session_ticket;
+ if (session_ticket->ticket.data) {
+ if (ss->ssl3.extension_data.ticket_timestamp_verified) {
+ extension_length += session_ticket->ticket.len;
+ } else if (!append &&
+ (session_ticket->ticket_lifetime_hint == 0 ||
+ (session_ticket->ticket_lifetime_hint +
+ session_ticket->received_timestamp > ssl_Time()))) {
+ extension_length += session_ticket->ticket.len;
+ ss->ssl3.extension_data.ticket_timestamp_verified = PR_TRUE;
+ }
+ }
+ }
+
+ if (append && maxBytes >= extension_length) {
+ SECStatus rv;
+ /* extension_type */
+ rv = ssl3_AppendHandshakeNumber(ss, session_ticket_xtn, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ if (session_ticket && session_ticket->ticket.data &&
+ ss->ssl3.extension_data.ticket_timestamp_verified) {
+ rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data,
+ session_ticket->ticket.len, 2);
+ ss->ssl3.extension_data.ticket_timestamp_verified = PR_FALSE;
+ } else {
+ rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+ }
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (!ss->sec.isServer) {
+ TLS1ExtensionData *ex_data = &ss->ssl3.extension_data;
+ ex_data->advertisedClientExtensions[
+ ex_data->numAdvertisedClientExtensions++] =
+ session_ticket_xtn;
+ }
+ } else if (maxBytes < extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+ return extension_length;
+
+ loser:
+ ss->ssl3.extension_data.ticket_timestamp_verified = PR_FALSE;
+ return -1;
+}
+
+/*
+ * NewSessionTicket
+ * Called from ssl3_HandleFinished
+ */
+SECStatus
+ssl3_SendNewSessionTicket(sslSocket *ss)
+{
+ int i;
+ SECStatus rv;
+ NewSessionTicket ticket;
+ SECItem plaintext;
+ SECItem plaintext_item = {0, NULL, 0};
+ SECItem ciphertext = {0, NULL, 0};
+ PRUint32 ciphertext_length;
+ PRBool ms_is_wrapped;
+ unsigned char wrapped_ms[SSL3_MASTER_SECRET_LENGTH];
+ SECItem ms_item = {0, NULL, 0};
+ SSL3KEAType effectiveExchKeyType = ssl_kea_null;
+ PRUint32 padding_length;
+ PRUint32 message_length;
+ PRUint32 cert_length;
+ uint8 length_buf[4];
+ PRUint32 now;
+ PK11SymKey *aes_key_pkcs11;
+ PK11SymKey *mac_key_pkcs11;
+ const unsigned char *aes_key;
+ const unsigned char *mac_key;
+ PRUint32 aes_key_length;
+ PRUint32 mac_key_length;
+ PRUint64 aes_ctx_buf[MAX_CIPHER_CONTEXT_LLONGS];
+ AESContext *aes_ctx;
+ CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
+ PK11Context *aes_ctx_pkcs11;
+ const SECHashObject *hashObj = NULL;
+ PRUint64 hmac_ctx_buf[MAX_MAC_CONTEXT_LLONGS];
+ HMACContext *hmac_ctx;
+ CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
+ PK11Context *hmac_ctx_pkcs11;
+ unsigned char computed_mac[TLS1_EX_SESS_TICKET_MAC_LENGTH];
+ unsigned int computed_mac_length;
+ unsigned char iv[AES_BLOCK_SIZE];
+ SECItem ivItem;
+ CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value,
+ * must be >= 0 */
+
+ SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+ ticket.ticket_lifetime_hint = TLS1_EX_SESS_TICKET_LIFETIME_HINT;
+ cert_length = (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) ?
+ 3 + ss->sec.ci.sid->peerCert->derCert.len : 0;
+
+ /* Get IV and encryption keys */
+ ivItem.data = iv;
+ ivItem.len = sizeof(iv);
+ rv = PK11_GenerateRandom(iv, sizeof(iv));
+ if (rv != SECSuccess) goto loser;
+
+ if (ss->opt.bypassPKCS11) {
+ rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
+ &mac_key, &mac_key_length);
+ } else {
+ rv = ssl3_GetSessionTicketKeysPKCS11(&aes_key_pkcs11,
+ &mac_key_pkcs11);
+ }
+ if (rv != SECSuccess) goto loser;
+
+ if (ss->ssl3.pwSpec->msItem.len && ss->ssl3.pwSpec->msItem.data) {
+ /* The master secret is available unwrapped. */
+ ms_item.data = ss->ssl3.pwSpec->msItem.data;
+ ms_item.len = ss->ssl3.pwSpec->msItem.len;
+ ms_is_wrapped = PR_FALSE;
+ } else {
+ /* Extract the master secret wrapped. */
+ sslSessionID sid;
+ PORT_Memset(&sid, 0, sizeof(sslSessionID));
+
+ if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
+ effectiveExchKeyType = kt_rsa;
+ } else {
+ effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
+ }
+
+ rv = ssl3_CacheWrappedMasterSecret(ss, &sid, ss->ssl3.pwSpec,
+ effectiveExchKeyType);
+ if (rv == SECSuccess) {
+ if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
+ goto loser;
+ memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret,
+ sid.u.ssl3.keys.wrapped_master_secret_len);
+ ms_item.data = wrapped_ms;
+ ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len;
+ msWrapMech = sid.u.ssl3.masterWrapMech;
+ } else {
+ /* TODO: else send an empty ticket. */
+ goto loser;
+ }
+ ms_is_wrapped = PR_TRUE;
+ }
+
+ ciphertext_length =
+ sizeof(PRUint16) /* ticket_version */
+ + sizeof(SSL3ProtocolVersion) /* ssl_version */
+ + sizeof(ssl3CipherSuite) /* ciphersuite */
+ + 1 /* compression */
+ + 10 /* cipher spec parameters */
+ + 1 /* SessionTicket.ms_is_wrapped */
+ + 1 /* effectiveExchKeyType */
+ + 4 /* msWrapMech */
+ + 2 /* master_secret.length */
+ + ms_item.len /* master_secret */
+ + 1 /* client_auth_type */
+ + cert_length /* cert */
+ + sizeof(ticket.ticket_lifetime_hint);
+ padding_length = AES_BLOCK_SIZE -
+ (ciphertext_length % AES_BLOCK_SIZE);
+ ciphertext_length += padding_length;
+
+ message_length =
+ sizeof(ticket.ticket_lifetime_hint) /* ticket_lifetime_hint */
+ + 2 /* length field for NewSessionTicket.ticket */
+ + sizeof(TLS1_EX_SESS_TICKET_KEY_NAME) /* key_name */
+ + AES_BLOCK_SIZE /* iv */
+ + 2 /* length field for NewSessionTicket.ticket.encrypted_state */
+ + ciphertext_length /* encrypted_state */
+ + TLS1_EX_SESS_TICKET_MAC_LENGTH; /* mac */
+
+ if (SECITEM_AllocItem(NULL, &plaintext_item, ciphertext_length) == NULL)
+ goto loser;
+
+ plaintext = plaintext_item;
+
+ /* ticket_version */
+ rv = ssl3_AppendNumberToItem(&plaintext, TLS1_EX_SESS_TICKET_VERSION,
+ sizeof(PRUint16));
+ if (rv != SECSuccess) goto loser;
+
+ /* ssl_version */
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->version,
+ sizeof(SSL3ProtocolVersion));
+ if (rv != SECSuccess) goto loser;
+
+ /* ciphersuite */
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.cipher_suite,
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) goto loser;
+
+ /* compression */
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.compression, 1);
+ if (rv != SECSuccess) goto loser;
+
+ /* cipher spec parameters */
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authAlgorithm, 1);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authKeyBits, 4);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaType, 1);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaKeyBits, 4);
+ if (rv != SECSuccess) goto loser;
+
+ /* master_secret */
+ rv = ssl3_AppendNumberToItem(&plaintext, ms_is_wrapped, 1);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext, effectiveExchKeyType, 1);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext, msWrapMech, 4);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext, ms_item.len, 2);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendToItem(&plaintext, ms_item.data, ms_item.len);
+ if (rv != SECSuccess) goto loser;
+
+ /* client_identity */
+ if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
+ rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendNumberToItem(&plaintext,
+ ss->sec.ci.sid->peerCert->derCert.len, 3);
+ if (rv != SECSuccess) goto loser;
+ rv = ssl3_AppendToItem(&plaintext,
+ ss->sec.ci.sid->peerCert->derCert.data,
+ ss->sec.ci.sid->peerCert->derCert.len);
+ if (rv != SECSuccess) goto loser;
+ } else {
+ rv = ssl3_AppendNumberToItem(&plaintext, 0, 1);
+ if (rv != SECSuccess) goto loser;
+ }
+
+ /* timestamp */
+ now = ssl_Time();
+ rv = ssl3_AppendNumberToItem(&plaintext, now,
+ sizeof(ticket.ticket_lifetime_hint));
+ if (rv != SECSuccess) goto loser;
+
+ PORT_Assert(plaintext.len == padding_length);
+ for (i = 0; i < padding_length; i++)
+ plaintext.data[i] = (unsigned char)padding_length;
+
+ if (SECITEM_AllocItem(NULL, &ciphertext, ciphertext_length) == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Generate encrypted portion of ticket. */
+ if (ss->opt.bypassPKCS11) {
+ aes_ctx = (AESContext *)aes_ctx_buf;
+ rv = AES_InitContext(aes_ctx, aes_key, aes_key_length, iv,
+ NSS_AES_CBC, 1, AES_BLOCK_SIZE);
+ if (rv != SECSuccess) goto loser;
+
+ rv = AES_Encrypt(aes_ctx, ciphertext.data, &ciphertext.len,
+ ciphertext.len, plaintext_item.data,
+ plaintext_item.len);
+ if (rv != SECSuccess) goto loser;
+ } else {
+ aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
+ CKA_ENCRYPT, aes_key_pkcs11, &ivItem);
+ if (!aes_ctx_pkcs11)
+ goto loser;
+
+ rv = PK11_CipherOp(aes_ctx_pkcs11, ciphertext.data, &ciphertext.len,
+ ciphertext.len, plaintext_item.data, plaintext_item.len);
+ PK11_Finalize(aes_ctx_pkcs11);
+ PK11_DestroyContext(aes_ctx_pkcs11, PR_TRUE);
+ if (rv != SECSuccess) goto loser;
+ }
+
+ /* Convert ciphertext length to network order. */
+ length_buf[0] = (ciphertext.len >> 8) & 0xff;
+ length_buf[1] = (ciphertext.len ) & 0xff;
+
+ /* Compute MAC. */
+ if (ss->opt.bypassPKCS11) {
+ hmac_ctx = (HMACContext *)hmac_ctx_buf;
+ hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
+ if (HMAC_Init(hmac_ctx, hashObj, mac_key,
+ mac_key_length, PR_FALSE) != SECSuccess)
+ goto loser;
+
+ HMAC_Begin(hmac_ctx);
+ HMAC_Update(hmac_ctx, (unsigned char *)TLS1_EX_SESS_TICKET_KEY_NAME,
+ sizeof(TLS1_EX_SESS_TICKET_KEY_NAME));
+ HMAC_Update(hmac_ctx, iv, sizeof(iv));
+ HMAC_Update(hmac_ctx, (unsigned char *)length_buf, 2);
+ HMAC_Update(hmac_ctx, ciphertext.data, ciphertext.len);
+ HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
+ sizeof(computed_mac));
+ } else {
+ SECItem macParam;
+ macParam.data = NULL;
+ macParam.len = 0;
+ hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
+ CKA_SIGN, mac_key_pkcs11, &macParam);
+ if (!hmac_ctx_pkcs11)
+ goto loser;
+
+ rv = PK11_DigestBegin(hmac_ctx_pkcs11);
+ rv = PK11_DigestOp(hmac_ctx_pkcs11,
+ (unsigned char *)TLS1_EX_SESS_TICKET_KEY_NAME,
+ sizeof(TLS1_EX_SESS_TICKET_KEY_NAME));
+ rv = PK11_DigestOp(hmac_ctx_pkcs11, iv, sizeof(iv));
+ rv = PK11_DigestOp(hmac_ctx_pkcs11, (unsigned char *)length_buf, 2);
+ rv = PK11_DigestOp(hmac_ctx_pkcs11, ciphertext.data, ciphertext.len);
+ rv = PK11_DigestFinal(hmac_ctx_pkcs11, computed_mac,
+ &computed_mac_length, sizeof(computed_mac));
+ PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+ if (rv != SECSuccess) goto loser;
+ }
+
+ /* Serialize the handshake message. */
+ rv = ssl3_AppendHandshakeHeader(ss, new_session_ticket, message_length);
+ if (rv != SECSuccess) goto loser;
+
+ rv = ssl3_AppendHandshakeNumber(ss, ticket.ticket_lifetime_hint,
+ sizeof(ticket.ticket_lifetime_hint));
+ if (rv != SECSuccess) goto loser;
+
+ rv = ssl3_AppendHandshakeNumber(ss,
+ message_length - sizeof(ticket.ticket_lifetime_hint) - 2, 2);
+ if (rv != SECSuccess) goto loser;
+
+ rv = ssl3_AppendHandshake(ss,
+ (unsigned char *)TLS1_EX_SESS_TICKET_KEY_NAME,
+ sizeof(TLS1_EX_SESS_TICKET_KEY_NAME));
+ if (rv != SECSuccess) goto loser;
+
+ rv = ssl3_AppendHandshake(ss, iv, sizeof(iv));
+ if (rv != SECSuccess) goto loser;
+
+ rv = ssl3_AppendHandshakeVariable(ss, ciphertext.data, ciphertext.len, 2);
+ if (rv != SECSuccess) goto loser;
+
+ rv = ssl3_AppendHandshake(ss, computed_mac, computed_mac_length);
+ if (rv != SECSuccess) goto loser;
+
+loser:
+ if (plaintext_item.data)
+ SECITEM_FreeItem(&plaintext_item, PR_FALSE);
+ if (ciphertext.data)
+ SECITEM_FreeItem(&ciphertext, PR_FALSE);
+
+ return rv;
+}
+
+/* When a client receives a SessionTicket extension a NewSessionTicket
+ * message is expected during the handshake.
+ */
+SECStatus
+ssl3_ClientHandleSessionTicketExt(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ TLS1ExtensionData *ex_data;
+ if (data->len != 0)
+ return SECFailure;
+
+ /* Keep track of negotiated extensions. */
+ ex_data = &ss->ssl3.extension_data;
+ ex_data->negotiatedExtensions[ex_data->numNegotiatedExtensions++] =
+ ex_type;
+ return SECSuccess;
+}
+
+SECStatus
+ssl3_ServerHandleSessionTicketExt(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ SECStatus rv;
+ SECItem *decrypted_state = NULL;
+ SessionTicket *parsed_session_ticket = NULL;
+ sslSessionID *sid = NULL;
+
+
+ /* Ignore the SessionTicket extension if processing is disabled. */
+ if (!ss->opt.enableSessionTicketExtension)
+ return SECSuccess;
+
+ /* Keep track of negotiated extensions. */
+ ss->ssl3.extension_data.negotiatedExtensions[
+ ss->ssl3.extension_data.numNegotiatedExtensions++] = ex_type;
+
+ /* Parse the received ticket sent in by the client. We are
+ * lenient about some parse errors, falling back to a fullshake
+ * instead of terminating the current connection.
+ */
+ if (data->len == 0) {
+ ss->ssl3.extension_data.empty_session_ticket = PR_TRUE;
+ } else {
+ int i;
+ SECItem extension_data;
+ EncryptedSessionTicket enc_session_ticket;
+ unsigned char computed_mac[TLS1_EX_SESS_TICKET_MAC_LENGTH];
+ unsigned int computed_mac_length;
+ const SECHashObject *hashObj;
+ PRUint64 hmac_ctx_buf[MAX_MAC_CONTEXT_LLONGS];
+ HMACContext *hmac_ctx;
+ PK11Context *hmac_ctx_pkcs11;
+ CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
+ PRUint64 aes_ctx_buf[MAX_CIPHER_CONTEXT_LLONGS];
+ AESContext *aes_ctx;
+ PK11Context *aes_ctx_pkcs11;
+ CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
+ unsigned char * padding;
+ PRUint32 padding_length;
+ unsigned char *buffer;
+ unsigned int buffer_len;
+ PRInt32 temp;
+ SECItem cert_item;
+
+ /* Turn off stateless session resumption if the client sends a
+ * SessionTicket extension, even if the extension turns out to be
+ * malformed (ss->sec.ci.sid is non-NULL when doing session
+ * renegotiation.)
+ */
+ if (ss->sec.ci.sid != NULL) {
+ ss->sec.uncache(ss->sec.ci.sid);
+ ssl_FreeSID(ss->sec.ci.sid);
+ ss->sec.ci.sid = NULL;
+ }
+
+ extension_data.data = data->data; /* Keep a copy for future use. */
+ extension_data.len = data->len;
+
+ if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket)
+ != SECSuccess)
+ return SECFailure;
+
+ /* Verify the MAC on the ticket. MAC verification may also
+ * fail if the MAC key has been recently refreshed.
+ */
+ if (ss->opt.bypassPKCS11) {
+ hmac_ctx = (HMACContext *)hmac_ctx_buf;
+ hashObj = HASH_GetRawHashObject(HASH_AlgSHA256);
+ if (HMAC_Init(hmac_ctx, hashObj, session_ticket_mac_key,
+ sizeof(session_ticket_mac_key), PR_FALSE) != SECSuccess)
+ goto no_ticket;
+ HMAC_Begin(hmac_ctx);
+ HMAC_Update(hmac_ctx, extension_data.data,
+ extension_data.len - TLS1_EX_SESS_TICKET_MAC_LENGTH);
+ if (HMAC_Finish(hmac_ctx, computed_mac, &computed_mac_length,
+ sizeof(computed_mac)) != SECSuccess)
+ goto no_ticket;
+ } else {
+ SECItem macParam;
+ macParam.data = NULL;
+ macParam.len = 0;
+ hmac_ctx_pkcs11 = PK11_CreateContextBySymKey(macMech,
+ CKA_SIGN, session_ticket_mac_key_pkcs11, &macParam);
+ if (!hmac_ctx_pkcs11)
+ goto no_ticket;
+ rv = PK11_DigestBegin(hmac_ctx_pkcs11);
+ rv = PK11_DigestOp(hmac_ctx_pkcs11, extension_data.data,
+ extension_data.len - TLS1_EX_SESS_TICKET_MAC_LENGTH);
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+ goto no_ticket;
+ }
+ rv = PK11_DigestFinal(hmac_ctx_pkcs11, computed_mac,
+ &computed_mac_length, sizeof(computed_mac));
+ PK11_DestroyContext(hmac_ctx_pkcs11, PR_TRUE);
+ if (rv != SECSuccess)
+ goto no_ticket;
+ }
+ if (PORT_Memcmp(computed_mac, enc_session_ticket.mac,
+ computed_mac_length) != 0)
+ goto no_ticket;
+
+ /* We ignore key_name for now.
+ * This is ok as MAC verification succeeded.
+ */
+
+ /* Decrypt the ticket. */
+
+ /* Plaintext is shorter than the ciphertext due to padding. */
+ decrypted_state = SECITEM_AllocItem(NULL, NULL,
+ enc_session_ticket.encrypted_state.len);
+
+ if (ss->opt.bypassPKCS11) {
+ aes_ctx = (AESContext *)aes_ctx_buf;
+ rv = AES_InitContext(aes_ctx, session_ticket_enc_key,
+ sizeof(session_ticket_enc_key), enc_session_ticket.iv,
+ NSS_AES_CBC, 0,AES_BLOCK_SIZE);
+ if (rv != SECSuccess)
+ goto no_ticket;
+
+ rv = AES_Decrypt(aes_ctx, decrypted_state->data,
+ &decrypted_state->len, decrypted_state->len,
+ enc_session_ticket.encrypted_state.data,
+ enc_session_ticket.encrypted_state.len);
+ if (rv != SECSuccess)
+ goto no_ticket;
+ } else {
+ SECItem ivItem;
+ ivItem.data = enc_session_ticket.iv;
+ ivItem.len = AES_BLOCK_SIZE;
+ aes_ctx_pkcs11 = PK11_CreateContextBySymKey(cipherMech,
+ CKA_DECRYPT, session_ticket_enc_key_pkcs11, &ivItem);
+ if (!aes_ctx_pkcs11)
+ goto no_ticket;
+
+ rv = PK11_CipherOp(aes_ctx_pkcs11, decrypted_state->data,
+ &decrypted_state->len, decrypted_state->len,
+ enc_session_ticket.encrypted_state.data,
+ enc_session_ticket.encrypted_state.len);
+ PK11_Finalize(aes_ctx_pkcs11);
+ PK11_DestroyContext(aes_ctx_pkcs11, PR_TRUE);
+ if (rv != SECSuccess)
+ goto no_ticket;
+ }
+
+ /* Check padding. */
+ padding_length =
+ (PRUint32)decrypted_state->data[decrypted_state->len - 1];
+ if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
+ goto no_ticket;
+
+ padding = &decrypted_state->data[decrypted_state->len - padding_length];
+ for (i = 0; i < padding_length; i++, padding++) {
+ if (padding_length != (PRUint32)*padding)
+ goto no_ticket;
+ }
+
+ /* Deserialize session state. */
+ buffer = decrypted_state->data;
+ buffer_len = decrypted_state->len;
+
+ parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
+ if (parsed_session_ticket == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Read ticket_version (which is ignored for now.) */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
+
+ /* Read SSLVersion. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
+
+ /* Read cipher_suite. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
+
+ /* Read compression_method. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->compression_method = (SSL3CompressionMethod)temp;
+
+ /* Read cipher spec parameters. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->authAlgorithm = (SSLSignType)temp;
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->authKeyBits = (PRUint32)temp;
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->keaType = (SSLKEAType)temp;
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->keaKeyBits = (PRUint32)temp;
+
+ /* Read wrapped master_secret. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
+
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->exchKeyType = (SSL3KEAType)temp;
+
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
+
+ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
+ if (temp < 0) goto no_ticket;
+ parsed_session_ticket->ms_length = (PRUint16)temp;
+ if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
+ parsed_session_ticket->ms_length >
+ sizeof(parsed_session_ticket->master_secret))
+ goto no_ticket;
+
+ /* Allow for the wrapped master secret to be longer. */
+ if (buffer_len < sizeof(SSL3_MASTER_SECRET_LENGTH))
+ goto no_ticket;
+ PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
+ parsed_session_ticket->ms_length);
+ buffer += parsed_session_ticket->ms_length;
+ buffer_len -= parsed_session_ticket->ms_length;
+
+ /* Read client_identity */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->client_identity.client_auth_type =
+ (ClientAuthenticationType)temp;
+ switch(parsed_session_ticket->client_identity.client_auth_type) {
+ case CLIENT_AUTH_ANONYMOUS:
+ break;
+ case CLIENT_AUTH_CERTIFICATE:
+ rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3,
+ &buffer, &buffer_len);
+ if (rv != SECSuccess) goto no_ticket;
+ rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
+ &cert_item);
+ if (rv != SECSuccess) goto no_ticket;
+ break;
+ default:
+ goto no_ticket;
+ }
+ /* Read timestamp. */
+ temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
+ if (temp < 0)
+ goto no_ticket;
+ parsed_session_ticket->timestamp = (PRUint32)temp;
+
+ /* Done parsing. Check that all bytes have been consumed. */
+ if (buffer_len != padding_length)
+ goto no_ticket;
+
+ /* Use the ticket if it has not expired, otherwise free the allocated
+ * memory since the ticket is of no use.
+ */
+ if (parsed_session_ticket->timestamp != 0 &&
+ parsed_session_ticket->timestamp + TLS1_EX_SESS_TICKET_LIFETIME_HINT > ssl_Time()) {
+
+ sid = ssl3_NewSessionID(ss, PR_TRUE);
+ if (sid == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Copy over parameters. */
+ sid->version = parsed_session_ticket->ssl_version;
+ sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
+ sid->u.ssl3.compression = parsed_session_ticket->compression_method;
+ sid->authAlgorithm = parsed_session_ticket->authAlgorithm;
+ sid->authKeyBits = parsed_session_ticket->authKeyBits;
+ sid->keaType = parsed_session_ticket->keaType;
+ sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
+
+ /* Copy master secret. */
+ if (ss->opt.bypassPKCS11 &&
+ parsed_session_ticket->ms_is_wrapped)
+ goto no_ticket;
+ if (parsed_session_ticket->ms_length >
+ sizeof(sid->u.ssl3.keys.wrapped_master_secret))
+ goto no_ticket;
+ PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+ parsed_session_ticket->master_secret,
+ parsed_session_ticket->ms_length);
+ sid->u.ssl3.keys.wrapped_master_secret_len =
+ parsed_session_ticket->ms_length;
+ sid->u.ssl3.exchKeyType = parsed_session_ticket->exchKeyType;
+ sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
+ sid->u.ssl3.keys.msIsWrapped =
+ parsed_session_ticket->ms_is_wrapped;
+ sid->u.ssl3.masterValid = PR_TRUE;
+ sid->u.ssl3.keys.resumable = PR_TRUE;
+
+ /* Copy over client cert from session ticket if there is one. */
+ if (parsed_session_ticket->peer_cert.data != NULL) {
+ if (sid->peerCert != NULL)
+ CERT_DestroyCertificate(sid->peerCert);
+ sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
+ &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
+ if (sid->peerCert == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+ ss->ssl3.stateless_resume = PR_TRUE;
+ ss->sec.ci.sid = sid;
+ }
+ }
+
+ if (0) {
+no_ticket:
+ if (sid) {
+ ssl_FreeSID(sid);
+ sid = NULL;
+ }
+ }
+ rv = SECSuccess;
+
+loser:
+ if (decrypted_state != NULL) {
+ SECITEM_FreeItem(decrypted_state, PR_TRUE);
+ decrypted_state = NULL;
+ }
+
+ if (parsed_session_ticket != NULL) {
+ if (parsed_session_ticket->peer_cert.data) {
+ SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE);
+ }
+ PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket));
+ }
+
+ return rv;
+}
+
+/*
+ * Read bytes. Using this function means the SECItem structure
+ * cannot be freed. The caller is expected to call this function
+ * on a shallow copy of the structure.
+ */
+static SECStatus
+ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes)
+{
+ if (bytes > item->len)
+ return SECFailure;
+
+ *buf = item->data;
+ item->data += bytes;
+ item->len -= bytes;
+ return SECSuccess;
+}
+
+static SECStatus
+ssl3_ParseEncryptedSessionTicket(sslSocket *ss, SECItem *data,
+ EncryptedSessionTicket *enc_session_ticket)
+{
+ if (ssl3_ConsumeFromItem(data, &enc_session_ticket->key_name,
+ sizeof(TLS1_EX_SESS_TICKET_KEY_NAME)) != SECSuccess)
+ return SECFailure;
+ if (ssl3_ConsumeFromItem(data, &enc_session_ticket->iv,
+ AES_BLOCK_SIZE) != SECSuccess)
+ return SECFailure;
+ if (ssl3_ConsumeHandshakeVariable(ss, &enc_session_ticket->encrypted_state,
+ 2, &data->data, &data->len) != SECSuccess)
+ return SECFailure;
+ if (ssl3_ConsumeFromItem(data, &enc_session_ticket->mac,
+ TLS1_EX_SESS_TICKET_MAC_LENGTH) != SECSuccess)
+ return SECFailure;
+ if (data->len != 0) /* Make sure that we have consumed all bytes. */
+ return SECFailure;
+
+ return SECSuccess;
+}
+
+/* go through hello extensions in buffer "b".
+ * For each one, find the extension handler in the table above, and
+ * if present, invoke that handler.
+ * ignore any extensions with unknown extension types.
+ */
+SECStatus
+ssl3_HandleHelloExtensions(sslSocket *ss,
+ SSL3Opaque **b,
+ PRUint32 *length)
+{
+ const ssl3HelloExtensionHandler * handlers =
+ ss->sec.isServer ? server_handlers : client_handlers;
+
+ while (*length) {
+ SECStatus rv;
+ PRInt32 extension_type;
+ SECItem extension_data;
+ TLS1ExtensionData *ex_data = &ss->ssl3.extension_data;
+ const ssl3HelloExtensionHandler * handler;
+
+ /* Get the extension's type field */
+ extension_type = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
+ if (extension_type < 0) /* failure to decode extension_type */
+ return SECFailure; /* alert already sent */
+
+ /* get the data for this extension, so we can pass it or skip it. */
+ rv = ssl3_ConsumeHandshakeVariable(ss, &extension_data, 2, b, length);
+ if (rv != SECSuccess)
+ return rv;
+
+ /* Check whether the server sent an extension which was not advertised
+ * in the ClientHello.
+ */
+ if (!ss->sec.isServer &&
+ !ssl3_ClientExtensionAdvertised(ss, extension_type))
+ return SECFailure;
+
+ /* Check whether an extension has been sent multiple times. */
+ if (arrayContainsExtension(ex_data->negotiatedExtensions,
+ ex_data->numNegotiatedExtensions, extension_type))
+ return SECFailure;
+
+ /* find extension_type in table of Client Hello Extension Handlers */
+ for (handler = handlers; handler->ex_type >= 0; handler++) {
+ /* if found, Call this handler */
+ if (handler->ex_type == extension_type) {
+ rv = (*handler->ex_handler)(ss, (PRUint16)extension_type,
+ &extension_data);
+ /* Ignore this result */
+ /* Treat all bad extensions as unrecognized types. */
+ break;
+ }
+ }
+ }
+ return SECSuccess;
+}
+
+/* Add a callback function to the table of senders of server hello extensions.
+ */
+SECStatus
+ssl3_RegisterServerHelloExtensionSender(sslSocket *ss, PRUint16 ex_type,
+ ssl3HelloExtensionSenderFunc cb)
+{
+ int i;
+ ssl3HelloExtensionSender *sender =
+ &ss->ssl3.extension_data.serverExtensionSenders[0];
+
+ for (i = 0; i < MAX_EXTENSION_SENDERS; ++i, ++sender) {
+ if (!sender->ex_sender) {
+ sender->ex_type = ex_type;
+ sender->ex_sender = cb;
+ return SECSuccess;
+ }
+ /* detect duplicate senders */
+ PORT_Assert(sender->ex_type != ex_type);
+ if (sender->ex_type == ex_type) {
+ /* duplicate */
+ break;
+ }
+ }
+ PORT_Assert(i < MAX_EXTENSION_SENDERS); /* table needs to grow */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+}
+
+/* call each of the extension senders and return the accumulated length */
+PRInt32
+ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes,
+ const ssl3HelloExtensionSender *sender)
+{
+ PRInt32 total_exten_len = 0;
+ int i;
+
+ if (!sender)
+ sender = &clientHelloSenders[0];
+
+ for (i = 0; i < MAX_EXTENSION_SENDERS; ++i, ++sender) {
+ if (sender->ex_sender) {
+ PRInt32 extLen = (*sender->ex_sender)(ss, append, maxBytes);
+ if (extLen < 0)
+ return -1;
+ maxBytes -= extLen;
+ total_exten_len += extLen;
+ }
+ }
+ return total_exten_len;
+}
diff --git a/security/nss/lib/ssl/ssl3prot.h b/security/nss/lib/ssl/ssl3prot.h
index b57314005..83ab67e7d 100644
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -150,13 +150,14 @@ typedef enum {
hello_request = 0,
client_hello = 1,
server_hello = 2,
+ new_session_ticket = 4,
certificate = 11,
server_key_exchange = 12,
certificate_request = 13,
server_hello_done = 14,
certificate_verify = 15,
client_key_exchange = 16,
- finished = 20
+ finished = 20,
} SSL3HandshakeType;
typedef struct {
@@ -307,4 +308,49 @@ typedef struct {
SSL3Opaque verify_data[12];
} TLSFinished;
+/*
+ * TLS1 extension related data structures and constants.
+ */
+
+/* SessionTicket extension related data structures. */
+
+/* NewSessionTicket handshake message. */
+typedef struct {
+ uint32 received_timestamp;
+ uint32 ticket_lifetime_hint;
+ SECItem ticket;
+} NewSessionTicket;
+
+typedef enum {
+ CLIENT_AUTH_ANONYMOUS = 0,
+ CLIENT_AUTH_CERTIFICATE = 1
+} ClientAuthenticationType;
+
+typedef struct {
+ ClientAuthenticationType client_auth_type : 1;
+ union {
+ SSL3Opaque *certificate_list;
+ } identity;
+} ClientIdentity;
+
+typedef struct {
+ unsigned char *key_name;
+ unsigned char *iv;
+ SECItem encrypted_state;
+ unsigned char *mac;
+} EncryptedSessionTicket;
+
+/* Supported extensions. */
+typedef enum {
+ server_name_xtn = 0,
+#ifdef NSS_ENABLE_ECC
+ elliptic_curves_xtn = 10,
+ elliptic_point_formats_xtn = 11,
+#endif
+ session_ticket_xtn = 35
+} ExtensionType;
+
+#define TLS1_EX_SESS_TICKET_KEY_NAME "NSS_SESS_TICKET!"
+#define TLS1_EX_SESS_TICKET_MAC_LENGTH 32
+
#endif /* __ssl3proto_h_ */
diff --git a/security/nss/lib/ssl/sslerr.h b/security/nss/lib/ssl/sslerr.h
index c17014c24..3c38fbefa 100644
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -192,6 +192,9 @@ SSL_ERROR_UNRECOGNIZED_NAME_ALERT = (SSL_ERROR_BASE + 106),
SSL_ERROR_BAD_CERT_STATUS_RESPONSE_ALERT = (SSL_ERROR_BASE + 107),
SSL_ERROR_BAD_CERT_HASH_VALUE_ALERT = (SSL_ERROR_BASE + 108),
+SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET = (SSL_ERROR_BASE + 109),
+SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET = (SSL_ERROR_BASE + 110),
+
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h
index 2d5bf3427..19eab2ee9 100644
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -176,7 +176,7 @@ typedef enum { SSLAppOpRead = 0,
/* This makes the cert cache entry exactly 4k. */
#define SSL_MAX_CACHED_CERT_LEN 4060
-#define MAX_EXTENSION_SENDERS 3
+#define MAX_EXTENSION_SENDERS 8
#define NUM_MIXERS 9
@@ -336,6 +336,7 @@ typedef struct sslOptionsStr {
unsigned int noStepDown : 1; /* 15 */
unsigned int bypassPKCS11 : 1; /* 16 */
unsigned int noLocks : 1; /* 17 */
+ unsigned int enableSessionTicketExtension : 1; /* 18 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -631,6 +632,10 @@ struct sslSessionIDStr {
char masterValid;
char clAuthValid;
+ /* Session ticket if we have one, is sent as an extension in the
+ * ClientHello message. This field is used by clients.
+ */
+ NewSessionTicket session_ticket;
} ssl3;
} u;
};
@@ -693,10 +698,31 @@ typedef enum {
wait_server_key,
wait_cert_request,
wait_hello_done,
+ wait_new_session_ticket,
idle_handshake
} SSL3WaitState;
/*
+ * TLS1 Extension related constants and data structures.
+ */
+typedef struct TLS1ExtensionDataStr TLS1ExtensionData;
+typedef struct SessionTicketDataStr SessionTicketData;
+
+struct TLS1ExtensionDataStr {
+ /* registered callbacks that send server hello extensions */
+ ssl3HelloExtensionSender serverExtensionSenders[MAX_EXTENSION_SENDERS];
+ /* Keep track of the extensions that are negotiated. */
+ PRUint16 numAdvertisedClientExtensions;
+ PRUint16 numNegotiatedExtensions;
+ PRUint16 advertisedClientExtensions[MAX_EXTENSION_SENDERS];
+ PRUint16 negotiatedExtensions[MAX_EXTENSION_SENDERS];
+
+ /* SessionTicket Extension related data. */
+ PRBool ticket_timestamp_verified;
+ PRBool empty_session_ticket;
+};
+
+/*
** This is the "hs" member of the "ssl3" struct.
** This entire struct is protected by ssl3HandshakeLock
*/
@@ -771,6 +797,13 @@ struct ssl3StateStr {
PRBool initialized;
SSL3HandshakeState hs;
ssl3CipherSpec specs[2]; /* one is current, one is pending. */
+
+ /*
+ * TLS1 Extension related data.
+ */
+ /* True when the current session is a stateless resume. */
+ PRBool stateless_resume;
+ TLS1ExtensionData extension_data;
};
typedef struct {
@@ -799,14 +832,28 @@ typedef struct SSLWrappedSymWrappingKeyStr {
PRUint16 wrapIVLen;
} SSLWrappedSymWrappingKey;
-
-
-
-
-
-
-
-
+typedef struct SessionTicket {
+ uint16 ticket_version;
+ SSL3ProtocolVersion ssl_version;
+ ssl3CipherSuite cipher_suite;
+ SSL3CompressionMethod compression_method;
+ SSLSignType authAlgorithm;
+ uint32 authKeyBits;
+ SSLKEAType keaType;
+ uint32 keaKeyBits;
+ /*
+ * exchKeyType and msWrapMech contain meaningful values only if
+ * ms_is_wrapped is true.
+ */
+ uint8 ms_is_wrapped;
+ SSLKEAType exchKeyType; /* XXX(wtc): same as keaType above? */
+ CK_MECHANISM_TYPE msWrapMech;
+ uint16 ms_length;
+ SSL3Opaque master_secret[48];
+ ClientIdentity client_identity;
+ SECItem peer_cert;
+ uint32 timestamp;
+} SessionTicket;
/*
* SSL2 buffers used in SSL3.
@@ -965,9 +1012,6 @@ struct sslSocketStr {
sslHandshakeFunc nextHandshake; /*firstHandshakeLock*/
sslHandshakeFunc securityHandshake; /*firstHandshakeLock*/
- /* registered callbacks that send server hello extensions */
- ssl3HelloExtensionSender serverExtensionSenders[MAX_EXTENSION_SENDERS];
-
/* the following variable is only used with socks or other proxies. */
char * peerID; /* String uniquely identifies target server. */
@@ -1047,7 +1091,6 @@ const unsigned char * preferredCipher;
};
-
/* All the global data items declared here should be protected using the
** ssl_global_data_lock, which is a reader/writer lock.
*/
@@ -1156,6 +1199,7 @@ extern SECStatus sslBuffer_Append(sslBuffer *b, const void * data,
extern void ssl2_UseClearSendFunc(sslSocket *ss);
extern void ssl_ChooseSessionIDProcs(sslSecurityInfo *sec);
+extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server);
extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port,
const char *peerID, const char *urlSvrName);
extern void ssl_FreeSID(sslSessionID *sid);
@@ -1399,15 +1443,55 @@ extern SECStatus ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key,
extern SECStatus ssl3_VerifySignedHashes(SSL3Hashes *hash,
CERTCertificate *cert, SECItem *buf, PRBool isTLS,
void *pwArg);
-
-/* functions that append extensions to hello messages. */
-extern PRInt32 ssl3_SendServerNameIndicationExtension( sslSocket * ss,
- PRBool append, PRUint32 maxBytes);
+extern SECStatus ssl3_CacheWrappedMasterSecret(sslSocket *ss,
+ sslSessionID *sid, ssl3CipherSpec *spec,
+ SSL3KEAType effectiveExchKeyType);
+
+/* Functions that handle ClientHello and ServerHello extensions. */
+extern SECStatus ssl3_HandleServerNameExt(sslSocket * ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_HandleSupportedCurvesExt(sslSocket * ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_HandleSupportedPointExt(sslSocket * ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_ClientHandleSessionTicketExt(sslSocket *ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_ServerHandleSessionTicketExt(sslSocket *ss,
+ PRUint16 ex_type, SECItem *data);
+
+/* ClientHello and ServerHello extension senders.
+ * Note that not all extension senders are exposed here; only those that
+ * that need exposure.
+ */
+extern PRInt32 ssl3_SendSessionTicketExt(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
+#ifdef NSS_ENABLE_ECC
+extern PRInt32 ssl3_SendSupportedCurvesExt(sslSocket *ss,
+ PRBool append, PRUint32 maxBytes);
+extern PRInt32 ssl3_SendSupportedPointExt(sslSocket *ss,
+ PRBool append, PRUint32 maxBytes);
+#endif
/* call the registered extension handlers. */
-extern SECStatus ssl3_HandleClientHelloExtensions(sslSocket *ss,
+extern SECStatus ssl3_HandleHelloExtensions(sslSocket *ss,
SSL3Opaque **b, PRUint32 *length);
+/* Hello Extension related routines. */
+extern PRBool ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type);
+extern PRBool ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type);
+extern SECStatus ssl3_GetSessionTicketKeysPKCS11(PK11SymKey **aes_key,
+ PK11SymKey **mac_key);
+extern SECStatus ssl3_GetSessionTicketKeys(const unsigned char **aes_key,
+ PRUint32 *aes_key_length, const unsigned char **mac_key,
+ PRUint32 *mac_key_length);
+extern SECStatus ssl3_SetSIDSessionTicket(sslSessionID *sid,
+ NewSessionTicket *session_ticket);
+extern SECStatus ssl3_SendNewSessionTicket(sslSocket *ss);
+
+/* Tell clients to consider tickets valid for this long. */
+#define TLS1_EX_SESS_TICKET_LIFETIME_HINT (2 * 24 * 60 * 60) /* 2 days */
+#define TLS1_EX_SESS_TICKET_VERSION (0x0100)
+
/* Construct a new NSPR socket for the app to use */
extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd);
extern void ssl_FreePRSocket(PRFileDesc *fd);
diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c
index e2dbcf0a5..c674e671c 100644
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -47,6 +47,9 @@
#include "sslproto.h"
#include "nssilock.h"
#include "nsslocks.h"
+
+#include "pk11func.h"
+
#if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
#include <time.h>
#endif
@@ -109,7 +112,10 @@ ssl_DestroySID(sslSessionID *sid)
if ( sid->localCert ) {
CERT_DestroyCertificate(sid->localCert);
}
-
+ if (sid->u.ssl3.session_ticket.ticket.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.session_ticket.ticket, PR_FALSE);
+ }
+
PORT_ZFree(sid, sizeof(sslSessionID));
}
@@ -245,8 +251,18 @@ CacheSID(sslSessionID *sid)
PRINT_BUF(8, (0, "cipherArg:",
sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
} else {
- if (sid->u.ssl3.sessionIDLength == 0)
+ if (sid->u.ssl3.sessionIDLength == 0 &&
+ sid->u.ssl3.session_ticket.ticket.data == NULL)
return;
+ /* Client generates the SessionID if this was a stateless resume. */
+ if (sid->u.ssl3.sessionIDLength == 0) {
+ SECStatus rv;
+ rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
+ SSL3_SESSIONID_BYTES);
+ if (rv != SECSuccess)
+ return;
+ sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
+ }
expirationPeriod = ssl3_sid_timeout;
PRINT_BUF(8, (0, "sessionID:",
sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
@@ -373,3 +389,35 @@ ssl_Time(void)
return myTime;
}
+SECStatus
+ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
+{
+ SECStatus rv;
+
+ /* We need to lock the cache, as this sid might already be in the cache. */
+ LOCK_CACHE;
+
+ /* A server might have sent us an empty ticket, which has the
+ * effect of clearing the previously known ticket.
+ */
+ if (sid->u.ssl3.session_ticket.ticket.data)
+ SECITEM_FreeItem(&sid->u.ssl3.session_ticket.ticket, PR_FALSE);
+ if (session_ticket->ticket.len > 0) {
+ rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.session_ticket.ticket,
+ &session_ticket->ticket);
+ if (rv != SECSuccess) {
+ UNLOCK_CACHE;
+ return rv;
+ }
+ } else {
+ sid->u.ssl3.session_ticket.ticket.data = NULL;
+ sid->u.ssl3.session_ticket.ticket.len = 0;
+ }
+ sid->u.ssl3.session_ticket.received_timestamp =
+ session_ticket->received_timestamp;
+ sid->u.ssl3.session_ticket.ticket_lifetime_hint =
+ session_ticket->ticket_lifetime_hint;
+
+ UNLOCK_CACHE;
+ return SECSuccess;
+}
diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
index f51cd2446..4354eece6 100644
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -178,6 +178,7 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* noStepDown */
PR_FALSE, /* bypassPKCS11 */
PR_FALSE, /* noLocks */
+ PR_FALSE, /* enableSessionTicketExtension */
};
sslSessionIDLookupFunc ssl_sid_lookup;
@@ -699,6 +700,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
}
break;
+ case SSL_ENABLE_SESSION_TICKET_EXTENSION:
+ ss->opt.enableSessionTicketExtension = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@@ -754,7 +759,9 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
case SSL_NO_STEP_DOWN: on = ss->opt.noStepDown; break;
case SSL_BYPASS_PKCS11: on = ss->opt.bypassPKCS11; break;
case SSL_NO_LOCKS: on = ss->opt.noLocks; break;
-
+ case SSL_ENABLE_SESSION_TICKET_EXTENSION:
+ on = ss->opt.enableSessionTicketExtension;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@@ -795,6 +802,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
case SSL_NO_STEP_DOWN: on = ssl_defaults.noStepDown; break;
case SSL_BYPASS_PKCS11: on = ssl_defaults.bypassPKCS11; break;
case SSL_NO_LOCKS: on = ssl_defaults.noLocks; break;
+ case SSL_ENABLE_SESSION_TICKET_EXTENSION:
+ on = ssl_defaults.enableSessionTicketExtension;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -922,6 +932,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
}
break;
+ case SSL_ENABLE_SESSION_TICKET_EXTENSION:
+ ssl_defaults.enableSessionTicketExtension = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
@@ -2176,4 +2190,3 @@ loser:
}
return ss;
}
-
diff --git a/security/nss/lib/ssl/sslt.h b/security/nss/lib/ssl/sslt.h
index 0fefa5782..65ae1859a 100644
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -59,6 +59,11 @@ typedef struct SSL3StatisticsStr {
long hch_sid_cache_hits;
long hch_sid_cache_misses;
long hch_sid_cache_not_ok;
+
+ /* statistics related to stateless resume */
+ long sch_sid_stateless_resumes;
+ long hsh_sid_stateless_resumes;
+ long hch_sid_stateless_resumes;
} SSL3Statistics;
/* Key Exchange algorithm values */
diff --git a/security/nss/tests/ssl/sslstress.txt b/security/nss/tests/ssl/sslstress.txt
index 97f67c207..4a1a211ee 100644
--- a/security/nss/tests/ssl/sslstress.txt
+++ b/security/nss/tests/ssl/sslstress.txt
@@ -8,13 +8,15 @@
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 -u -2_-c_1000_-C_c_-u Stress TLS RC4 128 with MD5 (session ticket)
#
# add client auth versions here...
#
- noECC 0 -r_-r -c_100_-C_A_-N_-n_TestUser Stress SSL2 RC4 128 with MD5 (client auth)
- noECC 0 -r_-r -c_100_-C_c_-T_-N_-n_TestUser Stress SSL3 RC4 128 with MD5 (client auth)
- noECC 0 -r_-r -c_100_-C_c_-N_-n_TestUser Stress TLS RC4 128 with MD5 (client auth)
+ noECC 0 -r_-r -c_100_-C_A_-N_-n_TestUser Stress SSL2 RC4 128 with MD5 (no reuse, client auth)
+ noECC 0 -r_-r -c_100_-C_c_-T_-N_-n_TestUser Stress SSL3 RC4 128 with MD5 (no reuse, client auth)
+ 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)
#
# ############################ ECC ciphers ############################
@@ -24,6 +26,7 @@
ECC 0 -c_:C004 -2_-c_100_-C_:C004_-N Stress TLS ECDH-ECDSA AES 128 CBC with SHA (no reuse)
ECC 0 -c_:C00E -2_-c_100_-C_:C00E_-N Stress TLS ECDH-RSA AES 128 CBC with SHA (no reuse)
ECC 0 -c_:C013 -2_-c_1000_-C_:C013 Stress TLS ECDHE-RSA AES 128 CBC with SHA
+ ECC 0 -c_:C004_-u -2_-c_1000_-C_:C004_-u Stress TLS ECDH-ECDSA AES 128 CBC with SHA (session ticket)
#
# add client auth versions here...
#
@@ -32,3 +35,4 @@
ECC 0 -r_-r_-c_:C004 -c_10_-C_:C004_-N_-n_TestUser-ec Stress TLS ECDH-ECDSA AES 128 CBC with SHA (no reuse, client auth)
ECC 0 -r_-r_-c_:C00E -c_10_-C_:C00E_-N_-n_TestUser-ecmixed Stress TLS ECDH-RSA AES 128 CBC with SHA (no reuse, client auth)
ECC 0 -r_-r_-c_:C013 -c_100_-C_:C013_-n_TestUser-ec Stress TLS ECDHE-RSA AES 128 CBC with SHA(client auth)
+ ECC 0 -r_-r_-c_:C013_-u -2_-c_100_-C_:C013_-n_TestUser-ec_-u Stress TLS ECDHE-RSA AES 128 CBC with SHA(session ticket, client auth)