From c1170773bc46b98d7dd9bb6c4dfad60b19fc8f85 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 7 May 2022 14:17:44 +0200 Subject: [PATCH 1/9] Implement certification of third party keys --- .../main/java/org/pgpainless/PGPainless.java | 5 + .../algorithm/CertificationType.java | 46 +++++++ .../key/certification/CertifyCertificate.java | 123 ++++++++++++++++++ .../key/certification/package-info.java | 8 ++ .../certification/CertifyCertificateTest.java | 67 ++++++++++ 5 files changed, 249 insertions(+) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/algorithm/CertificationType.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/certification/package-info.java create mode 100644 pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 827085ce..0f47e26e 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -16,6 +16,7 @@ import org.pgpainless.decryption_verification.DecryptionBuilder; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.encryption_signing.EncryptionBuilder; import org.pgpainless.encryption_signing.EncryptionStream; +import org.pgpainless.key.certification.CertifyCertificate; import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.generation.KeyRingTemplates; import org.pgpainless.key.info.KeyRingInfo; @@ -163,4 +164,8 @@ public final class PGPainless { public static Policy getPolicy() { return Policy.getInstance(); } + + public static CertifyCertificate certifyCertificate() { + return new CertifyCertificate(); + } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/CertificationType.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/CertificationType.java new file mode 100644 index 00000000..f5c8ec7e --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/CertificationType.java @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.algorithm; + +import javax.annotation.Nonnull; + +/** + * Subset of {@link SignatureType}, reduced to certification types. + */ +public enum CertificationType { + + /** + * The issuer of this certification does not make any particular assertion as to how well the certifier has + * checked that the owner of the key is in fact the person described by the User ID. + */ + GENERIC(SignatureType.GENERIC_CERTIFICATION), + + /** + * The issuer of this certification has not done any verification of the claim that the owner of this key is + * the User ID specified. + */ + NONE(SignatureType.NO_CERTIFICATION), + + /** + * The issuer of this certification has done some casual verification of the claim of identity. + */ + CASUAL(SignatureType.CASUAL_CERTIFICATION), + + /** + * The issuer of this certification has done some casual verification of the claim of identity. + */ + POSITIVE(SignatureType.POSITIVE_CERTIFICATION), + ; + + private final SignatureType signatureType; + + CertificationType(@Nonnull SignatureType signatureType) { + this.signatureType = signatureType; + } + + public @Nonnull SignatureType asSignatureType() { + return signatureType; + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java new file mode 100644 index 00000000..69029a84 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.key.certification; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.CertificationType; +import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.exception.KeyException; +import org.pgpainless.key.OpenPgpFingerprint; +import org.pgpainless.key.info.KeyRingInfo; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.key.util.KeyRingUtils; +import org.pgpainless.signature.builder.ThirdPartyCertificationSignatureBuilder; +import org.pgpainless.signature.subpackets.CertificationSubpackets; +import org.pgpainless.util.DateUtil; + +import java.util.Date; + +public class CertifyCertificate { + + CertifyUserId certifyUserId(PGPPublicKeyRing certificate, String userId) { + return new CertifyUserId(certificate, userId); + } + + public static class CertifyUserId { + + private final PGPPublicKeyRing certificate; + private final String userId; + private final CertificationType certificationType; + + CertifyUserId(PGPPublicKeyRing certificate, String userId) { + this(certificate, userId, CertificationType.GENERIC); + } + + CertifyUserId(PGPPublicKeyRing certificate, String userId, CertificationType certificationType) { + this.certificate = certificate; + this.userId = userId; + this.certificationType = certificationType; + } + + CertifyUserIdWithSubpackets withKey(PGPSecretKeyRing certificationKey, SecretKeyRingProtector protector) throws PGPException { + Date now = DateUtil.now(); + KeyRingInfo info = PGPainless.inspectKeyRing(certificationKey, now); + + // We only support certification-capable primary keys + OpenPgpFingerprint fingerprint = info.getFingerprint(); + PGPPublicKey certificationPubKey = info.getPublicKey(fingerprint); + if (!info.isKeyValidlyBound(certificationPubKey.getKeyID())) { + throw new KeyException.RevokedKeyException(fingerprint); + } + + Date expirationDate = info.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER); + if (expirationDate != null && expirationDate.before(now)) { + throw new KeyException.ExpiredKeyException(fingerprint, expirationDate); + } + + PGPSecretKey secretKey = certificationKey.getSecretKey(certificationPubKey.getKeyID()); + if (secretKey == null) { + throw new KeyException.MissingSecretKeyException(fingerprint, certificationPubKey.getKeyID()); + } + + ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder( + certificationType.asSignatureType(), secretKey, protector); + + return new CertifyUserIdWithSubpackets(certificate, userId, sigBuilder); + } + } + + public static class CertifyUserIdWithSubpackets { + + private final PGPPublicKeyRing certificate; + private final String userId; + private final ThirdPartyCertificationSignatureBuilder sigBuilder; + + CertifyUserIdWithSubpackets(PGPPublicKeyRing certificate, String userId, ThirdPartyCertificationSignatureBuilder sigBuilder) { + this.certificate = certificate; + this.userId = userId; + this.sigBuilder = sigBuilder; + } + + public CertifyUserIdResult withSubpackets(CertificationSubpackets.Callback subpacketCallback) throws PGPException { + sigBuilder.applyCallback(subpacketCallback); + return build(); + } + + public CertifyUserIdResult build() throws PGPException { + PGPSignature signature = sigBuilder.build(certificate, userId); + + return new CertifyUserIdResult(certificate, userId, signature); + } + } + + public static class CertifyUserIdResult { + + private final PGPPublicKeyRing certificate; + private final String userId; + private final PGPSignature certification; + + CertifyUserIdResult(PGPPublicKeyRing certificate, String userId, PGPSignature certification) { + this.certificate = certificate; + this.userId = userId; + this.certification = certification; + } + + public PGPSignature getCertification() { + return certification; + } + + public PGPPublicKeyRing getCertifiedCertificate() { + // inject the signature + PGPPublicKeyRing certified = KeyRingUtils.injectCertification(certificate, userId, certification); + return certified; + } + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/package-info.java new file mode 100644 index 00000000..db2f4857 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/package-info.java @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * API for key certifications. + */ +package org.pgpainless.key.certification; diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java new file mode 100644 index 00000000..7093865d --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.key.certification; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.Arrays; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.SignatureType; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.signature.consumer.SignatureVerifier; +import org.pgpainless.util.CollectionUtils; +import org.pgpainless.util.DateUtil; + +public class CertifyCertificateTest { + + @Test + public void testSuccessfulCertificationOfUserId() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { + SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); + PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice ", null); + String bobUserId = "Bob "; + PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing(bobUserId, null); + + PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob); + + CertifyCertificate.CertifyUserIdResult result = PGPainless.certifyCertificate() + .certifyUserId(bobCertificate, bobUserId) + .withKey(alice, protector) + .build(); + + assertNotNull(result); + PGPSignature signature = result.getCertification(); + assertNotNull(signature); + assertEquals(SignatureType.GENERIC_CERTIFICATION, SignatureType.valueOf(signature.getSignatureType())); + assertEquals(alice.getPublicKey().getKeyID(), signature.getKeyID()); + + assertTrue(SignatureVerifier.verifyUserIdCertification( + bobUserId, signature, alice.getPublicKey(), bob.getPublicKey(), PGPainless.getPolicy(), DateUtil.now())); + + PGPPublicKeyRing bobCertified = result.getCertifiedCertificate(); + PGPPublicKey bobCertifiedKey = bobCertified.getPublicKey(); + // There are 2 sigs now, bobs own and alice' + assertEquals(2, CollectionUtils.iteratorToList(bobCertifiedKey.getSignaturesForID(bobUserId)).size()); + List sigsByAlice = CollectionUtils.iteratorToList( + bobCertifiedKey.getSignaturesForKeyID(alice.getPublicKey().getKeyID())); + assertEquals(1, sigsByAlice.size()); + assertEquals(signature, sigsByAlice.get(0)); + + assertFalse(Arrays.areEqual(bobCertificate.getEncoded(), bobCertified.getEncoded())); + } +} From fa5ddfd11242c9b47bfb14d4f0e8d18f5a4901ef Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 7 May 2022 21:48:36 +0200 Subject: [PATCH 2/9] WIP: Implement delegations THERE ARE THINGS BROKEN NOW. DO NOT MERGE! --- .../main/java/org/pgpainless/PGPainless.java | 2 +- .../pgpainless/algorithm/Trustworthiness.java | 111 +++++++++++++ .../key/certification/CertifyCertificate.java | 146 ++++++++++++------ .../secretkeyring/SecretKeyRingEditor.java | 8 +- .../builder/DirectKeySignatureBuilder.java | 17 +- .../certification/CertifyCertificateTest.java | 40 ++++- .../DirectKeySignatureBuilderTest.java | 3 + 7 files changed, 272 insertions(+), 55 deletions(-) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 0f47e26e..a2f16ab9 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -165,7 +165,7 @@ public final class PGPainless { return Policy.getInstance(); } - public static CertifyCertificate certifyCertificate() { + public static CertifyCertificate certify() { return new CertifyCertificate(); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java new file mode 100644 index 00000000..573bbf9d --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.algorithm; + +public class Trustworthiness { + + private final int amount; + private final int depth; + + public static final int THRESHOLD_FULLY_CONVINCED = 120; + public static final int THRESHOLD_MARGINALLY_CONVINCED = 60; + public static final int THRESHOLD_NOT_TRUSTED = 0; + + public Trustworthiness(int amount, int depth) { + this.amount = capAmount(amount); + this.depth = capDepth(depth); + } + + public int getAmount() { + return amount; + } + + public int getDepth() { + return depth; + } + + /** + * This means that we are fully convinced of the trustworthiness of the key. + * + * @return builder + */ + public static Builder fullyTrusted() { + return new Builder(THRESHOLD_FULLY_CONVINCED); + } + + /** + * This means that we are marginally (partially) convinced of the trustworthiness of the key. + * + * @return builder + */ + public static Builder marginallyTrusted() { + return new Builder(THRESHOLD_MARGINALLY_CONVINCED); + } + + /** + * This means that we do not trust the key. + * Can be used to overwrite previous trust. + * + * @return builder + */ + public static Builder untrusted() { + return new Builder(THRESHOLD_NOT_TRUSTED); + } + + public static final class Builder { + + private final int amount; + + private Builder(int amount) { + this.amount = amount; + } + + /** + * The key is a trusted introducer (depth 1). + * Certifications made by this key are considered trustworthy. + * + * @return trust + */ + public Trustworthiness introducer() { + return new Trustworthiness(amount, 1); + } + + /** + * The key is a meta introducer (depth 2). + * This key can introduce trusted introducers of depth 1. + * + * @return trust + */ + public Trustworthiness metaIntroducer() { + return new Trustworthiness(amount, 2); + } + + /** + * The key is a level
n
meta introducer. + * This key can introduce meta introducers of depth
n - 1
. + * + * @param n depth + * @return trust + */ + public Trustworthiness levelNIntroducer(int n) { + return new Trustworthiness(amount, n); + } + } + + private static int capAmount(int amount) { + if (amount < 0 || amount > 255) { + throw new IllegalArgumentException("Trust amount MUST be a value between 0 and 255"); + } + return amount; + } + + private static int capDepth(int depth) { + if (depth < 0 || depth > 255) { + throw new IllegalArgumentException("Trust depth MUST be a value between 0 and 255"); + } + return depth; + } + +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java index 69029a84..77dab4e8 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java @@ -13,111 +13,171 @@ import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.CertificationType; import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.algorithm.Trustworthiness; import org.pgpainless.exception.KeyException; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; +import org.pgpainless.signature.builder.DirectKeySignatureBuilder; import org.pgpainless.signature.builder.ThirdPartyCertificationSignatureBuilder; import org.pgpainless.signature.subpackets.CertificationSubpackets; import org.pgpainless.util.DateUtil; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Date; public class CertifyCertificate { - CertifyUserId certifyUserId(PGPPublicKeyRing certificate, String userId) { - return new CertifyUserId(certificate, userId); + CertificationOnUserId userIdOnCertificate(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate) { + return new CertificationOnUserId(userId, certificate, CertificationType.GENERIC); } - public static class CertifyUserId { + CertificationOnUserId userIdOnCertificate(@Nonnull String userid, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { + return new CertificationOnUserId(userid, certificate, certificationType); + } + + DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate) { + return certificate(certificate, null); + } + + DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { + return new DelegationOnCertificate(certificate, trustworthiness); + } + + public static class CertificationOnUserId { private final PGPPublicKeyRing certificate; private final String userId; private final CertificationType certificationType; - CertifyUserId(PGPPublicKeyRing certificate, String userId) { - this(certificate, userId, CertificationType.GENERIC); - } - - CertifyUserId(PGPPublicKeyRing certificate, String userId, CertificationType certificationType) { - this.certificate = certificate; + CertificationOnUserId(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { this.userId = userId; + this.certificate = certificate; this.certificationType = certificationType; } - CertifyUserIdWithSubpackets withKey(PGPSecretKeyRing certificationKey, SecretKeyRingProtector protector) throws PGPException { - Date now = DateUtil.now(); - KeyRingInfo info = PGPainless.inspectKeyRing(certificationKey, now); - - // We only support certification-capable primary keys - OpenPgpFingerprint fingerprint = info.getFingerprint(); - PGPPublicKey certificationPubKey = info.getPublicKey(fingerprint); - if (!info.isKeyValidlyBound(certificationPubKey.getKeyID())) { - throw new KeyException.RevokedKeyException(fingerprint); - } - - Date expirationDate = info.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER); - if (expirationDate != null && expirationDate.before(now)) { - throw new KeyException.ExpiredKeyException(fingerprint, expirationDate); - } - - PGPSecretKey secretKey = certificationKey.getSecretKey(certificationPubKey.getKeyID()); - if (secretKey == null) { - throw new KeyException.MissingSecretKeyException(fingerprint, certificationPubKey.getKeyID()); - } + CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { + PGPSecretKey secretKey = getCertificationSecretKey(certificationKey); ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder( certificationType.asSignatureType(), secretKey, protector); - return new CertifyUserIdWithSubpackets(certificate, userId, sigBuilder); + return new CertificationOnUserIdWithSubpackets(certificate, userId, sigBuilder); } } - public static class CertifyUserIdWithSubpackets { + public static class CertificationOnUserIdWithSubpackets { private final PGPPublicKeyRing certificate; private final String userId; private final ThirdPartyCertificationSignatureBuilder sigBuilder; - CertifyUserIdWithSubpackets(PGPPublicKeyRing certificate, String userId, ThirdPartyCertificationSignatureBuilder sigBuilder) { + CertificationOnUserIdWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull String userId, @Nonnull ThirdPartyCertificationSignatureBuilder sigBuilder) { this.certificate = certificate; this.userId = userId; this.sigBuilder = sigBuilder; } - public CertifyUserIdResult withSubpackets(CertificationSubpackets.Callback subpacketCallback) throws PGPException { + public CertificationResult withSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback) throws PGPException { sigBuilder.applyCallback(subpacketCallback); return build(); } - public CertifyUserIdResult build() throws PGPException { + public CertificationResult build() throws PGPException { PGPSignature signature = sigBuilder.build(certificate, userId); - - return new CertifyUserIdResult(certificate, userId, signature); + PGPPublicKeyRing certifiedCertificate = KeyRingUtils.injectCertification(certificate, userId, signature); + return new CertificationResult(certifiedCertificate, signature); } } - public static class CertifyUserIdResult { + public static class DelegationOnCertificate { + + private final PGPPublicKeyRing certificate; + private final Trustworthiness trustworthiness; + + DelegationOnCertificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { + this.certificate = certificate; + this.trustworthiness = trustworthiness; + } + + public DelegationOnCertificateWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { + PGPSecretKey secretKey = getCertificationSecretKey(certificationKey); + + DirectKeySignatureBuilder sigBuilder = new DirectKeySignatureBuilder(secretKey, protector); + if (trustworthiness != null) { + sigBuilder.getHashedSubpackets().setTrust(true, trustworthiness.getDepth(), trustworthiness.getAmount()); + } + return new DelegationOnCertificateWithSubpackets(certificate, sigBuilder); + } + } + + public static class DelegationOnCertificateWithSubpackets { + + private final PGPPublicKeyRing certificate; + private final DirectKeySignatureBuilder sigBuilder; + + public DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull DirectKeySignatureBuilder sigBuilder) { + this.certificate = certificate; + this.sigBuilder = sigBuilder; + } + + public CertificationResult withSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback) throws PGPException { + sigBuilder.applyCallback(subpacketsCallback); + return build(); + } + + public CertificationResult build() throws PGPException { + PGPPublicKey delegatedKey = certificate.getPublicKey(); + PGPSignature delegation = sigBuilder.build(delegatedKey); + PGPPublicKeyRing delegatedCertificate = KeyRingUtils.injectCertification(certificate, delegatedKey, delegation); + return new CertificationResult(delegatedCertificate, delegation); + } + } + + public static class CertificationResult { private final PGPPublicKeyRing certificate; - private final String userId; private final PGPSignature certification; - CertifyUserIdResult(PGPPublicKeyRing certificate, String userId, PGPSignature certification) { + CertificationResult(@Nonnull PGPPublicKeyRing certificate, @Nonnull PGPSignature certification) { this.certificate = certificate; - this.userId = userId; this.certification = certification; } + @Nonnull public PGPSignature getCertification() { return certification; } + @Nonnull public PGPPublicKeyRing getCertifiedCertificate() { - // inject the signature - PGPPublicKeyRing certified = KeyRingUtils.injectCertification(certificate, userId, certification); - return certified; + return certificate; } } + + private static PGPSecretKey getCertificationSecretKey(PGPSecretKeyRing certificationKey) { + Date now = DateUtil.now(); + KeyRingInfo info = PGPainless.inspectKeyRing(certificationKey, now); + + // We only support certification-capable primary keys + OpenPgpFingerprint fingerprint = info.getFingerprint(); + PGPPublicKey certificationPubKey = info.getPublicKey(fingerprint); + if (!info.isKeyValidlyBound(certificationPubKey.getKeyID())) { + throw new KeyException.RevokedKeyException(fingerprint); + } + + Date expirationDate = info.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER); + if (expirationDate != null && expirationDate.before(now)) { + throw new KeyException.ExpiredKeyException(fingerprint, expirationDate); + } + + PGPSecretKey secretKey = certificationKey.getSecretKey(certificationPubKey.getKeyID()); + if (secretKey == null) { + throw new KeyException.MissingSecretKeyException(fingerprint, certificationPubKey.getKeyID()); + } + return secretKey; + } + } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index f5a5292c..19d7ffcc 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -59,6 +59,7 @@ import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.signature.builder.DirectKeySignatureBuilder; import org.pgpainless.signature.builder.RevocationSignatureBuilder; import org.pgpainless.signature.builder.SelfSignatureBuilder; +import org.pgpainless.signature.subpackets.CertificationSubpackets; import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpackets; @@ -613,9 +614,11 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { final Date keyCreationTime = publicKey.getCreationTime(); DirectKeySignatureBuilder builder = new DirectKeySignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig); - builder.applyCallback(new SelfSignatureSubpackets.Callback() { + System.out.println("FIXME"); // will cause checkstyle warning so I remember + /* + builder.applyCallback(new CertificationSubpackets.Callback() { @Override - public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { + public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) { if (expiration != null) { hashedSubpackets.setKeyExpirationTime(keyCreationTime, expiration); } else { @@ -623,6 +626,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } } }); + */ return builder.build(publicKey); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySignatureBuilder.java index caf08710..0e5498bd 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySignatureBuilder.java @@ -10,9 +10,10 @@ 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; +import org.pgpainless.signature.subpackets.CertificationSubpackets; public class DirectKeySignatureBuilder extends AbstractSignatureBuilder { @@ -25,15 +26,15 @@ public class DirectKeySignatureBuilder extends AbstractSignatureBuilder", null); String bobUserId = "Bob "; @@ -39,8 +40,8 @@ public class CertifyCertificateTest { PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob); - CertifyCertificate.CertifyUserIdResult result = PGPainless.certifyCertificate() - .certifyUserId(bobCertificate, bobUserId) + CertifyCertificate.CertificationResult result = PGPainless.certify() + .userIdOnCertificate(bobUserId, bobCertificate) .withKey(alice, protector) .build(); @@ -64,4 +65,37 @@ public class CertifyCertificateTest { assertFalse(Arrays.areEqual(bobCertificate.getEncoded(), bobCertified.getEncoded())); } + + @Test + public void testKeyDelegation() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { + SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); + PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice ", null); + PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing("Bob ", null); + + PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob); + + CertifyCertificate.CertificationResult result = PGPainless.certify() + .certificate(bobCertificate, Trustworthiness.fullyTrusted().introducer()) + .withKey(alice, protector) + .build(); + + assertNotNull(result); + PGPSignature signature = result.getCertification(); + assertNotNull(signature); + assertEquals(SignatureType.DIRECT_KEY, SignatureType.valueOf(signature.getSignatureType())); + assertEquals(alice.getPublicKey().getKeyID(), signature.getKeyID()); + + assertTrue(SignatureVerifier.verifyDirectKeySignature( + signature, alice.getPublicKey(), bob.getPublicKey(), PGPainless.getPolicy(), DateUtil.now())); + + PGPPublicKeyRing bobCertified = result.getCertifiedCertificate(); + PGPPublicKey bobCertifiedKey = bobCertified.getPublicKey(); + + List sigsByAlice = CollectionUtils.iteratorToList( + bobCertifiedKey.getSignaturesForKeyID(alice.getPublicKey().getKeyID())); + assertEquals(1, sigsByAlice.size()); + assertEquals(signature, sigsByAlice.get(0)); + + assertFalse(Arrays.areEqual(bobCertificate.getEncoded(), bobCertified.getEncoded())); + } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java index 945a06a5..6c1c0cf4 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java @@ -40,6 +40,8 @@ public class DirectKeySignatureBuilderTest { secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); + System.out.println("FIXME"); // will cause checkstyle warning, so I remember + /* dsb.applyCallback(new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { @@ -50,6 +52,7 @@ public class DirectKeySignatureBuilderTest { hashedSubpackets.setFeatures(Feature.MODIFICATION_DETECTION); } }); + */ Thread.sleep(1000); From d2b48e83d921c187eb9609ccce4474fd42bca6b8 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 11 May 2022 12:27:11 +0200 Subject: [PATCH 3/9] Implement certifying of certifications --- .../pgpainless/algorithm/Trustworthiness.java | 32 ++++- .../key/certification/CertifyCertificate.java | 111 ++++++++++++++++-- .../secretkeyring/SecretKeyRingEditor.java | 12 +- .../DirectKeySelfSignatureBuilder.java | 57 +++++++++ ... ThirdPartyDirectKeySignatureBuilder.java} | 6 +- .../algorithm/TrustworthinessTest.java | 74 ++++++++++++ .../certification/CertifyCertificateTest.java | 9 +- ...rdPartyDirectKeySignatureBuilderTest.java} | 7 +- 8 files changed, 277 insertions(+), 31 deletions(-) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.java rename pgpainless-core/src/main/java/org/pgpainless/signature/builder/{DirectKeySignatureBuilder.java => ThirdPartyDirectKeySignatureBuilder.java} (81%) create mode 100644 pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java rename pgpainless-core/src/test/java/org/pgpainless/signature/builder/{DirectKeySignatureBuilderTest.java => ThirdPartyDirectKeySignatureBuilderTest.java} (94%) diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java index 573bbf9d..0df31764 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java @@ -10,8 +10,8 @@ public class Trustworthiness { private final int depth; public static final int THRESHOLD_FULLY_CONVINCED = 120; - public static final int THRESHOLD_MARGINALLY_CONVINCED = 60; - public static final int THRESHOLD_NOT_TRUSTED = 0; + public static final int MARGINALLY_CONVINCED = 60; + public static final int NOT_TRUSTED = 0; public Trustworthiness(int amount, int depth) { this.amount = capAmount(amount); @@ -26,6 +26,30 @@ public class Trustworthiness { return depth; } + public boolean isNotTrusted() { + return getAmount() == NOT_TRUSTED; + } + + public boolean isMarginallyTrusted() { + return getAmount() > NOT_TRUSTED; + } + + public boolean isFullyTrusted() { + return getAmount() >= THRESHOLD_FULLY_CONVINCED; + } + + public boolean isIntroducer() { + return getDepth() >= 1; + } + + public boolean canIntroduce(int otherDepth) { + return getDepth() > otherDepth; + } + + public boolean canIntroduce(Trustworthiness other) { + return canIntroduce(other.getDepth()); + } + /** * This means that we are fully convinced of the trustworthiness of the key. * @@ -41,7 +65,7 @@ public class Trustworthiness { * @return builder */ public static Builder marginallyTrusted() { - return new Builder(THRESHOLD_MARGINALLY_CONVINCED); + return new Builder(MARGINALLY_CONVINCED); } /** @@ -51,7 +75,7 @@ public class Trustworthiness { * @return builder */ public static Builder untrusted() { - return new Builder(THRESHOLD_NOT_TRUSTED); + return new Builder(NOT_TRUSTED); } public static final class Builder { diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java index 77dab4e8..8503f8bd 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java @@ -19,7 +19,7 @@ import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; -import org.pgpainless.signature.builder.DirectKeySignatureBuilder; +import org.pgpainless.signature.builder.ThirdPartyDirectKeySignatureBuilder; import org.pgpainless.signature.builder.ThirdPartyCertificationSignatureBuilder; import org.pgpainless.signature.subpackets.CertificationSubpackets; import org.pgpainless.util.DateUtil; @@ -28,20 +28,60 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Date; +/** + * API for creating certifications and delegations (Signatures) on keys. + * This API can be used to sign another persons OpenPGP key. + * + * A certification over a user-id is thereby used to attest, that the user believes that the user-id really belongs + * to the owner of the certificate. + * A delegation over a key can be used to delegate trust by marking the certificate as a trusted introducer. + */ public class CertifyCertificate { + /** + * Create a certification over a User-Id. + * By default, this method will use {@link CertificationType#GENERIC} to create the signature. + * If you need to create another type of certification, use + * {@link #userIdOnCertificate(String, PGPPublicKeyRing, CertificationType)} instead. + * + * @param userId user-id to certify + * @param certificate certificate + * @return API + */ CertificationOnUserId userIdOnCertificate(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate) { return new CertificationOnUserId(userId, certificate, CertificationType.GENERIC); } + /** + * Create a certification of the given {@link CertificationType} over a User-Id. + * + * @param userid user-id to certify + * @param certificate certificate + * @param certificationType type of signature + * @return API + */ CertificationOnUserId userIdOnCertificate(@Nonnull String userid, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { return new CertificationOnUserId(userid, certificate, certificationType); } + /** + * Create a delegation (direct key signature) over a certificate. + * + * @param certificate certificate + * @return API + */ DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate) { return certificate(certificate, null); } + /** + * Create a delegation (direct key signature) containing a {@link org.bouncycastle.bcpg.sig.TrustSignature} packet + * over a certificate. + * + * @param certificate certificate + * @param trustworthiness trustworthiness of the certificate + * @return API + */ DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { return new DelegationOnCertificate(certificate, trustworthiness); } @@ -58,8 +98,16 @@ public class CertifyCertificate { this.certificationType = certificationType; } + /** + * Create the certification using the given key. + * + * @param certificationKey key used to create the certification + * @param protector protector to unlock the certification key + * @return API + * @throws PGPException in case of an OpenPGP related error + */ CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { - PGPSecretKey secretKey = getCertificationSecretKey(certificationKey); + PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey); ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder( certificationType.asSignatureType(), secretKey, protector); @@ -80,11 +128,24 @@ public class CertifyCertificate { this.sigBuilder = sigBuilder; } - public CertificationResult withSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback) throws PGPException { + /** + * Apply the given signature subpackets and build the certification. + * + * @param subpacketCallback callback to modify the signatures subpackets + * @return result + * @throws PGPException in case of an OpenPGP related error + */ + public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback) throws PGPException { sigBuilder.applyCallback(subpacketCallback); return build(); } + /** + * Build the certification signature. + * + * @return result + * @throws PGPException in case of an OpenPGP related error + */ public CertificationResult build() throws PGPException { PGPSignature signature = sigBuilder.build(certificate, userId); PGPPublicKeyRing certifiedCertificate = KeyRingUtils.injectCertification(certificate, userId, signature); @@ -102,10 +163,18 @@ public class CertifyCertificate { this.trustworthiness = trustworthiness; } + /** + * Build the delegation using the given certification key. + * + * @param certificationKey key to create the certification with + * @param protector protector to unlock the certification key + * @return API + * @throws PGPException in case of an OpenPGP related error + */ public DelegationOnCertificateWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { - PGPSecretKey secretKey = getCertificationSecretKey(certificationKey); + PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey); - DirectKeySignatureBuilder sigBuilder = new DirectKeySignatureBuilder(secretKey, protector); + ThirdPartyDirectKeySignatureBuilder sigBuilder = new ThirdPartyDirectKeySignatureBuilder(secretKey, protector); if (trustworthiness != null) { sigBuilder.getHashedSubpackets().setTrust(true, trustworthiness.getDepth(), trustworthiness.getAmount()); } @@ -116,18 +185,31 @@ public class CertifyCertificate { public static class DelegationOnCertificateWithSubpackets { private final PGPPublicKeyRing certificate; - private final DirectKeySignatureBuilder sigBuilder; + private final ThirdPartyDirectKeySignatureBuilder sigBuilder; - public DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull DirectKeySignatureBuilder sigBuilder) { + DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull ThirdPartyDirectKeySignatureBuilder sigBuilder) { this.certificate = certificate; this.sigBuilder = sigBuilder; } - public CertificationResult withSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback) throws PGPException { + /** + * Apply the given signature subpackets and build the delegation signature. + * + * @param subpacketsCallback callback to modify the signatures subpackets + * @return result + * @throws PGPException in case of an OpenPGP related error + */ + public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback) throws PGPException { sigBuilder.applyCallback(subpacketsCallback); return build(); } + /** + * Build the delegation signature. + * + * @return result + * @throws PGPException in case of an OpenPGP related error + */ public CertificationResult build() throws PGPException { PGPPublicKey delegatedKey = certificate.getPublicKey(); PGPSignature delegation = sigBuilder.build(delegatedKey); @@ -146,18 +228,28 @@ public class CertifyCertificate { this.certification = certification; } + /** + * Return the signature. + * + * @return signature + */ @Nonnull public PGPSignature getCertification() { return certification; } + /** + * Return the certificate, which now contains the signature. + * + * @return certificate + signature + */ @Nonnull public PGPPublicKeyRing getCertifiedCertificate() { return certificate; } } - private static PGPSecretKey getCertificationSecretKey(PGPSecretKeyRing certificationKey) { + private static PGPSecretKey getCertifyingSecretKey(PGPSecretKeyRing certificationKey) { Date now = DateUtil.now(); KeyRingInfo info = PGPainless.inspectKeyRing(certificationKey, now); @@ -179,5 +271,4 @@ public class CertifyCertificate { } return secretKey; } - } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 19d7ffcc..7418401f 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -56,10 +56,9 @@ import org.pgpainless.key.protection.fixes.S2KUsageFix; import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider; import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.key.util.RevocationAttributes; -import org.pgpainless.signature.builder.DirectKeySignatureBuilder; +import org.pgpainless.signature.builder.DirectKeySelfSignatureBuilder; import org.pgpainless.signature.builder.RevocationSignatureBuilder; import org.pgpainless.signature.builder.SelfSignatureBuilder; -import org.pgpainless.signature.subpackets.CertificationSubpackets; import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpackets; @@ -613,12 +612,10 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { PGPPublicKey publicKey = primaryKey.getPublicKey(); final Date keyCreationTime = publicKey.getCreationTime(); - DirectKeySignatureBuilder builder = new DirectKeySignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig); - System.out.println("FIXME"); // will cause checkstyle warning so I remember - /* - builder.applyCallback(new CertificationSubpackets.Callback() { + DirectKeySelfSignatureBuilder builder = new DirectKeySelfSignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig); + builder.applyCallback(new SelfSignatureSubpackets.Callback() { @Override - public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) { + public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { if (expiration != null) { hashedSubpackets.setKeyExpirationTime(keyCreationTime, expiration); } else { @@ -626,7 +623,6 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } } }); - */ return builder.build(publicKey); } 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 new file mode 100644 index 00000000..cf99f8d6 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySelfSignatureBuilder.java @@ -0,0 +1,57 @@ +// 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/DirectKeySignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java similarity index 81% rename from pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySignatureBuilder.java rename to pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java index 0e5498bd..dd720bce 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/DirectKeySignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilder.java @@ -15,14 +15,14 @@ import org.pgpainless.algorithm.SignatureType; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.signature.subpackets.CertificationSubpackets; -public class DirectKeySignatureBuilder extends AbstractSignatureBuilder { +public class ThirdPartyDirectKeySignatureBuilder extends AbstractSignatureBuilder { - public DirectKeySignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) + public ThirdPartyDirectKeySignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) throws PGPException { super(certificationKey, protector, archetypeSignature); } - public DirectKeySignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { + public ThirdPartyDirectKeySignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { super(SignatureType.DIRECT_KEY, signingKey, protector); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java b/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java new file mode 100644 index 00000000..017126aa --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.algorithm; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class TrustworthinessTest { + + @Test + public void fullyTrustedIntroducer() { + Trustworthiness it = Trustworthiness.fullyTrusted().introducer(); + assertTrue(it.isFullyTrusted()); + assertFalse(it.isNotTrusted()); + + assertTrue(it.isIntroducer()); + assertFalse(it.canIntroduce(it)); + } + + @Test + public void marginallyTrustedIntroducer() { + Trustworthiness it = Trustworthiness.marginallyTrusted().introducer(); + assertFalse(it.isFullyTrusted()); + assertTrue(it.isMarginallyTrusted()); + assertFalse(it.isNotTrusted()); + + assertTrue(it.isIntroducer()); + assertFalse(it.canIntroduce(2)); + } + + @Test + public void nonTrustedIntroducer() { + Trustworthiness it = Trustworthiness.untrusted().introducer(); + assertTrue(it.isNotTrusted()); + assertFalse(it.isMarginallyTrusted()); + assertFalse(it.isFullyTrusted()); + + assertTrue(it.isIntroducer()); + } + + @Test + public void trustedMetaIntroducer() { + Trustworthiness it = Trustworthiness.fullyTrusted().metaIntroducer(); + assertTrue(it.isFullyTrusted()); + assertTrue(it.isIntroducer()); + + Trustworthiness that = Trustworthiness.fullyTrusted().introducer(); + assertTrue(it.canIntroduce(that)); + assertFalse(that.canIntroduce(it)); + } + + @Test + public void invalidArguments() { + assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(300, 1)); + assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(60, 300)); + assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(-4, 1)); + assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(120, -1)); + } + + @Test + public void inBetweenValues() { + Trustworthiness it = new Trustworthiness(30, 1); + assertTrue(it.isMarginallyTrusted()); + assertFalse(it.isFullyTrusted()); + + it = new Trustworthiness(140, 1); + assertTrue(it.isFullyTrusted()); + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java index c6028925..4a72276c 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java @@ -14,6 +14,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.List; +import org.bouncycastle.bcpg.sig.TrustSignature; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; @@ -84,13 +85,19 @@ public class CertifyCertificateTest { assertNotNull(signature); assertEquals(SignatureType.DIRECT_KEY, SignatureType.valueOf(signature.getSignatureType())); assertEquals(alice.getPublicKey().getKeyID(), signature.getKeyID()); + TrustSignature trustSignaturePacket = signature.getHashedSubPackets().getTrust(); + assertNotNull(trustSignaturePacket); + Trustworthiness trustworthiness = new Trustworthiness(trustSignaturePacket.getTrustAmount(), trustSignaturePacket.getDepth()); + assertTrue(trustworthiness.isFullyTrusted()); + assertTrue(trustworthiness.isIntroducer()); + assertFalse(trustworthiness.canIntroduce(1)); assertTrue(SignatureVerifier.verifyDirectKeySignature( signature, alice.getPublicKey(), bob.getPublicKey(), PGPainless.getPolicy(), DateUtil.now())); PGPPublicKeyRing bobCertified = result.getCertifiedCertificate(); PGPPublicKey bobCertifiedKey = bobCertified.getPublicKey(); - + List sigsByAlice = CollectionUtils.iteratorToList( bobCertifiedKey.getSignaturesForKeyID(alice.getPublicKey().getKeyID())); assertEquals(1, sigsByAlice.size()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java similarity index 94% rename from pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java rename to pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java index 6c1c0cf4..46dc1ac5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/DirectKeySignatureBuilderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java @@ -29,19 +29,17 @@ import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; -public class DirectKeySignatureBuilderTest { +public class ThirdPartyDirectKeySignatureBuilderTest { @Test public void testDirectKeySignatureBuilding() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .modernKeyRing("Alice"); - DirectKeySignatureBuilder dsb = new DirectKeySignatureBuilder( + DirectKeySelfSignatureBuilder dsb = new DirectKeySelfSignatureBuilder( secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); - System.out.println("FIXME"); // will cause checkstyle warning, so I remember - /* dsb.applyCallback(new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { @@ -52,7 +50,6 @@ public class DirectKeySignatureBuilderTest { hashedSubpackets.setFeatures(Feature.MODIFICATION_DETECTION); } }); - */ Thread.sleep(1000); From 870af0e005be7b65a23ed6d4672fdaefec50dd95 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 12 May 2022 16:24:55 +0200 Subject: [PATCH 4/9] Add javadoc documentation to Trustworthiness class --- .../pgpainless/algorithm/Trustworthiness.java | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java index 0df31764..8d62f967 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java @@ -4,6 +4,11 @@ package org.pgpainless.algorithm; +/** + * Facade class for {@link org.bouncycastle.bcpg.sig.TrustSignature}. + * A trust signature subpacket marks the trustworthiness of a certificate and defines its capabilities to act + * as a trusted introducer. + */ public class Trustworthiness { private final int amount; @@ -18,34 +23,82 @@ public class Trustworthiness { this.depth = capDepth(depth); } + /** + * Get the trust amount. + * This value means how confident the issuer of the signature is in validity of the binding. + * + * @return trust amount + */ public int getAmount() { return amount; } + /** + * Get the depth of the trust signature. + * This value controls, whether the certificate can act as a trusted introducer. + * + * @return depth + */ public int getDepth() { return depth; } + /** + * Returns true, if the trust amount is equal to 0. + * This means the key is not trusted. + * + * Otherwise return false + * @return true if untrusted + */ public boolean isNotTrusted() { return getAmount() == NOT_TRUSTED; } + /** + * Return true if the certificate is at least marginally trusted. + * That is the case, if the trust amount is greater than 0. + * + * @return true if the cert is at least marginally trusted + */ public boolean isMarginallyTrusted() { return getAmount() > NOT_TRUSTED; } + /** + * Return true if the certificate is fully trusted. That is the case if the trust amount is + * greater than or equal to 120. + * + * @return true if the cert is fully trusted + */ public boolean isFullyTrusted() { return getAmount() >= THRESHOLD_FULLY_CONVINCED; } + /** + * Return true, if the cert is an introducer. That is the case if the depth is greater 0. + * + * @return true if introducer + */ public boolean isIntroducer() { return getDepth() >= 1; } + /** + * Return true, if the certified cert can introduce certificates with trust depth of
otherDepth
. + * + * @param otherDepth other certifications trust depth + * @return true if the cert can introduce the other + */ public boolean canIntroduce(int otherDepth) { return getDepth() > otherDepth; } + /** + * Return true, if the certified cert can introduce certificates with the given
other
trust depth. + * + * @param other other certificates trust depth + * @return true if the cert can introduce the other + */ public boolean canIntroduce(Trustworthiness other) { return canIntroduce(other.getDepth()); } @@ -107,13 +160,13 @@ public class Trustworthiness { } /** - * The key is a level
n
meta introducer. + * The key is a meta introducer of depth
n
. * This key can introduce meta introducers of depth
n - 1
. * * @param n depth * @return trust */ - public Trustworthiness levelNIntroducer(int n) { + public Trustworthiness metaIntroducerOfDepth(int n) { return new Trustworthiness(amount, n); } } From 1483ff9e24a411381185ec42ee499fd81ae7a459 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 12 May 2022 16:25:04 +0200 Subject: [PATCH 5/9] Add another test for Trustworthiness --- .../pgpainless/algorithm/TrustworthinessTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java b/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java index 017126aa..bf87ed65 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/algorithm/TrustworthinessTest.java @@ -71,4 +71,17 @@ public class TrustworthinessTest { it = new Trustworthiness(140, 1); assertTrue(it.isFullyTrusted()); } + + @Test + public void depthHierarchyTest() { + Trustworthiness l1 = Trustworthiness.fullyTrusted().metaIntroducerOfDepth(1); + Trustworthiness l2 = Trustworthiness.fullyTrusted().metaIntroducerOfDepth(2); + Trustworthiness l3 = Trustworthiness.fullyTrusted().metaIntroducerOfDepth(3); + + assertTrue(l3.canIntroduce(l2)); + assertTrue(l3.canIntroduce(l1)); + assertTrue(l2.canIntroduce(l1)); + assertFalse(l1.canIntroduce(l2)); + assertFalse(l1.canIntroduce(l3)); + } } From bbd94c6c9abb4adb842967ccb5ae4e7bc8943b30 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 12 May 2022 16:26:57 +0200 Subject: [PATCH 6/9] More documentation --- .../main/java/org/pgpainless/algorithm/Trustworthiness.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java index 8d62f967..26e66e9c 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/Trustworthiness.java @@ -14,9 +14,9 @@ public class Trustworthiness { private final int amount; private final int depth; - public static final int THRESHOLD_FULLY_CONVINCED = 120; - public static final int MARGINALLY_CONVINCED = 60; - public static final int NOT_TRUSTED = 0; + public static final int THRESHOLD_FULLY_CONVINCED = 120; // greater or equal is fully trusted + public static final int MARGINALLY_CONVINCED = 60; // default value for marginally convinced + public static final int NOT_TRUSTED = 0; // 0 is not trusted public Trustworthiness(int amount, int depth) { this.amount = capAmount(amount); From 8d2afdf3b6f1a94e1246309394115355005ba2df Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 May 2022 18:32:59 +0200 Subject: [PATCH 7/9] Make certify() methods public --- .../key/certification/CertifyCertificate.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java index 8503f8bd..4a8d3985 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java @@ -48,7 +48,7 @@ public class CertifyCertificate { * @param certificate certificate * @return API */ - CertificationOnUserId userIdOnCertificate(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate) { + public CertificationOnUserId userIdOnCertificate(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate) { return new CertificationOnUserId(userId, certificate, CertificationType.GENERIC); } @@ -60,7 +60,7 @@ public class CertifyCertificate { * @param certificationType type of signature * @return API */ - CertificationOnUserId userIdOnCertificate(@Nonnull String userid, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { + public CertificationOnUserId userIdOnCertificate(@Nonnull String userid, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { return new CertificationOnUserId(userid, certificate, certificationType); } @@ -70,7 +70,7 @@ public class CertifyCertificate { * @param certificate certificate * @return API */ - DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate) { + public DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate) { return certificate(certificate, null); } @@ -82,7 +82,7 @@ public class CertifyCertificate { * @param trustworthiness trustworthiness of the certificate * @return API */ - DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { + public DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { return new DelegationOnCertificate(certificate, trustworthiness); } @@ -106,7 +106,7 @@ public class CertifyCertificate { * @return API * @throws PGPException in case of an OpenPGP related error */ - CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { + public CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey); ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder( From 7223b40b23ac73702b14655eb793e17ff071ad38 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 24 May 2022 20:22:33 +0200 Subject: [PATCH 8/9] Add javadoc and indentation --- .../key/certification/CertifyCertificate.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java index 4a8d3985..a4f37b66 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java @@ -48,7 +48,8 @@ public class CertifyCertificate { * @param certificate certificate * @return API */ - public CertificationOnUserId userIdOnCertificate(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate) { + public CertificationOnUserId userIdOnCertificate(@Nonnull String userId, + @Nonnull PGPPublicKeyRing certificate) { return new CertificationOnUserId(userId, certificate, CertificationType.GENERIC); } @@ -60,12 +61,16 @@ public class CertifyCertificate { * @param certificationType type of signature * @return API */ - public CertificationOnUserId userIdOnCertificate(@Nonnull String userid, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { + public CertificationOnUserId userIdOnCertificate(@Nonnull String userid, + @Nonnull PGPPublicKeyRing certificate, + @Nonnull CertificationType certificationType) { return new CertificationOnUserId(userid, certificate, certificationType); } /** * Create a delegation (direct key signature) over a certificate. + * This can be used to mark a certificate as a trusted introducer + * (see {@link #certificate(PGPPublicKeyRing, Trustworthiness)}). * * @param certificate certificate * @return API @@ -77,12 +82,14 @@ public class CertifyCertificate { /** * Create a delegation (direct key signature) containing a {@link org.bouncycastle.bcpg.sig.TrustSignature} packet * over a certificate. + * This can be used to mark a certificate as a trusted introducer. * * @param certificate certificate * @param trustworthiness trustworthiness of the certificate * @return API */ - public DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { + public DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate, + @Nullable Trustworthiness trustworthiness) { return new DelegationOnCertificate(certificate, trustworthiness); } @@ -92,7 +99,9 @@ public class CertifyCertificate { private final String userId; private final CertificationType certificationType; - CertificationOnUserId(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) { + CertificationOnUserId(@Nonnull String userId, + @Nonnull PGPPublicKeyRing certificate, + @Nonnull CertificationType certificationType) { this.userId = userId; this.certificate = certificate; this.certificationType = certificationType; @@ -106,7 +115,9 @@ public class CertifyCertificate { * @return API * @throws PGPException in case of an OpenPGP related error */ - public CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { + public CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, + @Nonnull SecretKeyRingProtector protector) + throws PGPException { PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey); ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder( @@ -122,7 +133,9 @@ public class CertifyCertificate { private final String userId; private final ThirdPartyCertificationSignatureBuilder sigBuilder; - CertificationOnUserIdWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull String userId, @Nonnull ThirdPartyCertificationSignatureBuilder sigBuilder) { + CertificationOnUserIdWithSubpackets(@Nonnull PGPPublicKeyRing certificate, + @Nonnull String userId, + @Nonnull ThirdPartyCertificationSignatureBuilder sigBuilder) { this.certificate = certificate; this.userId = userId; this.sigBuilder = sigBuilder; @@ -135,7 +148,8 @@ public class CertifyCertificate { * @return result * @throws PGPException in case of an OpenPGP related error */ - public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback) throws PGPException { + public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback) + throws PGPException { sigBuilder.applyCallback(subpacketCallback); return build(); } @@ -158,7 +172,8 @@ public class CertifyCertificate { private final PGPPublicKeyRing certificate; private final Trustworthiness trustworthiness; - DelegationOnCertificate(@Nonnull PGPPublicKeyRing certificate, @Nullable Trustworthiness trustworthiness) { + DelegationOnCertificate(@Nonnull PGPPublicKeyRing certificate, + @Nullable Trustworthiness trustworthiness) { this.certificate = certificate; this.trustworthiness = trustworthiness; } @@ -171,7 +186,9 @@ public class CertifyCertificate { * @return API * @throws PGPException in case of an OpenPGP related error */ - public DelegationOnCertificateWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException { + public DelegationOnCertificateWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, + @Nonnull SecretKeyRingProtector protector) + throws PGPException { PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey); ThirdPartyDirectKeySignatureBuilder sigBuilder = new ThirdPartyDirectKeySignatureBuilder(secretKey, protector); @@ -187,7 +204,8 @@ public class CertifyCertificate { private final PGPPublicKeyRing certificate; private final ThirdPartyDirectKeySignatureBuilder sigBuilder; - DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull ThirdPartyDirectKeySignatureBuilder sigBuilder) { + DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate, + @Nonnull ThirdPartyDirectKeySignatureBuilder sigBuilder) { this.certificate = certificate; this.sigBuilder = sigBuilder; } @@ -199,7 +217,8 @@ public class CertifyCertificate { * @return result * @throws PGPException in case of an OpenPGP related error */ - public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback) throws PGPException { + public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback) + throws PGPException { sigBuilder.applyCallback(subpacketsCallback); return build(); } From a944d2a6b9b660935ae19bde8bf4ad7bab636175 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 20 Jun 2022 15:09:02 +0200 Subject: [PATCH 9/9] Fix build errors --- .../src/main/java/org/pgpainless/PGPainless.java | 5 +++++ .../key/certification/CertifyCertificateTest.java | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index a2f16ab9..74a1f239 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -165,6 +165,11 @@ public final class PGPainless { return Policy.getInstance(); } + /** + * Create different kinds of signatures on other keys. + * + * @return builder + */ public static CertifyCertificate certify() { return new CertifyCertificate(); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java index 4a72276c..3dbc4988 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/certification/CertifyCertificateTest.java @@ -35,9 +35,9 @@ public class CertifyCertificateTest { @Test public void testUserIdCertification() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); - PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice ", null); + PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice "); String bobUserId = "Bob "; - PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing(bobUserId, null); + PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing(bobUserId); PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob); @@ -70,8 +70,8 @@ public class CertifyCertificateTest { @Test public void testKeyDelegation() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); - PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice ", null); - PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing("Bob ", null); + PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice "); + PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing("Bob "); PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob);