diff options
Diffstat (limited to 'app')
9 files changed, 55 insertions, 95 deletions
diff --git a/app/build.gradle.kts b/app/build.gradle.kts index abd26932..85144bbc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,7 +47,6 @@ dependencies { implementation(projects.format.common) implementation(projects.passgen.diceware) implementation(projects.passgen.random) - implementation(projects.ssh) implementation(projects.ui.compose) implementation(libs.androidx.activity.ktx) implementation(libs.androidx.activity.compose) diff --git a/app/src/main/java/app/passwordstore/injection/ssh/SSHKeyManagerModule.kt b/app/src/main/java/app/passwordstore/injection/ssh/SSHKeyManagerModule.kt deleted file mode 100644 index c5abd487..00000000 --- a/app/src/main/java/app/passwordstore/injection/ssh/SSHKeyManagerModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package app.passwordstore.injection.ssh - -import android.content.Context -import app.passwordstore.ssh.SSHKeyManager -import dagger.Module -import dagger.Provides -import dagger.Reusable -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -object SSHKeyManagerModule { - - @Provides - @Reusable - fun provideSSHKeyManager(@ApplicationContext context: Context): SSHKeyManager { - return SSHKeyManager(context) - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt b/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt index 462924ac..c6c30b88 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt @@ -15,7 +15,6 @@ import androidx.fragment.app.FragmentActivity import app.passwordstore.R import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.injection.prefs.GitPreferences -import app.passwordstore.ssh.SSHKeyManager import app.passwordstore.ui.git.config.GitConfigActivity import app.passwordstore.ui.git.config.GitServerConfigActivity import app.passwordstore.ui.proxy.ProxySelectorActivity @@ -27,6 +26,7 @@ import app.passwordstore.util.extensions.launchActivity import app.passwordstore.util.extensions.sharedPrefs import app.passwordstore.util.extensions.snackbar import app.passwordstore.util.extensions.unsafeLazy +import app.passwordstore.util.git.sshj.SshKey import app.passwordstore.util.settings.GitSettings import app.passwordstore.util.settings.PreferenceKeys import com.github.michaelbull.result.onFailure @@ -42,13 +42,11 @@ import de.Maxr1998.modernpreferences.helpers.onClick import de.Maxr1998.modernpreferences.helpers.pref import de.Maxr1998.modernpreferences.helpers.switch -class RepositorySettings( - private val activity: FragmentActivity, - private val sshKeyManager: SSHKeyManager, -) : SettingsProvider { +class RepositorySettings(private val activity: FragmentActivity) : SettingsProvider { + private val generateSshKey = activity.registerForActivityResult(StartActivityForResult()) { - showSshKeyPref?.visible = sshKeyManager.canShowPublicKey() + showSshKeyPref?.visible = SshKey.canShowSshPublicKey } private val hiltEntryPoint by unsafeLazy { @@ -113,7 +111,7 @@ class RepositorySettings( showSshKeyPref = pref(PreferenceKeys.SSH_SEE_KEY) { titleRes = R.string.pref_ssh_see_key_title - visible = PasswordRepository.isGitRepo() && sshKeyManager.canShowPublicKey() + visible = PasswordRepository.isGitRepo() && SshKey.canShowSshPublicKey onClick { ShowSshKeyFragment().show(activity.supportFragmentManager, "public_key") true diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt index 697d0156..30e2b1b0 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt @@ -11,24 +11,19 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.os.BundleCompat import app.passwordstore.R import app.passwordstore.databinding.ActivityPreferenceRecyclerviewBinding -import app.passwordstore.ssh.SSHKeyManager import app.passwordstore.util.extensions.viewBinding import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint import de.Maxr1998.modernpreferences.Preference import de.Maxr1998.modernpreferences.PreferencesAdapter import de.Maxr1998.modernpreferences.helpers.screen import de.Maxr1998.modernpreferences.helpers.subScreen -import javax.inject.Inject -@AndroidEntryPoint class SettingsActivity : AppCompatActivity() { - @Inject lateinit var sshKeyManager: SSHKeyManager - private lateinit var repositorySettings: RepositorySettings private val miscSettings = MiscSettings(this) private val autofillSettings = AutofillSettings(this) private val passwordSettings = PasswordSettings(this) + private val repositorySettings = RepositorySettings(this) private val generalSettings = GeneralSettings(this) private val pgpSettings = PGPSettings(this) @@ -40,7 +35,6 @@ class SettingsActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(binding.root) Preference.Config.dialogBuilderFactory = { context -> MaterialAlertDialogBuilder(context) } - repositorySettings = RepositorySettings(this, sshKeyManager) val screen = screen(this) { subScreen { diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt index 4f52b540..a42d6aa1 100644 --- a/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt +++ b/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt @@ -9,19 +9,14 @@ import android.content.Intent import android.os.Bundle import androidx.fragment.app.DialogFragment import app.passwordstore.R -import app.passwordstore.ssh.SSHKeyManager +import app.passwordstore.util.git.sshj.SshKey import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject -@AndroidEntryPoint class ShowSshKeyFragment : DialogFragment() { - @Inject lateinit var sshKeyManager: SSHKeyManager - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val activity = requireActivity() - val publicKey = sshKeyManager.publicKey() + val publicKey = SshKey.sshPublicKey return MaterialAlertDialogBuilder(requireActivity()).run { setMessage(getString(R.string.ssh_keygen_message, publicKey)) setTitle(R.string.your_public_key) diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt index 34749e4c..68d5a40c 100644 --- a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt @@ -17,13 +17,12 @@ import androidx.lifecycle.lifecycleScope import app.passwordstore.R import app.passwordstore.databinding.ActivitySshKeygenBinding import app.passwordstore.injection.prefs.GitPreferences -import app.passwordstore.ssh.SSHKeyAlgorithm -import app.passwordstore.ssh.SSHKeyManager import app.passwordstore.util.auth.BiometricAuthenticator import app.passwordstore.util.auth.BiometricAuthenticator.Result import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.extensions.keyguardManager import app.passwordstore.util.extensions.viewBinding +import app.passwordstore.util.git.sshj.SshKey import com.github.michaelbull.result.fold import com.github.michaelbull.result.runCatching import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -34,13 +33,24 @@ import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +private enum class KeyGenType(val generateKey: suspend (requireAuthentication: Boolean) -> Unit) { + Rsa({ requireAuthentication -> + SshKey.generateKeystoreNativeKey(SshKey.Algorithm.Rsa, requireAuthentication) + }), + Ecdsa({ requireAuthentication -> + SshKey.generateKeystoreNativeKey(SshKey.Algorithm.Ecdsa, requireAuthentication) + }), + Ed25519({ requireAuthentication -> + SshKey.generateKeystoreWrappedEd25519Key(requireAuthentication) + }), +} + @AndroidEntryPoint class SshKeyGenActivity : AppCompatActivity() { - private var sshKeyAlgorithm = SSHKeyAlgorithm.ECDSA + private var keyGenType = KeyGenType.Ecdsa private val binding by viewBinding(ActivitySshKeygenBinding::inflate) @GitPreferences @Inject lateinit var gitPrefs: SharedPreferences - @Inject lateinit var sshKeyManager: SSHKeyManager @Inject lateinit var dispatcherProvider: DispatcherProvider override fun onCreate(savedInstanceState: Bundle?) { @@ -49,7 +59,7 @@ class SshKeyGenActivity : AppCompatActivity() { supportActionBar?.setDisplayHomeAsUpEnabled(true) with(binding) { generate.setOnClickListener { - if (sshKeyManager.keyExists()) { + if (SshKey.exists) { MaterialAlertDialogBuilder(this@SshKeyGenActivity).run { setTitle(R.string.ssh_keygen_existing_title) setMessage(R.string.ssh_keygen_existing_message) @@ -70,18 +80,18 @@ class SshKeyGenActivity : AppCompatActivity() { keyTypeExplanation.setText(R.string.ssh_keygen_explanation_ecdsa) keyTypeGroup.addOnButtonCheckedListener { _, checkedId, isChecked -> if (isChecked) { - sshKeyAlgorithm = + keyGenType = when (checkedId) { - R.id.key_type_ed25519 -> SSHKeyAlgorithm.ED25519 - R.id.key_type_ecdsa -> SSHKeyAlgorithm.ECDSA - R.id.key_type_rsa -> SSHKeyAlgorithm.RSA + R.id.key_type_ed25519 -> KeyGenType.Ed25519 + R.id.key_type_ecdsa -> KeyGenType.Ecdsa + R.id.key_type_rsa -> KeyGenType.Rsa else -> throw IllegalStateException("Impossible key type selection") } keyTypeExplanation.setText( - when (sshKeyAlgorithm) { - SSHKeyAlgorithm.ED25519 -> R.string.ssh_keygen_explanation_ed25519 - SSHKeyAlgorithm.ECDSA -> R.string.ssh_keygen_explanation_ecdsa - SSHKeyAlgorithm.RSA -> R.string.ssh_keygen_explanation_rsa + when (keyGenType) { + KeyGenType.Ed25519 -> R.string.ssh_keygen_explanation_ed25519 + KeyGenType.Ecdsa -> R.string.ssh_keygen_explanation_ecdsa + KeyGenType.Rsa -> R.string.ssh_keygen_explanation_rsa } ) } @@ -127,10 +137,9 @@ class SshKeyGenActivity : AppCompatActivity() { if (result !is Result.Success) throw UserNotAuthenticatedException(getString(R.string.biometric_auth_generic_failure)) } - sshKeyManager.generateKey(sshKeyAlgorithm, requireAuthentication) + keyGenType.generateKey(requireAuthentication) } } - // Check if we still need this gitPrefs.edit { remove("ssh_key_local_passphrase") } binding.generate.apply { text = getString(R.string.ssh_keygen_generate) diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt index a5d276ae..99b3bf3f 100644 --- a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt @@ -10,21 +10,14 @@ import android.os.Bundle import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope import app.passwordstore.R -import app.passwordstore.ssh.SSHKeyManager +import app.passwordstore.util.git.sshj.SshKey import com.github.michaelbull.result.onFailure import com.github.michaelbull.result.runCatching import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject -import kotlinx.coroutines.launch -@AndroidEntryPoint class SshKeyImportActivity : AppCompatActivity() { - @Inject lateinit var sshKeyManager: SSHKeyManager - private val sshKeyImportAction = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? -> if (uri == null) { @@ -32,17 +25,15 @@ class SshKeyImportActivity : AppCompatActivity() { return@registerForActivityResult } runCatching { - lifecycleScope.launch { - sshKeyManager.importKey(uri) - Toast.makeText( - this@SshKeyImportActivity, - resources.getString(R.string.ssh_key_success_dialog_title), - Toast.LENGTH_LONG - ) - .show() - setResult(RESULT_OK) - finish() - } + SshKey.import(uri) + Toast.makeText( + this, + resources.getString(R.string.ssh_key_success_dialog_title), + Toast.LENGTH_LONG + ) + .show() + setResult(RESULT_OK) + finish() } .onFailure { e -> MaterialAlertDialogBuilder(this) @@ -55,8 +46,8 @@ class SshKeyImportActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (sshKeyManager.keyExists()) { - MaterialAlertDialogBuilder(this@SshKeyImportActivity).run { + if (SshKey.exists) { + MaterialAlertDialogBuilder(this).run { setTitle(R.string.ssh_keygen_existing_title) setMessage(R.string.ssh_keygen_existing_message) setPositiveButton(R.string.ssh_keygen_existing_replace) { _, _ -> importSshKey() } diff --git a/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt b/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt index 199fde94..89194b79 100644 --- a/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt +++ b/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt @@ -9,7 +9,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity import app.passwordstore.R import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.ssh.SSHKeyManager import app.passwordstore.ui.sshkeygen.SshKeyGenActivity import app.passwordstore.ui.sshkeygen.SshKeyImportActivity import app.passwordstore.util.auth.BiometricAuthenticator @@ -21,6 +20,7 @@ import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.extensions.launchActivity import app.passwordstore.util.git.GitCommandExecutor import app.passwordstore.util.git.sshj.SshAuthMethod +import app.passwordstore.util.git.sshj.SshKey import app.passwordstore.util.git.sshj.SshjSessionFactory import app.passwordstore.util.settings.AuthMode import com.github.michaelbull.result.Err @@ -67,11 +67,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { private val hostKeyFile = callingActivity.filesDir.resolve(".host_key") private var sshSessionFactory: SshjSessionFactory? = null private val hiltEntryPoint = - EntryPointAccessors.fromApplication( - callingActivity.applicationContext, - GitOperationEntryPoint::class.java - ) - private val sshKeyManager = hiltEntryPoint.sshKeyManager() + EntryPointAccessors.fromApplication<GitOperationEntryPoint>(callingActivity) protected val repository = PasswordRepository.repository!! protected val git = Git(repository) private val authActivity @@ -121,7 +117,8 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { authMethod: SshAuthMethod, credentialsProvider: CredentialsProvider? = null ) { - sshSessionFactory = SshjSessionFactory(authMethod, hostKeyFile, sshKeyManager, hiltEntryPoint.dispatcherProvider()) + sshSessionFactory = + SshjSessionFactory(authMethod, hostKeyFile, hiltEntryPoint.dispatcherProvider()) commands.filterIsInstance<TransportCommand<*, *>>().forEach { command -> command.setTransportConfigCallback { transport: Transport -> (transport as? SshTransport)?.sshSessionFactory = sshSessionFactory @@ -169,8 +166,8 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { suspend fun executeAfterAuthentication(authMode: AuthMode): Result<Unit, Throwable> { when (authMode) { AuthMode.SshKey -> - if (sshKeyManager.keyExists()) { - if (sshKeyManager.needsAuthentication()) { + if (SshKey.exists) { + if (SshKey.mustAuthenticate) { val result = withContext(hiltEntryPoint.dispatcherProvider().main()) { suspendCoroutine { cont -> @@ -247,8 +244,6 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { @EntryPoint @InstallIn(SingletonComponent::class) interface GitOperationEntryPoint { - fun sshKeyManager(): SSHKeyManager - fun dispatcherProvider(): DispatcherProvider } } diff --git a/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt b/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt index 86416cd6..5e11a636 100644 --- a/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt +++ b/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt @@ -6,7 +6,6 @@ package app.passwordstore.util.git.sshj import android.util.Base64 import androidx.appcompat.app.AppCompatActivity -import app.passwordstore.ssh.SSHKeyManager import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.git.operation.CredentialFinder import app.passwordstore.util.settings.AuthMode @@ -71,7 +70,6 @@ abstract class InteractivePasswordFinder(private val dispatcherProvider: Dispatc class SshjSessionFactory( private val authMethod: SshAuthMethod, private val hostKeyFile: File, - private val sshKeyManager: SSHKeyManager, private val dispatcherProvider: DispatcherProvider, ) : SshSessionFactory() { @@ -84,7 +82,7 @@ class SshjSessionFactory( tms: Int ): RemoteSession { return currentSession - ?: SshjSession(uri, uri.user, authMethod, hostKeyFile, dispatcherProvider, sshKeyManager).connect().also { + ?: SshjSession(uri, uri.user, authMethod, hostKeyFile, dispatcherProvider).connect().also { logcat { "New SSH connection created" } currentSession = it } @@ -129,7 +127,6 @@ private class SshjSession( private val authMethod: SshAuthMethod, private val hostKeyFile: File, private val dispatcherProvider: DispatcherProvider, - private val sshKeyManager: SSHKeyManager, ) : RemoteSession { private lateinit var ssh: SSHClient @@ -165,7 +162,10 @@ private class SshjSession( is SshAuthMethod.SshKey -> { val pubkeyAuth = AuthPublickey( - sshKeyManager.keyProvider(ssh, CredentialFinder(authMethod.activity, AuthMode.SshKey, dispatcherProvider)) + SshKey.provide( + ssh, + CredentialFinder(authMethod.activity, AuthMode.SshKey, dispatcherProvider) + ) ) ssh.auth(username, pubkeyAuth, passwordAuth) } |