From cdac41ffa936ba6c0c3adbbb8ddbcba9b52dcded Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 29 Jun 2023 20:18:01 +0200 Subject: [PATCH] Remove pgpainless-core dependency from wot-dijkstra --- .../kotlin/org/pgpainless/wot/WebOfTrust.kt | 27 +++++++----- .../org/pgpainless/wot/WebOfTrustTest.java | 30 ++++++++------ wot-dijkstra/build.gradle | 3 -- .../wot/dijkstra/sq/CertSynopsis.kt | 6 +-- .../pgpainless/wot/dijkstra/sq/Fingerprint.kt | 33 +++++++++++++++ .../org/pgpainless/wot/dijkstra/sq/Network.kt | 14 +++---- .../org/pgpainless/wot/dijkstra/sq/Path.kt | 6 +-- .../org/pgpainless/wot/dijkstra/sq/Paths.kt | 2 +- .../wot/dijkstra/sq/RevocationState.kt | 41 +++++++++++++++++++ 9 files changed, 120 insertions(+), 42 deletions(-) create mode 100644 wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Fingerprint.kt create mode 100644 wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/RevocationState.kt diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/WebOfTrust.kt b/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/WebOfTrust.kt index 0faffb32..0a5a25cf 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/WebOfTrust.kt +++ b/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/WebOfTrust.kt @@ -8,7 +8,6 @@ import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSignature import org.pgpainless.PGPainless import org.pgpainless.algorithm.KeyFlag -import org.pgpainless.algorithm.RevocationState import org.pgpainless.exception.SignatureValidationException import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.info.KeyRingInfo @@ -122,6 +121,11 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { RevocationState.softRevoked(revocation.creationTime) } + @JvmStatic + private fun OpenPgpFingerprint.map(): Fingerprint { + return Fingerprint(toString()) + } + /** * Class for building the [Flow network][Network] from the given set of OpenPGP keys. * @@ -133,19 +137,19 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { private val LOGGER = LoggerFactory.getLogger(NetworkBuilder::class.java) // certificates keyed by fingerprint - private val byFingerprint: MutableMap = HashMap() + private val byFingerprint: MutableMap = HashMap() // certificates keyed by (sub-) key-id private val byKeyId: MutableMap> = HashMap() // certificate synopses keyed by fingerprint - private val certSynopsisMap: MutableMap = HashMap() + private val certSynopsisMap: MutableMap = HashMap() // Issuer -> Targets, edges keyed by issuer - private val edges: MutableMap> = HashMap() + private val edges: MutableMap> = HashMap() // Target -> Issuers, edges keyed by target - private val reverseEdges: MutableMap> = HashMap() + private val reverseEdges: MutableMap> = HashMap() init { synopsizeCertificates(validatedCertificates) @@ -161,8 +165,9 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { private fun synopsize(cert: KeyRingInfo) { // index by fingerprint - if (!byFingerprint.containsKey(cert.fingerprint)) { - byFingerprint[cert.fingerprint] = cert + val certFingerprint = cert.fingerprint.map() + if (!byFingerprint.containsKey(certFingerprint)) { + byFingerprint[certFingerprint] = cert } // index by key-ID @@ -189,7 +194,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { // Some keys are malformed and have no KeyFlags return } - certSynopsisMap[cert.fingerprint] = CertSynopsis(cert.fingerprint, + certSynopsisMap[certFingerprint] = CertSynopsis(certFingerprint, expirationDate, revocationStateFromSignature(cert.revocationSelfSignature), userIds) @@ -205,7 +210,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { private fun findEdgesWithTarget(validatedTarget: KeyRingInfo) { val validatedTargetKeyRing = KeyRingUtils.publicKeys(validatedTarget.keys) - val targetFingerprint = OpenPgpFingerprint.of(validatedTargetKeyRing) + val targetFingerprint = OpenPgpFingerprint.of(validatedTargetKeyRing).map() val targetPrimaryKey = validatedTargetKeyRing.publicKey!! val target = certSynopsisMap[targetFingerprint]!! @@ -231,7 +236,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { ?: return for (candidate in issuerCandidates) { val issuerKeyRing = KeyRingUtils.publicKeys(candidate.keys) - val issuerFingerprint = OpenPgpFingerprint.of(issuerKeyRing) + val issuerFingerprint = OpenPgpFingerprint.of(issuerKeyRing).map() val issuerSigningKey = issuerKeyRing.getPublicKey(delegation.keyID) val issuer = certSynopsisMap[issuerFingerprint] ?: continue @@ -257,7 +262,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { ?: continue for (candidate in issuerCandidates) { val issuerKeyRing = KeyRingUtils.publicKeys(candidate.keys) - val issuerFingerprint = OpenPgpFingerprint.of(issuerKeyRing) + val issuerFingerprint = OpenPgpFingerprint.of(issuerKeyRing).map() val issuerSigningKey = issuerKeyRing.getPublicKey(certification.keyID) ?: continue val issuer = certSynopsisMap[issuerFingerprint] diff --git a/pgpainless-wot/src/test/java/org/pgpainless/wot/WebOfTrustTest.java b/pgpainless-wot/src/test/java/org/pgpainless/wot/WebOfTrustTest.java index d3976864..e95a45d9 100644 --- a/pgpainless-wot/src/test/java/org/pgpainless/wot/WebOfTrustTest.java +++ b/pgpainless-wot/src/test/java/org/pgpainless/wot/WebOfTrustTest.java @@ -12,9 +12,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.List; +import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.junit.jupiter.api.Test; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.wot.dijkstra.sq.CertificationSet; +import org.pgpainless.wot.dijkstra.sq.Fingerprint; import org.pgpainless.wot.dijkstra.sq.Network; import org.pgpainless.wot.testfixtures.TestCertificateStores; import org.pgpainless.wot.testfixtures.WotTestVectors; @@ -23,16 +25,20 @@ import pgp.certificate_store.exception.BadDataException; public class WebOfTrustTest { - OpenPgpFingerprint fooBankCa = OpenPgpFingerprint.of(WotTestVectors.getTestVectors().getFreshFooBankCaCert()); - OpenPgpFingerprint fooBankEmployee = OpenPgpFingerprint.of(WotTestVectors.getTestVectors().getFreshFooBankEmployeeCert()); - OpenPgpFingerprint fooBankAdmin = OpenPgpFingerprint.of(WotTestVectors.getTestVectors().getFreshFooBankAdminCert()); - OpenPgpFingerprint barBankCa = OpenPgpFingerprint.of(WotTestVectors.getTestVectors().getFreshBarBankCaCert()); - OpenPgpFingerprint barBankEmployee = OpenPgpFingerprint.of(WotTestVectors.getTestVectors().getFreshBarBankEmployeeCert()); + Fingerprint fooBankCa = fingerprintOf(WotTestVectors.getTestVectors().getFreshFooBankCaCert()); + Fingerprint fooBankEmployee = fingerprintOf(WotTestVectors.getTestVectors().getFreshFooBankEmployeeCert()); + Fingerprint fooBankAdmin = fingerprintOf(WotTestVectors.getTestVectors().getFreshFooBankAdminCert()); + Fingerprint barBankCa = fingerprintOf(WotTestVectors.getTestVectors().getFreshBarBankCaCert()); + Fingerprint barBankEmployee = fingerprintOf(WotTestVectors.getTestVectors().getFreshBarBankEmployeeCert()); public WebOfTrustTest() throws IOException { } + private static Fingerprint fingerprintOf(PGPPublicKeyRing cert) { + return new Fingerprint(OpenPgpFingerprint.of(cert).toString()); + } + @Test public void testWithTwoNodesAndOneDelegation() throws BadDataException, IOException, InterruptedException { PGPCertificateDirectory certD = TestCertificateStores.oneDelegationGraph(); @@ -87,30 +93,30 @@ public class WebOfTrustTest { // CHECKSTYLE:ON } - private void assertHasIssuerAndTarget(CertificationSet certifications, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private void assertHasIssuerAndTarget(CertificationSet certifications, Fingerprint issuer, Fingerprint target) { assertEquals(issuer, certifications.getIssuer().getFingerprint()); assertEquals(target, certifications.getTarget().getFingerprint()); } - private void assertHasEdge(Network network, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private void assertHasEdge(Network network, Fingerprint issuer, Fingerprint target) { assertNotNull(getEdgeFromTo(network, issuer, target), "Expected edge from " + issuer + " to " + target + " but got none."); } - private void assertHasReverseEdge(Network network, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private void assertHasReverseEdge(Network network, Fingerprint issuer, Fingerprint target) { assertNotNull(getReverseEdgeFromTo(network, issuer, target), "Expected reverse edge to " + target + " from " + issuer + " but got none."); } - private void assertHasNoEdge(Network network, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private void assertHasNoEdge(Network network, Fingerprint issuer, Fingerprint target) { CertificationSet edge = getEdgeFromTo(network, issuer, target); assertNull(edge, "Expected no edge from " + issuer + " to " + target + " but got " + edge); } - private void assertHasNoReverseEdge(Network network, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private void assertHasNoReverseEdge(Network network, Fingerprint issuer, Fingerprint target) { CertificationSet reverseEdge = getReverseEdgeFromTo(network, issuer, target); assertNull(reverseEdge, "Expected no reverse edge on " + target + " from " + issuer + " but got " + reverseEdge); } - private CertificationSet getEdgeFromTo(Network network, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private CertificationSet getEdgeFromTo(Network network, Fingerprint issuer, Fingerprint target) { List edges = network.getEdges().get(issuer); if (edges == null) { return null; @@ -124,7 +130,7 @@ public class WebOfTrustTest { return null; } - private CertificationSet getReverseEdgeFromTo(Network network, OpenPgpFingerprint issuer, OpenPgpFingerprint target) { + private CertificationSet getReverseEdgeFromTo(Network network, Fingerprint issuer, Fingerprint target) { List revEdges = network.getReverseEdges().get(target); if (revEdges == null) { return null; diff --git a/wot-dijkstra/build.gradle b/wot-dijkstra/build.gradle index 06523e06..e846f58f 100644 --- a/wot-dijkstra/build.gradle +++ b/wot-dijkstra/build.gradle @@ -34,9 +34,6 @@ dependencies { // @Nullable, @Nonnull annotations implementation "com.google.code.findbugs:jsr305:3.0.2" - - // OpenPgpFingerprint, RevocationState - implementation(project(":pgpainless-core")) } test { diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt index 061dd524..ac11901a 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt @@ -4,20 +4,18 @@ package org.pgpainless.wot.dijkstra.sq -import org.pgpainless.algorithm.RevocationState -import org.pgpainless.key.OpenPgpFingerprint import java.util.* /** * A [CertSynopsis] is a proxy object containing information about a certificate. * - * @param fingerprint [OpenPgpFingerprint] of the certificate + * @param fingerprint [Fingerprint] of the certificate * @param expirationTime optional expiration time of the certificate * @param revocationState [RevocationState] denoting whether the certificate is revoked or not * @param userIds [Map] of user-ids on the certificate, along with their revocation states */ data class CertSynopsis( - val fingerprint: OpenPgpFingerprint, + val fingerprint: Fingerprint, val expirationTime: Date?, val revocationState: RevocationState, val userIds : Map) { diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Fingerprint.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Fingerprint.kt new file mode 100644 index 00000000..fb15bc48 --- /dev/null +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Fingerprint.kt @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.wot.dijkstra.sq + +class Fingerprint(fingerprint: String) { + + val fingerprint: String + + init { + this.fingerprint = fingerprint.uppercase() + } + + override fun equals(other: Any?): Boolean { + if (other == null) { + return false + } + return if (other is Fingerprint) { + toString() == other.toString() + } else { + false + } + } + + override fun hashCode(): Int { + return toString().hashCode() + } + + override fun toString(): String { + return fingerprint.uppercase() + } +} \ No newline at end of file diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt index 07a2e062..72ed83da 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt @@ -4,23 +4,21 @@ package org.pgpainless.wot.dijkstra.sq -import org.pgpainless.key.OpenPgpFingerprint - /** * A network consists of nodes, and edges between them. * For the Web of Trust, nodes consist of [CertSynopses][CertSynopsis], while the edges between the nodes are * [CertificationSets][CertificationSet]. * * @constructor creates a new network - * @param nodes contains a [Map] of [CertSynopsis] keyed by their [OpenPgpFingerprint] - * @param edges [Map] keyed by the [fingerprint][OpenPgpFingerprint] of an issuer, whose values are [Lists][List] containing all edges originating from the issuer. - * @param reverseEdges [Map] keyed by the [fingerprint][OpenPgpFingerprint] of a target, whose values are [Lists][List] containing all edges pointing to the target + * @param nodes contains a [Map] of [CertSynopsis] keyed by their [Fingerprint] + * @param edges [Map] keyed by the [fingerprint][Fingerprint] of an issuer, whose values are [Lists][List] containing all edges originating from the issuer. + * @param reverseEdges [Map] keyed by the [fingerprint][Fingerprint] of a target, whose values are [Lists][List] containing all edges pointing to the target * @param referenceTime reference time at which the [Network] was built */ class Network( - val nodes: Map, - val edges: Map>, - val reverseEdges: Map>, + val nodes: Map, + val edges: Map>, + val reverseEdges: Map>, val referenceTime: ReferenceTime) { companion object { diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Path.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Path.kt index 59244d1f..e341b18a 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Path.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Path.kt @@ -15,9 +15,9 @@ import kotlin.math.min * @param residualDepth residual depth that is decreased each time another edge is appended */ class Path( - val root: CertSynopsis, - val edges: MutableList, - var residualDepth: Depth + private val root: CertSynopsis, + private val edges: MutableList, + private var residualDepth: Depth ) { /** diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Paths.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Paths.kt index 7da2fbb9..7eea165d 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Paths.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Paths.kt @@ -9,7 +9,7 @@ package org.pgpainless.wot.dijkstra.sq * * @param paths list of paths */ -class Paths(val paths: MutableList) { +class Paths(private val paths: MutableList) { /** * Empty collection of paths. diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/RevocationState.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/RevocationState.kt new file mode 100644 index 00000000..559dde46 --- /dev/null +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/RevocationState.kt @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.wot.dijkstra.sq + +import java.util.* + +/** + * Revocation State of + */ +class RevocationState private constructor(val type: Type, val timestamp: Date?) { + + enum class Type { + Soft, + Hard, + None + } + + companion object { + @JvmStatic + fun notRevoked(): RevocationState = RevocationState(Type.None, null) + + @JvmStatic + fun softRevoked(timestamp: Date): RevocationState = RevocationState(Type.Soft, timestamp) + + @JvmStatic + fun hardRevoked(): RevocationState = RevocationState(Type.Hard, null) + } + + fun isHardRevocation(): Boolean = type == Type.Hard + + fun isSoftRevocation(): Boolean = type == Type.Soft + + fun isNotRevoked(): Boolean = type == Type.None + + fun isEffective(referenceTime: ReferenceTime): Boolean { + return isHardRevocation() || + (isSoftRevocation() && referenceTime.timestamp.after(timestamp)) + } +} \ No newline at end of file