src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java
changeset 50768 68fa3d4026ea
child 53064 103ed9569fc8
equal deleted inserted replaced
50767:356eaea05bf0 50768:68fa3d4026ea
       
     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 }