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 } |