/* * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ssl; import java.io.IOException; import java.security.spec.ECParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.AlgorithmParameters; import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; import java.security.AccessController; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.ArrayList; import javax.net.ssl.SSLProtocolException; import sun.security.action.GetPropertyAction; final class EllipticCurvesExtension extends HelloExtension { private static final int ARBITRARY_PRIME = 0xff01; private static final int ARBITRARY_CHAR2 = 0xff02; // speed up the searching private static final Map<String, Integer> oidToIdMap = new HashMap<>(); private static final Map<Integer, String> idToOidMap = new HashMap<>(); // speed up the parameters construction private static final Map<Integer, AlgorithmParameters> idToParams = new HashMap<>(); // the supported elliptic curves private static final int[] supportedCurveIds; // the curves of the extension private final int[] curveIds; // See sun.security.util.CurveDB for the OIDs private static enum NamedEllipticCurve { T163_K1(1, "sect163k1", "1.3.132.0.1", true), // NIST K-163 T163_R1(2, "sect163r1", "1.3.132.0.2", false), T163_R2(3, "sect163r2", "1.3.132.0.15", true), // NIST B-163 T193_R1(4, "sect193r1", "1.3.132.0.24", false), T193_R2(5, "sect193r2", "1.3.132.0.25", false), T233_K1(6, "sect233k1", "1.3.132.0.26", true), // NIST K-233 T233_R1(7, "sect233r1", "1.3.132.0.27", true), // NIST B-233 T239_K1(8, "sect239k1", "1.3.132.0.3", false), T283_K1(9, "sect283k1", "1.3.132.0.16", true), // NIST K-283 T283_R1(10, "sect283r1", "1.3.132.0.17", true), // NIST B-283 T409_K1(11, "sect409k1", "1.3.132.0.36", true), // NIST K-409 T409_R1(12, "sect409r1", "1.3.132.0.37", true), // NIST B-409 T571_K1(13, "sect571k1", "1.3.132.0.38", true), // NIST K-571 T571_R1(14, "sect571r1", "1.3.132.0.39", true), // NIST B-571 P160_K1(15, "secp160k1", "1.3.132.0.9", false), P160_R1(16, "secp160r1", "1.3.132.0.8", false), P160_R2(17, "secp160r2", "1.3.132.0.30", false), P192_K1(18, "secp192k1", "1.3.132.0.31", false), P192_R1(19, "secp192r1", "1.2.840.10045.3.1.1", true), // NIST P-192 P224_K1(20, "secp224k1", "1.3.132.0.32", false), P224_R1(21, "secp224r1", "1.3.132.0.33", true), // NIST P-224 P256_K1(22, "secp256k1", "1.3.132.0.10", false), P256_R1(23, "secp256r1", "1.2.840.10045.3.1.7", true), // NIST P-256 P384_R1(24, "secp384r1", "1.3.132.0.34", true), // NIST P-384 P521_R1(25, "secp521r1", "1.3.132.0.35", true); // NIST P-521 int id; String name; String oid; boolean isFips; NamedEllipticCurve(int id, String name, String oid, boolean isFips) { this.id = id; this.name = name; this.oid = oid; this.isFips = isFips; if (oidToIdMap.put(oid, id) != null || idToOidMap.put(id, oid) != null) { throw new RuntimeException( "Duplicate named elliptic curve definition: " + name); } } static NamedEllipticCurve getCurve(String name, boolean requireFips) { for (NamedEllipticCurve curve : NamedEllipticCurve.values()) { if (curve.name.equals(name) && (!requireFips || curve.isFips)) { return curve; } } return null; } } static { boolean requireFips = SunJSSE.isFIPS(); // hack code to initialize NamedEllipticCurve NamedEllipticCurve nec = NamedEllipticCurve.getCurve("secp256r1", false); // The value of the System Property defines a list of enabled named // curves in preference order, separated with comma. For example: // // jdk.tls.namedGroups="secp521r1, secp256r1, secp384r1" // // If the System Property is not defined or the value is empty, the // default curves and preferences will be used. String property = AccessController.doPrivileged( new GetPropertyAction("jdk.tls.namedGroups")); if (property != null && property.length() != 0) { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && property.charAt(property.length() - 1) == '"') { property = property.substring(1, property.length() - 1); } } ArrayList<Integer> idList; if (property != null && property.length() != 0) { // customized curves String[] curves = property.split(","); idList = new ArrayList<>(curves.length); for (String curve : curves) { curve = curve.trim(); if (!curve.isEmpty()) { NamedEllipticCurve namedCurve = NamedEllipticCurve.getCurve(curve, requireFips); if (namedCurve != null) { if (isAvailableCurve(namedCurve.id)) { idList.add(namedCurve.id); } } // ignore unknown curves } } } else { // default curves int[] ids; if (requireFips) { ids = new int[] { // only NIST curves in FIPS mode 23, 24, 25, 9, 10, 11, 12, 13, 14, }; } else { ids = new int[] { // NIST curves first 23, 24, 25, 9, 10, 11, 12, 13, 14, // non-NIST curves 22, }; } idList = new ArrayList<>(ids.length); for (int curveId : ids) { if (isAvailableCurve(curveId)) { idList.add(curveId); } } } if (idList.isEmpty()) { throw new IllegalArgumentException( "System property jdk.tls.namedGroups(" + property + ") " + "contains no supported elliptic curves"); } else { supportedCurveIds = new int[idList.size()]; int i = 0; for (Integer id : idList) { supportedCurveIds[i++] = id; } } } // check whether the curve is supported by the underlying providers private static boolean isAvailableCurve(int curveId) { String oid = idToOidMap.get(curveId); if (oid != null) { AlgorithmParameters params = null; try { params = JsseJce.getAlgorithmParameters("EC"); params.init(new ECGenParameterSpec(oid)); } catch (Exception e) { return false; } // cache the parameters idToParams.put(curveId, params); return true; } return false; } private EllipticCurvesExtension(int[] curveIds) { super(ExtensionType.EXT_ELLIPTIC_CURVES); this.curveIds = curveIds; } EllipticCurvesExtension(HandshakeInStream s, int len) throws IOException { super(ExtensionType.EXT_ELLIPTIC_CURVES); int k = s.getInt16(); if (((len & 1) != 0) || (k + 2 != len)) { throw new SSLProtocolException("Invalid " + type + " extension"); } // Note: unknown curves will be ignored later. curveIds = new int[k >> 1]; for (int i = 0; i < curveIds.length; i++) { curveIds[i] = s.getInt16(); } } // get the preferred active curve static int getActiveCurves(AlgorithmConstraints constraints) { return getPreferredCurve(supportedCurveIds, constraints); } static boolean hasActiveCurves(AlgorithmConstraints constraints) { return getActiveCurves(constraints) >= 0; } static EllipticCurvesExtension createExtension( AlgorithmConstraints constraints) { ArrayList<Integer> idList = new ArrayList<>(supportedCurveIds.length); for (int curveId : supportedCurveIds) { if (constraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), "EC", idToParams.get(curveId))) { idList.add(curveId); } } if (!idList.isEmpty()) { int[] ids = new int[idList.size()]; int i = 0; for (Integer id : idList) { ids[i++] = id; } return new EllipticCurvesExtension(ids); } return null; } // get the preferred activated curve int getPreferredCurve(AlgorithmConstraints constraints) { return getPreferredCurve(curveIds, constraints); } // get a preferred activated curve private static int getPreferredCurve(int[] curves, AlgorithmConstraints constraints) { for (int curveId : curves) { if (isSupported(curveId) && constraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), "EC", idToParams.get(curveId))) { return curveId; } } return -1; } boolean contains(int index) { for (int curveId : curveIds) { if (index == curveId) { return true; } } return false; } @Override int length() { return 6 + (curveIds.length << 1); } @Override void send(HandshakeOutStream s) throws IOException { s.putInt16(type.id); int k = curveIds.length << 1; s.putInt16(k + 2); s.putInt16(k); for (int curveId : curveIds) { s.putInt16(curveId); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Extension " + type + ", curve names: {"); boolean first = true; for (int curveId : curveIds) { if (first) { first = false; } else { sb.append(", "); } // first check if it is a known named curve, then try other cases. String curveName = getCurveName(curveId); if (curveName != null) { sb.append(curveName); } else if (curveId == ARBITRARY_PRIME) { sb.append("arbitrary_explicit_prime_curves"); } else if (curveId == ARBITRARY_CHAR2) { sb.append("arbitrary_explicit_char2_curves"); } else { sb.append("unknown curve " + curveId); } } sb.append("}"); return sb.toString(); } // Test whether the given curve is supported. static boolean isSupported(int index) { for (int curveId : supportedCurveIds) { if (index == curveId) { return true; } } return false; } static int getCurveIndex(ECParameterSpec params) { String oid = JsseJce.getNamedCurveOid(params); if (oid == null) { return -1; } Integer n = oidToIdMap.get(oid); return (n == null) ? -1 : n; } static String getCurveOid(int index) { return idToOidMap.get(index); } static ECGenParameterSpec getECGenParamSpec(int index) { AlgorithmParameters params = idToParams.get(index); try { return params.getParameterSpec(ECGenParameterSpec.class); } catch (InvalidParameterSpecException ipse) { // should be unlikely String curveOid = getCurveOid(index); return new ECGenParameterSpec(curveOid); } } private static String getCurveName(int index) { for (NamedEllipticCurve namedCurve : NamedEllipticCurve.values()) { if (namedCurve.id == index) { return namedCurve.name; } } return null; } }