mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-25 12:27:58 +01:00
WIP: Identify command
This commit is contained in:
parent
fbdcae3c81
commit
c98d6c4708
6 changed files with 94 additions and 17 deletions
|
@ -15,12 +15,15 @@ import org.pgpainless.wot.network.Fingerprint
|
||||||
import org.pgpainless.wot.network.ReferenceTime
|
import org.pgpainless.wot.network.ReferenceTime
|
||||||
import org.pgpainless.wot.network.Root
|
import org.pgpainless.wot.network.Root
|
||||||
import org.pgpainless.wot.network.Roots
|
import org.pgpainless.wot.network.Roots
|
||||||
|
import pgp.cert_d.PGPCertificateDirectory
|
||||||
import pgp.cert_d.PGPCertificateStoreAdapter
|
import pgp.cert_d.PGPCertificateStoreAdapter
|
||||||
|
import pgp.cert_d.SpecialNames
|
||||||
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory
|
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory
|
||||||
import pgp.certificate_store.PGPCertificateStore
|
import pgp.certificate_store.PGPCertificateStore
|
||||||
import picocli.CommandLine
|
import picocli.CommandLine
|
||||||
import picocli.CommandLine.*
|
import picocli.CommandLine.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
@ -108,13 +111,19 @@ class WotCLI: Callable<Int> {
|
||||||
|
|
||||||
private val trustRoots: Roots
|
private val trustRoots: Roots
|
||||||
get() {
|
get() {
|
||||||
val trustRootFingerprints = if (mCertificateSource.gpg || gpgOwnertrust) {
|
var trustRootFingerprints = mTrustRoot.map { Fingerprint(it) }.map { Root(it) }
|
||||||
readGpgOwnertrust().plus(mTrustRoot.map { Fingerprint(it) })
|
if (mCertificateSource.gpg || gpgOwnertrust) {
|
||||||
} else {
|
trustRootFingerprints = trustRootFingerprints.plus(readGpgOwnertrust())
|
||||||
mTrustRoot.map { Fingerprint(it) }
|
|
||||||
}
|
}
|
||||||
|
if (mCertificateSource.pgpCertD != null) {
|
||||||
return Roots(trustRootFingerprints.map { Root(it) })
|
try {
|
||||||
|
val rootCert = certificateStore.getCertificate(SpecialNames.TRUST_ROOT)
|
||||||
|
trustRootFingerprints = trustRootFingerprints.plus(Root(Fingerprint(rootCert.fingerprint), Int.MAX_VALUE))
|
||||||
|
} catch (e: NoSuchElementException) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Roots(trustRootFingerprints)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val amount: Int
|
private val amount: Int
|
||||||
|
@ -149,15 +158,31 @@ class WotCLI: Callable<Int> {
|
||||||
return PGPCertificateStoreAdapter(certD)
|
return PGPCertificateStoreAdapter(certD)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readGpgOwnertrust(): List<Fingerprint> = Runtime.getRuntime()
|
fun readGpgOwnertrust(): List<Root> = Runtime.getRuntime()
|
||||||
.exec("/usr/bin/gpg --export-ownertrust")
|
.exec("/usr/bin/gpg --export-ownertrust")
|
||||||
.inputStream
|
.inputStream
|
||||||
.bufferedReader()
|
.bufferedReader()
|
||||||
.readLines()
|
.readLines()
|
||||||
|
.asSequence()
|
||||||
.filterNot { it.startsWith("#") }
|
.filterNot { it.startsWith("#") }
|
||||||
.filterNot { it.isBlank() }
|
.filterNot { it.isBlank() }
|
||||||
.map { it.substring(0, it.indexOf(':')) }
|
.map {
|
||||||
.map { Fingerprint(it) }
|
Fingerprint(it.substring(0, it.indexOf(':'))) to it.elementAt(it.indexOf(':') + 1) }
|
||||||
|
.map {
|
||||||
|
it.first to when (it.second.digitToInt()) {
|
||||||
|
2 -> null // unknown
|
||||||
|
3 -> 0 // not trust
|
||||||
|
4 -> 40 // marginally trusted
|
||||||
|
5 -> 120 // fully trusted
|
||||||
|
6 -> Int.MAX_VALUE // ultimately trusted
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filterNot { it.second == null }
|
||||||
|
.map {
|
||||||
|
Root(it.first, it.second!!)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the command.
|
* Execute the command.
|
||||||
|
@ -196,6 +221,9 @@ class WotCLI: Callable<Int> {
|
||||||
CommandLine(WotCLI()).execute(*args)
|
CommandLine(WotCLI()).execute(*args)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|
|
@ -45,7 +45,7 @@ class AuthenticateCmd: Callable<Int> {
|
||||||
@CommandLine.Option(names = ["--email"], description = ["Consider all user-IDs that contain the given email address."])
|
@CommandLine.Option(names = ["--email"], description = ["Consider all user-IDs that contain the given email address."])
|
||||||
var email = false
|
var email = false
|
||||||
|
|
||||||
private val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the command.
|
* Execute the command.
|
||||||
|
@ -83,7 +83,7 @@ class AuthenticateCmd: Callable<Int> {
|
||||||
sb.appendLine(" ├ ${certification.issuer.fingerprint}${issuerUserId}")
|
sb.appendLine(" ├ ${certification.issuer.fingerprint}${issuerUserId}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.appendLine(" │ certified the following binding on ${dateFormat.format(certification.creationTime)}")
|
sb.appendLine(" │ certified the following binding on ${WotCLI.dateFormat.format(certification.creationTime)}")
|
||||||
}
|
}
|
||||||
sb.appendLine(" └ ${result.fingerprint} \"${result.userId}\"")
|
sb.appendLine(" └ ${result.fingerprint} \"${result.userId}\"")
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
package org.pgpainless.wot.cli.subcommands
|
package org.pgpainless.wot.cli.subcommands
|
||||||
|
|
||||||
|
import org.pgpainless.wot.api.IdentifyAPI
|
||||||
import org.pgpainless.wot.cli.WotCLI
|
import org.pgpainless.wot.cli.WotCLI
|
||||||
|
import org.pgpainless.wot.network.Fingerprint
|
||||||
import picocli.CommandLine
|
import picocli.CommandLine
|
||||||
import picocli.CommandLine.Command
|
import picocli.CommandLine.Command
|
||||||
import picocli.CommandLine.Parameters
|
import picocli.CommandLine.Parameters
|
||||||
|
@ -26,7 +28,36 @@ class IdentifyCmd: Callable<Int> {
|
||||||
*/
|
*/
|
||||||
override fun call(): Int {
|
override fun call(): Int {
|
||||||
val api = parent.api
|
val api = parent.api
|
||||||
TODO("Not yet implemented")
|
val result = api.identify(IdentifyAPI.Arguments(Fingerprint(fingerprint)))
|
||||||
|
println(formatResult(result))
|
||||||
|
return exitCode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatResult(result: IdentifyAPI.Result): String {
|
||||||
|
if (result.target == null || result.paths.isEmpty()) {
|
||||||
|
return "No paths found."
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildString {
|
||||||
|
result.paths.keys.forEach { userId ->
|
||||||
|
val target = result.target!!
|
||||||
|
appendLine("[✓] ${target.fingerprint} $userId: fully authenticated: (${result.percentage(userId)}%)")
|
||||||
|
result.paths[userId]!!.paths.forEach {path ->
|
||||||
|
val root = path.root
|
||||||
|
val userIdString = if (root.userIds.isEmpty()) "" else " (${root.userIds.keys.first()})"
|
||||||
|
appendLine(" ◯ ${root.fingerprint}$userIdString")
|
||||||
|
path.certifications.forEachIndexed { index, edge ->
|
||||||
|
appendLine(" │ certified the following binding on ${WotCLI.dateFormat.format(edge.creationTime)}")
|
||||||
|
append(" ").append(if (index == path.certifications.lastIndex) "└" else "├")
|
||||||
|
.appendLine(" ${edge.target.fingerprint} \"${edge.userId}\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exitCode(result: IdentifyAPI.Result): Int {
|
||||||
|
return if(result.paths.isEmpty()) -1 else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
package org.pgpainless.wot.api
|
package org.pgpainless.wot.api
|
||||||
|
|
||||||
import org.pgpainless.wot.network.Fingerprint
|
import org.pgpainless.wot.network.Fingerprint
|
||||||
|
import org.pgpainless.wot.network.Node
|
||||||
import org.pgpainless.wot.query.Paths
|
import org.pgpainless.wot.query.Paths
|
||||||
|
|
||||||
interface IdentifyAPI {
|
interface IdentifyAPI {
|
||||||
|
@ -13,5 +14,12 @@ interface IdentifyAPI {
|
||||||
|
|
||||||
data class Arguments(val fingerprint: Fingerprint)
|
data class Arguments(val fingerprint: Fingerprint)
|
||||||
|
|
||||||
data class Result(val paths: Paths)
|
data class Result(val paths: Map<String, Paths>, val target: Node?, val targetAmount: Int) {
|
||||||
|
fun percentage(userId: String): Int? {
|
||||||
|
if (paths[userId] == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return paths[userId]!!.amount * 100 / targetAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import org.pgpainless.wot.network.Fingerprint
|
||||||
import org.pgpainless.wot.network.Network
|
import org.pgpainless.wot.network.Network
|
||||||
import org.pgpainless.wot.network.ReferenceTime
|
import org.pgpainless.wot.network.ReferenceTime
|
||||||
import org.pgpainless.wot.network.Roots
|
import org.pgpainless.wot.network.Roots
|
||||||
|
import org.pgpainless.wot.query.Path
|
||||||
|
import org.pgpainless.wot.query.Paths
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web of Trust API, offering different operations.
|
* Web of Trust API, offering different operations.
|
||||||
|
@ -48,7 +50,16 @@ class WoTAPI(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun identify(arguments: IdentifyAPI.Arguments): IdentifyAPI.Result {
|
override fun identify(arguments: IdentifyAPI.Arguments): IdentifyAPI.Result {
|
||||||
TODO("Not yet implemented")
|
val cert = network.nodes[arguments.fingerprint] ?: return IdentifyAPI.Result(mutableMapOf(), null, trustAmount)
|
||||||
|
val allPaths = mutableMapOf<String, Paths>()
|
||||||
|
cert.userIds.keys.toList().forEach {
|
||||||
|
val query = Query(network, trustRoots, certificationNetwork)
|
||||||
|
val paths = query.authenticate(arguments.fingerprint, it, trustAmount)
|
||||||
|
if (paths.amount != 0) {
|
||||||
|
allPaths[it] = paths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IdentifyAPI.Result(allPaths, cert, trustAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun list(): ListAPI.Result {
|
override fun list(): ListAPI.Result {
|
||||||
|
|
|
@ -125,7 +125,6 @@ class DepthTest {
|
||||||
assertThrows<IllegalArgumentException> { limited(-1) }
|
assertThrows<IllegalArgumentException> { limited(-1) }
|
||||||
assertThrows<IllegalArgumentException> { limited(256) }
|
assertThrows<IllegalArgumentException> { limited(256) }
|
||||||
assertThrows<IllegalArgumentException> { auto(-1) }
|
assertThrows<IllegalArgumentException> { auto(-1) }
|
||||||
assertThrows<IllegalArgumentException> { auto(256) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue