diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java index e69ea0a7..6d53ad1d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java @@ -531,6 +531,11 @@ public final class SignatureSubpacketsUtil { return hashed(signature, SignatureSubpacket.exportableCertification); } + public static boolean isExportable(PGPSignature signature) { + Exportable exportable = getExportableCertification(signature); + return exportable == null || exportable.isExportable(); + } + /** * Return the trust signature packet from the signatures hashed area. * @@ -541,6 +546,22 @@ public final class SignatureSubpacketsUtil { return hashed(signature, SignatureSubpacket.trustSignature); } + public static int getTrustDepthOr(PGPSignature signature, int defaultDepth) { + TrustSignature packet = getTrustSignature(signature); + if (packet != null) { + return packet.getDepth(); + } + return defaultDepth; + } + + public static int getTrustAmountOr(PGPSignature signature, int defaultAmount) { + TrustSignature packet = getTrustSignature(signature); + if (packet != null) { + return packet.getTrustAmount(); + } + return defaultAmount; + } + /** * Return all regular expression subpackets from the hashed area of the given signature. * diff --git a/pgpainless-wot/src/main/java/org/pgpainless/wot/CertificationFactory.java b/pgpainless-wot/src/main/java/org/pgpainless/wot/CertificationFactory.java new file mode 100644 index 00000000..e641f4ad --- /dev/null +++ b/pgpainless-wot/src/main/java/org/pgpainless/wot/CertificationFactory.java @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.wot; + +import java.util.ArrayList; +import java.util.List; + +import org.bouncycastle.bcpg.sig.RegularExpression; +import org.bouncycastle.bcpg.sig.TrustSignature; +import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; +import org.pgpainless.wot.dijkstra.sq.CertSynopsis; +import org.pgpainless.wot.dijkstra.sq.Certification; +import org.pgpainless.wot.dijkstra.sq.Depth; +import org.pgpainless.wot.dijkstra.sq.Optional; +import org.pgpainless.wot.dijkstra.sq.RegexSet; + +/** + * Factory class for creating {@link Certification} objects from {@link PGPSignature PGPSignatures}. + * The purpose of this class is to minimize the number of PGPainless / Bouncycastle class dependencies in wot-dijkstra. + */ +public class CertificationFactory { + + /** + * Create a {@link Certification} object from a delegation signature. + * + * @param issuer signature issuer certificate + * @param target signature target certificate + * @param signature signature + * @return certification + */ + public static Certification fromDelegation(CertSynopsis issuer, + CertSynopsis target, + PGPSignature signature) { + return fromSignature(issuer, Optional.empty(), target, signature); + } + + /** + * Create a {@link Certification} object from a certification signature. + * + * @param issuer signature issuer certificate + * @param targetUserId signature target user ID + * @param target signature target certificate + * @param signature signature + * @return certification + */ + public static Certification fromCertification(CertSynopsis issuer, + String targetUserId, + CertSynopsis target, + PGPSignature signature) { + return fromSignature(issuer, Optional.just(targetUserId), target, signature); + } + + /** + * Create a {@link Certification} object from a signature. + * + * @param issuer signature issuer certificate + * @param targetUserId optional signature target user ID + * @param target signature target certificate + * @param signature signature + * @return certification + */ + public static Certification fromSignature(CertSynopsis issuer, + Optional targetUserId, + CertSynopsis target, + PGPSignature signature) { + return new Certification( + issuer, + target, + targetUserId, + SignatureSubpacketsUtil.getSignatureCreationTime(signature).getTime(), + Optional.maybe(SignatureSubpacketsUtil.getSignatureExpirationTimeAsDate(signature)), + SignatureSubpacketsUtil.isExportable(signature), + getTrustAmountFrom(signature), + getTrustDepthFrom(signature), + regexSetFrom(signature)); + } + + /** + * Extract the trust amount from the signature. + * If the signature has no {@link TrustSignature} subpacket, return a default value of 120. + * + * @param signature signature + * @return trust amount + */ + private static int getTrustAmountFrom(PGPSignature signature) { + TrustSignature packet = SignatureSubpacketsUtil.getTrustSignature(signature); + if (packet != null) { + return packet.getTrustAmount(); + } + return 120; // default value + } + + /** + * Extract the trust depth from the signature. + * If the signature has no {@link TrustSignature} subpacket, return a default value of 0. + * + * @param signature signature + * @return trust depth + */ + private static Depth getTrustDepthFrom(PGPSignature signature) { + TrustSignature packet = SignatureSubpacketsUtil.getTrustSignature(signature); + if (packet != null) { + return Depth.auto(packet.getDepth()); + } + return Depth.limited(0); + } + + /** + * Extract a {@link RegexSet} from the signature. + * If the signature has no {@link RegularExpression} subpacket, the result will equate to a wildcard. + * + * @param signature signature + * @return regex set + */ + private static RegexSet regexSetFrom(PGPSignature signature) { + List regexList = SignatureSubpacketsUtil.getRegularExpressions(signature); + List stringList = new ArrayList<>(); + for (RegularExpression regex : regexList) { + stringList.add(regex.getRegex()); + } + return RegexSet.fromExpressionList(stringList); + } +} diff --git a/pgpainless-wot/src/main/java/org/pgpainless/wot/WebOfTrust.java b/pgpainless-wot/src/main/java/org/pgpainless/wot/WebOfTrust.java index e1ea3baa..924136c5 100644 --- a/pgpainless-wot/src/main/java/org/pgpainless/wot/WebOfTrust.java +++ b/pgpainless-wot/src/main/java/org/pgpainless/wot/WebOfTrust.java @@ -246,7 +246,7 @@ public class WebOfTrust implements CertificateAuthority { boolean valid = SignatureVerifier.verifyDirectKeySignature(delegation, issuerSigningKey, targetPrimaryKey, policy, referenceTime.getTimestamp()); if (valid) { - indexEdge(new Certification(issuer, Optional.empty(), target, delegation)); + indexEdge(CertificationFactory.fromDelegation(issuer, target, delegation)); } } catch (SignatureValidationException e) { LOGGER.warn("Cannot verify signature by " + issuerFingerprint + " on cert of " + OpenPgpFingerprint.of(targetPrimaryKey), e); @@ -273,7 +273,7 @@ public class WebOfTrust implements CertificateAuthority { boolean valid = SignatureVerifier.verifySignatureOverUserId(userId, certification, issuerSigningKey, targetPrimaryKey, policy, referenceTime.getTimestamp()); if (valid) { - indexEdge(new Certification(issuer, Optional.just(userId), target, certification)); + indexEdge(CertificationFactory.fromCertification(issuer, userId, target, certification)); } } catch (SignatureValidationException e) { LOGGER.warn("Cannot verify signature for '" + userId + "' by " + issuerFingerprint + " on cert of " + target.getFingerprint(), e); diff --git a/wot-dijkstra/build.gradle b/wot-dijkstra/build.gradle index 083eb627..aa7c46d8 100644 --- a/wot-dijkstra/build.gradle +++ b/wot-dijkstra/build.gradle @@ -34,6 +34,7 @@ dependencies { // @Nullable, @Nonnull annotations implementation "com.google.code.findbugs:jsr305:3.0.2" + // OpenPgpFingerprint, RevocationState implementation(project(":pgpainless-core")) } diff --git a/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/Certification.java b/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/Certification.java index 29d3b973..baaed2d8 100644 --- a/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/Certification.java +++ b/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/Certification.java @@ -5,13 +5,6 @@ package org.pgpainless.wot.dijkstra.sq; import java.util.Date; -import java.util.List; - -import org.bouncycastle.bcpg.sig.Exportable; -import org.bouncycastle.bcpg.sig.RegularExpression; -import org.bouncycastle.bcpg.sig.TrustSignature; -import org.bouncycastle.openpgp.PGPSignature; -import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; public class Certification { @@ -63,29 +56,6 @@ public class Certification { this.regex = RegexSet.wildcard(); } - public Certification(CertSynopsis issuer, - Optional targetUserId, - CertSynopsis target, - PGPSignature signature) { - this.issuer = issuer; - this.target = target; - this.userId = targetUserId; - this.creationTime = SignatureSubpacketsUtil.getSignatureCreationTime(signature).getTime(); - this.expirationTime = Optional.maybe(SignatureSubpacketsUtil.getSignatureExpirationTimeAsDate(signature)); - Exportable exportablePacket = SignatureSubpacketsUtil.getExportableCertification(signature); - this.exportable = exportablePacket == null || exportablePacket.isExportable(); - TrustSignature trustSignaturePacket = SignatureSubpacketsUtil.getTrustSignature(signature); - if (trustSignaturePacket == null) { - this.trustDepth = Depth.limited(0); - this.trustAmount = 120; - } else { - this.trustDepth = Depth.auto(trustSignaturePacket.getDepth()); - this.trustAmount = trustSignaturePacket.getTrustAmount(); - } - List regularExpressionList = SignatureSubpacketsUtil.getRegularExpressions(signature); - this.regex = RegexSet.fromList(regularExpressionList); - } - /** * Get the issuer of the certification. * diff --git a/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/CertificationSet.java b/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/CertificationSet.java index 540ff850..24a667a7 100644 --- a/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/CertificationSet.java +++ b/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/CertificationSet.java @@ -9,8 +9,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.bouncycastle.openpgp.PGPSignature; - import javax.annotation.Nonnull; /** @@ -42,29 +40,6 @@ public final class CertificationSet { return set; } - /** - * Create a {@link CertificationSet} from a single certification. - * - * @param issuer issuer - * @param target target - * @param userId user-id - * @param certification certification - * @return singleton set - */ - public static CertificationSet fromCertification( - CertSynopsis issuer, - CertSynopsis target, - Optional userId, - PGPSignature certification) { - - Map, List> certificationMap = new HashMap<>(); - List certificationList = new ArrayList<>(); - certificationList.add(new Certification(issuer, userId, target, certification)); - certificationMap.put(userId, certificationList); - - return new CertificationSet(issuer, target, certificationMap); - } - private CertificationSet(CertSynopsis issuer, CertSynopsis target, Map, List> certifications) { diff --git a/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/RegexSet.java b/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/RegexSet.java index 65d80f2e..59807ff3 100644 --- a/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/RegexSet.java +++ b/wot-dijkstra/src/main/java/org/pgpainless/wot/dijkstra/sq/RegexSet.java @@ -12,8 +12,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nonnull; -import org.bouncycastle.bcpg.sig.RegularExpression; - public final class RegexSet { private final Set regexStrings; @@ -22,20 +20,17 @@ public final class RegexSet { this.regexStrings = regexStrings; } - public static RegexSet fromList(@Nonnull List regexList) { - Set regexStringSet = new HashSet<>(); - for (RegularExpression regex : regexList) { - regexStringSet.add(regex.getRegex()); - } + public static RegexSet fromExpressionList(@Nonnull List regexList) { + Set regexStringSet = new HashSet<>(regexList); return new RegexSet(regexStringSet); } - public static RegexSet fromRegex(@Nonnull RegularExpression regex) { - return fromList(Collections.singletonList(regex)); + public static RegexSet fromExpression(@Nonnull String regex) { + return fromExpressionList(Collections.singletonList(regex)); } public static RegexSet wildcard() { - return fromList(Collections.emptyList()); + return fromExpressionList(Collections.emptyList()); } public boolean matches(String string) {