diff options
| author | Fabian Henneke <FabianHenneke@users.noreply.github.com> | 2020-05-28 06:27:30 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-28 09:57:30 +0530 |
| commit | 97911c587754a7c11e594004b77c4ac53af8a468 (patch) | |
| tree | 680453c63fc3873139a3937268f73ec6985cf03a /app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt | |
| parent | fea82fed47d87138a9a24eb0358fc9a917474dc7 (diff) | |
| download | HardenedPass-97911c587754a7c11e594004b77c4ac53af8a468.tar.gz HardenedPass-97911c587754a7c11e594004b77c4ac53af8a468.zip | |
Use SSHJ for SSH public key authentication (#801)
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt')
| -rw-r--r-- | app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt | 160 |
1 files changed, 60 insertions, 100 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt index 712d1bc5..bfcf2f11 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt @@ -13,15 +13,14 @@ import androidx.preference.PreferenceManager import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputEditText -import com.jcraft.jsch.JSch -import com.jcraft.jsch.JSchException -import com.jcraft.jsch.KeyPair import com.zeapo.pwdstore.R import com.zeapo.pwdstore.UserPreference import com.zeapo.pwdstore.git.config.ConnectionMode import com.zeapo.pwdstore.git.config.GitConfigSessionFactory +import com.zeapo.pwdstore.git.config.InteractivePasswordFinder import com.zeapo.pwdstore.git.config.SshApiSessionFactory -import com.zeapo.pwdstore.git.config.SshConfigSessionFactory +import com.zeapo.pwdstore.git.config.SshAuthData +import com.zeapo.pwdstore.git.config.SshjSessionFactory import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.getEncryptedPrefs import com.zeapo.pwdstore.utils.requestInputFocusOnView @@ -30,6 +29,7 @@ import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.transport.SshSessionFactory import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider import java.io.File +import kotlin.coroutines.resume /** * Creates a new git operation @@ -42,6 +42,8 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit protected val repository: Repository? = PasswordRepository.getRepository(fileDir) internal var provider: UsernamePasswordCredentialsProvider? = null internal var command: GitCommand<*>? = null + private val sshKeyFile = callingActivity.filesDir.resolve(".ssh_key") + private val hostKeyFile = callingActivity.filesDir.resolve(".host_key") /** * Sets the authentication using user/pwd scheme @@ -56,16 +58,8 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit return this } - /** - * Sets the authentication using ssh-key scheme - * - * @param sshKey the ssh-key file - * @param username the username - * @param passphrase the passphrase - * @return the current object - */ - internal open fun setAuthentication(sshKey: File, username: String, passphrase: String): GitOperation { - val sessionFactory = SshConfigSessionFactory(sshKey.absolutePath, username, passphrase) + private fun withPublicKeyAuthentication(username: String, passphraseFinder: InteractivePasswordFinder): GitOperation { + val sessionFactory = SshjSessionFactory(username, SshAuthData.PublicKeyFile(sshKeyFile, passphraseFinder), hostKeyFile) SshSessionFactory.setInstance(sessionFactory) this.provider = null return this @@ -93,38 +87,17 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit * * @param connectionMode the server-connection mode * @param username the username - * @param sshKey the ssh-key file to use in ssh-key connection mode * @param identity the api identity to use for auth in OpenKeychain connection mode */ fun executeAfterAuthentication( connectionMode: ConnectionMode, username: String, - sshKey: File?, identity: SshApiSessionFactory.ApiIdentity? ) { - executeAfterAuthentication(connectionMode, username, sshKey, identity, false) - } - - /** - * Executes the GitCommand in an async task after creating the authentication - * - * @param connectionMode the server-connection mode - * @param username the username - * @param sshKey the ssh-key file to use in ssh-key connection mode - * @param identity the api identity to use for auth in OpenKeychain connection mode - * @param showError show the passphrase edit text in red - */ - private fun executeAfterAuthentication( - connectionMode: ConnectionMode, - username: String, - sshKey: File?, - identity: SshApiSessionFactory.ApiIdentity?, - showError: Boolean - ) { val encryptedSettings = callingActivity.applicationContext.getEncryptedPrefs("git_operation") when (connectionMode) { ConnectionMode.SshKey -> { - if (sshKey == null || !sshKey.exists()) { + if (!sshKeyFile.exists()) { MaterialAlertDialogBuilder(callingActivity) .setMessage(callingActivity.resources.getString(R.string.ssh_preferences_dialog_text)) .setTitle(callingActivity.resources.getString(R.string.ssh_preferences_dialog_title)) @@ -156,65 +129,47 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit callingActivity.finish() }.show() } else { - val layoutInflater = LayoutInflater.from(callingActivity) - @SuppressLint("InflateParams") val dialogView = layoutInflater.inflate(R.layout.git_passphrase_layout, null) - val passphrase = dialogView.findViewById<TextInputEditText>(R.id.git_auth_passphrase) - val sshKeyPassphrase = encryptedSettings.getString("ssh_key_local_passphrase", null) - if (showError) { - passphrase.error = callingActivity.resources.getString(R.string.git_operation_wrong_passphrase) - } - val jsch = JSch() - try { - val keyPair = KeyPair.load(jsch, callingActivity.filesDir.toString() + "/.ssh_key") + withPublicKeyAuthentication(username, InteractivePasswordFinder { cont, isRetry -> + val storedPassphrase = encryptedSettings.getString("ssh_key_local_passphrase", null) + if (isRetry) + encryptedSettings.edit { putString("ssh_key_local_passphrase", null) } + if (storedPassphrase.isNullOrEmpty()) { + val layoutInflater = LayoutInflater.from(callingActivity) - if (keyPair.isEncrypted) { - if (sshKeyPassphrase != null && sshKeyPassphrase.isNotEmpty()) { - if (keyPair.decrypt(sshKeyPassphrase)) { - // Authenticate using the ssh-key and then execute the command - setAuthentication(sshKey, username, sshKeyPassphrase).execute() - } else { - // call back the method - executeAfterAuthentication(connectionMode, username, sshKey, identity, true) - } - } else { - val dialog = MaterialAlertDialogBuilder(callingActivity) - .setTitle(callingActivity.resources.getString(R.string.passphrase_dialog_title)) - .setMessage(callingActivity.resources.getString(R.string.passphrase_dialog_text)) - .setView(dialogView) - .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> - if (keyPair.decrypt(passphrase.text.toString())) { - if (dialogView.findViewById<MaterialCheckBox>(R.id.git_auth_remember_passphrase).isChecked) { - encryptedSettings.edit { putString("ssh_key_local_passphrase", passphrase.text.toString()) } - } - // Authenticate using the ssh-key and then execute the command - setAuthentication(sshKey, username, passphrase.text.toString()).execute() - } else { - encryptedSettings.edit { putString("ssh_key_local_passphrase", null) } - // call back the method - executeAfterAuthentication(connectionMode, username, sshKey, identity, true) + @SuppressLint("InflateParams") + val dialogView = layoutInflater.inflate(R.layout.git_passphrase_layout, null) + val editPassphrase = dialogView.findViewById<TextInputEditText>(R.id.git_auth_passphrase) + val rememberPassphrase = dialogView.findViewById<MaterialCheckBox>(R.id.git_auth_remember_passphrase) + if (isRetry) + editPassphrase.error = callingActivity.resources.getString(R.string.git_operation_wrong_passphrase) + MaterialAlertDialogBuilder(callingActivity).run { + setTitle(R.string.passphrase_dialog_title) + setMessage(R.string.passphrase_dialog_text) + setView(dialogView) + setPositiveButton(R.string.dialog_ok) { _, _ -> + val passphrase = editPassphrase.text.toString() + if (rememberPassphrase.isChecked) { + encryptedSettings.edit { + putString("ssh_key_local_passphrase", passphrase) } } - .setNegativeButton(callingActivity.resources.getString(R.string.dialog_cancel)) { _, _ -> - callingActivity.finish() - } - .setOnCancelListener { callingActivity.finish() } - .create() - dialog.requestInputFocusOnView<TextInputEditText>(R.id.git_auth_passphrase) - dialog.show() + cont.resume(passphrase) + } + setNegativeButton(R.string.dialog_cancel) { _, _ -> + cont.resume(null) + } + setOnCancelListener { + cont.resume(null) + } + create() + }.run { + requestInputFocusOnView<TextInputEditText>(R.id.git_auth_passphrase) + show() } } else { - setAuthentication(sshKey, username, "").execute() + cont.resume(storedPassphrase) } - } catch (e: JSchException) { - e.printStackTrace() - MaterialAlertDialogBuilder(callingActivity) - .setTitle(callingActivity.resources.getString(R.string.git_operation_unable_to_open_ssh_key_title)) - .setMessage(callingActivity.resources.getString(R.string.git_operation_unable_to_open_ssh_key_message)) - .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> - callingActivity.finish() - } - .show() - } + }).execute() } } ConnectionMode.OpenKeychain -> { @@ -256,18 +211,23 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit /** * Action to execute on error */ - open fun onError(errorMessage: String) { + open fun onError(err: Exception) { // Clear various auth related fields on failure - if (SshSessionFactory.getInstance() is SshApiSessionFactory) { - PreferenceManager.getDefaultSharedPreferences(callingActivity.applicationContext) - .edit { putString("ssh_openkeystore_keyid", null) } - callingActivity.applicationContext - .getEncryptedPrefs("git_operation") - .edit { remove("ssh_key_local_passphrase") } - } else if (SshSessionFactory.getInstance() is GitConfigSessionFactory) { - callingActivity.applicationContext - .getEncryptedPrefs("git_operation") - .edit { remove("https_password") } + when (SshSessionFactory.getInstance()) { + is SshApiSessionFactory -> { + PreferenceManager.getDefaultSharedPreferences(callingActivity.applicationContext) + .edit { putString("ssh_openkeystore_keyid", null) } + } + is SshjSessionFactory -> { + callingActivity.applicationContext + .getEncryptedPrefs("git_operation") + .edit { remove("ssh_key_local_passphrase") } + } + is GitConfigSessionFactory -> { + callingActivity.applicationContext + .getEncryptedPrefs("git_operation") + .edit { remove("https_password") } + } } } |
