1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
index 882e356..396c408 100644
--- a/nss/lib/ssl/ssl3con.c
+++ b/nss/lib/ssl/ssl3con.c
@@ -7594,6 +7594,33 @@ ssl3_SendClientSecondRound(sslSocket *ss)
ssl_ReleaseXmitBufLock(ss); /*******************************/
+ if (!ss->ssl3.hs.isResuming &&
+ ssl3_ExtensionNegotiated(ss, ssl_channel_id_xtn)) {
+ /* If we are negotiating ChannelID on a full handshake then we record
+ * the handshake hashes in |sid| at this point. They will be needed in
+ * the event that we resume this session and use ChannelID on the
+ * resumption handshake. */
+ SSL3Hashes hashes;
+ SECItem *originalHandshakeHash =
+ &ss->sec.ci.sid->u.ssl3.originalHandshakeHash;
+ PORT_Assert(ss->sec.ci.sid->cached == never_cached);
+
+ ssl_GetSpecReadLock(ss);
+ PORT_Assert(ss->version > SSL_LIBRARY_VERSION_3_0);
+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.cwSpec, &hashes, 0);
+ ssl_ReleaseSpecReadLock(ss);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ PORT_Assert(originalHandshakeHash->len == 0);
+ originalHandshakeHash->data = PORT_Alloc(hashes.len);
+ if (!originalHandshakeHash->data)
+ return SECFailure;
+ originalHandshakeHash->len = hashes.len;
+ memcpy(originalHandshakeHash->data, hashes.u.raw, hashes.len);
+ }
+
if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
ss->ssl3.hs.ws = wait_new_session_ticket;
else
@@ -10590,6 +10617,7 @@ static SECStatus
ssl3_SendEncryptedExtensions(sslSocket *ss)
{
static const char CHANNEL_ID_MAGIC[] = "TLS Channel ID signature";
+ static const char CHANNEL_ID_RESUMPTION_MAGIC[] = "Resumption";
/* This is the ASN.1 prefix for a P-256 public key. Specifically it's:
* SEQUENCE
* SEQUENCE
@@ -10615,7 +10643,10 @@ ssl3_SendEncryptedExtensions(sslSocket *ss)
SECItem *spki = NULL;
SSL3Hashes hashes;
const unsigned char *pub_bytes;
- unsigned char signed_data[sizeof(CHANNEL_ID_MAGIC) + sizeof(SSL3Hashes)];
+ unsigned char signed_data[sizeof(CHANNEL_ID_MAGIC) +
+ sizeof(CHANNEL_ID_RESUMPTION_MAGIC) +
+ sizeof(SSL3Hashes)*2];
+ size_t signed_data_len;
unsigned char digest[SHA256_LENGTH];
SECItem digest_item;
unsigned char signature[64];
@@ -10665,11 +10696,26 @@ ssl3_SendEncryptedExtensions(sslSocket *ss)
pub_bytes = spki->data + sizeof(P256_SPKI_PREFIX);
- memcpy(signed_data, CHANNEL_ID_MAGIC, sizeof(CHANNEL_ID_MAGIC));
- memcpy(signed_data + sizeof(CHANNEL_ID_MAGIC), hashes.u.raw, hashes.len);
+ signed_data_len = 0;
+ memcpy(signed_data + signed_data_len, CHANNEL_ID_MAGIC,
+ sizeof(CHANNEL_ID_MAGIC));
+ signed_data_len += sizeof(CHANNEL_ID_MAGIC);
+ if (ss->ssl3.hs.isResuming) {
+ SECItem *originalHandshakeHash =
+ &ss->sec.ci.sid->u.ssl3.originalHandshakeHash;
+ PORT_Assert(originalHandshakeHash->len > 0);
- rv = PK11_HashBuf(SEC_OID_SHA256, digest, signed_data,
- sizeof(CHANNEL_ID_MAGIC) + hashes.len);
+ memcpy(signed_data + signed_data_len, CHANNEL_ID_RESUMPTION_MAGIC,
+ sizeof(CHANNEL_ID_RESUMPTION_MAGIC));
+ signed_data_len += sizeof(CHANNEL_ID_RESUMPTION_MAGIC);
+ memcpy(signed_data + signed_data_len, originalHandshakeHash->data,
+ originalHandshakeHash->len);
+ signed_data_len += originalHandshakeHash->len;
+ }
+ memcpy(signed_data + signed_data_len, hashes.u.raw, hashes.len);
+ signed_data_len += hashes.len;
+
+ rv = PK11_HashBuf(SEC_OID_SHA256, digest, signed_data, signed_data_len);
if (rv != SECSuccess)
goto loser;
diff --git a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c
index 03cf05c..166022c 100644
--- a/nss/lib/ssl/ssl3ext.c
+++ b/nss/lib/ssl/ssl3ext.c
@@ -812,6 +812,15 @@ ssl3_ClientSendChannelIDXtn(sslSocket * ss, PRBool append,
return 0;
}
+ if (ss->sec.ci.sid->cached != never_cached &&
+ ss->sec.ci.sid->u.ssl3.originalHandshakeHash.len == 0) {
+ /* We can't do ChannelID on a connection if we're resuming and didn't
+ * do ChannelID on the original connection: without ChannelID on the
+ * original connection we didn't record the handshake hashes needed for
+ * the signature. */
+ return 0;
+ }
+
if (append) {
SECStatus rv;
rv = ssl3_AppendHandshakeNumber(ss, ssl_channel_id_xtn, 2);
diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
index 9c789bf..ca68727 100644
--- a/nss/lib/ssl/sslimpl.h
+++ b/nss/lib/ssl/sslimpl.h
@@ -705,6 +705,14 @@ struct sslSessionIDStr {
*/
NewSessionTicket sessionTicket;
SECItem srvName;
+
+ /* originalHandshakeHash contains the hash of the original, full
+ * handshake prior to the server's final flow. This is either a
+ * SHA-1/MD5 combination (for TLS < 1.2) or the TLS PRF hash (for
+ * TLS 1.2). This is recorded and used only when ChannelID is
+ * negotiated as it's used to bind the ChannelID signature on the
+ * resumption handshake to the original handshake. */
+ SECItem originalHandshakeHash;
} ssl3;
} u;
};
diff --git a/nss/lib/ssl/sslnonce.c b/nss/lib/ssl/sslnonce.c
index a6f7349..eb5004c 100644
--- a/nss/lib/ssl/sslnonce.c
+++ b/nss/lib/ssl/sslnonce.c
@@ -148,6 +148,9 @@ ssl_DestroySID(sslSessionID *sid)
if (sid->u.ssl3.srvName.data) {
SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
}
+ if (sid->u.ssl3.originalHandshakeHash.data) {
+ SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
+ }
PORT_ZFree(sid, sizeof(sslSessionID));
}
diff --git a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
index e4d188f..b813c04 100644
--- a/nss/lib/ssl/sslt.h
+++ b/nss/lib/ssl/sslt.h
@@ -204,7 +204,7 @@ typedef enum {
ssl_app_layer_protocol_xtn = 16,
ssl_session_ticket_xtn = 35,
ssl_next_proto_nego_xtn = 13172,
- ssl_channel_id_xtn = 30031,
+ ssl_channel_id_xtn = 30032,
ssl_padding_xtn = 35655,
ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
} SSLExtensionType;
|