Verified Commit 66967d9e authored by Janne Koschinski's avatar Janne Koschinski
Browse files

Implement TLSv1.3 support

parent 60ace405
Pipeline #562 passed with stages
in 26 minutes and 18 seconds
......@@ -101,6 +101,7 @@ class CoreInfoFragment : ServiceBoundFragment() {
private val movementMethod = BetterLinkMovementMethod.newInstance()
private val cipherSuiteRegex = Regex("TLS_(.*)_WITH_(.*)")
private val cipherSuiteRegex13 = Regex("TLS_()(.*)")
init {
movementMethod.setOnLinkLongClickListener(LinkLongClickMenuHelper())
......@@ -188,19 +189,30 @@ class CoreInfoFragment : ServiceBoundFragment() {
secureDetails.visibility = View.GONE
}
val protocol = it.orNull()?.protocol
val cipherSuiteRegex =
if (it.orNull()?.protocol == "TLSv1.3") cipherSuiteRegex13
else cipherSuiteRegex
val (keyExchangeMechanism, cipherSuite) = it.orNull()?.cipherSuite?.let { cipherSuite ->
cipherSuiteRegex.matchEntire(cipherSuite)?.destructured
}?.let { (keyExchangeMechanism, cipherSuite) ->
Pair(keyExchangeMechanism, cipherSuite)
} ?: Pair(null, null)
val protocol = it.orNull()?.protocol
if (cipherSuite != null && keyExchangeMechanism != null && protocol != null) {
// TLSv1.3 has no key exchange mechanism in the ciphersuite
if (keyExchangeMechanism.isEmpty()) {
secureConnectionCiphersuite.text = context?.getString(R.string.label_core_connection_ciphersuite_13,
cipherSuite)
} else {
secureConnectionCiphersuite.text = context?.getString(R.string.label_core_connection_ciphersuite,
cipherSuite,
keyExchangeMechanism)
}
secureConnectionProtocol.text = context?.getString(R.string.label_core_connection_protocol,
protocol)
secureConnectionCiphersuite.text = context?.getString(R.string.label_core_connection_ciphersuite,
cipherSuite,
keyExchangeMechanism)
secureConnectionProtocol.visibility = View.VISIBLE
secureConnectionCiphersuite.visibility = View.VISIBLE
} else {
......
......@@ -36,6 +36,7 @@
<string name="label_core_connection_verified_by">Verbindung verifiziert von %1$s</string>
<string name="label_core_connection_protocol">Die Verbindung verwendet %1$s</string>
<string name="label_core_connection_ciphersuite">Die Verbindung ist mit %1$s verschlüsselt und authentifiziert und verwendet %2$s als Mechanismus für den Schlüsselaustausch</string>
<string name="label_core_connection_ciphersuite_13">Die Verbindung ist mit %1$s verschlüsselt und authentifiziert</string>
<string name="label_core_connection_verified_by_unknown">Unbekannt</string>
<string name="label_core_connection_insecure">Unsichere Verbindung</string>
......
......@@ -36,6 +36,7 @@
<string name="label_core_connection_verified_by">Connessione verificata da %1$s</string>
<string name="label_core_connection_protocol">La connessione utilizza %1$s</string>
<string name="label_core_connection_ciphersuite">La connessione è crittografata ed autenticata con %1$s ed utilizza %2$s come meccanismo di scambio delle chiavi</string>
<string name="label_core_connection_ciphersuite_13">La connessione è crittografata ed autenticata con %1$s</string>
<string name="label_core_connection_verified_by_unknown">Sconosciuto</string>
<string name="label_core_connection_insecure">Connessione insicura</string>
......
......@@ -36,6 +36,7 @@
<string name="label_core_connection_verified_by">Ryšys patvirtintas %1$s</string>
<string name="label_core_connection_protocol">Šis ryšys naudoja %1$s</string>
<string name="label_core_connection_ciphersuite">Šis ryšys yra užkoduotas ir autentifikuotas naudojant %1$s, ir naudoja %2$s raktų apsikeitimui</string>
<string name="label_core_connection_ciphersuite_13">Šis ryšys yra užkoduotas ir autentifikuotas naudojant %1$s</string>
<string name="label_core_connection_verified_by_unknown">Nežinoma</string>
<string name="label_core_connection_insecure">Nesaugus ryšys</string>
......
......@@ -36,6 +36,7 @@
<string name="label_core_connection_verified_by">Ligação verificada por %1$s</string>
<string name="label_core_connection_protocol">A ligação utiliza %1$s</string>
<string name="label_core_connection_ciphersuite">A ligação é encriptada e autenticada utilizando %1$s e usa %2$s como mecanismo de troca de chaves</string>
<string name="label_core_connection_ciphersuite_13">A ligação é encriptada e autenticada utilizando %1$s</string>
<string name="label_core_connection_verified_by_unknown">Desconhecido</string>
<string name="label_core_connection_insecure">Ligação insegura</string>
......
......@@ -36,6 +36,7 @@
<string name="label_core_connection_verified_by">Connection verified by %1$s</string>
<string name="label_core_connection_protocol">The connection uses %1$s</string>
<string name="label_core_connection_ciphersuite">The connection is encrypted and authenticated using %1$s and uses %2$s as the key exchange mechanism</string>
<string name="label_core_connection_ciphersuite_13">The connection is encrypted and authenticated using %1$s</string>
<string name="label_core_connection_verified_by_unknown">Unknown</string>
<string name="label_core_connection_insecure">Insecure Connection</string>
......
......@@ -22,6 +22,8 @@ package de.kuschku.libquassel.util.nio
import de.kuschku.libquassel.connection.HostnameVerifier
import de.kuschku.libquassel.connection.SocketAddress
import de.kuschku.libquassel.util.compatibility.CompatibilityUtils
import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel
import de.kuschku.libquassel.util.compatibility.StreamChannelFactory
import java.io.*
import java.net.Socket
......@@ -32,6 +34,7 @@ import java.nio.channels.InterruptibleChannel
import java.nio.channels.ReadableByteChannel
import java.nio.channels.WritableByteChannel
import java.security.GeneralSecurityException
import java.security.NoSuchAlgorithmException
import java.security.cert.X509Certificate
import java.util.zip.InflaterInputStream
import javax.net.ssl.SSLContext
......@@ -60,6 +63,8 @@ class WrappedChannel private constructor(
}
companion object {
const val DEFAULT_TLS_VERSION = "TLSv1.2"
fun ofSocket(s: Socket, closeListeners: List<Closeable> = emptyList()): WrappedChannel {
return WrappedChannel(
s,
......@@ -68,6 +73,12 @@ class WrappedChannel private constructor(
closeListeners = closeListeners + s.getInputStream() + s.getOutputStream()
)
}
fun selectBestTlsVersion(availableVersions: Array<String>): String? {
return availableVersions.filter {
it.startsWith("TLSv") && it >= "TLSv1.2"
}.sorted().lastOrNull()
}
}
fun withCompression(): WrappedChannel {
......@@ -82,7 +93,16 @@ class WrappedChannel private constructor(
@Throws(GeneralSecurityException::class, IOException::class)
fun withSSL(certificateManager: X509TrustManager, hostnameVerifier: HostnameVerifier,
address: SocketAddress): WrappedChannel {
val context = SSLContext.getInstance("TLSv1.2")
val tlsVersion = try {
selectBestTlsVersion(SSLContext.getDefault().defaultSSLParameters.protocols)
} catch (e: NoSuchAlgorithmException) {
null
}
log(LogLevel.DEBUG,
"WrappedChannel",
"TLS Version chosen is: $tlsVersion, with fallback $DEFAULT_TLS_VERSION")
val context = SSLContext.getInstance(tlsVersion ?: DEFAULT_TLS_VERSION)
val managers = arrayOf(certificateManager)
context.init(null, managers, null)
val factory = context.socketFactory
......
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2020 Janne Mareike Koschinski
* Copyright (c) 2020 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.util.nio
import de.kuschku.libquassel.util.nio.WrappedChannel.Companion.selectBestTlsVersion
import org.junit.Assert.assertEquals
import org.junit.Test
class WrappedChannelTest {
@Test
fun doesNotSelectOutdatedTlsVersions() {
assertEquals(null, selectBestTlsVersion(arrayOf(
"SSLv3", "TLSv1", "TLSv1.0", "TLSv1.1"
)))
}
@Test
fun rejectsNonTlsProtocols() {
assertEquals(null, selectBestTlsVersion(arrayOf(
"SSLv3", "UberSecurityProtocol5"
)))
}
@Test
fun selectsLatestTlsVersion() {
assertEquals("TLSv1.2", selectBestTlsVersion(arrayOf(
"SSLv3", "TLSv1", "TLSv1.0", "TLSv1.1", "TLSv1.2", "UberSecurityProtocol5"
)))
assertEquals("TLSv1.3", selectBestTlsVersion(arrayOf(
"SSLv3", "TLSv1", "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3", "UberSecurityProtocol5"
)))
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment