From 0440fb0ec5e1d476d4f61a8fe5b12f15bc7102a7 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Tue, 11 Aug 2026 17:13:17 +0300 Subject: [PATCH 1/4] fix(issue_3253_fix-PGPainless-key-parameter-validation-setup): enable PGPainless key parameter validation --- .../flowcrypt/email/FlowCryptApplication.kt | 60 ++++++++++--------- .../email/FlowCryptApplicationTest.kt | 21 +++++++ 2 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 FlowCrypt/src/test/java/com/flowcrypt/email/FlowCryptApplicationTest.kt diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt index d78a1bb35..4918156ca 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt @@ -8,6 +8,7 @@ package com.flowcrypt.email import android.app.Application import android.content.Context import android.util.Log +import androidx.annotation.VisibleForTesting import androidx.core.content.edit import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -85,12 +86,7 @@ class FlowCryptApplication : Application(), Configuration.Provider { } private fun setupPGPainless() { - PGPainless.setInstance( - PGPainless(algorithmPolicy = generatePGPainlessPolicy().apply { - //https://github.com/FlowCrypt/flowcrypt-android/issues/2111 - PGPainless.getInstance().algorithmPolicy.enableKeyParameterValidation = true - }) - ) + PGPainless.setInstance(createPGPainlessInstance()) } private fun setupGlobalSettingsForJavaMail() { @@ -104,27 +100,6 @@ class FlowCryptApplication : Application(), Configuration.Provider { initACRA() } - /** - * Allow sha1 for all builds except enterprise. It's a temporary solution. - * More details here https://github.com/FlowCrypt/flowcrypt-android/issues/1478 and here - * https://github.com/pgpainless/pgpainless/issues/158 - */ - @Suppress("KotlinConstantConditions") - private fun generatePGPainlessPolicy(): Policy { - val isEnterpriseBuild = BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE - val strongPolicySince2022 = HashAlgorithmPolicy.static2022SignatureHashAlgorithmPolicy() - val policyBefore2022Standard = - HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy() - return Policy.Builder(PGPainless.getInstance().algorithmPolicy) - .withDataSignatureHashAlgorithmPolicy( - if (isEnterpriseBuild) strongPolicySince2022 else policyBefore2022Standard - ) - .withCertificationSignatureHashAlgorithmPolicy( - if (isEnterpriseBuild) strongPolicySince2022 else policyBefore2022Standard - ) - .build() - } - private fun setupKeysStorage() { val keysStorage = KeysStorageImpl.getInstance(this) keysStorage.secretKeyRingsLiveData.observeForever { @@ -286,4 +261,35 @@ class FlowCryptApplication : Application(), Configuration.Provider { isAppForegroundedInternal = false } } + + companion object { + @VisibleForTesting + internal fun createPGPainlessInstance(): PGPainless { + return PGPainless(algorithmPolicy = generatePGPainlessPolicy().apply { + //https://github.com/FlowCrypt/flowcrypt-android/issues/2111 + enableKeyParameterValidation = true + }) + } + + /** + * Allow sha1 for all builds except enterprise. It's a temporary solution. + * More details here https://github.com/FlowCrypt/flowcrypt-android/issues/1478 and here + * https://github.com/pgpainless/pgpainless/issues/158 + */ + @Suppress("KotlinConstantConditions") + private fun generatePGPainlessPolicy(): Policy { + val isEnterpriseBuild = BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE + val strongPolicySince2022 = HashAlgorithmPolicy.static2022SignatureHashAlgorithmPolicy() + val policyBefore2022Standard = + HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy() + return Policy.Builder(PGPainless.getInstance().algorithmPolicy) + .withDataSignatureHashAlgorithmPolicy( + if (isEnterpriseBuild) strongPolicySince2022 else policyBefore2022Standard + ) + .withCertificationSignatureHashAlgorithmPolicy( + if (isEnterpriseBuild) strongPolicySince2022 else policyBefore2022Standard + ) + .build() + } + } } diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/FlowCryptApplicationTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/FlowCryptApplicationTest.kt new file mode 100644 index 000000000..7e087d077 --- /dev/null +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/FlowCryptApplicationTest.kt @@ -0,0 +1,21 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email + +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * @author Denys Bondarenko + */ +class FlowCryptApplicationTest { + @Test + fun testCreatePGPainlessInstanceEnablesKeyParameterValidation() { + val pgpainless = FlowCryptApplication.createPGPainlessInstance() + + assertTrue(pgpainless.algorithmPolicy.enableKeyParameterValidation) + } +} From a650ae821b315cff15e1fe3c93d2977a2132066b Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Tue, 11 Aug 2026 17:14:47 +0300 Subject: [PATCH 2/4] test(issue_3253_fix-PGPainless-key-parameter-validation-setup): re-enable public key parsing regression test --- .../test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt index 9d6e710d0..07c3d7106 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt @@ -17,7 +17,6 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue -import org.junit.Ignore import org.junit.Test import org.pgpainless.PGPainless import org.pgpainless.algorithm.HashAlgorithm @@ -150,7 +149,6 @@ class PgpKeyTest { } @Test - @Ignore("temporary disabled due to https://github.com/pgpainless/pgpainless/issues/488") fun testPublicKey_Issue1358() { val keyText = TestUtil.readResourceAsString("pgp/keys/issue-1358.public.gpg-key") val actual = PgpKey.parseKeys(source = keyText) From b58d060062d6cfade6daca91ade3e754e04c5d1e Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Tue, 11 Aug 2026 17:32:55 +0300 Subject: [PATCH 3/4] test(issue_3253_fix-PGPainless-key-parameter-validation-setup): verify public key parsing with production PGPainless policy --- .../flowcrypt/email/security/pgp/PgpKeyTest.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt index 07c3d7106..664d6724a 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt @@ -6,6 +6,7 @@ package com.flowcrypt.email.security.pgp import com.flowcrypt.email.BuildConfig +import com.flowcrypt.email.FlowCryptApplication import com.flowcrypt.email.security.model.Algo import com.flowcrypt.email.security.model.KeyId import com.flowcrypt.email.security.model.PgpKeyRingDetails @@ -150,9 +151,17 @@ class PgpKeyTest { @Test fun testPublicKey_Issue1358() { - val keyText = TestUtil.readResourceAsString("pgp/keys/issue-1358.public.gpg-key") - val actual = PgpKey.parseKeys(source = keyText) - assertEquals(1, actual.getAllKeys().size) + val originalPgpainless = PGPainless.getInstance() + try { + PGPainless.setInstance(FlowCryptApplication.createPGPainlessInstance()) + assertTrue(PGPainless.getInstance().algorithmPolicy.enableKeyParameterValidation) + + val keyText = TestUtil.readResourceAsString("pgp/keys/issue-1358.public.gpg-key") + val actual = PgpKey.parseKeys(source = keyText) + assertEquals(1, actual.getAllKeys().size) + } finally { + PGPainless.setInstance(originalPgpainless) + } } @Test From fea831a3b7157699d2226305d38c18244ec13e6b Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 13 Aug 2026 09:21:13 +0300 Subject: [PATCH 4/4] test(issue_3253_fix-PGPainless-key-parameter-validation-setup): use production policy for corrupted key validation --- .../java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt index 664d6724a..83782b302 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpKeyTest.kt @@ -166,8 +166,11 @@ class PgpKeyTest { @Test fun testReadCorruptedPrivateKey() { + val originalPgpainless = PGPainless.getInstance() try { - PGPainless.getInstance().algorithmPolicy.enableKeyParameterValidation = true + PGPainless.setInstance(FlowCryptApplication.createPGPainlessInstance()) + assertTrue(PGPainless.getInstance().algorithmPolicy.enableKeyParameterValidation) + val encryptedKeyText = TestUtil.readResourceAsString("pgp/keys/issue-1669-corrupted.private.gpg-key") val passphrase = Passphrase.fromPassword("123") @@ -175,7 +178,7 @@ class PgpKeyTest { PgpKey.checkSecretKeyIntegrity(encryptedKeyText, passphrase) } } finally { - PGPainless.getInstance().algorithmPolicy.enableKeyParameterValidation = false + PGPainless.setInstance(originalPgpainless) } }