Skip to content

Commit

Permalink
Issue 1255 pass in ram decrypt atts (#1285)
Browse files Browse the repository at this point in the history
* Improve handling PgpDecrypt.DecryptionErrorType.NEED_PASSPHRASE.| #1255

* Improved AttachmentNotificationManager.errorHappened().| #1255

* Added handling PgpDecrypt.DecryptionErrorType.NEED_PASSPHRASE during the att decryption(notification).| #1255

* Made KeysStorageImpl.getPgpKeyDetailsList() synchronized to prevent crashes.| #1255

* Added asking a missed passphrase if we trying to download encrypted attachments.| #1255

* Added a test.| #1255
  • Loading branch information
DenBond7 authored Jun 10, 2021
1 parent 9a5eacf commit 073d4c9
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.flowcrypt.email.R
import com.flowcrypt.email.TestConstants
import com.flowcrypt.email.api.email.model.AttachmentInfo
import com.flowcrypt.email.api.email.model.IncomingMessageInfo
import com.flowcrypt.email.api.retrofit.response.model.node.DecryptErrorDetails
import com.flowcrypt.email.api.retrofit.response.model.node.DecryptErrorMsgBlock
Expand Down Expand Up @@ -233,6 +234,41 @@ class MessageDetailsActivityPassphraseInRamTest : BaseMessageDetailsActivityTest
checkWebView(decryptedInfo)
}

@Test
fun testShowNeedPassphraseDialogWhenTryingToDownloadAttachment() {
val encryptedAttInfo = TestGeneralUtil.getObjectFromJson(
"messages/attachments/encrypted_att.json",
AttachmentInfo::class.java
)

val msgInfo = getMsgInfo(
"messages/info/encrypted_msg_info_text_with_one_att.json",
"messages/mime/encrypted_msg_info_plain_text_with_one_att.txt", encryptedAttInfo
)

launchActivity(msgInfo!!.msgEntity)

//close a dialog during start up
onView(withText(getResString(R.string.cancel)))
.inRoot(isDialog())
.check(matches(isDisplayed()))
.perform(click())

//Click on the download button
onView(withId(R.id.imageButtonDownloadAtt))
.check(matches(isDisplayed()))
.perform(click())

//check that a dialog is displayed
val tVStatusMessageText = getQuantityString(
R.plurals.please_provide_passphrase_for_following_keys,
1
)
onView(withText(tVStatusMessageText))
.inRoot(isDialog())
.check(matches(isDisplayed()))
}

private fun checkWebView(decryptedInfo: IncomingMessageInfo?) {
//todo-denbond7 improve that in the future
//we need that to wait while webview rendered content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class KeysStorageImpl private constructor(context: Context) : KeysStorage {
return secretKeyRingsLiveData.value ?: emptyList()
}

@Synchronized
override fun getPgpKeyDetailsList(): List<PgpKeyDetails> {
val list = mutableListOf<PgpKeyDetails>()
for (secretKey in getPGPSecretKeyRings()) {
Expand Down Expand Up @@ -160,11 +161,15 @@ class KeysStorageImpl private constructor(context: Context) : KeysStorage {
if (keyIDs.contains(keyId)) {
for (secretKey in pgpSecretKeyRing.secretKeys) {
val openPgpV4Fingerprint = OpenPgpV4Fingerprint(secretKey)
val passphrase = getPassphraseByFingerprint(openPgpV4Fingerprint.toString())
?: throw DecryptionException(
val fingerprint = openPgpV4Fingerprint.toString()
val passphrase = getPassphraseByFingerprint(fingerprint)
if (passphrase == null || passphrase.isEmpty) {
throw DecryptionException(
decryptionErrorType = PgpDecrypt.DecryptionErrorType.NEED_PASSPHRASE,
e = PGPException("flowcrypt: need passphrase")
e = PGPException("flowcrypt: need passphrase"),
fingerprints = listOf(fingerprint)
)
}
return@PasswordBasedSecretKeyRingProtector passphrase
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.flowcrypt.email.database.FlowCryptRoomDatabase
import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.extensions.org.bouncycastle.openpgp.toPgpKeyDetails
import com.flowcrypt.email.security.model.PgpKeyDetails
import com.flowcrypt.email.security.pgp.PgpDecrypt
import com.flowcrypt.email.security.pgp.PgpKey
import com.flowcrypt.email.security.pgp.PgpPwd
import com.flowcrypt.email.util.exception.DifferentPassPhrasesException
Expand Down Expand Up @@ -161,5 +162,13 @@ class SecurityUtils {
fun generateRandomUUID(): String {
return String(Hex.encodeHex(DigestUtils.sha1(UUID.randomUUID().toString())))
}

/**
* Check if the file extension fits the encrypted pattern.
* If yes - it can mean the file is encrypted
*/
fun isEncryptedData(fileName: String?): Boolean {
return PgpDecrypt.DETECT_SEPARATE_ENCRYPTED_ATTACHMENTS_PATTERN.find(fileName ?: "") != null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.extensions.kotlin.toHex
import com.flowcrypt.email.jetpack.viewmodel.AccountViewModel
import com.flowcrypt.email.security.KeysStorageImpl
import com.flowcrypt.email.security.SecurityUtils
import com.flowcrypt.email.security.pgp.PgpDecrypt
import com.flowcrypt.email.util.FileAndDirectoryUtils
import com.flowcrypt.email.util.GeneralUtil
Expand Down Expand Up @@ -653,8 +654,7 @@ class AttachmentDownloadManagerService : Service() {
throw NullPointerException("Error. The file is missing")
}

val regex = PgpDecrypt.DETECT_SEPARATE_ENCRYPTED_ATTACHMENTS_PATTERN
if (regex.find(att.name ?: "") == null) {
if (!SecurityUtils.isEncryptedData(att.name)) {
return file
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import androidx.core.app.NotificationCompat
import com.flowcrypt.email.BuildConfig
import com.flowcrypt.email.R
import com.flowcrypt.email.api.email.model.AttachmentInfo
import com.flowcrypt.email.security.pgp.PgpDecrypt
import com.flowcrypt.email.ui.notifications.CustomNotificationManager
import com.flowcrypt.email.ui.notifications.NotificationChannelManager
import com.flowcrypt.email.util.GeneralUtil
import com.flowcrypt.email.util.exception.DecryptionException
import java.util.Random

/**
Expand Down Expand Up @@ -127,13 +130,39 @@ class AttachmentNotificationManager(context: Context) : CustomNotificationManage
fun errorHappened(context: Context, attInfo: AttachmentInfo, e: Exception) {
val builder = genDefBuilder(context, attInfo)

val contentText = if (TextUtils.isEmpty(e.message))
context.getString(
R.string
.error_occurred_please_try_again
)
else
e.message
val bigText = StringBuilder()
val contentText = StringBuilder()
when {
TextUtils.isEmpty(e.message) -> {
contentText.append(context.getString(R.string.error_occurred_please_try_again))
bigText.append(e.javaClass.simpleName)
}

e.cause != null -> {
contentText.append(e.cause?.message ?: "")
bigText.append(e.javaClass.simpleName + ": " + e.message)
}

else -> {
contentText.append(e.message)
bigText.append(e.javaClass.simpleName + ": " + e.message)
}
}

if (e is DecryptionException) {
if (e.decryptionErrorType == PgpDecrypt.DecryptionErrorType.NEED_PASSPHRASE) {
contentText.clear()
contentText.append(context.getString(R.string.provide_passphrase_to_decrypt_file))
val fingerprint = e.fingerprints.firstOrNull()
fingerprint?.let {
val additionalText = context.resources.getQuantityString(
R.plurals.please_provide_passphrase_for_following_keys,
1
) + "\n" + GeneralUtil.doSectionsInText(" ", it, 4) + "\n\n"
bigText.insert(0, additionalText)
}
}
}

builder.setProgress(0, 0, false)
.setAutoCancel(true)
Expand All @@ -146,6 +175,10 @@ class AttachmentNotificationManager(context: Context) : CustomNotificationManage
.setGroup(GROUP_NAME_ATTACHMENTS)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)

if (bigText.isNotEmpty()) {
builder.setStyle(NotificationCompat.BigTextStyle().bigText(bigText))
}

notificationManagerCompat.notify(attInfo.id, attInfo.uid.toInt(), builder.build())
prepareAndShowNotificationsGroup(context, attInfo, false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import com.flowcrypt.email.jetpack.viewmodel.factory.MsgDetailsViewModelFactory
import com.flowcrypt.email.model.MessageEncryptionType
import com.flowcrypt.email.model.MessageType
import com.flowcrypt.email.model.PgpContact
import com.flowcrypt.email.security.SecurityUtils
import com.flowcrypt.email.service.attachment.AttachmentDownloadManagerService
import com.flowcrypt.email.ui.activity.CreateMessageActivity
import com.flowcrypt.email.ui.activity.ImportPrivateKeyActivity
Expand Down Expand Up @@ -129,6 +130,21 @@ class MessageDetailsFragment : BaseFragment(), ProgressBehaviour, View.OnClickLi
override fun onDownloadClick(attachmentInfo: AttachmentInfo) {
lastClickedAtt = attachmentInfo
lastClickedAtt?.orderNumber = GeneralUtil.genAttOrderId(requireContext())

if (SecurityUtils.isEncryptedData(attachmentInfo.name)) {
for (block in msgInfo?.msgBlocks ?: emptyList()) {
if (block.type == MsgBlock.Type.DECRYPT_ERROR) {
val decryptErrorMsgBlock = block as? DecryptErrorMsgBlock ?: continue
val decryptErrorDetails = decryptErrorMsgBlock.error?.details ?: continue
if (decryptErrorDetails.type == DecryptErrorDetails.Type.NEED_PASSPHRASE) {
val fingerprints = decryptErrorMsgBlock.error.longIds?.needPassphrase ?: continue
showNeedPassphraseDialog(fingerprints)
return
}
}
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
ContextCompat.checkSelfPermission(
requireContext(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ import com.flowcrypt.email.security.pgp.PgpDecrypt
*/
class DecryptionException(
val decryptionErrorType: PgpDecrypt.DecryptionErrorType,
val e: Exception
val e: Exception,
val fingerprints: List<String> = emptyList()
) : FlowCryptException(e)
1 change: 1 addition & 0 deletions FlowCrypt/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@
<string name="silent">Silent</string>
<string name="silent_notifications_notification_channel">The silent notifications channel</string>
<string name="passphrase_type_undefined">Pass phrase type is undefined</string>
<string name="provide_passphrase_to_decrypt_file">Please provide a pass phrase to decrypt this file</string>
<plurals name="please_provide_passphrase_for_following_keys">
<item quantity="one">Please provide a pass phrase for the following key</item>
<item quantity="other">Please provide a pass phrase for at least one of the following keys</item>
Expand Down

0 comments on commit 073d4c9

Please sign in to comment.