From f07063d55f3216366b141762dec12fa2b9f0cb29 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Nov 2023 16:21:08 +0100 Subject: [PATCH] Kotlin conversion: SignatureBuilder classes --- .../builder/AbstractSignatureBuilder.java | 140 ------------------ .../DirectKeySelfSignatureBuilder.java | 57 ------- .../PrimaryKeyBindingSignatureBuilder.java | 59 -------- .../builder/RevocationSignatureBuilder.java | 71 --------- .../builder/SelfSignatureBuilder.java | 74 --------- .../SubkeyBindingSignatureBuilder.java | 72 --------- ...irdPartyCertificationSignatureBuilder.java | 119 --------------- .../ThirdPartyDirectKeySignatureBuilder.java | 53 ------- .../builder/UniversalSignatureBuilder.java | 55 ------- .../signature/builder/package-info.java | 8 - .../pgpainless/signature/package-info.java | 8 - .../org/pgpainless/algorithm/SignatureType.kt | 4 +- .../secretkeyring/SecretKeyRingEditor.kt | 11 +- .../builder/AbstractSignatureBuilder.kt | 135 +++++++++++++++++ .../builder/DirectKeySelfSignatureBuilder.kt | 50 +++++++ .../PrimaryKeyBindingSignatureBuilder.kt | 61 ++++++++ .../builder/RevocationSignatureBuilder.kt | 70 +++++++++ .../signature/builder/SelfSignatureBuilder.kt | 69 +++++++++ .../builder/SubkeyBindingSignatureBuilder.kt | 73 +++++++++ ...ThirdPartyCertificationSignatureBuilder.kt | 117 +++++++++++++++ .../ThirdPartyDirectKeySignatureBuilder.kt | 57 +++++++ .../builder/UniversalSignatureBuilder.kt | 52 +++++++ ...irdPartyDirectKeySignatureBuilderTest.java | 6 +- 23 files changed, 693 insertions(+), 728 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/AbstractSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/RevocationSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/SelfSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/UniversalSignatureBuilder.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/package-info.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/package-info.java create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/AbstractSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SelfSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.kt create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/UniversalSignatureBuilder.kt 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 deleted file mode 100644 index 1079f92f..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/AbstractSignatureBuilder.java +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -import java.util.Set; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPrivateKey; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPSignatureGenerator; -import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.HashAlgorithm; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator; -import org.pgpainless.implementation.ImplementationFactory; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.key.protection.UnlockSecretKey; -import org.pgpainless.key.util.OpenPgpKeyAttributeUtil; -import org.pgpainless.signature.subpackets.SignatureSubpackets; -import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper; - -public abstract class AbstractSignatureBuilder> { - protected final PGPPrivateKey privateSigningKey; - protected final PGPPublicKey publicSigningKey; - - protected HashAlgorithm hashAlgorithm; - protected SignatureType signatureType; - - protected SignatureSubpackets unhashedSubpackets; - protected SignatureSubpackets hashedSubpackets; - - protected AbstractSignatureBuilder(SignatureType signatureType, - PGPSecretKey signingKey, - SecretKeyRingProtector protector, - HashAlgorithm hashAlgorithm, - SignatureSubpackets hashedSubpackets, - SignatureSubpackets unhashedSubpackets) - throws PGPException { - if (!isValidSignatureType(signatureType)) { - throw new IllegalArgumentException("Invalid signature type."); - } - this.signatureType = signatureType; - this.privateSigningKey = UnlockSecretKey.unlockSecretKey(signingKey, protector); - this.publicSigningKey = signingKey.getPublicKey(); - this.hashAlgorithm = hashAlgorithm; - this.hashedSubpackets = hashedSubpackets; - this.unhashedSubpackets = unhashedSubpackets; - } - - public AbstractSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector) - throws PGPException { - this( - signatureType, - signingKey, - protector, - negotiateHashAlgorithm(signingKey.getPublicKey()), - SignatureSubpackets.createHashedSubpackets(signingKey.getPublicKey()), - SignatureSubpackets.createEmptySubpackets() - ); - } - - public AbstractSignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) - throws PGPException { - this( - SignatureType.valueOf(archetypeSignature.getSignatureType()), - certificationKey, - protector, - negotiateHashAlgorithm(certificationKey.getPublicKey()), - SignatureSubpackets.refreshHashedSubpackets(certificationKey.getPublicKey(), archetypeSignature), - SignatureSubpackets.refreshUnhashedSubpackets(archetypeSignature) - ); - } - - /** - * Negotiate a {@link HashAlgorithm} to be used when creating the signature. - * - * @param publicKey signing public key - * @return hash algorithm - */ - protected static HashAlgorithm negotiateHashAlgorithm(PGPPublicKey publicKey) { - Set hashAlgorithmPreferences = OpenPgpKeyAttributeUtil.getOrGuessPreferredHashAlgorithms(publicKey); - return HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) - .negotiateHashAlgorithm(hashAlgorithmPreferences); - } - - public B overrideHashAlgorithm(@Nonnull HashAlgorithm hashAlgorithm) { - this.hashAlgorithm = hashAlgorithm; - return (B) this; - } - - /** - * Set the builders {@link SignatureType}. - * Note that only those types who are valid for the concrete subclass of this {@link AbstractSignatureBuilder} - * are allowed. Invalid choices result in an {@link IllegalArgumentException} to be thrown. - * - * @param type signature type - * @return builder - */ - public B setSignatureType(SignatureType type) { - if (!isValidSignatureType(type)) { - throw new IllegalArgumentException("Invalid signature type: " + type); - } - this.signatureType = type; - return (B) this; - } - - /** - * Build an instance of {@link PGPSignatureGenerator} initialized with the signing key - * and with hashed and unhashed subpackets. - * - * @return pgp signature generator - * - * @throws PGPException if the signature generator cannot be initialized - */ - protected PGPSignatureGenerator buildAndInitSignatureGenerator() throws PGPException { - PGPSignatureGenerator generator = new PGPSignatureGenerator( - ImplementationFactory.getInstance().getPGPContentSignerBuilder( - publicSigningKey.getAlgorithm(), hashAlgorithm.getAlgorithmId() - ) - ); - generator.setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(unhashedSubpackets)); - generator.setHashedSubpackets(SignatureSubpacketsHelper.toVector(hashedSubpackets)); - generator.init(signatureType.getCode(), privateSigningKey); - return generator; - } - - /** - * Return true if the given {@link SignatureType} is a valid choice for the concrete implementation - * of {@link AbstractSignatureBuilder}. - * - * @param type type - * @return return true if valid, false otherwise - */ - protected abstract boolean isValidSignatureType(SignatureType type); -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.java deleted file mode 100644 index cf99f8d6..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.java +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -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.bouncycastle.openpgp.PGPSignatureGenerator; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; - -public class DirectKeySelfSignatureBuilder extends AbstractSignatureBuilder { - - public DirectKeySelfSignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) - throws PGPException { - super(certificationKey, protector, archetypeSignature); - } - - public DirectKeySelfSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { - super(SignatureType.DIRECT_KEY, signingKey, protector); - } - - public SelfSignatureSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public SelfSignatureSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable SelfSignatureSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - public PGPSignature build(PGPPublicKey key) throws PGPException { - PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator(); - if (key.getKeyID() != publicSigningKey.getKeyID()) { - return signatureGenerator.generateCertification(publicSigningKey, key); - } else { - return signatureGenerator.generateCertification(key); - } - } - - @Override - protected boolean isValidSignatureType(SignatureType type) { - return type == SignatureType.DIRECT_KEY; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java deleted file mode 100644 index 156e739f..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -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.HashAlgorithm; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; -import org.pgpainless.signature.subpackets.SignatureSubpackets; - -public class PrimaryKeyBindingSignatureBuilder extends AbstractSignatureBuilder { - - public PrimaryKeyBindingSignatureBuilder(PGPSecretKey subkey, SecretKeyRingProtector subkeyProtector) - throws PGPException { - super(SignatureType.PRIMARYKEY_BINDING, subkey, subkeyProtector); - } - - public PrimaryKeyBindingSignatureBuilder(PGPSecretKey secretSubKey, - SecretKeyRingProtector subkeyProtector, - HashAlgorithm hashAlgorithm) - throws PGPException { - super(SignatureType.PRIMARYKEY_BINDING, secretSubKey, subkeyProtector, hashAlgorithm, - SignatureSubpackets.createHashedSubpackets(secretSubKey.getPublicKey()), - SignatureSubpackets.createEmptySubpackets()); - } - - public SelfSignatureSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public SelfSignatureSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable SelfSignatureSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - @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/RevocationSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/RevocationSignatureBuilder.java deleted file mode 100644 index 3c2dcab9..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/RevocationSignatureBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -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.bouncycastle.openpgp.PGPSignatureGenerator; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; - -public class RevocationSignatureBuilder extends AbstractSignatureBuilder { - - public RevocationSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { - super(signatureType, signingKey, protector); - getHashedSubpackets().setRevocable(false); - } - - @Override - protected boolean isValidSignatureType(SignatureType type) { - switch (type) { - case KEY_REVOCATION: - case SUBKEY_REVOCATION: - case CERTIFICATION_REVOCATION: - return true; - default: - return false; - } - } - - public RevocationSignatureSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public RevocationSignatureSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable RevocationSignatureSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - public PGPSignature build(PGPPublicKey revokeeSubkey) throws PGPException { - PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator(); - if (signatureType == SignatureType.KEY_REVOCATION) { - if (!revokeeSubkey.isMasterKey()) { - throw new IllegalArgumentException("Signature type is KEY_REVOCATION, but provided revokeeSubkey does not appear to be a primary key."); - } - return signatureGenerator.generateCertification(publicSigningKey); - } else { - return signatureGenerator.generateCertification(publicSigningKey, revokeeSubkey); - } - } - - public PGPSignature build(String revokeeUserId) throws PGPException { - PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator(); - if (signatureType != SignatureType.CERTIFICATION_REVOCATION) { - throw new IllegalArgumentException("Signature type is != CERTIFICATION_REVOCATION."); - } - return signatureGenerator.generateCertification(revokeeUserId, publicSigningKey); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SelfSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SelfSignatureBuilder.java deleted file mode 100644 index e6bf94c3..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SelfSignatureBuilder.java +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -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.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; - -public class SelfSignatureBuilder extends AbstractSignatureBuilder { - - public SelfSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { - this(SignatureType.GENERIC_CERTIFICATION, signingKey, protector); - } - - public SelfSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector) - throws PGPException { - super(signatureType, signingKey, protector); - } - - public SelfSignatureBuilder( - PGPSecretKey primaryKey, - SecretKeyRingProtector primaryKeyProtector, - PGPSignature oldCertification) - throws PGPException { - super(primaryKey, primaryKeyProtector, oldCertification); - } - - public SelfSignatureSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public SelfSignatureSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable SelfSignatureSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - public PGPSignature build(PGPPublicKey certifiedKey, String userId) throws PGPException { - return buildAndInitSignatureGenerator().generateCertification(userId, certifiedKey); - } - - public PGPSignature build(PGPPublicKey certifiedKey, PGPUserAttributeSubpacketVector userAttribute) - throws PGPException { - return buildAndInitSignatureGenerator().generateCertification(userAttribute, certifiedKey); - } - - @Override - protected boolean isValidSignatureType(SignatureType type) { - switch (type) { - case GENERIC_CERTIFICATION: - case NO_CERTIFICATION: - case CASUAL_CERTIFICATION: - case POSITIVE_CERTIFICATION: - case DIRECT_KEY: - return true; - default: - return false; - } - } -} 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 deleted file mode 100644 index c15e219e..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -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.HashAlgorithm; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; -import org.pgpainless.signature.subpackets.SignatureSubpackets; - -public class SubkeyBindingSignatureBuilder extends AbstractSignatureBuilder { - - public SubkeyBindingSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) - throws PGPException { - super(SignatureType.SUBKEY_BINDING, signingKey, protector); - } - - public SubkeyBindingSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector, HashAlgorithm hashAlgorithm) - throws PGPException { - super(SignatureType.SUBKEY_BINDING, signingKey, protector, hashAlgorithm, - SignatureSubpackets.createHashedSubpackets(signingKey.getPublicKey()), - SignatureSubpackets.createEmptySubpackets()); - } - - public SubkeyBindingSignatureBuilder( - PGPSecretKey signingKey, - SecretKeyRingProtector protector, - PGPSignature oldSubkeyBinding) - throws PGPException { - super(signingKey, protector, requireValidSignatureType(oldSubkeyBinding)); - } - - private static PGPSignature requireValidSignatureType(PGPSignature signature) { - if (signature.getSignatureType() == SignatureType.SUBKEY_BINDING.getCode()) { - return signature; - } - throw new IllegalArgumentException("Invalid signature type."); - } - - @Override - protected boolean isValidSignatureType(SignatureType type) { - return type == SignatureType.SUBKEY_BINDING; - } - - public SelfSignatureSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public SelfSignatureSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable SelfSignatureSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - public PGPSignature build(PGPPublicKey subkey) throws PGPException { - return buildAndInitSignatureGenerator() - .generateCertification(publicSigningKey, subkey); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.java deleted file mode 100644 index d75e269d..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.java +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.exception.WrongPassphraseException; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.CertificationSubpackets; - -/** - * Certification signature builder used to certify other users keys. - */ -public class ThirdPartyCertificationSignatureBuilder extends AbstractSignatureBuilder { - - /** - * Create a new certification signature builder. - * This constructor uses {@link SignatureType#GENERIC_CERTIFICATION} as signature type. - * - * @param signingKey our own certification key - * @param protector protector to unlock the certification key - * @throws WrongPassphraseException in case of a wrong passphrase - */ - public ThirdPartyCertificationSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) - throws PGPException { - this(SignatureType.GENERIC_CERTIFICATION, signingKey, protector); - } - - /** - * Create a new certification signature builder. - * - * @param signatureType type of certification - * @param signingKey our own certification key - * @param protector protector to unlock the certification key - * @throws WrongPassphraseException in case of a wrong passphrase - */ - public ThirdPartyCertificationSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector) - throws PGPException { - super(signatureType, signingKey, protector); - } - - /** - * Create a new certification signature builder. - * - * @param signingKey our own certification key - * @param protector protector to unlock the certification key - * @param archetypeSignature signature to use as a template for the new signature - * @throws WrongPassphraseException in case of a wrong passphrase - */ - public ThirdPartyCertificationSignatureBuilder( - PGPSecretKey signingKey, - SecretKeyRingProtector protector, - PGPSignature archetypeSignature) - throws PGPException { - super(signingKey, protector, archetypeSignature); - } - - public CertificationSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public CertificationSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable CertificationSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - /** - * Create a certification signature for the given user-id and the primary key of the given key ring. - * @param certifiedKey key ring - * @param userId user-id to certify - * @return signature - * - * @throws PGPException if the signature generator cannot be initialized - */ - public PGPSignature build(PGPPublicKeyRing certifiedKey, String userId) throws PGPException { - return buildAndInitSignatureGenerator().generateCertification(userId, certifiedKey.getPublicKey()); - } - - /** - * Create a certification signature for the given user attribute and the primary key of the given key ring. - * @param certifiedKey key ring - * @param userAttribute user-attributes to certify - * @return signature - * - * @throws PGPException if the signature generator cannot be initialized - */ - public PGPSignature build(PGPPublicKeyRing certifiedKey, PGPUserAttributeSubpacketVector userAttribute) - throws PGPException { - return buildAndInitSignatureGenerator().generateCertification(userAttribute, certifiedKey.getPublicKey()); - } - - @Override - protected boolean isValidSignatureType(@Nonnull SignatureType type) { - switch (type) { - case GENERIC_CERTIFICATION: - case NO_CERTIFICATION: - case CASUAL_CERTIFICATION: - case POSITIVE_CERTIFICATION: - return true; - default: - return false; - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java deleted file mode 100644 index 51a14052..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -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.bouncycastle.openpgp.PGPSignatureGenerator; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.CertificationSubpackets; - -public class ThirdPartyDirectKeySignatureBuilder extends AbstractSignatureBuilder { - - public ThirdPartyDirectKeySignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) - throws PGPException { - super(certificationKey, protector, archetypeSignature); - } - - public ThirdPartyDirectKeySignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { - super(SignatureType.DIRECT_KEY, signingKey, protector); - } - - public CertificationSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public CertificationSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable CertificationSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - public PGPSignature build(PGPPublicKey key) throws PGPException { - PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator(); - return signatureGenerator.generateCertification(key); - } - - @Override - protected boolean isValidSignatureType(SignatureType type) { - return type == SignatureType.DIRECT_KEY; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/UniversalSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/UniversalSignatureBuilder.java deleted file mode 100644 index 7ca512bf..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/UniversalSignatureBuilder.java +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -import javax.annotation.Nullable; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPSignatureGenerator; -import org.pgpainless.algorithm.SignatureType; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.SignatureSubpackets; - -/** - * Signature builder without restrictions on subpacket contents. - */ -public class UniversalSignatureBuilder extends AbstractSignatureBuilder { - - public UniversalSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector) - throws PGPException { - super(signatureType, signingKey, protector); - } - - public UniversalSignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) - throws PGPException { - super(certificationKey, protector, archetypeSignature); - } - - @Override - protected boolean isValidSignatureType(SignatureType type) { - return true; - } - - public SignatureSubpackets getHashedSubpackets() { - return hashedSubpackets; - } - - public SignatureSubpackets getUnhashedSubpackets() { - return unhashedSubpackets; - } - - public void applyCallback(@Nullable SignatureSubpackets.Callback callback) { - if (callback != null) { - callback.modifyHashedSubpackets(getHashedSubpackets()); - callback.modifyUnhashedSubpackets(getUnhashedSubpackets()); - } - } - - public PGPSignatureGenerator getSignatureGenerator() throws PGPException { - return buildAndInitSignatureGenerator(); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/package-info.java deleted file mode 100644 index c82eb0ec..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Classes related to OpenPGP signatures. - */ -package org.pgpainless.signature.builder; diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/signature/package-info.java deleted file mode 100644 index 624a2e24..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Classes related to OpenPGP signatures. - */ -package org.pgpainless.signature; diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt index 865549b8..3cca8655 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt @@ -157,7 +157,9 @@ enum class SignatureType(val code: Int) { * @throws IllegalArgumentException in case of an unmatched signature type code */ @JvmStatic - @Deprecated("Deprecated in favor of requireFromCode", ReplaceWith("requireFromCode")) + @Deprecated( + "Deprecated in favor of requireFromCode", + ReplaceWith("SignatureType.requireFromCode(code)")) fun valueOf(code: Int): SignatureType { try { return requireFromCode(code) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index 891d64e7..7dff25f7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt @@ -84,10 +84,7 @@ class SecretKeyRingEditor( } builder.applyCallback(callback) secretKeyRing = - injectCertification( - secretKeyRing, - sanitizedUserId, - builder.build(primaryKey.publicKey, sanitizedUserId)) + injectCertification(secretKeyRing, sanitizedUserId, builder.build(sanitizedUserId)) return this } @@ -620,7 +617,7 @@ class SecretKeyRingEditor( hashedSubpackets.setPrimaryUserId(null) } }) - return builder.build(secretKeyRing.publicKey, userId) + return builder.build(userId) } @Throws(PGPException::class) @@ -648,7 +645,7 @@ class SecretKeyRingEditor( } }) } - .build(secretKeyRing.publicKey, primaryUserId) + .build(primaryUserId) } @Throws(PGPException::class) @@ -675,7 +672,7 @@ class SecretKeyRingEditor( } }) } - .build(secretKeyRing.publicKey) + .build() } private fun selectUserIds(predicate: Predicate): List = diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/AbstractSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/AbstractSignatureBuilder.kt new file mode 100644 index 00000000..eaf05df1 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/AbstractSignatureBuilder.kt @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPPrivateKey +import org.bouncycastle.openpgp.PGPPublicKey +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.bouncycastle.openpgp.PGPSignatureGenerator +import org.pgpainless.PGPainless +import org.pgpainless.algorithm.HashAlgorithm +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator +import org.pgpainless.implementation.ImplementationFactory +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.key.protection.UnlockSecretKey +import org.pgpainless.key.util.OpenPgpKeyAttributeUtil +import org.pgpainless.signature.subpackets.SignatureSubpackets +import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper + +abstract class AbstractSignatureBuilder>( + protected val privateSigningKey: PGPPrivateKey, + protected val publicSigningKey: PGPPublicKey, + protected var _hashAlgorithm: HashAlgorithm, + protected var _signatureType: SignatureType, + protected val _hashedSubpackets: SignatureSubpackets, + protected val _unhashedSubpackets: SignatureSubpackets +) { + + protected abstract val signatureTypePredicate: Predicate + + init { + require(signatureTypePredicate.test(_signatureType)) { "Invalid signature type." } + } + + @Throws(PGPException::class) + protected constructor( + signatureType: SignatureType, + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + hashAlgorithm: HashAlgorithm, + hashedSubpackets: SignatureSubpackets, + unhashedSubpackets: SignatureSubpackets + ) : this( + UnlockSecretKey.unlockSecretKey(signingKey, protector), + signingKey.publicKey, + hashAlgorithm, + signatureType, + hashedSubpackets, + unhashedSubpackets) + + @Throws(PGPException::class) + constructor( + signatureType: SignatureType, + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : this( + signatureType, + signingKey, + protector, + negotiateHashAlgorithm(signingKey.publicKey), + SignatureSubpackets.createHashedSubpackets(signingKey.publicKey), + SignatureSubpackets.createEmptySubpackets()) + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + archetypeSignature: PGPSignature + ) : this( + SignatureType.requireFromCode(archetypeSignature.signatureType), + signingKey, + protector, + negotiateHashAlgorithm(signingKey.publicKey), + SignatureSubpackets.refreshHashedSubpackets(signingKey.publicKey, archetypeSignature), + SignatureSubpackets.refreshUnhashedSubpackets(archetypeSignature)) + + val hashAlgorithm = _hashAlgorithm + + fun overrideHashAlgorithm(hashAlgorithm: HashAlgorithm) = + apply { _hashAlgorithm = hashAlgorithm } as B + + /** + * Set the builders [SignatureType]. Note that only those types who are valid for the concrete + * subclass of this [AbstractSignatureBuilder] are allowed. Invalid choices result in an + * [IllegalArgumentException] to be thrown. + * + * @param type signature type + * @return builder + */ + fun setSignatureType(type: SignatureType) = + apply { + require(signatureTypePredicate.test(type)) { "Invalid signature type: $type" } + _signatureType = type + } + as B + + /** + * Build an instance of [PGPSignatureGenerator] initialized with the signing key and with hashed + * and unhashed subpackets. + * + * @return pgp signature generator + * @throws PGPException if the signature generator cannot be initialized + */ + @Throws(PGPException::class) + protected fun buildAndInitSignatureGenerator(): PGPSignatureGenerator = + PGPSignatureGenerator( + ImplementationFactory.getInstance() + .getPGPContentSignerBuilder( + publicSigningKey.algorithm, hashAlgorithm.algorithmId)) + .apply { + setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(_unhashedSubpackets)) + setHashedSubpackets(SignatureSubpacketsHelper.toVector(_hashedSubpackets)) + init(_signatureType.code, privateSigningKey) + } + + companion object { + + /** + * Negotiate a [HashAlgorithm] to be used when creating the signature. + * + * @param publicKey signing public key + * @return hash algorithm + */ + @JvmStatic + fun negotiateHashAlgorithm(publicKey: PGPPublicKey): HashAlgorithm = + HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) + .negotiateHashAlgorithm( + OpenPgpKeyAttributeUtil.getOrGuessPreferredHashAlgorithms(publicKey)) + } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.kt new file mode 100644 index 00000000..c4d11ea9 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.kt @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets + +/** + * [AbstractSignatureBuilder] devoted to direct-key self-signatures. Direct-key self-signatures are + * calculated by a primary-key over itself. + */ +class DirectKeySelfSignatureBuilder : AbstractSignatureBuilder { + + override val signatureTypePredicate: Predicate + get() = Predicate { it == SignatureType.DIRECT_KEY } + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + archetypeSignature: PGPSignature + ) : super(signingKey, protector, archetypeSignature) + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(SignatureType.DIRECT_KEY, signingKey, protector) + + val hashedSubpackets: SelfSignatureSubpackets = _hashedSubpackets + val unhashedSubpackets: SelfSignatureSubpackets = _unhashedSubpackets + + fun applyCallback(callback: SelfSignatureSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + @Throws(PGPException::class) + fun build(): PGPSignature = + buildAndInitSignatureGenerator().let { it.generateCertification(publicSigningKey) } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.kt new file mode 100644 index 00000000..f34d37a7 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.kt @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPPublicKey +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.pgpainless.algorithm.HashAlgorithm +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets +import org.pgpainless.signature.subpackets.SignatureSubpackets + +/** + * [AbstractSignatureBuilder] subclass devoted to build primary-key binding-signatures. Those + * signatures (also called "back-signatures") are binding signatures issued by signing-capable + * subkeys. + */ +class PrimaryKeyBindingSignatureBuilder : + AbstractSignatureBuilder { + + override val signatureTypePredicate: Predicate + get() = Predicate { it == SignatureType.PRIMARYKEY_BINDING } + + @Throws(PGPException::class) + constructor( + signingSubkey: PGPSecretKey, + subkeyProtector: SecretKeyRingProtector + ) : super(SignatureType.PRIMARYKEY_BINDING, signingSubkey, subkeyProtector) + + @Throws(PGPException::class) + constructor( + signingSubkey: PGPSecretKey, + subkeyProtector: SecretKeyRingProtector, + hashAlgorithm: HashAlgorithm + ) : super( + SignatureType.PRIMARYKEY_BINDING, + signingSubkey, + subkeyProtector, + hashAlgorithm, + SignatureSubpackets.createHashedSubpackets(signingSubkey.publicKey), + SignatureSubpackets.createEmptySubpackets()) + + val hashedSubpackets: SelfSignatureSubpackets = _hashedSubpackets + val unhashedSubpackets: SelfSignatureSubpackets = _unhashedSubpackets + + fun applyCallback(callback: SelfSignatureSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + @Throws(PGPException::class) + fun build(primaryKey: PGPPublicKey): PGPSignature = + buildAndInitSignatureGenerator().generateCertification(primaryKey, publicSigningKey) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt new file mode 100644 index 00000000..d8c594fe --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +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.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets + +/** [AbstractSignatureBuilder] subclass devoted to revocation signatures. */ +class RevocationSignatureBuilder : AbstractSignatureBuilder { + + override val signatureTypePredicate: Predicate + get() = + Predicate { + it in + listOf( + SignatureType.KEY_REVOCATION, + SignatureType.SUBKEY_REVOCATION, + SignatureType.CERTIFICATION_REVOCATION) + } + + @Throws(PGPException::class) + constructor( + signatureType: SignatureType, + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(signatureType, signingKey, protector) { + hashedSubpackets.setRevocable(false) + } + + val hashedSubpackets: RevocationSignatureSubpackets = _hashedSubpackets + val unhashedSubpackets: RevocationSignatureSubpackets = _unhashedSubpackets + + fun applyCallback(callback: RevocationSignatureSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + @Throws(PGPException::class) + fun build(revokeeKey: PGPPublicKey): PGPSignature = + buildAndInitSignatureGenerator().let { + if (_signatureType == SignatureType.KEY_REVOCATION) { + require(revokeeKey.isMasterKey) { + "Signature type is KEY_REVOCATION, but provided revokee does not appear to be a primary key." + } + it.generateCertification(publicSigningKey) + } else { + it.generateCertification(publicSigningKey, revokeeKey) + } + } + + @Throws(PGPException::class) + fun build(revokeeUserId: CharSequence): PGPSignature = + buildAndInitSignatureGenerator() + .also { + require(_signatureType == SignatureType.CERTIFICATION_REVOCATION) { + "Signature type is != CERTIFICATION_REVOCATION." + } + } + .generateCertification(revokeeUserId.toString(), publicSigningKey) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SelfSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SelfSignatureBuilder.kt new file mode 100644 index 00000000..370479c8 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SelfSignatureBuilder.kt @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets + +/** + * [AbstractSignatureBuilder] devoted to all types of self-certifications. Self-certifications are + * certifications calculated by a primary key over its own user-ids. + */ +class SelfSignatureBuilder : AbstractSignatureBuilder { + override val signatureTypePredicate: Predicate + get() = + Predicate { + it in + listOf( + SignatureType.GENERIC_CERTIFICATION, + SignatureType.NO_CERTIFICATION, + SignatureType.CASUAL_CERTIFICATION, + SignatureType.POSITIVE_CERTIFICATION) + } + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(SignatureType.GENERIC_CERTIFICATION, signingKey, protector) + + @Throws(PGPException::class) + constructor( + signatureType: SignatureType, + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(signatureType, signingKey, protector) + + @Throws(PGPException::class) + constructor( + primaryKey: PGPSecretKey, + primaryKeyProtector: SecretKeyRingProtector, + oldCertification: PGPSignature + ) : super(primaryKey, primaryKeyProtector, oldCertification) + + val hashedSubpackets: SelfSignatureSubpackets = _hashedSubpackets + val unhashedSubpackets: SelfSignatureSubpackets = _unhashedSubpackets + + fun applyCallback(callback: SelfSignatureSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + @Throws(PGPException::class) + fun build(userId: CharSequence): PGPSignature = + buildAndInitSignatureGenerator().generateCertification(userId.toString(), publicSigningKey) + + @Throws(PGPException::class) + fun build(userAttributes: PGPUserAttributeSubpacketVector): PGPSignature = + buildAndInitSignatureGenerator().generateCertification(userAttributes, publicSigningKey) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.kt new file mode 100644 index 00000000..6e2694e3 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.kt @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPPublicKey +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.pgpainless.algorithm.HashAlgorithm +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.SelfSignatureSubpackets +import org.pgpainless.signature.subpackets.SignatureSubpackets + +/** + * [AbstractSignatureBuilder] devoted to generating subkey binding signatures. A subkey binding + * signature is calculated by a primary key over a subkey. + */ +class SubkeyBindingSignatureBuilder : AbstractSignatureBuilder { + + override val signatureTypePredicate: Predicate + get() = Predicate { it == SignatureType.SUBKEY_BINDING } + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(SignatureType.SUBKEY_BINDING, signingKey, protector) + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + hashAlgorithm: HashAlgorithm + ) : super( + SignatureType.SUBKEY_BINDING, + signingKey, + protector, + hashAlgorithm, + SignatureSubpackets.createHashedSubpackets(signingKey.publicKey), + SignatureSubpackets.createEmptySubpackets()) + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + oldSubkeyBinding: PGPSignature + ) : super( + signingKey, + protector, + oldSubkeyBinding.also { + require(it.signatureType == SignatureType.SUBKEY_BINDING.code) { + "Invalid signature type." + } + }) + + val hashedSubpackets: SelfSignatureSubpackets = _hashedSubpackets + val unhashedSubpackets: SelfSignatureSubpackets = _unhashedSubpackets + + fun applyCallback(callback: SelfSignatureSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + @Throws(PGPException::class) + fun build(subkey: PGPPublicKey): PGPSignature = + buildAndInitSignatureGenerator().generateCertification(publicSigningKey, subkey) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.kt new file mode 100644 index 00000000..be45dc8a --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilder.kt @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPPublicKeyRing +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.CertificationSubpackets + +/** + * Certification signature builder used to certify other users keys. A third-party certification is + * calculated by the primary key of the issuer certificate, over a user-id on a third-party + * certificate. + */ +class ThirdPartyCertificationSignatureBuilder : + AbstractSignatureBuilder { + override val signatureTypePredicate: Predicate + get() = + Predicate { + it in + listOf( + SignatureType.GENERIC_CERTIFICATION, + SignatureType.NO_CERTIFICATION, + SignatureType.CASUAL_CERTIFICATION, + SignatureType.POSITIVE_CERTIFICATION) + } + + /** + * Create a new certification signature builder. This constructor uses + * [SignatureType.GENERIC_CERTIFICATION] as signature type. + * + * @param signingKey our own certification key + * @param protector protector to unlock the certification key + * @throws WrongPassphraseException in case of a wrong passphrase + */ + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(SignatureType.GENERIC_CERTIFICATION, signingKey, protector) + + /** + * Create a new certification signature builder. + * + * @param signatureType type of certification + * @param signingKey our own certification key + * @param protector protector to unlock the certification key + * @throws WrongPassphraseException in case of a wrong passphrase + */ + @Throws(PGPException::class) + constructor( + signatureType: SignatureType, + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(signatureType, signingKey, protector) + + /** + * Create a new certification signature builder. + * + * @param signingKey our own certification key + * @param protector protector to unlock the certification key + * @param archetypeSignature signature to use as a template for the new signature + * @throws WrongPassphraseException in case of a wrong passphrase + */ + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + archetypeSignature: PGPSignature + ) : super(signingKey, protector, archetypeSignature) + + val hashedSubpackets: CertificationSubpackets = _hashedSubpackets + val unhashedSubpackets: CertificationSubpackets = _unhashedSubpackets + + fun applyCallback(callback: CertificationSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + /** + * Create a certification signature for the given user-id and the given third-party certificate. + * + * @param certificate third-party certificate + * @param userId user-id to certify + * @return signature + * @throws PGPException if the signature generator cannot be initialized + */ + @Throws(PGPException::class) + fun build(certificate: PGPPublicKeyRing, userId: CharSequence): PGPSignature = + buildAndInitSignatureGenerator() + .generateCertification(userId.toString(), certificate.publicKey) + + /** + * Create a certification signature for the given user attribute and the given third-party + * certificate. + * + * @param certificate third-party certificate + * @param userAttribute user-attributes to certify + * @return signature + * @throws PGPException if the signature generator cannot be initialized + */ + @Throws(PGPException::class) + fun build( + certificate: PGPPublicKeyRing, + userAttribute: PGPUserAttributeSubpacketVector + ): PGPSignature = + buildAndInitSignatureGenerator().generateCertification(userAttribute, certificate.publicKey) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.kt new file mode 100644 index 00000000..32d677d5 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.kt @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPPublicKey +import org.bouncycastle.openpgp.PGPPublicKeyRing +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.CertificationSubpackets + +/** + * [AbstractSignatureBuilder] subclass devoted to generating direct-key signatures over primary keys + * of third-party certificates. Such signatures are also sometimes referred to as "delegations", + * i.e. in the context of the Web-of-Trust. + */ +class ThirdPartyDirectKeySignatureBuilder : + AbstractSignatureBuilder { + + override val signatureTypePredicate: Predicate + get() = Predicate { it == SignatureType.DIRECT_KEY } + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(SignatureType.DIRECT_KEY, signingKey, protector) + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + archetypeSignature: PGPSignature + ) : super(signingKey, protector, archetypeSignature) + + val hashedSubpackets: CertificationSubpackets = _hashedSubpackets + val unhashedSubpackets: CertificationSubpackets = _unhashedSubpackets + + fun applyCallback(callback: CertificationSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + @Throws(PGPException::class) + fun build(certificate: PGPPublicKeyRing): PGPSignature = build(certificate.publicKey) + + @Throws(PGPException::class) + fun build(certifiedKey: PGPPublicKey): PGPSignature = + buildAndInitSignatureGenerator().generateCertification(certifiedKey) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/UniversalSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/UniversalSignatureBuilder.kt new file mode 100644 index 00000000..a1aa57b2 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/UniversalSignatureBuilder.kt @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.signature.builder + +import java.util.function.Predicate +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.PGPSignature +import org.bouncycastle.openpgp.PGPSignatureGenerator +import org.pgpainless.algorithm.SignatureType +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.signature.subpackets.SignatureSubpackets + +/** + * Signature builder without restrictions on subpacket contents. Instead of providing a "build" + * method, this builder offers the user to decide on their own, how to generate the signature by + * exposing the [PGPSignatureGenerator] via [signatureGenerator]. + */ +class UniversalSignatureBuilder : AbstractSignatureBuilder { + + override val signatureTypePredicate: Predicate + get() = Predicate { true } + + @Throws(PGPException::class) + constructor( + signatureType: SignatureType, + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector + ) : super(signatureType, signingKey, protector) + + @Throws(PGPException::class) + constructor( + signingKey: PGPSecretKey, + protector: SecretKeyRingProtector, + archetypeSignature: PGPSignature + ) : super(signingKey, protector, archetypeSignature) + + val hashedSubpackets: SignatureSubpackets = _hashedSubpackets + val unhashedSubpackets: SignatureSubpackets = _unhashedSubpackets + + fun applyCallback(callback: SignatureSubpackets.Callback?) = apply { + callback?.let { + it.modifyHashedSubpackets(hashedSubpackets) + it.modifyUnhashedSubpackets(unhashedSubpackets) + } + } + + val signatureGenerator: PGPSignatureGenerator + @Throws(PGPException::class) get() = buildAndInitSignatureGenerator() +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java index 72acc125..56605f83 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java @@ -8,8 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.Date; @@ -33,7 +31,7 @@ import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; public class ThirdPartyDirectKeySignatureBuilderTest { @Test - public void testDirectKeySignatureBuilding() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + public void testDirectKeySignatureBuilding() throws PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .modernKeyRing("Alice"); @@ -55,7 +53,7 @@ public class ThirdPartyDirectKeySignatureBuilderTest { } }); - PGPSignature directKeySig = dsb.build(secretKeys.getPublicKey()); + PGPSignature directKeySig = dsb.build(); assertNotNull(directKeySig); secretKeys = KeyRingUtils.injectCertification(secretKeys, secretKeys.getPublicKey(), directKeySig);