src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java
changeset 50768 68fa3d4026ea
child 53056 9041178a0b69
child 56858 829e9b5ace08
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.security.CryptoPrimitive;
       
    31 import java.security.GeneralSecurityException;
       
    32 import java.text.MessageFormat;
       
    33 import java.util.Arrays;
       
    34 import java.util.Collections;
       
    35 import java.util.EnumSet;
       
    36 import java.util.LinkedList;
       
    37 import java.util.List;
       
    38 import java.util.Locale;
       
    39 import java.util.Map;
       
    40 import javax.net.ssl.SSLProtocolException;
       
    41 import sun.security.ssl.DHKeyExchange.DHECredentials;
       
    42 import sun.security.ssl.DHKeyExchange.DHEPossession;
       
    43 import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
       
    44 import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
       
    45 import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
       
    46 import sun.security.ssl.SSLExtension.ExtensionConsumer;
       
    47 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
       
    48 import sun.security.ssl.SSLHandshake.HandshakeMessage;
       
    49 import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
       
    50 import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
       
    51 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
       
    52 import sun.security.util.HexDumpEncoder;
       
    53 
       
    54 /**
       
    55  * Pack of the "key_share" extensions.
       
    56  */
       
    57 final class KeyShareExtension {
       
    58     static final HandshakeProducer chNetworkProducer =
       
    59             new CHKeyShareProducer();
       
    60     static final ExtensionConsumer chOnLoadConsumer =
       
    61             new CHKeyShareConsumer();
       
    62     static final SSLStringizer chStringizer =
       
    63             new CHKeyShareStringizer();
       
    64 
       
    65     static final HandshakeProducer shNetworkProducer =
       
    66             new SHKeyShareProducer();
       
    67     static final ExtensionConsumer shOnLoadConsumer =
       
    68             new SHKeyShareConsumer();
       
    69     static final HandshakeAbsence shOnLoadAbsence =
       
    70             new SHKeyShareAbsence();
       
    71     static final SSLStringizer shStringizer =
       
    72             new SHKeyShareStringizer();
       
    73 
       
    74     static final HandshakeProducer hrrNetworkProducer =
       
    75             new HRRKeyShareProducer();
       
    76     static final ExtensionConsumer hrrOnLoadConsumer =
       
    77             new HRRKeyShareConsumer();
       
    78     static final HandshakeProducer hrrNetworkReproducer =
       
    79             new HRRKeyShareReproducer();
       
    80     static final SSLStringizer hrrStringizer =
       
    81             new HRRKeyShareStringizer();
       
    82 
       
    83     /**
       
    84      * The key share entry used in "key_share" extensions.
       
    85      */
       
    86     private static final class KeyShareEntry {
       
    87         final int namedGroupId;
       
    88         final byte[] keyExchange;
       
    89 
       
    90         private KeyShareEntry(int namedGroupId, byte[] keyExchange) {
       
    91             this.namedGroupId = namedGroupId;
       
    92             this.keyExchange = keyExchange;
       
    93         }
       
    94 
       
    95         private byte[] getEncoded() {
       
    96             byte[] buffer = new byte[keyExchange.length + 4];
       
    97                                             //  2: named group id
       
    98                                             // +2: key exchange length
       
    99             ByteBuffer m = ByteBuffer.wrap(buffer);
       
   100             try {
       
   101                 Record.putInt16(m, namedGroupId);
       
   102                 Record.putBytes16(m, keyExchange);
       
   103             } catch (IOException ioe) {
       
   104                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   105                     SSLLogger.warning(
       
   106                         "Unlikely IOException", ioe);
       
   107                 }
       
   108             }
       
   109 
       
   110             return buffer;
       
   111         }
       
   112 
       
   113         private int getEncodedSize() {
       
   114             return keyExchange.length + 4;  //  2: named group id
       
   115                                             // +2: key exchange length
       
   116         }
       
   117 
       
   118         @Override
       
   119         public String toString() {
       
   120             MessageFormat messageFormat = new MessageFormat(
       
   121                 "\n'{'\n" +
       
   122                 "  \"named group\": {0}\n" +
       
   123                 "  \"key_exchange\": '{'\n" +
       
   124                 "{1}\n" +
       
   125                 "  '}'\n" +
       
   126                 "'}',", Locale.ENGLISH);
       
   127 
       
   128             HexDumpEncoder hexEncoder = new HexDumpEncoder();
       
   129             Object[] messageFields = {
       
   130                 NamedGroup.nameOf(namedGroupId),
       
   131                 Utilities.indent(hexEncoder.encode(keyExchange), "    ")
       
   132             };
       
   133 
       
   134             return messageFormat.format(messageFields);
       
   135         }
       
   136     }
       
   137 
       
   138     /**
       
   139      * The "key_share" extension in a ClientHello handshake message.
       
   140      */
       
   141     static final class CHKeyShareSpec implements SSLExtensionSpec {
       
   142         final List<KeyShareEntry> clientShares;
       
   143 
       
   144         private CHKeyShareSpec(List<KeyShareEntry> clientShares) {
       
   145             this.clientShares = clientShares;
       
   146         }
       
   147 
       
   148         private CHKeyShareSpec(ByteBuffer buffer) throws IOException {
       
   149             // struct {
       
   150             //      KeyShareEntry client_shares<0..2^16-1>;
       
   151             // } KeyShareClientHello;
       
   152             if (buffer.remaining() < 2) {
       
   153                 throw new SSLProtocolException(
       
   154                     "Invalid key_share extension: " +
       
   155                     "insufficient data (length=" + buffer.remaining() + ")");
       
   156             }
       
   157 
       
   158             int listLen = Record.getInt16(buffer);
       
   159             if (listLen != buffer.remaining()) {
       
   160                 throw new SSLProtocolException(
       
   161                     "Invalid key_share extension: " +
       
   162                     "incorrect list length (length=" + listLen + ")");
       
   163             }
       
   164 
       
   165             List<KeyShareEntry> keyShares = new LinkedList<>();
       
   166             while (buffer.hasRemaining()) {
       
   167                 int namedGroupId = Record.getInt16(buffer);
       
   168                 byte[] keyExchange = Record.getBytes16(buffer);
       
   169                 if (keyExchange.length == 0) {
       
   170                     throw new SSLProtocolException(
       
   171                         "Invalid key_share extension: empty key_exchange");
       
   172                 }
       
   173 
       
   174                 keyShares.add(new KeyShareEntry(namedGroupId, keyExchange));
       
   175             }
       
   176 
       
   177             this.clientShares = Collections.unmodifiableList(keyShares);
       
   178         }
       
   179 
       
   180         @Override
       
   181         public String toString() {
       
   182             MessageFormat messageFormat = new MessageFormat(
       
   183                 "\"client_shares\": '['{0}\n']'", Locale.ENGLISH);
       
   184 
       
   185             StringBuilder builder = new StringBuilder(512);
       
   186             for (KeyShareEntry entry : clientShares) {
       
   187                 builder.append(entry.toString());
       
   188             }
       
   189 
       
   190             Object[] messageFields = {
       
   191                 Utilities.indent(builder.toString())
       
   192             };
       
   193 
       
   194             return messageFormat.format(messageFields);
       
   195         }
       
   196     }
       
   197 
       
   198     private static final class CHKeyShareStringizer implements SSLStringizer {
       
   199         @Override
       
   200         public String toString(ByteBuffer buffer) {
       
   201             try {
       
   202                 return (new CHKeyShareSpec(buffer)).toString();
       
   203             } catch (IOException ioe) {
       
   204                 // For debug logging only, so please swallow exceptions.
       
   205                 return ioe.getMessage();
       
   206             }
       
   207         }
       
   208     }
       
   209 
       
   210     /**
       
   211      * Network data producer of the extension in a ClientHello
       
   212      * handshake message.
       
   213      */
       
   214     private static final
       
   215             class CHKeyShareProducer implements HandshakeProducer {
       
   216         // Prevent instantiation of this class.
       
   217         private CHKeyShareProducer() {
       
   218             // blank
       
   219         }
       
   220 
       
   221         @Override
       
   222         public byte[] produce(ConnectionContext context,
       
   223                 HandshakeMessage message) throws IOException {
       
   224             // The producing happens in client side only.
       
   225             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   226 
       
   227             // Is it a supported and enabled extension?
       
   228             if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
       
   229                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   230                     SSLLogger.fine(
       
   231                         "Ignore unavailable key_share extension");
       
   232                 }
       
   233                 return null;
       
   234             }
       
   235 
       
   236             List<NamedGroup> namedGroups;
       
   237             if (chc.serverSelectedNamedGroup != null) {
       
   238                 // Response to HelloRetryRequest
       
   239                 namedGroups = Arrays.asList(chc.serverSelectedNamedGroup);
       
   240             } else {
       
   241                 namedGroups = chc.clientRequestedNamedGroups;
       
   242                 if (namedGroups == null || namedGroups.isEmpty()) {
       
   243                     // No supported groups.
       
   244                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   245                         SSLLogger.warning(
       
   246                             "Ignore key_share extension, no supported groups");
       
   247                     }
       
   248                     return null;
       
   249                 }
       
   250             }
       
   251 
       
   252             List<KeyShareEntry> keyShares = new LinkedList<>();
       
   253             for (NamedGroup ng : namedGroups) {
       
   254                 SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
       
   255                 if (ke == null) {
       
   256                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   257                         SSLLogger.warning(
       
   258                             "No key exchange for named group " + ng.name);
       
   259                     }
       
   260                     continue;
       
   261                 }
       
   262 
       
   263                 SSLPossession[] poses = ke.createPossessions(chc);
       
   264                 for (SSLPossession pos : poses) {
       
   265                     // update the context
       
   266                     chc.handshakePossessions.add(pos);
       
   267                     if (!(pos instanceof ECDHEPossession) &&
       
   268                             !(pos instanceof DHEPossession)) {
       
   269                         // May need more possesion types in the future.
       
   270                         continue;
       
   271                     }
       
   272 
       
   273                     keyShares.add(new KeyShareEntry(ng.id, pos.encode()));
       
   274                 }
       
   275 
       
   276                 // One key share entry only.  Too much key share entries makes
       
   277                 // the ClientHello handshake message really big.
       
   278                 if (!keyShares.isEmpty()) {
       
   279                     break;
       
   280                 }
       
   281             }
       
   282 
       
   283             int listLen = 0;
       
   284             for (KeyShareEntry entry : keyShares) {
       
   285                 listLen += entry.getEncodedSize();
       
   286             }
       
   287             byte[] extData = new byte[listLen + 2];     //  2: list length
       
   288             ByteBuffer m = ByteBuffer.wrap(extData);
       
   289             Record.putInt16(m, listLen);
       
   290             for (KeyShareEntry entry : keyShares) {
       
   291                 m.put(entry.getEncoded());
       
   292             }
       
   293 
       
   294             // update the context
       
   295             chc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE,
       
   296                     new CHKeyShareSpec(keyShares));
       
   297 
       
   298             return extData;
       
   299         }
       
   300     }
       
   301 
       
   302     /**
       
   303      * Network data consumer of the extension in a ClientHello
       
   304      * handshake message.
       
   305      */
       
   306     private static final class CHKeyShareConsumer implements ExtensionConsumer {
       
   307         // Prevent instantiation of this class.
       
   308         private CHKeyShareConsumer() {
       
   309             // blank
       
   310         }
       
   311 
       
   312         @Override
       
   313         public void consume(ConnectionContext context,
       
   314             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   315             // The consuming happens in server side only.
       
   316             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   317 
       
   318             if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) {
       
   319                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   320                     SSLLogger.fine(
       
   321                             "The key_share extension has been loaded");
       
   322                 }
       
   323                 return;
       
   324             }
       
   325 
       
   326             // Is it a supported and enabled extension?
       
   327             if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
       
   328                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   329                     SSLLogger.fine(
       
   330                             "Ignore unavailable key_share extension");
       
   331                 }
       
   332                 return;     // ignore the extension
       
   333             }
       
   334 
       
   335             // Parse the extension
       
   336             CHKeyShareSpec spec;
       
   337             try {
       
   338                 spec = new CHKeyShareSpec(buffer);
       
   339             } catch (IOException ioe) {
       
   340                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   341                 return;     // fatal() always throws, make the compiler happy.
       
   342             }
       
   343 
       
   344             List<SSLCredentials> credentials = new LinkedList<>();
       
   345             for (KeyShareEntry entry : spec.clientShares) {
       
   346                 NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
       
   347                 if (ng == null || !SupportedGroups.isActivatable(
       
   348                         shc.sslConfig.algorithmConstraints, ng)) {
       
   349                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   350                         SSLLogger.fine(
       
   351                                 "Ignore unsupported named group: " +
       
   352                                 NamedGroup.nameOf(entry.namedGroupId));
       
   353                     }
       
   354                     continue;
       
   355                 }
       
   356 
       
   357                 if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
       
   358                     try {
       
   359                         ECDHECredentials ecdhec =
       
   360                             ECDHECredentials.valueOf(ng, entry.keyExchange);
       
   361                         if (ecdhec != null) {
       
   362                             if (!shc.algorithmConstraints.permits(
       
   363                                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   364                                     ecdhec.popPublicKey)) {
       
   365                                 SSLLogger.warning(
       
   366                                         "ECDHE key share entry does not " +
       
   367                                         "comply to algorithm constraints");
       
   368                             } else {
       
   369                                 credentials.add(ecdhec);
       
   370                             }
       
   371                         }
       
   372                     } catch (IOException | GeneralSecurityException ex) {
       
   373                         SSLLogger.warning(
       
   374                                 "Cannot decode named group: " +
       
   375                                 NamedGroup.nameOf(entry.namedGroupId));
       
   376                     }
       
   377                 } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
       
   378                     try {
       
   379                         DHECredentials dhec =
       
   380                                 DHECredentials.valueOf(ng, entry.keyExchange);
       
   381                         if (dhec != null) {
       
   382                             if (!shc.algorithmConstraints.permits(
       
   383                                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   384                                     dhec.popPublicKey)) {
       
   385                                 SSLLogger.warning(
       
   386                                         "DHE key share entry does not " +
       
   387                                         "comply to algorithm constraints");
       
   388                             } else {
       
   389                                 credentials.add(dhec);
       
   390                             }
       
   391                         }
       
   392                     } catch (IOException | GeneralSecurityException ex) {
       
   393                         SSLLogger.warning(
       
   394                                 "Cannot decode named group: " +
       
   395                                 NamedGroup.nameOf(entry.namedGroupId));
       
   396                     }
       
   397                 }
       
   398             }
       
   399 
       
   400             if (!credentials.isEmpty()) {
       
   401                 shc.handshakeCredentials.addAll(credentials);
       
   402             } else {
       
   403                 // New handshake credentials are required from the client side.
       
   404                 shc.handshakeProducers.put(
       
   405                         SSLHandshake.HELLO_RETRY_REQUEST.id,
       
   406                         SSLHandshake.HELLO_RETRY_REQUEST);
       
   407             }
       
   408 
       
   409             // update the context
       
   410             shc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, spec);
       
   411         }
       
   412     }
       
   413 
       
   414     /**
       
   415      * The key share entry used in ServerHello "key_share" extensions.
       
   416      */
       
   417     static final class SHKeyShareSpec implements SSLExtensionSpec {
       
   418         final KeyShareEntry serverShare;
       
   419 
       
   420         SHKeyShareSpec(KeyShareEntry serverShare) {
       
   421             this.serverShare = serverShare;
       
   422         }
       
   423 
       
   424         private SHKeyShareSpec(ByteBuffer buffer) throws IOException {
       
   425             // struct {
       
   426             //      KeyShareEntry server_share;
       
   427             // } KeyShareServerHello;
       
   428             if (buffer.remaining() < 5) {       // 5: minimal server_share
       
   429                 throw new SSLProtocolException(
       
   430                     "Invalid key_share extension: " +
       
   431                     "insufficient data (length=" + buffer.remaining() + ")");
       
   432             }
       
   433 
       
   434             int namedGroupId = Record.getInt16(buffer);
       
   435             byte[] keyExchange = Record.getBytes16(buffer);
       
   436 
       
   437             if (buffer.hasRemaining()) {
       
   438                 throw new SSLProtocolException(
       
   439                     "Invalid key_share extension: unknown extra data");
       
   440             }
       
   441 
       
   442             this.serverShare = new KeyShareEntry(namedGroupId, keyExchange);
       
   443         }
       
   444 
       
   445         @Override
       
   446         public String toString() {
       
   447             MessageFormat messageFormat = new MessageFormat(
       
   448                 "\"server_share\": '{'\n" +
       
   449                 "  \"named group\": {0}\n" +
       
   450                 "  \"key_exchange\": '{'\n" +
       
   451                 "{1}\n" +
       
   452                 "  '}'\n" +
       
   453                 "'}',", Locale.ENGLISH);
       
   454 
       
   455             HexDumpEncoder hexEncoder = new HexDumpEncoder();
       
   456             Object[] messageFields = {
       
   457                 NamedGroup.nameOf(serverShare.namedGroupId),
       
   458                 Utilities.indent(
       
   459                         hexEncoder.encode(serverShare.keyExchange), "    ")
       
   460             };
       
   461 
       
   462             return messageFormat.format(messageFields);
       
   463         }
       
   464     }
       
   465 
       
   466     private static final class SHKeyShareStringizer implements SSLStringizer {
       
   467         @Override
       
   468         public String toString(ByteBuffer buffer) {
       
   469             try {
       
   470                 return (new SHKeyShareSpec(buffer)).toString();
       
   471             } catch (IOException ioe) {
       
   472                 // For debug logging only, so please swallow exceptions.
       
   473                 return ioe.getMessage();
       
   474             }
       
   475         }
       
   476     }
       
   477 
       
   478     /**
       
   479      * Network data producer of the extension in a ServerHello
       
   480      * handshake message.
       
   481      */
       
   482     private static final class SHKeyShareProducer implements HandshakeProducer {
       
   483         // Prevent instantiation of this class.
       
   484         private SHKeyShareProducer() {
       
   485             // blank
       
   486         }
       
   487 
       
   488         @Override
       
   489         public byte[] produce(ConnectionContext context,
       
   490                 HandshakeMessage message) throws IOException {
       
   491             // The producing happens in client side only.
       
   492             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   493 
       
   494             // In response to key_share request only
       
   495             CHKeyShareSpec kss =
       
   496                     (CHKeyShareSpec)shc.handshakeExtensions.get(
       
   497                             SSLExtension.CH_KEY_SHARE);
       
   498             if (kss == null) {
       
   499                 // Unlikely, no key_share extension requested.
       
   500                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   501                     SSLLogger.warning(
       
   502                             "Ignore, no client key_share extension");
       
   503                 }
       
   504                 return null;
       
   505             }
       
   506 
       
   507             // Is it a supported and enabled extension?
       
   508             if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
       
   509                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   510                     SSLLogger.warning(
       
   511                             "Ignore, no available server key_share extension");
       
   512                 }
       
   513                 return null;
       
   514             }
       
   515 
       
   516             // use requested key share entries
       
   517             if ((shc.handshakeCredentials == null) ||
       
   518                     shc.handshakeCredentials.isEmpty()) {
       
   519                 // Unlikely, HelloRetryRequest should be used ealier.
       
   520                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   521                     SSLLogger.warning(
       
   522                             "No available client key share entries");
       
   523                 }
       
   524                 return null;
       
   525             }
       
   526 
       
   527             KeyShareEntry keyShare = null;
       
   528             for (SSLCredentials cd : shc.handshakeCredentials) {
       
   529                 NamedGroup ng = null;
       
   530                 if (cd instanceof ECDHECredentials) {
       
   531                     ng = ((ECDHECredentials)cd).namedGroup;
       
   532                 } else if (cd instanceof DHECredentials) {
       
   533                     ng = ((DHECredentials)cd).namedGroup;
       
   534                 }
       
   535 
       
   536                 if (ng == null) {
       
   537                     continue;
       
   538                 }
       
   539 
       
   540                 SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
       
   541                 if (ke == null) {
       
   542                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   543                         SSLLogger.warning(
       
   544                             "No key exchange for named group " + ng.name);
       
   545                     }
       
   546                     continue;
       
   547                 }
       
   548 
       
   549                 SSLPossession[] poses = ke.createPossessions(shc);
       
   550                 for (SSLPossession pos : poses) {
       
   551                     if (!(pos instanceof ECDHEPossession) &&
       
   552                             !(pos instanceof DHEPossession)) {
       
   553                         // May need more possesion types in the future.
       
   554                         continue;
       
   555                     }
       
   556 
       
   557                     // update the context
       
   558                     shc.handshakeKeyExchange = ke;
       
   559                     shc.handshakePossessions.add(pos);
       
   560                     keyShare = new KeyShareEntry(ng.id, pos.encode());
       
   561                     break;
       
   562                 }
       
   563 
       
   564                 if (keyShare != null) {
       
   565                     for (Map.Entry<Byte, HandshakeProducer> me :
       
   566                             ke.getHandshakeProducers(shc)) {
       
   567                         shc.handshakeProducers.put(
       
   568                                 me.getKey(), me.getValue());
       
   569                     }
       
   570 
       
   571                     // We have got one! Don't forgor to break.
       
   572                     break;
       
   573                 }
       
   574             }
       
   575 
       
   576             if (keyShare == null) {
       
   577                 // Unlikely, HelloRetryRequest should be used instead ealier.
       
   578                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   579                     SSLLogger.warning(
       
   580                             "No available server key_share extension");
       
   581                 }
       
   582                 return null;
       
   583             }
       
   584 
       
   585             byte[] extData = keyShare.getEncoded();
       
   586 
       
   587             // update the context
       
   588             SHKeyShareSpec spec = new SHKeyShareSpec(keyShare);
       
   589             shc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
       
   590 
       
   591             return extData;
       
   592         }
       
   593     }
       
   594 
       
   595     /**
       
   596      * Network data consumer of the extension in a ServerHello
       
   597      * handshake message.
       
   598      */
       
   599     private static final class SHKeyShareConsumer implements ExtensionConsumer {
       
   600         // Prevent instantiation of this class.
       
   601         private SHKeyShareConsumer() {
       
   602             // blank
       
   603         }
       
   604 
       
   605         @Override
       
   606         public void consume(ConnectionContext context,
       
   607             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   608             // Happens in client side only.
       
   609             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   610             if (chc.clientRequestedNamedGroups == null ||
       
   611                     chc.clientRequestedNamedGroups.isEmpty()) {
       
   612                 // No supported groups.
       
   613                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   614                         "Unexpected key_share extension in ServerHello");
       
   615                 return;     // fatal() always throws, make the compiler happy.
       
   616             }
       
   617 
       
   618             // Is it a supported and enabled extension?
       
   619             if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
       
   620                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   621                         "Unsupported key_share extension in ServerHello");
       
   622                 return;     // fatal() always throws, make the compiler happy.
       
   623             }
       
   624 
       
   625             // Parse the extension
       
   626             SHKeyShareSpec spec;
       
   627             try {
       
   628                 spec = new SHKeyShareSpec(buffer);
       
   629             } catch (IOException ioe) {
       
   630                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   631                 return;     // fatal() always throws, make the compiler happy.
       
   632             }
       
   633 
       
   634             KeyShareEntry keyShare = spec.serverShare;
       
   635             NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
       
   636             if (ng == null || !SupportedGroups.isActivatable(
       
   637                     chc.sslConfig.algorithmConstraints, ng)) {
       
   638                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   639                         "Unsupported named group: " +
       
   640                         NamedGroup.nameOf(keyShare.namedGroupId));
       
   641                 return;     // fatal() always throws, make the compiler happy.
       
   642             }
       
   643 
       
   644             SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
       
   645             if (ke == null) {
       
   646                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   647                         "No key exchange for named group " + ng.name);
       
   648                 return;     // fatal() always throws, make the compiler happy.
       
   649             }
       
   650 
       
   651             SSLCredentials credentials = null;
       
   652             if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
       
   653                 try {
       
   654                     ECDHECredentials ecdhec =
       
   655                             ECDHECredentials.valueOf(ng, keyShare.keyExchange);
       
   656                     if (ecdhec != null) {
       
   657                         if (!chc.algorithmConstraints.permits(
       
   658                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   659                                 ecdhec.popPublicKey)) {
       
   660                             chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   661                                     "ECDHE key share entry does not " +
       
   662                                     "comply to algorithm constraints");
       
   663                         } else {
       
   664                             credentials = ecdhec;
       
   665                         }
       
   666                     }
       
   667                 } catch (IOException | GeneralSecurityException ex) {
       
   668                     chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   669                             "Cannot decode named group: " +
       
   670                             NamedGroup.nameOf(keyShare.namedGroupId));
       
   671                 }
       
   672             } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
       
   673                 try {
       
   674                     DHECredentials dhec =
       
   675                             DHECredentials.valueOf(ng, keyShare.keyExchange);
       
   676                     if (dhec != null) {
       
   677                         if (!chc.algorithmConstraints.permits(
       
   678                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   679                                 dhec.popPublicKey)) {
       
   680                             chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   681                                     "DHE key share entry does not " +
       
   682                                     "comply to algorithm constraints");
       
   683                         } else {
       
   684                             credentials = dhec;
       
   685                         }
       
   686                     }
       
   687                 } catch (IOException | GeneralSecurityException ex) {
       
   688                     chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   689                             "Cannot decode named group: " +
       
   690                             NamedGroup.nameOf(keyShare.namedGroupId));
       
   691                 }
       
   692             } else {
       
   693                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   694                         "Unsupported named group: " +
       
   695                         NamedGroup.nameOf(keyShare.namedGroupId));
       
   696             }
       
   697 
       
   698             if (credentials == null) {
       
   699                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   700                         "Unsupported named group: " + ng.name);
       
   701             }
       
   702 
       
   703             // update the context
       
   704             chc.handshakeKeyExchange = ke;
       
   705             chc.handshakeCredentials.add(credentials);
       
   706             chc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
       
   707         }
       
   708     }
       
   709 
       
   710     /**
       
   711      * The absence processing if the extension is not present in
       
   712      * the ServerHello handshake message.
       
   713      */
       
   714     private static final class SHKeyShareAbsence implements HandshakeAbsence {
       
   715         @Override
       
   716         public void absent(ConnectionContext context,
       
   717                 HandshakeMessage message) throws IOException {
       
   718             // The producing happens in client side only.
       
   719             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   720 
       
   721             // Cannot use the previous requested key shares any more.
       
   722             if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
       
   723                 SSLLogger.fine(
       
   724                         "No key_share extension in ServerHello, " +
       
   725                         "cleanup the key shares if necessary");
       
   726             }
       
   727             chc.handshakePossessions.clear();
       
   728         }
       
   729     }
       
   730 
       
   731     /**
       
   732      * The key share entry used in HelloRetryRequest "key_share" extensions.
       
   733      */
       
   734     static final class HRRKeyShareSpec implements SSLExtensionSpec {
       
   735         final int selectedGroup;
       
   736 
       
   737         HRRKeyShareSpec(NamedGroup serverGroup) {
       
   738             this.selectedGroup = serverGroup.id;
       
   739         }
       
   740 
       
   741         private HRRKeyShareSpec(ByteBuffer buffer) throws IOException {
       
   742             // struct {
       
   743             //     NamedGroup selected_group;
       
   744             // } KeyShareHelloRetryRequest;
       
   745             if (buffer.remaining() != 2) {
       
   746                 throw new SSLProtocolException(
       
   747                     "Invalid key_share extension: " +
       
   748                     "improper data (length=" + buffer.remaining() + ")");
       
   749             }
       
   750 
       
   751             this.selectedGroup = Record.getInt16(buffer);
       
   752         }
       
   753 
       
   754         @Override
       
   755         public String toString() {
       
   756             MessageFormat messageFormat = new MessageFormat(
       
   757                 "\"selected group\": '['{0}']'", Locale.ENGLISH);
       
   758 
       
   759             Object[] messageFields = {
       
   760                     NamedGroup.nameOf(selectedGroup)
       
   761                 };
       
   762             return messageFormat.format(messageFields);
       
   763         }
       
   764     }
       
   765 
       
   766     private static final class HRRKeyShareStringizer implements SSLStringizer {
       
   767         @Override
       
   768         public String toString(ByteBuffer buffer) {
       
   769             try {
       
   770                 return (new HRRKeyShareSpec(buffer)).toString();
       
   771             } catch (IOException ioe) {
       
   772                 // For debug logging only, so please swallow exceptions.
       
   773                 return ioe.getMessage();
       
   774             }
       
   775         }
       
   776     }
       
   777 
       
   778     /**
       
   779      * Network data producer of the extension in a HelloRetryRequest
       
   780      * handshake message.
       
   781      */
       
   782     private static final
       
   783             class HRRKeyShareProducer implements HandshakeProducer {
       
   784         // Prevent instantiation of this class.
       
   785         private HRRKeyShareProducer() {
       
   786             // blank
       
   787         }
       
   788 
       
   789         @Override
       
   790         public byte[] produce(ConnectionContext context,
       
   791                 HandshakeMessage message) throws IOException {
       
   792             // The producing happens in server side only.
       
   793             ServerHandshakeContext shc = (ServerHandshakeContext) context;
       
   794 
       
   795             // Is it a supported and enabled extension?
       
   796             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
       
   797                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   798                         "Unsupported key_share extension in HelloRetryRequest");
       
   799                 return null;    // make the compiler happy.
       
   800             }
       
   801 
       
   802             if (shc.clientRequestedNamedGroups == null ||
       
   803                     shc.clientRequestedNamedGroups.isEmpty()) {
       
   804                 // No supported groups.
       
   805                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   806                         "Unexpected key_share extension in HelloRetryRequest");
       
   807                 return null;    // make the compiler happy.
       
   808             }
       
   809 
       
   810             NamedGroup selectedGroup = null;
       
   811             for (NamedGroup ng : shc.clientRequestedNamedGroups) {
       
   812                 if (SupportedGroups.isActivatable(
       
   813                         shc.sslConfig.algorithmConstraints, ng)) {
       
   814                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   815                         SSLLogger.fine(
       
   816                                 "HelloRetryRequest selected named group: " +
       
   817                                 ng.name);
       
   818                     }
       
   819 
       
   820                     selectedGroup = ng;
       
   821                     break;
       
   822                 }
       
   823             }
       
   824 
       
   825             if (selectedGroup == null) {
       
   826                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   827                         new IOException("No common named group"));
       
   828                 return null;    // make the complier happy
       
   829             }
       
   830 
       
   831             byte[] extdata = new byte[] {
       
   832                     (byte)((selectedGroup.id >> 8) & 0xFF),
       
   833                     (byte)(selectedGroup.id & 0xFF)
       
   834                 };
       
   835 
       
   836             // update the context
       
   837             shc.serverSelectedNamedGroup = selectedGroup;
       
   838             shc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE,
       
   839                     new HRRKeyShareSpec(selectedGroup));
       
   840 
       
   841             return extdata;
       
   842         }
       
   843     }
       
   844 
       
   845     /**
       
   846      * Network data producer of the extension for stateless
       
   847      * HelloRetryRequest reconstruction.
       
   848      */
       
   849     private static final
       
   850             class HRRKeyShareReproducer implements HandshakeProducer {
       
   851         // Prevent instantiation of this class.
       
   852         private HRRKeyShareReproducer() {
       
   853             // blank
       
   854         }
       
   855 
       
   856         @Override
       
   857         public byte[] produce(ConnectionContext context,
       
   858                 HandshakeMessage message) throws IOException {
       
   859             // The producing happens in server side only.
       
   860             ServerHandshakeContext shc = (ServerHandshakeContext) context;
       
   861 
       
   862             // Is it a supported and enabled extension?
       
   863             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
       
   864                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   865                         "Unsupported key_share extension in HelloRetryRequest");
       
   866                 return null;    // make the compiler happy.
       
   867             }
       
   868 
       
   869             CHKeyShareSpec spec = (CHKeyShareSpec)shc.handshakeExtensions.get(
       
   870                     SSLExtension.CH_KEY_SHARE);
       
   871             if (spec != null && spec.clientShares != null &&
       
   872                     spec.clientShares.size() == 1) {
       
   873                 int namedGroupId = spec.clientShares.get(0).namedGroupId;
       
   874 
       
   875                 byte[] extdata = new byte[] {
       
   876                         (byte)((namedGroupId >> 8) & 0xFF),
       
   877                         (byte)(namedGroupId & 0xFF)
       
   878                     };
       
   879 
       
   880                 return extdata;
       
   881             }
       
   882 
       
   883             return null;
       
   884         }
       
   885     }
       
   886 
       
   887     /**
       
   888      * Network data consumer of the extension in a HelloRetryRequest
       
   889      * handshake message.
       
   890      */
       
   891     private static final
       
   892             class HRRKeyShareConsumer implements ExtensionConsumer {
       
   893         // Prevent instantiation of this class.
       
   894         private HRRKeyShareConsumer() {
       
   895             // blank
       
   896         }
       
   897 
       
   898         @Override
       
   899         public void consume(ConnectionContext context,
       
   900             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   901             // The producing happens in client side only.
       
   902             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   903 
       
   904             // Is it a supported and enabled extension?
       
   905             if (!chc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
       
   906                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   907                         "Unsupported key_share extension in HelloRetryRequest");
       
   908                 return;     // make the compiler happy.
       
   909             }
       
   910 
       
   911             if (chc.clientRequestedNamedGroups == null ||
       
   912                     chc.clientRequestedNamedGroups.isEmpty()) {
       
   913                 // No supported groups.
       
   914                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   915                         "Unexpected key_share extension in HelloRetryRequest");
       
   916                 return;     // make the compiler happy.
       
   917             }
       
   918 
       
   919             // Parse the extension
       
   920             HRRKeyShareSpec spec;
       
   921             try {
       
   922                 spec = new HRRKeyShareSpec(buffer);
       
   923             } catch (IOException ioe) {
       
   924                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   925                 return;     // fatal() always throws, make the compiler happy.
       
   926             }
       
   927 
       
   928             NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup);
       
   929             if (serverGroup == null) {
       
   930                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   931                         "Unsupported HelloRetryRequest selected group: " +
       
   932                                 NamedGroup.nameOf(spec.selectedGroup));
       
   933                 return;     // fatal() always throws, make the compiler happy.
       
   934             }
       
   935 
       
   936             if (!chc.clientRequestedNamedGroups.contains(serverGroup)) {
       
   937                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   938                         "Unexpected HelloRetryRequest selected group: " +
       
   939                                 serverGroup.name);
       
   940                 return;     // fatal() always throws, make the compiler happy.
       
   941             }
       
   942 
       
   943             // update the context
       
   944 
       
   945             // When sending the new ClientHello, the client MUST replace the
       
   946             // original "key_share" extension with one containing only a new
       
   947             // KeyShareEntry for the group indicated in the selected_group
       
   948             // field of the triggering HelloRetryRequest.
       
   949             //
       
   950             chc.serverSelectedNamedGroup = serverGroup;
       
   951             chc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, spec);
       
   952         }
       
   953     }
       
   954 }