src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 47216 71c04702a3d5
child 56589 bafd8be2f970
child 56704 c3ee22c3a0f6
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
     1 /*
     1 /*
     2  * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
     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.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    24  */
    24  */
    25 
    25 
    26 package sun.security.ssl;
    26 package sun.security.ssl;
    27 
    27 
    28 import java.io.IOException;
    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;
    29 import java.security.spec.ECGenParameterSpec;
    36 import java.security.spec.ECGenParameterSpec;
       
    37 import java.security.spec.ECParameterSpec;
    30 import java.security.spec.InvalidParameterSpecException;
    38 import java.security.spec.InvalidParameterSpecException;
    31 import java.security.AlgorithmParameters;
    39 import java.text.MessageFormat;
    32 import java.security.AlgorithmConstraints;
    40 import java.util.ArrayList;
    33 import java.security.CryptoPrimitive;
    41 import java.util.Collections;
    34 import java.security.AccessController;
       
    35 import java.security.spec.AlgorithmParameterSpec;
       
    36 import javax.crypto.spec.DHParameterSpec;
       
    37 import java.util.EnumSet;
    42 import java.util.EnumSet;
    38 import java.util.HashMap;
    43 import java.util.HashMap;
       
    44 import java.util.LinkedList;
       
    45 import java.util.List;
       
    46 import java.util.Locale;
    39 import java.util.Map;
    47 import java.util.Map;
    40 import java.util.ArrayList;
    48 import javax.crypto.spec.DHParameterSpec;
    41 import javax.net.ssl.SSLProtocolException;
    49 import javax.net.ssl.SSLProtocolException;
    42 
       
    43 import sun.security.action.GetPropertyAction;
    50 import sun.security.action.GetPropertyAction;
    44 
    51 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
    45 //
    52 import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
    46 // Note: Since RFC 7919, the extension's semantics are expanded from
    53 import sun.security.ssl.SSLExtension.ExtensionConsumer;
    47 // "Supported Elliptic Curves" to "Supported Groups".  The enum datatype
    54 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
    48 // used in the extension has been renamed from NamedCurve to NamedGroup.
    55 import sun.security.ssl.SSLHandshake.HandshakeMessage;
    49 // Its semantics are likewise expanded from "named curve" to "named group".
    56 
    50 //
    57 /**
    51 final class SupportedGroupsExtension extends HelloExtension {
    58  * Pack of the "supported_groups" extensions [RFC 4492/7919].
    52 
    59  */
    53     /* Class and subclass dynamic debugging support */
    60 final class SupportedGroupsExtension {
    54     private static final Debug debug = Debug.getInstance("ssl");
    61     static final HandshakeProducer chNetworkProducer =
    55 
    62             new CHSupportedGroupsProducer();
    56     private static final int ARBITRARY_PRIME = 0xff01;
    63     static final ExtensionConsumer chOnLoadConcumer =
    57     private static final int ARBITRARY_CHAR2 = 0xff02;
    64             new CHSupportedGroupsConsumer();
    58 
    65     static final SSLStringize sgsStringize =
    59     // cache to speed up the parameters construction
    66             new SupportedGroupsStringize();
    60     private static final Map<NamedGroup,
    67 
    61                 AlgorithmParameters> namedGroupParams = new HashMap<>();
    68     static final HandshakeProducer eeNetworkProducer =
    62 
    69             new EESupportedGroupsProducer();
    63     // the supported named groups
    70     static final ExtensionConsumer eeOnLoadConcumer =
    64     private static final NamedGroup[] supportedNamedGroups;
    71             new EESupportedGroupsConsumer();
    65 
    72 
    66     // the named group presented in the extension
    73     /**
    67     private final int[] requestedNamedGroupIds;
    74      * The "supported_groups" extension.
    68 
    75      */
    69     static {
    76     static final class SupportedGroupsSpec implements SSLExtensionSpec {
    70         boolean requireFips = SunJSSE.isFIPS();
    77         final int[] namedGroupsIds;
    71 
    78 
    72         // The value of the System Property defines a list of enabled named
    79         private SupportedGroupsSpec(int[] namedGroupsIds) {
    73         // groups in preference order, separated with comma.  For example:
    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)
    74         //
   181         //
    75         //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
   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)
    76         //
   281         //
    77         // If the System Property is not defined or the value is empty, the
   282         // arbitrary prime and characteristic-2 curves
    78         // default groups and preferences will be used.
   283         ARBITRARY_PRIME  (0xFF01, "arbitrary_explicit_prime_curves",
    79         String property = AccessController.doPrivileged(
   284                             ProtocolVersion.PROTOCOLS_TO_12),
    80                     new GetPropertyAction("jdk.tls.namedGroups"));
   285         ARBITRARY_CHAR2  (0xFF02, "arbitrary_explicit_char2_curves",
    81         if (property != null && property.length() != 0) {
   286                             ProtocolVersion.PROTOCOLS_TO_12);
    82             // remove double quote marks from beginning/end of the property
   287 
    83             if (property.length() > 1 && property.charAt(0) == '"' &&
   288         final int id;               // hash + signature
    84                     property.charAt(property.length() - 1) == '"') {
   289         final NamedGroupType type;  // group type
    85                 property = property.substring(1, property.length() - 1);
   290         final String name;          // literal name
    86             }
   291         final String oid;           // object identifier of the named group
    87         }
   292         final String algorithm;     // signature algorithm
    88 
   293         final boolean isFips;       // can be used in FIPS mode?
    89         ArrayList<NamedGroup> groupList;
   294         final ProtocolVersion[] supportedProtocols;
    90         if (property != null && property.length() != 0) {   // customized groups
   295 
    91             String[] groups = property.split(",");
   296         // Constructor used for Elliptic Curve Groups (ECDHE)
    92             groupList = new ArrayList<>(groups.length);
   297         private NamedGroup(int id, String name, String oid, boolean isFips,
    93             for (String group : groups) {
   298                 ProtocolVersion[] supportedProtocols) {
    94                 group = group.trim();
   299             this.id = id;
    95                 if (!group.isEmpty()) {
   300             this.type = NamedGroupType.NAMED_GROUP_ECDHE;
    96                     NamedGroup namedGroup = NamedGroup.nameOf(group);
   301             this.name = name;
    97                     if (namedGroup != null &&
   302             this.oid = oid;
    98                             (!requireFips || namedGroup.isFips)) {
   303             this.algorithm = "EC";
    99                         if (isAvailableGroup(namedGroup)) {
   304             this.isFips = isFips;
   100                             groupList.add(namedGroup);
   305             this.supportedProtocols = supportedProtocols;
   101                         }
   306         }
   102                     }   // ignore unknown groups
   307 
   103                 }
   308         // Constructor used for Elliptic Curve Groups (XDH)
   104             }
   309         private NamedGroup(int id, String name,
   105 
   310                 boolean isFips, String algorithm,
   106             if (groupList.isEmpty() && JsseJce.isEcAvailable()) {
   311                 ProtocolVersion[] supportedProtocols) {
   107                 throw new IllegalArgumentException(
   312             this.id = id;
   108                     "System property jdk.tls.namedGroups(" + property + ") " +
   313             this.type = NamedGroupType.NAMED_GROUP_XDH;
   109                     "contains no supported elliptic curves");
   314             this.name = name;
   110             }
   315             this.oid = null;
   111         } else {        // default groups
   316             this.algorithm = algorithm;
   112             NamedGroup[] groups;
   317             this.isFips = isFips;
   113             if (requireFips) {
   318             this.supportedProtocols = supportedProtocols;
   114                 groups = new NamedGroup[] {
   319         }
   115                     // only NIST curves in FIPS mode
   320 
   116                     NamedGroup.SECP256_R1,
   321         // Constructor used for Finite Field Diffie-Hellman Groups (FFDHE)
   117                     NamedGroup.SECP384_R1,
   322         private NamedGroup(int id, String name, boolean isFips,
   118                     NamedGroup.SECP521_R1,
   323                 ProtocolVersion[] supportedProtocols) {
   119                     NamedGroup.SECT283_K1,
   324             this.id = id;
   120                     NamedGroup.SECT283_R1,
   325             this.type = NamedGroupType.NAMED_GROUP_FFDHE;
   121                     NamedGroup.SECT409_K1,
   326             this.name = name;
   122                     NamedGroup.SECT409_R1,
   327             this.oid = null;
   123                     NamedGroup.SECT571_K1,
   328             this.algorithm = "DiffieHellman";
   124                     NamedGroup.SECT571_R1,
   329             this.isFips = isFips;
   125 
   330             this.supportedProtocols = supportedProtocols;
   126                     // FFDHE 2048
   331         }
   127                     NamedGroup.FFDHE_2048,
   332 
   128                     NamedGroup.FFDHE_3072,
   333         // Constructor used for arbitrary prime and curves (ECDHE)
   129                     NamedGroup.FFDHE_4096,
   334         private NamedGroup(int id, String name,
   130                     NamedGroup.FFDHE_6144,
   335                 ProtocolVersion[] supportedProtocols) {
   131                     NamedGroup.FFDHE_8192,
   336             this.id = id;
   132                 };
   337             this.type = NamedGroupType.NAMED_GROUP_ARBITRARY;
   133             } else {
   338             this.name = name;
   134                 groups = new NamedGroup[] {
   339             this.oid = null;
   135                     // NIST curves first
   340             this.algorithm = "EC";
   136                     NamedGroup.SECP256_R1,
   341             this.isFips = false;
   137                     NamedGroup.SECP384_R1,
   342             this.supportedProtocols = supportedProtocols;
   138                     NamedGroup.SECP521_R1,
   343         }
   139                     NamedGroup.SECT283_K1,
   344 
   140                     NamedGroup.SECT283_R1,
   345         static NamedGroup valueOf(int id) {
   141                     NamedGroup.SECT409_K1,
   346             for (NamedGroup group : NamedGroup.values()) {
   142                     NamedGroup.SECT409_R1,
   347                 if (group.id == id) {
   143                     NamedGroup.SECT571_K1,
   348                     return group;
   144                     NamedGroup.SECT571_R1,
   349                 }
   145 
   350             }
   146                     // non-NIST curves
   351 
   147                     NamedGroup.SECP256_K1,
   352             return null;
   148 
   353         }
   149                     // FFDHE 2048
   354 
   150                     NamedGroup.FFDHE_2048,
   355         static NamedGroup valueOf(ECParameterSpec params) {
   151                     NamedGroup.FFDHE_3072,
   356             String oid = JsseJce.getNamedCurveOid(params);
   152                     NamedGroup.FFDHE_4096,
   357             if ((oid != null) && (!oid.isEmpty())) {
   153                     NamedGroup.FFDHE_6144,
   358                 for (NamedGroup group : NamedGroup.values()) {
   154                     NamedGroup.FFDHE_8192,
   359                     if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
   155                 };
   360                             oid.equals(group.oid)) {
   156             }
   361                         return group;
   157 
   362                     }
   158             groupList = new ArrayList<>(groups.length);
   363                 }
   159             for (NamedGroup group : groups) {
   364             }
   160                 if (isAvailableGroup(group)) {
   365 
   161                     groupList.add(group);
   366             return null;
   162                 }
   367         }
   163             }
   368 
   164         }
   369         static NamedGroup valueOf(DHParameterSpec params) {
   165 
   370             for (Map.Entry<NamedGroup, AlgorithmParameters> me :
   166         if (debug != null && groupList.isEmpty()) {
   371                     SupportedGroups.namedGroupParams.entrySet()) {
   167             Debug.log(
   372                 NamedGroup ng = me.getKey();
   168                 "Initialized [jdk.tls.namedGroups|default] list contains " +
   373                 if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
   169                 "no available elliptic curves. " +
   374                     continue;
   170                 (property != null ? "(" + property + ")" : "[Default]"));
   375                 }
   171         }
   376 
   172 
   377                 DHParameterSpec ngParams = null;
   173         supportedNamedGroups = new NamedGroup[groupList.size()];
   378                 AlgorithmParameters aps = me.getValue();
   174         int i = 0;
   379                 try {
   175         for (NamedGroup namedGroup : groupList) {
   380                     ngParams = aps.getParameterSpec(DHParameterSpec.class);
   176             supportedNamedGroups[i++] = namedGroup;
   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;
   177         }
   460         }
   178     }
   461     }
   179 
   462 
   180     // check whether the group is supported by the underlying providers
   463     static class SupportedGroups {
   181     private static boolean isAvailableGroup(NamedGroup namedGroup) {
   464         // To switch off the supported_groups extension for DHE cipher suite.
   182         AlgorithmParameters params = null;
   465         static final boolean enableFFDHE =
   183         AlgorithmParameterSpec spec = null;
   466                 Utilities.getBooleanProperty("jsse.enableFFDHE", true);
   184         if ("EC".equals(namedGroup.algorithm)) {
   467 
   185             if (namedGroup.oid != null) {
   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) {
   186                 try {
   598                 try {
   187                     params = JsseJce.getAlgorithmParameters("EC");
   599                     params = JsseJce.getAlgorithmParameters("DiffieHellman");
   188                     spec = new ECGenParameterSpec(namedGroup.oid);
   600                     spec = getFFDHEDHParameterSpec(namedGroup);
   189                 } catch (Exception e) {
   601                 } catch (NoSuchAlgorithmException e) {
   190                     return false;
   602                     return false;
   191                 }
   603                 }
   192             }
   604             }   // Otherwise, unsupported.
   193         } else if ("DiffieHellman".equals(namedGroup.algorithm)) {
   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);
   194             try {
   675             try {
   195                 params = JsseJce.getAlgorithmParameters("DiffieHellman");
   676                 return params.getParameterSpec(ECGenParameterSpec.class);
   196                 spec = getFFDHEDHParameterSpec(namedGroup);
   677             } catch (InvalidParameterSpecException ipse) {
   197             } catch (Exception e) {
   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)) {
   198                 return false;
   733                 return false;
   199             }
   734             }
   200         }
   735 
   201 
   736             return constraints.permits(
   202         if ((params != null) && (spec != null)) {
   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;
   203             try {
   890             try {
   204                 params.init(spec);
   891                 spec = new SupportedGroupsSpec(buffer);
   205             } catch (Exception e) {
   892             } catch (IOException ioe) {
   206                 return false;
   893                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
   207             }
   894                 return;     // fatal() always throws, make the compiler happy.
   208 
   895             }
   209             // cache the parameters
   896 
   210             namedGroupParams.put(namedGroup, params);
   897             // Update the context.
   211 
   898             List<NamedGroup> knownNamedGroups = new LinkedList<>();
   212             return true;
   899             for (int id : spec.namedGroupsIds) {
   213         }
   900                 NamedGroup ng = NamedGroup.valueOf(id);
   214 
   901                 if (ng != null) {
   215         return false;
   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         }
   216     }
   911     }
   217 
   912 
   218     private static DHParameterSpec getFFDHEDHParameterSpec(
   913     /**
   219             NamedGroup namedGroup) {
   914      * Network data producer of a "supported_groups" extension in
   220         DHParameterSpec spec = null;
   915      * the EncryptedExtensions handshake message.
   221         switch (namedGroup) {
   916      */
   222             case FFDHE_2048:
   917     private static final class EESupportedGroupsProducer
   223                 spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
   918             extends SupportedGroups implements HandshakeProducer {
   224                 break;
   919 
   225             case FFDHE_3072:
   920         // Prevent instantiation of this class.
   226                 spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
   921         private EESupportedGroupsProducer() {
   227                 break;
   922             // blank
   228             case FFDHE_4096:
   923         }
   229                 spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
   924 
   230                 break;
   925         @Override
   231             case FFDHE_6144:
   926         public byte[] produce(ConnectionContext context,
   232                 spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
   927                 HandshakeMessage message) throws IOException {
   233                 break;
   928             // The producing happens in server side only.
   234             case FFDHE_8192:
   929             ServerHandshakeContext shc = (ServerHandshakeContext)context;
   235                 spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
   930 
   236         }
   931             // Is it a supported and enabled extension?
   237 
   932             if (!shc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
   238         return spec;
   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         }
   239     }
   988     }
   240 
   989 
   241     private static DHParameterSpec getPredefinedDHParameterSpec(
   990     private static final
   242             NamedGroup namedGroup) {
   991             class EESupportedGroupsConsumer implements ExtensionConsumer {
   243         DHParameterSpec spec = null;
   992         // Prevent instantiation of this class.
   244         switch (namedGroup) {
   993         private EESupportedGroupsConsumer() {
   245             case FFDHE_2048:
   994             // blank
   246                 spec = PredefinedDHParameterSpecs.definedParams.get(2048);
   995         }
   247                 break;
   996 
   248             case FFDHE_3072:
   997         @Override
   249                 spec = PredefinedDHParameterSpecs.definedParams.get(3072);
   998         public void consume(ConnectionContext context,
   250                 break;
   999             HandshakeMessage message, ByteBuffer buffer) throws IOException {
   251             case FFDHE_4096:
  1000             // The comsuming happens in client side only.
   252                 spec = PredefinedDHParameterSpecs.definedParams.get(4096);
  1001             ClientHandshakeContext chc = (ClientHandshakeContext)context;
   253                 break;
  1002 
   254             case FFDHE_6144:
  1003             // Is it a supported and enabled extension?
   255                 spec = PredefinedDHParameterSpecs.definedParams.get(6144);
  1004             if (!chc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
   256                 break;
  1005                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   257             case FFDHE_8192:
  1006                     SSLLogger.fine(
   258                 spec = PredefinedDHParameterSpecs.definedParams.get(8192);
  1007                         "Ignore unavailable supported_groups extension");
   259         }
  1008                 }
   260 
  1009                 return;     // ignore the extension
   261         return spec;
  1010             }
   262     }
  1011 
   263 
  1012             // Parse the extension.
   264     private SupportedGroupsExtension(int[] requestedNamedGroupIds) {
  1013             SupportedGroupsSpec spec;
   265         super(ExtensionType.EXT_SUPPORTED_GROUPS);
  1014             try {
   266 
  1015                 spec = new SupportedGroupsSpec(buffer);
   267         this.requestedNamedGroupIds = requestedNamedGroupIds;
  1016             } catch (IOException ioe) {
   268     }
  1017                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
   269 
  1018                 return;     // fatal() always throws, make the compiler happy.
   270     SupportedGroupsExtension(HandshakeInStream s, int len) throws IOException {
  1019             }
   271         super(ExtensionType.EXT_SUPPORTED_GROUPS);
  1020 
   272 
  1021             // Update the context.
   273         int k = s.getInt16();
  1022             List<NamedGroup> knownNamedGroups =
   274         if (((len & 1) != 0) || (k == 0) || (k + 2 != len)) {
  1023                     new ArrayList<>(spec.namedGroupsIds.length);
   275             throw new SSLProtocolException("Invalid " + type + " extension");
  1024             for (int id : spec.namedGroupsIds) {
   276         }
  1025                 NamedGroup ng = NamedGroup.valueOf(id);
   277 
  1026                 if (ng != null) {
   278         // Note: unknown named group will be ignored later.
  1027                     knownNamedGroups.add(ng);
   279         requestedNamedGroupIds = new int[k >> 1];
  1028                 }
   280         for (int i = 0; i < requestedNamedGroupIds.length; i++) {
  1029             }
   281             requestedNamedGroupIds[i] = s.getInt16();
  1030 
   282         }
  1031             chc.conContext.serverRequestedNamedGroups = knownNamedGroups;
   283     }
  1032             chc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
   284 
  1033 
   285     // Get a local preferred supported ECDHE group permitted by the constraints.
  1034             // No impact on session resumption.
   286     static NamedGroup getPreferredECGroup(AlgorithmConstraints constraints) {
       
   287         for (NamedGroup namedGroup : supportedNamedGroups) {
       
   288             if ((namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
       
   289                 constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   290                     namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
       
   291 
       
   292                 return namedGroup;
       
   293             }
       
   294         }
       
   295 
       
   296         return null;
       
   297     }
       
   298 
       
   299     // Is there any supported group permitted by the constraints?
       
   300     static boolean isActivatable(
       
   301             AlgorithmConstraints constraints, NamedGroupType type) {
       
   302 
       
   303         boolean hasFFDHEGroups = false;
       
   304         for (NamedGroup namedGroup : supportedNamedGroups) {
       
   305             if (namedGroup.type == type) {
       
   306                 if (constraints.permits(
       
   307                         EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   308                         namedGroup.algorithm,
       
   309                         namedGroupParams.get(namedGroup))) {
       
   310 
       
   311                     return true;
       
   312                 }
       
   313 
       
   314                 if (!hasFFDHEGroups &&
       
   315                         (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
       
   316 
       
   317                     hasFFDHEGroups = true;
       
   318                 }
       
   319             }
       
   320         }
       
   321 
       
   322         // For compatibility, if no FFDHE groups are defined, the non-FFDHE
       
   323         // compatible mode (using DHE cipher suite without FFDHE extension)
       
   324         // is allowed.
       
   325         //
       
   326         // Note that the constraints checking on DHE parameters will be
       
   327         // performed during key exchanging in a handshake.
       
   328         if (!hasFFDHEGroups && (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
       
   329             return true;
       
   330         }
       
   331 
       
   332         return false;
       
   333     }
       
   334 
       
   335     // Create the default supported groups extension.
       
   336     static SupportedGroupsExtension createExtension(
       
   337             AlgorithmConstraints constraints,
       
   338             CipherSuiteList cipherSuites, boolean enableFFDHE) {
       
   339 
       
   340         ArrayList<Integer> groupList =
       
   341                 new ArrayList<>(supportedNamedGroups.length);
       
   342         for (NamedGroup namedGroup : supportedNamedGroups) {
       
   343             if ((!enableFFDHE) &&
       
   344                 (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
       
   345                 continue;
       
   346             }
       
   347 
       
   348             if (cipherSuites.contains(namedGroup.type) &&
       
   349                 constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   350                     namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
       
   351 
       
   352                 groupList.add(namedGroup.id);
       
   353             }
       
   354         }
       
   355 
       
   356         if (!groupList.isEmpty()) {
       
   357             int[] ids = new int[groupList.size()];
       
   358             int i = 0;
       
   359             for (Integer id : groupList) {
       
   360                 ids[i++] = id;
       
   361             }
       
   362 
       
   363             return new SupportedGroupsExtension(ids);
       
   364         }
       
   365 
       
   366         return null;
       
   367     }
       
   368 
       
   369     // get the preferred activated named group
       
   370     NamedGroup getPreferredGroup(
       
   371             AlgorithmConstraints constraints, NamedGroupType type) {
       
   372 
       
   373         for (int groupId : requestedNamedGroupIds) {
       
   374             NamedGroup namedGroup = NamedGroup.valueOf(groupId);
       
   375             if ((namedGroup != null) && (namedGroup.type == type) &&
       
   376                 SupportedGroupsExtension.supports(namedGroup) &&
       
   377                 constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   378                     namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
       
   379 
       
   380                 return namedGroup;
       
   381             }
       
   382         }
       
   383 
       
   384         return null;
       
   385     }
       
   386 
       
   387     boolean hasFFDHEGroup() {
       
   388         for (int groupId : requestedNamedGroupIds) {
       
   389             /*
       
   390              * [RFC 7919] Codepoints in the "Supported Groups Registry"
       
   391              * with a high byte of 0x01 (that is, between 256 and 511,
       
   392              * inclusive) are set aside for FFDHE groups.
       
   393              */
       
   394             if ((groupId >= 256) && (groupId <= 511)) {
       
   395                 return true;
       
   396             }
       
   397         }
       
   398 
       
   399         return false;
       
   400     }
       
   401 
       
   402     boolean contains(int index) {
       
   403         for (int groupId : requestedNamedGroupIds) {
       
   404             if (index == groupId) {
       
   405                 return true;
       
   406             }
       
   407         }
       
   408         return false;
       
   409     }
       
   410 
       
   411     @Override
       
   412     int length() {
       
   413         return 6 + (requestedNamedGroupIds.length << 1);
       
   414     }
       
   415 
       
   416     @Override
       
   417     void send(HandshakeOutStream s) throws IOException {
       
   418         s.putInt16(type.id);
       
   419         int k = requestedNamedGroupIds.length << 1;
       
   420         s.putInt16(k + 2);
       
   421         s.putInt16(k);
       
   422         for (int groupId : requestedNamedGroupIds) {
       
   423             s.putInt16(groupId);
       
   424         }
       
   425     }
       
   426 
       
   427     @Override
       
   428     public String toString() {
       
   429         StringBuilder sb = new StringBuilder();
       
   430         sb.append("Extension " + type + ", group names: {");
       
   431         boolean first = true;
       
   432         for (int groupId : requestedNamedGroupIds) {
       
   433             if (first) {
       
   434                 first = false;
       
   435             } else {
       
   436                 sb.append(", ");
       
   437             }
       
   438             // first check if it is a known named group, then try other cases.
       
   439             NamedGroup namedGroup = NamedGroup.valueOf(groupId);
       
   440             if (namedGroup != null) {
       
   441                 sb.append(namedGroup.name);
       
   442             } else if (groupId == ARBITRARY_PRIME) {
       
   443                 sb.append("arbitrary_explicit_prime_curves");
       
   444             } else if (groupId == ARBITRARY_CHAR2) {
       
   445                 sb.append("arbitrary_explicit_char2_curves");
       
   446             } else {
       
   447                 sb.append("unknown named group " + groupId);
       
   448             }
       
   449         }
       
   450         sb.append("}");
       
   451         return sb.toString();
       
   452     }
       
   453 
       
   454     static boolean supports(NamedGroup namedGroup) {
       
   455         for (NamedGroup group : supportedNamedGroups) {
       
   456             if (namedGroup.id == group.id) {
       
   457                 return true;
       
   458             }
       
   459         }
       
   460 
       
   461         return false;
       
   462     }
       
   463 
       
   464     static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) {
       
   465         if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
       
   466             throw new RuntimeException("Not a named EC group: " + namedGroup);
       
   467         }
       
   468 
       
   469         AlgorithmParameters params = namedGroupParams.get(namedGroup);
       
   470         try {
       
   471             return params.getParameterSpec(ECGenParameterSpec.class);
       
   472         } catch (InvalidParameterSpecException ipse) {
       
   473             // should be unlikely
       
   474             return new ECGenParameterSpec(namedGroup.oid);
       
   475         }
       
   476     }
       
   477 
       
   478     static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) {
       
   479         if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) {
       
   480             throw new RuntimeException("Not a named DH group: " + namedGroup);
       
   481         }
       
   482 
       
   483         AlgorithmParameters params = namedGroupParams.get(namedGroup);
       
   484         try {
       
   485             return params.getParameterSpec(DHParameterSpec.class);
       
   486         } catch (InvalidParameterSpecException ipse) {
       
   487             // should be unlikely
       
   488             return getPredefinedDHParameterSpec(namedGroup);
       
   489         }
  1035         }
   490     }
  1036     }
   491 }
  1037 }