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