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.AlgorithmConstraints;
  31 import java.security.AlgorithmParameters;
  32 import java.security.CryptoPrimitive;
  33 import java.security.spec.ECGenParameterSpec;
  34 import java.security.spec.InvalidParameterSpecException;
  35 import java.text.MessageFormat;
  36 import java.util.ArrayList;
  37 import java.util.Collections;
  38 import java.util.EnumSet;
  39 import java.util.LinkedList;
  40 import java.util.List;
  41 import java.util.Locale;
  42 import javax.net.ssl.SSLProtocolException;
  43 import sun.security.action.GetPropertyAction;
  44 import sun.security.ssl.NamedGroup.NamedGroupType;
  45 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
  46 import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
  47 import sun.security.ssl.SSLExtension.ExtensionConsumer;
  48 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
  49 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  50 
  51 /**
  52  * Pack of the "supported_groups" extensions [RFC 4492/7919].
  53  */
  54 final class SupportedGroupsExtension {
  55     static final HandshakeProducer chNetworkProducer =
  56             new CHSupportedGroupsProducer();
  57     static final ExtensionConsumer chOnLoadConsumer =
  58             new CHSupportedGroupsConsumer();
  59     static final HandshakeAbsence chOnTradAbsence =
  60             new CHSupportedGroupsOnTradeAbsence();
  61     static final SSLStringizer sgsStringizer =
  62             new SupportedGroupsStringizer();
  63 
  64     static final HandshakeProducer eeNetworkProducer =
  65             new EESupportedGroupsProducer();
  66     static final ExtensionConsumer eeOnLoadConsumer =
  67             new EESupportedGroupsConsumer();
  68 
  69     /**
  70      * The "supported_groups" extension.
  71      */
  72     static final class SupportedGroupsSpec implements SSLExtensionSpec {
  73         final int[] namedGroupsIds;
  74 
  75         private SupportedGroupsSpec(int[] namedGroupsIds) {
  76             this.namedGroupsIds = namedGroupsIds;
  77         }
  78 
  79         private SupportedGroupsSpec(List<NamedGroup> namedGroups) {
  80             this.namedGroupsIds = new int[namedGroups.size()];
  81             int i = 0;
  82             for (NamedGroup ng : namedGroups) {
  83                 namedGroupsIds[i++] = ng.id;
  84             }
  85         }
  86 
  87         private SupportedGroupsSpec(ByteBuffer m) throws IOException  {
  88             if (m.remaining() < 2) {      // 2: the length of the list
  89                 throw new SSLProtocolException(
  90                     "Invalid supported_groups extension: insufficient data");
  91             }
  92 
  93             byte[] ngs = Record.getBytes16(m);
  94             if (m.hasRemaining()) {
  95                 throw new SSLProtocolException(
  96                     "Invalid supported_groups extension: unknown extra data");
  97             }
  98 
  99             if ((ngs == null) || (ngs.length == 0) || (ngs.length % 2 != 0)) {
 100                 throw new SSLProtocolException(
 101                     "Invalid supported_groups extension: incomplete data");
 102             }
 103 
 104             int[] ids = new int[ngs.length / 2];
 105             for (int i = 0, j = 0; i < ngs.length;) {
 106                 ids[j++] = ((ngs[i++] & 0xFF) << 8) | (ngs[i++] & 0xFF);
 107             }
 108 
 109             this.namedGroupsIds = ids;
 110         }
 111 
 112         @Override
 113         public String toString() {
 114             MessageFormat messageFormat = new MessageFormat(
 115                 "\"versions\": '['{0}']'", Locale.ENGLISH);
 116 
 117             if (namedGroupsIds == null || namedGroupsIds.length == 0) {
 118                 Object[] messageFields = {
 119                         "<no supported named group specified>"
 120                     };
 121                 return messageFormat.format(messageFields);
 122             } else {
 123                 StringBuilder builder = new StringBuilder(512);
 124                 boolean isFirst = true;
 125                 for (int ngid : namedGroupsIds) {
 126                     if (isFirst) {
 127                         isFirst = false;
 128                     } else {
 129                         builder.append(", ");
 130                     }
 131 
 132                     builder.append(NamedGroup.nameOf(ngid));
 133                 }
 134 
 135                 Object[] messageFields = {
 136                         builder.toString()
 137                     };
 138 
 139                 return messageFormat.format(messageFields);
 140             }
 141         }
 142     }
 143 
 144     private static final
 145             class SupportedGroupsStringizer implements SSLStringizer {
 146         @Override
 147         public String toString(ByteBuffer buffer) {
 148             try {
 149                 return (new SupportedGroupsSpec(buffer)).toString();
 150             } catch (IOException ioe) {
 151                 // For debug logging only, so please swallow exceptions.
 152                 return ioe.getMessage();
 153             }
 154         }
 155     }
 156 
 157     static class SupportedGroups {
 158         // To switch off the supported_groups extension for DHE cipher suite.
 159         static final boolean enableFFDHE =
 160                 Utilities.getBooleanProperty("jsse.enableFFDHE", true);
 161 
 162         // the supported named groups
 163         static final NamedGroup[] supportedNamedGroups;
 164 
 165         static {
 166             boolean requireFips = SunJSSE.isFIPS();
 167 
 168             // The value of the System Property defines a list of enabled named
 169             // groups in preference order, separated with comma.  For example:
 170             //
 171             //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
 172             //
 173             // If the System Property is not defined or the value is empty, the
 174             // default groups and preferences will be used.
 175             String property = GetPropertyAction
 176                     .privilegedGetProperty("jdk.tls.namedGroups");
 177             if (property != null && !property.isEmpty()) {
 178                 // remove double quote marks from beginning/end of the property
 179                 if (property.length() > 1 && property.charAt(0) == '"' &&
 180                         property.charAt(property.length() - 1) == '"') {
 181                     property = property.substring(1, property.length() - 1);
 182                 }
 183             }
 184 
 185             ArrayList<NamedGroup> groupList;
 186             if (property != null && !property.isEmpty()) {
 187                 String[] groups = property.split(",");
 188                 groupList = new ArrayList<>(groups.length);
 189                 for (String group : groups) {
 190                     group = group.trim();
 191                     if (!group.isEmpty()) {
 192                         NamedGroup namedGroup = NamedGroup.nameOf(group);
 193                         if (namedGroup != null &&
 194                             (!requireFips || namedGroup.isFips)) {
 195                             if (isAvailableGroup(namedGroup)) {
 196                                 groupList.add(namedGroup);
 197                             }
 198                         }   // ignore unknown groups
 199                     }
 200                 }
 201 
 202                 if (groupList.isEmpty()) {
 203                     throw new IllegalArgumentException(
 204                             "System property jdk.tls.namedGroups(" +
 205                             property + ") contains no supported named groups");
 206                 }
 207             } else {        // default groups
 208                 NamedGroup[] groups;
 209                 if (requireFips) {
 210                     groups = new NamedGroup[] {
 211                         // only NIST curves in FIPS mode
 212                         NamedGroup.SECP256_R1,
 213                         NamedGroup.SECP384_R1,
 214                         NamedGroup.SECP521_R1,
 215 
 216                         // FFDHE (RFC 7919)
 217                         NamedGroup.FFDHE_2048,
 218                         NamedGroup.FFDHE_3072,
 219                         NamedGroup.FFDHE_4096,
 220                         NamedGroup.FFDHE_6144,
 221                         NamedGroup.FFDHE_8192,
 222                     };
 223                 } else {
 224                     groups = new NamedGroup[] {
 225 
 226                         // Primary XDH (RFC 7748) curves
 227                         NamedGroup.X25519,
 228 
 229                         // Primary NIST curves (e.g. used in TLSv1.3)
 230                         NamedGroup.SECP256_R1,
 231                         NamedGroup.SECP384_R1,
 232                         NamedGroup.SECP521_R1,
 233 
 234                         // Secondary XDH curves
 235                         NamedGroup.X448,
 236 
 237                         // Secondary NIST curves
 238 
 239                         // FFDHE 2048
 240                         NamedGroup.FFDHE_2048,
 241                         NamedGroup.FFDHE_3072,
 242                         NamedGroup.FFDHE_4096,
 243                         NamedGroup.FFDHE_6144,
 244                         NamedGroup.FFDHE_8192,
 245                     };
 246                 }
 247 
 248                 groupList = new ArrayList<>(groups.length);
 249                 for (NamedGroup group : groups) {
 250                     if (isAvailableGroup(group)) {
 251                         groupList.add(group);
 252                     }
 253                 }
 254 
 255                 if (groupList.isEmpty() &&
 256                         SSLLogger.isOn && SSLLogger.isOn("ssl")) {
 257                     SSLLogger.warning("No default named groups");
 258                 }
 259             }
 260 
 261             supportedNamedGroups = new NamedGroup[groupList.size()];
 262             int i = 0;
 263             for (NamedGroup namedGroup : groupList) {
 264                 supportedNamedGroups[i++] = namedGroup;
 265             }
 266         }
 267 
 268         // check whether the group is supported by the underlying providers
 269         private static boolean isAvailableGroup(NamedGroup namedGroup) {
 270             return namedGroup.isAvailableGroup();
 271         }
 272 
 273         static ECGenParameterSpec getECGenParamSpec(NamedGroup ng) {
 274             if (ng.type != NamedGroupType.NAMED_GROUP_ECDHE) {
 275                  throw new RuntimeException(
 276                          "Not a named EC group: " + ng);
 277             }
 278 
 279             // parameters are non-null
 280             AlgorithmParameters params = ng.getParameters();
 281             try {
 282                 return params.getParameterSpec(ECGenParameterSpec.class);
 283             } catch (InvalidParameterSpecException ipse) {
 284                 // should be unlikely
 285                 return new ECGenParameterSpec(ng.oid);
 286             }
 287         }
 288 
 289         static AlgorithmParameters getParameters(NamedGroup ng) {
 290             return ng.getParameters();
 291         }
 292 
 293         // Is there any supported group permitted by the constraints?
 294         static boolean isActivatable(
 295                 AlgorithmConstraints constraints, NamedGroupType type) {
 296 
 297             boolean hasFFDHEGroups = false;
 298             for (NamedGroup namedGroup : supportedNamedGroups) {
 299                 if (namedGroup.type == type) {
 300                     if (constraints.permits(
 301                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 302                             namedGroup.algorithm,
 303                             getParameters(namedGroup))) {
 304 
 305                         return true;
 306                     }
 307 
 308                     if (!hasFFDHEGroups &&
 309                             (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
 310                         hasFFDHEGroups = true;
 311                     }
 312                 }
 313             }
 314 
 315             // For compatibility, if no FFDHE groups are defined, the non-FFDHE
 316             // compatible mode (using DHE cipher suite without FFDHE extension)
 317             // is allowed.
 318             //
 319             // Note that the constraints checking on DHE parameters will be
 320             // performed during key exchanging in a handshake.
 321             return !hasFFDHEGroups && type == NamedGroupType.NAMED_GROUP_FFDHE;
 322         }
 323 
 324         // Is the named group permitted by the constraints?
 325         static boolean isActivatable(
 326                 AlgorithmConstraints constraints, NamedGroup namedGroup) {
 327             if (!isSupported(namedGroup)) {
 328                 return false;
 329             }
 330 
 331             return constraints.permits(
 332                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 333                             namedGroup.algorithm,
 334                             getParameters(namedGroup));
 335         }
 336 
 337         // Is the named group supported?
 338         static boolean isSupported(NamedGroup namedGroup) {
 339             for (NamedGroup group : supportedNamedGroups) {
 340                 if (namedGroup.id == group.id) {
 341                     return true;
 342                 }
 343             }
 344 
 345             return false;
 346         }
 347 
 348         static NamedGroup getPreferredGroup(
 349                 ProtocolVersion negotiatedProtocol,
 350                 AlgorithmConstraints constraints, NamedGroupType[] types,
 351                 List<NamedGroup> requestedNamedGroups) {
 352             for (NamedGroup namedGroup : requestedNamedGroups) {
 353                 if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
 354                         namedGroup.isAvailable(negotiatedProtocol) &&
 355                         isSupported(namedGroup) &&
 356                         constraints.permits(
 357                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 358                                 namedGroup.algorithm,
 359                                 getParameters(namedGroup))) {
 360                     return namedGroup;
 361                 }
 362             }
 363 
 364             return null;
 365         }
 366 
 367         static NamedGroup getPreferredGroup(
 368                 ProtocolVersion negotiatedProtocol,
 369                 AlgorithmConstraints constraints, NamedGroupType[] types) {
 370             for (NamedGroup namedGroup : supportedNamedGroups) {
 371                 if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
 372                         namedGroup.isAvailable(negotiatedProtocol) &&
 373                         constraints.permits(
 374                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 375                                 namedGroup.algorithm,
 376                                 getParameters(namedGroup))) {
 377                     return namedGroup;
 378                 }
 379             }
 380 
 381             return null;
 382         }
 383     }
 384 
 385     /**
 386      * Network data producer of a "supported_groups" extension in
 387      * the ClientHello handshake message.
 388      */
 389     private static final class CHSupportedGroupsProducer
 390             extends SupportedGroups implements HandshakeProducer {
 391         // Prevent instantiation of this class.
 392         private CHSupportedGroupsProducer() {
 393             // blank
 394         }
 395 
 396         @Override
 397         public byte[] produce(ConnectionContext context,
 398                 HandshakeMessage message) throws IOException {
 399             // The producing happens in client side only.
 400             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 401 
 402             // Is it a supported and enabled extension?
 403             if (!chc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) {
 404                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 405                     SSLLogger.fine(
 406                         "Ignore unavailable supported_groups extension");
 407                 }
 408                 return null;
 409             }
 410 
 411             // Produce the extension.
 412             ArrayList<NamedGroup> namedGroups =
 413                 new ArrayList<>(SupportedGroups.supportedNamedGroups.length);
 414             for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
 415                 if ((!SupportedGroups.enableFFDHE) &&
 416                     (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
 417                     continue;
 418                 }
 419 
 420                 if (ng.isAvailable(chc.activeProtocols) &&
 421                         ng.isSupported(chc.activeCipherSuites) &&
 422                         chc.algorithmConstraints.permits(
 423                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 424                             ng.algorithm, getParameters(ng))) {
 425                     namedGroups.add(ng);
 426                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 427                     SSLLogger.fine(
 428                         "Ignore inactive or disabled named group: " + ng.name);
 429                 }
 430             }
 431 
 432             if (namedGroups.isEmpty()) {
 433                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 434                     SSLLogger.warning("no available named group");
 435                 }
 436 
 437                 return null;
 438             }
 439 
 440             int vectorLen = namedGroups.size() << 1;
 441             byte[] extData = new byte[vectorLen + 2];
 442             ByteBuffer m = ByteBuffer.wrap(extData);
 443             Record.putInt16(m, vectorLen);
 444             for (NamedGroup namedGroup : namedGroups) {
 445                     Record.putInt16(m, namedGroup.id);
 446             }
 447 
 448             // Update the context.
 449             chc.clientRequestedNamedGroups =
 450                     Collections.<NamedGroup>unmodifiableList(namedGroups);
 451             chc.handshakeExtensions.put(CH_SUPPORTED_GROUPS,
 452                     new SupportedGroupsSpec(namedGroups));
 453 
 454             return extData;
 455         }
 456     }
 457 
 458     /**
 459      * Network data producer of a "supported_groups" extension in
 460      * the ClientHello handshake message.
 461      */
 462     private static final
 463             class CHSupportedGroupsConsumer implements ExtensionConsumer {
 464         // Prevent instantiation of this class.
 465         private CHSupportedGroupsConsumer() {
 466             // blank
 467         }
 468 
 469         @Override
 470         public void consume(ConnectionContext context,
 471             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 472             // The consuming happens in server side only.
 473             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 474 
 475             // Is it a supported and enabled extension?
 476             if (!shc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) {
 477                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 478                     SSLLogger.fine(
 479                         "Ignore unavailable supported_groups extension");
 480                 }
 481                 return;     // ignore the extension
 482             }
 483 
 484             // Parse the extension.
 485             SupportedGroupsSpec spec;
 486             try {
 487                 spec = new SupportedGroupsSpec(buffer);
 488             } catch (IOException ioe) {
 489                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 490             }
 491 
 492             // Update the context.
 493             List<NamedGroup> knownNamedGroups = new LinkedList<>();
 494             for (int id : spec.namedGroupsIds) {
 495                 NamedGroup ng = NamedGroup.valueOf(id);
 496                 if (ng != null) {
 497                     knownNamedGroups.add(ng);
 498                 }
 499             }
 500 
 501             shc.clientRequestedNamedGroups = knownNamedGroups;
 502             shc.handshakeExtensions.put(CH_SUPPORTED_GROUPS, spec);
 503 
 504             // No impact on session resumption.
 505         }
 506     }
 507 
 508     /**
 509      * The absence processing if the extension is not present in
 510      * a ClientHello handshake message.
 511      */
 512     private static final class CHSupportedGroupsOnTradeAbsence
 513             implements HandshakeAbsence {
 514         @Override
 515         public void absent(ConnectionContext context,
 516                 HandshakeMessage message) throws IOException {
 517             // The producing happens in server side only.
 518             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 519 
 520             // A client is considered to be attempting to negotiate using this
 521             // specification if the ClientHello contains a "supported_versions"
 522             // extension with 0x0304 contained in its body.  Such a ClientHello
 523             // message MUST meet the following requirements:
 524             //    -  If containing a "supported_groups" extension, it MUST also
 525             //       contain a "key_share" extension, and vice versa.  An empty
 526             //       KeyShare.client_shares vector is permitted.
 527             if (shc.negotiatedProtocol.useTLS13PlusSpec() &&
 528                     shc.handshakeExtensions.containsKey(
 529                             SSLExtension.CH_KEY_SHARE)) {
 530                 throw shc.conContext.fatal(Alert.MISSING_EXTENSION,
 531                         "No supported_groups extension to work with " +
 532                         "the key_share extension");
 533             }
 534         }
 535     }
 536 
 537     /**
 538      * Network data producer of a "supported_groups" extension in
 539      * the EncryptedExtensions handshake message.
 540      */
 541     private static final class EESupportedGroupsProducer
 542             extends SupportedGroups implements HandshakeProducer {
 543 
 544         // Prevent instantiation of this class.
 545         private EESupportedGroupsProducer() {
 546             // blank
 547         }
 548 
 549         @Override
 550         public byte[] produce(ConnectionContext context,
 551                 HandshakeMessage message) throws IOException {
 552             // The producing happens in server side only.
 553             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 554 
 555             // Is it a supported and enabled extension?
 556             if (!shc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
 557                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 558                     SSLLogger.fine(
 559                         "Ignore unavailable supported_groups extension");
 560                 }
 561                 return null;
 562             }
 563 
 564             // Produce the extension.
 565             //
 566             // Contains all groups the server supports, regardless of whether
 567             // they are currently supported by the client.
 568             ArrayList<NamedGroup> namedGroups = new ArrayList<>(
 569                     SupportedGroups.supportedNamedGroups.length);
 570             for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
 571                 if ((!SupportedGroups.enableFFDHE) &&
 572                     (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
 573                     continue;
 574                 }
 575 
 576                 if (ng.isAvailable(shc.activeProtocols) &&
 577                         ng.isSupported(shc.activeCipherSuites) &&
 578                         shc.algorithmConstraints.permits(
 579                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 580                             ng.algorithm, getParameters(ng))) {
 581                     namedGroups.add(ng);
 582                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 583                     SSLLogger.fine(
 584                         "Ignore inactive or disabled named group: " + ng.name);
 585                 }
 586             }
 587 
 588             if (namedGroups.isEmpty()) {
 589                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 590                     SSLLogger.warning("no available named group");
 591                 }
 592 
 593                 return null;
 594             }
 595 
 596             int vectorLen = namedGroups.size() << 1;
 597             byte[] extData = new byte[vectorLen + 2];
 598             ByteBuffer m = ByteBuffer.wrap(extData);
 599             Record.putInt16(m, vectorLen);
 600             for (NamedGroup namedGroup : namedGroups) {
 601                     Record.putInt16(m, namedGroup.id);
 602             }
 603 
 604             // Update the context.
 605             shc.conContext.serverRequestedNamedGroups =
 606                     Collections.<NamedGroup>unmodifiableList(namedGroups);
 607             SupportedGroupsSpec spec = new SupportedGroupsSpec(namedGroups);
 608             shc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
 609 
 610             return extData;
 611         }
 612     }
 613 
 614     private static final
 615             class EESupportedGroupsConsumer implements ExtensionConsumer {
 616         // Prevent instantiation of this class.
 617         private EESupportedGroupsConsumer() {
 618             // blank
 619         }
 620 
 621         @Override
 622         public void consume(ConnectionContext context,
 623             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 624             // The consuming happens in client side only.
 625             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 626 
 627             // Is it a supported and enabled extension?
 628             if (!chc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
 629                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 630                     SSLLogger.fine(
 631                         "Ignore unavailable supported_groups extension");
 632                 }
 633                 return;     // ignore the extension
 634             }
 635 
 636             // Parse the extension.
 637             SupportedGroupsSpec spec;
 638             try {
 639                 spec = new SupportedGroupsSpec(buffer);
 640             } catch (IOException ioe) {
 641                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 642             }
 643 
 644             // Update the context.
 645             List<NamedGroup> knownNamedGroups =
 646                     new ArrayList<>(spec.namedGroupsIds.length);
 647             for (int id : spec.namedGroupsIds) {
 648                 NamedGroup ng = NamedGroup.valueOf(id);
 649                 if (ng != null) {
 650                     knownNamedGroups.add(ng);
 651                 }
 652             }
 653 
 654             chc.conContext.serverRequestedNamedGroups = knownNamedGroups;
 655             chc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
 656 
 657             // No impact on session resumption.
 658         }
 659     }
 660 }