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