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 chOnLoadConcumer = 47 new CHSupportedVersionsConsumer(); 48 static final SSLStringize chStringize = 49 new CHSupportedVersionsStringize(); 50 51 static final HandshakeProducer shNetworkProducer = 52 new SHSupportedVersionsProducer(); 53 static final ExtensionConsumer shOnLoadConcumer = 54 new SHSupportedVersionsConsumer(); 55 static final SSLStringize shStringize = 56 new SHSupportedVersionsStringize(); 57 58 static final HandshakeProducer hrrNetworkProducer = 59 new HRRSupportedVersionsProducer(); 60 static final ExtensionConsumer hrrOnLoadConcumer = 61 new HRRSupportedVersionsConsumer(); 62 static final HandshakeProducer hrrReproducer = 63 new HRRSupportedVersionsReproducer(); 64 static final SSLStringize hrrStringize = 65 new SHSupportedVersionsStringize(); 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 CHSupportedVersionsStringize implements SSLStringize { 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 comsuming 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 SHSupportedVersionsStringize implements SSLStringize { 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 comsuming 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 comsuming 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 "[Reprocude] 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 }