pgpainless/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/WotNetwork.kt

149 lines
5.6 KiB
Kotlin

// SPDX-FileCopyrightText: 2023 Heiko Schaefer <heiko@schaefer.name>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.wot.dijkstra
import org.pgpainless.wot.network.*
import org.pgpainless.wot.query.Path
/**
* A wrapper for `Network` that performs the following functions:
*
* - Expose an (experimental) API that simplifies the implementation of the WoT algorithm (in `Query`)
* - TODO: polish, and possibly upstream into `Network`
*
* - Form residual networks for the Ford-Fulkerson algorithm via `suppressPath()`
* - `getEffectiveTrustAmount()` returns effective trust amounts of certifications
* (taking the residual network into account)
*/
internal class WotNetwork(private val network: Network, private val certificationNetwork: Boolean) {
fun nodeByFpr(fpr: Fingerprint): Node? = network.nodes[fpr]
fun getSelfSig(targetFpr: Fingerprint, targetUserid: String): EdgeComponent? {
val target = nodeByFpr(targetFpr)
if (target?.userIds?.get(targetUserid) != null) {
// Return a synthesized self-binding.
// XX: This EC should be generated during network generation.
return EdgeComponent(target, target, targetUserid, network.referenceTime.timestamp,
null, true, 120, Depth.limited(0), RegexSet.wildcard())
} else
return null
}
/**
* Return list of EdgeComponents that point to `fpr`.
*
* Doesn't currently return self-bindings.
*
* `targetUserid` is currently only evaluated to check matching with regex scoped delegations.
* Doesn't currently filter out EdgeComponents that point to `fpr`, but a different User ID
* (FIXME: filter by target User ID, if fpr == target, and target user id != null, then simplify Query?)
*
* In authentication network mode:
* - only return EdgeComponents whose depth allows authentication of a path that is `curLen` long.
* - only return EdgeComponents whose regexes match `targetUserid`.
*/
fun certificationsForSignee(fpr: Fingerprint, targetUserid: String, curLen: Int): List<EdgeComponent> {
val edges = network.reverseEdges[fpr] ?: return listOf()
val ec = edges.map { edge ->
edge.components.map { it.value }.flatten()
}
.flatten()
.filter {
if (!certificationNetwork) {
// Authentication network mode: honor depth limitation and regexes
it.trustDepth >= curLen && it.regexes.matches(targetUserid)
} else {
// Certification network mode: Keep certifications of any depth, and ignore regex scoping
true
}
}
return ec
}
fun isValidTarget(targetFpr: Fingerprint, targetUserid: String): Node? {
// Node must be in the network.
val target = nodeByFpr(targetFpr) ?: return null
// Target may not be expired at the reference time.
if ((target.expirationTime != null) &&
(target.expirationTime <= network.referenceTime.timestamp)) {
return null
}
// Target may not be revoked at the reference time.
if (target.revocationState.isEffective(network.referenceTime)) {
return null
}
// The target doesn't need to have self-signed the User ID to authenticate the User ID.
// But if the target has revoked it, then it can't be authenticated.
val targetUa: RevocationState? = target.userIds[targetUserid]
if (targetUa != null && targetUa.isEffective(network.referenceTime)) {
return null
}
return target
}
// Modifiers are processed in order by getEffectiveTrustAmount() [first cap, then suppress]
private val capCertificate: HashMap<Fingerprint, Int> = HashMap()
private val suppressPath: HashMap<Pair<Fingerprint, Fingerprint>, Int> = HashMap()
/**
* Get effective trust amount for a certification:
* Cap if the root has limited trust, and take into account additional constraints of the residual network.
*/
fun getEffectiveTrustAmount(ec: EdgeComponent): Int {
// Start from trust amount on the certification
var amount = ec.trustAmount
// Cap to issuer's `capCertificate`, if set.
// (Used in case of less than fully trusted roots)
capCertificate[ec.issuer.fingerprint]?.let {
if (it < amount) {
amount = it
}
}
// Suppress by certificate (for Ford-Fulkerson residual network)
suppressPath[Pair(ec.issuer.fingerprint, ec.target.fingerprint)]?.let {
if (amount > it) {
amount -= it
} else {
amount = 0
}
}
return amount
}
/** Limit a certificate's initial trust amount to `amount` */
fun capCertificate(fingerprint: Fingerprint, amount: Int) {
capCertificate[fingerprint] = amount
}
/**
* Add suppression rules for all certifications along the specified path:
* Each edge is suppressed by `amountToSuppress`.
*/
fun suppressPath(path: Path, amountToSuppress: Int) {
if (amountToSuppress == 0) return
assert(amountToSuppress <= 120)
for (c in path.certifications) {
val curAmount = suppressPath[Pair(c.issuer.fingerprint, c.target.fingerprint)] ?: 0
val newAmount = curAmount + amountToSuppress
assert(newAmount <= 120)
suppressPath[Pair(c.issuer.fingerprint, c.target.fingerprint)] = newAmount
}
}
}