1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 02:09:38 +02:00

Compare commits

...

5 commits

11 changed files with 407 additions and 98 deletions

View file

@ -175,52 +175,4 @@ interface BaseSignatureSubpackets {
fun addEmbeddedSignature(embeddedSignature: EmbeddedSignature): BaseSignatureSubpackets fun addEmbeddedSignature(embeddedSignature: EmbeddedSignature): BaseSignatureSubpackets
fun clearEmbeddedSignatures(): 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)
}
}
}
}
} }

View file

@ -6,7 +6,22 @@ package org.pgpainless.signature.subpackets
interface CertificationSubpackets : BaseSignatureSubpackets { interface CertificationSubpackets : BaseSignatureSubpackets {
interface Callback : SignatureSubpacketCallback<CertificationSubpackets> interface Callback : SignatureSubpacketCallback<CertificationSubpackets> {
fun then(nextCallback: SignatureSubpacketCallback<CertificationSubpackets>): 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 { companion object {

View file

@ -6,10 +6,32 @@ package org.pgpainless.signature.subpackets
import org.bouncycastle.bcpg.sig.RevocationReason import org.bouncycastle.bcpg.sig.RevocationReason
import org.pgpainless.key.util.RevocationAttributes import org.pgpainless.key.util.RevocationAttributes
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets.Callback
interface RevocationSignatureSubpackets : BaseSignatureSubpackets { interface RevocationSignatureSubpackets : BaseSignatureSubpackets {
interface Callback : SignatureSubpacketCallback<RevocationSignatureSubpackets> interface Callback : SignatureSubpacketCallback<RevocationSignatureSubpackets> {
fun then(
nextCallback: SignatureSubpacketCallback<RevocationSignatureSubpackets>
): 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( fun setRevocationReason(
revocationAttributes: RevocationAttributes revocationAttributes: RevocationAttributes

View file

@ -121,6 +121,10 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets {
algorithms: PreferredAEADCiphersuites.Builder? algorithms: PreferredAEADCiphersuites.Builder?
): SelfSignatureSubpackets ): SelfSignatureSubpackets
fun setPreferredAEADCiphersuites(
preferredAEADCiphersuites: PreferredAEADCiphersuites?
): SelfSignatureSubpackets
@Deprecated("Use of this subpacket is discouraged.") @Deprecated("Use of this subpacket is discouraged.")
fun addRevocationKey(revocationKey: PGPPublicKey): SelfSignatureSubpackets fun addRevocationKey(revocationKey: PGPPublicKey): SelfSignatureSubpackets

View file

@ -70,51 +70,6 @@ class SignatureSubpackets(
fun createEmptySubpackets(): SignatureSubpackets { fun createEmptySubpackets(): SignatureSubpackets {
return SignatureSubpackets(PGPSignatureSubpacketGenerator()) 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( override fun setRevocationReason(
@ -326,9 +281,15 @@ class SignatureSubpackets(
override fun setPreferredAEADCiphersuites( override fun setPreferredAEADCiphersuites(
algorithms: PreferredAEADCiphersuites.Builder? algorithms: PreferredAEADCiphersuites.Builder?
): SignatureSubpackets = apply { ): SignatureSubpackets = setPreferredAEADCiphersuites(algorithms?.build())
override fun setPreferredAEADCiphersuites(
preferredAEADCiphersuites: PreferredAEADCiphersuites?
) = apply {
subpacketsGenerator.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS) 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 { override fun addRevocationKey(revocationKey: PGPPublicKey): SignatureSubpackets = apply {

View file

@ -61,6 +61,11 @@ class SignatureSubpacketsHelper {
PreferredAlgorithms( PreferredAlgorithms(
it.type, it.isCritical, it.isLongLength, it.data)) it.type, it.isCritical, it.isLongLength, it.data))
} }
SignatureSubpacket.preferredAEADAlgorithms ->
(subpacket as PreferredAEADCiphersuites).let {
subpackets.setPreferredAEADCiphersuites(
PreferredAEADCiphersuites(it.isCritical, it.rawAlgorithms))
}
SignatureSubpacket.revocationKey -> SignatureSubpacket.revocationKey ->
(subpacket as RevocationKey).let { (subpacket as RevocationKey).let {
subpackets.addRevocationKey( subpackets.addRevocationKey(
@ -130,7 +135,6 @@ class SignatureSubpacketsHelper {
SignatureSubpacket.keyServerPreferences, SignatureSubpacket.keyServerPreferences,
SignatureSubpacket.preferredKeyServers, SignatureSubpacket.preferredKeyServers,
SignatureSubpacket.placeholder, SignatureSubpacket.placeholder,
SignatureSubpacket.preferredAEADAlgorithms,
SignatureSubpacket.attestedCertification -> SignatureSubpacket.attestedCertification ->
subpackets.addResidualSubpacket(subpacket) subpackets.addResidualSubpacket(subpacket)
else -> subpackets.addResidualSubpacket(subpacket) else -> subpackets.addResidualSubpacket(subpacket)

View file

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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"));
}
}

View file

@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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());
}
}

View file

@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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());
}
}

View file

@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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());
}
}

View file

@ -534,4 +534,8 @@ public class SignatureSubpacketsTest {
PreferredAlgorithms aeadAlgorithms = (PreferredAlgorithms) vector.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); PreferredAlgorithms aeadAlgorithms = (PreferredAlgorithms) vector.getSubpacket(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
assertArrayEquals(aead.getPreferences(), aeadAlgorithms.getPreferences()); assertArrayEquals(aead.getPreferences(), aeadAlgorithms.getPreferences());
} }
public static SignatureSubpacket[] toArray(SignatureSubpackets subpackets) {
return subpackets.getSubpacketsGenerator().generate().toArray();
}
} }