From 6e4ffe290265ef8b3cd82f5ab6a7eb8c0157bf6a Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Sun, 11 Jul 2021 22:52:26 +0530 Subject: Add initial implementation of Gopenpgp-backed PGP (#1441) --- .../aps/ui/autofill/AutofillFilterActivity.kt | 7 +- .../ui/autofill/GopenpgpAutofillDecryptActivity.kt | 155 +++++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/dev/msfjarvis/aps/ui/autofill/GopenpgpAutofillDecryptActivity.kt (limited to 'app/src/main/java/dev/msfjarvis/aps/ui/autofill') diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterActivity.kt index afa2a6a0..1d5160e2 100644 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterActivity.kt +++ b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterActivity.kt @@ -29,6 +29,7 @@ import com.github.androidpasswordstore.autofillparser.FormOrigin import dev.msfjarvis.aps.R import dev.msfjarvis.aps.data.password.PasswordItem import dev.msfjarvis.aps.databinding.ActivityOreoAutofillFilterBinding +import dev.msfjarvis.aps.util.FeatureFlags import dev.msfjarvis.aps.util.autofill.AutofillMatcher import dev.msfjarvis.aps.util.autofill.AutofillPreferences import dev.msfjarvis.aps.util.autofill.DirectoryStructure @@ -220,7 +221,11 @@ class AutofillFilterView : AppCompatActivity() { AutofillMatcher.addMatchFor(applicationContext, formOrigin, item.file) // intent?.extras? is checked to be non-null in onCreate decryptAction.launch( - AutofillDecryptActivity.makeDecryptFileIntent(item.file, intent!!.extras!!, this) + if (FeatureFlags.ENABLE_GOPENPGP) { + GopenpgpAutofillDecryptActivity.makeDecryptFileIntent(item.file, intent!!.extras!!, this) + } else { + AutofillDecryptActivity.makeDecryptFileIntent(item.file, intent!!.extras!!, this) + } ) } } diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/GopenpgpAutofillDecryptActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/GopenpgpAutofillDecryptActivity.kt new file mode 100644 index 00000000..1c9f19d7 --- /dev/null +++ b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/GopenpgpAutofillDecryptActivity.kt @@ -0,0 +1,155 @@ +/* + * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +package dev.msfjarvis.aps.ui.autofill + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.IntentSender +import android.os.Build +import android.os.Bundle +import android.view.autofill.AutofillManager +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.github.ajalt.timberkt.d +import com.github.ajalt.timberkt.e +import com.github.androidpasswordstore.autofillparser.AutofillAction +import com.github.androidpasswordstore.autofillparser.Credentials +import com.github.michaelbull.result.getOrElse +import com.github.michaelbull.result.onFailure +import com.github.michaelbull.result.onSuccess +import com.github.michaelbull.result.runCatching +import dagger.hilt.android.AndroidEntryPoint +import dev.msfjarvis.aps.injection.crypto.CryptoSet +import dev.msfjarvis.aps.injection.password.PasswordEntryFactory +import dev.msfjarvis.aps.ui.crypto.GopenpgpDecryptActivity +import dev.msfjarvis.aps.util.autofill.AutofillPreferences +import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder +import dev.msfjarvis.aps.util.autofill.DirectoryStructure +import java.io.File +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +@RequiresApi(Build.VERSION_CODES.O) +@AndroidEntryPoint +class GopenpgpAutofillDecryptActivity : AppCompatActivity() { + + companion object { + + private const val EXTRA_FILE_PATH = "dev.msfjarvis.aps.autofill.oreo.EXTRA_FILE_PATH" + private const val EXTRA_SEARCH_ACTION = "dev.msfjarvis.aps.autofill.oreo.EXTRA_SEARCH_ACTION" + + private var decryptFileRequestCode = 1 + + fun makeDecryptFileIntent(file: File, forwardedExtras: Bundle, context: Context): Intent { + return Intent(context, GopenpgpAutofillDecryptActivity::class.java).apply { + putExtras(forwardedExtras) + putExtra(EXTRA_SEARCH_ACTION, true) + putExtra(EXTRA_FILE_PATH, file.absolutePath) + } + } + + fun makeDecryptFileIntentSender(file: File, context: Context): IntentSender { + val intent = + Intent(context, GopenpgpAutofillDecryptActivity::class.java).apply { + putExtra(EXTRA_SEARCH_ACTION, false) + putExtra(EXTRA_FILE_PATH, file.absolutePath) + } + return PendingIntent.getActivity( + context, + decryptFileRequestCode++, + intent, + PendingIntent.FLAG_CANCEL_CURRENT + ) + .intentSender + } + } + + @Inject lateinit var passwordEntryFactory: PasswordEntryFactory + @Inject lateinit var cryptos: CryptoSet + + private lateinit var directoryStructure: DirectoryStructure + + override fun onStart() { + super.onStart() + val filePath = + intent?.getStringExtra(EXTRA_FILE_PATH) + ?: run { + e { "GopenpgpAutofillDecryptActivity started without EXTRA_FILE_PATH" } + finish() + return + } + val clientState = + intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) + ?: run { + e { "GopenpgpAutofillDecryptActivity started without EXTRA_CLIENT_STATE" } + finish() + return + } + val isSearchAction = intent?.getBooleanExtra(EXTRA_SEARCH_ACTION, true)!! + val action = if (isSearchAction) AutofillAction.Search else AutofillAction.Match + directoryStructure = AutofillPreferences.directoryStructure(this) + d { action.toString() } + lifecycleScope.launch { + val credentials = decryptCredential(File(filePath)) + if (credentials == null) { + setResult(RESULT_CANCELED) + } else { + val fillInDataset = + AutofillResponseBuilder.makeFillInDataset( + this@GopenpgpAutofillDecryptActivity, + credentials, + clientState, + action + ) + withContext(Dispatchers.Main) { + setResult( + RESULT_OK, + Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) } + ) + } + } + withContext(Dispatchers.Main) { finish() } + } + } + + private suspend fun decryptCredential(file: File): Credentials? { + runCatching { file.inputStream() } + .onFailure { e -> + e(e) { "File to decrypt not found" } + return null + } + .onSuccess { encryptedInput -> + runCatching { + val crypto = cryptos.first { it.canHandle(file.absolutePath) } + withContext(Dispatchers.IO) { + crypto.decrypt( + GopenpgpDecryptActivity.PRIV_KEY, + GopenpgpDecryptActivity.PASS.toByteArray(charset = Charsets.UTF_8), + encryptedInput.readBytes() + ) + } + } + .onFailure { e -> + e(e) { "Decryption with Gopenpgp failed" } + return null + } + .onSuccess { result -> + return runCatching { + val entry = passwordEntryFactory.create(lifecycleScope, result) + AutofillPreferences.credentialsFromStoreEntry(this, file, entry, directoryStructure) + } + .getOrElse { e -> + e(e) { "Failed to parse password entry" } + return null + } + } + } + return null + } +} -- cgit v1.2.3