network: let client override pins for alternative routing proxies

CP-405
This commit is contained in:
Mateusz Markowicz 2020-07-23 17:25:36 +02:00
parent 067cd068cf
commit fa4e784000
4 changed files with 36 additions and 28 deletions

View File

@ -31,31 +31,37 @@ import javax.net.ssl.X509TrustManager
*
* @param okBuilder builder to introduce pinning to.
* @param host host for which pins are added.
* @param pins list of pins in okhttp format.
* @param pins list of pins (base64, SHA-256). When empty pinning will be disabled (should be used
* only for testing).
*/
internal fun initPinning(okBuilder: OkHttpClient.Builder, host: String, pins: Array<String>) {
val pinner = CertificatePinner.Builder()
.add("**.$host", *pins)
.build()
okBuilder.certificatePinner(pinner)
if (pins.isNotEmpty()) {
val pinner = CertificatePinner.Builder()
.add("**.$host", *pins.map { "sha256/$it" }.toTypedArray())
.build()
okBuilder.certificatePinner(pinner)
}
}
/**
* Inits given okhttp builder with leaf SPKI pinning. Accepts certificate chain iff leaf certificate
* SPKI matches one of the [spkiPins].
* SPKI matches one of the [pins].
*
* @param okBuilder builder to introduce pinning to.
* @param spkiPins list of sha-256 SPKI hashes.
* @param pins list of pins (base64, SHA-256). When empty, pinning will be disabled and default
* certificate verification will be used (should be used only for testing).
*/
internal fun initSPKIleafPinning(builder: OkHttpClient.Builder, spkiPins: List<String>) {
val trustManager = LeafSPKIPinningTrustManager(spkiPins)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(trustManager), null)
builder.sslSocketFactory(sslContext.socketFactory, trustManager)
builder.hostnameVerifier(HostnameVerifier { _, _ ->
// Verification is based solely on SPKI pinning of leaf certificate
true
})
internal fun initSPKIleafPinning(builder: OkHttpClient.Builder, pins: List<String>) {
if (pins.isNotEmpty()) {
val trustManager = LeafSPKIPinningTrustManager(pins)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(trustManager), null)
builder.sslSocketFactory(sslContext.socketFactory, trustManager)
builder.hostnameVerifier(HostnameVerifier { _, _ ->
// Verification is based solely on SPKI pinning of leaf certificate
true
})
}
}
internal class LeafSPKIPinningTrustManager(pinnedSPKIHashes: List<String>) : X509TrustManager {

View File

@ -20,16 +20,16 @@ package me.proton.core.network.data.di
object Constants {
/**
* Certificate pins for Proton API in OkHttp format.
* Certificate pins for Proton API (base64, SHA-256).
*/
val DEFAULT_PINS = arrayOf(
"sha256/IEwk65VSaxv3s1/88vF/rM8PauJoIun3rzVCX5mLS3M",
"sha256/drtmcR2kFkM8qJClsuWgUzxgBkePfRCkRpqUesyDmeE=",
"sha256/YRGlaY0jyJ4Jw2/4M8FIftwbDIQfh8Sdro96CeEel54=",
"sha256/AfMENBVvOS8MnISprtvyPsjKlPooqh8nMB/pvCrpJpw=")
val DEFAULT_SPKI_PINS = arrayOf(
"IEwk65VSaxv3s1/88vF/rM8PauJoIun3rzVCX5mLS3M",
"drtmcR2kFkM8qJClsuWgUzxgBkePfRCkRpqUesyDmeE=",
"YRGlaY0jyJ4Jw2/4M8FIftwbDIQfh8Sdro96CeEel54=",
"AfMENBVvOS8MnISprtvyPsjKlPooqh8nMB/pvCrpJpw=")
/**
* SPKI pins for alternative Proton API leaf certificates (SHA-256).
* SPKI pins for alternative Proton API leaf certificates (base64, SHA-256).
*/
val ALTERNATIVE_API_SPKI_PINS = listOf(
"EU6TS9MO0L/GsDHvVc9D5fChYLNy5JdGYpJw0ccgetM=",

View File

@ -79,14 +79,16 @@ class ApiFactory(
* @param userData [UserData] to be used in the [ApiManager].
* @param interfaceClass Kotlin class for [Api] interface.
* @param clientErrorHandlers Extra error handlers provided by the client.
* @param certificatePins Overrides [Constants.DEFAULT_PINS]
* @param certificatePins Overrides [Constants.DEFAULT_SPKI_PINS]
* @param alternativeApiPins Overrides [Constants.ALTERNATIVE_API_SPKI_PINS]
* @return [ApiManager] instance.
*/
fun <Api : BaseRetrofitApi> ApiManager(
userData: UserData,
interfaceClass: KClass<Api>,
clientErrorHandlers: List<ApiErrorHandler<Api>> = emptyList(),
certificatePins: Array<String> = Constants.DEFAULT_PINS
certificatePins: Array<String> = Constants.DEFAULT_SPKI_PINS,
alternativeApiPins: List<String> = Constants.ALTERNATIVE_API_SPKI_PINS
): ApiManager<Api> {
val pinningStrategy = { builder: OkHttpClient.Builder ->
initPinning(builder, URI(baseUrl).host, certificatePins)
@ -107,7 +109,7 @@ class ApiFactory(
createBaseErrorHandlers<Api>(userData, ::javaMonoClockMs, mainScope) + clientErrorHandlers
val alternativePinningStrategy = { builder: OkHttpClient.Builder ->
initSPKIleafPinning(builder, Constants.ALTERNATIVE_API_SPKI_PINS)
initSPKIleafPinning(builder, alternativeApiPins)
}
val dohApiHandler = DohApiHandler(apiClient, primaryBackend, dohProvider, prefs, ::javaWallClockMs) { baseUrl ->
ProtonApiBackend(

View File

@ -76,7 +76,7 @@ class TestTLSHelper {
}
companion object {
val TEST_PINS = arrayOf("sha256/d/dc7p/QKB+PnyVi/JOHUQxrZpfGc2LEdq43JGGii4k=")
val BAD_PINS = arrayOf("sha256/a/dc7p/QKB+PnyVi/JOHUQxrZpfGc2LEdq43JGGii4k=")
val TEST_PINS = arrayOf("d/dc7p/QKB+PnyVi/JOHUQxrZpfGc2LEdq43JGGii4k=")
val BAD_PINS = arrayOf("a/dc7p/QKB+PnyVi/JOHUQxrZpfGc2LEdq43JGGii4k=")
}
}