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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
Index: net/third_party/nss/ssl/ssl3con.c
===================================================================
--- net/third_party/nss/ssl/ssl3con.c (revision 220594)
+++ net/third_party/nss/ssl/ssl3con.c (working copy)
@@ -3933,6 +3933,20 @@
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return SECFailure;
}
+
+ /* A backup SHA-1 hash for a potential client auth signature. */
+ if (!ss->sec.isServer) {
+ ss->ssl3.hs.md5 = PK11_CreateDigestContext(SEC_OID_SHA1);
+ if (ss->ssl3.hs.md5 == NULL) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ return SECFailure;
+ }
+
+ if (PK11_DigestBegin(ss->ssl3.hs.md5) != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ return SECFailure;
+ }
+ }
} else {
/* Both ss->ssl3.hs.md5 and ss->ssl3.hs.sha should be NULL or
* created successfully. */
@@ -4043,6 +4057,13 @@
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return rv;
}
+ if (ss->ssl3.hs.md5) {
+ rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ return rv;
+ }
+ }
} else {
rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
if (rv != SECSuccess) {
@@ -4791,6 +4812,30 @@
return rv;
}
+static SECStatus
+ssl3_ComputeBackupHandshakeHashes(sslSocket * ss,
+ SSL3Hashes * hashes) /* output goes here. */
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert( ss->ssl3.hs.hashType == handshake_hash_single );
+
+ rv = PK11_DigestFinal(ss->ssl3.hs.md5, hashes->u.raw, &hashes->len,
+ sizeof(hashes->u.raw));
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ rv = SECFailure;
+ goto loser;
+ }
+ hashes->hashAlg = SEC_OID_SHA1;
+
+loser:
+ PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
+ ss->ssl3.hs.md5 = NULL;
+ return rv;
+}
+
/*
* SSL 2 based implementations pass in the initial outbound buffer
* so that the handshake hash can contain the included information.
@@ -6044,7 +6089,17 @@
SSL_GETPID(), ss->fd));
ssl_GetSpecReadLock(ss);
- rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
+ /* In TLS 1.2, ssl3_ComputeHandshakeHashes always uses the handshake hash
+ * function (SHA-256). If the server or the client does not support SHA-256
+ * as a signature hash, we can either maintain a backup SHA-1 handshake
+ * hash or buffer all handshake messages.
+ */
+ if (ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) {
+ rv = ssl3_ComputeBackupHandshakeHashes(ss, &hashes);
+ PORT_Assert(ss->ssl3.hs.md5 == NULL);
+ } else {
+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
+ }
ssl_ReleaseSpecReadLock(ss);
if (rv != SECSuccess) {
goto done; /* err code was set by ssl3_ComputeHandshakeHashes */
@@ -6098,11 +6153,6 @@
if (rv != SECSuccess) {
goto done;
}
- /* We always sign using the handshake hash function. It's possible that
- * a server could support SHA-256 as the handshake hash but not as a
- * signature hash. In that case we wouldn't be able to do client
- * certificates with it. The alternative is to buffer all handshake
- * messages. */
sigAndHash.hashAlg = hashes.hashAlg;
rv = ssl3_AppendSignatureAndHashAlgorithm(ss, &sigAndHash);
@@ -6802,6 +6852,70 @@
}
+/*
+ * Returns true if the client authentication key is an RSA or DSA key that
+ * may be able to sign only SHA-1 hashes.
+ */
+static PRBool
+ssl3_ClientKeyPrefersSHA1(sslSocket *ss)
+{
+ SECKEYPublicKey *pubk;
+ PRBool prefer_sha1 = PR_FALSE;
+
+#if defined(NSS_PLATFORM_CLIENT_AUTH) && defined(_WIN32)
+ /* If the key is in CAPI, assume conservatively that the CAPI service
+ * provider may be unable to sign SHA-256 hashes.
+ */
+ if (ss->ssl3.platformClientKey->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
+ /* CAPI only supports RSA and DSA signatures, so we don't need to
+ * check the key type. */
+ return PR_TRUE;
+ }
+#endif /* NSS_PLATFORM_CLIENT_AUTH && _WIN32 */
+
+ /* If the key is a 1024-bit RSA or DSA key, assume conservatively that
+ * it may be unable to sign SHA-256 hashes. This is the case for older
+ * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and
+ * older, DSA key size is at most 1024 bits and the hash function must
+ * be SHA-1.
+ */
+ pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
+ if (pubk == NULL) {
+ return PR_FALSE;
+ }
+ if (pubk->keyType == rsaKey || pubk->keyType == dsaKey) {
+ prefer_sha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
+ }
+ SECKEY_DestroyPublicKey(pubk);
+ return prefer_sha1;
+}
+
+/* Destroys the backup handshake hash context if we don't need it. */
+static void
+ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
+ const SECItem *algorithms)
+{
+ PRBool need_backup_hash = PR_FALSE;
+ unsigned int i;
+
+ PORT_Assert(ss->ssl3.hs.md5);
+ if (ssl3_ClientKeyPrefersSHA1(ss)) {
+ /* Use SHA-1 if the server supports it. */
+ for (i = 0; i < algorithms->len; i += 2) {
+ if (algorithms->data[i] == tls_hash_sha1 &&
+ (algorithms->data[i+1] == tls_sig_rsa ||
+ algorithms->data[i+1] == tls_sig_dsa)) {
+ need_backup_hash = PR_TRUE;
+ break;
+ }
+ }
+ }
+ if (!need_backup_hash) {
+ PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
+ ss->ssl3.hs.md5 = NULL;
+ }
+}
+
typedef struct dnameNode {
struct dnameNode *next;
SECItem name;
@@ -6994,6 +7108,9 @@
}
goto send_no_certificate;
}
+ if (isTLS12) {
+ ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
+ }
break; /* not an error */
}
#endif /* NSS_PLATFORM_CLIENT_AUTH */
@@ -7029,6 +7146,9 @@
}
goto send_no_certificate;
}
+ if (isTLS12) {
+ ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
+ }
break; /* not an error */
case SECFailure:
@@ -7227,6 +7347,13 @@
(ss->ssl3.platformClientKey ||
ss->ssl3.clientPrivateKey != NULL);
+ if (!sendClientCert &&
+ ss->ssl3.hs.hashType == handshake_hash_single && ss->ssl3.hs.md5) {
+ /* Don't need the backup handshake hash. */
+ PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
+ ss->ssl3.hs.md5 = NULL;
+ }
+
/* We must wait for the server's certificate to be authenticated before
* sending the client certificate in order to disclosing the client
* certificate to an attacker that does not have a valid cert for the
Index: net/third_party/nss/ssl/sslimpl.h
===================================================================
--- net/third_party/nss/ssl/sslimpl.h (revision 220594)
+++ net/third_party/nss/ssl/sslimpl.h (working copy)
@@ -838,6 +838,9 @@
* SSL 3.0 - TLS 1.1 use both |md5| and |sha|. |md5| is used for MD5 and
* |sha| for SHA-1.
* TLS 1.2 and later use only |sha|, for SHA-256. */
+ /* NOTE: On the client side, TLS 1.2 and later use |md5| as a backup
+ * handshake hash for generating client auth signatures. Confusingly, the
+ * backup hash function is SHA-1. */
PK11Context * md5;
PK11Context * sha;
|