1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.conscrypt; 19 20 import java.security.KeyManagementException; 21 import java.security.KeyStore; 22 import java.security.KeyStoreException; 23 import java.security.NoSuchAlgorithmException; 24 import java.security.SecureRandom; 25 import java.security.UnrecoverableKeyException; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import javax.crypto.SecretKey; 29 import javax.net.ssl.KeyManager; 30 import javax.net.ssl.KeyManagerFactory; 31 import javax.net.ssl.TrustManager; 32 import javax.net.ssl.TrustManagerFactory; 33 import javax.net.ssl.X509ExtendedKeyManager; 34 import javax.net.ssl.X509KeyManager; 35 import javax.net.ssl.X509TrustManager; 36 import javax.security.auth.x500.X500Principal; 37 38 /** 39 * The instances of this class encapsulate all the info 40 * about enabled cipher suites and protocols, 41 * as well as the information about client/server mode of 42 * ssl socket, whether it require/want client authentication or not, 43 * and controls whether new SSL sessions may be established by this 44 * socket or not. 45 */ 46 final class SSLParametersImpl implements Cloneable { 47 48 // default source of X.509 certificate based authentication keys 49 private static volatile X509KeyManager defaultX509KeyManager; 50 // default source of X.509 certificate based authentication trust decisions 51 private static volatile X509TrustManager defaultX509TrustManager; 52 // default SSL parameters 53 private static volatile SSLParametersImpl defaultParameters; 54 55 // client session context contains the set of reusable 56 // client-side SSL sessions 57 private final ClientSessionContext clientSessionContext; 58 // server session context contains the set of reusable 59 // server-side SSL sessions 60 private final ServerSessionContext serverSessionContext; 61 // source of X.509 certificate based authentication keys or null if not provided 62 private final X509KeyManager x509KeyManager; 63 // source of Pre-Shared Key (PSK) authentication keys or null if not provided. 64 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package 65 private final PSKKeyManager pskKeyManager; 66 // source of X.509 certificate based authentication trust decisions or null if not provided 67 private final X509TrustManager x509TrustManager; 68 // source of random numbers 69 private SecureRandom secureRandom; 70 71 // protocols enabled for SSL connection 72 String[] enabledProtocols; 73 // set to indicate when obsolete protocols are filtered 74 boolean isEnabledProtocolsFiltered; 75 // cipher suites enabled for SSL connection 76 String[] enabledCipherSuites; 77 78 // if the peer with this parameters tuned to work in client mode 79 private boolean client_mode = true; 80 // if the peer with this parameters tuned to require client authentication 81 private boolean need_client_auth = false; 82 // if the peer with this parameters tuned to request client authentication 83 private boolean want_client_auth = false; 84 // if the peer with this parameters allowed to cteate new SSL session 85 private boolean enable_session_creation = true; 86 // Endpoint identification algorithm (e.g., HTTPS) 87 private String endpointIdentificationAlgorithm; 88 // Whether to use the local cipher suites order 89 private boolean useCipherSuitesOrder; 90 91 // client-side only, bypasses the property based configuration, used for tests 92 private boolean ctVerificationEnabled; 93 94 // server-side only. SCT and OCSP data to send to clients which request it 95 byte[] sctExtension; 96 byte[] ocspResponse; 97 98 byte[] alpnProtocols; 99 boolean useSessionTickets; 100 private Boolean useSni; 101 102 /** 103 * Whether the TLS Channel ID extension is enabled. This field is 104 * server-side only. 105 */ 106 boolean channelIdEnabled; 107 108 /** 109 * Initializes the parameters. Naturally this constructor is used 110 * in SSLContextImpl.engineInit method which directly passes its 111 * parameters. In other words this constructor holds all 112 * the functionality provided by SSLContext.init method. 113 * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[], 114 * SecureRandom)} for more information 115 */ SSLParametersImpl(KeyManager[] kms, TrustManager[] tms, SecureRandom sr, ClientSessionContext clientSessionContext, ServerSessionContext serverSessionContext, String[] protocols)116 SSLParametersImpl(KeyManager[] kms, TrustManager[] tms, 117 SecureRandom sr, ClientSessionContext clientSessionContext, 118 ServerSessionContext serverSessionContext, String[] protocols) 119 throws KeyManagementException { 120 this.serverSessionContext = serverSessionContext; 121 this.clientSessionContext = clientSessionContext; 122 123 // initialize key managers 124 if (kms == null) { 125 x509KeyManager = getDefaultX509KeyManager(); 126 // There's no default PSK key manager 127 pskKeyManager = null; 128 } else { 129 x509KeyManager = findFirstX509KeyManager(kms); 130 pskKeyManager = findFirstPSKKeyManager(kms); 131 } 132 133 // initialize x509TrustManager 134 if (tms == null) { 135 x509TrustManager = getDefaultX509TrustManager(); 136 } else { 137 x509TrustManager = findFirstX509TrustManager(tms); 138 } 139 140 // initialize secure random 141 // We simply use the SecureRandom passed in by the caller. If it's 142 // null, we don't replace it by a new instance. The native code below 143 // then directly accesses /dev/urandom. Not the most elegant solution, 144 // but faster than going through the SecureRandom object. 145 secureRandom = sr; 146 147 // initialize the list of cipher suites and protocols enabled by default 148 enabledProtocols = NativeCrypto.checkEnabledProtocols( 149 protocols == null ? NativeCrypto.DEFAULT_PROTOCOLS : protocols).clone(); 150 boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null); 151 boolean pskCipherSuitesNeeded = pskKeyManager != null; 152 enabledCipherSuites = getDefaultCipherSuites( 153 x509CipherSuitesNeeded, pskCipherSuitesNeeded); 154 } 155 getDefault()156 static SSLParametersImpl getDefault() throws KeyManagementException { 157 SSLParametersImpl result = defaultParameters; 158 if (result == null) { 159 // single-check idiom 160 defaultParameters = result = new SSLParametersImpl(null, 161 null, 162 null, 163 new ClientSessionContext(), 164 new ServerSessionContext(), 165 null); 166 } 167 return (SSLParametersImpl) result.clone(); 168 } 169 170 /** 171 * Returns the appropriate session context. 172 */ getSessionContext()173 AbstractSessionContext getSessionContext() { 174 return client_mode ? clientSessionContext : serverSessionContext; 175 } 176 177 /** 178 * @return client session context 179 */ getClientSessionContext()180 ClientSessionContext getClientSessionContext() { 181 return clientSessionContext; 182 } 183 184 /** 185 * @return X.509 key manager or {@code null} for none. 186 */ getX509KeyManager()187 X509KeyManager getX509KeyManager() { 188 return x509KeyManager; 189 } 190 191 /** 192 * @return Pre-Shared Key (PSK) key manager or {@code null} for none. 193 */ 194 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package getPSKKeyManager()195 PSKKeyManager getPSKKeyManager() { 196 return pskKeyManager; 197 } 198 199 /** 200 * @return X.509 trust manager or {@code null} for none. 201 */ getX509TrustManager()202 X509TrustManager getX509TrustManager() { 203 return x509TrustManager; 204 } 205 206 /** 207 * @return the names of enabled cipher suites 208 */ getEnabledCipherSuites()209 String[] getEnabledCipherSuites() { 210 return enabledCipherSuites.clone(); 211 } 212 213 /** 214 * Sets the enabled cipher suites after filtering through OpenSSL. 215 */ setEnabledCipherSuites(String[] cipherSuites)216 void setEnabledCipherSuites(String[] cipherSuites) { 217 enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(cipherSuites).clone(); 218 } 219 220 /** 221 * @return the set of enabled protocols 222 */ getEnabledProtocols()223 String[] getEnabledProtocols() { 224 return enabledProtocols.clone(); 225 } 226 227 /** 228 * Sets the list of available protocols for use in SSL connection. 229 * @throws IllegalArgumentException if {@code protocols == null} 230 */ setEnabledProtocols(String[] protocols)231 void setEnabledProtocols(String[] protocols) { 232 if (protocols == null) { 233 throw new IllegalArgumentException("protocols == null"); 234 } 235 String[] filteredProtocols = 236 filterFromProtocols(protocols, NativeCrypto.OBSOLETE_PROTOCOL_SSLV3); 237 isEnabledProtocolsFiltered = protocols.length != filteredProtocols.length; 238 enabledProtocols = NativeCrypto.checkEnabledProtocols(filteredProtocols).clone(); 239 } 240 241 /** 242 * Sets the list of ALPN protocols. This method internally converts the protocols to their 243 * wire-format form. 244 * 245 * @param alpnProtocols the list of ALPN protocols 246 * @see #setAlpnProtocols(byte[]) 247 */ setAlpnProtocols(String[] alpnProtocols)248 void setAlpnProtocols(String[] alpnProtocols) { 249 setAlpnProtocols(SSLUtils.toLengthPrefixedList(alpnProtocols)); 250 } 251 252 /** 253 * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of 254 * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings). 255 * Requires that all strings be encoded with US-ASCII. 256 * 257 * @param alpnProtocols the encoded form of the ALPN protocol list 258 * @see #setAlpnProtocols(String[]) 259 */ setAlpnProtocols(byte[] alpnProtocols)260 void setAlpnProtocols(byte[] alpnProtocols) { 261 if (alpnProtocols != null && alpnProtocols.length == 0) { 262 throw new IllegalArgumentException("alpnProtocols.length == 0"); 263 } 264 this.alpnProtocols = alpnProtocols; 265 } 266 getAlpnProtocols()267 byte[] getAlpnProtocols() { 268 return alpnProtocols; 269 } 270 271 /** 272 * Tunes the peer holding this parameters to work in client mode. 273 * @param mode if the peer is configured to work in client mode 274 */ setUseClientMode(boolean mode)275 void setUseClientMode(boolean mode) { 276 client_mode = mode; 277 } 278 279 /** 280 * Returns the value indicating if the parameters configured to work 281 * in client mode. 282 */ getUseClientMode()283 boolean getUseClientMode() { 284 return client_mode; 285 } 286 287 /** 288 * Tunes the peer holding this parameters to require client authentication 289 */ setNeedClientAuth(boolean need)290 void setNeedClientAuth(boolean need) { 291 need_client_auth = need; 292 // reset the want_client_auth setting 293 want_client_auth = false; 294 } 295 296 /** 297 * Returns the value indicating if the peer with this parameters tuned 298 * to require client authentication 299 */ getNeedClientAuth()300 boolean getNeedClientAuth() { 301 return need_client_auth; 302 } 303 304 /** 305 * Tunes the peer holding this parameters to request client authentication 306 */ setWantClientAuth(boolean want)307 void setWantClientAuth(boolean want) { 308 want_client_auth = want; 309 // reset the need_client_auth setting 310 need_client_auth = false; 311 } 312 313 /** 314 * Returns the value indicating if the peer with this parameters 315 * tuned to request client authentication 316 */ getWantClientAuth()317 boolean getWantClientAuth() { 318 return want_client_auth; 319 } 320 321 /** 322 * Allows/disallows the peer holding this parameters to 323 * create new SSL session 324 */ setEnableSessionCreation(boolean flag)325 void setEnableSessionCreation(boolean flag) { 326 enable_session_creation = flag; 327 } 328 329 /** 330 * Returns the value indicating if the peer with this parameters 331 * allowed to cteate new SSL session 332 */ getEnableSessionCreation()333 boolean getEnableSessionCreation() { 334 return enable_session_creation; 335 } 336 setUseSessionTickets(boolean useSessionTickets)337 void setUseSessionTickets(boolean useSessionTickets) { 338 this.useSessionTickets = useSessionTickets; 339 } 340 341 /** 342 * Whether connections using this SSL connection should use the TLS 343 * extension Server Name Indication (SNI). 344 */ setUseSni(boolean flag)345 void setUseSni(boolean flag) { 346 useSni = flag; 347 } 348 349 /** 350 * Returns whether connections using this SSL connection should use the TLS 351 * extension Server Name Indication (SNI). 352 */ getUseSni()353 boolean getUseSni() { 354 return useSni != null ? useSni : isSniEnabledByDefault(); 355 } 356 357 /** 358 * For testing only. 359 */ setCTVerificationEnabled(boolean enabled)360 void setCTVerificationEnabled(boolean enabled) { 361 ctVerificationEnabled = enabled; 362 } 363 364 /** 365 * For testing only. 366 */ setSCTExtension(byte[] extension)367 void setSCTExtension(byte[] extension) { 368 sctExtension = extension; 369 } 370 371 /** 372 * For testing only. 373 */ setOCSPResponse(byte[] response)374 void setOCSPResponse(byte[] response) { 375 ocspResponse = response; 376 } 377 getOCSPResponse()378 byte[] getOCSPResponse() { 379 return ocspResponse; 380 } 381 382 /** 383 * This filters {@code obsoleteProtocol} from the list of {@code protocols} 384 * down to help with app compatibility. 385 */ filterFromProtocols(String[] protocols, String obsoleteProtocol)386 private static String[] filterFromProtocols(String[] protocols, String obsoleteProtocol) { 387 if (protocols.length == 1 && obsoleteProtocol.equals(protocols[0])) { 388 return EMPTY_STRING_ARRAY; 389 } 390 391 ArrayList<String> newProtocols = new ArrayList<String>(); 392 for (String protocol : protocols) { 393 if (!obsoleteProtocol.equals(protocol)) { 394 newProtocols.add(protocol); 395 } 396 } 397 return newProtocols.toArray(EMPTY_STRING_ARRAY); 398 } 399 400 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 401 402 /** 403 * Returns whether Server Name Indication (SNI) is enabled by default for 404 * sockets. For more information on SNI, see RFC 6066 section 3. 405 */ isSniEnabledByDefault()406 private boolean isSniEnabledByDefault() { 407 try { 408 String enableSNI = System.getProperty("jsse.enableSNIExtension", "true"); 409 if ("true".equalsIgnoreCase(enableSNI)) { 410 return true; 411 } else if ("false".equalsIgnoreCase(enableSNI)) { 412 return false; 413 } else { 414 throw new RuntimeException( 415 "Can only set \"jsse.enableSNIExtension\" to \"true\" or \"false\""); 416 } 417 } catch (SecurityException e) { 418 return true; 419 } 420 } 421 422 /** 423 * For abstracting the X509KeyManager calls between 424 * {@link X509KeyManager#chooseClientAlias(String[], java.security.Principal[], java.net.Socket)} 425 * and 426 * {@link X509ExtendedKeyManager#chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine)} 427 */ 428 interface AliasChooser { chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers, String[] keyTypes)429 String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers, 430 String[] keyTypes); 431 chooseServerAlias(X509KeyManager keyManager, String keyType)432 String chooseServerAlias(X509KeyManager keyManager, String keyType); 433 } 434 435 /** 436 * For abstracting the {@code PSKKeyManager} calls between those taking an {@code SSLSocket} and 437 * those taking an {@code SSLEngine}. 438 */ 439 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package 440 interface PSKCallbacks { chooseServerPSKIdentityHint(PSKKeyManager keyManager)441 String chooseServerPSKIdentityHint(PSKKeyManager keyManager); chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint)442 String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint); getPSKKey(PSKKeyManager keyManager, String identityHint, String identity)443 SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity); 444 } 445 446 /** 447 * Returns the clone of this object. 448 * @return the clone. 449 */ 450 @Override clone()451 protected Object clone() { 452 try { 453 return super.clone(); 454 } catch (CloneNotSupportedException e) { 455 throw new AssertionError(e); 456 } 457 } 458 getDefaultX509KeyManager()459 private static X509KeyManager getDefaultX509KeyManager() throws KeyManagementException { 460 X509KeyManager result = defaultX509KeyManager; 461 if (result == null) { 462 // single-check idiom 463 defaultX509KeyManager = result = createDefaultX509KeyManager(); 464 } 465 return result; 466 } createDefaultX509KeyManager()467 private static X509KeyManager createDefaultX509KeyManager() throws KeyManagementException { 468 try { 469 String algorithm = KeyManagerFactory.getDefaultAlgorithm(); 470 KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); 471 kmf.init(null, null); 472 KeyManager[] kms = kmf.getKeyManagers(); 473 X509KeyManager result = findFirstX509KeyManager(kms); 474 if (result == null) { 475 throw new KeyManagementException("No X509KeyManager among default KeyManagers: " 476 + Arrays.toString(kms)); 477 } 478 return result; 479 } catch (NoSuchAlgorithmException e) { 480 throw new KeyManagementException(e); 481 } catch (KeyStoreException e) { 482 throw new KeyManagementException(e); 483 } catch (UnrecoverableKeyException e) { 484 throw new KeyManagementException(e); 485 } 486 } 487 488 /** 489 * Finds the first {@link X509KeyManager} element in the provided array. 490 * 491 * @return the first {@code X509KeyManager} or {@code null} if not found. 492 */ findFirstX509KeyManager(KeyManager[] kms)493 private static X509KeyManager findFirstX509KeyManager(KeyManager[] kms) { 494 for (KeyManager km : kms) { 495 if (km instanceof X509KeyManager) { 496 return (X509KeyManager)km; 497 } 498 } 499 return null; 500 } 501 502 /** 503 * Finds the first {@link PSKKeyManager} element in the provided array. 504 * 505 * @return the first {@code PSKKeyManager} or {@code null} if not found. 506 */ 507 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package findFirstPSKKeyManager(KeyManager[] kms)508 private static PSKKeyManager findFirstPSKKeyManager(KeyManager[] kms) { 509 for (KeyManager km : kms) { 510 if (km instanceof PSKKeyManager) { 511 return (PSKKeyManager)km; 512 } else if (km != null) { 513 try { 514 return DuckTypedPSKKeyManager.getInstance(km); 515 } catch (NoSuchMethodException ignored) {} 516 } 517 } 518 return null; 519 } 520 521 /** 522 * Gets the default X.509 trust manager. 523 */ getDefaultX509TrustManager()524 static X509TrustManager getDefaultX509TrustManager() 525 throws KeyManagementException { 526 X509TrustManager result = defaultX509TrustManager; 527 if (result == null) { 528 // single-check idiom 529 defaultX509TrustManager = result = createDefaultX509TrustManager(); 530 } 531 return result; 532 } 533 createDefaultX509TrustManager()534 private static X509TrustManager createDefaultX509TrustManager() 535 throws KeyManagementException { 536 try { 537 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); 538 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); 539 tmf.init((KeyStore) null); 540 TrustManager[] tms = tmf.getTrustManagers(); 541 X509TrustManager trustManager = findFirstX509TrustManager(tms); 542 if (trustManager == null) { 543 throw new KeyManagementException( 544 "No X509TrustManager in among default TrustManagers: " 545 + Arrays.toString(tms)); 546 } 547 return trustManager; 548 } catch (NoSuchAlgorithmException e) { 549 throw new KeyManagementException(e); 550 } catch (KeyStoreException e) { 551 throw new KeyManagementException(e); 552 } 553 } 554 555 /** 556 * Finds the first {@link X509TrustManager} element in the provided array. 557 * 558 * @return the first {@code X509ExtendedTrustManager} or 559 * {@code X509TrustManager} or {@code null} if not found. 560 */ findFirstX509TrustManager(TrustManager[] tms)561 private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) { 562 for (TrustManager tm : tms) { 563 if (tm instanceof X509TrustManager) { 564 return (X509TrustManager) tm; 565 } 566 } 567 return null; 568 } 569 getEndpointIdentificationAlgorithm()570 String getEndpointIdentificationAlgorithm() { 571 return endpointIdentificationAlgorithm; 572 } 573 setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm)574 void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) { 575 this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm; 576 } 577 getUseCipherSuitesOrder()578 boolean getUseCipherSuitesOrder() { 579 return useCipherSuitesOrder; 580 } 581 setUseCipherSuitesOrder(boolean useCipherSuitesOrder)582 void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) { 583 this.useCipherSuitesOrder = useCipherSuitesOrder; 584 } 585 getDefaultCipherSuites( boolean x509CipherSuitesNeeded, boolean pskCipherSuitesNeeded)586 private static String[] getDefaultCipherSuites( 587 boolean x509CipherSuitesNeeded, 588 boolean pskCipherSuitesNeeded) { 589 if (x509CipherSuitesNeeded) { 590 // X.509 based cipher suites need to be listed. 591 if (pskCipherSuitesNeeded) { 592 // Both X.509 and PSK based cipher suites need to be listed. Because TLS-PSK is not 593 // normally used, we assume that when PSK cipher suites are requested here they 594 // should be preferred over other cipher suites. Thus, we give PSK cipher suites 595 // higher priority than X.509 cipher suites. 596 // NOTE: There are cipher suites that use both X.509 and PSK (e.g., those based on 597 // RSA_PSK key exchange). However, these cipher suites are not currently supported. 598 return concat( 599 NativeCrypto.DEFAULT_PSK_CIPHER_SUITES, 600 NativeCrypto.DEFAULT_X509_CIPHER_SUITES, 601 new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV}); 602 } else { 603 // Only X.509 cipher suites need to be listed. 604 return concat( 605 NativeCrypto.DEFAULT_X509_CIPHER_SUITES, 606 new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV}); 607 } 608 } else if (pskCipherSuitesNeeded) { 609 // Only PSK cipher suites need to be listed. 610 return concat( 611 NativeCrypto.DEFAULT_PSK_CIPHER_SUITES, 612 new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV}); 613 } else { 614 // Neither X.509 nor PSK cipher suites need to be listed. 615 return new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV}; 616 } 617 } 618 concat(String[].... arrays)619 private static String[] concat(String[]... arrays) { 620 int resultLength = 0; 621 for (String[] array : arrays) { 622 resultLength += array.length; 623 } 624 String[] result = new String[resultLength]; 625 int resultOffset = 0; 626 for (String[] array : arrays) { 627 System.arraycopy(array, 0, result, resultOffset, array.length); 628 resultOffset += array.length; 629 } 630 return result; 631 } 632 633 /** 634 * Check if SCT verification is enforced for a given hostname. 635 */ isCTVerificationEnabled(String hostname)636 boolean isCTVerificationEnabled(String hostname) { 637 if (hostname == null) { 638 return false; 639 } 640 641 // Bypass the check. This is used for testing only 642 if (ctVerificationEnabled) { 643 return true; 644 } 645 return Platform.isCTVerificationRequired(hostname); 646 } 647 } 648