From afbd4e610513164ebd5e7461be17e5865b7c5e9f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 11:12:30 +0200 Subject: [PATCH] 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(); + } }