|
1 /* |
|
2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. |
|
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.nio.ByteBuffer; |
|
30 import java.text.MessageFormat; |
|
31 import java.util.Locale; |
|
32 import javax.net.ssl.SSLProtocolException; |
|
33 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_VERSIONS; |
|
34 import sun.security.ssl.SSLExtension.ExtensionConsumer; |
|
35 import static sun.security.ssl.SSLExtension.HRR_SUPPORTED_VERSIONS; |
|
36 import static sun.security.ssl.SSLExtension.SH_SUPPORTED_VERSIONS; |
|
37 import sun.security.ssl.SSLExtension.SSLExtensionSpec; |
|
38 import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
39 |
|
40 /** |
|
41 * Pack of the "supported_versions" extensions. |
|
42 */ |
|
43 final class SupportedVersionsExtension { |
|
44 static final HandshakeProducer chNetworkProducer = |
|
45 new CHSupportedVersionsProducer(); |
|
46 static final ExtensionConsumer chOnLoadConsumer = |
|
47 new CHSupportedVersionsConsumer(); |
|
48 static final SSLStringizer chStringizer = |
|
49 new CHSupportedVersionsStringizer(); |
|
50 |
|
51 static final HandshakeProducer shNetworkProducer = |
|
52 new SHSupportedVersionsProducer(); |
|
53 static final ExtensionConsumer shOnLoadConsumer = |
|
54 new SHSupportedVersionsConsumer(); |
|
55 static final SSLStringizer shStringizer = |
|
56 new SHSupportedVersionsStringizer(); |
|
57 |
|
58 static final HandshakeProducer hrrNetworkProducer = |
|
59 new HRRSupportedVersionsProducer(); |
|
60 static final ExtensionConsumer hrrOnLoadConsumer = |
|
61 new HRRSupportedVersionsConsumer(); |
|
62 static final HandshakeProducer hrrReproducer = |
|
63 new HRRSupportedVersionsReproducer(); |
|
64 static final SSLStringizer hrrStringizer = |
|
65 new SHSupportedVersionsStringizer(); |
|
66 /** |
|
67 * The "supported_versions" extension in ClientHello. |
|
68 */ |
|
69 static final class CHSupportedVersionsSpec implements SSLExtensionSpec { |
|
70 final int[] requestedProtocols; |
|
71 |
|
72 private CHSupportedVersionsSpec(int[] requestedProtocols) { |
|
73 this.requestedProtocols = requestedProtocols; |
|
74 } |
|
75 |
|
76 private CHSupportedVersionsSpec(ByteBuffer m) throws IOException { |
|
77 if (m.remaining() < 3) { // 1: the length of the list |
|
78 // +2: one version at least |
|
79 throw new SSLProtocolException( |
|
80 "Invalid supported_versions extension: insufficient data"); |
|
81 } |
|
82 |
|
83 byte[] vbs = Record.getBytes8(m); // Get the version bytes. |
|
84 if (m.hasRemaining()) { |
|
85 throw new SSLProtocolException( |
|
86 "Invalid supported_versions extension: unknown extra data"); |
|
87 } |
|
88 |
|
89 if (vbs == null || vbs.length == 0 || (vbs.length & 0x01) != 0) { |
|
90 throw new SSLProtocolException( |
|
91 "Invalid supported_versions extension: incomplete data"); |
|
92 } |
|
93 |
|
94 int[] protocols = new int[vbs.length >> 1]; |
|
95 for (int i = 0, j = 0; i < vbs.length;) { |
|
96 byte major = vbs[i++]; |
|
97 byte minor = vbs[i++]; |
|
98 protocols[j++] = ((major & 0xFF) << 8) | (minor & 0xFF); |
|
99 } |
|
100 |
|
101 this.requestedProtocols = protocols; |
|
102 } |
|
103 |
|
104 @Override |
|
105 public String toString() { |
|
106 MessageFormat messageFormat = new MessageFormat( |
|
107 "\"versions\": '['{0}']'", Locale.ENGLISH); |
|
108 |
|
109 if (requestedProtocols == null || requestedProtocols.length == 0) { |
|
110 Object[] messageFields = { |
|
111 "<no supported version specified>" |
|
112 }; |
|
113 return messageFormat.format(messageFields); |
|
114 } else { |
|
115 StringBuilder builder = new StringBuilder(512); |
|
116 boolean isFirst = true; |
|
117 for (int pv : requestedProtocols) { |
|
118 if (isFirst) { |
|
119 isFirst = false; |
|
120 } else { |
|
121 builder.append(", "); |
|
122 } |
|
123 |
|
124 builder.append(ProtocolVersion.nameOf(pv)); |
|
125 } |
|
126 |
|
127 Object[] messageFields = { |
|
128 builder.toString() |
|
129 }; |
|
130 |
|
131 return messageFormat.format(messageFields); |
|
132 } |
|
133 } |
|
134 } |
|
135 |
|
136 private static final |
|
137 class CHSupportedVersionsStringizer implements SSLStringizer { |
|
138 @Override |
|
139 public String toString(ByteBuffer buffer) { |
|
140 try { |
|
141 return (new CHSupportedVersionsSpec(buffer)).toString(); |
|
142 } catch (IOException ioe) { |
|
143 // For debug logging only, so please swallow exceptions. |
|
144 return ioe.getMessage(); |
|
145 } |
|
146 } |
|
147 } |
|
148 |
|
149 /** |
|
150 * Network data producer of a "supported_versions" extension in ClientHello. |
|
151 */ |
|
152 private static final |
|
153 class CHSupportedVersionsProducer implements HandshakeProducer { |
|
154 // Prevent instantiation of this class. |
|
155 private CHSupportedVersionsProducer() { |
|
156 // blank |
|
157 } |
|
158 |
|
159 @Override |
|
160 public byte[] produce(ConnectionContext context, |
|
161 HandshakeMessage message) throws IOException { |
|
162 // The producing happens in client side only. |
|
163 ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
164 |
|
165 // Is it a supported and enabled extension? |
|
166 if (!chc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { |
|
167 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
168 SSLLogger.fine( |
|
169 "Ignore unavailable extension: " + |
|
170 CH_SUPPORTED_VERSIONS.name); |
|
171 } |
|
172 return null; |
|
173 } |
|
174 |
|
175 // Produce the extension. |
|
176 // |
|
177 // The activated protocols are used as the supported versions. |
|
178 int[] protocols = new int[chc.activeProtocols.size()]; |
|
179 int verLen = protocols.length * 2; |
|
180 byte[] extData = new byte[verLen + 1]; // 1: versions length |
|
181 extData[0] = (byte)(verLen & 0xFF); |
|
182 int i = 0, j = 1; |
|
183 for (ProtocolVersion pv : chc.activeProtocols) { |
|
184 protocols[i++] = pv.id; |
|
185 extData[j++] = pv.major; |
|
186 extData[j++] = pv.minor; |
|
187 } |
|
188 |
|
189 // Update the context. |
|
190 chc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, |
|
191 new CHSupportedVersionsSpec(protocols)); |
|
192 |
|
193 return extData; |
|
194 } |
|
195 } |
|
196 |
|
197 /** |
|
198 * Network data consumer of a "supported_versions" extension in ClientHello. |
|
199 */ |
|
200 private static final |
|
201 class CHSupportedVersionsConsumer implements ExtensionConsumer { |
|
202 // Prevent instantiation of this class. |
|
203 private CHSupportedVersionsConsumer() { |
|
204 // blank |
|
205 } |
|
206 |
|
207 @Override |
|
208 public void consume(ConnectionContext context, |
|
209 HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
210 // The consuming happens in server side only. |
|
211 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
212 |
|
213 // Is it a supported and enabled extension? |
|
214 if (!shc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { |
|
215 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
216 SSLLogger.fine( |
|
217 "Ignore unavailable extension: " + |
|
218 CH_SUPPORTED_VERSIONS.name); |
|
219 } |
|
220 return; // ignore the extension |
|
221 } |
|
222 |
|
223 // Parse the extension. |
|
224 CHSupportedVersionsSpec spec; |
|
225 try { |
|
226 spec = new CHSupportedVersionsSpec(buffer); |
|
227 } catch (IOException ioe) { |
|
228 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
229 return; // fatal() always throws, make the compiler happy. |
|
230 } |
|
231 |
|
232 // Update the context. |
|
233 shc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, spec); |
|
234 |
|
235 // No impact on session resumption. |
|
236 // |
|
237 // Note that the protocol version negotiation happens before the |
|
238 // session resumption negotiation. And the session resumption |
|
239 // negotiation depends on the negotiated protocol version. |
|
240 } |
|
241 } |
|
242 |
|
243 /** |
|
244 * The "supported_versions" extension in ServerHello and HelloRetryRequest. |
|
245 */ |
|
246 static final class SHSupportedVersionsSpec implements SSLExtensionSpec { |
|
247 final int selectedVersion; |
|
248 |
|
249 private SHSupportedVersionsSpec(ProtocolVersion selectedVersion) { |
|
250 this.selectedVersion = selectedVersion.id; |
|
251 } |
|
252 |
|
253 private SHSupportedVersionsSpec(ByteBuffer m) throws IOException { |
|
254 if (m.remaining() != 2) { // 2: the selected version |
|
255 throw new SSLProtocolException( |
|
256 "Invalid supported_versions: insufficient data"); |
|
257 } |
|
258 |
|
259 byte major = m.get(); |
|
260 byte minor = m.get(); |
|
261 this.selectedVersion = ((major & 0xFF) << 8) | (minor & 0xFF); |
|
262 } |
|
263 |
|
264 @Override |
|
265 public String toString() { |
|
266 MessageFormat messageFormat = new MessageFormat( |
|
267 "\"selected version\": '['{0}']'", Locale.ENGLISH); |
|
268 |
|
269 Object[] messageFields = { |
|
270 ProtocolVersion.nameOf(selectedVersion) |
|
271 }; |
|
272 return messageFormat.format(messageFields); |
|
273 } |
|
274 } |
|
275 |
|
276 private static final |
|
277 class SHSupportedVersionsStringizer implements SSLStringizer { |
|
278 @Override |
|
279 public String toString(ByteBuffer buffer) { |
|
280 try { |
|
281 return (new SHSupportedVersionsSpec(buffer)).toString(); |
|
282 } catch (IOException ioe) { |
|
283 // For debug logging only, so please swallow exceptions. |
|
284 return ioe.getMessage(); |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 /** |
|
290 * Network data producer of a "supported_versions" extension in ServerHello. |
|
291 */ |
|
292 private static final |
|
293 class SHSupportedVersionsProducer implements HandshakeProducer { |
|
294 // Prevent instantiation of this class. |
|
295 private SHSupportedVersionsProducer() { |
|
296 // blank |
|
297 } |
|
298 |
|
299 @Override |
|
300 public byte[] produce(ConnectionContext context, |
|
301 HandshakeMessage message) throws IOException { |
|
302 // The producing happens in server side only. |
|
303 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
304 |
|
305 // In response to supported_versions request only |
|
306 CHSupportedVersionsSpec svs = (CHSupportedVersionsSpec) |
|
307 shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS); |
|
308 if (svs == null) { |
|
309 // Unlikely, no key_share extension requested. |
|
310 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
311 SSLLogger.warning( |
|
312 "Ignore unavailable supported_versions extension"); |
|
313 } |
|
314 return null; |
|
315 } |
|
316 |
|
317 // Is it a supported and enabled extension? |
|
318 if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { |
|
319 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
320 SSLLogger.fine( |
|
321 "Ignore unavailable extension: " + |
|
322 SH_SUPPORTED_VERSIONS.name); |
|
323 } |
|
324 return null; |
|
325 } |
|
326 |
|
327 // Produce the extension. |
|
328 byte[] extData = new byte[2]; |
|
329 extData[0] = shc.negotiatedProtocol.major; |
|
330 extData[1] = shc.negotiatedProtocol.minor; |
|
331 |
|
332 // Update the context. |
|
333 shc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, |
|
334 new SHSupportedVersionsSpec(shc.negotiatedProtocol)); |
|
335 |
|
336 return extData; |
|
337 } |
|
338 } |
|
339 |
|
340 /** |
|
341 * Network data consumer of a "supported_versions" extension in ServerHello. |
|
342 */ |
|
343 private static final |
|
344 class SHSupportedVersionsConsumer implements ExtensionConsumer { |
|
345 // Prevent instantiation of this class. |
|
346 private SHSupportedVersionsConsumer() { |
|
347 // blank |
|
348 } |
|
349 |
|
350 @Override |
|
351 public void consume(ConnectionContext context, |
|
352 HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
353 // The consuming happens in client side only. |
|
354 ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
355 |
|
356 // Is it a supported and enabled extension? |
|
357 if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { |
|
358 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
359 SSLLogger.fine( |
|
360 "Ignore unavailable extension: " + |
|
361 SH_SUPPORTED_VERSIONS.name); |
|
362 } |
|
363 return; // ignore the extension |
|
364 } |
|
365 |
|
366 // Parse the extension. |
|
367 SHSupportedVersionsSpec spec; |
|
368 try { |
|
369 spec = new SHSupportedVersionsSpec(buffer); |
|
370 } catch (IOException ioe) { |
|
371 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
372 return; // fatal() always throws, make the compiler happy. |
|
373 } |
|
374 |
|
375 // Update the context. |
|
376 chc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, spec); |
|
377 |
|
378 // No impact on session resumption. |
|
379 // |
|
380 // Note that the protocol version negotiation happens before the |
|
381 // session resumption negotiation. And the session resumption |
|
382 // negotiation depends on the negotiated protocol version. |
|
383 } |
|
384 } |
|
385 |
|
386 /** |
|
387 * Network data producer of a "supported_versions" extension in |
|
388 * HelloRetryRequest. |
|
389 */ |
|
390 private static final |
|
391 class HRRSupportedVersionsProducer implements HandshakeProducer { |
|
392 |
|
393 // Prevent instantiation of this class. |
|
394 private HRRSupportedVersionsProducer() { |
|
395 // blank |
|
396 } |
|
397 |
|
398 @Override |
|
399 public byte[] produce(ConnectionContext context, |
|
400 HandshakeMessage message) throws IOException { |
|
401 // The producing happens in server side only. |
|
402 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
403 |
|
404 // Is it a supported and enabled extension? |
|
405 if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { |
|
406 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
407 SSLLogger.fine( |
|
408 "Ignore unavailable extension: " + |
|
409 HRR_SUPPORTED_VERSIONS.name); |
|
410 } |
|
411 return null; |
|
412 } |
|
413 |
|
414 // Produce the extension. |
|
415 byte[] extData = new byte[2]; |
|
416 extData[0] = shc.negotiatedProtocol.major; |
|
417 extData[1] = shc.negotiatedProtocol.minor; |
|
418 |
|
419 // Update the context. |
|
420 shc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, |
|
421 new SHSupportedVersionsSpec(shc.negotiatedProtocol)); |
|
422 |
|
423 return extData; |
|
424 } |
|
425 } |
|
426 |
|
427 /** |
|
428 * Network data consumer of a "supported_versions" extension in |
|
429 * HelloRetryRequest. |
|
430 */ |
|
431 private static final |
|
432 class HRRSupportedVersionsConsumer implements ExtensionConsumer { |
|
433 |
|
434 // Prevent instantiation of this class. |
|
435 private HRRSupportedVersionsConsumer() { |
|
436 // blank |
|
437 } |
|
438 |
|
439 @Override |
|
440 public void consume(ConnectionContext context, |
|
441 HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
442 |
|
443 // The consuming happens in client side only. |
|
444 ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
445 |
|
446 // Is it a supported and enabled extension? |
|
447 if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { |
|
448 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
449 SSLLogger.fine( |
|
450 "Ignore unavailable extension: " + |
|
451 HRR_SUPPORTED_VERSIONS.name); |
|
452 } |
|
453 return; // ignore the extension |
|
454 } |
|
455 |
|
456 // Parse the extension. |
|
457 SHSupportedVersionsSpec spec; |
|
458 try { |
|
459 spec = new SHSupportedVersionsSpec(buffer); |
|
460 } catch (IOException ioe) { |
|
461 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
462 return; // fatal() always throws, make the compiler happy. |
|
463 } |
|
464 |
|
465 // Update the context. |
|
466 chc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, spec); |
|
467 |
|
468 // No impact on session resumption. |
|
469 // |
|
470 // Note that the protocol version negotiation happens before the |
|
471 // session resumption negotiation. And the session resumption |
|
472 // negotiation depends on the negotiated protocol version. |
|
473 } |
|
474 } |
|
475 |
|
476 /** |
|
477 * Network data producer of a "supported_versions" extension for stateless |
|
478 * HelloRetryRequest reconstruction. |
|
479 */ |
|
480 private static final |
|
481 class HRRSupportedVersionsReproducer implements HandshakeProducer { |
|
482 // Prevent instantiation of this class. |
|
483 private HRRSupportedVersionsReproducer() { |
|
484 // blank |
|
485 } |
|
486 |
|
487 @Override |
|
488 public byte[] produce(ConnectionContext context, |
|
489 HandshakeMessage message) throws IOException { |
|
490 // The producing happens in server side only. |
|
491 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
492 |
|
493 // Is it a supported and enabled extension? |
|
494 if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { |
|
495 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
496 SSLLogger.fine( |
|
497 "[Reproduce] Ignore unavailable extension: " + |
|
498 HRR_SUPPORTED_VERSIONS.name); |
|
499 } |
|
500 return null; |
|
501 } |
|
502 |
|
503 // Produce the extension. |
|
504 byte[] extData = new byte[2]; |
|
505 extData[0] = shc.negotiatedProtocol.major; |
|
506 extData[1] = shc.negotiatedProtocol.minor; |
|
507 |
|
508 return extData; |
|
509 } |
|
510 } |
|
511 } |