From ab6635af69d73ea06114c192a543f4c85eef1a8f Mon Sep 17 00:00:00 2001 From: Jonathan Fisher Date: Wed, 11 Nov 2020 12:22:48 +0000 Subject: [PATCH] tls config changes --- .../ariane/io/gemini/DummyTrustManager.kt | 17 ++- .../ariane/io/gemini/GeminiDatasource.kt | 122 ++++++++++++------ .../ariane/ui/settings/SettingsFragment.kt | 26 +++- 3 files changed, 112 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/oppen/ariane/io/gemini/DummyTrustManager.kt b/app/src/main/java/oppen/ariane/io/gemini/DummyTrustManager.kt index 25d74a4..de4e308 100644 --- a/app/src/main/java/oppen/ariane/io/gemini/DummyTrustManager.kt +++ b/app/src/main/java/oppen/ariane/io/gemini/DummyTrustManager.kt @@ -11,24 +11,27 @@ object DummyTrustManager { fun get(): Array { return arrayOf( object : X509TrustManager { - @Throws(CertificateException::class) override fun checkClientTrusted( - chain: Array?, + chain: Array?, authType: String? ) { + } - @Throws(CertificateException::class) override fun checkServerTrusted( - chain: Array?, + chain: Array?, authType: String? ) { + println("checkServerTrusted()") + println("checkServerTrusted() authType: $authType") + chain?.forEach { cert -> + println("checkServerTrusted() cert: ${cert.subjectDN}") + } } - override fun getAcceptedIssuers(): Array? { + override fun getAcceptedIssuers(): Array { return arrayOf() } - } - ) + }) } } \ No newline at end of file diff --git a/app/src/main/java/oppen/ariane/io/gemini/GeminiDatasource.kt b/app/src/main/java/oppen/ariane/io/gemini/GeminiDatasource.kt index 5f2eb25..d8046fa 100644 --- a/app/src/main/java/oppen/ariane/io/gemini/GeminiDatasource.kt +++ b/app/src/main/java/oppen/ariane/io/gemini/GeminiDatasource.kt @@ -22,7 +22,8 @@ const val GEMINI_SCHEME = "gemini" * */ class GeminiDatasource( - private val context: Context): Datasource { + private val context: Context +): Datasource { private val prefs = PreferenceManager.getDefaultSharedPreferences(context) private var last: URI? = null @@ -38,7 +39,7 @@ class GeminiDatasource( when (uri.scheme) { GEMINI_SCHEME -> { val cached = RuntimeCache.get(uri) - if(cached != null){ + if (cached != null) { last = uri onUpdate( GemState.ResponseGemtext( @@ -48,7 +49,7 @@ class GeminiDatasource( ) ) return - }else{ + } else { onUpdate(GemState.Requesting(uri)) GlobalScope.launch { @@ -71,7 +72,10 @@ class GeminiDatasource( !address.contains("://") -> { //looks like a relative link val lastAddress = last.toString() - val relAddress = "${lastAddress.substring(0, lastAddress.lastIndexOf("/") + 1)}$address" + val relAddress = "${lastAddress.substring( + 0, + lastAddress.lastIndexOf("/") + 1 + )}$address" URI.create(relAddress) } else -> { @@ -104,26 +108,6 @@ class GeminiDatasource( https://framagit.org/waweic/gemini-client/-/blob/master/app/src/main/java/rocks/ism/decentral/geminiclient/GeminiConnection.kt * */ - - private val trustAllCerts: Array = arrayOf(object : X509TrustManager { - override fun checkClientTrusted(chain: Array?, authType: String?) { - - } - - override fun checkServerTrusted(chain: Array?, authType: String?) { - println("checkServerTrusted()") - println("checkServerTrusted() authType: $authType") - chain?.forEach { cert -> - println("checkServerTrusted() cert: ${cert.subjectDN}") - } - } - - override fun getAcceptedIssuers(): Array { - return arrayOf() - } - }) - - private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit){ last = uri val port = if(uri.port == -1) 1965 else uri.port @@ -132,23 +116,28 @@ class GeminiDatasource( println("REQ_PROTOCOL: $protocol") - val sslContext = SSLContext.getInstance(protocol) - sslContext.init(null, trustAllCerts, SecureRandom()) + //todo - extract and reuse this + val sslContext = if(protocol == "TLS_ALL"){ + SSLContext.getInstance("TLS") + }else{ + SSLContext.getInstance(protocol) + } + sslContext.init(null, DummyTrustManager.get(), null) val factory: SSLSocketFactory = sslContext.socketFactory val socket: SSLSocket? try { socket = factory.createSocket(uri.host, port) as SSLSocket - socket.enabledCipherSuites = factory.supportedCipherSuites when (protocol) { - "TLS" -> socket.enabledProtocols = socket.supportedProtocols + "TLS" -> {}//Use default enabled protocols + "TLS_ALL" -> socket.enabledProtocols = socket.supportedProtocols else -> socket.enabledProtocols = arrayOf(protocol) } socket.startHandshake() - }catch(ce: ConnectException){ + }catch (ce: ConnectException){ println("socket error: $ce") onUpdate( GemState.ResponseError( @@ -159,7 +148,7 @@ class GeminiDatasource( ) ) return - }catch(she: SSLHandshakeException){ + }catch (she: SSLHandshakeException){ println("socket error: $she") onUpdate( GemState.ResponseError( @@ -209,7 +198,12 @@ class GeminiDatasource( header.code == GeminiResponse.INPUT -> onUpdate(GemState.ResponseInput(uri, header)) header.code == GeminiResponse.REDIRECT -> request(URI.create(header.meta), onUpdate) header.code != GeminiResponse.SUCCESS -> onUpdate(GemState.ResponseError(header)) - header.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, uri, header, onUpdate) + header.meta.startsWith("text/gemini") -> getGemtext( + bufferedReader, + uri, + header, + onUpdate + ) header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate) header.meta.startsWith("image/") -> getBinary(socket, uri, header, onUpdate) header.meta.startsWith("audio/") -> getBinary(socket, uri, header, onUpdate) @@ -228,7 +222,12 @@ class GeminiDatasource( socket.close() } - private fun getGemtext(reader: BufferedReader, uri: URI, header: GeminiResponse.Header, onUpdate: (state: GemState) -> Unit){ + private fun getGemtext( + reader: BufferedReader, + uri: URI, + header: GeminiResponse.Header, + onUpdate: (state: GemState) -> Unit + ){ val lines = mutableListOf() @@ -243,22 +242,53 @@ class GeminiDatasource( onUpdate(GemState.ResponseGemtext(uri, header, processed)) } - private fun getString(socket: SSLSocket?, uri: URI, header: GeminiResponse.Header, onUpdate: (state: GemState) -> Unit){ + private fun getString( + socket: SSLSocket?, + uri: URI, + header: GeminiResponse.Header, + onUpdate: (state: GemState) -> Unit + ){ val content = socket?.inputStream?.bufferedReader().use { reader -> reader?.readText() } socket?.close() onUpdate(GemState.ResponseText(uri, header, content ?: "Error fetching content")) } - private fun getBinary(socket: SSLSocket?, uri: URI, header: GeminiResponse.Header, onUpdate: (state: GemState) -> Unit){ + private fun getBinary( + socket: SSLSocket?, + uri: URI, + header: GeminiResponse.Header, + onUpdate: (state: GemState) -> Unit + ){ - val filenameRegex = Regex("[^A-Za-z0-9]") - val cacheFile = File(context.cacheDir, filenameRegex.replace(uri.path, "_")) + var filename: String? = null + val fileSegmentIndex: Int = uri.path.lastIndexOf('/') + if (fileSegmentIndex != -1) { + filename = uri.path.substring(fileSegmentIndex + 1) + } + + val host = uri.host.replace(".", "_") + val cacheName = "${host}_$filename" + println("Caching file: $filename from uri: $uri, cacheName: $cacheName") + + val cacheFile = File(context.cacheDir, cacheName) when { cacheFile.exists() -> { when { - header.meta.startsWith("image/") -> onUpdate(GemState.ResponseImage(uri, header, cacheFile.toUri())) - header.meta.startsWith("audio/") -> onUpdate(GemState.ResponseAudio(uri, header, cacheFile.toUri())) + header.meta.startsWith("image/") -> onUpdate( + GemState.ResponseImage( + uri, + header, + cacheFile.toUri() + ) + ) + header.meta.startsWith("audio/") -> onUpdate( + GemState.ResponseAudio( + uri, + header, + cacheFile.toUri() + ) + ) } } @@ -270,8 +300,20 @@ class GeminiDatasource( } when { - header.meta.startsWith("image/") -> onUpdate(GemState.ResponseImage(uri, header, cacheFile.toUri())) - header.meta.startsWith("audio/") -> onUpdate(GemState.ResponseAudio(uri, header, cacheFile.toUri())) + header.meta.startsWith("image/") -> onUpdate( + GemState.ResponseImage( + uri, + header, + cacheFile.toUri() + ) + ) + header.meta.startsWith("audio/") -> onUpdate( + GemState.ResponseAudio( + uri, + header, + cacheFile.toUri() + ) + ) } } } diff --git a/app/src/main/java/oppen/ariane/ui/settings/SettingsFragment.kt b/app/src/main/java/oppen/ariane/ui/settings/SettingsFragment.kt index b6a0817..a6c2784 100644 --- a/app/src/main/java/oppen/ariane/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/oppen/ariane/ui/settings/SettingsFragment.kt @@ -19,6 +19,9 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang val context = preferenceManager.context val screen = preferenceManager.createPreferenceScreen(context) + /** + * TLS Settings + */ val tlsCategory = PreferenceCategory(context) tlsCategory.key = "tls_category" tlsCategory.title = "TLS Config" @@ -30,6 +33,12 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang tlsDefaultPreference.onPreferenceChangeListener = this tlsCategory.addPreference(tlsDefaultPreference) + val tlsAllSupportedPreference = SwitchPreferenceCompat(context) + tlsAllSupportedPreference.key = "tls_All_Supported" + tlsAllSupportedPreference.title = "TLS Enable all supported" + tlsAllSupportedPreference.onPreferenceChangeListener = this + tlsCategory.addPreference(tlsAllSupportedPreference) + val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, null, SecureRandom()) val factory: SSLSocketFactory = sslContext.socketFactory @@ -55,6 +64,10 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang val default = preferenceScreen.findPreference("tls_Default") default?.isChecked = false } + if(key != "tls_All_Supported"){ + val default = preferenceScreen.findPreference("tls_All_Supported") + default?.isChecked = false + } protocols.forEach { protocol -> val tlsSwitchKey = "tls_${protocol.toLowerCase(Locale.getDefault())}" if (tlsSwitchKey != key) { @@ -66,12 +79,13 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang } } - val prefTitle = preference.title.toString() - - if(prefTitle.toLowerCase().contains("default")){ - setTLSProtocol("TLS") - }else{ - setTLSProtocol(prefTitle) + when (preference.key) { + "tls_Default" -> setTLSProtocol("TLS") + "tls_All_Supported" -> setTLSProtocol("TLS_ALL") + else -> { + val prefTitle = preference.title.toString() + setTLSProtocol(prefTitle) + } } return true