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 }