All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmTest.okhttp3.CallHandshakeTest.kt Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha.14
Show newest version
/*
 * Copyright (C) 2020 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package okhttp3

import mockwebserver3.MockResponse
import mockwebserver3.MockWebServer
import okhttp3.CipherSuite.Companion.TLS_AES_128_GCM_SHA256
import okhttp3.CipherSuite.Companion.TLS_AES_256_GCM_SHA384
import okhttp3.CipherSuite.Companion.TLS_CHACHA20_POLY1305_SHA256
import okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
import okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
import okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
import okhttp3.CipherSuite.Companion.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
import okhttp3.CipherSuite.Companion.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
import okhttp3.CipherSuite.Companion.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
import okhttp3.CipherSuite.Companion.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
import okhttp3.internal.effectiveCipherSuites
import okhttp3.internal.platform.Platform
import okhttp3.testing.PlatformRule
import okhttp3.tls.internal.TlsUtil.localhost
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import javax.net.ssl.SSLSocket

class CallHandshakeTest {
  private lateinit var client: OkHttpClient
  private lateinit var server: MockWebServer
  val handshakeCertificates = localhost()

  @RegisterExtension
  @JvmField
  val clientTestRule = OkHttpClientTestRule()

  @RegisterExtension
  @JvmField
  var platform = PlatformRule()

  private lateinit var handshakeEnabledCipherSuites: List
  private lateinit var defaultEnabledCipherSuites: List
  private lateinit var defaultSupportedCipherSuites: List

  val expectedModernTls12CipherSuites =
    listOf(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384)
  val expectedModernTls13CipherSuites = listOf(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384)

  @BeforeEach
  fun setup(server: MockWebServer) {
    this.server = server

    server.enqueue(MockResponse().setResponseCode(200))

    client = clientTestRule.newClientBuilder()
      .sslSocketFactory(
        handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager)
      .hostnameVerifier(RecordingHostnameVerifier())
      .build()
    server.useHttps(handshakeCertificates.sslSocketFactory())

    defaultEnabledCipherSuites =
      handshakeCertificates.sslSocketFactory().defaultCipherSuites.toList()
    defaultSupportedCipherSuites =
      handshakeCertificates.sslSocketFactory().supportedCipherSuites.toList()
  }

  @Test
  fun testDefaultHandshakeCipherSuiteOrderingTls12Restricted() {
    // We are avoiding making guarantees on ordering of secondary Platforms.
    platform.assumeNotConscrypt()
    platform.assumeNotBouncyCastle()

    val client = makeClient(ConnectionSpec.RESTRICTED_TLS, TlsVersion.TLS_1_2)

    val handshake = makeRequest(client)

    assertThat(handshake.cipherSuite).isIn(expectedModernTls12CipherSuites)

    // Probably something like
    // TLS_AES_128_GCM_SHA256
    // TLS_AES_256_GCM_SHA384
    // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    assertThat(handshakeEnabledCipherSuites).containsExactlyElementsOf(
      expectedConnectionCipherSuites(client))
  }

  @Test
  fun testDefaultHandshakeCipherSuiteOrderingTls12Modern() {
    // We are avoiding making guarantees on ordering of secondary Platforms.
    platform.assumeNotConscrypt()
    platform.assumeNotBouncyCastle()

    val client = makeClient(ConnectionSpec.MODERN_TLS, TlsVersion.TLS_1_2)

    val handshake = makeRequest(client)

    assertThat(handshake.cipherSuite).isIn(expectedModernTls12CipherSuites)

    // Probably something like
    // TLS_AES_128_GCM_SHA256
    // TLS_AES_256_GCM_SHA384
    // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    // TLS_RSA_WITH_AES_256_GCM_SHA384
    // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    // TLS_RSA_WITH_AES_128_GCM_SHA256
    // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    // TLS_RSA_WITH_AES_256_CBC_SHA
    // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
    // TLS_RSA_WITH_AES_128_CBC_SHA
    assertThat(handshakeEnabledCipherSuites).containsExactlyElementsOf(
      expectedConnectionCipherSuites(client))
  }

  @Test
  fun testDefaultHandshakeCipherSuiteOrderingTls13Modern() {
    val client = makeClient(ConnectionSpec.MODERN_TLS, TlsVersion.TLS_1_3)

    val handshake = makeRequest(client)

    assertThat(handshake.cipherSuite).isIn(expectedModernTls13CipherSuites)

    // TODO: filter down to TLSv1.3 when only activated.
    // Probably something like
    // TLS_AES_128_GCM_SHA256
    // TLS_AES_256_GCM_SHA384
    // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    // TLS_RSA_WITH_AES_256_GCM_SHA384
    // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    // TLS_RSA_WITH_AES_128_GCM_SHA256
    // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    // TLS_RSA_WITH_AES_256_CBC_SHA
    // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
    // TLS_RSA_WITH_AES_128_CBC_SHA
    assertThat(handshakeEnabledCipherSuites).containsExactlyElementsOf(
      expectedConnectionCipherSuites(client))
  }

  @Test
  fun testHandshakeCipherSuiteOrderingWhenReversed() {
    // We are avoiding making guarantees on ordering of secondary Platforms.
    platform.assumeNotConscrypt()
    platform.assumeNotBouncyCastle()

    val client = makeClient(ConnectionSpec.RESTRICTED_TLS, TlsVersion.TLS_1_2,
      defaultEnabledCipherSuites.asReversed())

    val handshake = makeRequest(client)

    // TODO better selection
    assertThat(handshake.cipherSuite).isIn(expectedModernTls12CipherSuites)

    // TODO reversed ciphers
    assertThat(handshakeEnabledCipherSuites).containsExactlyElementsOf(
      expectedConnectionCipherSuites(client))
  }

  @Test
  fun defaultOrderMaintained() {
    val client = makeClient()
    makeRequest(client)

    val socketOrderedByDefaults =
      handshakeEnabledCipherSuites.sortedBy { defaultEnabledCipherSuites.indexOf(it) }
    assertThat(handshakeEnabledCipherSuites).containsExactlyElementsOf(socketOrderedByDefaults)
  }

  @Test
  fun advertisedOrderInRestricted() {
    assertThat(ConnectionSpec.RESTRICTED_TLS.cipherSuites).containsExactly(
      TLS_AES_128_GCM_SHA256,
      TLS_AES_256_GCM_SHA384,
      TLS_CHACHA20_POLY1305_SHA256,
      TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
      TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
      TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    )
  }

  @Test
  fun effectiveOrderInRestrictedJdk11() {
    platform.assumeJdkVersion(11)
    // We are avoiding making guarantees on ordering of secondary Platforms.
    platform.assumeNotConscrypt()
    platform.assumeNotBouncyCastle()

    val platform = Platform.get()
    val platformDefaultCipherSuites =
      platform.newSslSocketFactory(platform.platformTrustManager()).defaultCipherSuites
    val cipherSuites =
      ConnectionSpec.RESTRICTED_TLS.effectiveCipherSuites(platformDefaultCipherSuites)

    if (cipherSuites.contains(TLS_CHACHA20_POLY1305_SHA256.javaName)) {
      assertThat(cipherSuites).containsExactlyElementsOf(listOf(
        TLS_AES_256_GCM_SHA384,
        TLS_AES_128_GCM_SHA256,
        TLS_CHACHA20_POLY1305_SHA256,
        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
      ).map { it.javaName })
    } else {
      assertThat(cipherSuites).containsExactlyElementsOf(listOf(
        TLS_AES_128_GCM_SHA256,
        TLS_AES_256_GCM_SHA384,
        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
      ).map { it.javaName })
    }
  }

  private fun expectedConnectionCipherSuites(client: OkHttpClient): Set {
    // TODO correct for the client provided order
//    return client.connectionSpecs.first().cipherSuites!!.map { it.javaName }.intersect(defaultEnabledCipherSuites)
    return defaultEnabledCipherSuites.intersect(
      client.connectionSpecs.first().cipherSuites!!.map { it.javaName })
  }

  private fun makeClient(
    connectionSpec: ConnectionSpec? = null,
    tlsVersion: TlsVersion? = null,
    cipherSuites: List? = null
  ): OkHttpClient {
    return this.client.newBuilder()
      .apply {
        if (connectionSpec != null) {
          connectionSpecs(listOf(ConnectionSpec.Builder(connectionSpec)
            .apply {
              if (tlsVersion != null) {
                tlsVersions(tlsVersion)
              }
              if (cipherSuites != null) {
                cipherSuites(*cipherSuites.toTypedArray())
              }
            }
            .build()))
        }
      }
      .addNetworkInterceptor {
        val socket = it.connection()!!.socket() as SSLSocket

        handshakeEnabledCipherSuites = socket.enabledCipherSuites.toList()

        it.proceed(it.request())
      }
      .build()
  }

  private fun makeRequest(client: OkHttpClient): Handshake {
    val call = client.newCall(Request(server.url("/")))
    return call.execute().use { it.handshake!! }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy