Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/AndroidProjectSystem.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/MasterHttpRelayVPN-RUST.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1,870 changes: 1,870 additions & 0 deletions .idea/caches/deviceStreaming.xml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions .idea/deviceManager.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/markdown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/migrations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/runConfigurations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
id("com.google.devtools.ksp")
id("com.google.dagger.hilt.android")
}

android {
Expand Down Expand Up @@ -129,6 +131,10 @@ dependencies {
// from MhrvApp.onCreate without touching every composable.
implementation("androidx.appcompat:appcompat:1.7.0")

implementation("com.google.dagger:hilt-android:2.57.1")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
ksp("com.google.dagger:hilt-android-compiler:2.57.1")

// Compose UI.
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
Expand Down
2 changes: 1 addition & 1 deletion android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# These methods are declared `external` in Kotlin — the JNI linker looks
# them up by exact name at load time.
-keep class com.therealaleph.mhrv.Native { *; }
-keep class com.therealaleph.mhrv.MhrvVpnService { *; }
-keep class com.therealaleph.mhrv.data.MhrvVpnService { *; }
4 changes: 2 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
tools:targetApi="34">

<activity
android:name=".MainActivity"
android:name=".ui.MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@style/Theme.Mhrv">
Expand Down Expand Up @@ -88,7 +88,7 @@
and route through us.
-->
<service
android:name=".MhrvVpnService"
android:name=".data.MhrvVpnService"
android:exported="false"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse">
Expand Down
7 changes: 6 additions & 1 deletion android/app/src/main/java/com/therealaleph/mhrv/MhrvApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import android.app.Application
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import com.therealaleph.mhrv.data.ConfigStore
import com.therealaleph.mhrv.data.UiLang
import dagger.hilt.android.HiltAndroidApp

/**
* Application-level setup. The only job here right now is to catch
Expand All @@ -19,6 +22,7 @@ import androidx.core.os.LocaleListCompat
* including the tun2proxy worker and the log-drain coroutine —
* important because those don't have an activity in scope.
*/
@HiltAndroidApp
class MhrvApp : Application() {
override fun onCreate() {
super.onCreate()
Expand Down Expand Up @@ -53,7 +57,8 @@ class MhrvApp : Application() {
"uncaught on thread=${thread.name} (id=${thread.id}): ${throwable.message}",
throwable,
)
} catch (_: Throwable) { }
} catch (_: Throwable) {
}
// Let the default handler still terminate the process and
// show the system "app closed" dialog — we just wanted to
// get a log line out the door first.
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/java/com/therealaleph/mhrv/Native.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@ object Native {
* @return 0 on normal shutdown, negative on error. BLOCKS.
*/
external fun runTun2proxy(cliArgs: String, tunMtu: Int): Int
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.therealaleph.mhrv.data

data class CaCertInfo(
val fingerprint: ByteArray,
val fingerprintHex: String,
val subjectCn: String?
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.therealaleph.mhrv
package com.therealaleph.mhrv.data

import android.content.ContentValues
import android.content.Context
Expand All @@ -9,10 +9,12 @@ import android.provider.MediaStore
import android.provider.Settings
import android.security.KeyChain
import android.util.Base64
import com.therealaleph.mhrv.Native
import java.io.File
import java.security.KeyStore
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate

/**
* Helpers for the MITM-CA install UX.
Expand Down Expand Up @@ -195,7 +197,7 @@ object CaInstall {
val der = readDer(ctx) ?: return null
return try {
val cf = CertificateFactory.getInstance("X.509")
val cert = cf.generateCertificate(der.inputStream()) as java.security.cert.X509Certificate
val cert = cf.generateCertificate(der.inputStream()) as X509Certificate
val dn = cert.subjectX500Principal.name // RFC 2253, CN=foo,O=bar
Regex("""CN=([^,]+)""").find(dn)?.groupValues?.get(1)
} catch (_: Throwable) { null }
Expand Down Expand Up @@ -230,4 +232,4 @@ object CaInstall {
if (body.isEmpty()) return null
return try { Base64.decode(body, Base64.DEFAULT) } catch (_: Throwable) { null }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.therealaleph.mhrv.data

interface CaRepository {
suspend fun prepareCaDialogData(): CaCertInfo?
suspend fun saveToDownloads(): String?
suspend fun checkInstalled(fingerprint: ByteArray): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.therealaleph.mhrv.data

import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject

class CaRepositoryImpl @Inject constructor(@ApplicationContext private val ctx: Context) :
CaRepository {

override suspend fun prepareCaDialogData(): CaCertInfo? {
return withContext(Dispatchers.IO) {
val exported = CaInstall.export(ctx)
if (!exported) return@withContext null
val fingerprint = CaInstall.fingerprint(ctx) ?: return@withContext null
val hex = CaInstall.fingerprintHex(fingerprint)
val subjectCn = CaInstall.subjectCn(ctx)
return@withContext CaCertInfo(
fingerprint, hex, subjectCn
)
}
}

override suspend fun saveToDownloads(): String? {
return withContext(Dispatchers.IO) {
return@withContext CaInstall.saveToDownloads(ctx)
}
}

override suspend fun checkInstalled(fingerprint: ByteArray): Boolean {
return withContext(Dispatchers.IO) {
return@withContext CaInstall.isInstalled(fingerprint)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.therealaleph.mhrv.data

import android.content.Context
import kotlinx.coroutines.flow.StateFlow

interface ConfigRepository {
val config: StateFlow<MhrvConfig>
suspend fun saveConfig(cfg: MhrvConfig)
suspend fun loadConfig(): MhrvConfig
fun encodeConfig(cfg: MhrvConfig): String
fun decodeConfig(string: String): MhrvConfig?
}
Loading
Loading