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 CHSupportedVersionsSpec(int[] requestedProtocols)72 private CHSupportedVersionsSpec(int[] requestedProtocols) { 73 this.requestedProtocols = requestedProtocols; 74 } 75 CHSupportedVersionsSpec(ByteBuffer m)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 toString()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 toString(ByteBuffer buffer)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. CHSupportedVersionsProducer()155 private CHSupportedVersionsProducer() { 156 // blank 157 } 158 159 @Override produce(ConnectionContext context, HandshakeMessage message)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. CHSupportedVersionsConsumer()203 private CHSupportedVersionsConsumer() { 204 // blank 205 } 206 207 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)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 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 229 } 230 231 // Update the context. 232 shc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, spec); 233 234 // No impact on session resumption. 235 // 236 // Note that the protocol version negotiation happens before the 237 // session resumption negotiation. And the session resumption 238 // negotiation depends on the negotiated protocol version. 239 } 240 } 241 242 /** 243 * The "supported_versions" extension in ServerHello and HelloRetryRequest. 244 */ 245 static final class SHSupportedVersionsSpec implements SSLExtensionSpec { 246 final int selectedVersion; 247 SHSupportedVersionsSpec(ProtocolVersion selectedVersion)248 private SHSupportedVersionsSpec(ProtocolVersion selectedVersion) { 249 this.selectedVersion = selectedVersion.id; 250 } 251 SHSupportedVersionsSpec(ByteBuffer m)252 private SHSupportedVersionsSpec(ByteBuffer m) throws IOException { 253 if (m.remaining() != 2) { // 2: the selected version 254 throw new SSLProtocolException( 255 "Invalid supported_versions: insufficient data"); 256 } 257 258 byte major = m.get(); 259 byte minor = m.get(); 260 this.selectedVersion = ((major & 0xFF) << 8) | (minor & 0xFF); 261 } 262 263 @Override toString()264 public String toString() { 265 MessageFormat messageFormat = new MessageFormat( 266 "\"selected version\": '['{0}']'", Locale.ENGLISH); 267 268 Object[] messageFields = { 269 ProtocolVersion.nameOf(selectedVersion) 270 }; 271 return messageFormat.format(messageFields); 272 } 273 } 274 275 private static final 276 class SHSupportedVersionsStringizer implements SSLStringizer { 277 @Override toString(ByteBuffer buffer)278 public String toString(ByteBuffer buffer) { 279 try { 280 return (new SHSupportedVersionsSpec(buffer)).toString(); 281 } catch (IOException ioe) { 282 // For debug logging only, so please swallow exceptions. 283 return ioe.getMessage(); 284 } 285 } 286 } 287 288 /** 289 * Network data producer of a "supported_versions" extension in ServerHello. 290 */ 291 private static final 292 class SHSupportedVersionsProducer implements HandshakeProducer { 293 // Prevent instantiation of this class. SHSupportedVersionsProducer()294 private SHSupportedVersionsProducer() { 295 // blank 296 } 297 298 @Override produce(ConnectionContext context, HandshakeMessage message)299 public byte[] produce(ConnectionContext context, 300 HandshakeMessage message) throws IOException { 301 // The producing happens in server side only. 302 ServerHandshakeContext shc = (ServerHandshakeContext)context; 303 304 // In response to supported_versions request only 305 CHSupportedVersionsSpec svs = (CHSupportedVersionsSpec) 306 shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS); 307 if (svs == null) { 308 // Unlikely, no key_share extension requested. 309 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 310 SSLLogger.warning( 311 "Ignore unavailable supported_versions extension"); 312 } 313 return null; 314 } 315 316 // Is it a supported and enabled extension? 317 if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { 318 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 319 SSLLogger.fine( 320 "Ignore unavailable extension: " + 321 SH_SUPPORTED_VERSIONS.name); 322 } 323 return null; 324 } 325 326 // Produce the extension. 327 byte[] extData = new byte[2]; 328 extData[0] = shc.negotiatedProtocol.major; 329 extData[1] = shc.negotiatedProtocol.minor; 330 331 // Update the context. 332 shc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, 333 new SHSupportedVersionsSpec(shc.negotiatedProtocol)); 334 335 return extData; 336 } 337 } 338 339 /** 340 * Network data consumer of a "supported_versions" extension in ServerHello. 341 */ 342 private static final 343 class SHSupportedVersionsConsumer implements ExtensionConsumer { 344 // Prevent instantiation of this class. SHSupportedVersionsConsumer()345 private SHSupportedVersionsConsumer() { 346 // blank 347 } 348 349 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)350 public void consume(ConnectionContext context, 351 HandshakeMessage message, ByteBuffer buffer) throws IOException { 352 // The consuming happens in client side only. 353 ClientHandshakeContext chc = (ClientHandshakeContext)context; 354 355 // Is it a supported and enabled extension? 356 if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { 357 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 358 SSLLogger.fine( 359 "Ignore unavailable extension: " + 360 SH_SUPPORTED_VERSIONS.name); 361 } 362 return; // ignore the extension 363 } 364 365 // Parse the extension. 366 SHSupportedVersionsSpec spec; 367 try { 368 spec = new SHSupportedVersionsSpec(buffer); 369 } catch (IOException ioe) { 370 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 371 } 372 373 // Update the context. 374 chc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, spec); 375 376 // No impact on session resumption. 377 // 378 // Note that the protocol version negotiation happens before the 379 // session resumption negotiation. And the session resumption 380 // negotiation depends on the negotiated protocol version. 381 } 382 } 383 384 /** 385 * Network data producer of a "supported_versions" extension in 386 * HelloRetryRequest. 387 */ 388 private static final 389 class HRRSupportedVersionsProducer implements HandshakeProducer { 390 391 // Prevent instantiation of this class. HRRSupportedVersionsProducer()392 private HRRSupportedVersionsProducer() { 393 // blank 394 } 395 396 @Override produce(ConnectionContext context, HandshakeMessage message)397 public byte[] produce(ConnectionContext context, 398 HandshakeMessage message) throws IOException { 399 // The producing happens in server side only. 400 ServerHandshakeContext shc = (ServerHandshakeContext)context; 401 402 // Is it a supported and enabled extension? 403 if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { 404 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 405 SSLLogger.fine( 406 "Ignore unavailable extension: " + 407 HRR_SUPPORTED_VERSIONS.name); 408 } 409 return null; 410 } 411 412 // Produce the extension. 413 byte[] extData = new byte[2]; 414 extData[0] = shc.negotiatedProtocol.major; 415 extData[1] = shc.negotiatedProtocol.minor; 416 417 // Update the context. 418 shc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, 419 new SHSupportedVersionsSpec(shc.negotiatedProtocol)); 420 421 return extData; 422 } 423 } 424 425 /** 426 * Network data consumer of a "supported_versions" extension in 427 * HelloRetryRequest. 428 */ 429 private static final 430 class HRRSupportedVersionsConsumer implements ExtensionConsumer { 431 432 // Prevent instantiation of this class. HRRSupportedVersionsConsumer()433 private HRRSupportedVersionsConsumer() { 434 // blank 435 } 436 437 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)438 public void consume(ConnectionContext context, 439 HandshakeMessage message, ByteBuffer buffer) throws IOException { 440 441 // The consuming happens in client side only. 442 ClientHandshakeContext chc = (ClientHandshakeContext)context; 443 444 // Is it a supported and enabled extension? 445 if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { 446 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 447 SSLLogger.fine( 448 "Ignore unavailable extension: " + 449 HRR_SUPPORTED_VERSIONS.name); 450 } 451 return; // ignore the extension 452 } 453 454 // Parse the extension. 455 SHSupportedVersionsSpec spec; 456 try { 457 spec = new SHSupportedVersionsSpec(buffer); 458 } catch (IOException ioe) { 459 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 460 } 461 462 // Update the context. 463 chc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, spec); 464 465 // No impact on session resumption. 466 // 467 // Note that the protocol version negotiation happens before the 468 // session resumption negotiation. And the session resumption 469 // negotiation depends on the negotiated protocol version. 470 } 471 } 472 473 /** 474 * Network data producer of a "supported_versions" extension for stateless 475 * HelloRetryRequest reconstruction. 476 */ 477 private static final 478 class HRRSupportedVersionsReproducer implements HandshakeProducer { 479 // Prevent instantiation of this class. HRRSupportedVersionsReproducer()480 private HRRSupportedVersionsReproducer() { 481 // blank 482 } 483 484 @Override produce(ConnectionContext context, HandshakeMessage message)485 public byte[] produce(ConnectionContext context, 486 HandshakeMessage message) throws IOException { 487 // The producing happens in server side only. 488 ServerHandshakeContext shc = (ServerHandshakeContext)context; 489 490 // Is it a supported and enabled extension? 491 if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { 492 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 493 SSLLogger.fine( 494 "[Reproduce] Ignore unavailable extension: " + 495 HRR_SUPPORTED_VERSIONS.name); 496 } 497 return null; 498 } 499 500 // Produce the extension. 501 byte[] extData = new byte[2]; 502 extData[0] = shc.negotiatedProtocol.major; 503 extData[1] = shc.negotiatedProtocol.minor; 504 505 return extData; 506 } 507 } 508 } 509