From a0d206bb15af5baa1c9e3ad1676cda877d1fda7b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 7 Jul 2023 15:41:17 +0200 Subject: [PATCH] Move the API definition to wot-dijkstra --- pgpainless-wot-cli/README.md | 2 ++ .../kotlin/org/pgpainless/wot/cli/WotCLI.kt | 12 +++++-- .../wot/cli/subcommands/AuthenticateCmd.kt | 20 +++++++++++- .../cli/subcommands/AuthenticateCmdTest.kt | 2 +- .../kotlin/org/pgpainless/wot/WebOfTrust.kt | 22 ++++++++++--- .../wot/dijkstra/sq}/api/AuthenticateAPI.kt | 2 +- .../dijkstra/sq}/api/AuthenticationLevel.kt | 2 +- .../pgpainless/wot/dijkstra/sq/api}/Format.kt | 2 +- .../wot/dijkstra/sq}/api/IdentifyAPI.kt | 2 +- .../wot/dijkstra/sq}/api/ListAPI.kt | 2 +- .../wot/dijkstra/sq}/api/LookupAPI.kt | 2 +- .../wot/dijkstra/sq}/api/PathAPI.kt | 2 +- .../pgpainless/wot/dijkstra/sq}/api/WoTAPI.kt | 31 +++++-------------- 13 files changed, 64 insertions(+), 39 deletions(-) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/AuthenticateAPI.kt (97%) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/AuthenticationLevel.kt (92%) rename {pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api}/Format.kt (94%) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/IdentifyAPI.kt (89%) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/ListAPI.kt (85%) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/LookupAPI.kt (88%) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/PathAPI.kt (94%) rename {pgpainless-wot/src/main/kotlin/org/pgpainless/wot => wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq}/api/WoTAPI.kt (62%) diff --git a/pgpainless-wot-cli/README.md b/pgpainless-wot-cli/README.md index e24ead9f..66f00e45 100644 --- a/pgpainless-wot-cli/README.md +++ b/pgpainless-wot-cli/README.md @@ -8,6 +8,8 @@ SPDX-License-Identifier: Apache-2.0 This module contains a command line interface application that acts as a front-end for [`pgpainless-wot`](../pgpainless-wot). +The interface of the application is modelled after the [sq-wot](https://gitlab.com/sequoia-pgp/sequoia-wot/) +reference implementation. ## Build diff --git a/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/WotCLI.kt b/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/WotCLI.kt index bb0a6ed3..3de88038 100644 --- a/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/WotCLI.kt +++ b/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/WotCLI.kt @@ -6,10 +6,11 @@ package org.pgpainless.wot.cli import org.pgpainless.certificate_store.PGPainlessCertD import org.pgpainless.util.DateUtil -import org.pgpainless.wot.api.WoTAPI +import org.pgpainless.wot.WebOfTrust import org.pgpainless.wot.cli.subcommands.* import org.pgpainless.wot.dijkstra.sq.Fingerprint import org.pgpainless.wot.dijkstra.sq.ReferenceTime +import org.pgpainless.wot.dijkstra.sq.api.WoTAPI import pgp.cert_d.PGPCertificateStoreAdapter import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory import pgp.certificate_store.PGPCertificateStore @@ -19,6 +20,11 @@ import java.io.File import java.util.concurrent.Callable import kotlin.system.exitProcess +/** + * Command Line Interface for pgpainless-wot, modelled after the reference implementation "sq-wot". + * + * @see Sequoia Web of Trust Reference Implementation + */ @Command(name = "pgpainless-wot", subcommands = [ AuthenticateCmd::class, @@ -110,8 +116,10 @@ class WotCLI: Callable { val api: WoTAPI get() { + val network = WebOfTrust(certificateStore) + .buildNetwork(referenceTime = referenceTime) return WoTAPI( - certStores = listOf(certificateStore), + network = network, trustRoots = trustRoots, gossip = false, certificationNetwork = false, diff --git a/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmd.kt b/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmd.kt index b87c86f9..3d7cc296 100644 --- a/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmd.kt +++ b/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmd.kt @@ -4,7 +4,7 @@ package org.pgpainless.wot.cli.subcommands -import org.pgpainless.wot.api.AuthenticateAPI +import org.pgpainless.wot.dijkstra.sq.api.AuthenticateAPI import org.pgpainless.wot.cli.WotCLI import org.pgpainless.wot.dijkstra.sq.Fingerprint import org.pgpainless.wot.dijkstra.sq.Path @@ -15,18 +15,33 @@ import picocli.CommandLine.ParentCommand import java.text.SimpleDateFormat import java.util.concurrent.Callable +/** + * Authenticate a binding between a certification and one of its user-ids. + */ @Command(name = "authenticate") class AuthenticateCmd: Callable { + /** + * Parent command to acquire global options from. + */ @ParentCommand lateinit var parent: WotCLI + /** + * Fingerprint of the certificate. + */ @Parameters(index = "0") lateinit var fingerprint: String + /** + * User-ID to authenticate. + */ @Parameters(index = "1") lateinit var userId: String + /** + * Handle the User-ID as an email address. + */ @CommandLine.Option(names = ["--email"], description = ["Consider all user-IDs that contain the given email address."]) var email = false @@ -46,6 +61,9 @@ class AuthenticateCmd: Callable { return 0 } + /** + * Format the [AuthenticateAPI.Result] as a [String] which can be printed to standard out. + */ internal fun formatResult(result: AuthenticateAPI.Result): String { if (result.percentage < 100) { return "No paths found." diff --git a/pgpainless-wot-cli/src/test/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmdTest.kt b/pgpainless-wot-cli/src/test/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmdTest.kt index e1e4072c..93831302 100644 --- a/pgpainless-wot-cli/src/test/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmdTest.kt +++ b/pgpainless-wot-cli/src/test/kotlin/org/pgpainless/wot/cli/subcommands/AuthenticateCmdTest.kt @@ -1,7 +1,7 @@ package org.pgpainless.wot.cli.subcommands import org.junit.jupiter.api.Test -import org.pgpainless.wot.api.AuthenticateAPI +import org.pgpainless.wot.dijkstra.sq.api.AuthenticateAPI import org.pgpainless.wot.dijkstra.sq.* import java.text.SimpleDateFormat import kotlin.test.assertEquals 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 66500d7b..3d80ab36 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/WebOfTrust.kt +++ b/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/WebOfTrust.kt @@ -43,14 +43,23 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { * * @param certificateDirectory PGP-Certificate-Directory instance */ - constructor(certificateDirectory: PGPCertificateDirectory): this(PGPCertificateStoreAdapter(certificateDirectory)) + constructor(certificateDirectory: PGPCertificateDirectory): + this(PGPCertificateStoreAdapter(certificateDirectory)) - fun buildNetwork(policy: Policy = PGPainless.getPolicy(), referenceTime: ReferenceTime = now()): Network { + /** + * + */ + fun buildNetwork(policy: Policy = PGPainless.getPolicy(), + referenceTime: ReferenceTime = now()): Network { val certificates = getAllCertificatesFromTheStore() val networkFactory = PGPNetworkFactory.fromCertificates(certificates, policy, referenceTime) return networkFactory.buildNetwork() } + /** + * Return a [Sequence] containing all [Certificates][Certificate] in the [PGPCertificateStore], + * with the specially named "trust-root" certificate optionally appended if present. + */ private fun getAllCertificatesFromTheStore(): Sequence { var trustRoot: Certificate? = null try { @@ -157,7 +166,8 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { while (userIds.hasNext()) { val userId = userIds.next() // There are potentially multiple certifications per user-ID - val userIdSigs = SignatureUtils.get3rdPartyCertificationsFor(userId, validatedTargetKeyRing) + val userIdSigs = SignatureUtils.get3rdPartyCertificationsFor( + userId, validatedTargetKeyRing) userIdSigs.forEach { processCertificationOnUserId(targetPrimaryKey, target, userId, it) } @@ -194,7 +204,8 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { } } catch (e: SignatureValidationException) { val targetFingerprint = OpenPgpFingerprint.of(targetPrimaryKey) - LOGGER.warn("Cannot verify signature by $issuerFingerprint on cert of $targetFingerprint", e) + LOGGER.warn("Cannot verify signature by $issuerFingerprint" + + " on cert of $targetFingerprint", e) } } } @@ -321,7 +332,8 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) { referenceTime: ReferenceTime): List { return certificates .mapNotNull { - try { PGPainless.readKeyRing().publicKeyRing(it.inputStream) } catch (e: IOException) { null } + try { PGPainless.readKeyRing().publicKeyRing(it.inputStream) } + catch (e: IOException) { null } } .map { KeyRingInfo(it, policy, referenceTime.timestamp) } .toList() diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/AuthenticateAPI.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/AuthenticateAPI.kt similarity index 97% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/AuthenticateAPI.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/AuthenticateAPI.kt index cd19a7e4..93e36ce6 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/AuthenticateAPI.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/AuthenticateAPI.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api import org.pgpainless.wot.dijkstra.sq.Fingerprint import org.pgpainless.wot.dijkstra.sq.Paths diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/AuthenticationLevel.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/AuthenticationLevel.kt similarity index 92% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/AuthenticationLevel.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/AuthenticationLevel.kt index 07303a00..680f4a56 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/AuthenticationLevel.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/AuthenticationLevel.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api /** * Enum for different levels of Trust. diff --git a/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/Format.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/Format.kt similarity index 94% rename from pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/Format.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/Format.kt index 6048e5a2..d39059f5 100644 --- a/pgpainless-wot-cli/src/main/kotlin/org/pgpainless/wot/cli/subcommands/Format.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/Format.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.cli.subcommands +package org.pgpainless.wot.dijkstra.sq.api /** * Enum listing possible output formats. diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/IdentifyAPI.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/IdentifyAPI.kt similarity index 89% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/IdentifyAPI.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/IdentifyAPI.kt index 647fdafb..64d179e0 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/IdentifyAPI.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/IdentifyAPI.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api import org.pgpainless.wot.dijkstra.sq.Fingerprint import org.pgpainless.wot.dijkstra.sq.Paths diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/ListAPI.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/ListAPI.kt similarity index 85% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/ListAPI.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/ListAPI.kt index cf64e056..45499865 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/ListAPI.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/ListAPI.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api import org.pgpainless.wot.dijkstra.sq.Paths diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/LookupAPI.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/LookupAPI.kt similarity index 88% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/LookupAPI.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/LookupAPI.kt index 09ff9f75..f13df777 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/LookupAPI.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/LookupAPI.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api import org.pgpainless.wot.dijkstra.sq.Paths diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/PathAPI.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/PathAPI.kt similarity index 94% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/PathAPI.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/PathAPI.kt index 01faa9f9..709c6400 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/PathAPI.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/PathAPI.kt @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api import org.pgpainless.wot.dijkstra.sq.Fingerprint diff --git a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/WoTAPI.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/WoTAPI.kt similarity index 62% rename from pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/WoTAPI.kt rename to wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/WoTAPI.kt index ded80e0f..70f42162 100644 --- a/pgpainless-wot/src/main/kotlin/org/pgpainless/wot/api/WoTAPI.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/api/WoTAPI.kt @@ -2,20 +2,16 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.wot.api +package org.pgpainless.wot.dijkstra.sq.api -import org.pgpainless.certificate_store.PGPainlessCertD -import org.pgpainless.util.NotationRegistry import org.pgpainless.wot.dijkstra.sq.Fingerprint +import org.pgpainless.wot.dijkstra.sq.Network import org.pgpainless.wot.dijkstra.sq.ReferenceTime -import pgp.cert_d.PGPCertificateStoreAdapter -import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory -import pgp.certificate_store.PGPCertificateStore /** * Web of Trust API, offering different operations. * - * @param certStores one or more [PGPCertificateStores][PGPCertificateStore] to retrieve certificates from. + * @param network initialized [Network] containing certificates as nodes and certifications as edges. * @param trustRoots one or more [Fingerprints][Fingerprint] of trust-roots. * @param gossip if true, consider all certificates as weakly trusted trust-roots * @param certificationNetwork if true, all certifications are treated as delegations with infinite trust depth and no regular expressions @@ -24,35 +20,24 @@ import pgp.certificate_store.PGPCertificateStore * @param knownNotationRegistry registry of known notations */ class WoTAPI( - val certStores: List = getDefaultCertStores(), + val network: Network, val trustRoots: List, val gossip: Boolean = false, val certificationNetwork: Boolean = false, val trustAmount: Int = AuthenticationLevel.Fully.amount, - val referenceTime: ReferenceTime = ReferenceTime.now(), - val knownNotationRegistry: NotationRegistry = NotationRegistry() + val referenceTime: ReferenceTime = ReferenceTime.now() ): AuthenticateAPI, IdentifyAPI, ListAPI, LookupAPI, PathAPI { /** * Secondary constructor, taking an [AuthenticationLevel] instead of an [Int]. */ - constructor(certStores: List = getDefaultCertStores(), + constructor(network: Network, trustRoots: List, gossip: Boolean = false, certificationNetwork: Boolean = false, trustAmount: AuthenticationLevel = AuthenticationLevel.Fully, - referenceTime: ReferenceTime = ReferenceTime.now(), - knownNotationRegistry: NotationRegistry = NotationRegistry()): - this(certStores,trustRoots, gossip,certificationNetwork, trustAmount.amount, referenceTime, knownNotationRegistry) - - companion object { - @JvmStatic - fun getDefaultCertStores(): List { - val certD = PGPainlessCertD.fileBased(InMemorySubkeyLookupFactory()) - val asStore = PGPCertificateStoreAdapter(certD) - return listOf(asStore) - } - } + referenceTime: ReferenceTime = ReferenceTime.now()): + this(network,trustRoots, gossip,certificationNetwork, trustAmount.amount, referenceTime) override fun authenticate(arguments: AuthenticateAPI.Arguments): AuthenticateAPI.Result { TODO("Not yet implemented")