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