Skip to content
5 changes: 3 additions & 2 deletions FlowCrypt/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/


Expand Down Expand Up @@ -209,6 +209,7 @@ android {
"META-INF/*.DSA",
"META-INF/*.RSA",
"META-INF/javamail.providers",
"META-INF/versions/9/OSGI-INF/MANIFEST.MF",
)
}

Expand Down Expand Up @@ -468,7 +469,7 @@ dependencies {
implementation("org.bitbucket.b_c:jose4j:0.9.6")
implementation("org.jsoup:jsoup:1.18.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
implementation("org.pgpainless:pgpainless-core:1.6.7")
implementation("org.pgpainless:pgpainless-core:1.7.2")
implementation("org.eclipse.angus:angus-mail:2.0.3")
implementation("org.eclipse.angus:gimap:2.0.3")
implementation("commons-io:commons-io:2.18.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.ui
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ import com.flowcrypt.email.ui.activity.fragment.ParseAndSavePubKeysFragmentArgs
import com.flowcrypt.email.util.PrivateKeysManager
import com.flowcrypt.email.util.TestGeneralUtil
import com.flowcrypt.email.viewaction.ClickOnViewInRecyclerViewItem
import org.hamcrest.Matchers.not
import org.junit.Ignore
import org.hamcrest.Matchers.allOf
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
Expand Down Expand Up @@ -86,14 +85,17 @@ class ParseAndSavePubKeysFragmentInIsolationTest : BaseTest() {
)

onView(withId(R.id.rVPubKeys))
.check(matches(withRecyclerViewItemCount(5)))
.check(matches(withRecyclerViewItemCount(6)))

onView(withId(R.id.rVPubKeys))
.check(
matches(
not(
hasItem(
withChild(
hasItem(
withChild(
allOf(
hasSibling(
withText(getResString(R.string.cannot_be_used_for_encryption))
),
hasSibling(
withText(
getResString(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email
Expand Down Expand Up @@ -38,7 +38,6 @@ import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.sender.HttpSender
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.policy.Policy.HashAlgorithmPolicy
import java.util.Calendar
import java.util.concurrent.TimeUnit
Expand Down Expand Up @@ -79,7 +78,7 @@ class FlowCryptApplication : Application(), Configuration.Provider {
enableDeprecatedSHA1ForPGPainlessPolicy()

//https://github.com/FlowCrypt/flowcrypt-android/issues/2111
PGPainless.getPolicy().isEnableKeyParameterValidation = true
PGPainless.getPolicy().enableKeyParameterValidation = true
}

private fun setupGlobalSettingsForJavaMail() {
Expand All @@ -100,16 +99,18 @@ class FlowCryptApplication : Application(), Configuration.Provider {
*/
private fun enableDeprecatedSHA1ForPGPainlessPolicy() {
@Suppress("KotlinConstantConditions")
if (BuildConfig.FLAVOR != Constants.FLAVOR_NAME_ENTERPRISE) {
PGPainless.getPolicy().signatureHashAlgorithmPolicy = HashAlgorithmPolicy(
HashAlgorithm.SHA512, listOf(
HashAlgorithm.SHA512,
HashAlgorithm.SHA384,
HashAlgorithm.SHA256,
HashAlgorithm.SHA224,
HashAlgorithm.SHA1
)
)
if (BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE) {
PGPainless.getPolicy().dataSignatureHashAlgorithmPolicy =
HashAlgorithmPolicy.static2022SignatureHashAlgorithmPolicy()

PGPainless.getPolicy().certificationSignatureHashAlgorithmPolicy =
HashAlgorithmPolicy.static2022SignatureHashAlgorithmPolicy()
} else {
PGPainless.getPolicy().dataSignatureHashAlgorithmPolicy =
HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy()

PGPainless.getPolicy().certificationSignatureHashAlgorithmPolicy =
HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: ivan
* Contributors: denbond7
*/

package com.flowcrypt.email.extensions.org.bouncycastle.openpgp
Expand All @@ -12,12 +12,16 @@ import com.flowcrypt.email.security.SecurityUtils
import com.flowcrypt.email.security.model.Algo
import com.flowcrypt.email.security.model.KeyId
import com.flowcrypt.email.security.model.PgpKeyRingDetails
import org.bouncycastle.bcpg.HashAlgorithmTags
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.pgpainless.algorithm.PublicKeyAlgorithm
import org.bouncycastle.openpgp.PGPSignature
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.bouncycastle.extensions.getCurveName
import org.pgpainless.bouncycastle.extensions.issuerKeyId
import org.pgpainless.key.OpenPgpV4Fingerprint
import org.pgpainless.key.generation.type.eddsa.EdDSACurve
import org.pgpainless.key.info.KeyInfo
import org.pgpainless.key.info.KeyRingInfo
import java.io.IOException
import java.time.Instant
Expand All @@ -40,11 +44,7 @@ fun PGPKeyRing.toPgpKeyRingDetails(hideArmorMeta: Boolean = false): PgpKeyRingDe
algorithm = keyRingInfo.algorithm.name,
algorithmId = keyRingInfo.algorithm.algorithmId,
bits = if (keyRingInfo.publicKey.bitStrength != -1) keyRingInfo.publicKey.bitStrength else 0,
curve = when (keyRingInfo.algorithm) {
PublicKeyAlgorithm.ECDSA, PublicKeyAlgorithm.ECDH -> KeyInfo.getCurveName(publicKey)
PublicKeyAlgorithm.EDDSA -> EdDSACurve._Ed25519.getName() // for EDDSA KeyInfo.getCurveName(publicKey) return null
else -> null
}
curve = runCatching { publicKey.getCurveName() }.getOrNull()
)

val keyIdList = publicKeys.iterator().asSequence().toList()
Expand All @@ -56,6 +56,13 @@ fun PGPKeyRing.toPgpKeyRingDetails(hideArmorMeta: Boolean = false): PgpKeyRingDe
throw IllegalArgumentException("There are no fingerprints")
}

if (containsHashAlgorithmWithSHA1()) {
val sigHashAlgoPolicy = PGPainless.getPolicy().certificationSignatureHashAlgorithmPolicy
if (!sigHashAlgoPolicy.isAcceptable(HashAlgorithm.SHA1)) {
throw PGPException("Unsupported signature(HashAlgorithm = SHA1)")
}
}

val privateKey = if (keyRingInfo.isSecretKey) armor(hideArmorMeta = hideArmorMeta) else null
val publicKey = if (keyRingInfo.isSecretKey) {
(this as PGPSecretKeyRing).toPublicKeyRing().armor(hideArmorMeta = hideArmorMeta)
Expand Down Expand Up @@ -100,3 +107,25 @@ val PGPKeyRing.expiration: Instant?
Instant.ofEpochMilli(publicKey.creationTime.time + publicKey.validSeconds * 1000)
}
}

/**
* https://github.com/pgpainless/pgpainless/issues/461
*/
fun PGPKeyRing.containsHashAlgorithmWithSHA1(): Boolean {
val hasSha1DirectKeySelfSignatures = publicKey.getSignaturesOfType(PGPSignature.DIRECT_KEY)
.asSequence()
.any { signature ->
signature.issuerKeyId == publicKey.keyID
&& signature.hashAlgorithm == HashAlgorithmTags.SHA1
}

return hasSha1DirectKeySelfSignatures || publicKey.userIDs.asSequence().any { uid ->
publicKey.getSignaturesForID(uid)
.asSequence()
.any { signature ->
signature.isCertification
&& signature.issuerKeyId == publicKey.keyID
&& signature.hashAlgorithm == HashAlgorithmTags.SHA1
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.extensions.org.pgpainless.util
Expand All @@ -12,5 +12,5 @@ import org.pgpainless.util.Passphrase
*/
val Passphrase.asString: String?
get() {
return chars?.let { String(it) }
return runCatching { getChars() }.getOrNull()?.let { String(it) }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.jetpack.viewmodel
Expand Down Expand Up @@ -150,7 +150,7 @@ class CheckPrivateKeysViewModel(
resultList.add(
CheckResult(
pgpKeyRingDetails = copy,
passphrase = passphrase.chars ?: throw IllegalArgumentException(),
passphrase = passphrase.getChars() ?: throw IllegalArgumentException(),
e = e
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class EditPrivateKeyViewModel(val fingerprint: String, application: Application)
roomDatabase.keysDao().updateSuspend(entity.copy(privateKey = encryptedPrvKey))

//update contacts and pub keys
val email = userId.email.lowercase()
val email = userId.email?.lowercase() ?: ""
var cachedRecipientWithPubKeys = roomDatabase.recipientDao()
.getRecipientWithPubKeysByEmailSuspend(email)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors:
* DenBond7
* Ivan Pizhenko
* Contributors: denbond7
*/

package com.flowcrypt.email.jetpack.viewmodel
Expand Down Expand Up @@ -498,7 +496,7 @@ class PrivateKeysViewModel(application: Application) : AccountViewModel(applicat
protectPrivateKeysLiveData.value =
Result.success(PgpKey.parsePrivateKeys(encryptedKeysSource).map { key ->
key.copy(
tempPassphrase = passphrase.chars,
tempPassphrase = passphrase.getChars(),
importInfo = (key.importInfo ?: PgpKeyRingDetails.ImportInfo()).copy(
importSourceType = sourceTypeInfo[key.fingerprint]
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.security
Expand Down Expand Up @@ -111,7 +111,7 @@ class KeysStorageImpl private constructor(context: Context) : KeysStorage {
return rings.map {
val pgpKeyRingDetails = it.toPgpKeyRingDetails()
val passphrase = getPassphraseByFingerprint(pgpKeyRingDetails.fingerprint)
pgpKeyRingDetails.copy(tempPassphrase = passphrase?.chars)
pgpKeyRingDetails.copy(tempPassphrase = passphrase?.getChars())
}
}

Expand Down Expand Up @@ -161,12 +161,12 @@ class KeysStorageImpl private constructor(context: Context) : KeysStorage {
override fun getSecretKeyRingProtector(): SecretKeyRingProtector {
val availablePGPSecretKeyRings = getPGPSecretKeyRings()
val passphraseProvider = object : SecretKeyPassphraseProvider {
override fun getPassphraseFor(keyId: Long): Passphrase? {
return doGetPassphrase(keyId, true)
override fun getPassphraseFor(keyId: Long?): Passphrase? {
return keyId?.let { doGetPassphrase(keyId, true) }
}

override fun hasPassphrase(keyId: Long): Boolean {
return doGetPassphrase(keyId, false) != null
override fun hasPassphrase(keyId: Long?): Boolean {
return keyId != null && doGetPassphrase(keyId, false) != null
}

private fun doGetPassphrase(keyId: Long, throwException: Boolean): Passphrase? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.security.pgp
Expand Down Expand Up @@ -172,7 +172,7 @@ object PgpEncryptAndOrSign {
generateDetachedSignatures: Boolean = false,
): EncryptionStream {
val encOpt = EncryptionOptions().apply {
passphrase?.let { addPassphrase(passphrase) }
passphrase?.let { addMessagePassphrase(passphrase) }
pgpPublicKeyRingCollection?.forEach {
addRecipient(it)
}
Expand Down Expand Up @@ -207,10 +207,10 @@ object PgpEncryptAndOrSign {
ProducerOptions.encrypt(encOpt)
}

producerOptions.isAsciiArmor = doArmor
producerOptions.isHideArmorHeaders = doArmor && hideArmorMeta
producerOptions.setAsciiArmor(doArmor)
producerOptions.setHideArmorHeaders(doArmor && hideArmorMeta)

fileName?.let { producerOptions.fileName = it }
fileName?.let { producerOptions.setFileName(it) }

return PGPainless.encryptAndOrSign()
.onOutputStream(destOutputStream)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: Ivan Pizhenko
* Contributors: denbond7
*/

package com.flowcrypt.email.security.pgp
Expand Down Expand Up @@ -187,7 +187,7 @@ object PgpKey {

private fun encryptKey(key: PGPSecretKeyRing, passphrase: Passphrase): PGPSecretKeyRing {
return PGPainless.modifyKeyRing(key)
.changePassphraseFromOldPassphrase(null)
.changePassphraseFromOldPassphrase(Passphrase.emptyPassphrase())
.withSecureDefaultSettings()
.toNewPassphrase(passphrase)
.done()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: Ivan Pizhenko,
* DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.security.pgp
Expand Down Expand Up @@ -831,15 +830,15 @@ object PgpMsg {

keyIdOfSigningKeys.addAll(invalidSignatureFailures.filter {
it.validationException.message?.matches("Missing verification key.?".toRegex()) == true
}.mapNotNull { it.signatureVerification.signature.keyID })
}.map { it.signature.keyID })
}

if (verifiedSignatures.isEmpty()) {
verifiedSignatures.addAll(messageMetadata.verifiedSignatures)
} else {
val keyIdsOfAllVerifiedSignatures = verifiedSignatures.map { it.signingKey?.keyId }
val keyIdsOfAllVerifiedSignatures = verifiedSignatures.map { it.signingKey.keyId }
val keyIdsOfCurrentVerifiedSignatures = messageMetadata.verifiedSignatures.map {
it.signingKey?.keyId
it.signingKey.keyId
}
if (keyIdsOfAllVerifiedSignatures != keyIdsOfCurrentVerifiedSignatures) {
hasMixedSignatures = true
Expand Down Expand Up @@ -1342,7 +1341,7 @@ object PgpMsg {

private fun moveElementsOutOfAnchorTag(element: Element, parent: Element) {
if (element.tag().normalName() == "a" && element.hasAttr(FC_INNER_TEXT_TYPE_ATTR)) {
val children = element.children().map { it as Node }.toTypedArray()
val children = element.children().map { it }.toTypedArray()
val n = element.childrenSize()
var index = 0
while (index < n && parent.child(index) !== element) ++index
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
* Contributors: denbond7
*/

package com.flowcrypt.email.security.pgp
Expand Down Expand Up @@ -34,7 +34,7 @@ object PgpSignature {
srcStream,
multiPassStrategy.messageOutputStream
)
String(multiPassStrategy.bytes)
String(multiPassStrategy.getBytes())
} catch (e: Exception) {
if (isSilent) {
e.printStackTrace()
Expand Down
Loading
Loading