refactor: consistently adopt PGP over GPG for naming

PGP is the standard, GPG is an implementation of it. We're adhering to PGP, and not using GPG.
This commit is contained in:
Harsh Shandilya 2023-06-15 15:49:32 +05:30
parent c168ce2e86
commit 5dac84c3c8
No known key found for this signature in database
19 changed files with 94 additions and 95 deletions

View file

@ -6,9 +6,9 @@
package app.passwordstore.data.crypto package app.passwordstore.data.crypto
import android.content.SharedPreferences import android.content.SharedPreferences
import app.passwordstore.crypto.GpgIdentifier
import app.passwordstore.crypto.PGPDecryptOptions import app.passwordstore.crypto.PGPDecryptOptions
import app.passwordstore.crypto.PGPEncryptOptions import app.passwordstore.crypto.PGPEncryptOptions
import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.crypto.PGPKeyManager import app.passwordstore.crypto.PGPKeyManager
import app.passwordstore.crypto.PGPainlessCryptoHandler import app.passwordstore.crypto.PGPainlessCryptoHandler
import app.passwordstore.crypto.errors.CryptoHandlerException import app.passwordstore.crypto.errors.CryptoHandlerException
@ -40,20 +40,20 @@ constructor(
suspend fun decrypt( suspend fun decrypt(
password: String, password: String,
identities: List<GpgIdentifier>, identities: List<PGPIdentifier>,
message: ByteArrayInputStream, message: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
) = withContext(dispatcherProvider.io()) { decryptPgp(password, identities, message, out) } ) = withContext(dispatcherProvider.io()) { decryptPgp(password, identities, message, out) }
suspend fun encrypt( suspend fun encrypt(
identities: List<GpgIdentifier>, identities: List<PGPIdentifier>,
content: ByteArrayInputStream, content: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
) = withContext(dispatcherProvider.io()) { encryptPgp(identities, content, out) } ) = withContext(dispatcherProvider.io()) { encryptPgp(identities, content, out) }
private suspend fun decryptPgp( private suspend fun decryptPgp(
password: String, password: String,
identities: List<GpgIdentifier>, identities: List<PGPIdentifier>,
message: ByteArrayInputStream, message: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
): Result<Unit, CryptoHandlerException> { ): Result<Unit, CryptoHandlerException> {
@ -63,7 +63,7 @@ constructor(
} }
private suspend fun encryptPgp( private suspend fun encryptPgp(
identities: List<GpgIdentifier>, identities: List<PGPIdentifier>,
content: ByteArrayInputStream, content: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
): Result<Unit, CryptoHandlerException> { ): Result<Unit, CryptoHandlerException> {

View file

@ -4,15 +4,14 @@ import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey import androidx.security.crypto.MasterKey
import app.passwordstore.crypto.GpgIdentifier import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.getString import app.passwordstore.util.extensions.getString
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
/** Implements a rudimentary [EncryptedSharedPreferences]-backed cache for GPG passphrases. */ /** Implements a rudimentary [EncryptedSharedPreferences]-backed cache for GPG passphrases. */
@Suppress("Unused") // Soon class PGPPassphraseCache
class GPGPassphraseCache
@Inject @Inject
constructor( constructor(
private val dispatcherProvider: DispatcherProvider, private val dispatcherProvider: DispatcherProvider,
@ -20,7 +19,7 @@ constructor(
suspend fun cachePassphrase( suspend fun cachePassphrase(
context: Context, context: Context,
identifier: GpgIdentifier, identifier: PGPIdentifier,
passphrase: String, passphrase: String,
) { ) {
withContext(dispatcherProvider.io()) { withContext(dispatcherProvider.io()) {
@ -30,7 +29,7 @@ constructor(
suspend fun retrieveCachedPassphrase( suspend fun retrieveCachedPassphrase(
context: Context, context: Context,
identifier: GpgIdentifier, identifier: PGPIdentifier,
): String? { ): String? {
return withContext(dispatcherProvider.io()) { return withContext(dispatcherProvider.io()) {
getPreferences(context).getString(identifier.toString()) getPreferences(context).getString(identifier.toString())
@ -39,7 +38,7 @@ constructor(
suspend fun clearCachedPassphrase( suspend fun clearCachedPassphrase(
context: Context, context: Context,
identifier: GpgIdentifier, identifier: PGPIdentifier,
) { ) {
withContext(dispatcherProvider.io()) { withContext(dispatcherProvider.io()) {
getPreferences(context).edit { remove(identifier.toString()) } getPreferences(context).edit { remove(identifier.toString()) }

View file

@ -7,7 +7,7 @@ package app.passwordstore.data.password
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.ui.crypto.BasePgpActivity import app.passwordstore.ui.crypto.BasePGPActivity
import app.passwordstore.ui.main.LaunchActivity import app.passwordstore.ui.main.LaunchActivity
import java.io.File import java.io.File
@ -21,7 +21,7 @@ data class PasswordItem(
val fullPathToParent = file.absolutePath.replace(rootDir.absolutePath, "").replace(file.name, "") val fullPathToParent = file.absolutePath.replace(rootDir.absolutePath, "").replace(file.name, "")
val longName = BasePgpActivity.getLongName(fullPathToParent, rootDir.absolutePath, toString()) val longName = BasePGPActivity.getLongName(fullPathToParent, rootDir.absolutePath, toString())
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return (other is PasswordItem) && (other.file == file) return (other is PasswordItem) && (other.file == file)

View file

@ -13,16 +13,16 @@ import android.os.Bundle
import android.view.autofill.AutofillManager import android.view.autofill.AutofillManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.data.crypto.GPGPassphraseCache import app.passwordstore.data.crypto.PGPPassphraseCache
import app.passwordstore.data.passfile.PasswordEntry import app.passwordstore.data.passfile.PasswordEntry
import app.passwordstore.ui.crypto.BasePgpActivity import app.passwordstore.ui.crypto.BasePGPActivity
import app.passwordstore.ui.crypto.PasswordDialog import app.passwordstore.ui.crypto.PasswordDialog
import app.passwordstore.util.auth.BiometricAuthenticator import app.passwordstore.util.auth.BiometricAuthenticator
import app.passwordstore.util.autofill.AutofillPreferences import app.passwordstore.util.autofill.AutofillPreferences
import app.passwordstore.util.autofill.AutofillResponseBuilder import app.passwordstore.util.autofill.AutofillResponseBuilder
import app.passwordstore.util.autofill.DirectoryStructure import app.passwordstore.util.autofill.DirectoryStructure
import app.passwordstore.util.extensions.asLog import app.passwordstore.util.extensions.asLog
import app.passwordstore.util.features.Feature.EnableGPGPassphraseCache import app.passwordstore.util.features.Feature.EnablePGPPassphraseCache
import app.passwordstore.util.features.Features import app.passwordstore.util.features.Features
import com.github.androidpasswordstore.autofillparser.AutofillAction import com.github.androidpasswordstore.autofillparser.AutofillAction
import com.github.androidpasswordstore.autofillparser.Credentials import com.github.androidpasswordstore.autofillparser.Credentials
@ -41,11 +41,11 @@ import logcat.LogPriority.ERROR
import logcat.logcat import logcat.logcat
@AndroidEntryPoint @AndroidEntryPoint
class AutofillDecryptActivity : BasePgpActivity() { class AutofillDecryptActivity : BasePGPActivity() {
@Inject lateinit var passwordEntryFactory: PasswordEntry.Factory @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory
@Inject lateinit var features: Features @Inject lateinit var features: Features
@Inject lateinit var passphraseCache: GPGPassphraseCache @Inject lateinit var passphraseCache: PGPPassphraseCache
private lateinit var directoryStructure: DirectoryStructure private lateinit var directoryStructure: DirectoryStructure
@ -70,9 +70,9 @@ class AutofillDecryptActivity : BasePgpActivity() {
directoryStructure = AutofillPreferences.directoryStructure(this) directoryStructure = AutofillPreferences.directoryStructure(this)
logcat { action.toString() } logcat { action.toString() }
requireKeysExist { requireKeysExist {
val gpgIdentifiers = getGpgIdentifiers("") ?: return@requireKeysExist val gpgIdentifiers = getPGPIdentifiers("") ?: return@requireKeysExist
if ( if (
features.isEnabled(EnableGPGPassphraseCache) && BiometricAuthenticator.canAuthenticate(this) features.isEnabled(EnablePGPPassphraseCache) && BiometricAuthenticator.canAuthenticate(this)
) { ) {
BiometricAuthenticator.authenticate( BiometricAuthenticator.authenticate(
this, this,
@ -143,7 +143,7 @@ class AutofillDecryptActivity : BasePgpActivity() {
} }
private suspend fun decryptCredential(file: File, password: String): Credentials? { private suspend fun decryptCredential(file: File, password: String): Credentials? {
val gpgIdentifiers = getGpgIdentifiers("") ?: return null val gpgIdentifiers = getPGPIdentifiers("") ?: return null
runCatching { file.readBytes().inputStream() } runCatching { file.readBytes().inputStream() }
.onFailure { e -> .onFailure { e ->
logcat(ERROR) { e.asLog("File to decrypt not found") } logcat(ERROR) { e.asLog("File to decrypt not found") }

View file

@ -18,7 +18,7 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.crypto.GpgIdentifier import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.data.crypto.CryptoRepository import app.passwordstore.data.crypto.CryptoRepository
import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.injection.prefs.SettingsPreferences import app.passwordstore.injection.prefs.SettingsPreferences
@ -41,7 +41,7 @@ import kotlinx.coroutines.withContext
@Suppress("Registered") @Suppress("Registered")
@AndroidEntryPoint @AndroidEntryPoint
open class BasePgpActivity : AppCompatActivity() { open class BasePGPActivity : AppCompatActivity() {
/** Full path to the repository */ /** Full path to the repository */
val repoPath by unsafeLazy { intent.getStringExtra("REPO_PATH")!! } val repoPath by unsafeLazy { intent.getStringExtra("REPO_PATH")!! }
@ -110,12 +110,12 @@ open class BasePgpActivity : AppCompatActivity() {
val hasKeys = repository.hasKeys() val hasKeys = repository.hasKeys()
if (!hasKeys) { if (!hasKeys) {
withContext(dispatcherProvider.main()) { withContext(dispatcherProvider.main()) {
MaterialAlertDialogBuilder(this@BasePgpActivity) MaterialAlertDialogBuilder(this@BasePGPActivity)
.setTitle(resources.getString(R.string.no_keys_imported_dialog_title)) .setTitle(resources.getString(R.string.no_keys_imported_dialog_title))
.setMessage(resources.getString(R.string.no_keys_imported_dialog_message)) .setMessage(resources.getString(R.string.no_keys_imported_dialog_message))
.setPositiveButton(resources.getString(R.string.button_label_import)) { _, _ -> .setPositiveButton(resources.getString(R.string.button_label_import)) { _, _ ->
onKeyImport = onKeysExist onKeyImport = onKeysExist
keyImportAction.launch(Intent(this@BasePgpActivity, PGPKeyImportActivity::class.java)) keyImportAction.launch(Intent(this@BasePGPActivity, PGPKeyImportActivity::class.java))
} }
.show() .show()
} }
@ -147,12 +147,12 @@ open class BasePgpActivity : AppCompatActivity() {
} }
/** /**
* Get a list of available [GpgIdentifier]s for the current password repository. This method * Get a list of available [PGPIdentifier]s for the current password repository. This method
* throws when no identifiers were able to be parsed. If this method returns null, it means that * throws when no identifiers were able to be parsed. If this method returns null, it means that
* an invalid identifier was encountered and further execution must stop to let the user correct * an invalid identifier was encountered and further execution must stop to let the user correct
* the problem. * the problem.
*/ */
fun getGpgIdentifiers(subDir: String): List<GpgIdentifier>? { fun getPGPIdentifiers(subDir: String): List<PGPIdentifier>? {
val repoRoot = PasswordRepository.getRepositoryDirectory() val repoRoot = PasswordRepository.getRepositoryDirectory()
val gpgIdentifierFile = val gpgIdentifierFile =
File(repoRoot, subDir).findTillRoot(".gpg-id", repoRoot) File(repoRoot, subDir).findTillRoot(".gpg-id", repoRoot)
@ -162,7 +162,7 @@ open class BasePgpActivity : AppCompatActivity() {
.readLines() .readLines()
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
.map { line -> .map { line ->
GpgIdentifier.fromString(line) PGPIdentifier.fromString(line)
?: run { ?: run {
// The line being empty means this is most likely an empty `.gpg-id` // The line being empty means this is most likely an empty `.gpg-id`
// file we created. Skip the validation so we can make the user add a // file we created. Skip the validation so we can make the user add a
@ -176,7 +176,7 @@ open class BasePgpActivity : AppCompatActivity() {
return null return null
} }
} }
.filterIsInstance<GpgIdentifier>() .filterIsInstance<PGPIdentifier>()
if (gpgIdentifiers.isEmpty()) { if (gpgIdentifiers.isEmpty()) {
error("Failed to parse identifiers from .gpg-id: ${gpgIdentifierFile.readText()}") error("Failed to parse identifiers from .gpg-id: ${gpgIdentifierFile.readText()}")
} }

View file

@ -11,8 +11,8 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.crypto.GpgIdentifier import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.data.crypto.GPGPassphraseCache import app.passwordstore.data.crypto.PGPPassphraseCache
import app.passwordstore.data.passfile.PasswordEntry import app.passwordstore.data.passfile.PasswordEntry
import app.passwordstore.data.password.FieldItem import app.passwordstore.data.password.FieldItem
import app.passwordstore.databinding.DecryptLayoutBinding import app.passwordstore.databinding.DecryptLayoutBinding
@ -21,7 +21,7 @@ import app.passwordstore.util.auth.BiometricAuthenticator
import app.passwordstore.util.extensions.getString import app.passwordstore.util.extensions.getString
import app.passwordstore.util.extensions.unsafeLazy import app.passwordstore.util.extensions.unsafeLazy
import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.features.Feature.EnableGPGPassphraseCache import app.passwordstore.util.features.Feature.EnablePGPPassphraseCache
import app.passwordstore.util.features.Features import app.passwordstore.util.features.Features
import app.passwordstore.util.settings.Constants import app.passwordstore.util.settings.Constants
import app.passwordstore.util.settings.PreferenceKeys import app.passwordstore.util.settings.PreferenceKeys
@ -42,10 +42,10 @@ import logcat.LogPriority.ERROR
import logcat.logcat import logcat.logcat
@AndroidEntryPoint @AndroidEntryPoint
class DecryptActivity : BasePgpActivity() { class DecryptActivity : BasePGPActivity() {
@Inject lateinit var passwordEntryFactory: PasswordEntry.Factory @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory
@Inject lateinit var passphraseCache: GPGPassphraseCache @Inject lateinit var passphraseCache: PGPPassphraseCache
@Inject lateinit var features: Features @Inject lateinit var features: Features
private val binding by viewBinding(DecryptLayoutBinding::inflate) private val binding by viewBinding(DecryptLayoutBinding::inflate)
@ -67,7 +67,7 @@ class DecryptActivity : BasePgpActivity() {
} }
} }
if ( if (
features.isEnabled(EnableGPGPassphraseCache) && features.isEnabled(EnablePGPPassphraseCache) &&
BiometricAuthenticator.canAuthenticate(this@DecryptActivity) BiometricAuthenticator.canAuthenticate(this@DecryptActivity)
) { ) {
BiometricAuthenticator.authenticate( BiometricAuthenticator.authenticate(
@ -150,7 +150,7 @@ class DecryptActivity : BasePgpActivity() {
} }
private fun decrypt(isError: Boolean, authResult: BiometricAuthenticator.Result) { private fun decrypt(isError: Boolean, authResult: BiometricAuthenticator.Result) {
val gpgIdentifiers = getGpgIdentifiers("") ?: return val gpgIdentifiers = getPGPIdentifiers("") ?: return
lifecycleScope.launch(dispatcherProvider.main()) { lifecycleScope.launch(dispatcherProvider.main()) {
if (authResult is BiometricAuthenticator.Result.Success) { if (authResult is BiometricAuthenticator.Result.Success) {
val cachedPassphrase = val cachedPassphrase =
@ -168,7 +168,7 @@ class DecryptActivity : BasePgpActivity() {
private fun askPassphrase( private fun askPassphrase(
isError: Boolean, isError: Boolean,
gpgIdentifiers: List<GpgIdentifier>, gpgIdentifiers: List<PGPIdentifier>,
authResult: BiometricAuthenticator.Result, authResult: BiometricAuthenticator.Result,
) { ) {
if (retries < MAX_RETRIES) { if (retries < MAX_RETRIES) {
@ -206,7 +206,7 @@ class DecryptActivity : BasePgpActivity() {
private suspend fun decryptWithCachedPassphrase( private suspend fun decryptWithCachedPassphrase(
passphrase: String, passphrase: String,
identifiers: List<GpgIdentifier>, identifiers: List<PGPIdentifier>,
authResult: BiometricAuthenticator.Result, authResult: BiometricAuthenticator.Result,
) { ) {
when (val result = decryptWithPassphrase(passphrase, identifiers)) { when (val result = decryptWithPassphrase(passphrase, identifiers)) {
@ -225,7 +225,7 @@ class DecryptActivity : BasePgpActivity() {
private suspend fun decryptWithPassphrase( private suspend fun decryptWithPassphrase(
password: String, password: String,
gpgIdentifiers: List<GpgIdentifier>, gpgIdentifiers: List<PGPIdentifier>,
) = runCatching { ) = runCatching {
val message = withContext(dispatcherProvider.io()) { File(fullPath).readBytes().inputStream() } val message = withContext(dispatcherProvider.io()) { File(fullPath).readBytes().inputStream() }
val outputStream = ByteArrayOutputStream() val outputStream = ByteArrayOutputStream()

View file

@ -63,7 +63,7 @@ import logcat.asLog
import logcat.logcat import logcat.logcat
@AndroidEntryPoint @AndroidEntryPoint
class PasswordCreationActivity : BasePgpActivity() { class PasswordCreationActivity : BasePGPActivity() {
private val binding by viewBinding(PasswordCreationActivityBinding::inflate) private val binding by viewBinding(PasswordCreationActivityBinding::inflate)
@Inject lateinit var passwordEntryFactory: PasswordEntry.Factory @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory
@ -330,7 +330,7 @@ class PasswordCreationActivity : BasePgpActivity() {
} }
// pass enters the key ID into `.gpg-id`. // pass enters the key ID into `.gpg-id`.
val gpgIdentifiers = getGpgIdentifiers(directory.text.toString()) ?: return@with val gpgIdentifiers = getPGPIdentifiers(directory.text.toString()) ?: return@with
val content = "$editPass\n$editExtra" val content = "$editPass\n$editExtra"
val path = val path =
when { when {

View file

@ -11,7 +11,7 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit import androidx.core.content.edit
import app.passwordstore.ui.crypto.BasePgpActivity import app.passwordstore.ui.crypto.BasePGPActivity
import app.passwordstore.ui.crypto.DecryptActivity import app.passwordstore.ui.crypto.DecryptActivity
import app.passwordstore.ui.passwords.PasswordStore import app.passwordstore.ui.passwords.PasswordStore
import app.passwordstore.util.auth.BiometricAuthenticator import app.passwordstore.util.auth.BiometricAuthenticator
@ -56,12 +56,12 @@ class LaunchActivity : AppCompatActivity() {
if (intent.action == ACTION_DECRYPT_PASS) if (intent.action == ACTION_DECRYPT_PASS)
getDecryptIntent().apply { getDecryptIntent().apply {
putExtra( putExtra(
BasePgpActivity.EXTRA_FILE_PATH, BasePGPActivity.EXTRA_FILE_PATH,
intent.getStringExtra(BasePgpActivity.EXTRA_FILE_PATH) intent.getStringExtra(BasePGPActivity.EXTRA_FILE_PATH)
) )
putExtra( putExtra(
BasePgpActivity.EXTRA_REPO_PATH, BasePGPActivity.EXTRA_REPO_PATH,
intent.getStringExtra(BasePgpActivity.EXTRA_REPO_PATH) intent.getStringExtra(BasePGPActivity.EXTRA_REPO_PATH)
) )
} }
else Intent(this, PasswordStore::class.java) else Intent(this, PasswordStore::class.java)

View file

@ -25,8 +25,8 @@ import androidx.lifecycle.lifecycleScope
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.data.password.PasswordItem import app.passwordstore.data.password.PasswordItem
import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.ui.crypto.BasePgpActivity import app.passwordstore.ui.crypto.BasePGPActivity
import app.passwordstore.ui.crypto.BasePgpActivity.Companion.getLongName import app.passwordstore.ui.crypto.BasePGPActivity.Companion.getLongName
import app.passwordstore.ui.crypto.DecryptActivity import app.passwordstore.ui.crypto.DecryptActivity
import app.passwordstore.ui.crypto.PasswordCreationActivity import app.passwordstore.ui.crypto.PasswordCreationActivity
import app.passwordstore.ui.dialogs.FolderCreationDialogFragment import app.passwordstore.ui.dialogs.FolderCreationDialogFragment
@ -393,9 +393,9 @@ class PasswordStore : BaseGitActivity() {
val currentDir = currentDir val currentDir = currentDir
logcat(INFO) { "Adding file to : ${currentDir.absolutePath}" } logcat(INFO) { "Adding file to : ${currentDir.absolutePath}" }
val intent = Intent(this, PasswordCreationActivity::class.java) val intent = Intent(this, PasswordCreationActivity::class.java)
intent.putExtra(BasePgpActivity.EXTRA_FILE_PATH, currentDir.absolutePath) intent.putExtra(BasePGPActivity.EXTRA_FILE_PATH, currentDir.absolutePath)
intent.putExtra( intent.putExtra(
BasePgpActivity.EXTRA_REPO_PATH, BasePGPActivity.EXTRA_REPO_PATH,
PasswordRepository.getRepositoryDirectory().absolutePath PasswordRepository.getRepositoryDirectory().absolutePath
) )
listRefreshAction.launch(intent) listRefreshAction.launch(intent)

View file

@ -32,7 +32,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.crypto.GpgIdentifier import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.ui.compose.theme.APSThemePreview import app.passwordstore.ui.compose.theme.APSThemePreview
import app.passwordstore.util.extensions.conditional import app.passwordstore.util.extensions.conditional
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -41,10 +41,10 @@ import kotlinx.collections.immutable.toPersistentList
@Composable @Composable
fun KeyList( fun KeyList(
identifiers: ImmutableList<GpgIdentifier>, identifiers: ImmutableList<PGPIdentifier>,
onItemClick: (identifier: GpgIdentifier) -> Unit, onItemClick: (identifier: PGPIdentifier) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onKeySelected: ((identifier: GpgIdentifier) -> Unit)? = null, onKeySelected: ((identifier: PGPIdentifier) -> Unit)? = null,
) { ) {
if (identifiers.isEmpty()) { if (identifiers.isEmpty()) {
Column( Column(
@ -69,10 +69,10 @@ fun KeyList(
@Composable @Composable
private fun KeyItem( private fun KeyItem(
identifier: GpgIdentifier, identifier: PGPIdentifier,
onItemClick: (identifier: GpgIdentifier) -> Unit, onItemClick: (identifier: PGPIdentifier) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onKeySelected: ((identifier: GpgIdentifier) -> Unit)? = null, onKeySelected: ((identifier: PGPIdentifier) -> Unit)? = null,
) { ) {
var isDeleting by remember { mutableStateOf(false) } var isDeleting by remember { mutableStateOf(false) }
DeleteConfirmationDialog( DeleteConfirmationDialog(
@ -85,8 +85,8 @@ private fun KeyItem(
) )
val label = val label =
when (identifier) { when (identifier) {
is GpgIdentifier.KeyId -> identifier.id.toString() is PGPIdentifier.KeyId -> identifier.id.toString()
is GpgIdentifier.UserId -> identifier.email is PGPIdentifier.UserId -> identifier.email
} }
Row( Row(
modifier = modifier =
@ -144,8 +144,8 @@ private fun KeyListPreview() {
KeyList( KeyList(
identifiers = identifiers =
listOfNotNull( listOfNotNull(
GpgIdentifier.fromString("ultramicroscopicsilicovolcanoconiosis@example.com"), PGPIdentifier.fromString("ultramicroscopicsilicovolcanoconiosis@example.com"),
GpgIdentifier.fromString("0xB950AE2813841585"), PGPIdentifier.fromString("0xB950AE2813841585"),
) )
.toPersistentList(), .toPersistentList(),
onItemClick = {} onItemClick = {}

View file

@ -32,7 +32,7 @@ class PGPSettings(private val activity: FragmentActivity) : SettingsProvider {
titleRes = R.string.pref_pgp_ascii_armor_title titleRes = R.string.pref_pgp_ascii_armor_title
persistent = true persistent = true
} }
switch(Feature.EnableGPGPassphraseCache.configKey) { switch(Feature.EnablePGPPassphraseCache.configKey) {
titleRes = R.string.pref_passphrase_cache_title titleRes = R.string.pref_passphrase_cache_title
summaryRes = R.string.pref_passphrase_cache_summary summaryRes = R.string.pref_passphrase_cache_summary
defaultValue = false defaultValue = false

View file

@ -23,8 +23,8 @@ enum class Feature(
/** Opt into the new SSH layer implemented as a freestanding module. */ /** Opt into the new SSH layer implemented as a freestanding module. */
EnableNewSSHLayer(false, "enable_new_ssh"), EnableNewSSHLayer(false, "enable_new_ssh"),
/** Opt into a cache layer for GPG passphrases. */ /** Opt into a cache layer for PGP passphrases. */
EnableGPGPassphraseCache(false, "enable_gpg_passphrase_cache"), EnablePGPPassphraseCache(false, "enable_gpg_passphrase_cache"),
; ;
companion object { companion object {

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import app.passwordstore.crypto.GpgIdentifier
import app.passwordstore.crypto.KeyUtils import app.passwordstore.crypto.KeyUtils
import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.crypto.PGPKeyManager import app.passwordstore.crypto.PGPKeyManager
import com.github.michaelbull.result.Err import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
@ -20,7 +20,7 @@ import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
class PGPKeyListViewModel @Inject constructor(private val keyManager: PGPKeyManager) : ViewModel() { class PGPKeyListViewModel @Inject constructor(private val keyManager: PGPKeyManager) : ViewModel() {
var keys: ImmutableList<GpgIdentifier> by mutableStateOf(persistentListOf()) var keys: ImmutableList<PGPIdentifier> by mutableStateOf(persistentListOf())
init { init {
updateKeySet() updateKeySet()
@ -40,7 +40,7 @@ class PGPKeyListViewModel @Inject constructor(private val keyManager: PGPKeyMana
} }
} }
fun deleteKey(identifier: GpgIdentifier) { fun deleteKey(identifier: PGPIdentifier) {
viewModelScope.launch { viewModelScope.launch {
keyManager.removeKey(identifier) keyManager.removeKey(identifier)
updateKeySet() updateKeySet()

View file

@ -5,8 +5,8 @@
package app.passwordstore.crypto package app.passwordstore.crypto
import app.passwordstore.crypto.GpgIdentifier.KeyId import app.passwordstore.crypto.PGPIdentifier.KeyId
import app.passwordstore.crypto.GpgIdentifier.UserId import app.passwordstore.crypto.PGPIdentifier.UserId
import com.github.michaelbull.result.get import com.github.michaelbull.result.get
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPKeyRing

View file

@ -8,11 +8,11 @@ package app.passwordstore.crypto
import java.util.Locale import java.util.Locale
import java.util.regex.Pattern import java.util.regex.Pattern
/** Supertype for valid identifiers of GPG keys. */ /** Supertype for valid identifiers of PGP keys. */
public sealed class GpgIdentifier { public sealed class PGPIdentifier {
/** A [GpgIdentifier] that represents either a long key ID or a fingerprint. */ /** A [PGPIdentifier] that represents either a long key ID or a fingerprint. */
public data class KeyId(val id: Long) : GpgIdentifier() { public data class KeyId(val id: Long) : PGPIdentifier() {
override fun toString(): String { override fun toString(): String {
return convertKeyIdToHex(id) return convertKeyIdToHex(id)
} }
@ -36,10 +36,10 @@ public sealed class GpgIdentifier {
} }
/** /**
* A [GpgIdentifier] that represents the textual name/email combination corresponding to a key. * A [PGPIdentifier] that represents the textual name/email combination corresponding to a key.
* Despite the [email] property in this class, the value is not guaranteed to be a valid email. * Despite the [email] property in this class, the value is not guaranteed to be a valid email.
*/ */
public data class UserId(val email: String) : GpgIdentifier() { public data class UserId(val email: String) : PGPIdentifier() {
override fun toString(): String { override fun toString(): String {
return email return email
} }
@ -53,10 +53,10 @@ public sealed class GpgIdentifier {
private const val TRUNCATED_FINGERPRINT_LENGTH = 16 private const val TRUNCATED_FINGERPRINT_LENGTH = 16
/** /**
* Attempts to parse an untyped String identifier into a concrete subtype of [GpgIdentifier]. * Attempts to parse an untyped String identifier into a concrete subtype of [PGPIdentifier].
*/ */
@Suppress("ReturnCount") @Suppress("ReturnCount")
public fun fromString(identifier: String): GpgIdentifier? { public fun fromString(identifier: String): PGPIdentifier? {
if (identifier.isEmpty()) return null if (identifier.isEmpty()) return null
// Match long key IDs: // Match long key IDs:
// FF22334455667788 or 0xFF22334455667788 // FF22334455667788 or 0xFF22334455667788

View file

@ -32,7 +32,7 @@ public class PGPKeyManager
constructor( constructor(
filesDir: String, filesDir: String,
private val dispatcher: CoroutineDispatcher, private val dispatcher: CoroutineDispatcher,
) : KeyManager<PGPKey, GpgIdentifier> { ) : KeyManager<PGPKey, PGPIdentifier> {
private val keyDir = File(filesDir, KEY_DIR_NAME) private val keyDir = File(filesDir, KEY_DIR_NAME)
@ -74,7 +74,7 @@ constructor(
} }
/** @see KeyManager.removeKey */ /** @see KeyManager.removeKey */
override suspend fun removeKey(identifier: GpgIdentifier): Result<Unit, Throwable> = override suspend fun removeKey(identifier: PGPIdentifier): Result<Unit, Throwable> =
withContext(dispatcher) { withContext(dispatcher) {
runSuspendCatching { runSuspendCatching {
if (!keyDirExists()) throw KeyDirectoryUnavailableException if (!keyDirExists()) throw KeyDirectoryUnavailableException
@ -87,7 +87,7 @@ constructor(
} }
/** @see KeyManager.getKeyById */ /** @see KeyManager.getKeyById */
override suspend fun getKeyById(id: GpgIdentifier): Result<PGPKey, Throwable> = override suspend fun getKeyById(id: PGPIdentifier): Result<PGPKey, Throwable> =
withContext(dispatcher) { withContext(dispatcher) {
runSuspendCatching { runSuspendCatching {
if (!keyDirExists()) throw KeyDirectoryUnavailableException if (!keyDirExists()) throw KeyDirectoryUnavailableException
@ -97,14 +97,14 @@ constructor(
val matchResult = val matchResult =
when (id) { when (id) {
is GpgIdentifier.KeyId -> { is PGPIdentifier.KeyId -> {
val keyIdMatch = val keyIdMatch =
keys keys
.map { key -> key to tryGetId(key) } .map { key -> key to tryGetId(key) }
.firstOrNull { (_, keyId) -> keyId?.id == id.id } .firstOrNull { (_, keyId) -> keyId?.id == id.id }
keyIdMatch?.first keyIdMatch?.first
} }
is GpgIdentifier.UserId -> { is PGPIdentifier.UserId -> {
val selector = SelectUserId.byEmail(id.email) val selector = SelectUserId.byEmail(id.email)
val userIdMatch = val userIdMatch =
keys keys
@ -134,7 +134,7 @@ constructor(
} }
/** @see KeyManager.getKeyById */ /** @see KeyManager.getKeyById */
override suspend fun getKeyId(key: PGPKey): GpgIdentifier? = tryGetId(key) override suspend fun getKeyId(key: PGPKey): PGPIdentifier? = tryGetId(key)
/** Checks if [keyDir] exists and attempts to create it if not. */ /** Checks if [keyDir] exists and attempts to create it if not. */
private fun keyDirExists(): Boolean { private fun keyDirExists(): Boolean {

View file

@ -18,7 +18,7 @@ class KeyUtilsTest {
assertIs<PGPSecretKeyRing>(keyring) assertIs<PGPSecretKeyRing>(keyring)
val keyId = tryGetId(key) val keyId = tryGetId(key)
assertNotNull(keyId) assertNotNull(keyId)
assertIs<GpgIdentifier.KeyId>(keyId) assertIs<PGPIdentifier.KeyId>(keyId)
assertEquals("b950ae2813841585", keyId.toString()) assertEquals("b950ae2813841585", keyId.toString())
} }
} }

View file

@ -9,33 +9,33 @@ import kotlin.test.Test
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
class GpgIdentifierTest { class PGPIdentifierTest {
@Test @Test
fun parseHexKeyIdWithout0xPrefix() { fun parseHexKeyIdWithout0xPrefix() {
val identifier = GpgIdentifier.fromString("79E8208280490C77") val identifier = PGPIdentifier.fromString("79E8208280490C77")
assertNotNull(identifier) assertNotNull(identifier)
assertTrue { identifier is GpgIdentifier.KeyId } assertTrue { identifier is PGPIdentifier.KeyId }
} }
@Test @Test
fun parseHexKeyId() { fun parseHexKeyId() {
val identifier = GpgIdentifier.fromString("0x79E8208280490C77") val identifier = PGPIdentifier.fromString("0x79E8208280490C77")
assertNotNull(identifier) assertNotNull(identifier)
assertTrue { identifier is GpgIdentifier.KeyId } assertTrue { identifier is PGPIdentifier.KeyId }
} }
@Test @Test
fun parseValidEmail() { fun parseValidEmail() {
val identifier = GpgIdentifier.fromString("john.doe@example.org") val identifier = PGPIdentifier.fromString("john.doe@example.org")
assertNotNull(identifier) assertNotNull(identifier)
assertTrue { identifier is GpgIdentifier.UserId } assertTrue { identifier is PGPIdentifier.UserId }
} }
@Test @Test
fun parseEmailWithoutTLD() { fun parseEmailWithoutTLD() {
val identifier = GpgIdentifier.fromString("john.doe@example") val identifier = PGPIdentifier.fromString("john.doe@example")
assertNotNull(identifier) assertNotNull(identifier)
assertTrue { identifier is GpgIdentifier.UserId } assertTrue { identifier is PGPIdentifier.UserId }
} }
} }

View file

@ -1,8 +1,8 @@
package app.passwordstore.crypto package app.passwordstore.crypto
import app.passwordstore.crypto.GpgIdentifier.KeyId
import app.passwordstore.crypto.GpgIdentifier.UserId
import app.passwordstore.crypto.KeyUtils.tryGetId import app.passwordstore.crypto.KeyUtils.tryGetId
import app.passwordstore.crypto.PGPIdentifier.KeyId
import app.passwordstore.crypto.PGPIdentifier.UserId
import app.passwordstore.crypto.errors.KeyAlreadyExistsException import app.passwordstore.crypto.errors.KeyAlreadyExistsException
import app.passwordstore.crypto.errors.KeyNotFoundException import app.passwordstore.crypto.errors.KeyNotFoundException
import app.passwordstore.crypto.errors.NoKeysAvailableException import app.passwordstore.crypto.errors.NoKeysAvailableException