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