diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/AbstractSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/AbstractSignatureBuilder.java index 3c859883..b99693bc 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/AbstractSignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/AbstractSignatureBuilder.java @@ -43,8 +43,8 @@ public abstract class AbstractSignatureBuilder +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.algorithm.SignatureType; +import org.pgpainless.exception.WrongPassphraseException; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; + +public class PrimaryKeyBindingSignatureBuilder extends AbstractSignatureBuilder { + + public PrimaryKeyBindingSignatureBuilder(PGPSecretKey subkey, SecretKeyRingProtector subkeyProtector) + throws WrongPassphraseException { + super(SignatureType.PRIMARYKEY_BINDING, subkey, subkeyProtector); + } + + public SelfSignatureSubpackets getHashedSubpackets() { + return hashedSubpackets; + } + + public SelfSignatureSubpackets getUnhashedSubpackets() { + return unhashedSubpackets; + } + + @Override + protected boolean isValidSignatureType(SignatureType type) { + return type == SignatureType.PRIMARYKEY_BINDING; + } + + public PGPSignature build(PGPPublicKey primaryKey) throws PGPException { + return buildAndInitSignatureGenerator() + .generateCertification(primaryKey, publicSigningKey); + } +} 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 new file mode 100644 index 00000000..e211e098 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureBuilder.java @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2021 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder; + +import java.io.IOException; +import javax.annotation.Nullable; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.signature.subpackets.BindingSignatureCallback; +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; + +public class SignatureBuilder { + + public SubkeyBindingSignatureBuilder subkeyBindingSignatureBuilder( + PGPSecretKey primaryKey, + SecretKeyRingProtector primaryKeyProtector, + PGPSecretKey subkey, + SecretKeyRingProtector subkeyProtector, + @Nullable BindingSignatureCallback subkeyBindingSubpacketsCallback, + @Nullable BindingSignatureCallback primaryKeyBindingSubpacketsCallback, + KeyFlag... flags) + throws PGPException, IOException { + if (flags.length == 0) { + throw new IllegalArgumentException("Keyflags for subkey binding cannot be empty."); + } + SubkeyBindingSignatureBuilder subkeyBindingBuilder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector); + + SelfSignatureSubpackets hashedSubpackets = subkeyBindingBuilder.getHashedSubpackets(); + hashedSubpackets.setKeyFlags(flags); + + boolean isSigningKey = false; + for (KeyFlag flag : flags) { + if (flag == KeyFlag.SIGN_DATA) { + isSigningKey = true; + break; + } + } + if (isSigningKey) { + PGPSignature backsig = primaryKeyBindingSignature( + subkey, subkeyProtector, primaryKey.getPublicKey(), primaryKeyBindingSubpacketsCallback); + hashedSubpackets.addEmbeddedSignature(backsig); + } + + if (subkeyBindingSubpacketsCallback != null) { + subkeyBindingSubpacketsCallback.modifyHashedSubpackets(subkeyBindingBuilder.getHashedSubpackets()); + subkeyBindingSubpacketsCallback.modifyUnhashedSubpackets(subkeyBindingBuilder.getUnhashedSubpackets()); + } + + return subkeyBindingBuilder; + } + + public PGPSignature primaryKeyBindingSignature( + PGPSecretKey subkey, + SecretKeyRingProtector subkeyProtector, + PGPPublicKey primaryKey, + BindingSignatureCallback primaryKeyBindingSubpacketsCallback) throws PGPException { + + PrimaryKeyBindingSignatureBuilder builder = new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector); + if (primaryKeyBindingSubpacketsCallback != null) { + primaryKeyBindingSubpacketsCallback.modifyHashedSubpackets(builder.getHashedSubpackets()); + primaryKeyBindingSubpacketsCallback.modifyUnhashedSubpackets(builder.getUnhashedSubpackets()); + } + + return builder.build(primaryKey); + } + +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java index 8e74510b..fad7e31b 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java @@ -15,8 +15,9 @@ import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; public class SubkeyBindingSignatureBuilder extends AbstractSignatureBuilder { - public SubkeyBindingSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector) throws WrongPassphraseException { - super(signatureType, signingKey, protector); + public SubkeyBindingSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) + throws WrongPassphraseException { + super(SignatureType.SUBKEY_BINDING, signingKey, protector); } @Override 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 new file mode 100644 index 00000000..ee2629d7 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/BindingSignatureCallback.java @@ -0,0 +1,12 @@ +// 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/SignatureSubpacketGeneratorWrapper.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapper.java index 6bb9aaf4..636bbefa 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapper.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapper.java @@ -75,23 +75,44 @@ public class SignatureSubpacketGeneratorWrapper private final List unsupportedSubpackets = new ArrayList<>(); public SignatureSubpacketGeneratorWrapper() { - setSignatureCreationTime(new Date()); + } - public SignatureSubpacketGeneratorWrapper(PGPPublicKey issuer) { - setSignatureCreationTime(new Date()); - setIssuerFingerprintAndKeyId(issuer); + public static SignatureSubpacketGeneratorWrapper refreshHashedSubpackets(PGPPublicKey issuer, PGPSignature oldSignature) { + return createHashedSubpacketsFrom(issuer, oldSignature.getHashedSubPackets()); } - public SignatureSubpacketGeneratorWrapper(PGPPublicKey issuer, PGPSignatureSubpacketVector base) { - extractSubpacketsFromVector(base); - setSignatureCreationTime(new Date()); - setIssuerFingerprintAndKeyId(issuer); + public static SignatureSubpacketGeneratorWrapper refreshUnhashedSubpackets(PGPSignature oldSignature) { + return createSubpacketsFrom(oldSignature.getUnhashedSubPackets()); } - public SignatureSubpacketGeneratorWrapper(PGPSignatureSubpacketVector base) { - extractSubpacketsFromVector(base); - setSignatureCreationTime(new Date()); + public static SignatureSubpacketGeneratorWrapper createHashedSubpacketsFrom(PGPPublicKey issuer, PGPSignatureSubpacketVector base) { + SignatureSubpacketGeneratorWrapper wrapper = createSubpacketsFrom(base); + wrapper.setIssuerFingerprintAndKeyId(issuer); + return wrapper; + } + + public static SignatureSubpacketGeneratorWrapper createSubpacketsFrom(PGPSignatureSubpacketVector base) { + SignatureSubpacketGeneratorWrapper wrapper = new SignatureSubpacketGeneratorWrapper(); + wrapper.extractSubpacketsFromVector(base); + wrapper.setSignatureCreationTime(new Date()); + return wrapper; + } + + public static SignatureSubpacketGeneratorWrapper createEmptySubpackets() { + return new SignatureSubpacketGeneratorWrapper(); + } + + public static SignatureSubpacketGeneratorWrapper createHashedSubpackets() { + SignatureSubpacketGeneratorWrapper wrapper = new SignatureSubpacketGeneratorWrapper(); + wrapper.setSignatureCreationTime(new Date()); + return wrapper; + } + + public static SignatureSubpacketGeneratorWrapper createHashedSubpackets(PGPPublicKey issuer) { + SignatureSubpacketGeneratorWrapper wrapper = createHashedSubpackets(); + wrapper.setIssuerFingerprintAndKeyId(issuer); + return wrapper; } private void extractSubpacketsFromVector(PGPSignatureSubpacketVector base) { 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 341940e5..b2c06cfb 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 @@ -20,7 +20,6 @@ import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.algorithm.SignatureType; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.util.Passphrase; @@ -40,7 +39,7 @@ public class SubkeyBindingSignatureBuilderTest { PGPPublicKey subkey = PGPainless.inspectKeyRing(tempSubkeyRing) .getEncryptionSubkeys(EncryptionPurpose.STORAGE_AND_COMMUNICATIONS).get(0); - SubkeyBindingSignatureBuilder skbb = new SubkeyBindingSignatureBuilder(SignatureType.SUBKEY_BINDING, secretKey.getSecretKey(), protector); + 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); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapperTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapperTest.java index 709b424c..fb1b0cd3 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapperTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketGeneratorWrapperTest.java @@ -69,13 +69,13 @@ public class SignatureSubpacketGeneratorWrapperTest { @BeforeEach public void createWrapper() { - wrapper = new SignatureSubpacketGeneratorWrapper(key); + wrapper = SignatureSubpacketGeneratorWrapper.createHashedSubpackets(key); } @Test public void initialStateTest() { Date now = new Date(); - wrapper = new SignatureSubpacketGeneratorWrapper(); + wrapper = SignatureSubpacketGeneratorWrapper.createHashedSubpackets(); PGPSignatureSubpacketVector vector = wrapper.getGenerator().generate(); assertEquals(now.getTime(), vector.getSignatureCreationTime().getTime(), 1000);