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