Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.conscrypt;
     18 
     19 import android.annotation.SuppressLint;
     20 import android.annotation.TargetApi;
     21 import android.os.Build;
     22 import android.util.Log;
     23 import dalvik.system.BlockGuard;
     24 import dalvik.system.CloseGuard;
     25 import java.io.FileDescriptor;
     26 import java.io.IOException;
     27 import java.lang.reflect.Constructor;
     28 import java.lang.reflect.Field;
     29 import java.lang.reflect.InvocationTargetException;
     30 import java.lang.reflect.Method;
     31 import java.net.InetAddress;
     32 import java.net.InetSocketAddress;
     33 import java.net.Socket;
     34 import java.net.SocketException;
     35 import java.net.SocketImpl;
     36 import java.security.AlgorithmParameters;
     37 import java.security.KeyStore;
     38 import java.security.KeyStoreException;
     39 import java.security.NoSuchAlgorithmException;
     40 import java.security.PrivateKey;
     41 import java.security.Security;
     42 import java.security.cert.CertificateException;
     43 import java.security.cert.X509Certificate;
     44 import java.security.spec.AlgorithmParameterSpec;
     45 import java.security.spec.ECParameterSpec;
     46 import java.security.spec.InvalidParameterSpecException;
     47 import java.util.Arrays;
     48 import java.util.Collections;
     49 import java.util.List;
     50 import javax.net.ssl.SNIHostName;
     51 import javax.net.ssl.SNIServerName;
     52 import javax.net.ssl.SSLEngine;
     53 import javax.net.ssl.SSLParameters;
     54 import javax.net.ssl.SSLSession;
     55 import javax.net.ssl.SSLSocketFactory;
     56 import javax.net.ssl.StandardConstants;
     57 import javax.net.ssl.X509TrustManager;
     58 import org.conscrypt.ct.CTLogStore;
     59 import org.conscrypt.ct.CTPolicy;
     60 
     61 /**
     62  * Platform-specific methods for unbundled Android.
     63  */
     64 final class Platform {
     65     private static final String TAG = "Conscrypt";
     66 
     67     private static Method m_getCurveName;
     68     static {
     69         try {
     70             m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName");
     71             m_getCurveName.setAccessible(true);
     72         } catch (Exception ignored) {
     73         }
     74     }
     75 
     76     private Platform() {}
     77 
     78     public static void setup() {}
     79 
     80     /**
     81      * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
     82      * if the default constructor is used.
     83      */
     84     public static String getDefaultProviderName() {
     85         return "Conscrypt";
     86     }
     87 
     88     static boolean provideTrustManagerByDefault() {
     89         return false;
     90     }
     91 
     92     public static FileDescriptor getFileDescriptor(Socket s) {
     93         try {
     94             Field f_impl = Socket.class.getDeclaredField("impl");
     95             f_impl.setAccessible(true);
     96             Object socketImpl = f_impl.get(s);
     97             Field f_fd = SocketImpl.class.getDeclaredField("fd");
     98             f_fd.setAccessible(true);
     99             return (FileDescriptor) f_fd.get(socketImpl);
    100         } catch (Exception e) {
    101             throw new RuntimeException("Can't get FileDescriptor from socket", e);
    102         }
    103     }
    104 
    105     public static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
    106         return getFileDescriptor(socket);
    107     }
    108 
    109     public static String getCurveName(ECParameterSpec spec) {
    110         if (m_getCurveName == null) {
    111             return null;
    112         }
    113         try {
    114             return (String) m_getCurveName.invoke(spec);
    115         } catch (Exception e) {
    116             return null;
    117         }
    118     }
    119 
    120     public static void setCurveName(ECParameterSpec spec, String curveName) {
    121         try {
    122             Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class);
    123             setCurveName.invoke(spec, curveName);
    124         } catch (Exception ignored) {
    125         }
    126     }
    127 
    128     /**
    129      * Call Os.setsockoptTimeval via reflection.
    130      */
    131     public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
    132         try {
    133             FileDescriptor fd = getFileDescriptor(s);
    134             if (fd == null || !fd.valid()) {
    135                 // Mirror the behavior of platform sockets when calling methods with bad fds
    136                 throw new SocketException("Socket closed");
    137             }
    138             Class<?> c_structTimeval =
    139                     getClass("android.system.StructTimeval", "libcore.io.StructTimeval");
    140             if (c_structTimeval == null) {
    141                 Log.w(TAG, "StructTimeval == null; not setting socket write timeout");
    142                 return;
    143             }
    144 
    145             Method m_fromMillis = c_structTimeval.getDeclaredMethod("fromMillis", long.class);
    146             if (m_fromMillis == null) {
    147                 Log.w(TAG, "fromMillis == null; not setting socket write timeout");
    148                 return;
    149             }
    150 
    151             Object timeval = m_fromMillis.invoke(null, timeoutMillis);
    152 
    153             Class<?> c_Libcore = Class.forName("libcore.io.Libcore");
    154             if (c_Libcore == null) {
    155                 Log.w(TAG, "Libcore == null; not setting socket write timeout");
    156                 return;
    157             }
    158 
    159             Field f_os = c_Libcore.getField("os");
    160             if (f_os == null) {
    161                 Log.w(TAG, "os == null; not setting socket write timeout");
    162                 return;
    163             }
    164 
    165             Object instance_os = f_os.get(null);
    166             if (instance_os == null) {
    167                 Log.w(TAG, "instance_os == null; not setting socket write timeout");
    168                 return;
    169             }
    170 
    171             Class<?> c_osConstants =
    172                     getClass("android.system.OsConstants", "libcore.io.OsConstants");
    173             if (c_osConstants == null) {
    174                 Log.w(TAG, "OsConstants == null; not setting socket write timeout");
    175                 return;
    176             }
    177 
    178             Field f_SOL_SOCKET = c_osConstants.getField("SOL_SOCKET");
    179             if (f_SOL_SOCKET == null) {
    180                 Log.w(TAG, "SOL_SOCKET == null; not setting socket write timeout");
    181                 return;
    182             }
    183 
    184             Field f_SO_SNDTIMEO = c_osConstants.getField("SO_SNDTIMEO");
    185             if (f_SO_SNDTIMEO == null) {
    186                 Log.w(TAG, "SO_SNDTIMEO == null; not setting socket write timeout");
    187                 return;
    188             }
    189 
    190             Method m_setsockoptTimeval = instance_os.getClass().getMethod("setsockoptTimeval",
    191                     FileDescriptor.class, int.class, int.class, c_structTimeval);
    192             if (m_setsockoptTimeval == null) {
    193                 Log.w(TAG, "setsockoptTimeval == null; not setting socket write timeout");
    194                 return;
    195             }
    196 
    197             m_setsockoptTimeval.invoke(instance_os, fd, f_SOL_SOCKET.get(null),
    198                     f_SO_SNDTIMEO.get(null), timeval);
    199         } catch (Exception e) {
    200             // We don't want to spam the logcat since this isn't a fatal error, but we want to know
    201             // why this might be happening.
    202             logStackTraceSnippet("Could not set socket write timeout: " + e, e);
    203             Throwable cause = e.getCause();
    204             while (cause != null) {
    205                 logStackTraceSnippet("Caused by: " + cause, cause);
    206                 cause = cause.getCause();
    207             }
    208         }
    209     }
    210 
    211     /**
    212      * Logs an abbreviated stacktrace (summary and a couple of StackTraceElements).
    213      */
    214     private static void logStackTraceSnippet(String summary, Throwable throwable) {
    215         Log.w(TAG, summary);
    216         StackTraceElement[] elements = throwable.getStackTrace();
    217         for (int i = 0; i < 2 && i < elements.length; i++) {
    218             Log.w(TAG, "\tat " + elements[i].toString());
    219         }
    220     }
    221 
    222     private static void setSSLParametersOnImpl(SSLParameters params, SSLParametersImpl impl)
    223             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    224         Method m_getEndpointIdentificationAlgorithm =
    225                 params.getClass().getMethod("getEndpointIdentificationAlgorithm");
    226         impl.setEndpointIdentificationAlgorithm(
    227                 (String) m_getEndpointIdentificationAlgorithm.invoke(params));
    228 
    229         Method m_getUseCipherSuitesOrder = params.getClass().getMethod("getUseCipherSuitesOrder");
    230         impl.setUseCipherSuitesOrder((boolean) m_getUseCipherSuitesOrder.invoke(params));
    231     }
    232 
    233     public static void setSSLParameters(
    234             SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
    235         try {
    236             setSSLParametersOnImpl(params, impl);
    237 
    238             if (Build.VERSION.SDK_INT >= 24) {
    239                 String sniHostname = getSniHostnameFromParams(params);
    240                 if (sniHostname != null) {
    241                     socket.setHostname(sniHostname);
    242                 }
    243             }
    244         } catch (NoSuchMethodException ignored) {
    245         } catch (IllegalAccessException ignored) {
    246         } catch (InvocationTargetException e) {
    247             throw new RuntimeException(e.getCause());
    248         }
    249     }
    250 
    251     public static void setSSLParameters(
    252             SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
    253         try {
    254             setSSLParametersOnImpl(params, impl);
    255 
    256             if (Build.VERSION.SDK_INT >= 24) {
    257                 String sniHostname = getSniHostnameFromParams(params);
    258                 if (sniHostname != null) {
    259                     engine.setHostname(sniHostname);
    260                 }
    261             }
    262         } catch (NoSuchMethodException ignored) {
    263         } catch (IllegalAccessException ignored) {
    264         } catch (InvocationTargetException e) {
    265             throw new RuntimeException(e.getCause());
    266         }
    267     }
    268 
    269     @TargetApi(24)
    270     private static String getSniHostnameFromParams(SSLParameters params)
    271             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    272         Method m_getServerNames = params.getClass().getMethod("getServerNames");
    273         @SuppressWarnings("unchecked")
    274         List<SNIServerName> serverNames = (List<SNIServerName>) m_getServerNames.invoke(params);
    275         if (serverNames != null) {
    276             for (SNIServerName serverName : serverNames) {
    277                 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
    278                     return ((SNIHostName) serverName).getAsciiName();
    279                 }
    280             }
    281         }
    282 
    283         return null;
    284     }
    285 
    286     private static void getSSLParametersFromImpl(SSLParameters params, SSLParametersImpl impl)
    287             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    288         Method m_setEndpointIdentificationAlgorithm =
    289                 params.getClass().getMethod("setEndpointIdentificationAlgorithm", String.class);
    290         m_setEndpointIdentificationAlgorithm.invoke(
    291                 params, impl.getEndpointIdentificationAlgorithm());
    292 
    293         Method m_setUseCipherSuitesOrder =
    294                 params.getClass().getMethod("setUseCipherSuitesOrder", boolean.class);
    295         m_setUseCipherSuitesOrder.invoke(params, impl.getUseCipherSuitesOrder());
    296     }
    297 
    298     public static void getSSLParameters(
    299             SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
    300         try {
    301             getSSLParametersFromImpl(params, impl);
    302 
    303             if (Build.VERSION.SDK_INT >= 24) {
    304                 setParametersSniHostname(params, impl, socket);
    305             }
    306         } catch (NoSuchMethodException ignored) {
    307         } catch (IllegalAccessException ignored) {
    308         } catch (InvocationTargetException e) {
    309             throw new RuntimeException(e.getCause());
    310         }
    311     }
    312 
    313     @TargetApi(24)
    314     private static void setParametersSniHostname(
    315             SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket)
    316             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    317         if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
    318             Method m_setServerNames = params.getClass().getMethod("setServerNames", List.class);
    319             m_setServerNames.invoke(params,
    320                     Collections.<SNIServerName>singletonList(
    321                             new SNIHostName(socket.getHostname())));
    322         }
    323     }
    324 
    325     public static void getSSLParameters(
    326             SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
    327         try {
    328             getSSLParametersFromImpl(params, impl);
    329 
    330             if (Build.VERSION.SDK_INT >= 24) {
    331                 setParametersSniHostname(params, impl, engine);
    332             }
    333         } catch (NoSuchMethodException ignored) {
    334         } catch (IllegalAccessException ignored) {
    335         } catch (InvocationTargetException e) {
    336             throw new RuntimeException(e.getCause());
    337         }
    338     }
    339 
    340     @TargetApi(24)
    341     private static void setParametersSniHostname(
    342             SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine)
    343             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    344         if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
    345             Method m_setServerNames = params.getClass().getMethod("setServerNames", List.class);
    346             m_setServerNames.invoke(params,
    347                     Collections.<SNIServerName>singletonList(
    348                             new SNIHostName(engine.getHostname())));
    349         }
    350     }
    351 
    352     /**
    353      * Tries to return a Class reference of one of the supplied class names.
    354      */
    355     private static Class<?> getClass(String... klasses) {
    356         for (String klass : klasses) {
    357             try {
    358                 return Class.forName(klass);
    359             } catch (Exception ignored) {
    360             }
    361         }
    362         return null;
    363     }
    364 
    365     public static void setEndpointIdentificationAlgorithm(
    366             SSLParameters params, String endpointIdentificationAlgorithm) {
    367         // TODO: implement this for unbundled
    368     }
    369 
    370     public static String getEndpointIdentificationAlgorithm(SSLParameters params) {
    371         // TODO: implement this for unbundled
    372         return null;
    373     }
    374 
    375     /**
    376      * Helper function to unify calls to the different names used for each function taking a
    377      * Socket, SSLEngine, or String (legacy Android).
    378      */
    379     private static boolean checkTrusted(String methodName, X509TrustManager tm,
    380             X509Certificate[] chain, String authType, Class<?> argumentClass,
    381             Object argumentInstance) throws CertificateException {
    382         // Use duck-typing to try and call the hostname-aware method if available.
    383         try {
    384             Method method = tm.getClass().getMethod(
    385                     methodName, X509Certificate[].class, String.class, argumentClass);
    386             method.invoke(tm, chain, authType, argumentInstance);
    387             return true;
    388         } catch (NoSuchMethodException ignored) {
    389         } catch (IllegalAccessException ignored) {
    390         } catch (InvocationTargetException e) {
    391             if (e.getCause() instanceof CertificateException) {
    392                 throw(CertificateException) e.getCause();
    393             }
    394             throw new RuntimeException(e.getCause());
    395         }
    396         return false;
    397     }
    398 
    399     @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
    400     public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
    401             String authType, AbstractConscryptSocket socket) throws CertificateException {
    402         if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
    403                 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
    404                            socket.getHandshakeSession().getPeerHost())) {
    405             tm.checkClientTrusted(chain, authType);
    406         }
    407     }
    408 
    409     @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
    410     public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
    411             String authType, AbstractConscryptSocket socket) throws CertificateException {
    412         if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
    413                 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
    414                            socket.getHandshakeSession().getPeerHost())) {
    415             tm.checkServerTrusted(chain, authType);
    416         }
    417     }
    418 
    419     @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
    420     public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
    421             String authType, ConscryptEngine engine) throws CertificateException {
    422         if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
    423                 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
    424                            engine.getHandshakeSession().getPeerHost())) {
    425             tm.checkClientTrusted(chain, authType);
    426         }
    427     }
    428 
    429     @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
    430     public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
    431             String authType, ConscryptEngine engine) throws CertificateException {
    432         if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
    433                 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
    434                            engine.getHandshakeSession().getPeerHost())) {
    435             tm.checkServerTrusted(chain, authType);
    436         }
    437     }
    438 
    439     /**
    440      * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
    441      * builds since we didn't backport, so return null. This code is from
    442      * Chromium's net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java
    443      */
    444     @SuppressWarnings("LiteralClassName")
    445     public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) {
    446         // This fixup only applies to pre-JB-MR1
    447         if (Build.VERSION.SDK_INT >= 17) {
    448             return null;
    449         }
    450 
    451         // First, check that this is a proper instance of OpenSSLRSAPrivateKey
    452         // or one of its sub-classes.
    453         Class<?> superClass;
    454         try {
    455             superClass =
    456                     Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
    457         } catch (Exception e) {
    458             // This may happen if the target device has a completely different
    459             // implementation of the java.security APIs, compared to vanilla
    460             // Android. Highly unlikely, but still possible.
    461             Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
    462             return null;
    463         }
    464         if (!superClass.isInstance(javaKey)) {
    465             // This may happen if the PrivateKey was not created by the
    466             // Conscrypt provider, which should be the default. That could happen if an
    467             // OEM decided to implement a different default provider. Also highly unlikely.
    468             Log.e(TAG,
    469                     "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:"
    470                             + javaKey.getClass().getCanonicalName());
    471             return null;
    472         }
    473 
    474         try {
    475             // Use reflection to invoke the 'getOpenSSLKey()' method on
    476             // the private key. This returns another Java object that wraps
    477             // a native EVP_PKEY. Note that the method is final, so calling
    478             // the superclass implementation is ok.
    479             Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
    480             getKey.setAccessible(true);
    481             Object opensslKey = null;
    482             try {
    483                 opensslKey = getKey.invoke(javaKey);
    484             } finally {
    485                 getKey.setAccessible(false);
    486             }
    487             if (opensslKey == null) {
    488                 // Bail when detecting OEM "enhancement".
    489                 Log.e(TAG, "Could not getOpenSSLKey on instance: " + javaKey.toString());
    490                 return null;
    491             }
    492 
    493             // Use reflection to invoke the 'getPkeyContext' method on the
    494             // result of the getOpenSSLKey(). This is an 32-bit integer
    495             // which is the address of an EVP_PKEY object. Note that this
    496             // method these days returns a 64-bit long, but since this code
    497             // path is used for older Android versions, it may still return
    498             // a 32-bit int here. To be on the safe side, we cast the return
    499             // value via Number rather than directly to Integer or Long.
    500             Method getPkeyContext;
    501             try {
    502                 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
    503             } catch (Exception e) {
    504                 // Bail here too, something really not working as expected.
    505                 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
    506                 return null;
    507             }
    508             getPkeyContext.setAccessible(true);
    509             long evp_pkey = 0;
    510             try {
    511                 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
    512             } finally {
    513                 getPkeyContext.setAccessible(false);
    514             }
    515             if (evp_pkey == 0) {
    516                 // The PrivateKey is probably rotten for some reason.
    517                 Log.e(TAG, "getPkeyContext() returned null");
    518                 return null;
    519             }
    520             return new OpenSSLKey(evp_pkey);
    521         } catch (Exception e) {
    522             Log.e(TAG, "Error during conversion of privatekey instance: " + javaKey.toString(), e);
    523             return null;
    524         }
    525     }
    526 
    527     /**
    528      * Logs to the system EventLog system.
    529      */
    530     @SuppressWarnings("LiteralClassName")
    531     public static void logEvent(String message) {
    532         try {
    533             Class<?> processClass = Class.forName("android.os.Process");
    534             Object processInstance = processClass.getDeclaredConstructor().newInstance();
    535             Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
    536             int uid = (Integer) myUidMethod.invoke(processInstance);
    537 
    538             Class<?> eventLogClass = Class.forName("android.util.EventLog");
    539             Object eventLogInstance = eventLogClass.getDeclaredConstructor().newInstance();
    540             Method writeEventMethod =
    541                     eventLogClass.getMethod("writeEvent", Integer.TYPE, Object[].class);
    542             writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
    543                     new Object[] {"conscrypt", uid, message});
    544         } catch (Exception e) {
    545             // Fail silently
    546         }
    547     }
    548 
    549     static SSLEngine wrapEngine(ConscryptEngine engine) {
    550         // For now, don't wrap on Android.
    551         return engine;
    552     }
    553 
    554     static SSLEngine unwrapEngine(SSLEngine engine) {
    555         // For now, don't wrap on Android.
    556         return engine;
    557     }
    558 
    559     static ConscryptEngineSocket createEngineSocket(SSLParametersImpl sslParameters)
    560             throws IOException {
    561         if (Build.VERSION.SDK_INT >= 24) {
    562             return new Java8EngineSocket(sslParameters);
    563         }
    564         return new ConscryptEngineSocket(sslParameters);
    565     }
    566 
    567     static ConscryptEngineSocket createEngineSocket(String hostname, int port,
    568             SSLParametersImpl sslParameters) throws IOException {
    569         if (Build.VERSION.SDK_INT >= 24) {
    570             return new Java8EngineSocket(hostname, port, sslParameters);
    571         }
    572         return new ConscryptEngineSocket(hostname, port, sslParameters);
    573     }
    574 
    575     static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
    576             SSLParametersImpl sslParameters) throws IOException {
    577         if (Build.VERSION.SDK_INT >= 24) {
    578             return new Java8EngineSocket(address, port, sslParameters);
    579         }
    580         return new ConscryptEngineSocket(address, port, sslParameters);
    581     }
    582 
    583     static ConscryptEngineSocket createEngineSocket(String hostname, int port,
    584             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    585             throws IOException {
    586         if (Build.VERSION.SDK_INT >= 24) {
    587             return new Java8EngineSocket(hostname, port, clientAddress, clientPort, sslParameters);
    588         }
    589         return new ConscryptEngineSocket(hostname, port, clientAddress, clientPort, sslParameters);
    590     }
    591 
    592     static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
    593             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    594             throws IOException {
    595         if (Build.VERSION.SDK_INT >= 24) {
    596             return new Java8EngineSocket(address, port, clientAddress, clientPort, sslParameters);
    597         }
    598         return new ConscryptEngineSocket(address, port, clientAddress, clientPort, sslParameters);
    599     }
    600 
    601     static ConscryptEngineSocket createEngineSocket(Socket socket, String hostname, int port,
    602             boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
    603         if (Build.VERSION.SDK_INT >= 24) {
    604             return new Java8EngineSocket(socket, hostname, port, autoClose, sslParameters);
    605         }
    606         return new ConscryptEngineSocket(socket, hostname, port, autoClose, sslParameters);
    607     }
    608 
    609     static ConscryptFileDescriptorSocket createFileDescriptorSocket(SSLParametersImpl sslParameters)
    610             throws IOException {
    611         if (Build.VERSION.SDK_INT >= 24) {
    612             return new Java8FileDescriptorSocket(sslParameters);
    613         }
    614         return new ConscryptFileDescriptorSocket(sslParameters);
    615     }
    616 
    617     static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
    618             SSLParametersImpl sslParameters) throws IOException {
    619         if (Build.VERSION.SDK_INT >= 24) {
    620             return new Java8FileDescriptorSocket(hostname, port, sslParameters);
    621         }
    622         return new ConscryptFileDescriptorSocket(hostname, port, sslParameters);
    623     }
    624 
    625     static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
    626             SSLParametersImpl sslParameters) throws IOException {
    627         if (Build.VERSION.SDK_INT >= 24) {
    628             return new Java8FileDescriptorSocket(address, port, sslParameters);
    629         }
    630         return new ConscryptFileDescriptorSocket(address, port, sslParameters);
    631     }
    632 
    633     static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
    634             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    635             throws IOException {
    636         if (Build.VERSION.SDK_INT >= 24) {
    637             return new Java8FileDescriptorSocket(
    638                     hostname, port, clientAddress, clientPort, sslParameters);
    639         }
    640         return new ConscryptFileDescriptorSocket(
    641                 hostname, port, clientAddress, clientPort, sslParameters);
    642     }
    643 
    644     static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
    645             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    646             throws IOException {
    647         if (Build.VERSION.SDK_INT >= 24) {
    648             return new Java8FileDescriptorSocket(
    649                     address, port, clientAddress, clientPort, sslParameters);
    650         }
    651         return new ConscryptFileDescriptorSocket(
    652                 address, port, clientAddress, clientPort, sslParameters);
    653     }
    654 
    655     static ConscryptFileDescriptorSocket createFileDescriptorSocket(Socket socket, String hostname,
    656             int port, boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
    657         if (Build.VERSION.SDK_INT >= 24) {
    658             return new Java8FileDescriptorSocket(socket, hostname, port, autoClose, sslParameters);
    659         }
    660         return new ConscryptFileDescriptorSocket(socket, hostname, port, autoClose, sslParameters);
    661     }
    662 
    663     /**
    664      * Wrap the SocketFactory with the platform wrapper if needed for compatability.
    665      */
    666     public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
    667         if (Build.VERSION.SDK_INT < 19) {
    668             return new PreKitKatPlatformOpenSSLSocketAdapterFactory(factory);
    669         } else if (Build.VERSION.SDK_INT < 22) {
    670             return new KitKatPlatformOpenSSLSocketAdapterFactory(factory);
    671         }
    672         return factory;
    673     }
    674 
    675     /**
    676      * Convert from platform's GCMParameterSpec to our internal version.
    677      */
    678     @SuppressWarnings("LiteralClassName")
    679     public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
    680         Class<?> gcmSpecClass;
    681         try {
    682             gcmSpecClass = Class.forName("javax.crypto.spec.GCMParameterSpec");
    683         } catch (ClassNotFoundException e) {
    684             gcmSpecClass = null;
    685         }
    686 
    687         if (gcmSpecClass != null && gcmSpecClass.isAssignableFrom(params.getClass())) {
    688             try {
    689                 int tLen;
    690                 byte[] iv;
    691 
    692                 Method getTLenMethod = gcmSpecClass.getMethod("getTLen");
    693                 Method getIVMethod = gcmSpecClass.getMethod("getIV");
    694                 tLen = (int) getTLenMethod.invoke(params);
    695                 iv = (byte[]) getIVMethod.invoke(params);
    696 
    697                 return new GCMParameters(tLen, iv);
    698             } catch (NoSuchMethodException e) {
    699                 throw new RuntimeException("GCMParameterSpec lacks expected methods", e);
    700             } catch (IllegalAccessException e) {
    701                 throw new RuntimeException("GCMParameterSpec lacks expected methods", e);
    702             } catch (InvocationTargetException e) {
    703                 throw new RuntimeException(
    704                         "Could not fetch GCM parameters", e.getTargetException());
    705             }
    706         }
    707         return null;
    708     }
    709 
    710     /**
    711      * Convert from an opaque AlgorithmParameters to the platform's GCMParameterSpec.
    712      */
    713     @SuppressWarnings("LiteralClassName")
    714     static AlgorithmParameterSpec fromGCMParameters(AlgorithmParameters params) {
    715         Class<?> gcmSpecClass;
    716         try {
    717             gcmSpecClass = Class.forName("javax.crypto.spec.GCMParameterSpec");
    718         } catch (ClassNotFoundException e) {
    719             gcmSpecClass = null;
    720         }
    721 
    722         if (gcmSpecClass != null) {
    723             try {
    724                 return params.getParameterSpec((Class) gcmSpecClass);
    725             } catch (InvalidParameterSpecException e) {
    726                 return null;
    727             }
    728         }
    729         return null;
    730     }
    731 
    732     /**
    733      * Creates a platform version of {@code GCMParameterSpec}.
    734      */
    735     @SuppressWarnings("LiteralClassName")
    736     public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
    737         Class<?> gcmSpecClass;
    738         try {
    739             gcmSpecClass = Class.forName("javax.crypto.spec.GCMParameterSpec");
    740         } catch (ClassNotFoundException e) {
    741             gcmSpecClass = null;
    742         }
    743 
    744         if (gcmSpecClass != null) {
    745             try {
    746                 Constructor<?> constructor = gcmSpecClass.getConstructor(int.class, byte[].class);
    747                 return (AlgorithmParameterSpec) constructor.newInstance(tagLenInBits, iv);
    748             } catch (NoSuchMethodException | InstantiationException | IllegalAccessException
    749                     | IllegalArgumentException e) {
    750                 e.printStackTrace();
    751             } catch (InvocationTargetException e) {
    752                 e.getCause().printStackTrace();
    753             }
    754         }
    755         return null;
    756     }
    757 
    758     /*
    759      * CloseGuard functions.
    760      */
    761 
    762     public static CloseGuard closeGuardGet() {
    763         if (Build.VERSION.SDK_INT < 14) {
    764             return null;
    765         }
    766 
    767         return CloseGuard.get();
    768     }
    769 
    770     public static void closeGuardOpen(Object guardObj, String message) {
    771         if (Build.VERSION.SDK_INT < 14) {
    772             return;
    773         }
    774 
    775         CloseGuard guard = (CloseGuard) guardObj;
    776         guard.open(message);
    777     }
    778 
    779     public static void closeGuardClose(Object guardObj) {
    780         if (Build.VERSION.SDK_INT < 14) {
    781             return;
    782         }
    783 
    784         CloseGuard guard = (CloseGuard) guardObj;
    785         guard.close();
    786     }
    787 
    788     public static void closeGuardWarnIfOpen(Object guardObj) {
    789         if (Build.VERSION.SDK_INT < 14) {
    790             return;
    791         }
    792 
    793         CloseGuard guard = (CloseGuard) guardObj;
    794         guard.warnIfOpen();
    795     }
    796 
    797     /*
    798      * BlockGuard functions.
    799      */
    800 
    801     public static void blockGuardOnNetwork() {
    802         BlockGuard.getThreadPolicy().onNetwork();
    803     }
    804 
    805     /**
    806      * OID to Algorithm Name mapping.
    807      */
    808     @SuppressWarnings("LiteralClassName")
    809     public static String oidToAlgorithmName(String oid) {
    810         // Old Harmony style
    811         try {
    812             Class<?> algNameMapperClass =
    813                     Class.forName("org.apache.harmony.security.utils.AlgNameMapper");
    814             Method map2AlgNameMethod =
    815                     algNameMapperClass.getDeclaredMethod("map2AlgName", String.class);
    816             map2AlgNameMethod.setAccessible(true);
    817             return (String) map2AlgNameMethod.invoke(null, oid);
    818         } catch (InvocationTargetException e) {
    819             Throwable cause = e.getCause();
    820             if (cause instanceof RuntimeException) {
    821                 throw(RuntimeException) cause;
    822             } else if (cause instanceof Error) {
    823                 throw(Error) cause;
    824             }
    825             throw new RuntimeException(e);
    826         } catch (Exception ignored) {
    827         }
    828 
    829         // Newer OpenJDK style
    830         try {
    831             Class<?> algorithmIdClass = Class.forName("sun.security.x509.AlgorithmId");
    832             Method getMethod = algorithmIdClass.getDeclaredMethod("get", String.class);
    833             getMethod.setAccessible(true);
    834             Method getNameMethod = algorithmIdClass.getDeclaredMethod("getName");
    835             getNameMethod.setAccessible(true);
    836 
    837             Object algIdObj = getMethod.invoke(null, oid);
    838             return (String) getNameMethod.invoke(algIdObj);
    839         } catch (InvocationTargetException e) {
    840             Throwable cause = e.getCause();
    841             if (cause instanceof RuntimeException) {
    842                 throw(RuntimeException) cause;
    843             } else if (cause instanceof Error) {
    844                 throw(Error) cause;
    845             }
    846             throw new RuntimeException(e);
    847         } catch (Exception ignored) {
    848         }
    849 
    850         return oid;
    851     }
    852 
    853     /**
    854      * Provides extended capabilities for the session if supported by the platform.
    855      */
    856     public static SSLSession wrapSSLSession(ExternalSession sslSession) {
    857         if (Build.VERSION.SDK_INT >= 24) {
    858             return new Java8ExtendedSSLSession(sslSession);
    859         }
    860 
    861         return sslSession;
    862     }
    863 
    864     public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
    865         if (Build.VERSION.SDK_INT > 27) {
    866             try {
    867                 Method getHolder = InetAddress.class.getDeclaredMethod("holder");
    868                 getHolder.setAccessible(true);
    869 
    870                 Method getOriginalHostName = Class.forName("java.net.InetAddress$InetAddressHolder")
    871                                                      .getDeclaredMethod("getOriginalHostName");
    872                 getOriginalHostName.setAccessible(true);
    873 
    874                 String originalHostName =
    875                         (String) getOriginalHostName.invoke(getHolder.invoke(addr));
    876                 if (originalHostName == null) {
    877                     return addr.getHostAddress();
    878                 }
    879                 return originalHostName;
    880             } catch (InvocationTargetException e) {
    881                 throw new RuntimeException("Failed to get originalHostName", e);
    882             } catch (ClassNotFoundException ignore) {
    883                 // passthrough and return addr.getHostAddress()
    884             } catch (IllegalAccessException ignore) {
    885             } catch (NoSuchMethodException ignore) {
    886             }
    887         }
    888         return addr.getHostAddress();
    889     }
    890 
    891     /*
    892      * Pre-Java-7 backward compatibility.
    893      */
    894 
    895     public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
    896         if (Build.VERSION.SDK_INT > 23) {
    897             try {
    898                 Method m_getHostString = InetSocketAddress.class.getDeclaredMethod("getHostString");
    899                 return (String) m_getHostString.invoke(addr);
    900             } catch (InvocationTargetException e) {
    901                 throw new RuntimeException(e);
    902             } catch (Exception ignored) {
    903             }
    904         }
    905         return null;
    906     }
    907 
    908     // X509ExtendedTrustManager was added in API 24
    909     static boolean supportsX509ExtendedTrustManager() {
    910         return Build.VERSION.SDK_INT > 23;
    911     }
    912 
    913     /**
    914      * Check if SCT verification is required for a given hostname.
    915      *
    916      * SCT Verification is enabled using {@code Security} properties.
    917      * The "conscrypt.ct.enable" property must be true, as well as a per domain property.
    918      * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce."
    919      * is used as the property name.
    920      * Basic globbing is also supported.
    921      *
    922      * For example, for the domain foo.bar.com, the following properties will be
    923      * looked up, in order of precedence.
    924      * - conscrypt.ct.enforce.com.bar.foo
    925      * - conscrypt.ct.enforce.com.bar.*
    926      * - conscrypt.ct.enforce.com.*
    927      * - conscrypt.ct.enforce.*
    928      */
    929     public static boolean isCTVerificationRequired(String hostname) {
    930         if (hostname == null) {
    931             return false;
    932         }
    933         // TODO: Use the platform version on platforms that support it
    934 
    935         String property = Security.getProperty("conscrypt.ct.enable");
    936         if (property == null || !Boolean.valueOf(property)) {
    937             return false;
    938         }
    939 
    940         List<String> parts = Arrays.asList(hostname.split("\\."));
    941         Collections.reverse(parts);
    942 
    943         boolean enable = false;
    944         String propertyName = "conscrypt.ct.enforce";
    945         // The loop keeps going on even once we've found a match
    946         // This allows for finer grained settings on subdomains
    947         for (String part : parts) {
    948             property = Security.getProperty(propertyName + ".*");
    949             if (property != null) {
    950                 enable = Boolean.valueOf(property);
    951             }
    952 
    953             propertyName = propertyName + "." + part;
    954         }
    955 
    956         property = Security.getProperty(propertyName);
    957         if (property != null) {
    958             enable = Boolean.valueOf(property);
    959         }
    960         return enable;
    961     }
    962 
    963     static boolean supportsConscryptCertStore() {
    964         return false;
    965     }
    966 
    967     static KeyStore getDefaultCertKeyStore() throws KeyStoreException {
    968         KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
    969         try {
    970             keyStore.load(null, null);
    971         } catch (IOException e) {
    972             throw new KeyStoreException(e);
    973         } catch (CertificateException e) {
    974             throw new KeyStoreException(e);
    975         } catch (NoSuchAlgorithmException e) {
    976             throw new KeyStoreException(e);
    977         }
    978         return keyStore;
    979     }
    980 
    981     static ConscryptCertStore newDefaultCertStore() {
    982         return null;
    983     }
    984 
    985     static CertBlacklist newDefaultBlacklist() {
    986         return null;
    987     }
    988 
    989     static CTLogStore newDefaultLogStore() {
    990         return null;
    991     }
    992 
    993     static CTPolicy newDefaultPolicy(CTLogStore logStore) {
    994         return null;
    995     }
    996 }
    997