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                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 341             }
 342 
 343             List<SSLCredentials> credentials = new LinkedList<>();
 344             for (KeyShareEntry entry : spec.clientShares) {
 345                 NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
 346                 if (ng == null || !SupportedGroups.isActivatable(
 347                         shc.sslConfig.algorithmConstraints, ng)) {
 348                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 349                         SSLLogger.fine(
 350                                 "Ignore unsupported named group: " +
 351                                 NamedGroup.nameOf(entry.namedGroupId));
 352                     }
 353                     continue;
 354                 }
 355 
 356                 if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
 357                     try {
 358                         ECDHECredentials ecdhec =
 359                             ECDHECredentials.valueOf(ng, entry.keyExchange);
 360                         if (ecdhec != null) {
 361                             if (!shc.algorithmConstraints.permits(
 362                                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 363                                     ecdhec.popPublicKey)) {
 364                                 SSLLogger.warning(
 365                                         "ECDHE key share entry does not " +
 366                                         "comply to algorithm constraints");
 367                             } else {
 368                                 credentials.add(ecdhec);
 369                             }
 370                         }
 371                     } catch (IOException | GeneralSecurityException ex) {
 372                         SSLLogger.warning(
 373                                 "Cannot decode named group: " +
 374                                 NamedGroup.nameOf(entry.namedGroupId));
 375                     }
 376                 } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
 377                     try {
 378                         DHECredentials dhec =
 379                                 DHECredentials.valueOf(ng, entry.keyExchange);
 380                         if (dhec != null) {
 381                             if (!shc.algorithmConstraints.permits(
 382                                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 383                                     dhec.popPublicKey)) {
 384                                 SSLLogger.warning(
 385                                         "DHE key share entry does not " +
 386                                         "comply to algorithm constraints");
 387                             } else {
 388                                 credentials.add(dhec);
 389                             }
 390                         }
 391                     } catch (IOException | GeneralSecurityException ex) {
 392                         SSLLogger.warning(
 393                                 "Cannot decode named group: " +
 394                                 NamedGroup.nameOf(entry.namedGroupId));
 395                     }
 396                 }
 397             }
 398 
 399             if (!credentials.isEmpty()) {
 400                 shc.handshakeCredentials.addAll(credentials);
 401             } else {
 402                 // New handshake credentials are required from the client side.
 403                 shc.handshakeProducers.put(
 404                         SSLHandshake.HELLO_RETRY_REQUEST.id,
 405                         SSLHandshake.HELLO_RETRY_REQUEST);
 406             }
 407 
 408             // update the context
 409             shc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, spec);
 410         }
 411     }
 412 
 413     /**
 414      * The key share entry used in ServerHello "key_share" extensions.
 415      */
 416     static final class SHKeyShareSpec implements SSLExtensionSpec {
 417         final KeyShareEntry serverShare;
 418 
 419         SHKeyShareSpec(KeyShareEntry serverShare) {
 420             this.serverShare = serverShare;
 421         }
 422 
 423         private SHKeyShareSpec(ByteBuffer buffer) throws IOException {
 424             // struct {
 425             //      KeyShareEntry server_share;
 426             // } KeyShareServerHello;
 427             if (buffer.remaining() < 5) {       // 5: minimal server_share
 428                 throw new SSLProtocolException(
 429                     "Invalid key_share extension: " +
 430                     "insufficient data (length=" + buffer.remaining() + ")");
 431             }
 432 
 433             int namedGroupId = Record.getInt16(buffer);
 434             byte[] keyExchange = Record.getBytes16(buffer);
 435 
 436             if (buffer.hasRemaining()) {
 437                 throw new SSLProtocolException(
 438                     "Invalid key_share extension: unknown extra data");
 439             }
 440 
 441             this.serverShare = new KeyShareEntry(namedGroupId, keyExchange);
 442         }
 443 
 444         @Override
 445         public String toString() {
 446             MessageFormat messageFormat = new MessageFormat(
 447                 "\"server_share\": '{'\n" +
 448                 "  \"named group\": {0}\n" +
 449                 "  \"key_exchange\": '{'\n" +
 450                 "{1}\n" +
 451                 "  '}'\n" +
 452                 "'}',", Locale.ENGLISH);
 453 
 454             HexDumpEncoder hexEncoder = new HexDumpEncoder();
 455             Object[] messageFields = {
 456                 NamedGroup.nameOf(serverShare.namedGroupId),
 457                 Utilities.indent(
 458                         hexEncoder.encode(serverShare.keyExchange), "    ")
 459             };
 460 
 461             return messageFormat.format(messageFields);
 462         }
 463     }
 464 
 465     private static final class SHKeyShareStringizer implements SSLStringizer {
 466         @Override
 467         public String toString(ByteBuffer buffer) {
 468             try {
 469                 return (new SHKeyShareSpec(buffer)).toString();
 470             } catch (IOException ioe) {
 471                 // For debug logging only, so please swallow exceptions.
 472                 return ioe.getMessage();
 473             }
 474         }
 475     }
 476 
 477     /**
 478      * Network data producer of the extension in a ServerHello
 479      * handshake message.
 480      */
 481     private static final class SHKeyShareProducer implements HandshakeProducer {
 482         // Prevent instantiation of this class.
 483         private SHKeyShareProducer() {
 484             // blank
 485         }
 486 
 487         @Override
 488         public byte[] produce(ConnectionContext context,
 489                 HandshakeMessage message) throws IOException {
 490             // The producing happens in client side only.
 491             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 492 
 493             // In response to key_share request only
 494             CHKeyShareSpec kss =
 495                     (CHKeyShareSpec)shc.handshakeExtensions.get(
 496                             SSLExtension.CH_KEY_SHARE);
 497             if (kss == null) {
 498                 // Unlikely, no key_share extension requested.
 499                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 500                     SSLLogger.warning(
 501                             "Ignore, no client key_share extension");
 502                 }
 503                 return null;
 504             }
 505 
 506             // Is it a supported and enabled extension?
 507             if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
 508                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 509                     SSLLogger.warning(
 510                             "Ignore, no available server key_share extension");
 511                 }
 512                 return null;
 513             }
 514 
 515             // use requested key share entries
 516             if ((shc.handshakeCredentials == null) ||
 517                     shc.handshakeCredentials.isEmpty()) {
 518                 // Unlikely, HelloRetryRequest should be used ealier.
 519                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 520                     SSLLogger.warning(
 521                             "No available client key share entries");
 522                 }
 523                 return null;
 524             }
 525 
 526             KeyShareEntry keyShare = null;
 527             for (SSLCredentials cd : shc.handshakeCredentials) {
 528                 NamedGroup ng = null;
 529                 if (cd instanceof ECDHECredentials) {
 530                     ng = ((ECDHECredentials)cd).namedGroup;
 531                 } else if (cd instanceof DHECredentials) {
 532                     ng = ((DHECredentials)cd).namedGroup;
 533                 }
 534 
 535                 if (ng == null) {
 536                     continue;
 537                 }
 538 
 539                 SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
 540                 if (ke == null) {
 541                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 542                         SSLLogger.warning(
 543                             "No key exchange for named group " + ng.name);
 544                     }
 545                     continue;
 546                 }
 547 
 548                 SSLPossession[] poses = ke.createPossessions(shc);
 549                 for (SSLPossession pos : poses) {
 550                     if (!(pos instanceof ECDHEPossession) &&
 551                             !(pos instanceof DHEPossession)) {
 552                         // May need more possesion types in the future.
 553                         continue;
 554                     }
 555 
 556                     // update the context
 557                     shc.handshakeKeyExchange = ke;
 558                     shc.handshakePossessions.add(pos);
 559                     keyShare = new KeyShareEntry(ng.id, pos.encode());
 560                     break;
 561                 }
 562 
 563                 if (keyShare != null) {
 564                     for (Map.Entry<Byte, HandshakeProducer> me :
 565                             ke.getHandshakeProducers(shc)) {
 566                         shc.handshakeProducers.put(
 567                                 me.getKey(), me.getValue());
 568                     }
 569 
 570                     // We have got one! Don't forgor to break.
 571                     break;
 572                 }
 573             }
 574 
 575             if (keyShare == null) {
 576                 // Unlikely, HelloRetryRequest should be used instead ealier.
 577                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 578                     SSLLogger.warning(
 579                             "No available server key_share extension");
 580                 }
 581                 return null;
 582             }
 583 
 584             byte[] extData = keyShare.getEncoded();
 585 
 586             // update the context
 587             SHKeyShareSpec spec = new SHKeyShareSpec(keyShare);
 588             shc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
 589 
 590             return extData;
 591         }
 592     }
 593 
 594     /**
 595      * Network data consumer of the extension in a ServerHello
 596      * handshake message.
 597      */
 598     private static final class SHKeyShareConsumer implements ExtensionConsumer {
 599         // Prevent instantiation of this class.
 600         private SHKeyShareConsumer() {
 601             // blank
 602         }
 603 
 604         @Override
 605         public void consume(ConnectionContext context,
 606             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 607             // Happens in client side only.
 608             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 609             if (chc.clientRequestedNamedGroups == null ||
 610                     chc.clientRequestedNamedGroups.isEmpty()) {
 611                 // No supported groups.
 612                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 613                         "Unexpected key_share extension in ServerHello");
 614             }
 615 
 616             // Is it a supported and enabled extension?
 617             if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
 618                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 619                         "Unsupported key_share extension in ServerHello");
 620             }
 621 
 622             // Parse the extension
 623             SHKeyShareSpec spec;
 624             try {
 625                 spec = new SHKeyShareSpec(buffer);
 626             } catch (IOException ioe) {
 627                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 628             }
 629 
 630             KeyShareEntry keyShare = spec.serverShare;
 631             NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
 632             if (ng == null || !SupportedGroups.isActivatable(
 633                     chc.sslConfig.algorithmConstraints, ng)) {
 634                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 635                         "Unsupported named group: " +
 636                         NamedGroup.nameOf(keyShare.namedGroupId));
 637             }
 638 
 639             SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
 640             if (ke == null) {
 641                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 642                         "No key exchange for named group " + ng.name);
 643             }
 644 
 645             SSLCredentials credentials = null;
 646             if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
 647                 try {
 648                     ECDHECredentials ecdhec =
 649                             ECDHECredentials.valueOf(ng, keyShare.keyExchange);
 650                     if (ecdhec != null) {
 651                         if (!chc.algorithmConstraints.permits(
 652                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 653                                 ecdhec.popPublicKey)) {
 654                             throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 655                                     "ECDHE key share entry does not " +
 656                                     "comply to algorithm constraints");
 657                         } else {
 658                             credentials = ecdhec;
 659                         }
 660                     }
 661                 } catch (IOException | GeneralSecurityException ex) {
 662                     throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 663                             "Cannot decode named group: " +
 664                             NamedGroup.nameOf(keyShare.namedGroupId));
 665                 }
 666             } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
 667                 try {
 668                     DHECredentials dhec =
 669                             DHECredentials.valueOf(ng, keyShare.keyExchange);
 670                     if (dhec != null) {
 671                         if (!chc.algorithmConstraints.permits(
 672                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 673                                 dhec.popPublicKey)) {
 674                             throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 675                                     "DHE key share entry does not " +
 676                                     "comply to algorithm constraints");
 677                         } else {
 678                             credentials = dhec;
 679                         }
 680                     }
 681                 } catch (IOException | GeneralSecurityException ex) {
 682                     throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 683                             "Cannot decode named group: " +
 684                             NamedGroup.nameOf(keyShare.namedGroupId));
 685                 }
 686             } else {
 687                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 688                         "Unsupported named group: " +
 689                         NamedGroup.nameOf(keyShare.namedGroupId));
 690             }
 691 
 692             if (credentials == null) {
 693                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 694                         "Unsupported named group: " + ng.name);
 695             }
 696 
 697             // update the context
 698             chc.handshakeKeyExchange = ke;
 699             chc.handshakeCredentials.add(credentials);
 700             chc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
 701         }
 702     }
 703 
 704     /**
 705      * The absence processing if the extension is not present in
 706      * the ServerHello handshake message.
 707      */
 708     private static final class SHKeyShareAbsence implements HandshakeAbsence {
 709         @Override
 710         public void absent(ConnectionContext context,
 711                 HandshakeMessage message) throws IOException {
 712             // The producing happens in client side only.
 713             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 714 
 715             // Cannot use the previous requested key shares any more.
 716             if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
 717                 SSLLogger.fine(
 718                         "No key_share extension in ServerHello, " +
 719                         "cleanup the key shares if necessary");
 720             }
 721             chc.handshakePossessions.clear();
 722         }
 723     }
 724 
 725     /**
 726      * The key share entry used in HelloRetryRequest "key_share" extensions.
 727      */
 728     static final class HRRKeyShareSpec implements SSLExtensionSpec {
 729         final int selectedGroup;
 730 
 731         HRRKeyShareSpec(NamedGroup serverGroup) {
 732             this.selectedGroup = serverGroup.id;
 733         }
 734 
 735         private HRRKeyShareSpec(ByteBuffer buffer) throws IOException {
 736             // struct {
 737             //     NamedGroup selected_group;
 738             // } KeyShareHelloRetryRequest;
 739             if (buffer.remaining() != 2) {
 740                 throw new SSLProtocolException(
 741                     "Invalid key_share extension: " +
 742                     "improper data (length=" + buffer.remaining() + ")");
 743             }
 744 
 745             this.selectedGroup = Record.getInt16(buffer);
 746         }
 747 
 748         @Override
 749         public String toString() {
 750             MessageFormat messageFormat = new MessageFormat(
 751                 "\"selected group\": '['{0}']'", Locale.ENGLISH);
 752 
 753             Object[] messageFields = {
 754                     NamedGroup.nameOf(selectedGroup)
 755                 };
 756             return messageFormat.format(messageFields);
 757         }
 758     }
 759 
 760     private static final class HRRKeyShareStringizer implements SSLStringizer {
 761         @Override
 762         public String toString(ByteBuffer buffer) {
 763             try {
 764                 return (new HRRKeyShareSpec(buffer)).toString();
 765             } catch (IOException ioe) {
 766                 // For debug logging only, so please swallow exceptions.
 767                 return ioe.getMessage();
 768             }
 769         }
 770     }
 771 
 772     /**
 773      * Network data producer of the extension in a HelloRetryRequest
 774      * handshake message.
 775      */
 776     private static final
 777             class HRRKeyShareProducer implements HandshakeProducer {
 778         // Prevent instantiation of this class.
 779         private HRRKeyShareProducer() {
 780             // blank
 781         }
 782 
 783         @Override
 784         public byte[] produce(ConnectionContext context,
 785                 HandshakeMessage message) throws IOException {
 786             // The producing happens in server side only.
 787             ServerHandshakeContext shc = (ServerHandshakeContext) context;
 788 
 789             // Is it a supported and enabled extension?
 790             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
 791                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 792                         "Unsupported key_share extension in HelloRetryRequest");
 793             }
 794 
 795             if (shc.clientRequestedNamedGroups == null ||
 796                     shc.clientRequestedNamedGroups.isEmpty()) {
 797                 // No supported groups.
 798                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 799                         "Unexpected key_share extension in HelloRetryRequest");
 800             }
 801 
 802             NamedGroup selectedGroup = null;
 803             for (NamedGroup ng : shc.clientRequestedNamedGroups) {
 804                 if (SupportedGroups.isActivatable(
 805                         shc.sslConfig.algorithmConstraints, ng)) {
 806                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 807                         SSLLogger.fine(
 808                                 "HelloRetryRequest selected named group: " +
 809                                 ng.name);
 810                     }
 811 
 812                     selectedGroup = ng;
 813                     break;
 814                 }
 815             }
 816 
 817             if (selectedGroup == null) {
 818                 throw shc.conContext.fatal(
 819                         Alert.UNEXPECTED_MESSAGE, "No common named group");
 820             }
 821 
 822             byte[] extdata = new byte[] {
 823                     (byte)((selectedGroup.id >> 8) & 0xFF),
 824                     (byte)(selectedGroup.id & 0xFF)
 825                 };
 826 
 827             // update the context
 828             shc.serverSelectedNamedGroup = selectedGroup;
 829             shc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE,
 830                     new HRRKeyShareSpec(selectedGroup));
 831 
 832             return extdata;
 833         }
 834     }
 835 
 836     /**
 837      * Network data producer of the extension for stateless
 838      * HelloRetryRequest reconstruction.
 839      */
 840     private static final
 841             class HRRKeyShareReproducer implements HandshakeProducer {
 842         // Prevent instantiation of this class.
 843         private HRRKeyShareReproducer() {
 844             // blank
 845         }
 846 
 847         @Override
 848         public byte[] produce(ConnectionContext context,
 849                 HandshakeMessage message) throws IOException {
 850             // The producing happens in server side only.
 851             ServerHandshakeContext shc = (ServerHandshakeContext) context;
 852 
 853             // Is it a supported and enabled extension?
 854             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
 855                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 856                         "Unsupported key_share extension in HelloRetryRequest");
 857             }
 858 
 859             CHKeyShareSpec spec = (CHKeyShareSpec)shc.handshakeExtensions.get(
 860                     SSLExtension.CH_KEY_SHARE);
 861             if (spec != null && spec.clientShares != null &&
 862                     spec.clientShares.size() == 1) {
 863                 int namedGroupId = spec.clientShares.get(0).namedGroupId;
 864 
 865                 byte[] extdata = new byte[] {
 866                         (byte)((namedGroupId >> 8) & 0xFF),
 867                         (byte)(namedGroupId & 0xFF)
 868                     };
 869 
 870                 return extdata;
 871             }
 872 
 873             return null;
 874         }
 875     }
 876 
 877     /**
 878      * Network data consumer of the extension in a HelloRetryRequest
 879      * handshake message.
 880      */
 881     private static final
 882             class HRRKeyShareConsumer implements ExtensionConsumer {
 883         // Prevent instantiation of this class.
 884         private HRRKeyShareConsumer() {
 885             // blank
 886         }
 887 
 888         @Override
 889         public void consume(ConnectionContext context,
 890             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 891             // The producing happens in client side only.
 892             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 893 
 894             // Is it a supported and enabled extension?
 895             if (!chc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
 896                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 897                         "Unsupported key_share extension in HelloRetryRequest");
 898             }
 899 
 900             if (chc.clientRequestedNamedGroups == null ||
 901                     chc.clientRequestedNamedGroups.isEmpty()) {
 902                 // No supported groups.
 903                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 904                         "Unexpected key_share extension in HelloRetryRequest");
 905             }
 906 
 907             // Parse the extension
 908             HRRKeyShareSpec spec;
 909             try {
 910                 spec = new HRRKeyShareSpec(buffer);
 911             } catch (IOException ioe) {
 912                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 913             }
 914 
 915             NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup);
 916             if (serverGroup == null) {
 917                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 918                         "Unsupported HelloRetryRequest selected group: " +
 919                                 NamedGroup.nameOf(spec.selectedGroup));
 920             }
 921 
 922             if (!chc.clientRequestedNamedGroups.contains(serverGroup)) {
 923                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 924                         "Unexpected HelloRetryRequest selected group: " +
 925                                 serverGroup.name);
 926             }
 927 
 928             // update the context
 929 
 930             // When sending the new ClientHello, the client MUST replace the
 931             // original "key_share" extension with one containing only a new
 932             // KeyShareEntry for the group indicated in the selected_group
 933             // field of the triggering HelloRetryRequest.
 934             //
 935             chc.serverSelectedNamedGroup = serverGroup;
 936             chc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, spec);
 937         }
 938     }
 939 }