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