author | xuelei |
Tue, 12 Feb 2019 13:36:15 -0800 | |
changeset 53734 | cb1642ccc732 |
parent 50768 | 68fa3d4026ea |
child 54443 | dfba4e321ab3 |
permissions | -rw-r--r-- |
30904 | 1 |
/* |
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
2 |
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. |
30904 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. Oracle designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Oracle in the LICENSE file that accompanied this code. |
|
10 |
* |
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
24 |
*/ |
|
25 |
||
26 |
package sun.security.ssl; |
|
27 |
||
28 |
import java.io.IOException; |
|
29 |
import java.security.MessageDigest; |
|
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
30 |
import java.security.NoSuchAlgorithmException; |
30904 | 31 |
import java.security.SecureRandom; |
50768 | 32 |
import java.util.Arrays; |
33 |
import static sun.security.ssl.ClientHello.ClientHelloMessage; |
|
30904 | 34 |
|
50768 | 35 |
/** |
36 |
* (D)TLS handshake cookie manager |
|
30904 | 37 |
*/ |
50768 | 38 |
abstract class HelloCookieManager { |
39 |
||
40 |
static class Builder { |
|
41 |
||
42 |
final SecureRandom secureRandom; |
|
43 |
||
44 |
private volatile D10HelloCookieManager d10HelloCookieManager; |
|
45 |
private volatile D13HelloCookieManager d13HelloCookieManager; |
|
46 |
private volatile T13HelloCookieManager t13HelloCookieManager; |
|
47 |
||
48 |
Builder(SecureRandom secureRandom) { |
|
49 |
this.secureRandom = secureRandom; |
|
50 |
} |
|
30904 | 51 |
|
50768 | 52 |
HelloCookieManager valueOf(ProtocolVersion protocolVersion) { |
53 |
if (protocolVersion.isDTLS) { |
|
54 |
if (protocolVersion.useTLS13PlusSpec()) { |
|
55 |
if (d13HelloCookieManager != null) { |
|
56 |
return d13HelloCookieManager; |
|
57 |
} |
|
58 |
||
59 |
synchronized (this) { |
|
60 |
if (d13HelloCookieManager == null) { |
|
61 |
d13HelloCookieManager = |
|
62 |
new D13HelloCookieManager(secureRandom); |
|
63 |
} |
|
64 |
} |
|
30904 | 65 |
|
50768 | 66 |
return d13HelloCookieManager; |
67 |
} else { |
|
68 |
if (d10HelloCookieManager != null) { |
|
69 |
return d10HelloCookieManager; |
|
70 |
} |
|
30904 | 71 |
|
50768 | 72 |
synchronized (this) { |
73 |
if (d10HelloCookieManager == null) { |
|
74 |
d10HelloCookieManager = |
|
75 |
new D10HelloCookieManager(secureRandom); |
|
76 |
} |
|
77 |
} |
|
30904 | 78 |
|
50768 | 79 |
return d10HelloCookieManager; |
80 |
} |
|
81 |
} else { |
|
82 |
if (protocolVersion.useTLS13PlusSpec()) { |
|
83 |
if (t13HelloCookieManager != null) { |
|
84 |
return t13HelloCookieManager; |
|
85 |
} |
|
30904 | 86 |
|
50768 | 87 |
synchronized (this) { |
88 |
if (t13HelloCookieManager == null) { |
|
89 |
t13HelloCookieManager = |
|
90 |
new T13HelloCookieManager(secureRandom); |
|
91 |
} |
|
92 |
} |
|
30904 | 93 |
|
50768 | 94 |
return t13HelloCookieManager; |
95 |
} |
|
96 |
} |
|
97 |
||
98 |
return null; |
|
99 |
} |
|
30904 | 100 |
} |
101 |
||
50768 | 102 |
abstract byte[] createCookie(ServerHandshakeContext context, |
103 |
ClientHelloMessage clientHello) throws IOException; |
|
104 |
||
105 |
abstract boolean isCookieValid(ServerHandshakeContext context, |
|
106 |
ClientHelloMessage clientHello, byte[] cookie) throws IOException; |
|
107 |
||
108 |
// DTLS 1.0/1.2 |
|
109 |
private static final |
|
110 |
class D10HelloCookieManager extends HelloCookieManager { |
|
111 |
||
112 |
final SecureRandom secureRandom; |
|
113 |
private int cookieVersion; // allow to wrap, version + sequence |
|
114 |
private byte[] cookieSecret; |
|
115 |
private byte[] legacySecret; |
|
116 |
||
117 |
D10HelloCookieManager(SecureRandom secureRandom) { |
|
118 |
this.secureRandom = secureRandom; |
|
119 |
||
120 |
this.cookieVersion = secureRandom.nextInt(); |
|
121 |
this.cookieSecret = new byte[32]; |
|
122 |
this.legacySecret = new byte[32]; |
|
123 |
||
124 |
secureRandom.nextBytes(cookieSecret); |
|
125 |
System.arraycopy(cookieSecret, 0, legacySecret, 0, 32); |
|
126 |
} |
|
127 |
||
128 |
@Override |
|
129 |
byte[] createCookie(ServerHandshakeContext context, |
|
130 |
ClientHelloMessage clientHello) throws IOException { |
|
131 |
int version; |
|
132 |
byte[] secret; |
|
133 |
||
134 |
synchronized (this) { |
|
135 |
version = cookieVersion; |
|
136 |
secret = cookieSecret; |
|
137 |
||
138 |
// the cookie secret usage limit is 2^24 |
|
139 |
if ((cookieVersion & 0xFFFFFF) == 0) { // reset the secret |
|
140 |
System.arraycopy(cookieSecret, 0, legacySecret, 0, 32); |
|
141 |
secureRandom.nextBytes(cookieSecret); |
|
142 |
} |
|
143 |
||
144 |
cookieVersion++; |
|
30904 | 145 |
} |
146 |
||
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
147 |
MessageDigest md; |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
148 |
try { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
149 |
md = MessageDigest.getInstance("SHA-256"); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
150 |
} catch (NoSuchAlgorithmException nsae) { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
151 |
throw new RuntimeException( |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
152 |
"MessageDigest algorithm SHA-256 is not available", nsae); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
153 |
} |
50768 | 154 |
byte[] helloBytes = clientHello.getHelloCookieBytes(); |
155 |
md.update(helloBytes); |
|
156 |
byte[] cookie = md.digest(secret); // 32 bytes |
|
157 |
cookie[0] = (byte)((version >> 24) & 0xFF); |
|
158 |
||
159 |
return cookie; |
|
30904 | 160 |
} |
161 |
||
50768 | 162 |
@Override |
163 |
boolean isCookieValid(ServerHandshakeContext context, |
|
164 |
ClientHelloMessage clientHello, byte[] cookie) throws IOException { |
|
165 |
// no cookie exchange or not a valid cookie length |
|
166 |
if ((cookie == null) || (cookie.length != 32)) { |
|
167 |
return false; |
|
168 |
} |
|
30904 | 169 |
|
50768 | 170 |
byte[] secret; |
171 |
synchronized (this) { |
|
172 |
if (((cookieVersion >> 24) & 0xFF) == cookie[0]) { |
|
173 |
secret = cookieSecret; |
|
174 |
} else { |
|
175 |
secret = legacySecret; // including out of window cookies |
|
176 |
} |
|
177 |
} |
|
178 |
||
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
179 |
MessageDigest md; |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
180 |
try { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
181 |
md = MessageDigest.getInstance("SHA-256"); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
182 |
} catch (NoSuchAlgorithmException nsae) { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
183 |
throw new RuntimeException( |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
184 |
"MessageDigest algorithm SHA-256 is not available", nsae); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
185 |
} |
50768 | 186 |
byte[] helloBytes = clientHello.getHelloCookieBytes(); |
187 |
md.update(helloBytes); |
|
188 |
byte[] target = md.digest(secret); // 32 bytes |
|
189 |
target[0] = cookie[0]; |
|
190 |
||
191 |
return Arrays.equals(target, cookie); |
|
192 |
} |
|
30904 | 193 |
} |
194 |
||
50768 | 195 |
private static final |
196 |
class D13HelloCookieManager extends HelloCookieManager { |
|
197 |
D13HelloCookieManager(SecureRandom secureRandom) { |
|
198 |
} |
|
199 |
||
200 |
@Override |
|
201 |
byte[] createCookie(ServerHandshakeContext context, |
|
202 |
ClientHelloMessage clientHello) throws IOException { |
|
203 |
throw new UnsupportedOperationException("Not supported yet."); |
|
204 |
} |
|
30904 | 205 |
|
50768 | 206 |
@Override |
207 |
boolean isCookieValid(ServerHandshakeContext context, |
|
208 |
ClientHelloMessage clientHello, byte[] cookie) throws IOException { |
|
209 |
throw new UnsupportedOperationException("Not supported yet."); |
|
210 |
} |
|
211 |
} |
|
212 |
||
213 |
private static final |
|
214 |
class T13HelloCookieManager extends HelloCookieManager { |
|
215 |
||
216 |
final SecureRandom secureRandom; |
|
217 |
private int cookieVersion; // version + sequence |
|
218 |
private final byte[] cookieSecret; |
|
219 |
private final byte[] legacySecret; |
|
220 |
||
221 |
T13HelloCookieManager(SecureRandom secureRandom) { |
|
222 |
this.secureRandom = secureRandom; |
|
223 |
this.cookieVersion = secureRandom.nextInt(); |
|
224 |
this.cookieSecret = new byte[64]; |
|
225 |
this.legacySecret = new byte[64]; |
|
226 |
||
227 |
secureRandom.nextBytes(cookieSecret); |
|
228 |
System.arraycopy(cookieSecret, 0, legacySecret, 0, 64); |
|
30904 | 229 |
} |
230 |
||
50768 | 231 |
@Override |
232 |
byte[] createCookie(ServerHandshakeContext context, |
|
233 |
ClientHelloMessage clientHello) throws IOException { |
|
234 |
int version; |
|
235 |
byte[] secret; |
|
236 |
||
237 |
synchronized (this) { |
|
238 |
version = cookieVersion; |
|
239 |
secret = cookieSecret; |
|
240 |
||
241 |
// the cookie secret usage limit is 2^24 |
|
242 |
if ((cookieVersion & 0xFFFFFF) == 0) { // reset the secret |
|
243 |
System.arraycopy(cookieSecret, 0, legacySecret, 0, 64); |
|
244 |
secureRandom.nextBytes(cookieSecret); |
|
245 |
} |
|
246 |
||
247 |
cookieVersion++; // allow wrapped version number |
|
248 |
} |
|
249 |
||
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
250 |
MessageDigest md; |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
251 |
try { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
252 |
md = MessageDigest.getInstance( |
50768 | 253 |
context.negotiatedCipherSuite.hashAlg.name); |
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
254 |
} catch (NoSuchAlgorithmException nsae) { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
255 |
throw new RuntimeException( |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
256 |
"MessageDigest algorithm " + |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
257 |
context.negotiatedCipherSuite.hashAlg.name + |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
258 |
" is not available", nsae); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
259 |
} |
50768 | 260 |
byte[] headerBytes = clientHello.getHeaderBytes(); |
261 |
md.update(headerBytes); |
|
262 |
byte[] headerCookie = md.digest(secret); |
|
30904 | 263 |
|
50768 | 264 |
// hash of ClientHello handshake message |
265 |
context.handshakeHash.update(); |
|
266 |
byte[] clientHelloHash = context.handshakeHash.digest(); |
|
30904 | 267 |
|
50768 | 268 |
// version and cipher suite |
269 |
// |
|
270 |
// Store the negotiated cipher suite in the cookie as well. |
|
271 |
// cookie[0]/[1]: cipher suite |
|
272 |
// cookie[2]: cookie version |
|
273 |
// + (hash length): Mac(ClientHello header) |
|
274 |
// + (hash length): Hash(ClientHello) |
|
275 |
byte[] prefix = new byte[] { |
|
276 |
(byte)((context.negotiatedCipherSuite.id >> 8) & 0xFF), |
|
277 |
(byte)(context.negotiatedCipherSuite.id & 0xFF), |
|
278 |
(byte)((version >> 24) & 0xFF) |
|
279 |
}; |
|
30904 | 280 |
|
50768 | 281 |
byte[] cookie = Arrays.copyOf(prefix, |
282 |
prefix.length + headerCookie.length + clientHelloHash.length); |
|
283 |
System.arraycopy(headerCookie, 0, cookie, |
|
284 |
prefix.length, headerCookie.length); |
|
285 |
System.arraycopy(clientHelloHash, 0, cookie, |
|
286 |
prefix.length + headerCookie.length, clientHelloHash.length); |
|
287 |
||
288 |
return cookie; |
|
30904 | 289 |
} |
290 |
||
50768 | 291 |
@Override |
292 |
boolean isCookieValid(ServerHandshakeContext context, |
|
293 |
ClientHelloMessage clientHello, byte[] cookie) throws IOException { |
|
294 |
// no cookie exchange or not a valid cookie length |
|
295 |
if ((cookie == null) || (cookie.length <= 32)) { // 32: roughly |
|
296 |
return false; |
|
297 |
} |
|
298 |
||
299 |
int csId = ((cookie[0] & 0xFF) << 8) | (cookie[1] & 0xFF); |
|
300 |
CipherSuite cs = CipherSuite.valueOf(csId); |
|
301 |
if (cs == null || cs.hashAlg == null || cs.hashAlg.hashLength == 0) { |
|
302 |
return false; |
|
303 |
} |
|
30904 | 304 |
|
50768 | 305 |
int hashLen = cs.hashAlg.hashLength; |
306 |
if (cookie.length != (3 + hashLen * 2)) { |
|
307 |
return false; |
|
308 |
} |
|
309 |
||
310 |
byte[] prevHeadCookie = |
|
311 |
Arrays.copyOfRange(cookie, 3, 3 + hashLen); |
|
312 |
byte[] prevClientHelloHash = |
|
313 |
Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length); |
|
314 |
||
315 |
byte[] secret; |
|
316 |
synchronized (this) { |
|
317 |
if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) { |
|
318 |
secret = cookieSecret; |
|
319 |
} else { |
|
320 |
secret = legacySecret; // including out of window cookies |
|
321 |
} |
|
30904 | 322 |
} |
323 |
||
53734
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
324 |
MessageDigest md; |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
325 |
try { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
326 |
md = MessageDigest.getInstance(cs.hashAlg.name); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
327 |
} catch (NoSuchAlgorithmException nsae) { |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
328 |
throw new RuntimeException( |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
329 |
"MessageDigest algorithm " + |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
330 |
cs.hashAlg.name + " is not available", nsae); |
cb1642ccc732
8217835: Remove the experimental SunJSSE FIPS compliant mode
xuelei
parents:
50768
diff
changeset
|
331 |
} |
50768 | 332 |
byte[] headerBytes = clientHello.getHeaderBytes(); |
333 |
md.update(headerBytes); |
|
334 |
byte[] headerCookie = md.digest(secret); |
|
335 |
||
336 |
if (!Arrays.equals(headerCookie, prevHeadCookie)) { |
|
337 |
return false; |
|
30904 | 338 |
} |
50768 | 339 |
|
340 |
// Use the ClientHello hash in the cookie for transtript |
|
341 |
// hash calculation for stateless HelloRetryRequest. |
|
342 |
// |
|
343 |
// Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) = |
|
344 |
// Hash(message_hash || /* Handshake type */ |
|
345 |
// 00 00 Hash.length || /* Handshake message length (bytes) */ |
|
346 |
// Hash(ClientHello1) || /* Hash of ClientHello1 */ |
|
347 |
// HelloRetryRequest || ... || Mn) |
|
30904 | 348 |
|
50768 | 349 |
// Reproduce HelloRetryRequest handshake message |
350 |
byte[] hrrMessage = |
|
351 |
ServerHello.hrrReproducer.produce(context, clientHello); |
|
352 |
context.handshakeHash.push(hrrMessage); |
|
353 |
||
354 |
// Construct the 1st ClientHello message for transcript hash |
|
355 |
byte[] hashedClientHello = new byte[4 + hashLen]; |
|
356 |
hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id; |
|
357 |
hashedClientHello[1] = (byte)0x00; |
|
358 |
hashedClientHello[2] = (byte)0x00; |
|
359 |
hashedClientHello[3] = (byte)(hashLen & 0xFF); |
|
360 |
System.arraycopy(prevClientHelloHash, 0, |
|
361 |
hashedClientHello, 4, hashLen); |
|
362 |
||
363 |
context.handshakeHash.push(hashedClientHello); |
|
364 |
||
365 |
return true; |
|
366 |
} |
|
30904 | 367 |
} |
368 |
} |