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