Skip to content

Commit

Permalink
add Conscrypt provider, SSL classes, fix notifications and change fil…
Browse files Browse the repository at this point in the history
…es item layout
  • Loading branch information
tsynik committed Oct 24, 2024
1 parent 2e15a5c commit 23b1c31
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 59 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.android.builder.core.BuilderConstants

plugins {
id("com.android.application")
id("com.google.devtools.ksp")
Expand Down Expand Up @@ -89,6 +91,7 @@ dependencies {
implementation 'com.google.firebase:firebase-crashlytics:18.2.5'
// SSL
implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
// http client/parser
def jsoupVersion = '1.16.1' // 1.16.2, 1.17.1, 1.17.2 thorow java.lang.VerifyError on api19
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ru.yourok.torrserve.server.local.services

import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
Expand All @@ -15,6 +16,7 @@ import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.core.graphics.drawable.IconCompat
import ru.yourok.torrserve.R
import ru.yourok.torrserve.app.App
import ru.yourok.torrserve.atv.Utils
Expand Down Expand Up @@ -95,6 +97,7 @@ class NotificationTS : Service() {
return mBinder
}

@SuppressLint("InlinedApi")
private fun createNotification() {
synchronized(lock) {
val exitIntent = Intent(this, TorrService::class.java)
Expand All @@ -120,27 +123,30 @@ class NotificationTS : Service() {
.createNotificationChannel(channel)
}
val accessibilityNote = if (Accessibility.isEnabledService(App.context)) this.getText(R.string.accessibility_note) else ""
if (builder == null)
if (builder == null) {
builder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ts_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.stat_running))
.setAutoCancel(false)
.setOngoing(true)
.setContentIntent(contentPendingIntent)
.setStyle(NotificationCompat.BigTextStyle().bigText(accessibilityNote))
.addAction(
android.R.drawable.ic_delete,
this.getText(R.string.exit),
android.R.drawable.ic_menu_close_clear_cancel,
this.getText(R.string.exit).toString().uppercase(),
exitPendingIntent
)
else
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
builder?.setSmallIcon(IconCompat.createWithResource(this, R.drawable.ts_icon))
else
builder?.setSmallIcon(R.drawable.ts_icon_white)
} else
builder?.setStyle(NotificationCompat.BigTextStyle().bigText(accessibilityNote))

if (Utils.isAmazonTV)
builder?.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_notification))

builder?.let {
ServiceCompat.startForeground(this, notificationId, it.build(), FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
ServiceCompat.startForeground(this, notificationId, it.build(), FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ class ServerUpdateFragment : TSFragment() {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
App.toast(App.context.getString(R.string.warn_error_download_server) + ": " + e.message)
App.toast(App.context.getString(R.string.warn_error_download_server) + ": " + e.message, true)
btn.isEnabled = true
}
hideProgress()
}
Expand Down Expand Up @@ -124,7 +125,8 @@ class ServerUpdateFragment : TSFragment() {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
App.toast(App.context.getString(R.string.error_download_ffprobe) + ": " + e.message)
App.toast(App.context.getString(R.string.error_download_ffprobe) + ": " + e.message, true)
btn.isEnabled = true
}
hideProgress()
}
Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/ru/yourok/torrserve/utils/AllTrustManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.yourok.torrserve.utils

import android.annotation.SuppressLint
import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager

@SuppressLint("CustomX509TrustManager")
class AllTrustManager : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
// Perform no check whatsoever on the validity of the SSL certificate
}

@SuppressLint("TrustAllX509TrustManager")
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
// Perform no check whatsoever on the validity of the SSL certificate
}

override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
9 changes: 8 additions & 1 deletion app/src/main/java/ru/yourok/torrserve/utils/Http.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import java.net.HttpURLConnection.HTTP_OK
import java.net.HttpURLConnection.HTTP_PARTIAL
import java.net.HttpURLConnection.HTTP_SEE_OTHER
import java.net.URL
import java.security.GeneralSecurityException
import java.util.Locale
import java.util.zip.GZIPInputStream
import javax.net.ssl.HostnameVerifier
Expand Down Expand Up @@ -54,7 +55,13 @@ class Http(url: Uri) {
true // Just allow them all
}
HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames)
HttpsURLConnection.setDefaultSSLSocketFactory(Net.insecureTlsSocketFactory())
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
try {
// Only TLSv1.2 and TLSv1.3 protocol available and trust all certs (insecure).
HttpsURLConnection.setDefaultSSLSocketFactory(TlsSocketFactory())
} catch (_: GeneralSecurityException) {
}
}
}
else
Expand Down
43 changes: 4 additions & 39 deletions app/src/main/java/ru/yourok/torrserve/utils/Net.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ru.yourok.torrserve.utils

import android.annotation.SuppressLint
import android.net.Uri
import info.guardianproject.netcipher.client.TlsOnlySocketFactory
import org.jsoup.Connection
import org.jsoup.Jsoup
import ru.yourok.torrserve.atv.Utils.isBrokenTCL
Expand Down Expand Up @@ -45,7 +44,7 @@ object Net {
.ignoreContentType(true)
.method(Connection.Method.POST)
if (!isBrokenTCL)
req.sslSocketFactory(insecureTlsSocketFactory())
req.sslSocketFactory(TlsSocketFactory())
if (save)
req.data("save", "true")
req.data("title", title)
Expand All @@ -69,7 +68,7 @@ object Net {
.method(Connection.Method.POST)
.maxBodySize(0) // The default maximum is 2MB, 0 = unlimited body
if (!isBrokenTCL)
conn.sslSocketFactory(insecureTlsSocketFactory())
conn.sslSocketFactory(TlsSocketFactory())

val auth = getAuthB64()
if (auth.isNotEmpty())
Expand Down Expand Up @@ -98,7 +97,7 @@ object Net {
.ignoreContentType(true)
.timeout(duration)
if (!isBrokenTCL)
conn.sslSocketFactory(insecureTlsSocketFactory())
conn.sslSocketFactory(TlsSocketFactory())

val auth = getAuthB64()
if (auth.isNotEmpty())
Expand Down Expand Up @@ -134,7 +133,7 @@ object Net {
.ignoreContentType(true)
.timeout(duration)
if (!isBrokenTCL)
conn.sslSocketFactory(insecureTlsSocketFactory())
conn.sslSocketFactory(TlsSocketFactory())

val response = conn.execute()

Expand All @@ -153,40 +152,6 @@ object Net {
}
}

// https://stackoverflow.com/questions/26649389/how-to-disable-sslv3-in-android-for-httpsurlconnection
fun insecureTlsSocketFactory(): SSLSocketFactory {
val trustAllCerts = arrayOf<TrustManager>(@SuppressLint("CustomX509TrustManager")
object : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}

@SuppressLint("TrustAllX509TrustManager")
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
}

override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
})

try {
val sslContext = SSLContext.getInstance("TLSv1.2")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
return TlsOnlySocketFactory(sslContext.socketFactory)
} catch (e: Exception) {
when (e) {
is RuntimeException, is KeyManagementException -> {
throw RuntimeException("Failed to create a SSL socket factory", e)
}

else -> throw e
}
}
}

fun isValidPublicIp4(ip: String?): Boolean {
val address: Inet4Address? = try {
InetAddress.getByName(ip) as? Inet4Address
Expand Down
98 changes: 98 additions & 0 deletions app/src/main/java/ru/yourok/torrserve/utils/TlsSocketFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package ru.yourok.torrserve.utils

import org.conscrypt.Conscrypt
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.security.Provider
import java.security.Security
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager

class TlsSocketFactory : SSLSocketFactory {
private val enabledProtocols: Array<String>
private val delegate: SSLSocketFactory

constructor(enabledProtocols: Array<String>) {
this.enabledProtocols = enabledProtocols
this.delegate = socketFactory
}

constructor() {
this.enabledProtocols = TLS_RESTRICTED
this.delegate = socketFactory
}

constructor(base: SSLSocketFactory) {
this.enabledProtocols = TLS_RESTRICTED
this.delegate = base
}

override fun getDefaultCipherSuites(): Array<String> {
return delegate.defaultCipherSuites
}

override fun getSupportedCipherSuites(): Array<String> {
return delegate.supportedCipherSuites
}

@Throws(IOException::class)
override fun createSocket(): Socket {
return patch(delegate.createSocket())
}

@Throws(IOException::class)
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
return patch(delegate.createSocket(s, host, port, autoClose))
}

@Throws(IOException::class)
override fun createSocket(host: String, port: Int): Socket {
return patch(delegate.createSocket(host, port))
}

@Throws(IOException::class)
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
return patch(delegate.createSocket(host, port, localHost, localPort))
}

@Throws(IOException::class)
override fun createSocket(host: InetAddress, port: Int): Socket {
return patch(delegate.createSocket(host, port))
}

@Throws(IOException::class)
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
return patch(delegate.createSocket(address, port, localAddress, localPort))
}

private fun patch(s: Socket): Socket {
if (s is SSLSocket) {
s.enabledProtocols = enabledProtocols
}
return s
}

companion object {
private var conscrypt: Provider? = null
val TLS_MODERN: Array<String> = arrayOf("TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3")
val TLS_RESTRICTED: Array<String> = arrayOf("TLSv1.2", "TLSv1.3")

@get:Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
private val socketFactory: SSLSocketFactory
get() {
if (conscrypt == null) {
conscrypt = Conscrypt.newProvider()
// Add as provider
Security.insertProviderAt(conscrypt, 1)
}
val context = SSLContext.getInstance("TLS", conscrypt)
context.init(null, arrayOf<TrustManager>(AllTrustManager()), null)
return context.socketFactory
}
}
}
Binary file added app/src/main/res/drawable-hdpi/ts_icon_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 9 additions & 7 deletions app/src/main/res/layout/torrent_files_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,30 @@
android:id="@+id/ivViewed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center|end"
android:layout_gravity="end|center"
android:layout_marginTop="-3dp"
android:layout_marginEnd="3dp"
android:layout_marginRight="3dp"
app:srcCompat="@drawable/eye_show" />

<TextView
android:id="@+id/tvFileSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/tvFileName"
android:layout_above="@+id/tvExt"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="center|end"
android:layout_gravity="end|center"
android:layout_margin="3dp"
android:paddingStart="5dp"
android:paddingLeft="5dp"
android:paddingTop="1dp"
android:paddingEnd="5dp"
android:paddingRight="5dp"
android:paddingBottom="2dp"
android:paddingBottom="1dp"
android:textSize="12sp"
android:textStyle="bold"
tools:text="5 MB" />
Expand All @@ -54,10 +56,10 @@
android:id="@+id/tvExt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tvFileName"
android:layout_alignBottom="@+id/tvFileName"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="center"
android:layout_gravity="end|bottom"
android:layout_margin="3dp"
android:paddingStart="5dp"
android:paddingLeft="5dp"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
<string name="not_loaded_exit_hint">The local TorrServer is not running. Try clicking \"Exit\" in the Main Menu and launch the application again, or restart your device. If nothing changes after this, update TorrServer in the \"Update\" section.</string>
<string name="not_loaded_select_hint">Select an available TorrServer from the list, or specify the address of a new one.</string>
<string name="copy_to_clipboard">Copied to clipboard</string>
<string name="stat_running">Service is running</string>
<string name="stat_running">TorrServer is running</string>
<string name="server_not_responding">TorrServer is not responding</string>
<string name="done_sending_settings">Settings saved</string>

Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.5.0'
classpath 'com.android.tools.build:gradle:8.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}"
classpath 'com.google.gms:google-services:4.4.2'
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.2'
}
}

Expand Down

0 comments on commit 23b1c31

Please sign in to comment.