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:
parent
c168ce2e86
commit
5dac84c3c8
19 changed files with 94 additions and 95 deletions
|
@ -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> {
|
||||||
|
|
|
@ -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()) }
|
|
@ -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)
|
||||||
|
|
|
@ -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") }
|
||||||
|
|
|
@ -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()}")
|
||||||
}
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 = {}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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 {
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue