jdk/src/java.base/share/classes/sun/security/ssl/EllipticCurvesExtension.java
changeset 45082 db3363bf1d2d
parent 45081 53876ff5a88a
parent 45078 8344afa5ea9e
child 45085 10f543db5dcb
equal deleted inserted replaced
45081:53876ff5a88a 45082:db3363bf1d2d
     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.ECParameterSpec;
       
    30 import java.security.spec.ECGenParameterSpec;
       
    31 import java.security.spec.InvalidParameterSpecException;
       
    32 import java.security.AlgorithmParameters;
       
    33 import java.security.AlgorithmConstraints;
       
    34 import java.security.CryptoPrimitive;
       
    35 import java.security.AccessController;
       
    36 import java.util.EnumSet;
       
    37 import java.util.HashMap;
       
    38 import java.util.Map;
       
    39 import java.util.ArrayList;
       
    40 import javax.net.ssl.SSLProtocolException;
       
    41 
       
    42 import sun.security.action.GetPropertyAction;
       
    43 
       
    44 final class EllipticCurvesExtension extends HelloExtension {
       
    45 
       
    46     /* Class and subclass dynamic debugging support */
       
    47     private static final Debug debug = Debug.getInstance("ssl");
       
    48 
       
    49     private static final int ARBITRARY_PRIME = 0xff01;
       
    50     private static final int ARBITRARY_CHAR2 = 0xff02;
       
    51 
       
    52     // speed up the searching
       
    53     private static final Map<String, Integer> oidToIdMap = new HashMap<>();
       
    54     private static final Map<Integer, String> idToOidMap = new HashMap<>();
       
    55 
       
    56     // speed up the parameters construction
       
    57     private static final Map<Integer,
       
    58                 AlgorithmParameters> idToParams = new HashMap<>();
       
    59 
       
    60     // the supported elliptic curves
       
    61     private static final int[] supportedCurveIds;
       
    62 
       
    63     // the curves of the extension
       
    64     private final int[] curveIds;
       
    65 
       
    66     // See sun.security.util.CurveDB for the OIDs
       
    67     private static enum NamedEllipticCurve {
       
    68         T163_K1(1,  "sect163k1",    "1.3.132.0.1",      true),  // NIST K-163
       
    69         T163_R1(2,  "sect163r1",    "1.3.132.0.2",      false),
       
    70         T163_R2(3,  "sect163r2",    "1.3.132.0.15",     true),  // NIST B-163
       
    71         T193_R1(4,  "sect193r1",    "1.3.132.0.24",     false),
       
    72         T193_R2(5,  "sect193r2",    "1.3.132.0.25",     false),
       
    73         T233_K1(6,  "sect233k1",    "1.3.132.0.26",     true),  // NIST K-233
       
    74         T233_R1(7,  "sect233r1",    "1.3.132.0.27",     true),  // NIST B-233
       
    75         T239_K1(8,  "sect239k1",    "1.3.132.0.3",      false),
       
    76         T283_K1(9,  "sect283k1",    "1.3.132.0.16",     true),  // NIST K-283
       
    77         T283_R1(10, "sect283r1",    "1.3.132.0.17",     true),  // NIST B-283
       
    78         T409_K1(11, "sect409k1",    "1.3.132.0.36",     true),  // NIST K-409
       
    79         T409_R1(12, "sect409r1",    "1.3.132.0.37",     true),  // NIST B-409
       
    80         T571_K1(13, "sect571k1",    "1.3.132.0.38",     true),  // NIST K-571
       
    81         T571_R1(14, "sect571r1",    "1.3.132.0.39",     true),  // NIST B-571
       
    82 
       
    83         P160_K1(15, "secp160k1",    "1.3.132.0.9",      false),
       
    84         P160_R1(16, "secp160r1",    "1.3.132.0.8",      false),
       
    85         P160_R2(17, "secp160r2",    "1.3.132.0.30",     false),
       
    86         P192_K1(18, "secp192k1",    "1.3.132.0.31",     false),
       
    87         P192_R1(19, "secp192r1",    "1.2.840.10045.3.1.1", true), // NIST P-192
       
    88         P224_K1(20, "secp224k1",    "1.3.132.0.32",     false),
       
    89         P224_R1(21, "secp224r1",    "1.3.132.0.33",     true),  // NIST P-224
       
    90         P256_K1(22, "secp256k1",    "1.3.132.0.10",     false),
       
    91         P256_R1(23, "secp256r1",    "1.2.840.10045.3.1.7", true), // NIST P-256
       
    92         P384_R1(24, "secp384r1",    "1.3.132.0.34",     true),  // NIST P-384
       
    93         P521_R1(25, "secp521r1",    "1.3.132.0.35",     true);  // NIST P-521
       
    94 
       
    95         int          id;
       
    96         String       name;
       
    97         String       oid;
       
    98         boolean      isFips;
       
    99 
       
   100         NamedEllipticCurve(int id, String name, String oid, boolean isFips) {
       
   101             this.id = id;
       
   102             this.name = name;
       
   103             this.oid = oid;
       
   104             this.isFips = isFips;
       
   105 
       
   106             if (oidToIdMap.put(oid, id) != null ||
       
   107                 idToOidMap.put(id, oid) != null) {
       
   108 
       
   109                 throw new RuntimeException(
       
   110                         "Duplicate named elliptic curve definition: " + name);
       
   111             }
       
   112         }
       
   113 
       
   114         static NamedEllipticCurve getCurve(String name, boolean requireFips) {
       
   115             for (NamedEllipticCurve curve : NamedEllipticCurve.values()) {
       
   116                 if (curve.name.equals(name) && (!requireFips || curve.isFips)) {
       
   117                     return curve;
       
   118                 }
       
   119             }
       
   120 
       
   121             return null;
       
   122         }
       
   123     }
       
   124 
       
   125     static {
       
   126         boolean requireFips = SunJSSE.isFIPS();
       
   127 
       
   128         // hack code to initialize NamedEllipticCurve
       
   129         NamedEllipticCurve nec =
       
   130                 NamedEllipticCurve.getCurve("secp256r1", false);
       
   131 
       
   132         // The value of the System Property defines a list of enabled named
       
   133         // curves in preference order, separated with comma.  For example:
       
   134         //
       
   135         //      jdk.tls.namedGroups="secp521r1, secp256r1, secp384r1"
       
   136         //
       
   137         // If the System Property is not defined or the value is empty, the
       
   138         // default curves and preferences will be used.
       
   139         String property = AccessController.doPrivileged(
       
   140                     new GetPropertyAction("jdk.tls.namedGroups"));
       
   141         if (property != null && property.length() != 0) {
       
   142             // remove double quote marks from beginning/end of the property
       
   143             if (property.length() > 1 && property.charAt(0) == '"' &&
       
   144                     property.charAt(property.length() - 1) == '"') {
       
   145                 property = property.substring(1, property.length() - 1);
       
   146             }
       
   147         }
       
   148 
       
   149         ArrayList<Integer> idList;
       
   150         if (property != null && property.length() != 0) {   // customized curves
       
   151             String[] curves = property.split(",");
       
   152             idList = new ArrayList<>(curves.length);
       
   153             for (String curve : curves) {
       
   154                 curve = curve.trim();
       
   155                 if (!curve.isEmpty()) {
       
   156                     NamedEllipticCurve namedCurve =
       
   157                             NamedEllipticCurve.getCurve(curve, requireFips);
       
   158                     if (namedCurve != null) {
       
   159                         if (isAvailableCurve(namedCurve.id)) {
       
   160                             idList.add(namedCurve.id);
       
   161                         }
       
   162                     }   // ignore unknown curves
       
   163                 }
       
   164             }
       
   165             if (idList.isEmpty() && JsseJce.isEcAvailable()) {
       
   166                 throw new IllegalArgumentException(
       
   167                     "System property jdk.tls.namedGroups(" + property + ") " +
       
   168                     "contains no supported elliptic curves");
       
   169             }
       
   170         } else {        // default curves
       
   171             int[] ids;
       
   172             if (requireFips) {
       
   173                 ids = new int[] {
       
   174                     // only NIST curves in FIPS mode
       
   175                     23, 24, 25, 9, 10, 11, 12, 13, 14,
       
   176                 };
       
   177             } else {
       
   178                 ids = new int[] {
       
   179                     // NIST curves first
       
   180                     23, 24, 25, 9, 10, 11, 12, 13, 14,
       
   181                     // non-NIST curves
       
   182                     22,
       
   183                 };
       
   184             }
       
   185 
       
   186             idList = new ArrayList<>(ids.length);
       
   187             for (int curveId : ids) {
       
   188                 if (isAvailableCurve(curveId)) {
       
   189                     idList.add(curveId);
       
   190                 }
       
   191             }
       
   192         }
       
   193 
       
   194         if (debug != null && idList.isEmpty()) {
       
   195             Debug.log(
       
   196                 "Initialized [jdk.tls.namedGroups|default] list contains " +
       
   197                 "no available elliptic curves. " +
       
   198                 (property != null ? "(" + property + ")" : "[Default]"));
       
   199         }
       
   200 
       
   201         supportedCurveIds = new int[idList.size()];
       
   202         int i = 0;
       
   203         for (Integer id : idList) {
       
   204             supportedCurveIds[i++] = id;
       
   205         }
       
   206     }
       
   207 
       
   208     // check whether the curve is supported by the underlying providers
       
   209     private static boolean isAvailableCurve(int curveId) {
       
   210         String oid = idToOidMap.get(curveId);
       
   211         if (oid != null) {
       
   212             AlgorithmParameters params = null;
       
   213             try {
       
   214                 params = JsseJce.getAlgorithmParameters("EC");
       
   215                 params.init(new ECGenParameterSpec(oid));
       
   216             } catch (Exception e) {
       
   217                 return false;
       
   218             }
       
   219 
       
   220             // cache the parameters
       
   221             idToParams.put(curveId, params);
       
   222 
       
   223             return true;
       
   224         }
       
   225 
       
   226         return false;
       
   227     }
       
   228 
       
   229     private EllipticCurvesExtension(int[] curveIds) {
       
   230         super(ExtensionType.EXT_ELLIPTIC_CURVES);
       
   231 
       
   232         this.curveIds = curveIds;
       
   233     }
       
   234 
       
   235     EllipticCurvesExtension(HandshakeInStream s, int len)
       
   236             throws IOException {
       
   237         super(ExtensionType.EXT_ELLIPTIC_CURVES);
       
   238 
       
   239         int k = s.getInt16();
       
   240         if (((len & 1) != 0) || (k + 2 != len)) {
       
   241             throw new SSLProtocolException("Invalid " + type + " extension");
       
   242         }
       
   243 
       
   244         // Note: unknown curves will be ignored later.
       
   245         curveIds = new int[k >> 1];
       
   246         for (int i = 0; i < curveIds.length; i++) {
       
   247             curveIds[i] = s.getInt16();
       
   248         }
       
   249     }
       
   250 
       
   251     // get the preferred active curve
       
   252     static int getActiveCurves(AlgorithmConstraints constraints) {
       
   253         return getPreferredCurve(supportedCurveIds, constraints);
       
   254     }
       
   255 
       
   256     static boolean hasActiveCurves(AlgorithmConstraints constraints) {
       
   257         return getActiveCurves(constraints) >= 0;
       
   258     }
       
   259 
       
   260     static EllipticCurvesExtension createExtension(
       
   261                 AlgorithmConstraints constraints) {
       
   262 
       
   263         ArrayList<Integer> idList = new ArrayList<>(supportedCurveIds.length);
       
   264         for (int curveId : supportedCurveIds) {
       
   265             if (constraints.permits(
       
   266                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   267                                 "EC", idToParams.get(curveId))) {
       
   268                 idList.add(curveId);
       
   269             }
       
   270         }
       
   271 
       
   272         if (!idList.isEmpty()) {
       
   273             int[] ids = new int[idList.size()];
       
   274             int i = 0;
       
   275             for (Integer id : idList) {
       
   276                 ids[i++] = id;
       
   277             }
       
   278 
       
   279             return new EllipticCurvesExtension(ids);
       
   280         }
       
   281 
       
   282         return null;
       
   283     }
       
   284 
       
   285     // get the preferred activated curve
       
   286     int getPreferredCurve(AlgorithmConstraints constraints) {
       
   287         return getPreferredCurve(curveIds, constraints);
       
   288     }
       
   289 
       
   290     // get a preferred activated curve
       
   291     private static int getPreferredCurve(int[] curves,
       
   292                 AlgorithmConstraints constraints) {
       
   293         for (int curveId : curves) {
       
   294             if (isSupported(curveId) && constraints.permits(
       
   295                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
       
   296                                 "EC", idToParams.get(curveId))) {
       
   297                 return curveId;
       
   298             }
       
   299         }
       
   300 
       
   301         return -1;
       
   302     }
       
   303 
       
   304     boolean contains(int index) {
       
   305         for (int curveId : curveIds) {
       
   306             if (index == curveId) {
       
   307                 return true;
       
   308             }
       
   309         }
       
   310         return false;
       
   311     }
       
   312 
       
   313     @Override
       
   314     int length() {
       
   315         return 6 + (curveIds.length << 1);
       
   316     }
       
   317 
       
   318     @Override
       
   319     void send(HandshakeOutStream s) throws IOException {
       
   320         s.putInt16(type.id);
       
   321         int k = curveIds.length << 1;
       
   322         s.putInt16(k + 2);
       
   323         s.putInt16(k);
       
   324         for (int curveId : curveIds) {
       
   325             s.putInt16(curveId);
       
   326         }
       
   327     }
       
   328 
       
   329     @Override
       
   330     public String toString() {
       
   331         StringBuilder sb = new StringBuilder();
       
   332         sb.append("Extension " + type + ", curve names: {");
       
   333         boolean first = true;
       
   334         for (int curveId : curveIds) {
       
   335             if (first) {
       
   336                 first = false;
       
   337             } else {
       
   338                 sb.append(", ");
       
   339             }
       
   340             // first check if it is a known named curve, then try other cases.
       
   341             String curveName = getCurveName(curveId);
       
   342             if (curveName != null) {
       
   343                 sb.append(curveName);
       
   344             } else if (curveId == ARBITRARY_PRIME) {
       
   345                 sb.append("arbitrary_explicit_prime_curves");
       
   346             } else if (curveId == ARBITRARY_CHAR2) {
       
   347                 sb.append("arbitrary_explicit_char2_curves");
       
   348             } else {
       
   349                 sb.append("unknown curve " + curveId);
       
   350             }
       
   351         }
       
   352         sb.append("}");
       
   353         return sb.toString();
       
   354     }
       
   355 
       
   356     // Test whether the given curve is supported.
       
   357     static boolean isSupported(int index) {
       
   358         for (int curveId : supportedCurveIds) {
       
   359             if (index == curveId) {
       
   360                 return true;
       
   361             }
       
   362         }
       
   363 
       
   364         return false;
       
   365     }
       
   366 
       
   367     static int getCurveIndex(ECParameterSpec params) {
       
   368         String oid = JsseJce.getNamedCurveOid(params);
       
   369         if (oid == null) {
       
   370             return -1;
       
   371         }
       
   372         Integer n = oidToIdMap.get(oid);
       
   373         return (n == null) ? -1 : n;
       
   374     }
       
   375 
       
   376     static String getCurveOid(int index) {
       
   377         return idToOidMap.get(index);
       
   378     }
       
   379 
       
   380     static ECGenParameterSpec getECGenParamSpec(int index) {
       
   381         AlgorithmParameters params = idToParams.get(index);
       
   382         try {
       
   383             return params.getParameterSpec(ECGenParameterSpec.class);
       
   384         } catch (InvalidParameterSpecException ipse) {
       
   385             // should be unlikely
       
   386             String curveOid = getCurveOid(index);
       
   387             return new ECGenParameterSpec(curveOid);
       
   388         }
       
   389     }
       
   390 
       
   391     private static String getCurveName(int index) {
       
   392         for (NamedEllipticCurve namedCurve : NamedEllipticCurve.values()) {
       
   393             if (namedCurve.id == index) {
       
   394                 return namedCurve.name;
       
   395             }
       
   396         }
       
   397 
       
   398         return null;
       
   399     }
       
   400 }