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.NoSuchAlgorithmException;
  34 import java.security.spec.AlgorithmParameterSpec;
  35 import java.security.spec.ECGenParameterSpec;
  36 import java.security.spec.ECParameterSpec;
  37 import java.security.spec.InvalidParameterSpecException;
  38 import java.text.MessageFormat;
  39 import java.util.ArrayList;
  40 import java.util.Collections;
  41 import java.util.EnumSet;
  42 import java.util.HashMap;
  43 import java.util.LinkedList;
  44 import java.util.List;
  45 import java.util.Locale;
  46 import java.util.Map;
  47 import javax.crypto.spec.DHParameterSpec;
  48 import javax.net.ssl.SSLProtocolException;
  49 import sun.security.action.GetPropertyAction;
  50 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
  51 import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
  52 import sun.security.ssl.SSLExtension.ExtensionConsumer;
  53 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
  54 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  55 
  56 /**
  57  * Pack of the "supported_groups" extensions [RFC 4492/7919].
  58  */
  59 final class SupportedGroupsExtension {
  60     static final HandshakeProducer chNetworkProducer =
  61             new CHSupportedGroupsProducer();
  62     static final ExtensionConsumer chOnLoadConsumer =
  63             new CHSupportedGroupsConsumer();
  64     static final SSLStringizer sgsStringizer =
  65             new SupportedGroupsStringizer();
  66 
  67     static final HandshakeProducer eeNetworkProducer =
  68             new EESupportedGroupsProducer();
  69     static final ExtensionConsumer eeOnLoadConsumer =
  70             new EESupportedGroupsConsumer();
  71 
  72     /**
  73      * The "supported_groups" extension.
  74      */
  75     static final class SupportedGroupsSpec implements SSLExtensionSpec {
  76         final int[] namedGroupsIds;
  77 
  78         private SupportedGroupsSpec(int[] namedGroupsIds) {
  79             this.namedGroupsIds = namedGroupsIds;
  80         }
  81 
  82         private SupportedGroupsSpec(List<NamedGroup> namedGroups) {
  83             this.namedGroupsIds = new int[namedGroups.size()];
  84             int i = 0;
  85             for (NamedGroup ng : namedGroups) {
  86                 namedGroupsIds[i++] = ng.id;
  87             }
  88         }
  89 
  90         private SupportedGroupsSpec(ByteBuffer m) throws IOException  {
  91             if (m.remaining() < 2) {      // 2: the length of the list
  92                 throw new SSLProtocolException(
  93                     "Invalid supported_groups extension: insufficient data");
  94             }
  95 
  96             byte[] ngs = Record.getBytes16(m);
  97             if (m.hasRemaining()) {
  98                 throw new SSLProtocolException(
  99                     "Invalid supported_groups extension: unknown extra data");
 100             }
 101 
 102             if ((ngs == null) || (ngs.length == 0) || (ngs.length % 2 != 0)) {
 103                 throw new SSLProtocolException(
 104                     "Invalid supported_groups extension: incomplete data");
 105             }
 106 
 107             int[] ids = new int[ngs.length / 2];
 108             for (int i = 0, j = 0; i < ngs.length;) {
 109                 ids[j++] = ((ngs[i++] & 0xFF) << 8) | (ngs[i++] & 0xFF);
 110             }
 111 
 112             this.namedGroupsIds = ids;
 113         }
 114 
 115         @Override
 116         public String toString() {
 117             MessageFormat messageFormat = new MessageFormat(
 118                 "\"versions\": '['{0}']'", Locale.ENGLISH);
 119 
 120             if (namedGroupsIds == null || namedGroupsIds.length == 0) {
 121                 Object[] messageFields = {
 122                         "<no supported named group specified>"
 123                     };
 124                 return messageFormat.format(messageFields);
 125             } else {
 126                 StringBuilder builder = new StringBuilder(512);
 127                 boolean isFirst = true;
 128                 for (int ngid : namedGroupsIds) {
 129                     if (isFirst) {
 130                         isFirst = false;
 131                     } else {
 132                         builder.append(", ");
 133                     }
 134 
 135                     builder.append(NamedGroup.nameOf(ngid));
 136                 }
 137 
 138                 Object[] messageFields = {
 139                         builder.toString()
 140                     };
 141 
 142                 return messageFormat.format(messageFields);
 143             }
 144         }
 145     }
 146 
 147     private static final
 148             class SupportedGroupsStringizer implements SSLStringizer {
 149         @Override
 150         public String toString(ByteBuffer buffer) {
 151             try {
 152                 return (new SupportedGroupsSpec(buffer)).toString();
 153             } catch (IOException ioe) {
 154                 // For debug logging only, so please swallow exceptions.
 155                 return ioe.getMessage();
 156             }
 157         }
 158     }
 159 
 160     static enum NamedGroupType {
 161         NAMED_GROUP_ECDHE,          // Elliptic Curve Groups (ECDHE)
 162         NAMED_GROUP_FFDHE,          // Finite Field Groups (DHE)
 163         NAMED_GROUP_XDH,            // Finite Field Groups (XDH)
 164         NAMED_GROUP_ARBITRARY,      // arbitrary prime and curves (ECDHE)
 165         NAMED_GROUP_NONE;           // Not predefined named group
 166 
 167         boolean isSupported(List<CipherSuite> cipherSuites) {
 168             for (CipherSuite cs : cipherSuites) {
 169                 if (cs.keyExchange == null || cs.keyExchange.groupType == this) {
 170                     return true;
 171                 }
 172             }
 173 
 174             return false;
 175         }
 176     }
 177 
 178     static enum NamedGroup {
 179         // Elliptic Curves (RFC 4492)
 180         //
 181         // See sun.security.util.CurveDB for the OIDs
 182         // NIST K-163
 183         SECT163_K1  (0x0001, "sect163k1", "1.3.132.0.1", true,
 184                             ProtocolVersion.PROTOCOLS_TO_12),
 185         SECT163_R1  (0x0002, "sect163r1", "1.3.132.0.2", false,
 186                             ProtocolVersion.PROTOCOLS_TO_12),
 187 
 188         // NIST B-163
 189         SECT163_R2  (0x0003, "sect163r2", "1.3.132.0.15", true,
 190                             ProtocolVersion.PROTOCOLS_TO_12),
 191         SECT193_R1  (0x0004, "sect193r1", "1.3.132.0.24", false,
 192                             ProtocolVersion.PROTOCOLS_TO_12),
 193         SECT193_R2  (0x0005, "sect193r2", "1.3.132.0.25", false,
 194                             ProtocolVersion.PROTOCOLS_TO_12),
 195 
 196         // NIST K-233
 197         SECT233_K1  (0x0006, "sect233k1", "1.3.132.0.26", true,
 198                             ProtocolVersion.PROTOCOLS_TO_12),
 199 
 200         // NIST B-233
 201         SECT233_R1  (0x0007, "sect233r1", "1.3.132.0.27", true,
 202                             ProtocolVersion.PROTOCOLS_TO_12),
 203         SECT239_K1  (0x0008, "sect239k1", "1.3.132.0.3", false,
 204                             ProtocolVersion.PROTOCOLS_TO_12),
 205 
 206         // NIST K-283
 207         SECT283_K1  (0x0009, "sect283k1", "1.3.132.0.16", true,
 208                             ProtocolVersion.PROTOCOLS_TO_12),
 209 
 210         // NIST B-283
 211         SECT283_R1  (0x000A, "sect283r1", "1.3.132.0.17", true,
 212                             ProtocolVersion.PROTOCOLS_TO_12),
 213 
 214         // NIST K-409
 215         SECT409_K1  (0x000B, "sect409k1", "1.3.132.0.36", true,
 216                             ProtocolVersion.PROTOCOLS_TO_12),
 217 
 218         // NIST B-409
 219         SECT409_R1  (0x000C, "sect409r1", "1.3.132.0.37", true,
 220                             ProtocolVersion.PROTOCOLS_TO_12),
 221 
 222         // NIST K-571
 223         SECT571_K1  (0x000D, "sect571k1", "1.3.132.0.38", true,
 224                             ProtocolVersion.PROTOCOLS_TO_12),
 225 
 226         // NIST B-571
 227         SECT571_R1  (0x000E, "sect571r1", "1.3.132.0.39", true,
 228                             ProtocolVersion.PROTOCOLS_TO_12),
 229         SECP160_K1  (0x000F, "secp160k1", "1.3.132.0.9", false,
 230                             ProtocolVersion.PROTOCOLS_TO_12),
 231         SECP160_R1  (0x0010, "secp160r1", "1.3.132.0.8", false,
 232                             ProtocolVersion.PROTOCOLS_TO_12),
 233         SECP160_R2  (0x0011, "secp160r2", "1.3.132.0.30", false,
 234                             ProtocolVersion.PROTOCOLS_TO_12),
 235         SECP192_K1  (0x0012, "secp192k1", "1.3.132.0.31", false,
 236                             ProtocolVersion.PROTOCOLS_TO_12),
 237 
 238         // NIST P-192
 239         SECP192_R1  (0x0013, "secp192r1", "1.2.840.10045.3.1.1", true,
 240                             ProtocolVersion.PROTOCOLS_TO_12),
 241         SECP224_K1  (0x0014, "secp224k1", "1.3.132.0.32", false,
 242                             ProtocolVersion.PROTOCOLS_TO_12),
 243         // NIST P-224
 244         SECP224_R1  (0x0015, "secp224r1", "1.3.132.0.33", true,
 245                             ProtocolVersion.PROTOCOLS_TO_12),
 246         SECP256_K1  (0x0016, "secp256k1", "1.3.132.0.10", false,
 247                             ProtocolVersion.PROTOCOLS_TO_12),
 248 
 249         // NIST P-256
 250         SECP256_R1  (0x0017, "secp256r1", "1.2.840.10045.3.1.7", true,
 251                             ProtocolVersion.PROTOCOLS_TO_13),
 252 
 253         // NIST P-384
 254         SECP384_R1  (0x0018, "secp384r1", "1.3.132.0.34", true,
 255                             ProtocolVersion.PROTOCOLS_TO_13),
 256 
 257         // NIST P-521
 258         SECP521_R1  (0x0019, "secp521r1", "1.3.132.0.35", true,
 259                             ProtocolVersion.PROTOCOLS_TO_13),
 260 
 261         // x25519 and x448
 262         X25519      (0x001D, "x25519", true, "x25519",
 263                             ProtocolVersion.PROTOCOLS_TO_13),
 264         X448        (0x001E, "x448", true, "x448",
 265                             ProtocolVersion.PROTOCOLS_TO_13),
 266 
 267         // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
 268         FFDHE_2048  (0x0100, "ffdhe2048",  true,
 269                             ProtocolVersion.PROTOCOLS_TO_13),
 270         FFDHE_3072  (0x0101, "ffdhe3072",  true,
 271                             ProtocolVersion.PROTOCOLS_TO_13),
 272         FFDHE_4096  (0x0102, "ffdhe4096",  true,
 273                             ProtocolVersion.PROTOCOLS_TO_13),
 274         FFDHE_6144  (0x0103, "ffdhe6144",  true,
 275                             ProtocolVersion.PROTOCOLS_TO_13),
 276         FFDHE_8192  (0x0104, "ffdhe8192",  true,
 277                             ProtocolVersion.PROTOCOLS_TO_13),
 278 
 279         // Elliptic Curves (RFC 4492)
 280         //
 281         // arbitrary prime and characteristic-2 curves
 282         ARBITRARY_PRIME  (0xFF01, "arbitrary_explicit_prime_curves",
 283                             ProtocolVersion.PROTOCOLS_TO_12),
 284         ARBITRARY_CHAR2  (0xFF02, "arbitrary_explicit_char2_curves",
 285                             ProtocolVersion.PROTOCOLS_TO_12);
 286 
 287         final int id;               // hash + signature
 288         final NamedGroupType type;  // group type
 289         final String name;          // literal name
 290         final String oid;           // object identifier of the named group
 291         final String algorithm;     // signature algorithm
 292         final boolean isFips;       // can be used in FIPS mode?
 293         final ProtocolVersion[] supportedProtocols;
 294 
 295         // Constructor used for Elliptic Curve Groups (ECDHE)
 296         private NamedGroup(int id, String name, String oid, boolean isFips,
 297                 ProtocolVersion[] supportedProtocols) {
 298             this.id = id;
 299             this.type = NamedGroupType.NAMED_GROUP_ECDHE;
 300             this.name = name;
 301             this.oid = oid;
 302             this.algorithm = "EC";
 303             this.isFips = isFips;
 304             this.supportedProtocols = supportedProtocols;
 305         }
 306 
 307         // Constructor used for Elliptic Curve Groups (XDH)
 308         private NamedGroup(int id, String name,
 309                 boolean isFips, String algorithm,
 310                 ProtocolVersion[] supportedProtocols) {
 311             this.id = id;
 312             this.type = NamedGroupType.NAMED_GROUP_XDH;
 313             this.name = name;
 314             this.oid = null;
 315             this.algorithm = algorithm;
 316             this.isFips = isFips;
 317             this.supportedProtocols = supportedProtocols;
 318         }
 319 
 320         // Constructor used for Finite Field Diffie-Hellman Groups (FFDHE)
 321         private NamedGroup(int id, String name, boolean isFips,
 322                 ProtocolVersion[] supportedProtocols) {
 323             this.id = id;
 324             this.type = NamedGroupType.NAMED_GROUP_FFDHE;
 325             this.name = name;
 326             this.oid = null;
 327             this.algorithm = "DiffieHellman";
 328             this.isFips = isFips;
 329             this.supportedProtocols = supportedProtocols;
 330         }
 331 
 332         // Constructor used for arbitrary prime and curves (ECDHE)
 333         private NamedGroup(int id, String name,
 334                 ProtocolVersion[] supportedProtocols) {
 335             this.id = id;
 336             this.type = NamedGroupType.NAMED_GROUP_ARBITRARY;
 337             this.name = name;
 338             this.oid = null;
 339             this.algorithm = "EC";
 340             this.isFips = false;
 341             this.supportedProtocols = supportedProtocols;
 342         }
 343 
 344         static NamedGroup valueOf(int id) {
 345             for (NamedGroup group : NamedGroup.values()) {
 346                 if (group.id == id) {
 347                     return group;
 348                 }
 349             }
 350 
 351             return null;
 352         }
 353 
 354         static NamedGroup valueOf(ECParameterSpec params) {
 355             String oid = JsseJce.getNamedCurveOid(params);
 356             if ((oid != null) && (!oid.isEmpty())) {
 357                 for (NamedGroup group : NamedGroup.values()) {
 358                     if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
 359                             oid.equals(group.oid)) {
 360                         return group;
 361                     }
 362                 }
 363             }
 364 
 365             return null;
 366         }
 367 
 368         static NamedGroup valueOf(DHParameterSpec params) {
 369             for (Map.Entry<NamedGroup, AlgorithmParameters> me :
 370                     SupportedGroups.namedGroupParams.entrySet()) {
 371                 NamedGroup ng = me.getKey();
 372                 if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
 373                     continue;
 374                 }
 375 
 376                 DHParameterSpec ngParams = null;
 377                 AlgorithmParameters aps = me.getValue();
 378                 try {
 379                     ngParams = aps.getParameterSpec(DHParameterSpec.class);
 380                 } catch (InvalidParameterSpecException ipse) {
 381                     // should be unlikely
 382                 }
 383 
 384                 if (ngParams == null) {
 385                     continue;
 386                 }
 387 
 388                 if (ngParams.getP().equals(params.getP()) &&
 389                         ngParams.getG().equals(params.getG())) {
 390                     return ng;
 391                 }
 392             }
 393 
 394             return null;
 395         }
 396 
 397         static NamedGroup nameOf(String name) {
 398             for (NamedGroup group : NamedGroup.values()) {
 399                 if (group.name.equals(name)) {
 400                     return group;
 401                 }
 402             }
 403 
 404             return null;
 405         }
 406 
 407         static String nameOf(int id) {
 408             for (NamedGroup group : NamedGroup.values()) {
 409                 if (group.id == id) {
 410                     return group.name;
 411                 }
 412             }
 413 
 414             return "UNDEFINED-NAMED-GROUP(" + id + ")";
 415         }
 416 
 417         boolean isAvailable(List<ProtocolVersion> protocolVersions) {
 418             for (ProtocolVersion pv : supportedProtocols) {
 419                 if (protocolVersions.contains(pv)) {
 420                     return true;
 421                 }
 422             }
 423             return false;
 424         }
 425 
 426         boolean isAvailable(ProtocolVersion protocolVersion) {
 427             for (ProtocolVersion pv : supportedProtocols) {
 428                 if (protocolVersion == pv) {
 429                     return true;
 430                 }
 431             }
 432             return false;
 433         }
 434 
 435         boolean isSupported(List<CipherSuite> cipherSuites) {
 436             for (CipherSuite cs : cipherSuites) {
 437                 boolean isMatch = isAvailable(cs.supportedProtocols);
 438                 if (isMatch && (cs.keyExchange == null ||
 439                         cs.keyExchange.groupType == type)) {
 440                     return true;
 441                 }
 442             }
 443             return false;
 444         }
 445 
 446         // lazy loading of parameters
 447         AlgorithmParameters getParameters() {
 448             return SupportedGroups.namedGroupParams.get(this);
 449         }
 450 
 451         AlgorithmParameterSpec getParameterSpec() {
 452             if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) {
 453                 return SupportedGroups.getECGenParamSpec(this);
 454             } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) {
 455                 return SupportedGroups.getDHParameterSpec(this);
 456             }
 457 
 458             return null;
 459         }
 460     }
 461 
 462     static class SupportedGroups {
 463         // To switch off the supported_groups extension for DHE cipher suite.
 464         static final boolean enableFFDHE =
 465                 Utilities.getBooleanProperty("jsse.enableFFDHE", true);
 466 
 467         // cache to speed up the parameters construction
 468         static final Map<NamedGroup,
 469                     AlgorithmParameters> namedGroupParams = new HashMap<>();
 470 
 471         // the supported named groups
 472         static final NamedGroup[] supportedNamedGroups;
 473 
 474         static {
 475             boolean requireFips = SunJSSE.isFIPS();
 476 
 477             // The value of the System Property defines a list of enabled named
 478             // groups in preference order, separated with comma.  For example:
 479             //
 480             //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
 481             //
 482             // If the System Property is not defined or the value is empty, the
 483             // default groups and preferences will be used.
 484             String property = GetPropertyAction
 485                     .privilegedGetProperty("jdk.tls.namedGroups");
 486             if (property != null && !property.isEmpty()) {
 487                 // remove double quote marks from beginning/end of the property
 488                 if (property.length() > 1 && property.charAt(0) == '"' &&
 489                         property.charAt(property.length() - 1) == '"') {
 490                     property = property.substring(1, property.length() - 1);
 491                 }
 492             }
 493 
 494             ArrayList<NamedGroup> groupList;
 495             if (property != null && !property.isEmpty()) {
 496                 String[] groups = property.split(",");
 497                 groupList = new ArrayList<>(groups.length);
 498                 for (String group : groups) {
 499                     group = group.trim();
 500                     if (!group.isEmpty()) {
 501                         NamedGroup namedGroup = NamedGroup.nameOf(group);
 502                         if (namedGroup != null &&
 503                                 (!requireFips || namedGroup.isFips)) {
 504                             if (isAvailableGroup(namedGroup)) {
 505                                 groupList.add(namedGroup);
 506                             }
 507                         }   // ignore unknown groups
 508                     }
 509                 }
 510 
 511                 if (groupList.isEmpty()) {
 512                     throw new IllegalArgumentException(
 513                             "System property jdk.tls.namedGroups(" +
 514                             property + ") contains no supported named groups");
 515                 }
 516             } else {        // default groups
 517                 NamedGroup[] groups;
 518                 if (requireFips) {
 519                     groups = new NamedGroup[] {
 520                         // only NIST curves in FIPS mode
 521                         NamedGroup.SECP256_R1,
 522                         NamedGroup.SECP384_R1,
 523                         NamedGroup.SECP521_R1,
 524                         NamedGroup.SECT283_K1,
 525                         NamedGroup.SECT283_R1,
 526                         NamedGroup.SECT409_K1,
 527                         NamedGroup.SECT409_R1,
 528                         NamedGroup.SECT571_K1,
 529                         NamedGroup.SECT571_R1,
 530 
 531                         // FFDHE 2048
 532                         NamedGroup.FFDHE_2048,
 533                         NamedGroup.FFDHE_3072,
 534                         NamedGroup.FFDHE_4096,
 535                         NamedGroup.FFDHE_6144,
 536                         NamedGroup.FFDHE_8192,
 537                     };
 538                 } else {
 539                     groups = new NamedGroup[] {
 540                         // NIST curves first
 541                         NamedGroup.SECP256_R1,
 542                         NamedGroup.SECP384_R1,
 543                         NamedGroup.SECP521_R1,
 544                         NamedGroup.SECT283_K1,
 545                         NamedGroup.SECT283_R1,
 546                         NamedGroup.SECT409_K1,
 547                         NamedGroup.SECT409_R1,
 548                         NamedGroup.SECT571_K1,
 549                         NamedGroup.SECT571_R1,
 550 
 551                         // non-NIST curves
 552                         NamedGroup.SECP256_K1,
 553 
 554                         // FFDHE 2048
 555                         NamedGroup.FFDHE_2048,
 556                         NamedGroup.FFDHE_3072,
 557                         NamedGroup.FFDHE_4096,
 558                         NamedGroup.FFDHE_6144,
 559                         NamedGroup.FFDHE_8192,
 560                     };
 561                 }
 562 
 563                 groupList = new ArrayList<>(groups.length);
 564                 for (NamedGroup group : groups) {
 565                     if (isAvailableGroup(group)) {
 566                         groupList.add(group);
 567                     }
 568                 }
 569 
 570                 if (groupList.isEmpty() &&
 571                         SSLLogger.isOn && SSLLogger.isOn("ssl")) {
 572                     SSLLogger.warning("No default named groups");
 573                 }
 574             }
 575 
 576             supportedNamedGroups = new NamedGroup[groupList.size()];
 577             int i = 0;
 578             for (NamedGroup namedGroup : groupList) {
 579                 supportedNamedGroups[i++] = namedGroup;
 580             }
 581         }
 582 
 583         // check whether the group is supported by the underlying providers
 584         private static boolean isAvailableGroup(NamedGroup namedGroup) {
 585             AlgorithmParameters params = null;
 586             AlgorithmParameterSpec spec = null;
 587             if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
 588                 if (namedGroup.oid != null) {
 589                     try {
 590                         params = JsseJce.getAlgorithmParameters("EC");
 591                         spec = new ECGenParameterSpec(namedGroup.oid);
 592                     } catch (NoSuchAlgorithmException e) {
 593                         return false;
 594                     }
 595                 }
 596             } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
 597                 try {
 598                     params = JsseJce.getAlgorithmParameters("DiffieHellman");
 599                     spec = getFFDHEDHParameterSpec(namedGroup);
 600                 } catch (NoSuchAlgorithmException e) {
 601                     return false;
 602                 }
 603             }   // Otherwise, unsupported.
 604 
 605             if ((params != null) && (spec != null)) {
 606                 try {
 607                     params.init(spec);
 608                 } catch (InvalidParameterSpecException e) {
 609                     return false;
 610                 }
 611 
 612                 // cache the parameters
 613                 namedGroupParams.put(namedGroup, params);
 614 
 615                 return true;
 616             }
 617 
 618             return false;
 619         }
 620 
 621         private static DHParameterSpec getFFDHEDHParameterSpec(
 622                 NamedGroup namedGroup) {
 623             DHParameterSpec spec = null;
 624             switch (namedGroup) {
 625                 case FFDHE_2048:
 626                     spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
 627                     break;
 628                 case FFDHE_3072:
 629                     spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
 630                     break;
 631                 case FFDHE_4096:
 632                     spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
 633                     break;
 634                 case FFDHE_6144:
 635                     spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
 636                     break;
 637                 case FFDHE_8192:
 638                     spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
 639             }
 640 
 641             return spec;
 642         }
 643 
 644         private static DHParameterSpec getPredefinedDHParameterSpec(
 645                 NamedGroup namedGroup) {
 646             DHParameterSpec spec = null;
 647             switch (namedGroup) {
 648                 case FFDHE_2048:
 649                     spec = PredefinedDHParameterSpecs.definedParams.get(2048);
 650                     break;
 651                 case FFDHE_3072:
 652                     spec = PredefinedDHParameterSpecs.definedParams.get(3072);
 653                     break;
 654                 case FFDHE_4096:
 655                     spec = PredefinedDHParameterSpecs.definedParams.get(4096);
 656                     break;
 657                 case FFDHE_6144:
 658                     spec = PredefinedDHParameterSpecs.definedParams.get(6144);
 659                     break;
 660                 case FFDHE_8192:
 661                     spec = PredefinedDHParameterSpecs.definedParams.get(8192);
 662             }
 663 
 664             return spec;
 665         }
 666 
 667         static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) {
 668             if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
 669                 throw new RuntimeException(
 670                         "Not a named EC group: " + namedGroup);
 671             }
 672 
 673             AlgorithmParameters params = namedGroupParams.get(namedGroup);
 674             if (params == null) {
 675                 throw new RuntimeException(
 676                         "Not a supported EC named group: " + namedGroup);
 677             }
 678 
 679             try {
 680                 return params.getParameterSpec(ECGenParameterSpec.class);
 681             } catch (InvalidParameterSpecException ipse) {
 682                 // should be unlikely
 683                 return new ECGenParameterSpec(namedGroup.oid);
 684             }
 685         }
 686 
 687         static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) {
 688             if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) {
 689                 throw new RuntimeException(
 690                         "Not a named DH group: " + namedGroup);
 691             }
 692 
 693             AlgorithmParameters params = namedGroupParams.get(namedGroup);
 694             if (params == null) {
 695                 throw new RuntimeException(
 696                         "Not a supported DH named group: " + namedGroup);
 697             }
 698 
 699             try {
 700                 return params.getParameterSpec(DHParameterSpec.class);
 701             } catch (InvalidParameterSpecException ipse) {
 702                 // should be unlikely
 703                 return getPredefinedDHParameterSpec(namedGroup);
 704             }
 705         }
 706 
 707         // Is there any supported group permitted by the constraints?
 708         static boolean isActivatable(
 709                 AlgorithmConstraints constraints, NamedGroupType type) {
 710 
 711             boolean hasFFDHEGroups = false;
 712             for (NamedGroup namedGroup : supportedNamedGroups) {
 713                 if (namedGroup.type == type) {
 714                     if (constraints.permits(
 715                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 716                             namedGroup.algorithm,
 717                             namedGroupParams.get(namedGroup))) {
 718 
 719                         return true;
 720                     }
 721 
 722                     if (!hasFFDHEGroups &&
 723                             (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
 724                         hasFFDHEGroups = true;
 725                     }
 726                 }
 727             }
 728 
 729             // For compatibility, if no FFDHE groups are defined, the non-FFDHE
 730             // compatible mode (using DHE cipher suite without FFDHE extension)
 731             // is allowed.
 732             //
 733             // Note that the constraints checking on DHE parameters will be
 734             // performed during key exchanging in a handshake.
 735             return !hasFFDHEGroups && type == NamedGroupType.NAMED_GROUP_FFDHE;
 736         }
 737 
 738         // Is the named group permitted by the constraints?
 739         static boolean isActivatable(
 740                 AlgorithmConstraints constraints, NamedGroup namedGroup) {
 741             if (!isSupported(namedGroup)) {
 742                 return false;
 743             }
 744 
 745             return constraints.permits(
 746                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 747                             namedGroup.algorithm,
 748                             namedGroupParams.get(namedGroup));
 749         }
 750 
 751         // Is the named group supported?
 752         static boolean isSupported(NamedGroup namedGroup) {
 753             for (NamedGroup group : supportedNamedGroups) {
 754                 if (namedGroup.id == group.id) {
 755                     return true;
 756                 }
 757             }
 758 
 759             return false;
 760         }
 761 
 762         static NamedGroup getPreferredGroup(
 763                 ProtocolVersion negotiatedProtocol,
 764                 AlgorithmConstraints constraints, NamedGroupType type,
 765                 List<NamedGroup> requestedNamedGroups) {
 766             for (NamedGroup namedGroup : requestedNamedGroups) {
 767                 if ((namedGroup.type == type) &&
 768                         namedGroup.isAvailable(negotiatedProtocol) &&
 769                         isSupported(namedGroup) &&
 770                         constraints.permits(
 771                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 772                                 namedGroup.algorithm,
 773                                 namedGroupParams.get(namedGroup))) {
 774                     return namedGroup;
 775                 }
 776             }
 777 
 778             return null;
 779         }
 780 
 781         static NamedGroup getPreferredGroup(
 782                 ProtocolVersion negotiatedProtocol,
 783                 AlgorithmConstraints constraints, NamedGroupType type) {
 784             for (NamedGroup namedGroup : supportedNamedGroups) {
 785                 if ((namedGroup.type == type) &&
 786                         namedGroup.isAvailable(negotiatedProtocol) &&
 787                         constraints.permits(
 788                                 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 789                                 namedGroup.algorithm,
 790                                 namedGroupParams.get(namedGroup))) {
 791                     return namedGroup;
 792                 }
 793             }
 794 
 795             return null;
 796         }
 797     }
 798 
 799     /**
 800      * Network data producer of a "supported_groups" extension in
 801      * the ClientHello handshake message.
 802      */
 803     private static final class CHSupportedGroupsProducer
 804             extends SupportedGroups implements HandshakeProducer {
 805         // Prevent instantiation of this class.
 806         private CHSupportedGroupsProducer() {
 807             // blank
 808         }
 809 
 810         @Override
 811         public byte[] produce(ConnectionContext context,
 812                 HandshakeMessage message) throws IOException {
 813             // The producing happens in client side only.
 814             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 815 
 816             // Is it a supported and enabled extension?
 817             if (!chc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) {
 818                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 819                     SSLLogger.fine(
 820                         "Ignore unavailable supported_groups extension");
 821                 }
 822                 return null;
 823             }
 824 
 825             // Produce the extension.
 826             ArrayList<NamedGroup> namedGroups =
 827                 new ArrayList<>(SupportedGroups.supportedNamedGroups.length);
 828             for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
 829                 if ((!SupportedGroups.enableFFDHE) &&
 830                     (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
 831                     continue;
 832                 }
 833 
 834                 if (ng.isAvailable(chc.activeProtocols) &&
 835                         ng.isSupported(chc.activeCipherSuites) &&
 836                         chc.algorithmConstraints.permits(
 837                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 838                             ng.algorithm, namedGroupParams.get(ng))) {
 839                     namedGroups.add(ng);
 840                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 841                     SSLLogger.fine(
 842                         "Ignore inactive or disabled named group: " + ng.name);
 843                 }
 844             }
 845 
 846             if (namedGroups.isEmpty()) {
 847                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 848                     SSLLogger.warning("no available named group");
 849                 }
 850 
 851                 return null;
 852             }
 853 
 854             int vectorLen = namedGroups.size() << 1;
 855             byte[] extData = new byte[vectorLen + 2];
 856             ByteBuffer m = ByteBuffer.wrap(extData);
 857             Record.putInt16(m, vectorLen);
 858             for (NamedGroup namedGroup : namedGroups) {
 859                     Record.putInt16(m, namedGroup.id);
 860             }
 861 
 862             // Update the context.
 863             chc.clientRequestedNamedGroups =
 864                     Collections.<NamedGroup>unmodifiableList(namedGroups);
 865             chc.handshakeExtensions.put(CH_SUPPORTED_GROUPS,
 866                     new SupportedGroupsSpec(namedGroups));
 867 
 868             return extData;
 869         }
 870     }
 871 
 872     /**
 873      * Network data producer of a "supported_groups" extension in
 874      * the ClientHello handshake message.
 875      */
 876     private static final
 877             class CHSupportedGroupsConsumer implements ExtensionConsumer {
 878         // Prevent instantiation of this class.
 879         private CHSupportedGroupsConsumer() {
 880             // blank
 881         }
 882 
 883         @Override
 884         public void consume(ConnectionContext context,
 885             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 886             // The consuming happens in server side only.
 887             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 888 
 889             // Is it a supported and enabled extension?
 890             if (!shc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) {
 891                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 892                     SSLLogger.fine(
 893                         "Ignore unavailable supported_groups extension");
 894                 }
 895                 return;     // ignore the extension
 896             }
 897 
 898             // Parse the extension.
 899             SupportedGroupsSpec spec;
 900             try {
 901                 spec = new SupportedGroupsSpec(buffer);
 902             } catch (IOException ioe) {
 903                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 904                 return;     // fatal() always throws, make the compiler happy.
 905             }
 906 
 907             // Update the context.
 908             List<NamedGroup> knownNamedGroups = new LinkedList<>();
 909             for (int id : spec.namedGroupsIds) {
 910                 NamedGroup ng = NamedGroup.valueOf(id);
 911                 if (ng != null) {
 912                     knownNamedGroups.add(ng);
 913                 }
 914             }
 915 
 916             shc.clientRequestedNamedGroups = knownNamedGroups;
 917             shc.handshakeExtensions.put(CH_SUPPORTED_GROUPS, spec);
 918 
 919             // No impact on session resumption.
 920         }
 921     }
 922 
 923     /**
 924      * Network data producer of a "supported_groups" extension in
 925      * the EncryptedExtensions handshake message.
 926      */
 927     private static final class EESupportedGroupsProducer
 928             extends SupportedGroups implements HandshakeProducer {
 929 
 930         // Prevent instantiation of this class.
 931         private EESupportedGroupsProducer() {
 932             // blank
 933         }
 934 
 935         @Override
 936         public byte[] produce(ConnectionContext context,
 937                 HandshakeMessage message) throws IOException {
 938             // The producing happens in server side only.
 939             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 940 
 941             // Is it a supported and enabled extension?
 942             if (!shc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
 943                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 944                     SSLLogger.fine(
 945                         "Ignore unavailable supported_groups extension");
 946                 }
 947                 return null;
 948             }
 949 
 950             // Produce the extension.
 951             //
 952             // Contains all groups the server supports, regardless of whether
 953             // they are currently supported by the client.
 954             ArrayList<NamedGroup> namedGroups = new ArrayList<>(
 955                     SupportedGroups.supportedNamedGroups.length);
 956             for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
 957                 if ((!SupportedGroups.enableFFDHE) &&
 958                     (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
 959                     continue;
 960                 }
 961 
 962                 if (ng.isAvailable(shc.activeProtocols) &&
 963                         ng.isSupported(shc.activeCipherSuites) &&
 964                         shc.algorithmConstraints.permits(
 965                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 966                             ng.algorithm, namedGroupParams.get(ng))) {
 967                     namedGroups.add(ng);
 968                 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 969                     SSLLogger.fine(
 970                         "Ignore inactive or disabled named group: " + ng.name);
 971                 }
 972             }
 973 
 974             if (namedGroups.isEmpty()) {
 975                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 976                     SSLLogger.warning("no available named group");
 977                 }
 978 
 979                 return null;
 980             }
 981 
 982             int vectorLen = namedGroups.size() << 1;
 983             byte[] extData = new byte[vectorLen + 2];
 984             ByteBuffer m = ByteBuffer.wrap(extData);
 985             Record.putInt16(m, vectorLen);
 986             for (NamedGroup namedGroup : namedGroups) {
 987                     Record.putInt16(m, namedGroup.id);
 988             }
 989 
 990             // Update the context.
 991             shc.conContext.serverRequestedNamedGroups =
 992                     Collections.<NamedGroup>unmodifiableList(namedGroups);
 993             SupportedGroupsSpec spec = new SupportedGroupsSpec(namedGroups);
 994             shc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
 995 
 996             return extData;
 997         }
 998     }
 999 
1000     private static final
1001             class EESupportedGroupsConsumer implements ExtensionConsumer {
1002         // Prevent instantiation of this class.
1003         private EESupportedGroupsConsumer() {
1004             // blank
1005         }
1006 
1007         @Override
1008         public void consume(ConnectionContext context,
1009             HandshakeMessage message, ByteBuffer buffer) throws IOException {
1010             // The consuming happens in client side only.
1011             ClientHandshakeContext chc = (ClientHandshakeContext)context;
1012 
1013             // Is it a supported and enabled extension?
1014             if (!chc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
1015                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
1016                     SSLLogger.fine(
1017                         "Ignore unavailable supported_groups extension");
1018                 }
1019                 return;     // ignore the extension
1020             }
1021 
1022             // Parse the extension.
1023             SupportedGroupsSpec spec;
1024             try {
1025                 spec = new SupportedGroupsSpec(buffer);
1026             } catch (IOException ioe) {
1027                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
1028                 return;     // fatal() always throws, make the compiler happy.
1029             }
1030 
1031             // Update the context.
1032             List<NamedGroup> knownNamedGroups =
1033                     new ArrayList<>(spec.namedGroupsIds.length);
1034             for (int id : spec.namedGroupsIds) {
1035                 NamedGroup ng = NamedGroup.valueOf(id);
1036                 if (ng != null) {
1037                     knownNamedGroups.add(ng);
1038                 }
1039             }
1040 
1041             chc.conContext.serverRequestedNamedGroups = knownNamedGroups;
1042             chc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
1043 
1044             // No impact on session resumption.
1045         }
1046     }
1047 }