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 
  70         private SupportedGroupsSpec(int[] namedGroupsIds) {
  71             this.namedGroupsIds = namedGroupsIds;
  72         }
  73 
  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 
  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
 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
 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?
 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?
 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?
 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 
 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 
 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.
 349         private CHSupportedGroupsProducer() {
 350             // blank
 351         }
 352 
 353         @Override
 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.
 420         private CHSupportedGroupsConsumer() {
 421             // blank
 422         }
 423 
 424         @Override
 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
 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.
 500         private EESupportedGroupsProducer() {
 501             // blank
 502         }
 503 
 504         @Override
 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.
 570         private EESupportedGroupsConsumer() {
 571             // blank
 572         }
 573 
 574         @Override
 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 }