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