Add migration to new SshKey backend (#1076)

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Fabian Henneke 2020-09-04 11:16:05 +02:00 committed by GitHub
parent e731943437
commit 4214b7fbb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 35 additions and 12 deletions

View file

@ -12,14 +12,17 @@ import com.github.ajalt.timberkt.e
import com.github.ajalt.timberkt.i import com.github.ajalt.timberkt.i
import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.GitSettings
import com.zeapo.pwdstore.git.config.Protocol import com.zeapo.pwdstore.git.config.Protocol
import com.zeapo.pwdstore.git.sshj.SshKey
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.getString
import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File
import java.net.URI import java.net.URI
fun runMigrations(context: Context) { fun runMigrations(context: Context) {
migrateToGitUrlBasedConfig(context) migrateToGitUrlBasedConfig(context)
migrateToHideAll(context) migrateToHideAll(context)
migrateToSshKey(context)
} }
private fun migrateToGitUrlBasedConfig(context: Context) { private fun migrateToGitUrlBasedConfig(context: Context) {
@ -94,3 +97,19 @@ private fun migrateToHideAll(context: Context) {
putBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, isHidden) putBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, isHidden)
} }
} }
private fun migrateToSshKey(context: Context) {
val privateKeyFile = File(context.filesDir, ".ssh_key")
if (context.sharedPrefs.contains(PreferenceKeys.USE_GENERATED_KEY) &&
!SshKey.exists &&
privateKeyFile.exists()) {
// Currently uses a private key imported or generated with an old version of Password Store.
// Generated keys come with a public key which the user should still be able to view after
// the migration (not possible for regular imported keys), hence the special case.
val isGeneratedKey = context.sharedPrefs.getBoolean(PreferenceKeys.USE_GENERATED_KEY, false)
SshKey.useLegacyKey(isGeneratedKey)
context.sharedPrefs.edit {
remove(PreferenceKeys.USE_GENERATED_KEY)
}
}
}

View file

@ -46,7 +46,6 @@ import com.zeapo.pwdstore.crypto.BasePgpActivity.Companion.getLongName
import com.zeapo.pwdstore.crypto.DecryptActivity import com.zeapo.pwdstore.crypto.DecryptActivity
import com.zeapo.pwdstore.crypto.PasswordCreationActivity import com.zeapo.pwdstore.crypto.PasswordCreationActivity
import com.zeapo.pwdstore.git.BaseGitActivity import com.zeapo.pwdstore.git.BaseGitActivity
import com.zeapo.pwdstore.git.GitServerConfigActivity
import com.zeapo.pwdstore.git.config.AuthMode import com.zeapo.pwdstore.git.config.AuthMode
import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.GitSettings
import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment
@ -89,12 +88,6 @@ class PasswordStore : BaseGitActivity() {
ViewModelProvider.AndroidViewModelFactory(application) ViewModelProvider.AndroidViewModelFactory(application)
} }
private val listRefreshAction = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
refreshPasswordList()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
// open search view on search key, or Ctr+F // open search view on search key, or Ctr+F
if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) && if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) &&

View file

@ -10,8 +10,8 @@ import com.github.ajalt.timberkt.Timber.tag
import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.d
import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.e
import com.github.michaelbull.result.Err import com.github.michaelbull.result.Err
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.GitSettings
import com.zeapo.pwdstore.git.operation.BreakOutOfDetached import com.zeapo.pwdstore.git.operation.BreakOutOfDetached

View file

@ -7,6 +7,7 @@ package com.zeapo.pwdstore.git
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.github.michaelbull.result.Result
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.git.GitException.PullException import com.zeapo.pwdstore.git.GitException.PullException
@ -14,7 +15,6 @@ import com.zeapo.pwdstore.git.GitException.PushException
import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.GitSettings
import com.zeapo.pwdstore.git.operation.GitOperation import com.zeapo.pwdstore.git.operation.GitOperation
import com.zeapo.pwdstore.utils.snackbar import com.zeapo.pwdstore.utils.snackbar
import com.github.michaelbull.result.Result
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.CommitCommand import org.eclipse.jgit.api.CommitCommand

View file

@ -76,7 +76,7 @@ object SshKey {
val sshPublicKey val sshPublicKey
get() = if (publicKeyFile.exists()) publicKeyFile.readText() else null get() = if (publicKeyFile.exists()) publicKeyFile.readText() else null
val canShowSshPublicKey val canShowSshPublicKey
get() = type in listOf(Type.KeystoreNative, Type.KeystoreWrappedEd25519) get() = type in listOf(Type.LegacyGenerated, Type.KeystoreNative, Type.KeystoreWrappedEd25519)
val exists val exists
get() = type != null get() = type != null
val mustAuthenticate: Boolean val mustAuthenticate: Boolean
@ -128,6 +128,9 @@ object SshKey {
Imported("imported"), Imported("imported"),
KeystoreNative("keystore_native"), KeystoreNative("keystore_native"),
KeystoreWrappedEd25519("keystore_wrapped_ed25519"), KeystoreWrappedEd25519("keystore_wrapped_ed25519"),
// Behaves like `Imported`, but allows to view the public key.
LegacyGenerated("legacy_generated"),
; ;
companion object { companion object {
@ -204,6 +207,11 @@ object SshKey {
type = Type.Imported type = Type.Imported
} }
@Deprecated("To be used only in Migrations.kt")
fun useLegacyKey(isGenerated: Boolean) {
type = if (isGenerated) Type.LegacyGenerated else Type.Imported
}
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
private suspend fun getOrCreateWrappingMasterKey(requireAuthentication: Boolean) = withContext(Dispatchers.IO) { private suspend fun getOrCreateWrappingMasterKey(requireAuthentication: Boolean) = withContext(Dispatchers.IO) {
MasterKey.Builder(context, KEYSTORE_ALIAS).run { MasterKey.Builder(context, KEYSTORE_ALIAS).run {
@ -268,7 +276,7 @@ object SshKey {
} }
fun provide(client: SSHClient, passphraseFinder: InteractivePasswordFinder): KeyProvider? = when (type) { fun provide(client: SSHClient, passphraseFinder: InteractivePasswordFinder): KeyProvider? = when (type) {
Type.Imported -> client.loadKeys(privateKeyFile.absolutePath, passphraseFinder) Type.LegacyGenerated, Type.Imported -> client.loadKeys(privateKeyFile.absolutePath, passphraseFinder)
Type.KeystoreNative -> KeystoreNativeKeyProvider Type.KeystoreNative -> KeystoreNativeKeyProvider
Type.KeystoreWrappedEd25519 -> KeystoreWrappedEd25519KeyProvider Type.KeystoreWrappedEd25519 -> KeystoreWrappedEd25519KeyProvider
null -> null null -> null

View file

@ -178,7 +178,7 @@ open class PasswordRepository protected constructor() {
val dir = getRepositoryDirectory() val dir = getRepositoryDirectory()
// uninitialize the repo if the dir does not exist or is absolutely empty // uninitialize the repo if the dir does not exist or is absolutely empty
settings.edit { settings.edit {
if (!dir.exists() || !dir.isDirectory || dir.listFiles()?.isEmpty() == true) { if (!dir.exists() || !dir.isDirectory || requireNotNull(dir.listFiles()).isEmpty()) {
putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
} else { } else {
putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true)

View file

@ -76,4 +76,7 @@ object PreferenceKeys {
const val SSH_OPENKEYSTORE_CLEAR_KEY_ID = "ssh_openkeystore_clear_keyid" const val SSH_OPENKEYSTORE_CLEAR_KEY_ID = "ssh_openkeystore_clear_keyid"
const val SSH_OPENKEYSTORE_KEYID = "ssh_openkeystore_keyid" const val SSH_OPENKEYSTORE_KEYID = "ssh_openkeystore_keyid"
const val SSH_SEE_KEY = "ssh_see_key" const val SSH_SEE_KEY = "ssh_see_key"
@Deprecated("To be used only in Migrations.kt")
const val USE_GENERATED_KEY = "use_generated_key"
} }