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