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