aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
diff options
context:
space:
mode:
authorFabian Henneke <FabianHenneke@users.noreply.github.com>2020-09-01 10:12:27 +0200
committerGitHub <noreply@github.com>2020-09-01 13:42:27 +0530
commitcbb96397d1f5cffef7984b08a426646aaea93e5b (patch)
treeda11f9d477758f69f7349dea56bfcee3a92b2446 /app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
parent55d64fb73710d8dcadd28089ab233cd543830184 (diff)
downloadHardenedPass-cbb96397d1f5cffef7984b08a426646aaea93e5b.tar.gz
HardenedPass-cbb96397d1f5cffef7984b08a426646aaea93e5b.zip
Add Keystore backend for SSH public key authentication (#1070)
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt99
1 files changed, 74 insertions, 25 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
index 98bda9d7..810a8925 100644
--- a/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
@@ -5,6 +5,7 @@
package com.zeapo.pwdstore.sshkeygen
import android.os.Bundle
+import android.security.keystore.UserNotAuthenticatedException
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.InputMethodManager
@@ -13,22 +14,34 @@ import androidx.core.content.edit
import androidx.core.content.getSystemService
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.jcraft.jsch.JSch
-import com.jcraft.jsch.KeyPair
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.ActivitySshKeygenBinding
+import com.zeapo.pwdstore.git.sshj.SshKey
+import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.getEncryptedPrefs
-import com.zeapo.pwdstore.utils.sharedPrefs
+import com.zeapo.pwdstore.utils.keyguardManager
import com.zeapo.pwdstore.utils.viewBinding
-import java.io.File
-import java.io.FileOutputStream
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.Dispatchers
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)
+ }),
+}
+
class SshKeyGenActivity : AppCompatActivity() {
- private var keyLength = 4096
+ private var keyGenType = KeyGenType.Ecdsa
private val binding by viewBinding(ActivitySshKeygenBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
@@ -37,17 +50,45 @@ class SshKeyGenActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
with(binding) {
generate.setOnClickListener {
- lifecycleScope.launch { generate(passphrase.text.toString(), comment.text.toString()) }
+ if (SshKey.exists) {
+ MaterialAlertDialogBuilder(this@SshKeyGenActivity).run {
+ setTitle(R.string.ssh_keygen_existing_title)
+ setMessage(R.string.ssh_keygen_existing_message)
+ setPositiveButton(R.string.ssh_keygen_existing_replace) { _, _ ->
+ lifecycleScope.launch {
+ generate()
+ }
+ }
+ setNegativeButton(R.string.ssh_keygen_existing_keep) { _, _ ->
+ finish()
+ }
+ show()
+ }
+ } else {
+ lifecycleScope.launch {
+ generate()
+ }
+ }
}
- keyLengthGroup.check(R.id.key_length_4096)
- keyLengthGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
+ keyTypeGroup.check(R.id.key_type_ecdsa)
+ keyTypeExplanation.setText(R.string.ssh_keygen_explanation_ecdsa)
+ keyTypeGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (isChecked) {
- when (checkedId) {
- R.id.key_length_2048 -> keyLength = 2048
- R.id.key_length_4096 -> keyLength = 4096
+ keyGenType = when (checkedId) {
+ 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 (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
+ })
}
}
+ keyRequireAuthentication.isEnabled = keyguardManager.isDeviceSecure
+ keyRequireAuthentication.isChecked = keyRequireAuthentication.isEnabled
}
}
@@ -62,21 +103,27 @@ class SshKeyGenActivity : AppCompatActivity() {
}
}
- private suspend fun generate(passphrase: String, comment: String) {
+ private suspend fun generate() {
+ binding.generate.apply {
+ text = getString(R.string.ssh_key_gen_generating_progress)
+ isEnabled = false
+ }
binding.generate.text = getString(R.string.ssh_key_gen_generating_progress)
val e = try {
withContext(Dispatchers.IO) {
- val kp = KeyPair.genKeyPair(JSch(), KeyPair.RSA, keyLength)
- var file = File(filesDir, ".ssh_key")
- var out = FileOutputStream(file, false)
- if (passphrase.isNotEmpty()) {
- kp?.writePrivateKey(out, passphrase.toByteArray())
- } else {
- kp?.writePrivateKey(out)
+ val requireAuthentication = binding.keyRequireAuthentication.isChecked
+ if (requireAuthentication) {
+ val result = withContext(Dispatchers.Main) {
+ suspendCoroutine<BiometricAuthenticator.Result> { cont ->
+ BiometricAuthenticator.authenticate(this@SshKeyGenActivity, R.string.biometric_prompt_title_ssh_keygen) {
+ cont.resume(it)
+ }
+ }
+ }
+ if (result !is BiometricAuthenticator.Result.Success)
+ throw UserNotAuthenticatedException(getString(R.string.biometric_auth_generic_failure))
}
- file = File(filesDir, ".ssh_key.pub")
- out = FileOutputStream(file, false)
- kp?.writePublicKey(out, comment)
+ keyGenType.generateKey(requireAuthentication)
}
null
} catch (e: Exception) {
@@ -87,11 +134,13 @@ class SshKeyGenActivity : AppCompatActivity() {
remove("ssh_key_local_passphrase")
}
}
- binding.generate.text = getString(R.string.ssh_keygen_generating_done)
+ binding.generate.apply {
+ text = getString(R.string.ssh_keygen_generate)
+ isEnabled = true
+ }
if (e == null) {
val df = ShowSshKeyFragment()
df.show(supportFragmentManager, "public_key")
- sharedPrefs.edit { putBoolean("use_generated_key", true) }
} else {
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.error_generate_ssh_key))