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