From ffebdda2145e0f969cd624febca1f693ac3aad81 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 11:10:35 +0200 Subject: [PATCH 1/5] Remove unused SignatureSubpackets callback related methods --- .../subpackets/BaseSignatureSubpackets.kt | 48 ------------------- .../subpackets/SignatureSubpackets.kt | 45 ----------------- 2 files changed, 93 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt index 4b4c40d5..87e8d57e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt @@ -175,52 +175,4 @@ interface BaseSignatureSubpackets { fun addEmbeddedSignature(embeddedSignature: EmbeddedSignature): BaseSignatureSubpackets fun clearEmbeddedSignatures(): BaseSignatureSubpackets - - companion object { - - /** Factory method for a [Callback] that does nothing. */ - @JvmStatic fun nop() = object : Callback {} - - /** - * Factory function with receiver, which returns a [Callback] that modifies the hashed - * subpacket area of a [BaseSignatureSubpackets] object. - * - * Can be called like this: - * ``` - * val callback = BaseSignatureSubpackets.applyHashed { - * setCreationTime(date) - * ... - * } - * ``` - */ - @JvmStatic - fun applyHashed(function: BaseSignatureSubpackets.() -> Unit): Callback { - return object : Callback { - override fun modifyHashedSubpackets(hashedSubpackets: BaseSignatureSubpackets) { - function(hashedSubpackets) - } - } - } - - /** - * Factory function with receiver, which returns a [Callback] that modifies the unhashed - * subpacket area of a [BaseSignatureSubpackets] object. - * - * Can be called like this: - * ``` - * val callback = BaseSignatureSubpackets.applyUnhashed { - * setCreationTime(date) - * ... - * } - * ``` - */ - @JvmStatic - fun applyUnhashed(function: BaseSignatureSubpackets.() -> Unit): Callback { - return object : Callback { - override fun modifyUnhashedSubpackets(unhashedSubpackets: BaseSignatureSubpackets) { - function(unhashedSubpackets) - } - } - } - } } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt index e30c6100..8b831969 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt @@ -70,51 +70,6 @@ class SignatureSubpackets( fun createEmptySubpackets(): SignatureSubpackets { return SignatureSubpackets(PGPSignatureSubpacketGenerator()) } - - /** Factory method for a [Callback] that does nothing. */ - @JvmStatic fun nop() = object : Callback {} - - /** - * Factory function with receiver, which returns a [Callback] that modifies the hashed - * subpacket area of a [SignatureSubpackets] object. - * - * Can be called like this: - * ``` - * val callback = SignatureSubpackets.applyHashed { - * setCreationTime(date) - * ... - * } - * ``` - */ - @JvmStatic - fun applyHashed(function: SignatureSubpackets.() -> Unit): Callback { - return object : Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SignatureSubpackets) { - function(hashedSubpackets) - } - } - } - - /** - * Factory function with receiver, which returns a [Callback] that modifies the unhashed - * subpacket area of a [SignatureSubpackets] object. - * - * Can be called like this: - * ``` - * val callback = SignatureSubpackets.applyUnhashed { - * setCreationTime(date) - * ... - * } - * ``` - */ - @JvmStatic - fun applyUnhashed(function: SignatureSubpackets.() -> Unit): Callback { - return object : Callback { - override fun modifyUnhashedSubpackets(unhashedSubpackets: SignatureSubpackets) { - function(unhashedSubpackets) - } - } - } } override fun setRevocationReason( From 9a8ecb7fa01e14774c2f6d219fd2cc136ff89a31 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 11:11:20 +0200 Subject: [PATCH 2/5] Add missing implementations of then() method --- .../subpackets/CertificationSubpackets.kt | 17 ++++++++++++- .../RevocationSignatureSubpackets.kt | 24 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt index bb1d6550..a37c6984 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt @@ -6,7 +6,22 @@ package org.pgpainless.signature.subpackets interface CertificationSubpackets : BaseSignatureSubpackets { - interface Callback : SignatureSubpacketCallback + interface Callback : SignatureSubpacketCallback { + fun then(nextCallback: SignatureSubpacketCallback): Callback { + val currCallback = this + return object : Callback { + override fun modifyHashedSubpackets(hashedSubpackets: CertificationSubpackets) { + currCallback.modifyHashedSubpackets(hashedSubpackets) + nextCallback.modifyHashedSubpackets(hashedSubpackets) + } + + override fun modifyUnhashedSubpackets(unhashedSubpackets: CertificationSubpackets) { + currCallback.modifyUnhashedSubpackets(unhashedSubpackets) + nextCallback.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + } + } companion object { diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt index 79807322..5eaf5313 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt @@ -6,10 +6,32 @@ package org.pgpainless.signature.subpackets import org.bouncycastle.bcpg.sig.RevocationReason import org.pgpainless.key.util.RevocationAttributes +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets.Callback interface RevocationSignatureSubpackets : BaseSignatureSubpackets { - interface Callback : SignatureSubpacketCallback + interface Callback : SignatureSubpacketCallback { + fun then( + nextCallback: SignatureSubpacketCallback + ): Callback { + val currCallback = this + return object : Callback { + override fun modifyHashedSubpackets( + hashedSubpackets: RevocationSignatureSubpackets + ) { + currCallback.modifyHashedSubpackets(hashedSubpackets) + nextCallback.modifyHashedSubpackets(hashedSubpackets) + } + + override fun modifyUnhashedSubpackets( + unhashedSubpackets: RevocationSignatureSubpackets + ) { + currCallback.modifyUnhashedSubpackets(unhashedSubpackets) + nextCallback.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + } + } fun setRevocationReason( revocationAttributes: RevocationAttributes From 2270c69af7547f95677a4b39e140c111257153cd Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 11:12:08 +0200 Subject: [PATCH 3/5] setPreferredAEADCiphersuites(): Add missing method taking PreferredAEADCiphersuites object --- .../signature/subpackets/SelfSignatureSubpackets.kt | 4 ++++ .../signature/subpackets/SignatureSubpackets.kt | 10 ++++++++-- .../signature/subpackets/SignatureSubpacketsHelper.kt | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt index f15f0071..8619155c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt @@ -121,6 +121,10 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets { algorithms: PreferredAEADCiphersuites.Builder? ): SelfSignatureSubpackets + fun setPreferredAEADCiphersuites( + preferredAEADCiphersuites: PreferredAEADCiphersuites? + ): SelfSignatureSubpackets + @Deprecated("Use of this subpacket is discouraged.") fun addRevocationKey(revocationKey: PGPPublicKey): SelfSignatureSubpackets diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt index 8b831969..2a5ea016 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt @@ -281,9 +281,15 @@ class SignatureSubpackets( override fun setPreferredAEADCiphersuites( algorithms: PreferredAEADCiphersuites.Builder? - ): SignatureSubpackets = apply { + ): SignatureSubpackets = setPreferredAEADCiphersuites(algorithms?.build()) + + override fun setPreferredAEADCiphersuites( + preferredAEADCiphersuites: PreferredAEADCiphersuites? + ) = apply { subpacketsGenerator.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS) - algorithms?.let { subpacketsGenerator.setPreferredAEADCiphersuites(algorithms) } + preferredAEADCiphersuites?.let { + subpacketsGenerator.setPreferredAEADCiphersuites(it.isCritical, it.rawAlgorithms) + } } override fun addRevocationKey(revocationKey: PGPPublicKey): SignatureSubpackets = apply { diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt index ceb484d3..203b8e4f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt @@ -61,6 +61,11 @@ class SignatureSubpacketsHelper { PreferredAlgorithms( it.type, it.isCritical, it.isLongLength, it.data)) } + SignatureSubpacket.preferredAEADAlgorithms -> + (subpacket as PreferredAEADCiphersuites).let { + subpackets.setPreferredAEADCiphersuites( + PreferredAEADCiphersuites(it.isCritical, it.rawAlgorithms)) + } SignatureSubpacket.revocationKey -> (subpacket as RevocationKey).let { subpackets.addRevocationKey( @@ -130,7 +135,6 @@ class SignatureSubpacketsHelper { SignatureSubpacket.keyServerPreferences, SignatureSubpacket.preferredKeyServers, SignatureSubpacket.placeholder, - SignatureSubpacket.preferredAEADAlgorithms, SignatureSubpacket.attestedCertification -> subpackets.addResidualSubpacket(subpacket) else -> subpackets.addResidualSubpacket(subpacket) From afbd4e610513164ebd5e7461be17e5865b7c5e9f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 11:12:30 +0200 Subject: [PATCH 4/5] Add tests for SignatureSubpacketsCallback implementations --- .../CertificationSubpacketsTest.java | 101 ++++++++++++++++ .../RevocationSignatureSubpacketsTest.java | 101 ++++++++++++++++ .../SelfSignatureSubpacketsTest.java | 108 ++++++++++++++++++ .../subpackets/SignatureSubpacketsTest.java | 4 + 4 files changed, 314 insertions(+) create mode 100644 pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/CertificationSubpacketsTest.java create mode 100644 pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpacketsTest.java create mode 100644 pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SelfSignatureSubpacketsTest.java diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/CertificationSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/CertificationSubpacketsTest.java new file mode 100644 index 00000000..4394ee47 --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/CertificationSubpacketsTest.java @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.subpackets; + +import kotlin.Unit; +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.sig.IssuerFingerprint; +import org.bouncycastle.bcpg.sig.NotationData; +import org.junit.jupiter.api.Test; +import org.pgpainless.key.TestKeys; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.pgpainless.signature.subpackets.SignatureSubpacketsTest.toArray; + +public class CertificationSubpacketsTest { + + @Test + public void testNopDoesNothing() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + CertificationSubpackets.Callback cb = CertificationSubpackets.nop(); + + cb.modifyHashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + } + + @Test + public void testApplyHashed() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + CertificationSubpackets.Callback cb = CertificationSubpackets.applyHashed( + selfSignatureSubpackets -> { + selfSignatureSubpackets.setIssuerFingerprint(new IssuerFingerprint(false, 4, TestKeys.ROMEO_FINGERPRINT.getBytes())); + return Unit.INSTANCE; + }); + + assertEquals(0, toArray(subpackets).length); + + // The callback only applies to hashed subpackets, so modifying unhashed area does nothing + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyHashedSubpackets(subpackets); + assertEquals(1, toArray(subpackets).length); + } + + @Test + public void testApplyUnhashed() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + CertificationSubpackets.Callback cb = CertificationSubpackets.applyUnhashed( + selfSignatureSubpackets -> { + selfSignatureSubpackets.setIssuerKeyId(123L); + return Unit.INSTANCE; + }); + + assertEquals(0, toArray(subpackets).length); + + // The callback only applies to unhashed subpackets, so modifying hashed area does nothing + cb.modifyHashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(1, toArray(subpackets).length); + } + + @Test + public void testThen() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + + CertificationSubpackets.Callback first = CertificationSubpackets.applyHashed(selfSignatureSubpackets -> { + selfSignatureSubpackets.setIssuerFingerprint(new IssuerFingerprint(false, 4, TestKeys.ROMEO_FINGERPRINT.getBytes())); + selfSignatureSubpackets.addNotationData(false, "test@pgpainless.org", "foo"); + return Unit.INSTANCE; + }); + + CertificationSubpackets.Callback second = CertificationSubpackets.applyHashed(selfSignatureSubpackets -> { + selfSignatureSubpackets.setIssuerFingerprint(new IssuerFingerprint(true, 4, TestKeys.ROMEO_FINGERPRINT.getBytes())); + selfSignatureSubpackets.addNotationData(false, "test@pgpainless.org", "bar"); + return Unit.INSTANCE; + }); + + CertificationSubpackets.Callback both = first.then(second); + both.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + both.modifyHashedSubpackets(subpackets); + + SignatureSubpacket[] array = toArray(subpackets); + assertEquals(3, array.length); + NotationData n1 = (NotationData) array[0]; + assertEquals("foo", n1.getNotationValue()); + IssuerFingerprint fingerprint = (IssuerFingerprint) array[1]; + assertTrue(fingerprint.isCritical()); + NotationData n2 = (NotationData) array[2]; + assertEquals("bar", n2.getNotationValue()); + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpacketsTest.java new file mode 100644 index 00000000..8d4d2d96 --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpacketsTest.java @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.subpackets; + +import kotlin.Unit; +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.bcpg.sig.RevocationReason; +import org.junit.jupiter.api.Test; +import org.pgpainless.key.util.RevocationAttributes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.pgpainless.signature.subpackets.SignatureSubpacketsTest.toArray; + +public class RevocationSignatureSubpacketsTest { + + @Test + public void testNopDoesNothing() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + RevocationSignatureSubpackets.Callback cb = RevocationSignatureSubpackets.nop(); + + cb.modifyHashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + } + + + @Test + public void testApplyHashed() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + RevocationSignatureSubpackets.Callback cb = RevocationSignatureSubpackets.applyHashed( + selfSignatureSubpackets -> { + selfSignatureSubpackets.setRevocationReason(true, RevocationAttributes.Reason.KEY_COMPROMISED, "Leaked"); + return Unit.INSTANCE; + }); + + assertEquals(0, toArray(subpackets).length); + + // The callback only applies to hashed subpackets, so modifying unhashed area does nothing + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyHashedSubpackets(subpackets); + assertEquals(1, toArray(subpackets).length); + } + + @Test + public void testApplyUnhashed() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + RevocationSignatureSubpackets.Callback cb = RevocationSignatureSubpackets.applyUnhashed( + selfSignatureSubpackets -> { + selfSignatureSubpackets.setIssuerKeyId(123L); + return Unit.INSTANCE; + }); + + assertEquals(0, toArray(subpackets).length); + + // The callback only applies to unhashed subpackets, so modifying hashed area does nothing + cb.modifyHashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(1, toArray(subpackets).length); + } + + @Test + public void testThen() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + + RevocationSignatureSubpackets.Callback first = RevocationSignatureSubpackets.applyHashed(selfSignatureSubpackets -> { + selfSignatureSubpackets.setRevocationReason(true, RevocationAttributes.Reason.KEY_COMPROMISED, "Leakett (typo)"); + selfSignatureSubpackets.addNotationData(false, "test@pgpainless.org", "foo"); + return Unit.INSTANCE; + }); + + RevocationSignatureSubpackets.Callback second = RevocationSignatureSubpackets.applyHashed(selfSignatureSubpackets -> { + selfSignatureSubpackets.setRevocationReason(true, RevocationAttributes.Reason.KEY_COMPROMISED, "Leaked"); + selfSignatureSubpackets.addNotationData(false, "test@pgpainless.org", "bar"); + return Unit.INSTANCE; + }); + + RevocationSignatureSubpackets.Callback both = first.then(second); + both.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + both.modifyHashedSubpackets(subpackets); + + SignatureSubpacket[] array = toArray(subpackets); + assertEquals(3, array.length); + NotationData n1 = (NotationData) array[0]; + assertEquals("foo", n1.getNotationValue()); + RevocationReason reason = (RevocationReason) array[1]; + assertEquals(RevocationAttributes.Reason.KEY_COMPROMISED.code(), reason.getRevocationReason()); + NotationData n2 = (NotationData) array[2]; + assertEquals("bar", n2.getNotationValue()); + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SelfSignatureSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SelfSignatureSubpacketsTest.java new file mode 100644 index 00000000..6a19a3ff --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SelfSignatureSubpacketsTest.java @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.subpackets; + +import kotlin.Unit; +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.bcpg.sig.PreferredAlgorithms; +import org.junit.jupiter.api.Test; +import org.pgpainless.algorithm.HashAlgorithm; +import org.pgpainless.algorithm.KeyFlag; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.pgpainless.signature.subpackets.SignatureSubpacketsTest.toArray; + +public class SelfSignatureSubpacketsTest { + + @Test + public void testNopDoesNothing() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + SelfSignatureSubpackets.Callback cb = SelfSignatureSubpackets.nop(); + + cb.modifyHashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + } + + @Test + public void testApplyHashed() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + SelfSignatureSubpackets.Callback cb = SelfSignatureSubpackets.applyHashed( + selfSignatureSubpackets -> { + selfSignatureSubpackets.setKeyFlags(KeyFlag.CERTIFY_OTHER); + return Unit.INSTANCE; + }); + + assertEquals(0, toArray(subpackets).length); + + // The callback only applies to hashed subpackets, so modifying unhashed area does nothing + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyHashedSubpackets(subpackets); + assertEquals(1, toArray(subpackets).length); + } + + @Test + public void testApplyUnhashed() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + SelfSignatureSubpackets.Callback cb = SelfSignatureSubpackets.applyUnhashed( + selfSignatureSubpackets -> { + selfSignatureSubpackets.setIssuerKeyId(123L); + return Unit.INSTANCE; + }); + + assertEquals(0, toArray(subpackets).length); + + // The callback only applies to unhashed subpackets, so modifying hashed area does nothing + cb.modifyHashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + cb.modifyUnhashedSubpackets(subpackets); + assertEquals(1, toArray(subpackets).length); + } + + @Test + public void testThen() { + SignatureSubpackets subpackets = new SignatureSubpackets(); + + SelfSignatureSubpackets.Callback first = SelfSignatureSubpackets.applyHashed(selfSignatureSubpackets -> { + selfSignatureSubpackets.setPreferredHashAlgorithms(HashAlgorithm.SHA256, HashAlgorithm.SHA512); + selfSignatureSubpackets.setKeyFlags(KeyFlag.CERTIFY_OTHER); + selfSignatureSubpackets.addNotationData(false, "test@pgpainless.org", "foo"); + return Unit.INSTANCE; + }); + + SelfSignatureSubpackets.Callback second = SelfSignatureSubpackets.applyHashed(selfSignatureSubpackets -> { + selfSignatureSubpackets.setKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA); + selfSignatureSubpackets.addNotationData(false, "test@pgpainless.org", "bar"); + return Unit.INSTANCE; + }); + + SelfSignatureSubpackets.Callback both = first.then(second); + both.modifyUnhashedSubpackets(subpackets); + assertEquals(0, toArray(subpackets).length); + + both.modifyHashedSubpackets(subpackets); + + SignatureSubpacket[] array = toArray(subpackets); + assertEquals(4, array.length); + PreferredAlgorithms hashAlgs = (PreferredAlgorithms) array[0]; + assertArrayEquals( + new int[] {HashAlgorithm.SHA256.getAlgorithmId(), HashAlgorithm.SHA512.getAlgorithmId()}, + hashAlgs.getPreferences()); + NotationData n1 = (NotationData) array[1]; + assertEquals("foo", n1.getNotationValue()); + KeyFlags flags = (KeyFlags) array[2]; + assertEquals(KeyFlag.toBitmask(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA), flags.getFlags()); + NotationData n2 = (NotationData) array[3]; + assertEquals("bar", n2.getNotationValue()); + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java index d4897a7b..733356d5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java @@ -534,4 +534,8 @@ public class SignatureSubpacketsTest { PreferredAlgorithms aeadAlgorithms = (PreferredAlgorithms) vector.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); assertArrayEquals(aead.getPreferences(), aeadAlgorithms.getPreferences()); } + + public static SignatureSubpacket[] toArray(SignatureSubpackets subpackets) { + return subpackets.getSubpacketsGenerator().generate().toArray(); + } } From b3602bf2e491a54ecd4259ad93c9486050b32156 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 11:21:31 +0200 Subject: [PATCH 5/5] Add test for PolicyAdapter properly adapting NotationRegistry implementations --- .../org/bouncycastle/PolicyAdapterTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pgpainless-core/src/test/java/org/bouncycastle/PolicyAdapterTest.java diff --git a/pgpainless-core/src/test/java/org/bouncycastle/PolicyAdapterTest.java b/pgpainless-core/src/test/java/org/bouncycastle/PolicyAdapterTest.java new file mode 100644 index 00000000..9407d7d0 --- /dev/null +++ b/pgpainless-core/src/test/java/org/bouncycastle/PolicyAdapterTest.java @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.bouncycastle; + +import org.bouncycastle.openpgp.api.OpenPGPPolicy; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.bouncycastle.PolicyAdapter; +import org.pgpainless.policy.Policy; +import org.pgpainless.util.NotationRegistry; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PolicyAdapterTest { + + @Test + public void testNotationRegistryAdaption() { + NotationRegistry pgpainlessNotationReg = new NotationRegistry(); + pgpainlessNotationReg.addKnownNotation("foo"); + + Policy policy = PGPainless.getInstance().getAlgorithmPolicy() + .copy() + .withNotationRegistry(pgpainlessNotationReg) + .build(); + + PolicyAdapter adapter = new PolicyAdapter(policy); + OpenPGPPolicy.OpenPGPNotationRegistry bcNotationReg = adapter.getNotationRegistry(); + assertTrue(bcNotationReg.isNotationKnown("foo")); + assertFalse(bcNotationReg.isNotationKnown("bar")); + bcNotationReg.addKnownNotation("bar"); + + assertTrue(pgpainlessNotationReg.isKnownNotation("bar")); + } +}