summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwtc%google.com <devnull@localhost>2008-03-06 20:16:24 +0000
committerwtc%google.com <devnull@localhost>2008-03-06 20:16:24 +0000
commitb3060348b18a759fa4a497e627ff63116ae9f449 (patch)
tree24c2b65d6e66e4591d041e949d3b7df8bbb086dc
parent95b8f2c393895ceca4a5a3aaf6528668490b4720 (diff)
downloadnss-hg-b3060348b18a759fa4a497e627ff63116ae9f449.tar.gz
Bug 403563: implement the TLS session ticket extension (rfc4507bis). The
patch is contributed by Nagendra Modadugu <ngm+mozilla@google.com>. A small portion (PKCS #11 code and tests) was written by Wan-Teh Chang <wtc@google.com>. r=nelson,wtc Modified Files: cmd/lib/SSLerrs.h 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/sslsnce.c lib/ssl/sslsock.c lib/ssl/sslt.h tests/ssl/sslstress.txt Added Files: lib/ssl/ssl3ext.c
-rw-r--r--security/nss/cmd/lib/SSLerrs.h6
-rw-r--r--security/nss/cmd/selfserv/selfserv.c40
-rw-r--r--security/nss/cmd/strsclnt/strsclnt.c56
-rw-r--r--security/nss/cmd/tstclnt/tstclnt.c28
-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.c265
-rw-r--r--security/nss/lib/ssl/ssl3ecc.c205
-rw-r--r--security/nss/lib/ssl/ssl3ext.c1267
-rw-r--r--security/nss/lib/ssl/ssl3prot.h53
-rw-r--r--security/nss/lib/ssl/sslerr.h3
-rw-r--r--security/nss/lib/ssl/sslimpl.h118
-rw-r--r--security/nss/lib/ssl/sslnonce.c48
-rw-r--r--security/nss/lib/ssl/sslsnce.c236
-rw-r--r--security/nss/lib/ssl/sslsock.c15
-rw-r--r--security/nss/lib/ssl/sslt.h6
-rw-r--r--security/nss/tests/ssl/sslstress.txt10
17 files changed, 2068 insertions, 291 deletions
diff --git a/security/nss/cmd/lib/SSLerrs.h b/security/nss/cmd/lib/SSLerrs.h
index 376ee8399..023524929 100644
--- a/security/nss/cmd/lib/SSLerrs.h
+++ b/security/nss/cmd/lib/SSLerrs.h
@@ -384,3 +384,9 @@ ER3(SSL_ERROR_BAD_CERT_STATUS_RESPONSE_ALERT , (SSL_ERROR_BASE + 107),
ER3(SSL_ERROR_BAD_CERT_HASH_VALUE_ALERT , (SSL_ERROR_BASE + 108),
"SSL peer reported bad certificate hash value.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET, (SSL_ERROR_BASE + 109),
+"SSL received an unexpected New Session Ticket handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET, (SSL_ERROR_BASE + 110),
+"SSL received a malformed New Session Ticket handshake message.")
diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c
index af4bc752c..1ea1cd499 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 TLS.\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,29 @@ 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, %ld ticket parse failures\n",
+ ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
+ ssl3stats->hch_sid_cache_not_ok, ssl3stats->hch_sid_stateless_resumes,
+ ssl3stats->hch_sid_ticket_parse_failures);
+}
+
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 &&
@@ -660,6 +671,7 @@ PRBool disableStepDown = PR_FALSE;
PRBool bypassPKCS11 = PR_FALSE;
PRBool disableLocking = PR_FALSE;
PRBool testbypass = PR_FALSE;
+PRBool enableSessionTickets = PR_FALSE;
static const char stopCmd[] = { "GET /stop " };
static const char getCmd[] = { "GET " };
@@ -1447,6 +1459,12 @@ server_main(
errExit("error disabling SSL socket locking ");
}
}
+ if (enableSessionTickets) {
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
+ if (rv != SECSuccess) {
+ errExit("error enabling Session Ticket extension ");
+ }
+ }
for (kea = kt_rsa; kea < kt_kea_size; kea++) {
if (cert[kea] != NULL) {
@@ -1693,6 +1711,7 @@ main(int argc, char **argv)
char emptyString[] = { "" };
char* certPrefix = emptyString;
PRUint32 protos = 0;
+ SSL3Statistics *ssl3stats;
tmp = strrchr(argv[0], '/');
tmp = tmp ? tmp + 1 : argv[0];
@@ -1705,7 +1724,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) {
@@ -1784,6 +1803,8 @@ main(int argc, char **argv)
if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS;
break;
+ case 'u': enableSessionTickets = PR_TRUE; break;
+
case 'v': verbose++; break;
case 'w': passwd = PORT_Strdup(optstate->value); break;
@@ -2093,6 +2114,13 @@ main(int argc, char **argv)
VLOG(("selfserv: server_thread: exiting"));
cleanup:
+ printSSLStatistics();
+ ssl3stats = SSL_GetStatistics();
+ if (ssl3stats->hch_sid_ticket_parse_failures != 0) {
+ fprintf(stderr, "selfserv: Experienced ticket parse failure(s)\n");
+ exit(1);
+ }
+
{
int i;
for (i=0; i<kt_kea_size; i++) {
diff --git a/security/nss/cmd/strsclnt/strsclnt.c b/security/nss/cmd/strsclnt/strsclnt.c
index 97e1b1ae9..bc7232ea3 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 enableSessionTickets = 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);
}
@@ -1233,6 +1237,12 @@ client_main(
}
}
+ if (enableSessionTickets) {
+ rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
+ if (rv != SECSuccess)
+ errExit("SSL_OptionSet SSL_ENABLE_SESSION_TICKETS");
+ }
+
SSL_SetURL(model_sock, hostName);
SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate,
@@ -1338,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) {
@@ -1384,7 +1394,9 @@ main(int argc, char **argv)
max_threads = active_threads = tmpInt;
break;
- case 'v': verbose++; break;
+ case 'u': enableSessionTickets = PR_TRUE; break;
+
+ case 'v': verbose++; break;
case 'w': passwd = PL_strdup(optstate->value); break;
@@ -1481,29 +1493,41 @@ 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) ||
- (ssl3stats->hsh_sid_cache_not_ok != 0) ||
- (certsTested > 1);
- else {
+ if (!NoReuse) {
+ if (enableSessionTickets)
+ exitVal = (ssl3stats->hsh_sid_stateless_resumes == 0);
+ else
+ exitVal = (ssl3stats->hsh_sid_cache_misses > 1) ||
+ (ssl3stats->hsh_sid_stateless_resumes != 0);
+ if (!exitVal)
+ exitVal = (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);
diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c
index 9c763bd89..6377c0236 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;
@@ -182,22 +183,26 @@ void printSecurityInfo(PRFileDesc *fd)
cert = NULL;
}
fprintf(stderr,
- "%ld cache hits; %ld cache misses, %ld cache not reusable\n",
+ "%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);
}
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 +223,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,
@@ -512,6 +519,7 @@ int main(int argc, char **argv)
int bypassPKCS11 = 0;
int disableLocking = 0;
int useExportPolicy = 0;
+ int enableSessionTickets = 0;
PRSocketOptionData opt;
PRNetAddr addr;
PRPollDesc pollset[2];
@@ -540,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 '?':
@@ -580,8 +588,12 @@ int main(int argc, char **argv)
case 's': disableLocking = 1; break;
+ case 'u': enableSessionTickets = PR_TRUE; break;
+
case 'v': verbose++; break;
+ case 'r': renegotiate = atoi(optstate->value); break;
+
case 'w':
password = PORT_Strdup(optstate->value);
useCommandLinePassword = PR_TRUE;
@@ -824,6 +836,12 @@ int main(int argc, char **argv)
return 1;
}
+ /* enable Session Ticket extension. */
+ rv = SSL_OptionSet(s, SSL_ENABLE_SESSION_TICKETS, enableSessionTickets);
+ 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 2be497569..aca8cf8b4 100644
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -113,7 +113,7 @@ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd);
#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_TICKETS 18 /* Enable TLS SessionTicket */
- /* extension (not implemented) */
+ /* 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 b06bd306f..0197b3bb3 100644
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -77,7 +77,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);
@@ -529,7 +528,7 @@ typedef struct tooLongStr {
#endif
} tooLong;
-static void SSL_AtomicIncrementLong(long * x)
+void SSL_AtomicIncrementLong(long * x)
{
if ((sizeof *x) == sizeof(PRInt32)) {
PR_AtomicIncrement((PRInt32 *)x);
@@ -3501,6 +3500,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->xtnData, 0, sizeof(TLSExtensionData));
+
SSL_TRC(30,("%d: SSL3[%d]: reset handshake hashes",
SSL_GETPID(), ss->fd ));
rv = ssl3_RestartHandshakeHashes(ss);
@@ -3566,6 +3570,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.sessionTicket.ticket.data)
+ SSL_AtomicIncrementLong(& ssl3stats.sch_sid_stateless_resumes );
+
rv = ssl3_NegotiateVersion(ss, sid->version);
if (rv != SECSuccess)
return rv; /* error code was set */
@@ -4652,11 +4661,21 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
ss->ssl3.hs.compression = (SSL3CompressionMethod)temp;
-#ifdef DISALLOW_SERVER_HELLO_EXTENSIONS
- if (length != 0) { /* malformed */
- goto alert_loser;
+ /* Note that if !isTLS && length != 0, we do NOT goto alert_loser.
+ * There are some old SSL 3.0 implementations that do send stuff
+ * after the end of the server hello, and we deliberately ignore
+ * such stuff in the interest of maximal interoperability (being
+ * "generous in what you accept").
+ */
+ 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;
}
-#endif
/* Any errors after this point are not "malformed" errors. */
desc = handshake_failure;
@@ -4764,7 +4783,17 @@ 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.sessionTicket.ticket.data != NULL)
+ SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes );
+
+ if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn))
+ ss->ssl3.hs.ws = wait_new_session_ticket;
+ else
+ ss->ssl3.hs.ws = wait_change_cipher;
+
ss->ssl3.hs.isResuming = PR_TRUE;
/* copy the peer cert from the SID */
@@ -5359,7 +5388,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:
@@ -5400,7 +5432,7 @@ ssl3_SendHelloRequest(sslSocket *ss)
* ssl3_HandleClientHello()
* ssl3_HandleV2ClientHello()
*/
-static sslSessionID *
+sslSessionID *
ssl3_NewSessionID(sslSocket *ss, PRBool is_server)
{
sslSessionID *sid;
@@ -5549,8 +5581,12 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
return rv; /* error code is set. */
}
- memset(&ss->serverExtensionSenders[0], 0,
- sizeof ss->serverExtensionSenders);
+ /* We might be starting session renegotiation in which case we should
+ * clear previous state.
+ */
+ PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
+ ss->statelessResume = PR_FALSE;
+
rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
return rv; /* ssl3_InitState has set the error code. */
@@ -5587,21 +5623,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) {
@@ -5616,6 +5637,84 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
desc = handshake_failure;
+ /* Handle TLS hello extensions for SSL3 & TLS. We do not know if
+ * we are restarting a previous session until extensions have been
+ * parsed, since we might have received a SessionTicket extension.
+ * Note: we allow extensions even when negotiating SSL3 for the sake
+ * of interoperability (and backwards compatibility).
+ */
+
+ 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_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->xtnData.emptySessionTicket) {
+ 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->statelessResume) {
+ /* 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_SendSessionTicketXtn);
+ }
+
if (sid != NULL) {
/* We've found a session cache entry for this client.
* Now, if we're going to require a client-auth cert,
@@ -5691,26 +5790,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);
@@ -5855,6 +5934,8 @@ compression_found:
* XXX make sure compression still matches
*/
SSL_AtomicIncrementLong(& ssl3stats.hch_sid_cache_hits );
+ if (ss->statelessResume)
+ SSL_AtomicIncrementLong(& ssl3stats.hch_sid_stateless_resumes );
ss->ssl3.hs.isResuming = PR_TRUE;
ss->sec.authAlgorithm = sid->authAlgorithm;
@@ -5995,8 +6076,7 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, int length)
ssl_GetSSL3HandshakeLock(ss);
- memset(&ss->serverExtensionSenders[0], 0,
- sizeof ss->serverExtensionSenders);
+ PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
@@ -6162,12 +6242,12 @@ ssl3_SendServerHello(sslSocket *ss)
sid = ss->sec.ci.sid;
extensions_len = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes,
- &ss->serverExtensionSenders[0]);
+ &ss->xtnData.serverSenders[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) {
@@ -6214,7 +6294,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->xtnData.serverSenders[0]);
PORT_Assert(sent_len == extensions_len);
if (sent_len != extensions_len) {
if (sent_len >= 0)
@@ -6804,6 +6884,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;
@@ -7459,17 +7584,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;
@@ -7531,7 +7656,7 @@ ssl3_CacheWrappedMasterSecret(sslSocket *ss, SSL3KEAType effectiveExchKeyType)
wmsItem.data = sid->u.ssl3.keys.wrapped_master_secret;
wmsItem.len = sizeof sid->u.ssl3.keys.wrapped_master_secret;
rv = PK11_WrapSymKey(mechanism, NULL, wrappingKey,
- ss->ssl3.crSpec->master_secret, &wmsItem);
+ spec->master_secret, &wmsItem);
/* rv is examined below. */
sid->u.ssl3.keys.wrapped_master_secret_len = wmsItem.len;
PK11_FreeSymKey(wrappingKey);
@@ -7605,6 +7730,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. */
@@ -7679,7 +7818,9 @@ 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); /*************************************/
@@ -7842,6 +7983,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;
@@ -8271,6 +8420,8 @@ ssl3_InitState(sslSocket *ss)
#endif
ssl_ReleaseSpecWriteLock(ss);
+ PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
+
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 d404d4f9a..38144df8b 100644
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -462,7 +462,6 @@ ssl3_GetCurveNameForServerSocket(sslSocket *ss)
ECName ec_curve = ec_noName;
int signatureKeyStrength = 521;
int requiredECCbits = ss->sec.secretKeyBits * 2;
- int i;
if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa) {
svrPublicKey = SSL_GET_SERVER_PUBLIC_KEY(ss, kt_ecdh);
@@ -1047,7 +1046,7 @@ static const PRUint8 ECPtFmt[6] = {
* which says that we support all TLS-defined named curves.
*/
PRInt32
-ssl3_SendSupportedEllipticCurvesExtension(
+ssl3_SendSupportedCurvesXtn(
sslSocket * ss,
PRBool append,
PRUint32 maxBytes)
@@ -1056,6 +1055,13 @@ 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) {
+ TLSExtensionData *xtnData = &ss->xtnData;
+ xtnData->advertised[xtnData->numAdvertised++] =
+ elliptic_curves_xtn;
+ }
}
return (sizeof EClist);
}
@@ -1064,7 +1070,7 @@ ssl3_SendSupportedEllipticCurvesExtension(
* which says that we only support uncompressed points.
*/
PRInt32
-ssl3_SendSupportedPointFormatsExtension(
+ssl3_SendSupportedPointFormatsXtn(
sslSocket * ss,
PRBool append,
PRUint32 maxBytes)
@@ -1073,6 +1079,13 @@ 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) {
+ TLSExtensionData *xtnData = &ss->xtnData;
+ xtnData->advertised[xtnData->numAdvertised++] =
+ ec_point_formats_xtn;
+ }
}
return (sizeof ECPtFmt);
}
@@ -1080,9 +1093,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_HandleSupportedPointFormatsXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
{
int i;
@@ -1096,7 +1109,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_SendSupportedPointFormatsXtn);
return rv;
}
}
@@ -1127,9 +1140,8 @@ 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_HandleSupportedCurvesXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
{
PRInt32 list_len;
PRUint32 peerCurves = 0;
@@ -1180,176 +1192,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..0857e5186
--- /dev/null
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -0,0 +1,1267 @@
+/*
+ * 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 ssl3ecc.c */
+/* $Id$ */
+
+#include "nssrenam.h"
+#include "nss.h"
+#include "ssl.h"
+#include "sslimpl.h"
+#include "pk11pub.h"
+#include "blapi.h"
+#include "prinit.h"
+
+static unsigned char key_name[SESS_TICKET_KEY_NAME_LEN];
+static PK11SymKey *session_ticket_enc_key_pkcs11 = NULL;
+static PK11SymKey *session_ticket_mac_key_pkcs11 = NULL;
+
+static unsigned char session_ticket_enc_key[32];
+static unsigned char session_ticket_mac_key[SHA256_LENGTH];
+
+static PRBool session_ticket_keys_initialized = PR_FALSE;
+static PRCallOnceType generate_session_keys_once;
+
+static PRInt32 ssl3_SendServerNameXtn(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);
+static SECStatus ssl3_GetSessionTicketKeysPKCS11(sslSocket *ss,
+ PK11SymKey **aes_key, PK11SymKey **mac_key);
+static SECStatus ssl3_GetSessionTicketKeys(const unsigned char **aes_key,
+ PRUint32 *aes_key_length, const unsigned char **mac_key,
+ PRUint32 *mac_key_length);
+
+/*
+ * 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++ = (uint8) (num >> 24);
+ case 3:
+ *p++ = (uint8) (num >> 16);
+ case 2:
+ *p++ = (uint8) (num >> 8);
+ case 1:
+ *p = (uint8) num;
+ }
+ 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;
+ }
+ PORT_Memset(&generate_session_keys_once, 0,
+ sizeof(generate_session_keys_once));
+ return SECSuccess;
+}
+
+
+static PRStatus
+ssl3_GenerateSessionTicketKeysPKCS11(void *data)
+{
+ SECStatus rv;
+ sslSocket *ss = (sslSocket *)data;
+ SECKEYPrivateKey *svrPrivKey = ss->serverCerts[kt_rsa].SERVERKEY;
+ SECKEYPublicKey *svrPubKey = ss->serverCerts[kt_rsa].serverKeyPair->pubKey;
+
+ if (svrPrivKey == NULL || svrPubKey == NULL) {
+ SSL_DBG(("%d: SSL[%d]: Pub or priv key(s) is NULL.",
+ SSL_GETPID(), ss->fd));
+ goto loser;
+ }
+
+ /* Get a copy of the session keys from shared memory. */
+ PORT_Memcpy(key_name, SESS_TICKET_KEY_NAME_PREFIX,
+ sizeof(SESS_TICKET_KEY_NAME_PREFIX));
+ if (!ssl_GetSessionTicketKeysPKCS11(svrPrivKey, svrPubKey,
+ ss->pkcs11PinArg, &key_name[SESS_TICKET_KEY_NAME_PREFIX_LEN],
+ &session_ticket_enc_key_pkcs11, &session_ticket_mac_key_pkcs11))
+ return PR_FAILURE;
+
+ rv = NSS_RegisterShutdown(ssl3_SessionTicketShutdown, NULL);
+ if (rv != SECSuccess)
+ goto loser;
+
+ return PR_SUCCESS;
+
+loser:
+ ssl3_SessionTicketShutdown(NULL, NULL);
+ return PR_FAILURE;
+}
+
+static SECStatus
+ssl3_GetSessionTicketKeysPKCS11(sslSocket *ss, PK11SymKey **aes_key,
+ PK11SymKey **mac_key)
+{
+ if (PR_CallOnceWithArg(&generate_session_keys_once,
+ ssl3_GenerateSessionTicketKeysPKCS11, ss) != PR_SUCCESS)
+ return SECFailure;
+
+ if (session_ticket_enc_key_pkcs11 == NULL ||
+ session_ticket_mac_key_pkcs11 == NULL)
+ return SECFailure;
+
+ *aes_key = session_ticket_enc_key_pkcs11;
+ *mac_key = session_ticket_mac_key_pkcs11;
+ return SECSuccess;
+}
+
+static PRStatus
+ssl3_GenerateSessionTicketKeys(void)
+{
+ PORT_Memcpy(key_name, SESS_TICKET_KEY_NAME_PREFIX,
+ sizeof(SESS_TICKET_KEY_NAME_PREFIX));
+
+ if (!ssl_GetSessionTicketKeys(&key_name[SESS_TICKET_KEY_NAME_PREFIX_LEN],
+ session_ticket_enc_key, session_ticket_mac_key))
+ return PR_FAILURE;
+
+ session_ticket_keys_initialized = PR_TRUE;
+ return PR_SUCCESS;
+}
+
+static SECStatus
+ssl3_GetSessionTicketKeys(const unsigned char **aes_key,
+ PRUint32 *aes_key_length, const unsigned char **mac_key,
+ PRUint32 *mac_key_length)
+{
+ if (PR_CallOnce(&generate_session_keys_once,
+ ssl3_GenerateSessionTicketKeys) != SECSuccess)
+ return SECFailure;
+
+ if (!session_ticket_keys_initialized)
+ 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 clientHelloHandlers[] = {
+ { server_name_xtn, &ssl3_HandleServerNameXtn },
+#ifdef NSS_ENABLE_ECC
+ { elliptic_curves_xtn, &ssl3_HandleSupportedCurvesXtn },
+ { ec_point_formats_xtn, &ssl3_HandleSupportedPointFormatsXtn },
+#endif
+ { session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn },
+ { -1, NULL }
+};
+
+static const ssl3HelloExtensionHandler serverHelloHandlers[] = {
+ { server_name_xtn, &ssl3_HandleServerNameXtn },
+ /* TODO: add a handler for ec_point_formats_xtn */
+ { session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn },
+ { -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_EXTENSIONS] = {
+ { server_name_xtn, &ssl3_SendServerNameXtn },
+#ifdef NSS_ENABLE_ECC
+ { elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
+ { ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
+#else
+ { -1, NULL },
+ { -1, NULL },
+#endif
+ { session_ticket_xtn, ssl3_SendSessionTicketXtn }
+};
+
+static PRBool
+arrayContainsExtension(const PRUint16 *array, PRUint32 len, PRUint16 ex_type)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (ex_type == array[i])
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRBool
+ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type) {
+ TLSExtensionData *xtnData = &ss->xtnData;
+ return arrayContainsExtension(xtnData->negotiated,
+ xtnData->numNegotiated, ex_type);
+}
+
+static PRBool
+ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type) {
+ TLSExtensionData *xtnData = &ss->xtnData;
+ return arrayContainsExtension(xtnData->advertised,
+ xtnData->numAdvertised, 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_SendServerNameXtn(
+ 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) {
+ TLSExtensionData *xtnData = &ss->xtnData;
+ xtnData->advertised[xtnData->numAdvertised++] = server_name_xtn;
+ }
+ }
+ return len + 9;
+}
+
+/* handle an incoming SNI extension, by ignoring it. */
+SECStatus
+ssl3_HandleServerNameXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
+{
+ /* TODO: if client, should verify extension_data is empty. */
+ /* TODO: if server, should send empty extension_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_SendSessionTicketXtn(
+ sslSocket * ss,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ PRInt32 extension_length;
+ NewSessionTicket *session_ticket = NULL;
+
+ /* Ignore the SessionTicket extension if processing is disabled. */
+ if (!ss->opt.enableSessionTickets)
+ return 0;
+
+ /* Empty extension length = extension_type (2-bytes) +
+ * length(extension_data) (2-bytes)
+ */
+ 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) {
+ sslSessionID *sid = ss->sec.ci.sid;
+ session_ticket = &sid->u.ssl3.sessionTicket;
+ if (session_ticket->ticket.data) {
+ if (ss->xtnData.ticketTimestampVerified) {
+ 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->xtnData.ticketTimestampVerified = 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->xtnData.ticketTimestampVerified) {
+ rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data,
+ session_ticket->ticket.len, 2);
+ ss->xtnData.ticketTimestampVerified = PR_FALSE;
+ } else {
+ rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+ }
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (!ss->sec.isServer) {
+ TLSExtensionData *xtnData = &ss->xtnData;
+ xtnData->advertised[xtnData->numAdvertised++] = session_ticket_xtn;
+ }
+ } else if (maxBytes < extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+ return extension_length;
+
+ loser:
+ ss->xtnData.ticketTimestampVerified = 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[TLS_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 = TLS_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(ss, &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 */
+ + SESS_TICKET_KEY_NAME_LEN /* key_name */
+ + AES_BLOCK_SIZE /* iv */
+ + 2 /* length field for NewSessionTicket.ticket.encrypted_state */
+ + ciphertext_length /* encrypted_state */
+ + TLS_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, TLS_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,
+ (int *)&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, key_name, SESS_TICKET_KEY_NAME_LEN);
+ 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, key_name,
+ SESS_TICKET_KEY_NAME_LEN);
+ 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, key_name, SESS_TICKET_KEY_NAME_LEN);
+ 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_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ if (data->len != 0)
+ return SECFailure;
+
+ /* Keep track of negotiated extensions. */
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+ return SECSuccess;
+}
+
+SECStatus
+ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ SECStatus rv;
+ SECItem *decrypted_state = NULL;
+ SessionTicket *parsed_session_ticket = NULL;
+ sslSessionID *sid = NULL;
+ SSL3Statistics *ssl3stats;
+
+ /* Ignore the SessionTicket extension if processing is disabled. */
+ if (!ss->opt.enableSessionTickets)
+ return SECSuccess;
+
+ /* Keep track of negotiated extensions. */
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = 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->xtnData.emptySessionTicket = PR_TRUE;
+ } else {
+ int i;
+ SECItem extension_data;
+ EncryptedSessionTicket enc_session_ticket;
+ unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
+ unsigned int computed_mac_length;
+ const SECHashObject *hashObj;
+ const unsigned char *aes_key;
+ const unsigned char *mac_key;
+ PK11SymKey *aes_key_pkcs11;
+ PK11SymKey *mac_key_pkcs11;
+ PRUint32 aes_key_length;
+ PRUint32 mac_key_length;
+ 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;
+
+ /* Get session ticket keys. */
+ if (ss->opt.bypassPKCS11) {
+ rv = ssl3_GetSessionTicketKeys(&aes_key, &aes_key_length,
+ &mac_key, &mac_key_length);
+ } else {
+ rv = ssl3_GetSessionTicketKeysPKCS11(ss, &aes_key_pkcs11,
+ &mac_key_pkcs11);
+ }
+ if (rv != SECSuccess) {
+ SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
+ SSL_GETPID(), ss->fd));
+ goto loser;
+ }
+
+ /* If the ticket sent by the client was generated under a key different
+ * from the one we have, bypass ticket processing.
+ */
+ if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
+ SESS_TICKET_KEY_NAME_LEN) != 0) {
+ SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
+ SSL_GETPID(), ss->fd));
+ goto no_ticket;
+ }
+
+ /* 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, 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 - TLS_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, mac_key_pkcs11, &macParam);
+ if (!hmac_ctx_pkcs11) {
+ SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
+ SSL_GETPID(), ss->fd, PORT_GetError()));
+ goto no_ticket;
+ } else {
+ SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
+ SSL_GETPID(), ss->fd));
+ }
+ rv = PK11_DigestBegin(hmac_ctx_pkcs11);
+ rv = PK11_DigestOp(hmac_ctx_pkcs11, extension_data.data,
+ extension_data.len - TLS_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) {
+ SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
+ SSL_GETPID(), ss->fd));
+ 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, aes_key,
+ sizeof(session_ticket_enc_key), enc_session_ticket.iv,
+ NSS_AES_CBC, 0,AES_BLOCK_SIZE);
+ if (rv != SECSuccess) {
+ SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
+ SSL_GETPID(), ss->fd));
+ 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, aes_key_pkcs11, &ivItem);
+ if (!aes_ctx_pkcs11) {
+ SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
+ SSL_GETPID(), ss->fd));
+ goto no_ticket;
+ }
+
+ rv = PK11_CipherOp(aes_ctx_pkcs11, decrypted_state->data,
+ (int *)&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 +
+ TLS_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->statelessResume = PR_TRUE;
+ ss->sec.ci.sid = sid;
+ }
+ }
+
+ if (0) {
+no_ticket:
+ SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
+ SSL_GETPID(), ss->fd));
+ ssl3stats = SSL_GetStatistics();
+ SSL_AtomicIncrementLong(& ssl3stats->hch_sid_ticket_parse_failures );
+ 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,
+ SESS_TICKET_KEY_NAME_LEN) != 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,
+ TLS_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, and
+ * if present, invoke that handler.
+ * Servers ignore any extensions with unknown extension types.
+ * Clients reject any extensions with unadvertised extension types.
+ */
+SECStatus
+ssl3_HandleHelloExtensions(sslSocket *ss, SSL3Opaque **b, PRUint32 *length)
+{
+ const ssl3HelloExtensionHandler * handlers =
+ ss->sec.isServer ? clientHelloHandlers : serverHelloHandlers;
+
+ 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;
+
+ /* 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; /* TODO: send unsupported_extension alert */
+
+ /* Check whether an extension has been sent multiple times. */
+ if (ssl3_ExtensionNegotiated(ss, extension_type))
+ return SECFailure;
+
+ /* find extension_type in table of 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->xtnData.serverSenders[0];
+
+ for (i = 0; i < MAX_EXTENSIONS; ++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_EXTENSIONS); /* 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_EXTENSIONS; ++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..e410b1cfd 100644
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -150,6 +150,7 @@ typedef enum {
hello_request = 0,
client_hello = 1,
server_hello = 2,
+ new_session_ticket = 4,
certificate = 11,
server_key_exchange = 12,
certificate_request = 13,
@@ -307,4 +308,56 @@ typedef struct {
SSL3Opaque verify_data[12];
} TLSFinished;
+/*
+ * TLS 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;
+ union {
+ SSL3Opaque *certificate_list;
+ } identity;
+} ClientIdentity;
+
+#define SESS_TICKET_KEY_NAME_LEN 16
+#define SESS_TICKET_KEY_NAME_PREFIX "NSS!"
+#define SESS_TICKET_KEY_NAME_PREFIX_LEN 4
+#define SESS_TICKET_KEY_VAR_NAME_LEN 12
+
+typedef struct {
+ unsigned char *key_name;
+ unsigned char *iv;
+ SECItem encrypted_state;
+ unsigned char *mac;
+} EncryptedSessionTicket;
+
+/* Supported extensions. */
+/* Update MAX_EXTENSIONS whenever a new extension type is added. */
+typedef enum {
+ server_name_xtn = 0,
+#ifdef NSS_ENABLE_ECC
+ elliptic_curves_xtn = 10,
+ ec_point_formats_xtn = 11,
+#endif
+ session_ticket_xtn = 35
+} ExtensionType;
+
+#define MAX_EXTENSIONS 4
+
+#define TLS_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 b0175e194..e1e2c2131 100644
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -174,8 +174,6 @@ 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 NUM_MIXERS 9
/* Mask of the 25 named curves we support. */
@@ -334,6 +332,7 @@ typedef struct sslOptionsStr {
unsigned int noStepDown : 1; /* 15 */
unsigned int bypassPKCS11 : 1; /* 16 */
unsigned int noLocks : 1; /* 17 */
+ unsigned int enableSessionTickets : 1; /* 18 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -629,6 +628,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 sessionTicket;
} ssl3;
} u;
};
@@ -691,10 +694,31 @@ typedef enum {
wait_server_key,
wait_cert_request,
wait_hello_done,
+ wait_new_session_ticket,
idle_handshake
} SSL3WaitState;
/*
+ * TLS extension related constants and data structures.
+ */
+typedef struct TLSExtensionDataStr TLSExtensionData;
+typedef struct SessionTicketDataStr SessionTicketData;
+
+struct TLSExtensionDataStr {
+ /* registered callbacks that send server hello extensions */
+ ssl3HelloExtensionSender serverSenders[MAX_EXTENSIONS];
+ /* Keep track of the extensions that are negotiated. */
+ PRUint16 numAdvertised;
+ PRUint16 numNegotiated;
+ PRUint16 advertised[MAX_EXTENSIONS];
+ PRUint16 negotiated[MAX_EXTENSIONS];
+
+ /* SessionTicket Extension related data. */
+ PRBool ticketTimestampVerified;
+ PRBool emptySessionTicket;
+};
+
+/*
** This is the "hs" member of the "ssl3" struct.
** This entire struct is protected by ssl3HandshakeLock
*/
@@ -797,14 +821,28 @@ typedef struct SSLWrappedSymWrappingKeyStr {
PRUint16 wrapIVLen;
} SSLWrappedSymWrappingKey;
-
-
-
-
-
-
-
-
+typedef struct SessionTicketStr {
+ 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.
@@ -963,9 +1001,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. */
@@ -1042,6 +1077,13 @@ const unsigned char * preferredCipher;
/* SSL3 state info. Formerly was a pointer */
ssl3State ssl3;
+
+ /*
+ * TLS extension related data.
+ */
+ /* True when the current session is a stateless resume. */
+ PRBool statelessResume;
+ TLSExtensionData xtnData;
};
@@ -1154,6 +1196,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);
@@ -1397,15 +1440,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,
+extern SECStatus ssl3_CacheWrappedMasterSecret(sslSocket *ss,
+ sslSessionID *sid, ssl3CipherSpec *spec,
+ SSL3KEAType effectiveExchKeyType);
+
+/* Functions that handle ClientHello and ServerHello extensions. */
+extern SECStatus ssl3_HandleServerNameXtn(sslSocket * ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_HandleSupportedCurvesXtn(sslSocket * ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_HandleSupportedPointFormatsXtn(sslSocket * ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_ClientHandleSessionTicketXtn(sslSocket *ss,
+ PRUint16 ex_type, SECItem *data);
+extern SECStatus ssl3_ServerHandleSessionTicketXtn(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_SendSessionTicketXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
+#ifdef NSS_ENABLE_ECC
+extern PRInt32 ssl3_SendSupportedCurvesXtn(sslSocket *ss,
+ PRBool append, PRUint32 maxBytes);
+extern PRInt32 ssl3_SendSupportedPointFormatsXtn(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_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type);
+extern SECStatus ssl3_SetSIDSessionTicket(sslSessionID *sid,
+ NewSessionTicket *session_ticket);
+extern SECStatus ssl3_SendNewSessionTicket(sslSocket *ss);
+extern PRBool ssl_GetSessionTicketKeys(unsigned char *keyName,
+ unsigned char *encKey, unsigned char *macKey);
+extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
+ SECKEYPublicKey *svrPubKey, void *pwArg,
+ unsigned char *keyName, PK11SymKey **aesKey,
+ PK11SymKey **macKey);
+
+/* Tell clients to consider tickets valid for this long. */
+#define TLS_EX_SESS_TICKET_LIFETIME_HINT (2 * 24 * 60 * 60) /* 2 days */
+#define TLS_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);
@@ -1461,6 +1544,7 @@ extern int ssl_MapLowLevelError(int hiLevelError);
extern PRUint32 ssl_Time(void);
+extern void SSL_AtomicIncrementLong(long * x);
SECStatus SSL_DisableDefaultExportCipherSuites(void);
SECStatus SSL_DisableExportCipherSuites(PRFileDesc * fd);
diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c
index 299486a6d..608b56394 100644
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -40,6 +40,7 @@
#include "nssrenam.h"
#include "cert.h"
+#include "pk11pub.h"
#include "secitem.h"
#include "ssl.h"
#include "nss.h"
@@ -219,6 +220,9 @@ ssl_DestroySID(sslSessionID *sid)
if ( sid->localCert ) {
CERT_DestroyCertificate(sid->localCert);
}
+ if (sid->u.ssl3.sessionTicket.ticket.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
+ }
PORT_ZFree(sid, sizeof(sslSessionID));
}
@@ -355,8 +359,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.sessionTicket.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));
@@ -483,3 +497,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.sessionTicket.ticket.data)
+ SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
+ if (session_ticket->ticket.len > 0) {
+ rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.sessionTicket.ticket,
+ &session_ticket->ticket);
+ if (rv != SECSuccess) {
+ UNLOCK_CACHE;
+ return rv;
+ }
+ } else {
+ sid->u.ssl3.sessionTicket.ticket.data = NULL;
+ sid->u.ssl3.sessionTicket.ticket.len = 0;
+ }
+ sid->u.ssl3.sessionTicket.received_timestamp =
+ session_ticket->received_timestamp;
+ sid->u.ssl3.sessionTicket.ticket_lifetime_hint =
+ session_ticket->ticket_lifetime_hint;
+
+ UNLOCK_CACHE;
+ return SECSuccess;
+}
diff --git a/security/nss/lib/ssl/sslsnce.c b/security/nss/lib/ssl/sslsnce.c
index ee3c65085..d884473dc 100644
--- a/security/nss/lib/ssl/sslsnce.c
+++ b/security/nss/lib/ssl/sslsnce.c
@@ -67,6 +67,10 @@
* sidCacheEntry sidCacheData[ numSIDCacheEntries];
* certCacheEntry certCacheData[numCertCacheEntries];
* SSLWrappedSymWrappingKey keyCacheData[kt_kea_size][SSL_NUM_WRAP_MECHS];
+ * uint8 keyNameSuffix[SESS_TICKET_KEY_VAR_NAME_LEN]
+ * encKeyCacheEntry ticketEncKey; // Wrapped in non-bypass mode
+ * encKeyCacheEntry ticketMacKey; // Wrapped in non-bypass mode
+ * PRBool ticketKeysValid;
* } cacheMemCacheData;
*/
#include "nssrenam.h"
@@ -80,6 +84,7 @@
#include "sslproto.h"
#include "pk11func.h"
#include "base64.h"
+#include "keyhi.h"
#include <stdio.h>
@@ -176,6 +181,12 @@ struct sidCacheSetStr {
};
typedef struct sidCacheSetStr sidCacheSet;
+struct encKeyCacheEntryStr {
+ PRUint8 bytes[512];
+ PRInt32 length;
+};
+typedef struct encKeyCacheEntryStr encKeyCacheEntry;
+
struct cacheDescStr {
PRUint32 cacheMemSize;
@@ -212,6 +223,10 @@ struct cacheDescStr {
sidCacheEntry * sidCacheData;
certCacheEntry * certCacheData;
SSLWrappedSymWrappingKey * keyCacheData;
+ uint8 * ticketKeyNameSuffix;
+ encKeyCacheEntry * ticketEncKey;
+ encKeyCacheEntry * ticketMacKey;
+ PRUint32 * ticketKeysValid;
/* Only the private copies of these pointers are valid */
char * cacheMem;
@@ -997,10 +1012,27 @@ InitCache(cacheDesc *cache, int maxCacheEntries, PRUint32 ssl2_timeout,
ptr = (ptrdiff_t)(cache->keyCacheData + cache->numKeyCacheEntries);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
- cache->cacheMemSize = ptr;
-
cache->keyCacheSize = (char *)ptr - (char *)cache->keyCacheData;
+ cache->ticketKeyNameSuffix = (uint8 *)ptr;
+ ptr = (ptrdiff_t)(cache->ticketKeyNameSuffix +
+ SESS_TICKET_KEY_VAR_NAME_LEN);
+ ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
+
+ cache->ticketEncKey = (encKeyCacheEntry *)ptr;
+ ptr = (ptrdiff_t)(cache->ticketEncKey + 1);
+ ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
+
+ cache->ticketMacKey = (encKeyCacheEntry *)ptr;
+ ptr = (ptrdiff_t)(cache->ticketMacKey + 1);
+ ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
+
+ cache->ticketKeysValid = (PRUint32 *)ptr;
+ ptr = (ptrdiff_t)(cache->ticketKeysValid + 1);
+ ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
+
+ cache->cacheMemSize = ptr;
+
if (ssl2_timeout) {
if (ssl2_timeout > MAX_SSL2_TIMEOUT) {
ssl2_timeout = MAX_SSL2_TIMEOUT;
@@ -1086,6 +1118,10 @@ InitCache(cacheDesc *cache, int maxCacheEntries, PRUint32 ssl2_timeout,
*(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
*(ptrdiff_t *)(&cache->certCacheData) += ptr;
*(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
+ *(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
+ *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
+ *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
+ *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
/* initialize the locks */
init_time = ssl_Time();
@@ -1363,6 +1399,10 @@ SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
*(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
*(ptrdiff_t *)(&cache->certCacheData) += ptr;
*(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
+ *(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
+ *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
+ *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
+ *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
cache->cacheMemMap = my.cacheMemMap;
cache->cacheMem = my.cacheMem;
@@ -1593,6 +1633,198 @@ ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
return rv;
}
+/* Wrap and cache a session ticket key. */
+static PRBool
+WrapTicketKey(SECKEYPublicKey *svrPubKey, PK11SymKey *symKey,
+ const char *keyName, encKeyCacheEntry* cacheEntry)
+{
+ SECItem wrappedKey = {siBuffer, NULL, 0};
+
+ wrappedKey.len = SECKEY_PublicKeyStrength(svrPubKey);
+ PORT_Assert(wrappedKey.len <= sizeof(cacheEntry->bytes));
+ if (wrappedKey.len > sizeof(cacheEntry->bytes))
+ return PR_FALSE;
+ wrappedKey.data = cacheEntry->bytes;
+
+ if (PK11_PubWrapSymKey(CKM_RSA_PKCS, svrPubKey, symKey, &wrappedKey)
+ != SECSuccess) {
+ SSL_DBG(("%d: SSL[%s]: Unable to wrap session ticket %s.",
+ SSL_GETPID(), "unknown", keyName));
+ return PR_FALSE;
+ }
+ cacheEntry->length = wrappedKey.len;
+ return PR_TRUE;
+}
+
+static PRBool
+GenerateAndWrapTicketKeys(SECKEYPublicKey *svrPubKey, void *pwArg,
+ unsigned char *keyName, PK11SymKey **aesKey,
+ PK11SymKey **macKey)
+{
+ PK11SlotInfo *slot;
+ CK_MECHANISM_TYPE mechanismArray[2];
+ PK11SymKey *aesKeyTmp = NULL;
+ PK11SymKey *macKeyTmp = NULL;
+ cacheDesc *cache = &globalCache;
+
+ if (PK11_GenerateRandom(cache->ticketKeyNameSuffix,
+ SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess) {
+ SSL_DBG(("%d: SSL[%s]: Unable to generate random key name bytes.",
+ SSL_GETPID(), "unknown"));
+ goto loser;
+ }
+
+ mechanismArray[0] = CKM_AES_CBC;
+ mechanismArray[1] = CKM_SHA256_HMAC;
+
+ slot = PK11_GetBestSlotMultiple(mechanismArray, 2, pwArg);
+ if (slot) {
+ aesKeyTmp = PK11_KeyGen(slot, mechanismArray[0], NULL, 32, pwArg);
+ macKeyTmp = PK11_KeyGen(slot, mechanismArray[1], NULL, SHA256_LENGTH,
+ pwArg);
+ PK11_FreeSlot(slot);
+ }
+
+ if (aesKeyTmp == NULL || macKeyTmp == NULL) {
+ SSL_DBG(("%d: SSL[%s]: Unable to generate session ticket keys.",
+ SSL_GETPID(), "unknown"));
+ goto loser;
+ }
+
+ /* Export the keys to the shared cache in wrapped form. */
+ if (!WrapTicketKey(svrPubKey, aesKeyTmp, "enc key", cache->ticketEncKey))
+ goto loser;
+ if (!WrapTicketKey(svrPubKey, macKeyTmp, "mac key", cache->ticketMacKey))
+ goto loser;
+
+ PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
+ SESS_TICKET_KEY_VAR_NAME_LEN);
+ *aesKey = aesKeyTmp;
+ *macKey = macKeyTmp;
+ return PR_TRUE;
+
+loser:
+ if (aesKeyTmp)
+ PK11_FreeSymKey(aesKeyTmp);
+ if (macKeyTmp)
+ PK11_FreeSymKey(macKeyTmp);
+ return PR_FALSE;
+}
+
+static PRBool
+UnwrapCachedTicketKeys(SECKEYPrivateKey *svrPrivKey, unsigned char *keyName,
+ PK11SymKey **aesKey, PK11SymKey **macKey)
+{
+ SECItem wrappedKey = {siBuffer, NULL, 0};
+ PK11SymKey *aesKeyTmp = NULL;
+ PK11SymKey *macKeyTmp = NULL;
+ cacheDesc *cache = &globalCache;
+
+ wrappedKey.data = cache->ticketEncKey->bytes;
+ wrappedKey.len = cache->ticketEncKey->length;
+ PORT_Assert(wrappedKey.len <= sizeof(cache->ticketEncKey->bytes));
+ aesKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
+ CKM_AES_CBC, CKA_DECRYPT, 0);
+
+ wrappedKey.data = cache->ticketMacKey->bytes;
+ wrappedKey.len = cache->ticketMacKey->length;
+ PORT_Assert(wrappedKey.len <= sizeof(cache->ticketMacKey->bytes));
+ macKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
+ CKM_SHA256_HMAC, CKA_SIGN, 0);
+
+ if (aesKeyTmp == NULL || macKeyTmp == NULL) {
+ SSL_DBG(("%d: SSL[%s]: Unable to unwrap session ticket keys.",
+ SSL_GETPID(), "unknown"));
+ goto loser;
+ }
+ SSL_DBG(("%d: SSL[%s]: Successfully unwrapped session ticket keys.",
+ SSL_GETPID(), "unknown"));
+
+ PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
+ SESS_TICKET_KEY_VAR_NAME_LEN);
+ *aesKey = aesKeyTmp;
+ *macKey = macKeyTmp;
+ return PR_TRUE;
+
+loser:
+ if (aesKeyTmp)
+ PK11_FreeSymKey(aesKeyTmp);
+ if (macKeyTmp)
+ PK11_FreeSymKey(macKeyTmp);
+ return PR_FALSE;
+}
+
+PRBool
+ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
+ SECKEYPublicKey *svrPubKey, void *pwArg,
+ unsigned char *keyName, PK11SymKey **aesKey,
+ PK11SymKey **macKey)
+{
+ PRUint32 now = 0;
+ PRBool rv = PR_FALSE;
+ PRBool keysGenerated = PR_FALSE;
+ cacheDesc *cache = &globalCache;
+
+ now = LockSidCacheLock(cache->keyCacheLock, now);
+ if (!now)
+ return rv;
+
+ if (!*(cache->ticketKeysValid)) {
+ /* Keys do not exist, create them. */
+ if (!GenerateAndWrapTicketKeys(svrPubKey, pwArg, keyName,
+ aesKey, macKey))
+ goto loser;
+ keysGenerated = PR_TRUE;
+ *(cache->ticketKeysValid) = 1;
+ }
+
+ rv = PR_TRUE;
+
+ loser:
+ UnlockSidCacheLock(cache->keyCacheLock);
+ if (rv && !keysGenerated)
+ rv = UnwrapCachedTicketKeys(svrPrivKey, keyName, aesKey, macKey);
+ return rv;
+}
+
+PRBool
+ssl_GetSessionTicketKeys(unsigned char *keyName, unsigned char *encKey,
+ unsigned char *macKey)
+{
+ PRBool rv = PR_FALSE;
+ PRUint32 now = 0;
+ cacheDesc *cache = &globalCache;
+
+ /* Grab lock. */
+ now = LockSidCacheLock(cache->keyCacheLock, now);
+ if (!now)
+ return rv;
+
+ if (!*(cache->ticketKeysValid)) {
+ if (PK11_GenerateRandom(cache->ticketKeyNameSuffix,
+ SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess)
+ goto loser;
+ if (PK11_GenerateRandom(cache->ticketEncKey->bytes, 32) != SECSuccess)
+ goto loser;
+ if (PK11_GenerateRandom(cache->ticketMacKey->bytes,
+ SHA256_LENGTH) != SECSuccess)
+ goto loser;
+ *(cache->ticketKeysValid) = 1;
+ }
+
+ rv = PR_TRUE;
+
+ loser:
+ UnlockSidCacheLock(cache->keyCacheLock);
+ if (rv) {
+ PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
+ SESS_TICKET_KEY_VAR_NAME_LEN);
+ PORT_Memcpy(encKey, cache->ticketEncKey->bytes, 32);
+ PORT_Memcpy(macKey, cache->ticketMacKey->bytes, SHA256_LENGTH);
+ }
+ return rv;
+}
+
/* The caller passes in the new value it wants
* to set. This code tests the wrapped sym key entry in the shared memory.
* If it is uninitialized, this function writes the caller's value into
diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
index f51cd2446..98db73a79 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, /* enableSessionTickets */
};
sslSessionIDLookupFunc ssl_sid_lookup;
@@ -699,6 +700,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
}
break;
+ case SSL_ENABLE_SESSION_TICKETS:
+ ss->opt.enableSessionTickets = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
@@ -754,6 +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_TICKETS:
+ on = ss->opt.enableSessionTickets;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -795,6 +803,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_TICKETS:
+ on = ssl_defaults.enableSessionTickets;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -922,6 +933,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
}
break;
+ case SSL_ENABLE_SESSION_TICKETS:
+ ssl_defaults.enableSessionTickets = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
diff --git a/security/nss/lib/ssl/sslt.h b/security/nss/lib/ssl/sslt.h
index 0fefa5782..59de25955 100644
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -59,6 +59,12 @@ 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;
+ long hch_sid_ticket_parse_failures;
} 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)