diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureBuilder.java index f64c0302..8b1308dd 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureBuilder.java @@ -14,36 +14,60 @@ import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.exception.WrongPassphraseException; import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.BindingSignatureCallback; -import org.pgpainless.signature.subpackets.SelfSignatureCallback; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; -public class SignatureBuilder { +public final class SignatureBuilder { - public SubkeyBindingSignatureBuilder bindSubkey( + private SignatureBuilder() { + + } + + public static SubkeyBindingSignatureBuilder bindNonSigningSubkey( + PGPSecretKey primaryKey, + SecretKeyRingProtector primaryKeyProtector, + @Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback, + KeyFlag... flags) throws WrongPassphraseException { + if (hasSignDataFlag(flags)) { + throw new IllegalArgumentException("Binding a subkey with SIGN_DATA flag requires primary key backsig." + + "Please use the method bindSigningSubkey()."); + } + + return bindSubkey(primaryKey, primaryKeyProtector, subkeyBindingSubpacketsCallback, flags); + } + + public static SubkeyBindingSignatureBuilder bindSigningSubkey( PGPSecretKey primaryKey, SecretKeyRingProtector primaryKeyProtector, PGPSecretKey subkey, SecretKeyRingProtector subkeyProtector, - @Nullable BindingSignatureCallback subkeyBindingSubpacketsCallback, - @Nullable BindingSignatureCallback primaryKeyBindingSubpacketsCallback, + @Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback, + @Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback, KeyFlag... flags) throws PGPException, IOException { - if (flags.length == 0) { - throw new IllegalArgumentException("Keyflags for subkey binding cannot be empty."); - } - SubkeyBindingSignatureBuilder subkeyBinder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector); - SelfSignatureSubpackets hashedSubpackets = subkeyBinder.getHashedSubpackets(); - SelfSignatureSubpackets unhashedSubpackets = subkeyBinder.getUnhashedSubpackets(); - hashedSubpackets.setKeyFlags(flags); + SubkeyBindingSignatureBuilder subkeyBinder = bindSubkey(primaryKey, primaryKeyProtector, subkeyBindingSubpacketsCallback, flags); if (hasSignDataFlag(flags)) { PGPSignature backsig = createPrimaryKeyBinding( subkey, subkeyProtector, primaryKeyBindingSubpacketsCallback, primaryKey.getPublicKey()); - hashedSubpackets.addEmbeddedSignature(backsig); + subkeyBinder.getHashedSubpackets().addEmbeddedSignature(backsig); } + return subkeyBinder; + } + + private static SubkeyBindingSignatureBuilder bindSubkey(PGPSecretKey primaryKey, + SecretKeyRingProtector primaryKeyProtector, + @Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback, + KeyFlag... flags) throws WrongPassphraseException { + if (flags.length == 0) { + throw new IllegalArgumentException("Keyflags for subkey binding cannot be empty."); + } + SubkeyBindingSignatureBuilder subkeyBinder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector); + SelfSignatureSubpackets hashedSubpackets = subkeyBinder.getHashedSubpackets(); + SelfSignatureSubpackets unhashedSubpackets = subkeyBinder.getUnhashedSubpackets(); + hashedSubpackets.setKeyFlags(flags); + if (subkeyBindingSubpacketsCallback != null) { subkeyBindingSubpacketsCallback.modifyHashedSubpackets(hashedSubpackets); subkeyBindingSubpacketsCallback.modifyUnhashedSubpackets(unhashedSubpackets); @@ -52,10 +76,10 @@ public class SignatureBuilder { return subkeyBinder; } - public PrimaryKeyBindingSignatureBuilder bindPrimaryKey( + public static PrimaryKeyBindingSignatureBuilder bindPrimaryKey( PGPSecretKey subkey, SecretKeyRingProtector subkeyProtector, - @Nullable BindingSignatureCallback primaryKeyBindingSubpacketsCallback) throws WrongPassphraseException { + @Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback) throws WrongPassphraseException { PrimaryKeyBindingSignatureBuilder primaryKeyBinder = new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector); if (primaryKeyBindingSubpacketsCallback != null) { @@ -66,20 +90,20 @@ public class SignatureBuilder { return primaryKeyBinder; } - public PGPSignature createPrimaryKeyBinding( + public static PGPSignature createPrimaryKeyBinding( PGPSecretKey subkey, SecretKeyRingProtector subkeyProtector, - @Nullable BindingSignatureCallback primaryKeyBindingSubpacketsCallback, + @Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback, PGPPublicKey primaryKey) throws PGPException { return bindPrimaryKey(subkey, subkeyProtector, primaryKeyBindingSubpacketsCallback) .build(primaryKey); } - public CertificationSignatureBuilder selfCertifyUserId( + public static CertificationSignatureBuilder selfCertifyUserId( PGPSecretKey primaryKey, SecretKeyRingProtector primaryKeyProtector, - @Nullable SelfSignatureCallback selfSignatureCallback, + @Nullable SelfSignatureSubpackets.Callback selfSignatureCallback, KeyFlag... flags) throws WrongPassphraseException { CertificationSignatureBuilder certifier = new CertificationSignatureBuilder(primaryKey, primaryKeyProtector); @@ -91,10 +115,10 @@ public class SignatureBuilder { return certifier; } - public CertificationSignatureBuilder renewSelfCertification( + public static CertificationSignatureBuilder renewSelfCertification( PGPSecretKey primaryKey, SecretKeyRingProtector primaryKeyProtector, - @Nullable SelfSignatureCallback selfSignatureCallback, + @Nullable SelfSignatureSubpackets.Callback selfSignatureCallback, PGPSignature oldCertification) throws WrongPassphraseException { CertificationSignatureBuilder certifier = new CertificationSignatureBuilder(primaryKey, primaryKeyProtector, oldCertification); @@ -103,11 +127,11 @@ public class SignatureBuilder { return null; } - public PGPSignature createUserIdSelfCertification( + public static PGPSignature createUserIdSelfCertification( String userId, PGPSecretKey primaryKey, SecretKeyRingProtector primaryKeyProtector, - @Nullable SelfSignatureCallback selfSignatureCallback, + @Nullable SelfSignatureSubpackets.Callback selfSignatureCallback, KeyFlag... flags) throws PGPException { return selfCertifyUserId(primaryKey, primaryKeyProtector, selfSignatureCallback, flags) diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/BindingSignatureCallback.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/BindingSignatureCallback.java deleted file mode 100644 index ee2629d7..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/BindingSignatureCallback.java +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.subpackets; - -public interface BindingSignatureCallback { - - void modifyHashedSubpackets(SelfSignatureSubpackets subpackets); - - void modifyUnhashedSubpackets(SelfSignatureSubpackets subpackets); -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.java index b71c3a05..d3ffd9d5 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.java @@ -12,6 +12,16 @@ import org.pgpainless.key.util.RevocationAttributes; public interface RevocationSignatureSubpackets extends BaseSignatureSubpackets { + interface Callback { + default void modifyHashedSubpackets(RevocationSignatureSubpackets subpackets) { + + } + + default void modifyUnhashedSubpackets(RevocationSignatureSubpackets subpackets) { + + } + } + SignatureSubpacketGeneratorWrapper setRevocationReason(RevocationAttributes revocationAttributes); SignatureSubpacketGeneratorWrapper setRevocationReason(boolean isCritical, RevocationAttributes revocationAttributes); diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureCallback.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureCallback.java deleted file mode 100644 index fda37510..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureCallback.java +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.subpackets; - -public interface SelfSignatureCallback { - - void modifyHashedSubpackets(SelfSignatureSubpackets subpackets); - - void modifyUnhashedSubpackets(SelfSignatureSubpackets subpackets); - -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.java index 6643902b..fa4eb719 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.java @@ -24,6 +24,16 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm; public interface SelfSignatureSubpackets extends BaseSignatureSubpackets { + interface Callback { + default void modifyHashedSubpackets(SelfSignatureSubpackets subpackets) { + + } + + default void modifyUnhashedSubpackets(SelfSignatureSubpackets subpackets) { + + } + } + SignatureSubpacketGeneratorWrapper setKeyFlags(KeyFlag... keyFlags); SignatureSubpacketGeneratorWrapper setKeyFlags(boolean isCritical, KeyFlag... keyFlags); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java index 79f2471e..d3f27bab 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java @@ -5,12 +5,15 @@ package org.pgpainless.signature.builder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; +import java.util.Iterator; import java.util.List; +import org.bouncycastle.bcpg.sig.NotationData; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKey; @@ -22,6 +25,8 @@ import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; +import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; import org.pgpainless.util.Passphrase; public class SubkeyBindingSignatureBuilderTest { @@ -36,20 +41,35 @@ public class SubkeyBindingSignatureBuilderTest { PGPSecretKeyRing tempSubkeyRing = PGPainless.generateKeyRing() .modernKeyRing("Subkeys", null); - PGPPublicKey subkey = PGPainless.inspectKeyRing(tempSubkeyRing) + PGPPublicKey subkeyPub = PGPainless.inspectKeyRing(tempSubkeyRing) .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0); + PGPSecretKey subkeySec = tempSubkeyRing.getSecretKey(subkeyPub.getKeyID()); - SubkeyBindingSignatureBuilder skbb = new SubkeyBindingSignatureBuilder(secretKey.getSecretKey(), protector); - skbb.getHashedSubpackets().addNotationData(false, "testnotation@pgpainless.org", "hello-world"); - skbb.getHashedSubpackets().setKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE); - PGPSignature binding = skbb.build(subkey); - subkey = PGPPublicKey.addCertification(subkey, binding); - PGPSecretKey secSubkey = tempSubkeyRing.getSecretKey(subkey.getKeyID()); - secSubkey = PGPSecretKey.replacePublicKey(secSubkey, subkey); - secretKey = PGPSecretKeyRing.insertSecretKey(secretKey, secSubkey); + PGPSignature binding = SignatureBuilder.bindNonSigningSubkey( + secretKey.getSecretKey(), protector, + new SelfSignatureSubpackets.Callback() { + @Override + public void modifyHashedSubpackets(SelfSignatureSubpackets subpackets) { + subpackets.addNotationData(false, "testnotation@pgpainless.org", "hello-world"); + } + }, KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) + .build(subkeyPub); + + subkeyPub = PGPPublicKey.addCertification(subkeyPub, binding); + subkeySec = PGPSecretKey.replacePublicKey(subkeySec, subkeyPub); + secretKey = PGPSecretKeyRing.insertSecretKey(secretKey, subkeySec); info = PGPainless.inspectKeyRing(secretKey); List nextSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY); assertEquals(previousSubkeys.size() + 1, nextSubkeys.size()); + subkeyPub = secretKey.getPublicKey(subkeyPub.getKeyID()); + Iterator newBindingSigs = subkeyPub.getSignaturesForKeyID(secretKey.getPublicKey().getKeyID()); + PGPSignature bindingSig = newBindingSigs.next(); + assertNotNull(bindingSig); + List notations = SignatureSubpacketsUtil.getHashedNotationData(bindingSig); + + assertEquals(1, notations.size()); + assertEquals("testnotation@pgpainless.org", notations.get(0).getNotationName()); + assertEquals("hello-world", notations.get(0).getNotationValue()); } }